mirror of
https://github.com/barkeser2002/flower.git
synced 2026-09-25 16:16:16 +03:00
399 lines
12 KiB
C++
399 lines
12 KiB
C++
#include "rhi.h"
|
|
#include <util/cityhash/city.h>
|
|
|
|
namespace engine
|
|
{
|
|
void GpuResource::init(const VulkanContext* context, const std::string& name, VkDeviceSize size)
|
|
{
|
|
m_runtimeUUID = buildRuntimeUUID64u();
|
|
m_context = context;
|
|
m_name = name;
|
|
m_size = size;
|
|
|
|
ASSERT(m_size > 0, "you should set size of buffer before create.");
|
|
}
|
|
|
|
|
|
VulkanBuffer::VulkanBuffer(
|
|
const VulkanContext* context,
|
|
const std::string& name,
|
|
VkBufferUsageFlags usageFlags,
|
|
VmaAllocationCreateFlags vmaUsage,
|
|
VkDeviceSize size,
|
|
void* data)
|
|
{
|
|
GpuResource::init(context, name, size);
|
|
|
|
VkBufferCreateInfo bufferInfo{};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = m_size;
|
|
bufferInfo.usage = usageFlags;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
VmaAllocationCreateInfo vmaallocInfo = {};
|
|
vmaallocInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
|
vmaallocInfo.flags = vmaUsage;
|
|
|
|
RHICheck(vmaCreateBuffer(m_context->getVMA(), &bufferInfo, &vmaallocInfo, &m_buffer, &m_allocation, nullptr));
|
|
|
|
m_bSupportDeviceAddress = (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
|
|
if (m_bSupportDeviceAddress)
|
|
{
|
|
// Get buffer address.
|
|
VkBufferDeviceAddressInfo bufferDeviceAddressInfo{};
|
|
bufferDeviceAddressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
|
|
bufferDeviceAddressInfo.buffer = m_buffer;
|
|
m_deviceAddress = vkGetBufferDeviceAddress(m_context->getDevice(), &bufferDeviceAddressInfo);
|
|
}
|
|
|
|
// Copy data if need upload.
|
|
if (data != nullptr)
|
|
{
|
|
void* dataMapped = nullptr;
|
|
vmaMapMemory(m_context->getVMA(), m_allocation, &dataMapped);
|
|
memcpy(dataMapped, data, m_size);
|
|
vmaUnmapMemory(m_context->getVMA(), m_allocation);
|
|
}
|
|
|
|
m_context->setResourceName(VK_OBJECT_TYPE_BUFFER, (uint64_t)m_buffer, m_name.c_str());
|
|
|
|
m_defaultInfo.offset = 0;
|
|
m_defaultInfo.range = m_size;
|
|
m_defaultInfo.buffer = m_buffer;
|
|
}
|
|
|
|
void VulkanBuffer::rename(const std::string& newName)
|
|
{
|
|
m_name = newName;
|
|
m_context->setResourceName(VK_OBJECT_TYPE_BUFFER, (uint64_t)m_buffer, newName.c_str());
|
|
}
|
|
|
|
void VulkanBuffer::map(VkDeviceSize size)
|
|
{
|
|
if (m_mapped == nullptr)
|
|
{
|
|
RHICheck(vmaMapMemory(m_context->getVMA(), m_allocation, &m_mapped));
|
|
}
|
|
}
|
|
|
|
void VulkanBuffer::copyTo(const void* data, VkDeviceSize size)
|
|
{
|
|
if (m_mapped != nullptr)
|
|
{
|
|
LOG_ERROR("Buffer already mapped, don't use this function.");
|
|
}
|
|
|
|
map(size);
|
|
memcpy(m_mapped, data, size);
|
|
unmap();
|
|
}
|
|
|
|
void VulkanBuffer::copyAndUpload(VkCommandBuffer cmd, const void* data, VulkanBuffer* buffer)
|
|
{
|
|
CHECK(buffer->getSize() <= this->getSize());
|
|
|
|
// copy vertex buffer gpu.
|
|
copyTo(data, buffer->getSize());
|
|
|
|
// copy to gpu
|
|
VkBufferCopy copyRegion{};
|
|
copyRegion.srcOffset = 0;
|
|
copyRegion.dstOffset = 0;
|
|
copyRegion.size = buffer->getSize();
|
|
vkCmdCopyBuffer(cmd, this->getVkBuffer(), buffer->getVkBuffer(), 1, ©Region);
|
|
}
|
|
|
|
void VulkanBuffer::unmap()
|
|
{
|
|
if (m_mapped != nullptr)
|
|
{
|
|
vmaUnmapMemory(m_context->getVMA(), m_allocation);
|
|
m_mapped = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
VulkanBuffer::~VulkanBuffer()
|
|
{
|
|
if (m_mapped)
|
|
{
|
|
unmap();
|
|
}
|
|
|
|
vmaDestroyBuffer(m_context->getVMA(), m_buffer, m_allocation);
|
|
}
|
|
|
|
|
|
void VulkanBuffer::bind(VkDeviceSize offset)
|
|
{
|
|
RHICheck(vmaBindBufferMemory2(m_context->getVMA(), m_allocation, offset, m_buffer, nullptr));
|
|
}
|
|
|
|
void VulkanBuffer::flush(VkDeviceSize size, VkDeviceSize offset)
|
|
{
|
|
RHICheck(vmaFlushAllocation(m_context->getVMA(), m_allocation, offset, size));
|
|
}
|
|
|
|
void VulkanBuffer::invalidate(VkDeviceSize size, VkDeviceSize offset)
|
|
{
|
|
RHICheck(vmaInvalidateAllocation(m_context->getVMA(), m_allocation, offset, size));
|
|
}
|
|
|
|
void VulkanBuffer::stageCopyFrom(VkBuffer inBuffer, VkDeviceSize size, VkDeviceSize srcOffset, VkDeviceSize destOffset)
|
|
{
|
|
m_context->executeImmediatelyMajorGraphics([&](VkCommandBuffer cb) {
|
|
VkBufferCopy copyRegion{};
|
|
|
|
copyRegion.srcOffset = srcOffset;
|
|
copyRegion.dstOffset = destOffset;
|
|
copyRegion.size = size;
|
|
vkCmdCopyBuffer(cb, inBuffer, m_buffer, 1, ©Region);
|
|
});
|
|
}
|
|
|
|
VulkanImage::VulkanImage(
|
|
const VulkanContext* context,
|
|
const std::string& name,
|
|
const VkImageCreateInfo& createInfo,
|
|
VkMemoryPropertyFlags preperty) : m_createInfo(createInfo)
|
|
{
|
|
// Create image to query memory size.
|
|
RHICheck(vkCreateImage(context->getDevice(), &m_createInfo, nullptr, &m_image));
|
|
VkMemoryRequirements memRequirements;
|
|
vkGetImageMemoryRequirements(context->getDevice(), m_image, &memRequirements);
|
|
vkDestroyImage(context->getDevice(), m_image, nullptr);
|
|
m_image = VK_NULL_HANDLE;
|
|
|
|
// Parent init.
|
|
GpuResource::init(context, name, memRequirements.size);
|
|
|
|
// Init some subresources.
|
|
size_t subresourceNum = m_createInfo.arrayLayers * m_createInfo.mipLevels;
|
|
m_layouts.resize(subresourceNum);
|
|
m_ownerQueueFamilyIndices.resize(subresourceNum);
|
|
for (size_t i = 0; i < m_layouts.size(); i++)
|
|
{
|
|
m_layouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
m_ownerQueueFamilyIndices[i] = VK_QUEUE_FAMILY_IGNORED;
|
|
}
|
|
|
|
VmaAllocationCreateInfo imageAllocCreateInfo = {};
|
|
imageAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
|
imageAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
|
|
imageAllocCreateInfo.pUserData = (void*)m_name.c_str();
|
|
VmaAllocationInfo gpuImageAllocInfo = {};
|
|
|
|
RHICheck(vmaCreateImage(m_context->getVMA(), &m_createInfo, &imageAllocCreateInfo, &m_image, &m_allocation, &gpuImageAllocInfo));
|
|
|
|
m_context->setResourceName(VK_OBJECT_TYPE_IMAGE, (uint64_t)m_image, m_name.c_str());
|
|
}
|
|
|
|
VulkanImage::~VulkanImage()
|
|
{
|
|
if (m_image != VK_NULL_HANDLE)
|
|
{
|
|
vmaDestroyImage(m_context->getVMA(), m_image, m_allocation);
|
|
m_image = VK_NULL_HANDLE;
|
|
}
|
|
|
|
for (auto& pair : m_cacheImageViews)
|
|
{
|
|
vkDestroyImageView(m_context->getDevice(), pair.second, nullptr);
|
|
}
|
|
m_cacheImageViews.clear();
|
|
}
|
|
|
|
size_t VulkanImage::getSubresourceIndex(uint32_t layerIndex, uint32_t mipLevel) const
|
|
{
|
|
CHECK((layerIndex < m_createInfo.arrayLayers) && (mipLevel < m_createInfo.mipLevels));
|
|
return layerIndex * m_createInfo.mipLevels + mipLevel;
|
|
}
|
|
|
|
VkImageLayout VulkanImage::getCurrentLayout(uint32_t layerIndex, uint32_t mipLevel) const
|
|
{
|
|
size_t subresourceIndex = getSubresourceIndex(layerIndex, mipLevel);
|
|
return m_layouts.at(subresourceIndex);
|
|
}
|
|
|
|
VkImageView VulkanImage::getOrCreateView(VkImageSubresourceRange range, VkImageViewType viewType)
|
|
{
|
|
VkImageViewCreateInfo info = {};
|
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
info.image = m_image;
|
|
info.subresourceRange = range;
|
|
info.format = m_createInfo.format;
|
|
info.viewType = viewType;
|
|
uint64_t hashVal = CityHash64((const char*)&info, sizeof(VkImageViewCreateInfo));
|
|
if (!m_cacheImageViews.contains(hashVal))
|
|
{
|
|
m_cacheImageViews[hashVal] = VK_NULL_HANDLE;
|
|
|
|
RHICheck(vkCreateImageView(m_context->getDevice(), &info, NULL, &m_cacheImageViews[hashVal]));
|
|
}
|
|
|
|
return m_cacheImageViews[hashVal];
|
|
}
|
|
|
|
void VulkanImage::transitionLayout(RHICommandBufferBase& cmd, VkImageLayout newLayout, VkImageSubresourceRange range)
|
|
{
|
|
transitionLayout(cmd.cmd, cmd.queueFamily, newLayout, range);
|
|
}
|
|
|
|
void VulkanImage::transitionLayout(VkCommandBuffer cb, uint32_t newQueueFamily, VkImageLayout newLayout, VkImageSubresourceRange range)
|
|
{
|
|
std::vector<VkImageMemoryBarrier> barriers;
|
|
|
|
VkDependencyFlags dependencyFlags{};
|
|
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
|
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
|
|
|
uint32_t maxLayer = glm::min(range.baseArrayLayer + range.layerCount, m_createInfo.arrayLayers);
|
|
for (uint32_t layerIndex = range.baseArrayLayer; layerIndex < maxLayer; layerIndex++)
|
|
{
|
|
uint32_t maxMip = glm::min(range.baseMipLevel + range.levelCount, m_createInfo.mipLevels);
|
|
for (uint32_t mipIndex = range.baseMipLevel; mipIndex < maxMip; mipIndex++)
|
|
{
|
|
size_t flatId = getSubresourceIndex(layerIndex, mipIndex);
|
|
|
|
VkImageLayout oldLayout = m_layouts.at(flatId);
|
|
uint32_t oldFamily = m_ownerQueueFamilyIndices.at(flatId);
|
|
|
|
if ((newLayout == oldLayout) && (oldFamily == newQueueFamily))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
|
|
m_layouts[flatId] = newLayout;
|
|
m_ownerQueueFamilyIndices[flatId] = newQueueFamily;
|
|
|
|
VkImageMemoryBarrier barrier{};
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.oldLayout = oldLayout;
|
|
barrier.newLayout = newLayout;
|
|
barrier.srcQueueFamilyIndex = (oldFamily == VK_QUEUE_FAMILY_IGNORED) ? newQueueFamily : oldFamily;
|
|
barrier.dstQueueFamilyIndex = newQueueFamily;
|
|
barrier.image = m_image;
|
|
|
|
VkImageSubresourceRange rangSpecial{
|
|
.aspectMask = range.aspectMask,
|
|
.baseMipLevel = mipIndex,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = layerIndex,
|
|
.layerCount = 1,
|
|
};
|
|
|
|
barrier.subresourceRange = rangSpecial;
|
|
|
|
VkAccessFlags srcMask{};
|
|
VkAccessFlags dstMask{};
|
|
|
|
switch (oldLayout)
|
|
{
|
|
case VK_IMAGE_LAYOUT_UNDEFINED:
|
|
srcMask = 0;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_PREINITIALIZED:
|
|
srcMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
|
srcMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
|
srcMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
|
srcMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
|
srcMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
|
srcMask = VK_ACCESS_SHADER_READ_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_GENERAL:
|
|
srcMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
break;
|
|
default:
|
|
LOG_RHI_FATAL("Image layout transition no support.");
|
|
srcMask = ~0;
|
|
break;
|
|
}
|
|
|
|
switch (newLayout)
|
|
{
|
|
case VK_IMAGE_LAYOUT_GENERAL:
|
|
dstMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
|
dstMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
|
dstMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
|
dstMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
|
dstMask = dstMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
break;
|
|
|
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
|
if (srcMask == 0)
|
|
{
|
|
srcMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
}
|
|
dstMask = VK_ACCESS_SHADER_READ_BIT;
|
|
break;
|
|
default:
|
|
LOG_RHI_FATAL("Image layout transition no support.");
|
|
dstMask = ~0;
|
|
break;
|
|
}
|
|
|
|
barrier.srcAccessMask = srcMask;
|
|
barrier.dstAccessMask = dstMask;
|
|
barriers.push_back(barrier);
|
|
}
|
|
}
|
|
|
|
if (barriers.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
vkCmdPipelineBarrier(
|
|
cb,
|
|
srcStageMask,
|
|
dstStageMask,
|
|
dependencyFlags,
|
|
0,
|
|
nullptr,
|
|
0,
|
|
nullptr,
|
|
(uint32_t)barriers.size(),
|
|
barriers.data()
|
|
);
|
|
}
|
|
|
|
void VulkanImage::transitionLayout(VkCommandBuffer cmd, VkImageLayout newLayout, VkImageSubresourceRange range)
|
|
{
|
|
transitionLayout(cmd, m_context->getGraphiscFamily(), newLayout, range);
|
|
}
|
|
|
|
void VulkanImage::transitionLayoutImmediately(VkImageLayout newLayout, VkImageSubresourceRange range)
|
|
{
|
|
m_context->executeImmediatelyMajorGraphics([&, this](VkCommandBuffer cb)
|
|
{
|
|
transitionLayout(cb, m_context->getGraphiscFamily(), newLayout, range);
|
|
});
|
|
}
|
|
void pushGpuResourceAsPendingKill(std::shared_ptr<GpuResource> s)
|
|
{
|
|
getContext()->pushGpuResourceAsPendingKill(s);
|
|
}
|
|
} |