blob: 4ea98b334f6681ad4c228e35f298e5de92397beb [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::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();
}
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::TextureView> VulkanContext::makeTextureView(
rcp<vkutil::Texture> texture)
{
const VkImageCreateInfo& texInfo = texture->info();
return makeTextureView(
texture,
{
.image = *texture,
.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::TextureView> VulkanContext::makeTextureView(
rcp<vkutil::Texture> texture,
const VkImageViewCreateInfo& info)
{
assert(texture);
return rcp(
new vkutil::TextureView(ref_rcp(this), std::move(texture), info));
}
rcp<vkutil::TextureView> VulkanContext::makeExternalTextureView(
const VkImageViewCreateInfo& info)
{
return rcp<vkutil::TextureView>(
new vkutil::TextureView(ref_rcp(this), nullptr, 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