This commit is contained in:
Brandon Mak 2022-09-11 22:34:55 +08:00
parent bf447c1d1d
commit 47e9e3d3f2
6 changed files with 236 additions and 65 deletions

View File

@ -33,6 +33,8 @@ namespace SHADE
};
cmdBufferHdl->GetVkCommandBuffer().copyBuffer(stagingBuffer, vkBuffer, 1, &copyRegion);
}
// TODO: Need to destroy staging buffer. Obviously not here but after the command has finished executing.
}
vk::Buffer SHVkBuffer::GetVkBuffer(void) const noexcept

View File

@ -48,7 +48,6 @@ namespace SHADE
vk::BufferUsageFlags bufferUsageFlags;
//! Reference to the allocator
//VmaAllocator const& vmaAllocator;
std::reference_wrapper<VmaAllocator const> vmaAllocator;
/*-----------------------------------------------------------------------*/

View File

@ -406,6 +406,12 @@ namespace SHADE
}
//void SHVkCommandBuffer::PipelineBarrier(vk::PipelineStageFlags ) const noexcept
//{
// //vkCommandBuffer.pipelineBarrier()
//}
/***************************************************************************/
/*!

View File

@ -114,6 +114,9 @@ namespace SHADE
void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept;
void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept;
// memory barriers
//void PipelineBarrier (void) const noexcept;
// Push Constant variable setting
template <typename T>
void SetPushConstantVariable(std::string variableName, T const& data) noexcept

View File

@ -5,16 +5,88 @@
#include "Tools/SHLogger.h"
#include "SHVkImageView.h"
#include "Graphics/Instance/SHVkInstance.h"
#include "Graphics/Buffers/SHVkBuffer.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
If an image is a GPU only resource, we need to prep a staging buffer
to use for transferring data to the GPU. #NoteToSelf: I don't really
like this because its duplicate code. Should try to find a way to utilize
the logical device for this.
\param data
Data to transfer.
\param srcSize
Size in bytes of the data.
*/
/***************************************************************************/
void SHVkImage::PrepStagingBuffer(void* data, uint32_t srcSize) noexcept
{
// For creation of buffer
vk::BufferCreateInfo bufferInfo{};
// size stored same as GPU buffer
bufferInfo.size = srcSize;
// We just want to set the transfer bit
bufferInfo.usage = vk::BufferUsageFlagBits::eTransferSrc;
// sharing mode exclusive
bufferInfo.sharingMode = vk::SharingMode::eExclusive;
// Set to auto detect bits
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
// We want to just write all at once. Using random access bit could make this slow
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
// parameters of a vmaAllocation retrieved via vmaGetAllocationInfo
VmaAllocationInfo allocInfo;
// results of allocation
VmaAllocation stagingAlloc;
// To get around VMA's usage for C version of vulkan, create a temp first...,
VkBuffer tempBuffer{};
// Create the buffer...
vmaCreateBuffer(*vmaAllocator,
&bufferInfo.operator VkBufferCreateInfo & (), // TODO: Verify if this works (can use renderdoc to check buffer variables?)
&allocCreateInfo,
&tempBuffer, &stagingAlloc, &allocInfo);
// then assign it to the hpp version
stagingBuffer = tempBuffer;
// Just map, copy then unmap
void* stagingBufferMappedPtr = nullptr;
vmaMapMemory(*vmaAllocator, stagingAlloc, &stagingBufferMappedPtr);
if (stagingBufferMappedPtr)
std::memcpy(static_cast<uint8_t*>(stagingBufferMappedPtr), static_cast<uint8_t*>(data), srcSize);
const VkDeviceSize offsets = 0;
const VkDeviceSize sizes = srcSize;
vmaFlushAllocations(*vmaAllocator, 1, &stagingAlloc, &offsets, &sizes);
vmaUnmapMemory(*vmaAllocator, stagingAlloc);
}
SHVkImage::SHVkImage(
VmaAllocator const& vmaAllocator,
SHImageCreateParams const& imageDetails,
unsigned char* data,
uint32_t dataSize,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
VmaAllocator const* allocator,
SHImageCreateParams const& imageDetails,
unsigned char* data,
uint32_t dataSize,
std::span<uint32_t> inMipOffsets,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
) noexcept
: imageType { imageDetails.imageType }
, width{ imageDetails.width }
@ -25,6 +97,11 @@ namespace SHADE
, imageFormat{ imageDetails.imageFormat }
, usageFlags{}
, createFlags{}
, vmaAllocator{allocator}
, mipOffsets { inMipOffsets }
, boundToCoherent{false}
, randomAccessOptimized {false}
, mappedPtr{nullptr}
{
for (auto& bit : imageDetails.usageBits)
usageFlags |= bit;
@ -66,7 +143,7 @@ namespace SHADE
VmaAllocationInfo allocInfo{};
VkImage tempImage;
auto result = vmaCreateImage(vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo&(), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
auto result = vmaCreateImage(*vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo&(), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
if (result != VK_SUCCESS)
SHVulkanDebugUtil::ReportVkError(vk::Result(result), "Failed to create vulkan image. ");
@ -75,55 +152,43 @@ namespace SHADE
vkImage = tempImage;
//if (allocFlags & )
// At this point the image and device memory have been created.
if (allocFlags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)
randomAccessOptimized = true;
// TODO: This constructor can only create a GPU only resource for now. Due to time constraint, I was trying to create a ctor
// fast to finish up the ImGUI backend. In the future, there definitely needs to be more versatility to the constructor.
// Get the memory property flags
VkMemoryPropertyFlags memPropFlags;
vmaGetAllocationMemoryProperties(*vmaAllocator, alloc, &memPropFlags);
// mainly host visible. Can be cached (need to flush/invalidate), uncached (always coherent) and coherent (virtual).
//if (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
//{
// // If memory is marked to be coherent between CPU and GPU (no need flush/invalidate) (TODO: Verify if VMA_ALLOCATION_CREATE_MAPPED_BIT is used when VMA_MEMORY_USAGE_AUTO is set)
// // TODO: also verify that coherent bit = pointer is already mapped
// if (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
// {
// boundToCoherent = true;
// mappedPtr = allocInfo.pMappedData;
// }
// else
// mappedPtr = nullptr;
// if (data)
// MapWriteUnmap(data, srcSize, 0, 0);
//}
//else
//{
// We can prep first so that we can do transfers later via 1 cmd buffer recording
PrepStagingBuffer(data, dataSize);
//}
}
/***************************************************************************/
/*!
\brief
This is mainly used for images that aren't created internally because
they cannot be created in the traditional way (e.g. swapchain images).
\param inVkImage
Image already created outside
\param width
Width of the image
\param height
Height of the image
\param depth
Depth of the image
\param levels
Number of levels in the image
\param arrayLayers
if the image is an array, this value will be > 1.
\param imageFormat
Format of the image
*/
/***************************************************************************/
SHVkImage::SHVkImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t arrayLayers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept
: vkImage (inVkImage)
, width{ inWidth }
, height{ inHeight }
, depth{ inDepth }
, mipLevelCount{ levels }
, layerCount{ arrayLayers }
, imageFormat{ format }
, usageFlags{flags}
, alloc{}
, imageType{type}
, createFlags{}
{
}
SHVkImage::SHVkImage(VmaAllocator const& vmaAllocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept
SHVkImage::SHVkImage(VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept
: width {w}
, height{h}
, depth {1}
@ -132,6 +197,7 @@ namespace SHADE
, imageFormat{format}
, usageFlags{usage}
, createFlags {create}
, vmaAllocator {allocator}
{
vk::ImageCreateInfo imageCreateInfo{};
imageCreateInfo.imageType = vk::ImageType::e2D;
@ -157,7 +223,7 @@ namespace SHADE
VmaAllocationInfo allocInfo{};
VkImage tempImage;
auto result = vmaCreateImage(vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo & (), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
auto result = vmaCreateImage(*vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo & (), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
vkImage = tempImage;
if (result != VK_SUCCESS)
@ -171,6 +237,69 @@ namespace SHADE
return SHVkInstance::GetResourceManager().Create<SHVkImageView>(inLogicalDeviceHdl, parent, createParams);
}
void SHVkImage::TransferToDeviceResource(Handle<SHVkCommandBuffer> const& cmdBufferHdl) noexcept
{
// prepare copy regions
std::vector<vk::BufferImageCopy> copyRegions{mipOffsets.size()};
for (uint32_t i = 0; i < mipOffsets.size(); ++i)
{
copyRegions[i].bufferOffset = mipOffsets[i];
copyRegions[i].bufferRowLength = 0; // for padding
copyRegions[i].bufferImageHeight = 0; // for padding
copyRegions[i].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; // TODO: Need to change this to base it off image format.
copyRegions[i].imageSubresource.mipLevel = i;
copyRegions[i].imageSubresource.baseArrayLayer = 0; // TODO: Array textures not supported yet
copyRegions[i].imageSubresource.layerCount = layerCount;
copyRegions[i].imageOffset = vk::Offset3D{ 0,0,0 };
copyRegions[i].imageExtent = vk::Extent3D{ width >> i, height >> i, 1 };
}
}
void SHVkImage::TransitionImage(vk::ImageLayout oldLayout, vk::ImageLayout newLayout) noexcept
{
vk::ImageMemoryBarrier barrier{};
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = vkImage;
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; // TODO: Need to change this to base it off image format.
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mipLevelCount;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = layerCount;
vk::PipelineStageFlagBits srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
vk::PipelineStageFlagBits dstStage = vk::PipelineStageFlagBits::eTopOfPipe;
if (oldLayout == vk::ImageLayout::eUndefined && newLayout == vk::ImageLayout::eTransferDstOptimal)
{
srcStage = vk::PipelineStageFlagBits::eTopOfPipe;
dstStage = vk::PipelineStageFlagBits::eTransfer;
barrier.srcAccessMask = vk::AccessFlagBits::eNone;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
}
else if (oldLayout == vk::ImageLayout::eTransferDstOptimal && newLayout == vk::ImageLayout::eShaderReadOnlyOptimal)
{
srcStage = vk::PipelineStageFlagBits::eTransfer;
// TODO, what if we want to access in compute shader
dstStage = vk::PipelineStageFlagBits::eFragmentShader;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
}
else
{
SHLOG_ERROR("Image layouts are invalid. ");
}
//cmdBufferHdl
}
void SHVkImage::LinkWithExteriorImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t layers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept
{
vkImage = inVkImage;

View File

@ -10,6 +10,7 @@ namespace SHADE
{
class SHVkLogicalDevice;
class SHVkImageView;
class SHVkCommandBuffer;
struct SHImageCreateParams
{
@ -47,6 +48,12 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Pointer to the vma allocator. #NoteToSelf: Not super proud of this being a pointer.
//! The only reason why this is a pointer is because a reference_wrapper cannot be default constructed.
//! And the reason why we want a default constructor is because sometimes we don't want to create images
//! but merely link them from outside (swapchain images)
VmaAllocator const* vmaAllocator;
//! 1D, 2D or 3D
vk::ImageType imageType = vk::ImageType::e2D;
@ -80,6 +87,29 @@ namespace SHADE
//! allocation object containing details of an allocation
VmaAllocation alloc{};
//! Whether or not this image is HOST_VISIBLE and random access optimized
bool randomAccessOptimized;
//! Whether or not the memory the image is bound to is memory coherent (updates on CPU can be seen on GPU without flushing cache)
bool boundToCoherent;
//! Persistently mapped pointer if applicable (will be void if image is
//! not created with the correct flags). Note that this is only used for
//! persistent mapping. One time updates do not use this pointer.
void* mappedPtr;
//! Staging buffer for images purely in the GPU
vk::Buffer stagingBuffer;
//! Mipmap offsets for initializing the vk::BufferImageCopy during transfer to GPU resource
std::span<uint32_t> mipOffsets;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void PrepStagingBuffer(void* data, uint32_t srcSize) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTOR AND DTOR */
@ -87,16 +117,16 @@ namespace SHADE
SHVkImage(void) noexcept = default;
SHVkImage(
VmaAllocator const& vmaAllocator,
SHImageCreateParams const& imageDetails,
unsigned char* data,
uint32_t dataSize,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
VmaAllocator const* allocator,
SHImageCreateParams const& imageDetails,
unsigned char* data,
uint32_t dataSize,
std::span<uint32_t> inMipOffsets,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
) noexcept;
SHVkImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t arrayLayers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept;
SHVkImage(VmaAllocator const& vmaAllocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept;
SHVkImage(VmaAllocator const* allocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept;
SHVkImage(SHVkImage&& rhs) noexcept = default;
SHVkImage& operator=(SHVkImage && rhs) noexcept = default;
@ -104,7 +134,9 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
Handle<SHVkImageView> CreateImageView(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) const noexcept;
Handle<SHVkImageView> CreateImageView (Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) const noexcept;
void TransferToDeviceResource (Handle<SHVkCommandBuffer> const& cmdBufferHdl) noexcept;
void TransitionImage (vk::ImageLayout oldLayout, vk::ImageLayout newLayout) noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */