| /* |
| * Copyright 2023 Rive |
| */ |
| |
| #include "rive/renderer/vulkan/vulkan_context.hpp" |
| |
| #include "rive/rive_types.hpp" |
| |
| namespace rive::gpu |
| { |
| static VmaAllocator make_vma_allocator(VmaAllocatorCreateInfo vmaCreateInfo) |
| { |
| VmaAllocator vmaAllocator; |
| VK_CHECK(vmaCreateAllocator(&vmaCreateInfo, &vmaAllocator)); |
| return vmaAllocator; |
| } |
| |
| VulkanContext::VulkanContext(VkInstance instance, |
| VkPhysicalDevice physicalDevice_, |
| VkDevice device_, |
| const VulkanFeatures& features_, |
| PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr, |
| PFN_vkGetDeviceProcAddr fp_vkGetDeviceProcAddr) : |
| instance(instance), |
| physicalDevice(physicalDevice_), |
| device(device_), |
| features(features_), |
| vmaAllocator(make_vma_allocator({ |
| // We are single-threaded. |
| .flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT, |
| .physicalDevice = physicalDevice, |
| .device = device, |
| .pVulkanFunctions = &initVmaVulkanFunctions(fp_vkGetInstanceProcAddr, |
| fp_vkGetDeviceProcAddr), |
| .instance = instance, |
| .vulkanApiVersion = features.vulkanApiVersion, |
| })) |
| // clang-format off |
| #define LOAD_VULKAN_INSTANCE_COMMAND(CMD) \ |
| , CMD(reinterpret_cast<PFN_vk##CMD>(fp_vkGetInstanceProcAddr(instance, "vk" #CMD))) |
| #define LOAD_VULKAN_DEVICE_COMMAND(CMD) \ |
| , CMD(reinterpret_cast<PFN_vk##CMD>(fp_vkGetDeviceProcAddr(device, "vk" #CMD))) |
| RIVE_VULKAN_DEVICE_COMMANDS(LOAD_VULKAN_DEVICE_COMMAND) |
| #undef LOAD_VULKAN_DEVICE_COMMAND |
| #undef LOAD_VULKAN_INSTANCE_COMMAND |
| // clang-format on |
| {} |
| |
| VulkanContext::~VulkanContext() |
| { |
| assert(m_shutdown); |
| assert(m_resourcePurgatory.empty()); |
| vmaDestroyAllocator(vmaAllocator); |
| } |
| |
| void VulkanContext::onNewFrameBegun() |
| { |
| ++m_currentFrameIdx; |
| |
| // Delete all resources that are no longer referenced by an in-flight |
| // command buffer. |
| while (!m_resourcePurgatory.empty() && |
| m_resourcePurgatory.front().expirationFrameIdx <= m_currentFrameIdx) |
| { |
| m_resourcePurgatory.pop_front(); |
| } |
| } |
| |
| void VulkanContext::onRenderingResourceReleased( |
| const vkutil::RenderingResource* resource) |
| { |
| assert(resource->vulkanContext() == this); |
| if (!m_shutdown) |
| { |
| // Hold this resource until it is no longer referenced by an in-flight |
| // command buffer. |
| m_resourcePurgatory.emplace_back(resource, m_currentFrameIdx); |
| } |
| else |
| { |
| // We're in a shutdown cycle. Delete immediately. |
| delete resource; |
| } |
| } |
| |
| void VulkanContext::shutdown() |
| { |
| m_shutdown = true; |
| |
| // Validate m_resourcePurgatory: We shouldn't have any resources queued up |
| // with larger expirations than "gpu::kBufferRingSize" frames. |
| for (size_t i = 0; i < gpu::kBufferRingSize; ++i) |
| { |
| onNewFrameBegun(); |
| } |
| assert(m_resourcePurgatory.empty()); |
| |
| // Explicitly delete the resources anyway, just to be safe for release mode. |
| m_resourcePurgatory.clear(); |
| } |
| |
| rcp<vkutil::Buffer> VulkanContext::makeBuffer(const VkBufferCreateInfo& info, |
| vkutil::Mappability mappability) |
| { |
| return rcp(new vkutil::Buffer(ref_rcp(this), info, mappability)); |
| } |
| |
| rcp<vkutil::Texture> VulkanContext::makeTexture(const VkImageCreateInfo& info) |
| { |
| return rcp(new vkutil::Texture(ref_rcp(this), info)); |
| } |
| |
| rcp<vkutil::Framebuffer> VulkanContext::makeFramebuffer( |
| const VkFramebufferCreateInfo& info) |
| { |
| return rcp(new vkutil::Framebuffer(ref_rcp(this), info)); |
| } |
| |
| static VkImageViewType image_view_type_for_image_type(VkImageType type) |
| { |
| switch (type) |
| { |
| case VK_IMAGE_TYPE_2D: |
| return VK_IMAGE_VIEW_TYPE_2D; |
| default: |
| fprintf( |
| stderr, |
| "vkutil::image_view_type_for_image_type: VkImageType %u is not " |
| "supported\n", |
| type); |
| } |
| RIVE_UNREACHABLE(); |
| } |
| |
| rcp<vkutil::TextureView> VulkanContext::makeTextureView( |
| rcp<vkutil::Texture> texture) |
| { |
| return makeTextureView(texture, |
| { |
| .image = *texture, |
| .viewType = image_view_type_for_image_type( |
| texture->info().imageType), |
| .format = texture->info().format, |
| .subresourceRange = |
| { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .levelCount = texture->info().mipLevels, |
| .layerCount = 1, |
| }, |
| }); |
| } |
| |
| rcp<vkutil::TextureView> VulkanContext::makeTextureView( |
| rcp<vkutil::Texture> texture, |
| const VkImageViewCreateInfo& info) |
| { |
| assert(texture); |
| auto usage = texture->info().usage; |
| return rcp(new vkutil::TextureView(ref_rcp(this), |
| std::move(texture), |
| usage, |
| info)); |
| } |
| |
| rcp<vkutil::TextureView> VulkanContext::makeExternalTextureView( |
| const VkImageUsageFlags flags, |
| const VkImageViewCreateInfo& info) |
| { |
| return rcp<vkutil::TextureView>( |
| new vkutil::TextureView(ref_rcp(this), nullptr, flags, info)); |
| } |
| |
| void VulkanContext::updateImageDescriptorSets( |
| VkDescriptorSet vkDescriptorSet, |
| VkWriteDescriptorSet writeSet, |
| std::initializer_list<VkDescriptorImageInfo> imageInfos) |
| { |
| writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeSet.dstSet = vkDescriptorSet; |
| writeSet.descriptorCount = |
| math::lossless_numeric_cast<uint32_t>(imageInfos.size()); |
| writeSet.pImageInfo = imageInfos.begin(); |
| UpdateDescriptorSets(device, 1, &writeSet, 0, nullptr); |
| } |
| |
| void VulkanContext::updateBufferDescriptorSets( |
| VkDescriptorSet vkDescriptorSet, |
| VkWriteDescriptorSet writeSet, |
| std::initializer_list<VkDescriptorBufferInfo> bufferInfos) |
| { |
| writeSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeSet.dstSet = vkDescriptorSet; |
| writeSet.descriptorCount = |
| math::lossless_numeric_cast<uint32_t>(bufferInfos.size()); |
| writeSet.pBufferInfo = bufferInfos.begin(); |
| UpdateDescriptorSets(device, 1, &writeSet, 0, nullptr); |
| } |
| |
| void VulkanContext::memoryBarrier(VkCommandBuffer commandBuffer, |
| VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| VkDependencyFlags dependencyFlags, |
| VkMemoryBarrier memoryBarrier) |
| { |
| memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; |
| CmdPipelineBarrier(commandBuffer, |
| srcStageMask, |
| dstStageMask, |
| dependencyFlags, |
| 1, |
| &memoryBarrier, |
| 0, |
| nullptr, |
| 0, |
| nullptr); |
| } |
| |
| void VulkanContext::imageMemoryBarriers( |
| VkCommandBuffer commandBuffer, |
| VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| VkDependencyFlags dependencyFlags, |
| uint32_t count, |
| VkImageMemoryBarrier* imageMemoryBarriers) |
| { |
| for (uint32_t i = 0; i < count; ++i) |
| { |
| auto& imageMemoryBarrier = imageMemoryBarriers[i]; |
| imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; |
| if (imageMemoryBarrier.subresourceRange.aspectMask == 0) |
| { |
| imageMemoryBarrier.subresourceRange.aspectMask = |
| VK_IMAGE_ASPECT_COLOR_BIT; |
| } |
| if (imageMemoryBarrier.subresourceRange.levelCount == 0) |
| { |
| imageMemoryBarrier.subresourceRange.levelCount = 1; |
| } |
| if (imageMemoryBarrier.subresourceRange.layerCount == 0) |
| { |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| } |
| } |
| CmdPipelineBarrier(commandBuffer, |
| srcStageMask, |
| dstStageMask, |
| dependencyFlags, |
| 0, |
| nullptr, |
| 0, |
| nullptr, |
| count, |
| imageMemoryBarriers); |
| } |
| |
| void VulkanContext::bufferMemoryBarrier( |
| VkCommandBuffer commandBuffer, |
| VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, |
| VkDependencyFlags dependencyFlags, |
| VkBufferMemoryBarrier bufferMemoryBarrier) |
| { |
| bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; |
| if (bufferMemoryBarrier.size == 0) |
| { |
| bufferMemoryBarrier.size = VK_WHOLE_SIZE; |
| } |
| CmdPipelineBarrier(commandBuffer, |
| srcStageMask, |
| dstStageMask, |
| dependencyFlags, |
| 0, |
| nullptr, |
| 1, |
| &bufferMemoryBarrier, |
| 0, |
| nullptr); |
| } |
| |
| void VulkanContext::blitSubRect(VkCommandBuffer commandBuffer, |
| VkImage src, |
| VkImage dst, |
| const IAABB& blitBounds) |
| { |
| if (blitBounds.empty()) |
| { |
| return; |
| } |
| |
| VkImageBlit imageBlit = { |
| .srcSubresource = |
| { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .layerCount = 1, |
| }, |
| .dstSubresource = |
| { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .layerCount = 1, |
| }, |
| }; |
| |
| imageBlit.srcOffsets[0] = {blitBounds.left, blitBounds.top, 0}; |
| imageBlit.srcOffsets[1] = {blitBounds.right, blitBounds.bottom, 1}; |
| |
| imageBlit.dstOffsets[0] = {blitBounds.left, blitBounds.top, 0}; |
| imageBlit.dstOffsets[1] = {blitBounds.right, blitBounds.bottom, 1}; |
| |
| CmdBlitImage(commandBuffer, |
| src, |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| dst, |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
| 1, |
| &imageBlit, |
| VK_FILTER_NEAREST); |
| } |
| } // namespace rive::gpu |