JBR-8471 Vulkan: Reuse descriptor sets in blit routines

This commit is contained in:
Nikita Gubarkov
2025-03-25 10:07:14 +01:00
committed by jbrbot
parent 8e289426ed
commit fc007288b5
8 changed files with 176 additions and 62 deletions

View File

@@ -1,10 +1,11 @@
#version 450
layout(binding = 0) uniform sampler2D u_TexSampler;
layout(set = 0, binding = 0) uniform texture2D u_Texture;
layout(set = 1, binding = 0) uniform sampler u_Sampler;
layout(location = 0) in vec2 in_TexCoord;
layout(location = 0) out vec4 out_Color;
void main() {
// TODO consider in/out alpha type.
out_Color = texture(u_TexSampler, in_TexCoord);
out_Color = texture(sampler2D(u_Texture, u_Sampler), in_TexCoord);
}

View File

@@ -122,6 +122,7 @@ DEVICE_FUNCTION_TABLE_ENTRY(vkUpdateDescriptorSets);
DEVICE_FUNCTION_TABLE_ENTRY(vkCreateDescriptorPool);
DEVICE_FUNCTION_TABLE_ENTRY(vkDestroyDescriptorPool);
DEVICE_FUNCTION_TABLE_ENTRY(vkAllocateDescriptorSets);
DEVICE_FUNCTION_TABLE_ENTRY(vkFreeDescriptorSets);
DEVICE_FUNCTION_TABLE_ENTRY(vkCmdBindDescriptorSets);
DEVICE_FUNCTION_TABLE_ENTRY(vkGetImageMemoryRequirements2);
DEVICE_FUNCTION_TABLE_ENTRY(vkCreateBuffer);

View File

