rendervulkan: Implement NV12 compositing

Dependent on latest Mesa for my commit to support unnormalized ycbcr:

855cb78d46
This commit is contained in:
Joshua Ashton 2021-05-15 12:03:11 +01:00 committed by Pierre-Loup A. Griffais
parent 39fff770f4
commit 00622fff62
3 changed files with 161 additions and 40 deletions

View file

@ -11,6 +11,7 @@ const int MaxLayers = 4;
layout(constant_id = 0) const int c_layerCount = 1; layout(constant_id = 0) const int c_layerCount = 1;
layout(constant_id = 1) const bool c_swapChannels = false; layout(constant_id = 1) const bool c_swapChannels = false;
layout(constant_id = 2) const uint c_ycbcrMask = 0;
layout(binding = 0, rgba8) writeonly uniform image2D dst; layout(binding = 0, rgba8) writeonly uniform image2D dst;
@ -23,8 +24,14 @@ uniform layers_t {
layout(binding = 2) uniform sampler2D s_samplers[MaxLayers]; layout(binding = 2) uniform sampler2D s_samplers[MaxLayers];
layout(binding = 6) uniform sampler2D s_ycbcr_samplers[MaxLayers];
vec4 sampleLayer(uint layerIdx, vec2 uv) { vec4 sampleLayer(uint layerIdx, vec2 uv) {
vec2 coord = (uv + u_offset[layerIdx]) * u_scale[layerIdx]; vec2 coord = (uv + u_offset[layerIdx]) * u_scale[layerIdx];
if ((c_ycbcrMask & (1 << layerIdx)) != 0)
return texture(s_ycbcr_samplers[layerIdx], coord);
else
return texture(s_samplers[layerIdx], coord); return texture(s_samplers[layerIdx], coord);
} }

View file

@ -80,7 +80,7 @@ VkDescriptorSetLayout descriptorSetLayout;
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet; VkDescriptorSet descriptorSet;
std::array<std::array<VkPipeline, 2>, k_nMaxLayers> pipelines; std::array<std::array<std::array<VkPipeline, k_nMaxYcbcrMask>, 2>, k_nMaxLayers> pipelines;
VkBuffer uploadBuffer; VkBuffer uploadBuffer;
VkDeviceMemory uploadBufferMemory; VkDeviceMemory uploadBufferMemory;
@ -429,6 +429,8 @@ bool CVulkanTexture::BInit( uint32_t width, uint32_t height, VkFormat format, cr
imageInfo.pNext = &externalImageCreateInfo; imageInfo.pNext = &externalImageCreateInfo;
} }
m_format = imageInfo.format;
if (vkCreateImage(device, &imageInfo, nullptr, &m_vkImage) != VK_SUCCESS) { if (vkCreateImage(device, &imageInfo, nullptr, &m_vkImage) != VK_SUCCESS) {
fprintf( stderr, "vkCreateImage failed\n" ); fprintf( stderr, "vkCreateImage failed\n" );
return false; return false;
@ -986,7 +988,7 @@ retry:
}, },
{ {
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
k_nMaxSets * k_nMaxLayers, 2 * k_nMaxSets * k_nMaxLayers,
}, },
}; };
@ -1006,6 +1008,56 @@ retry:
return false; return false;
} }
VkFormatProperties nv12Properties;
vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, &nv12Properties);
bool cosited = nv12Properties.optimalTilingFeatures & VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT;
VkSamplerYcbcrConversionCreateInfo ycbcrSamplerConversionCreateInfo =
{
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = nullptr,
.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709,
.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW,
.xChromaOffset = cosited ? VK_CHROMA_LOCATION_COSITED_EVEN : VK_CHROMA_LOCATION_MIDPOINT,
.yChromaOffset = cosited ? VK_CHROMA_LOCATION_COSITED_EVEN : VK_CHROMA_LOCATION_MIDPOINT,
.chromaFilter = VK_FILTER_LINEAR,
.forceExplicitReconstruction = VK_FALSE,
};
VkSamplerYcbcrConversion ycbcrConversion;
vkCreateSamplerYcbcrConversion( device, &ycbcrSamplerConversionCreateInfo, nullptr, &ycbcrConversion );
VkSampler ycbcrSampler = VK_NULL_HANDLE;
VkSamplerYcbcrConversionInfo ycbcrSamplerConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = ycbcrConversion,
};
VkSamplerCreateInfo ycbcrSamplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &ycbcrSamplerConversionInfo,
.magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR,
// Using clamp to edge here for now, otherwise we end up with a
// 1/2 pixel of green on the left and top of the screen due to
// the texel replacement happening before the YCbCr conversion.
// TODO: Find out why we are even hitting the border in the first place,
// maybe there is some weird half-texel stuff somewhere in Gamescope causing this,
// I imagine if this was more obvious it would affect games too.
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_TRUE,
};
vkCreateSampler( device, &ycbcrSamplerInfo, nullptr, &ycbcrSampler );
std::array<VkSampler, k_nMaxLayers> ycbcrSamplers = {ycbcrSampler, ycbcrSampler, ycbcrSampler, ycbcrSampler};
std::vector< VkDescriptorSetLayoutBinding > vecLayoutBindings; std::vector< VkDescriptorSetLayoutBinding > vecLayoutBindings;
VkDescriptorSetLayoutBinding descriptorSetLayoutBindings = VkDescriptorSetLayoutBinding descriptorSetLayoutBindings =
{ {
@ -1029,6 +1081,13 @@ retry:
vecLayoutBindings.push_back( descriptorSetLayoutBindings ); vecLayoutBindings.push_back( descriptorSetLayoutBindings );
descriptorSetLayoutBindings.binding = 6;
descriptorSetLayoutBindings.descriptorCount = k_nMaxLayers;
descriptorSetLayoutBindings.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorSetLayoutBindings.pImmutableSamplers = ycbcrSamplers.data();
vecLayoutBindings.push_back( descriptorSetLayoutBindings );
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo =
{ {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
@ -1062,7 +1121,7 @@ retry:
return false; return false;
} }
const std::array<VkSpecializationMapEntry, 2> specializationEntries = {{ const std::array<VkSpecializationMapEntry, 3> specializationEntries = {{
{ {
.constantID = 0, .constantID = 0,
.offset = 0, .offset = 0,
@ -1073,16 +1132,24 @@ retry:
.offset = sizeof(uint32_t), .offset = sizeof(uint32_t),
.size = sizeof(VkBool32) .size = sizeof(VkBool32)
}, },
{
.constantID = 2,
.offset = sizeof(uint32_t) + sizeof(uint32_t),
.size = sizeof(uint32_t)
},
}}; }};
for (uint32_t layerCount = 0; layerCount < k_nMaxLayers; layerCount++) { for (uint32_t layerCount = 0; layerCount < k_nMaxLayers; layerCount++) {
for (VkBool32 swapChannels = 0; swapChannels < 2; swapChannels++) { for (VkBool32 swapChannels = 0; swapChannels < 2; swapChannels++) {
for (uint32_t ycbcrMask = 0; ycbcrMask < k_nMaxYcbcrMask; ycbcrMask++) {
struct { struct {
uint32_t layerCount; uint32_t layerCount;
VkBool32 swapChannels; VkBool32 swapChannels;
uint32_t ycbcrMask;
} specializationData = { } specializationData = {
.layerCount = layerCount + 1, .layerCount = layerCount + 1,
.swapChannels = swapChannels .swapChannels = swapChannels,
.ycbcrMask = ycbcrMask,
}; };
VkSpecializationInfo specializationInfo = { VkSpecializationInfo specializationInfo = {
@ -1110,11 +1177,12 @@ retry:
0 0
}; };
res = vkCreateComputePipelines(device, 0, 1, &computePipelineCreateInfo, 0, &pipelines[layerCount][swapChannels]); res = vkCreateComputePipelines(device, 0, 1, &computePipelineCreateInfo, 0, &pipelines[layerCount][swapChannels][ycbcrMask]);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
return false; return false;
} }
} }
}
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
@ -1793,7 +1861,7 @@ VkSampler vulkan_make_sampler( struct VulkanPipeline_t::LayerBinding_t *pBinding
return ret; return ret;
} }
void vulkan_update_descriptor( struct VulkanPipeline_t *pPipeline ) void vulkan_update_descriptor( struct VulkanPipeline_t *pPipeline, int nYCBCRMask )
{ {
{ {
VkImageView targetImageView; VkImageView targetImageView;
@ -1877,7 +1945,9 @@ void vulkan_update_descriptor( struct VulkanPipeline_t *pPipeline )
} }
} }
VkWriteDescriptorSet writeDescriptorSet = { std::array< VkWriteDescriptorSet, 2 > writeDescriptorSets;
writeDescriptorSets[0] = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr, .pNext = nullptr,
.dstSet = descriptorSet, .dstSet = descriptorSet,
@ -1890,7 +1960,36 @@ void vulkan_update_descriptor( struct VulkanPipeline_t *pPipeline )
.pTexelBufferView = nullptr, .pTexelBufferView = nullptr,
}; };
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); if ( nYCBCRMask != 0 )
{
// Duplicate image descriptors for ycbcr.
std::array< VkDescriptorImageInfo, k_nMaxLayers > ycbcrImageDescriptors;
for (uint32_t i = 0; i < k_nMaxLayers; i++)
{
ycbcrImageDescriptors[i] = imageDescriptors[i];
// We use immutable samplers.
ycbcrImageDescriptors[i].sampler = VK_NULL_HANDLE;
}
writeDescriptorSets[1] = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = descriptorSet,
.dstBinding = 6,
.dstArrayElement = 0,
.descriptorCount = ycbcrImageDescriptors.size(),
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = ycbcrImageDescriptors.data(),
.pBufferInfo = nullptr,
.pTexelBufferView = nullptr,
};
vkUpdateDescriptorSets(device, 2, writeDescriptorSets.data(), 0, nullptr);
}
else
{
vkUpdateDescriptorSets(device, 1, writeDescriptorSets.data(), 0, nullptr);
}
} }
bool vulkan_composite( struct Composite_t *pComposite, struct VulkanPipeline_t *pPipeline, bool bScreenshot ) bool vulkan_composite( struct Composite_t *pComposite, struct VulkanPipeline_t *pPipeline, bool bScreenshot )
@ -1911,6 +2010,17 @@ bool vulkan_composite( struct Composite_t *pComposite, struct VulkanPipeline_t *
} }
} }
pComposite->nYCBCRMask = 0;
for (uint32_t i = 0; i < k_nMaxLayers; i++)
{
if ( pPipeline->layerBindings[ i ].tex != 0 )
{
CVulkanTexture *pTex = g_mapVulkanTextures[ pPipeline->layerBindings[ i ].tex ];
if (pTex->m_format == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM)
pComposite->nYCBCRMask |= 1 << i;
}
}
// Sample a bit closer to texel centers in most cases // Sample a bit closer to texel centers in most cases
// TODO: probably actually need to apply a general scale/bias to properly // TODO: probably actually need to apply a general scale/bias to properly
// sample from the center in all four corners in all scaling scenarios // sample from the center in all four corners in all scaling scenarios
@ -1925,7 +2035,7 @@ bool vulkan_composite( struct Composite_t *pComposite, struct VulkanPipeline_t *
assert ( g_output.fence == VK_NULL_HANDLE ); assert ( g_output.fence == VK_NULL_HANDLE );
vulkan_update_descriptor( pPipeline ); vulkan_update_descriptor( pPipeline, pComposite->nYCBCRMask );
VkCommandBuffer curCommandBuffer = g_output.commandBuffers[ g_output.nCurCmdBuffer ]; VkCommandBuffer curCommandBuffer = g_output.commandBuffers[ g_output.nCurCmdBuffer ];
@ -1950,7 +2060,7 @@ bool vulkan_composite( struct Composite_t *pComposite, struct VulkanPipeline_t *
return false; return false;
} }
vkCmdBindPipeline(curCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines[pComposite->nLayerCount - 1][pComposite->nSwapChannels]); vkCmdBindPipeline(curCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelines[pComposite->nLayerCount - 1][pComposite->nSwapChannels][pComposite->nYCBCRMask]);
vkCmdBindDescriptorSets(curCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, vkCmdBindDescriptorSets(curCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE,
pipelineLayout, 0, 1, &descriptorSet, 0, 0); pipelineLayout, 0, 1, &descriptorSet, 0, 0);

View file

@ -7,6 +7,7 @@
typedef uint32_t VulkanTexture_t; typedef uint32_t VulkanTexture_t;
#define k_nMaxLayers 4 #define k_nMaxLayers 4
#define k_nMaxYcbcrMask 16
// These two structs are horrible // These two structs are horrible
struct VulkanPipeline_t struct VulkanPipeline_t
@ -36,6 +37,7 @@ struct Composite_t
{ {
int nLayerCount; int nLayerCount;
int nSwapChannels; int nSwapChannels;
int nYCBCRMask;
struct CompositeData_t struct CompositeData_t
{ {
@ -118,6 +120,8 @@ public:
VulkanTexture_t handle = 0; VulkanTexture_t handle = 0;
void *m_pMappedData = nullptr; void *m_pMappedData = nullptr;
VkFormat m_format = VK_FORMAT_UNDEFINED;
}; };
extern std::vector< const char * > g_vecSDLInstanceExts; extern std::vector< const char * > g_vecSDLInstanceExts;