blob: 4dbc1cd0dcbac88bb6d544cdd554f844079811f8 [file] [log] [blame] [edit]
/*
* Copyright 2023 Rive
*/
#include "rive/renderer/vulkan/vulkan_context.hpp"
#include "rive/rive_types.hpp"
#include <vk_mem_alloc.h>
namespace rive::gpu
{
static VmaAllocator make_vma_allocator(
const VulkanContext* vk,
PFN_vkGetInstanceProcAddr pfnvkGetInstanceProcAddr)
{
VmaAllocator vmaAllocator;
VmaVulkanFunctions vmaVulkanFunctions = {
.vkGetInstanceProcAddr = pfnvkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vk->GetDeviceProcAddr,
.vkGetPhysicalDeviceProperties = vk->GetPhysicalDeviceProperties,
};
VmaAllocatorCreateInfo vmaCreateInfo = {
// We are single-threaded.
.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT,
.physicalDevice = vk->physicalDevice,
.device = vk->device,
.pVulkanFunctions = &vmaVulkanFunctions,
.instance = vk->instance,
.vulkanApiVersion = vk->features.apiVersion,
};
VK_CHECK(vmaCreateAllocator(&vmaCreateInfo, &vmaAllocator));
return vmaAllocator;
}
VulkanContext::VulkanContext(
VkInstance instance,
VkPhysicalDevice physicalDevice_,
VkDevice device_,
const VulkanFeatures& features_,
PFN_vkGetInstanceProcAddr pfnvkGetInstanceProcAddr) :
instance(instance),
physicalDevice(physicalDevice_),
device(device_),
features(features_),
#define LOAD_VULKAN_INSTANCE_COMMAND(CMD) \
CMD(reinterpret_cast<PFN_vk##CMD>( \
pfnvkGetInstanceProcAddr(instance, "vk" #CMD))),
RIVE_VULKAN_INSTANCE_COMMANDS(LOAD_VULKAN_INSTANCE_COMMAND)
#undef LOAD_VULKAN_INSTANCE_COMMAND
#define LOAD_VULKAN_DEVICE_COMMAND(CMD) \
CMD(reinterpret_cast<PFN_vk##CMD>(GetDeviceProcAddr(device, "vk" #CMD))),
RIVE_VULKAN_DEVICE_COMMANDS(LOAD_VULKAN_DEVICE_COMMAND)
#undef LOAD_VULKAN_DEVICE_COMMAND
m_vmaAllocator(make_vma_allocator(this, pfnvkGetInstanceProcAddr))
{
// VK spec says between D24_S8 and D32_S8, one of them must be supported
m_supportsD24S8 = isFormatSupportedWithFeatureFlags(
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
// This assert should never fire unless some hardware is breaking the VK
// spec by reporting that it does not support one of these formats.
assert((m_supportsD24S8 ||
isFormatSupportedWithFeatureFlags(
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) &&
"No suitable depth format supported!");
}
VulkanContext::~VulkanContext() { vmaDestroyAllocator(m_vmaAllocator); }
bool VulkanContext::isFormatSupportedWithFeatureFlags(
VkFormat format,
VkFormatFeatureFlagBits featureFlags)
{
// Can flesch this out, but currently just checks if format's optimal tiling
// features include the provided bits.
VkFormatProperties properties;
GetPhysicalDeviceFormatProperties(physicalDevice, format, &properties);
return properties.optimalTilingFeatures & featureFlags;
}
rcp<vkutil::Buffer> VulkanContext::makeBuffer(const VkBufferCreateInfo& info,
vkutil::Mappability mappability)
{
return rcp(new vkutil::Buffer(ref_rcp(this), info, mappability));
}
rcp<vkutil::Image> VulkanContext::makeImage(const VkImageCreateInfo& info)
{
return rcp(new vkutil::Image(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();
}
static VkImageAspectFlags image_aspect_flags_for_format(VkFormat format)
{
switch (format)
{
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
default:
return VK_IMAGE_ASPECT_COLOR_BIT;
}
RIVE_UNREACHABLE();
}
rcp<vkutil::ImageView> VulkanContext::makeImageView(rcp<vkutil::Image> image)
{
const VkImageCreateInfo& texInfo = image->info();
return makeImageView(
image,
{
.image = *image,
.viewType = image_view_type_for_image_type(texInfo.imageType),
.format = texInfo.format,
.subresourceRange =
{
.aspectMask = image_aspect_flags_for_format(texInfo.format),
.levelCount = texInfo.mipLevels,
.layerCount = 1,
},
});
}
rcp<vkutil::ImageView> VulkanContext::makeImageView(
rcp<vkutil::Image> image,
const VkImageViewCreateInfo& info)
{
assert(image);
return rcp(new vkutil::ImageView(ref_rcp(this), std::move(image), info));
}
rcp<vkutil::ImageView> VulkanContext::makeExternalImageView(
const VkImageViewCreateInfo& info)
{
return rcp<vkutil::ImageView>(
new vkutil::ImageView(ref_rcp(this), nullptr, info));
}
rcp<vkutil::Texture2D> VulkanContext::makeTexture2D(
const VkImageCreateInfo& info)
{
return rcp<vkutil::Texture2D>(new vkutil::Texture2D(ref_rcp(this), 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 =
VK_REMAINING_MIP_LEVELS;
}
if (imageMemoryBarrier.subresourceRange.layerCount == 0)
{
imageMemoryBarrier.subresourceRange.layerCount =
VK_REMAINING_ARRAY_LAYERS;
}
}
CmdPipelineBarrier(commandBuffer,
srcStageMask,
dstStageMask,
dependencyFlags,
0,
nullptr,
0,
nullptr,
count,
imageMemoryBarriers);
}
const vkutil::ImageAccess& VulkanContext::simpleImageMemoryBarrier(
VkCommandBuffer commandBuffer,
const vkutil::ImageAccess& srcAccess,
const vkutil::ImageAccess& dstAccess,
VkImage image,
vkutil::ImageAccessAction imageAccessAction,
VkDependencyFlags dependencyFlags)
{
assert(image != VK_NULL_HANDLE);
if (srcAccess != dstAccess)
{
imageMemoryBarrier(
commandBuffer,
srcAccess.pipelineStages,
dstAccess.pipelineStages,
dependencyFlags,
{
.srcAccessMask = srcAccess.accessMask,
.dstAccessMask = dstAccess.accessMask,
.oldLayout = imageAccessAction ==
vkutil::ImageAccessAction::preserveContents
? srcAccess.layout
: VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = dstAccess.layout,
.image = image,
});
}
return dstAccess;
}
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 srcImage,
VkImageLayout srcImageLayout,
VkImage dstImage,
VkImageLayout dstImageLayout,
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,
srcImage,
srcImageLayout,
dstImage,
dstImageLayout,
1,
&imageBlit,
VK_FILTER_NEAREST);
}
} // namespace rive::gpu