@@ -144,8 +144,11 @@ void VKImage_Destroy(VKDevice* device, VKImage* image) {
if (image == NULL) return;
if (image->viewMap != NULL) {
for (const VKImageViewKey* k = NULL; (k = MAP_NEXT_KEY(image->viewMap, k)) != NULL;) {
const VkImageView* view = MAP_FIND(image->viewMap, *k);
device->vkDestroyImageView(device->handle, *view, NULL);
const VKImageViewInfo* viewInfo = MAP_FIND(image->viewMap, *k);
if (viewInfo->descriptorSet != VK_NULL_HANDLE) {
device->vkFreeDescriptorSets(device->handle, viewInfo->descriptorPool, 1, &viewInfo->descriptorSet);
}
device->vkDestroyImageView(device->handle, viewInfo->view, NULL);
}
MAP_FREE(image->viewMap);
}
@@ -154,13 +157,38 @@ void VKImage_Destroy(VKDevice* device, VKImage* image) {
free(image);
}
VkImageView VKImage_GetView(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
static VKImageViewInfo* VKImage_GetViewInfo(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
VKImageViewKey key = { format, swizzle };
const VkImageView* view = MAP_FIND(image->viewMap, key);
if (view == NULL) {
VkImageView* newView = &MAP_AT(image->viewMap, key);
*newView = VKImage_CreateView(device, image->handle, format, VK_UNPACK_SWIZZLE(swizzle));
view = newView;
VKImageViewInfo* viewInfo = MAP_FIND(image->viewMap, key);
if (viewInfo == NULL || viewInfo->view == VK_NULL_HANDLE) {
if (viewInfo == NULL) viewInfo = &MAP_AT(image->viewMap, key);
viewInfo->view = VKImage_CreateView(device, image->handle, format, VK_UNPACK_SWIZZLE(swizzle));
}
return *view;
return viewInfo;
}
VkImageView VKImage_GetView(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
return VKImage_GetViewInfo(device, image, format, swizzle)->view;
}
VkDescriptorSet VKImage_GetDescriptorSet(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle) {
VKImageViewInfo* info = VKImage_GetViewInfo(device, image, format, swizzle);
if (info->descriptorSet == VK_NULL_HANDLE) {
VKRenderer_CreateImageDescriptorSet(device->renderer, &info->descriptorPool, &info->descriptorSet);
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = info->view
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = info->descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.pImageInfo = &imageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
}
return info->descriptorSet;
}

View File

@@ -35,12 +35,19 @@ typedef struct {
VKPackedSwizzle swizzle;
} VKImageViewKey;
typedef struct {
VkImageView view;
VkDescriptorSet descriptorSet;
VkDescriptorPool descriptorPool; // Non-owning.
} VKImageViewInfo;
struct VKImage {
VkImage handle;
VKMemory memory;
MAP(VKImageViewKey, VkImageView) viewMap;
VkFormat format;
VkExtent2D extent;
VkImage handle;
VKMemory memory;
VkFormat format;
VkExtent2D extent;
MAP(VKImageViewKey, VKImageViewInfo) viewMap;
VkImageLayout layout;
VkPipelineStageFlagBits lastStage;
@@ -59,4 +66,6 @@ void VKImage_Destroy(VKDevice* device, VKImage* image);
VkImageView VKImage_GetView(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle);
VkDescriptorSet VKImage_GetDescriptorSet(VKDevice* device, VKImage* image, VkFormat format, VKPackedSwizzle swizzle);
#endif // VKImage_h_Included

View File

@@ -395,7 +395,7 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
VkDescriptorSetLayoutBinding textureLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
@@ -408,8 +408,27 @@ static VkResult VKPipelines_InitPipelineLayouts(VKDevice* device, VKPipelineCont
result = device->vkCreateDescriptorSetLayout(device->handle, &textureDescriptorSetLayoutCreateInfo, NULL, &pipelines->textureDescriptorSetLayout);
VK_IF_ERROR(result) return result;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &pipelines->textureDescriptorSetLayout;
VkDescriptorSetLayoutBinding samplerLayoutBinding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.pImmutableSamplers = NULL
};
VkDescriptorSetLayoutCreateInfo samplerDescriptorSetLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &samplerLayoutBinding
};
result = device->vkCreateDescriptorSetLayout(device->handle, &samplerDescriptorSetLayoutCreateInfo, NULL, &pipelines->samplerDescriptorSetLayout);
VK_IF_ERROR(result) return result;
VkDescriptorSetLayout textureDescriptorSetLayouts[] = {
pipelines->textureDescriptorSetLayout,
pipelines->samplerDescriptorSetLayout
};
createInfo.setLayoutCount = 2;
createInfo.pSetLayouts = textureDescriptorSetLayouts;
result = device->vkCreatePipelineLayout(device->handle, &createInfo, NULL, &pipelines->texturePipelineLayout);
VK_IF_ERROR(result) return result;
@@ -453,6 +472,22 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
return NULL;
}
// Create descriptor pool.
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1
};
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = 1
};
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &poolInfo, NULL, &pipelineContext->samplerDescriptorPool)) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
// Create sampler.
VkSamplerCreateInfo samplerCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
@@ -467,6 +502,32 @@ VKPipelineContext* VKPipelines_CreateContext(VKDevice* device) {
return NULL;
}
// Create sampler descriptor set.
VkDescriptorSetAllocateInfo samplerDescriptorSetAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = pipelineContext->samplerDescriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &pipelineContext->samplerDescriptorSetLayout
};
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &samplerDescriptorSetAllocateInfo,
&pipelineContext->linearRepeatSamplerDescriptorSet)) {
VKPipelines_DestroyContext(pipelineContext);
return NULL;
}
VkDescriptorImageInfo samplerImageInfo = {
.sampler = pipelineContext->linearRepeatSampler
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = pipelineContext->linearRepeatSamplerDescriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &samplerImageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
J2dRlsTraceLn(J2D_TRACE_INFO, "VKPipelines_CreateContext(%p)", pipelineContext);
return pipelineContext;
}
@@ -482,11 +543,13 @@ void VKPipelines_DestroyContext(VKPipelineContext* pipelineContext) {
ARRAY_FREE(pipelineContext->renderPassContexts);
VKPipelines_DestroyShaders(device, pipelineContext->shaders);
device->vkDestroyDescriptorPool(device->handle, pipelineContext->samplerDescriptorPool, NULL);
device->vkDestroySampler(device->handle, pipelineContext->linearRepeatSampler, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->colorPipelineLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->texturePipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->textureDescriptorSetLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->samplerDescriptorSetLayout, NULL);
device->vkDestroyPipelineLayout(device->handle, pipelineContext->maskFillPipelineLayout, NULL);
device->vkDestroyDescriptorSetLayout(device->handle, pipelineContext->maskFillDescriptorSetLayout, NULL);

View File

@@ -72,10 +72,13 @@ struct VKPipelineContext {
VKDevice* device;
VkPipelineLayout colorPipelineLayout;
VkDescriptorSetLayout textureDescriptorSetLayout;
VkDescriptorSetLayout samplerDescriptorSetLayout;
VkPipelineLayout texturePipelineLayout;
VkDescriptorSetLayout maskFillDescriptorSetLayout;
VkPipelineLayout maskFillPipelineLayout;
VkDescriptorPool samplerDescriptorPool;
VkDescriptorSet linearRepeatSamplerDescriptorSet;
VkSampler linearRepeatSampler;
struct VKShaders* shaders;

View File

@@ -99,6 +99,7 @@ struct VKRenderer {
POOL(VkFramebuffer, framebufferDestructionQueue);
ARRAY(VKMemory) bufferMemoryPages;
ARRAY(VkDescriptorPool) descriptorPools;
ARRAY(VkDescriptorPool) imageDescriptorPools;
/**
* Last known timestamp reached by GPU execution. Resources with equal or less timestamp may be safely reused.
@@ -253,6 +254,48 @@ static VKTexelBuffer VKRenderer_GetMaskFillBuffer(VKRenderer* renderer) {
return texelBuffers[0];
}
#define IMAGE_DESCRIPTOR_POOL_SIZE 64
static VkDescriptorSet VKRenderer_AllocateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool descriptorPool) {
VKDevice* device = renderer->device;
VkDescriptorSetAllocateInfo allocateInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &renderer->pipelineContext->textureDescriptorSetLayout
};
VkDescriptorSet set;
VkResult result = device->vkAllocateDescriptorSets(device->handle, &allocateInfo, &set);
if (result == VK_SUCCESS) return set;
if (result != VK_ERROR_OUT_OF_POOL_MEMORY && result != VK_ERROR_FRAGMENTED_POOL) {
VK_IF_ERROR(result) VK_UNHANDLED_ERROR();
}
return VK_NULL_HANDLE;
}
void VKRenderer_CreateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool* descriptorPool, VkDescriptorSet* set) {
VKDevice* device = renderer->device;
for (int i = ARRAY_SIZE(renderer->imageDescriptorPools) - 1; i >= 0; i--) {
*set = VKRenderer_AllocateImageDescriptorSet(renderer, renderer->imageDescriptorPools[i]);
if (*set != VK_NULL_HANDLE) {
*descriptorPool = renderer->imageDescriptorPools[i];
return;
}
}
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = IMAGE_DESCRIPTOR_POOL_SIZE
};
VkDescriptorPoolCreateInfo poolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = IMAGE_DESCRIPTOR_POOL_SIZE
};
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &poolInfo, NULL, descriptorPool)) VK_UNHANDLED_ERROR();
ARRAY_PUSH_BACK(renderer->imageDescriptorPools) = *descriptorPool;
*set = VKRenderer_AllocateImageDescriptorSet(renderer, *descriptorPool);
}
static VkSemaphore VKRenderer_AddPendingSemaphore(VKRenderer* renderer) {
VKDevice* device = renderer->device;
VkSemaphore semaphore = VK_NULL_HANDLE;
@@ -374,6 +417,10 @@ void VKRenderer_Destroy(VKRenderer* renderer) {
device->vkDestroyDescriptorPool(device->handle, renderer->descriptorPools[i], NULL);
}
ARRAY_FREE(renderer->descriptorPools);
for (uint32_t i = 0; i < ARRAY_SIZE(renderer->imageDescriptorPools); i++) {
device->vkDestroyDescriptorPool(device->handle, renderer->imageDescriptorPools[i], NULL);
}
ARRAY_FREE(renderer->imageDescriptorPools);
device->vkDestroySemaphore(device->handle, renderer->timelineSemaphore, NULL);
device->vkDestroyCommandPool(device->handle, renderer->commandPool, NULL);
@@ -1272,48 +1319,6 @@ void VKRenderer_TextureRender(VKImage *destImage, VKImage *srcImage,
VkCommandBuffer cb = renderPass->commandBuffer;
VKDevice* device = surface->device;
// TODO We create a new descriptor set on each command, we'll implement reusing them later.
VkDescriptorPoolSize poolSize = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1
};
VkDescriptorPoolCreateInfo descrPoolInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = 1,
.pPoolSizes = &poolSize,
.maxSets = 1
};
VkDescriptorPool descriptorPool;
VK_IF_ERROR(device->vkCreateDescriptorPool(device->handle, &descrPoolInfo, NULL, &descriptorPool)) VK_UNHANDLED_ERROR();
VkDescriptorSetAllocateInfo descrAllocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &device->renderer->pipelineContext->textureDescriptorSetLayout
};
VkDescriptorSet descriptorSet;
VK_IF_ERROR(device->vkAllocateDescriptorSets(device->handle, &descrAllocInfo, &descriptorSet)) VK_UNHANDLED_ERROR();
VkDescriptorImageInfo imageInfo = {
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = VKImage_GetView(device, srcImage, srcImage->format, 0),
.sampler = device->renderer->pipelineContext->linearRepeatSampler
};
VkWriteDescriptorSet descriptorWrites = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &imageInfo
};
device->vkUpdateDescriptorSets(device->handle, 1, &descriptorWrites, 0, NULL);
// TODO We flush all pending draws and rebind the vertex buffer with the provided one.
// We will make it work with our unified vertex buffer later.
VKRenderer_FlushDraw(surface);
@@ -1321,8 +1326,10 @@ void VKRenderer_TextureRender(VKImage *destImage, VKImage *srcImage,
VkBuffer vertexBuffers[] = {vertexBuffer};
VkDeviceSize offsets[] = {0};
device->vkCmdBindVertexBuffers(cb, 0, 1, vertexBuffers, offsets);
VkDescriptorSet descriptorSets[] = { VKImage_GetDescriptorSet(device, srcImage, srcImage->format, 0),
device->renderer->pipelineContext->linearRepeatSamplerDescriptorSet };
device->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS,
device->renderer->pipelineContext->texturePipelineLayout, 0, 1, &descriptorSet, 0, NULL);
device->renderer->pipelineContext->texturePipelineLayout, 0, 2, descriptorSets, 0, NULL);
device->vkCmdDraw(cb, vertexNum, 1, 0, 0);
}

View File

@@ -78,6 +78,8 @@ VkCommandBuffer VKRenderer_Record(VKRenderer* renderer);
void VKRenderer_AddImageBarrier(VkImageMemoryBarrier* barriers, VKBarrierBatch* batch,
VKImage* image, VkPipelineStageFlags stage, VkAccessFlags access, VkImageLayout layout);
void VKRenderer_CreateImageDescriptorSet(VKRenderer* renderer, VkDescriptorPool* descriptorPool, VkDescriptorSet* set);
void VKRenderer_Destroy(VKRenderer* renderer);
/**