LCOV - code coverage report
Current view: top level - src/vulkan - gpu_pass.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 370 406 91.1 %
Date: 2025-03-29 09:04:10 Functions: 12 13 92.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 123 191 64.4 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "gpu.h"
      19                 :            : #include "cache.h"
      20                 :            : #include "glsl/spirv.h"
      21                 :            : 
      22                 :            : // For pl_pass.priv
      23                 :            : struct pl_pass_vk {
      24                 :            :     // Pipeline / render pass
      25                 :            :     VkPipeline base;
      26                 :            :     VkPipeline pipe;
      27                 :            :     VkPipelineLayout pipeLayout;
      28                 :            :     VkRenderPass renderPass;
      29                 :            :     // Descriptor set (bindings)
      30                 :            :     bool use_pushd;
      31                 :            :     VkDescriptorSetLayout dsLayout;
      32                 :            :     VkDescriptorPool dsPool;
      33                 :            :     // To keep track of which descriptor sets are and aren't available, we
      34                 :            :     // allocate a fixed number and use a bitmask of all available sets.
      35                 :            :     VkDescriptorSet dss[16];
      36                 :            :     uint16_t dmask;
      37                 :            : 
      38                 :            :     // For recompilation
      39                 :            :     VkVertexInputAttributeDescription *attrs;
      40                 :            :     VkPipelineCache cache;
      41                 :            :     VkShaderModule vert;
      42                 :            :     VkShaderModule shader;
      43                 :            : 
      44                 :            :     // For updating
      45                 :            :     VkWriteDescriptorSet *dswrite;
      46                 :            :     VkDescriptorImageInfo *dsiinfo;
      47                 :            :     VkDescriptorBufferInfo *dsbinfo;
      48                 :            :     VkSpecializationInfo specInfo;
      49                 :            :     size_t spec_size;
      50                 :            : };
      51                 :            : 
      52                 :       1209 : int vk_desc_namespace(pl_gpu gpu, enum pl_desc_type type)
      53                 :            : {
      54                 :       1209 :     return 0;
      55                 :            : }
      56                 :            : 
      57                 :        160 : static void pass_destroy_cb(pl_gpu gpu, pl_pass pass)
      58                 :            : {
      59                 :        160 :     struct pl_vk *p = PL_PRIV(gpu);
      60                 :        160 :     struct vk_ctx *vk = p->vk;
      61                 :        160 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
      62                 :            : 
      63                 :        160 :     vk->DestroyPipeline(vk->dev, pass_vk->pipe, PL_VK_ALLOC);
      64                 :        160 :     vk->DestroyPipeline(vk->dev, pass_vk->base, PL_VK_ALLOC);
      65                 :        160 :     vk->DestroyRenderPass(vk->dev, pass_vk->renderPass, PL_VK_ALLOC);
      66                 :        160 :     vk->DestroyPipelineLayout(vk->dev, pass_vk->pipeLayout, PL_VK_ALLOC);
      67                 :        160 :     vk->DestroyPipelineCache(vk->dev, pass_vk->cache, PL_VK_ALLOC);
      68                 :        160 :     vk->DestroyDescriptorPool(vk->dev, pass_vk->dsPool, PL_VK_ALLOC);
      69                 :        160 :     vk->DestroyDescriptorSetLayout(vk->dev, pass_vk->dsLayout, PL_VK_ALLOC);
      70                 :        160 :     vk->DestroyShaderModule(vk->dev, pass_vk->vert, PL_VK_ALLOC);
      71                 :        160 :     vk->DestroyShaderModule(vk->dev, pass_vk->shader, PL_VK_ALLOC);
      72                 :            : 
      73                 :        160 :     pl_free((void *) pass);
      74                 :        160 : }
      75                 :            : 
      76                 :        160 : VK_CB_FUNC_DEF(pass_destroy_cb);
      77                 :            : 
      78                 :        160 : void vk_pass_destroy(pl_gpu gpu, pl_pass pass)
      79                 :            : {
      80                 :        160 :     vk_gpu_idle_callback(gpu, VK_CB_FUNC(pass_destroy_cb), gpu, pass);
      81                 :        160 : }
      82                 :            : 
      83                 :            : static const VkDescriptorType dsType[] = {
      84                 :            :     [PL_DESC_SAMPLED_TEX] = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      85                 :            :     [PL_DESC_STORAGE_IMG] = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
      86                 :            :     [PL_DESC_BUF_UNIFORM] = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
      87                 :            :     [PL_DESC_BUF_STORAGE] = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
      88                 :            :     [PL_DESC_BUF_TEXEL_UNIFORM] = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
      89                 :            :     [PL_DESC_BUF_TEXEL_STORAGE] = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
      90                 :            : };
      91                 :            : 
      92                 :        267 : static VkResult vk_compile_glsl(pl_gpu gpu, void *alloc,
      93                 :            :                                 enum glsl_shader_stage stage,
      94                 :            :                                 const char *shader,
      95                 :            :                                 pl_cache_obj *out_spirv)
      96                 :            : {
      97                 :        267 :     struct pl_vk *p = PL_PRIV(gpu);
      98                 :        267 :     pl_cache cache = pl_gpu_cache(gpu);
      99                 :            :     uint64_t key = CACHE_KEY_SPIRV;
     100         [ +  + ]:        267 :     if (cache) { // skip computing key if `cache
     101                 :          2 :         pl_hash_merge(&key, p->spirv->signature);
     102                 :          2 :         pl_hash_merge(&key, pl_str0_hash(shader));
     103                 :          2 :         out_spirv->key = key;
     104         [ +  + ]:          2 :         if (pl_cache_get(cache, out_spirv)) {
     105                 :          1 :             PL_DEBUG(gpu, "Re-using cached SPIR-V object 0x%"PRIx64, key);
     106                 :          1 :             return VK_SUCCESS;
     107                 :            :         }
     108                 :            :     }
     109                 :            : 
     110                 :            :     pl_clock_t start = pl_clock_now();
     111                 :        266 :     pl_str spirv = pl_spirv_compile_glsl(p->spirv, alloc, gpu->glsl, stage, shader);
     112                 :        266 :     pl_log_cpu_time(gpu->log, start, pl_clock_now(), "translating SPIR-V");
     113                 :        266 :     out_spirv->data = spirv.buf;
     114                 :        266 :     out_spirv->size = spirv.len;
     115                 :        266 :     out_spirv->free = pl_free;
     116         [ +  - ]:        266 :     return spirv.len ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED;
     117                 :            : }
     118                 :            : 
     119                 :            : static const VkShaderStageFlags stageFlags[] = {
     120                 :            :     [PL_PASS_RASTER]  = VK_SHADER_STAGE_FRAGMENT_BIT |
     121                 :            :                         VK_SHADER_STAGE_VERTEX_BIT,
     122                 :            :     [PL_PASS_COMPUTE] = VK_SHADER_STAGE_COMPUTE_BIT,
     123                 :            : };
     124                 :            : 
     125                 :            : static inline void destroy_pipeline(struct vk_ctx *vk, void *pipeline)
     126                 :            : {
     127                 :          0 :     vk->DestroyPipeline(vk->dev, vk_unwrap_handle(pipeline), PL_VK_ALLOC);
     128                 :            : }
     129                 :            : 
     130                 :          0 : VK_CB_FUNC_DEF(destroy_pipeline);
     131                 :            : 
     132                 :        161 : static VkResult vk_recreate_pipelines(struct vk_ctx *vk, pl_pass pass,
     133                 :            :                                       bool derivable, VkPipeline base,
     134                 :            :                                       VkPipeline *out_pipe)
     135                 :            : {
     136                 :        161 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
     137                 :            :     const struct pl_pass_params *params = &pass->params;
     138                 :            : 
     139                 :            :     // The old pipeline might still be in use, so we have to destroy it
     140                 :            :     // asynchronously with a device idle callback
     141         [ -  + ]:        161 :     if (*out_pipe) {
     142                 :            :         // We don't need to use `vk_gpu_idle_callback` because the only command
     143                 :            :         // that can access a VkPipeline, `vk_pass_run`, always flushes `p->cmd`.
     144                 :          0 :         vk_dev_callback(vk, VK_CB_FUNC(destroy_pipeline), vk, vk_wrap_handle(*out_pipe));
     145                 :          0 :         *out_pipe = VK_NULL_HANDLE;
     146                 :            :     }
     147                 :            : 
     148                 :            :     VkPipelineCreateFlags flags = 0;
     149         [ +  + ]:        161 :     if (derivable)
     150                 :            :         flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
     151         [ +  + ]:        161 :     if (base)
     152                 :          1 :         flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
     153                 :            : 
     154                 :        161 :     const VkSpecializationInfo *specInfo = &pass_vk->specInfo;
     155         [ +  + ]:        161 :     if (!specInfo->dataSize)
     156                 :            :         specInfo = NULL;
     157                 :            : 
     158      [ +  +  - ]:        161 :     switch (params->type) {
     159                 :        108 :     case PL_PASS_RASTER: {
     160                 :            :         static const VkBlendFactor blendFactors[] = {
     161                 :            :             [PL_BLEND_ZERO]                = VK_BLEND_FACTOR_ZERO,
     162                 :            :             [PL_BLEND_ONE]                 = VK_BLEND_FACTOR_ONE,
     163                 :            :             [PL_BLEND_SRC_ALPHA]           = VK_BLEND_FACTOR_SRC_ALPHA,
     164                 :            :             [PL_BLEND_ONE_MINUS_SRC_ALPHA] = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
     165                 :            :         };
     166                 :            : 
     167                 :        108 :         VkPipelineColorBlendAttachmentState blendState = {
     168                 :            :             .colorBlendOp = VK_BLEND_OP_ADD,
     169                 :            :             .alphaBlendOp = VK_BLEND_OP_ADD,
     170                 :            :             .colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
     171                 :            :                               VK_COLOR_COMPONENT_G_BIT |
     172                 :            :                               VK_COLOR_COMPONENT_B_BIT |
     173                 :            :                               VK_COLOR_COMPONENT_A_BIT,
     174                 :            :         };
     175                 :            : 
     176                 :        108 :         const struct pl_blend_params *blend = params->blend_params;
     177         [ +  + ]:        108 :         if (blend) {
     178                 :          2 :             blendState.blendEnable = true;
     179                 :          2 :             blendState.srcColorBlendFactor = blendFactors[blend->src_rgb];
     180                 :          2 :             blendState.dstColorBlendFactor = blendFactors[blend->dst_rgb];
     181                 :          2 :             blendState.srcAlphaBlendFactor = blendFactors[blend->src_alpha];
     182                 :          2 :             blendState.dstAlphaBlendFactor = blendFactors[blend->dst_alpha];
     183                 :            :         }
     184                 :            : 
     185                 :            :         static const VkPrimitiveTopology topologies[PL_PRIM_TYPE_COUNT] = {
     186                 :            :             [PL_PRIM_TRIANGLE_LIST]  = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
     187                 :            :             [PL_PRIM_TRIANGLE_STRIP] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
     188                 :            :         };
     189                 :            : 
     190                 :        108 :         VkGraphicsPipelineCreateInfo cinfo = {
     191                 :            :             .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
     192                 :            :             .flags = flags,
     193                 :            :             .stageCount = 2,
     194                 :        108 :             .pStages = (VkPipelineShaderStageCreateInfo[]) {
     195                 :            :                 {
     196                 :            :                     .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
     197                 :            :                     .stage = VK_SHADER_STAGE_VERTEX_BIT,
     198                 :        108 :                     .module = pass_vk->vert,
     199                 :            :                     .pName = "main",
     200                 :            :                 }, {
     201                 :            :                     .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
     202                 :            :                     .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
     203                 :        108 :                     .module = pass_vk->shader,
     204                 :            :                     .pName = "main",
     205                 :            :                     .pSpecializationInfo = specInfo,
     206                 :            :                 }
     207                 :            :             },
     208                 :        108 :             .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo) {
     209                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
     210                 :            :                 .vertexBindingDescriptionCount = 1,
     211                 :        108 :                 .pVertexBindingDescriptions = &(VkVertexInputBindingDescription) {
     212                 :            :                     .binding = 0,
     213                 :        108 :                     .stride = params->vertex_stride,
     214                 :            :                     .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
     215                 :            :                 },
     216                 :        108 :                 .vertexAttributeDescriptionCount = params->num_vertex_attribs,
     217                 :        108 :                 .pVertexAttributeDescriptions = pass_vk->attrs,
     218                 :            :             },
     219                 :        108 :             .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) {
     220                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
     221                 :        108 :                 .topology = topologies[params->vertex_type],
     222                 :            :             },
     223                 :        108 :             .pViewportState = &(VkPipelineViewportStateCreateInfo) {
     224                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
     225                 :            :                 .viewportCount = 1,
     226                 :            :                 .scissorCount = 1,
     227                 :            :             },
     228                 :        108 :             .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) {
     229                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
     230                 :            :                 .polygonMode = VK_POLYGON_MODE_FILL,
     231                 :            :                 .cullMode = VK_CULL_MODE_NONE,
     232                 :            :                 .lineWidth = 1.0f,
     233                 :            :             },
     234                 :        108 :             .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) {
     235                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
     236                 :            :                 .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
     237                 :            :             },
     238                 :        108 :             .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) {
     239                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
     240                 :            :                 .attachmentCount = 1,
     241                 :            :                 .pAttachments = &blendState,
     242                 :            :             },
     243                 :        108 :             .pDynamicState = &(VkPipelineDynamicStateCreateInfo) {
     244                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
     245                 :            :                 .dynamicStateCount = 2,
     246                 :        108 :                 .pDynamicStates = (VkDynamicState[]){
     247                 :            :                     VK_DYNAMIC_STATE_VIEWPORT,
     248                 :            :                     VK_DYNAMIC_STATE_SCISSOR,
     249                 :            :                 },
     250                 :            :             },
     251                 :        108 :             .layout = pass_vk->pipeLayout,
     252                 :        108 :             .renderPass = pass_vk->renderPass,
     253                 :            :             .basePipelineHandle = base,
     254                 :            :             .basePipelineIndex = -1,
     255                 :            :         };
     256                 :            : 
     257                 :        108 :         return vk->CreateGraphicsPipelines(vk->dev, pass_vk->cache, 1, &cinfo,
     258                 :            :                                            PL_VK_ALLOC, out_pipe);
     259                 :            :     }
     260                 :            : 
     261                 :         53 :     case PL_PASS_COMPUTE: {
     262                 :         53 :         VkComputePipelineCreateInfo cinfo = {
     263                 :            :             .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
     264                 :            :             .flags = flags,
     265                 :            :             .stage = {
     266                 :            :                 .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
     267                 :            :                 .stage = VK_SHADER_STAGE_COMPUTE_BIT,
     268                 :         53 :                 .module = pass_vk->shader,
     269                 :            :                 .pName = "main",
     270                 :            :                 .pSpecializationInfo = specInfo,
     271                 :            :             },
     272                 :         53 :             .layout = pass_vk->pipeLayout,
     273                 :            :             .basePipelineHandle = base,
     274                 :            :             .basePipelineIndex = -1,
     275                 :            :         };
     276                 :            : 
     277                 :         53 :         return vk->CreateComputePipelines(vk->dev, pass_vk->cache, 1, &cinfo,
     278                 :            :                                           PL_VK_ALLOC, out_pipe);
     279                 :            :     }
     280                 :            : 
     281                 :            :     case PL_PASS_INVALID:
     282                 :            :     case PL_PASS_TYPE_COUNT:
     283                 :            :         break;
     284                 :            :     }
     285                 :            : 
     286                 :          0 :     pl_unreachable();
     287                 :            : }
     288                 :            : 
     289                 :        160 : pl_pass vk_pass_create(pl_gpu gpu, const struct pl_pass_params *params)
     290                 :            : {
     291                 :        160 :     struct pl_vk *p = PL_PRIV(gpu);
     292                 :        160 :     struct vk_ctx *vk = p->vk;
     293                 :            :     bool success = false;
     294                 :            : 
     295                 :        160 :     struct pl_pass_t *pass = pl_zalloc_obj(NULL, pass, struct pl_pass_vk);
     296                 :        160 :     pass->params = pl_pass_params_copy(pass, params);
     297                 :            : 
     298                 :        160 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
     299                 :        160 :     pass_vk->dmask = -1; // all descriptors available
     300                 :            : 
     301                 :            :     // temporary allocations
     302                 :        160 :     void *tmp = pl_tmp(NULL);
     303                 :            : 
     304                 :        160 :     int num_desc = params->num_descriptors;
     305         [ +  + ]:        160 :     if (!num_desc)
     306                 :          4 :         goto no_descriptors;
     307         [ -  + ]:        156 :     if (num_desc > vk->props.limits.maxPerStageResources) {
     308                 :          0 :         PL_ERR(gpu, "Pass with %d descriptors exceeds the maximum number of "
     309                 :            :                "per-stage resources %" PRIu32"!",
     310                 :            :                num_desc, vk->props.limits.maxPerStageResources);
     311                 :          0 :         goto error;
     312                 :            :     }
     313                 :            : 
     314                 :        156 :     pass_vk->dswrite = pl_calloc(pass, num_desc, sizeof(VkWriteDescriptorSet));
     315                 :        156 :     pass_vk->dsiinfo = pl_calloc(pass, num_desc, sizeof(VkDescriptorImageInfo));
     316                 :        156 :     pass_vk->dsbinfo = pl_calloc(pass, num_desc, sizeof(VkDescriptorBufferInfo));
     317                 :            : 
     318                 :            : #define NUM_DS (PL_ARRAY_SIZE(pass_vk->dss))
     319                 :            : 
     320                 :        156 :     int dsSize[PL_DESC_TYPE_COUNT] = {0};
     321                 :            :     VkDescriptorSetLayoutBinding *bindings = pl_calloc_ptr(tmp, num_desc, bindings);
     322                 :            : 
     323                 :        156 :     uint32_t max_tex = vk->props.limits.maxPerStageDescriptorSampledImages,
     324                 :        156 :              max_img = vk->props.limits.maxPerStageDescriptorStorageImages,
     325                 :        156 :              max_ubo = vk->props.limits.maxPerStageDescriptorUniformBuffers,
     326                 :        156 :              max_ssbo = vk->props.limits.maxPerStageDescriptorStorageBuffers;
     327                 :            : 
     328                 :        156 :     uint32_t *dsLimits[PL_DESC_TYPE_COUNT] = {
     329                 :            :         [PL_DESC_SAMPLED_TEX] = &max_tex,
     330                 :            :         [PL_DESC_STORAGE_IMG] = &max_img,
     331                 :            :         [PL_DESC_BUF_UNIFORM] = &max_ubo,
     332                 :            :         [PL_DESC_BUF_STORAGE] = &max_ssbo,
     333                 :            :         [PL_DESC_BUF_TEXEL_UNIFORM] = &max_tex,
     334                 :            :         [PL_DESC_BUF_TEXEL_STORAGE] = &max_img,
     335                 :            :     };
     336                 :            : 
     337         [ +  + ]:        478 :     for (int i = 0; i < num_desc; i++) {
     338                 :        322 :         struct pl_desc *desc = &params->descriptors[i];
     339         [ -  + ]:        322 :         if (!(*dsLimits[desc->type])--) {
     340                 :          0 :             PL_ERR(gpu, "Pass exceeds the maximum number of per-stage "
     341                 :            :                    "descriptors of type %u!", (unsigned) desc->type);
     342                 :          0 :             goto error;
     343                 :            :         }
     344                 :            : 
     345                 :        322 :         dsSize[desc->type]++;
     346                 :        322 :         bindings[i] = (VkDescriptorSetLayoutBinding) {
     347                 :        322 :             .binding = desc->binding,
     348                 :        322 :             .descriptorType = dsType[desc->type],
     349                 :            :             .descriptorCount = 1,
     350                 :        322 :             .stageFlags = stageFlags[params->type],
     351                 :            :         };
     352                 :            :     }
     353                 :            : 
     354                 :        156 :     VkDescriptorSetLayoutCreateInfo dinfo = {
     355                 :            :         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
     356                 :            :         .pBindings = bindings,
     357                 :            :         .bindingCount = num_desc,
     358                 :            :     };
     359                 :            : 
     360   [ +  -  +  - ]:        156 :     if (p->max_push_descriptors && num_desc <= p->max_push_descriptors) {
     361                 :        156 :         dinfo.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
     362                 :        156 :         pass_vk->use_pushd = true;
     363         [ #  # ]:          0 :     } else if (p->max_push_descriptors) {
     364                 :          0 :         PL_INFO(gpu, "Pass with %d descriptors exceeds the maximum push "
     365                 :            :                 "descriptor count (%d). Falling back to descriptor sets!",
     366                 :            :                 num_desc, p->max_push_descriptors);
     367                 :            :     }
     368                 :            : 
     369         [ -  + ]:        156 :     VK(vk->CreateDescriptorSetLayout(vk->dev, &dinfo, PL_VK_ALLOC,
     370                 :            :                                      &pass_vk->dsLayout));
     371                 :            : 
     372         [ +  - ]:        156 :     if (!pass_vk->use_pushd) {
     373                 :            :         PL_ARRAY(VkDescriptorPoolSize) dsPoolSizes = {0};
     374                 :            : 
     375         [ #  # ]:          0 :         for (enum pl_desc_type t = 0; t < PL_DESC_TYPE_COUNT; t++) {
     376         [ #  # ]:          0 :             if (dsSize[t] > 0) {
     377   [ #  #  #  #  :          0 :                 PL_ARRAY_APPEND(tmp, dsPoolSizes, (VkDescriptorPoolSize) {
                   #  # ]
     378                 :            :                     .type = dsType[t],
     379                 :            :                     .descriptorCount = dsSize[t] * NUM_DS,
     380                 :            :                 });
     381                 :            :             }
     382                 :            :         }
     383                 :            : 
     384         [ #  # ]:          0 :         if (dsPoolSizes.num) {
     385                 :          0 :             VkDescriptorPoolCreateInfo pinfo = {
     386                 :            :                 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
     387                 :            :                 .maxSets = NUM_DS,
     388                 :            :                 .pPoolSizes = dsPoolSizes.elem,
     389                 :          0 :                 .poolSizeCount = dsPoolSizes.num,
     390                 :            :             };
     391                 :            : 
     392         [ #  # ]:          0 :             VK(vk->CreateDescriptorPool(vk->dev, &pinfo, PL_VK_ALLOC, &pass_vk->dsPool));
     393                 :            : 
     394                 :            :             VkDescriptorSetLayout layouts[NUM_DS];
     395         [ #  # ]:          0 :             for (int i = 0; i < NUM_DS; i++)
     396                 :          0 :                 layouts[i] = pass_vk->dsLayout;
     397                 :            : 
     398                 :          0 :             VkDescriptorSetAllocateInfo ainfo = {
     399                 :            :                 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
     400                 :          0 :                 .descriptorPool = pass_vk->dsPool,
     401                 :            :                 .descriptorSetCount = NUM_DS,
     402                 :            :                 .pSetLayouts = layouts,
     403                 :            :             };
     404                 :            : 
     405         [ #  # ]:          0 :             VK(vk->AllocateDescriptorSets(vk->dev, &ainfo, pass_vk->dss));
     406                 :            :         }
     407                 :            :     }
     408                 :            : 
     409                 :        156 : no_descriptors: ;
     410                 :            : 
     411                 :        160 :     bool has_spec = params->num_constants;
     412         [ +  + ]:        160 :     if (has_spec) {
     413                 :            :         PL_ARRAY(VkSpecializationMapEntry) entries = {0};
     414         [ +  - ]:        154 :         PL_ARRAY_RESIZE(pass, entries, params->num_constants);
     415                 :            :         size_t spec_size = 0;
     416                 :            : 
     417         [ +  + ]:       1187 :         for (int i = 0; i < params->num_constants; i++) {
     418                 :       1033 :             const struct pl_constant *con = &params->constants[i];
     419                 :       1033 :             size_t con_size = pl_var_type_size(con->type);
     420                 :       1033 :             entries.elem[i] = (VkSpecializationMapEntry) {
     421                 :       1033 :                 .constantID = con->id,
     422                 :       1033 :                 .offset = con->offset,
     423                 :            :                 .size = con_size,
     424                 :            :             };
     425                 :            : 
     426                 :       1033 :             size_t req_size = con->offset + con_size;
     427                 :       1033 :             spec_size = PL_MAX(spec_size, req_size);
     428                 :            :         }
     429                 :            : 
     430                 :        154 :         pass_vk->spec_size = spec_size;
     431                 :        154 :         pass_vk->specInfo = (VkSpecializationInfo) {
     432                 :        154 :             .mapEntryCount = params->num_constants,
     433                 :            :             .pMapEntries = entries.elem,
     434                 :            :         };
     435                 :            : 
     436         [ +  - ]:        154 :         if (params->constant_data) {
     437                 :        154 :             pass_vk->specInfo.pData = pl_memdup(pass, params->constant_data, spec_size);
     438                 :        154 :             pass_vk->specInfo.dataSize = spec_size;
     439                 :            :         }
     440                 :            :     }
     441                 :            : 
     442                 :        160 :     VkPipelineLayoutCreateInfo linfo = {
     443                 :            :         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
     444                 :        160 :         .setLayoutCount = num_desc ? 1 : 0,
     445                 :        160 :         .pSetLayouts = &pass_vk->dsLayout,
     446                 :        160 :         .pushConstantRangeCount = params->push_constants_size ? 1 : 0,
     447                 :        160 :         .pPushConstantRanges = &(VkPushConstantRange){
     448                 :        160 :             .stageFlags = stageFlags[params->type],
     449                 :            :             .offset = 0,
     450                 :            :             .size = params->push_constants_size,
     451                 :            :         },
     452                 :            :     };
     453                 :            : 
     454         [ -  + ]:        160 :     VK(vk->CreatePipelineLayout(vk->dev, &linfo, PL_VK_ALLOC,
     455                 :            :                                 &pass_vk->pipeLayout));
     456                 :            : 
     457                 :        160 :     pl_cache_obj vert = {0}, frag = {0}, comp = {0};
     458   [ +  +  -  - ]:        160 :     switch (params->type) {
     459                 :        107 :     case PL_PASS_RASTER: ;
     460         [ -  + ]:        107 :         VK(vk_compile_glsl(gpu, tmp, GLSL_SHADER_VERTEX, params->vertex_shader, &vert));
     461         [ -  + ]:        107 :         VK(vk_compile_glsl(gpu, tmp, GLSL_SHADER_FRAGMENT, params->glsl_shader, &frag));
     462                 :            :         break;
     463                 :         53 :     case PL_PASS_COMPUTE:
     464         [ -  + ]:         53 :         VK(vk_compile_glsl(gpu, tmp, GLSL_SHADER_COMPUTE, params->glsl_shader, &comp));
     465                 :            :         break;
     466                 :            :     case PL_PASS_INVALID:
     467                 :            :     case PL_PASS_TYPE_COUNT:
     468                 :          0 :         pl_unreachable();
     469                 :            :     }
     470                 :            : 
     471                 :            :     // Use hash of generated SPIR-V as key for pipeline cache
     472                 :        160 :     const pl_cache cache = pl_gpu_cache(gpu);
     473                 :        160 :     pl_cache_obj pipecache = {0};
     474         [ +  + ]:        160 :     if (cache) {
     475                 :          2 :         pipecache.key = CACHE_KEY_VK_PIPE;
     476                 :          2 :         pl_hash_merge(&pipecache.key, pl_var_hash(vk->props.pipelineCacheUUID));
     477                 :          2 :         pl_hash_merge(&pipecache.key, pl_mem_hash(vert.data, vert.size));
     478                 :          2 :         pl_hash_merge(&pipecache.key, pl_mem_hash(frag.data, frag.size));
     479                 :          2 :         pl_hash_merge(&pipecache.key, pl_mem_hash(comp.data, comp.size));
     480                 :          2 :         pl_cache_get(cache, &pipecache);
     481                 :            :     }
     482                 :            : 
     483         [ +  + ]:        160 :     if (cache || has_spec) {
     484                 :            :         // Don't create pipeline cache unless we either plan on caching the
     485                 :            :         // result of this shader to a pl_cache, or if we will possibly re-use
     486                 :            :         // it due to the presence of specialization constants
     487                 :        154 :         VkPipelineCacheCreateInfo pcinfo = {
     488                 :            :             .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
     489                 :        154 :             .pInitialData = pipecache.data,
     490                 :        154 :             .initialDataSize = pipecache.size,
     491                 :            :         };
     492                 :            : 
     493         [ -  + ]:        154 :         VK(vk->CreatePipelineCache(vk->dev, &pcinfo, PL_VK_ALLOC, &pass_vk->cache));
     494                 :            :     }
     495                 :            : 
     496                 :        160 :     VkShaderModuleCreateInfo sinfo = {
     497                 :            :         .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
     498                 :            :     };
     499                 :            : 
     500                 :            :     pl_clock_t start = pl_clock_now();
     501   [ +  +  -  - ]:        160 :     switch (params->type) {
     502                 :        107 :     case PL_PASS_RASTER: {
     503                 :        107 :         sinfo.pCode = (uint32_t *) vert.data;
     504                 :        107 :         sinfo.codeSize = vert.size;
     505         [ -  + ]:        107 :         VK(vk->CreateShaderModule(vk->dev, &sinfo, PL_VK_ALLOC, &pass_vk->vert));
     506         [ +  - ]:        107 :         PL_VK_NAME(SHADER_MODULE, pass_vk->vert, "vertex");
     507                 :            : 
     508                 :        107 :         sinfo.pCode = (uint32_t *) frag.data;
     509                 :        107 :         sinfo.codeSize = frag.size;
     510         [ -  + ]:        107 :         VK(vk->CreateShaderModule(vk->dev, &sinfo, PL_VK_ALLOC, &pass_vk->shader));
     511         [ +  - ]:        107 :         PL_VK_NAME(SHADER_MODULE, pass_vk->shader, "fragment");
     512                 :            : 
     513                 :        107 :         pass_vk->attrs = pl_calloc_ptr(pass, params->num_vertex_attribs, pass_vk->attrs);
     514         [ +  + ]:        327 :         for (int i = 0; i < params->num_vertex_attribs; i++) {
     515                 :        220 :             struct pl_vertex_attrib *va = &params->vertex_attribs[i];
     516                 :        220 :             const struct vk_format **pfmt_vk = PL_PRIV(va->fmt);
     517                 :            : 
     518                 :        440 :             pass_vk->attrs[i] = (VkVertexInputAttributeDescription) {
     519                 :            :                 .binding  = 0,
     520                 :        220 :                 .location = va->location,
     521                 :        220 :                 .offset   = va->offset,
     522         [ +  - ]:        220 :                 .format   = PL_DEF((*pfmt_vk)->bfmt, (*pfmt_vk)->tfmt),
     523                 :            :             };
     524                 :            :         }
     525                 :            : 
     526                 :        214 :         VkRenderPassCreateInfo rinfo = {
     527                 :            :             .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
     528                 :            :             .attachmentCount = 1,
     529                 :        214 :             .pAttachments = &(VkAttachmentDescription) {
     530                 :        107 :                 .format = (VkFormat) params->target_format->signature,
     531                 :            :                 .samples = VK_SAMPLE_COUNT_1_BIT,
     532                 :        107 :                 .loadOp = pass->params.load_target
     533                 :            :                             ? VK_ATTACHMENT_LOAD_OP_LOAD
     534         [ +  + ]:        107 :                             : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
     535                 :            :                 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
     536                 :            :                 .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     537                 :            :                 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     538                 :            :             },
     539                 :            :             .subpassCount = 1,
     540                 :        107 :             .pSubpasses = &(VkSubpassDescription) {
     541                 :            :                 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
     542                 :            :                 .colorAttachmentCount = 1,
     543                 :        107 :                 .pColorAttachments = &(VkAttachmentReference) {
     544                 :            :                     .attachment = 0,
     545                 :            :                     .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     546                 :            :                 },
     547                 :            :             },
     548                 :            :         };
     549                 :            : 
     550         [ -  + ]:        107 :         VK(vk->CreateRenderPass(vk->dev, &rinfo, PL_VK_ALLOC, &pass_vk->renderPass));
     551                 :        107 :         break;
     552                 :            :     }
     553                 :         53 :     case PL_PASS_COMPUTE: {
     554                 :         53 :         sinfo.pCode = (uint32_t *) comp.data;
     555                 :         53 :         sinfo.codeSize = comp.size;
     556         [ -  + ]:         53 :         VK(vk->CreateShaderModule(vk->dev, &sinfo, PL_VK_ALLOC, &pass_vk->shader));
     557         [ +  - ]:         53 :         PL_VK_NAME(SHADER_MODULE, pass_vk->shader, "compute");
     558                 :            :         break;
     559                 :            :     }
     560                 :            :     case PL_PASS_INVALID:
     561                 :            :     case PL_PASS_TYPE_COUNT:
     562                 :          0 :         pl_unreachable();
     563                 :            :     }
     564                 :            : 
     565                 :            :     pl_clock_t after_compilation = pl_clock_now();
     566                 :        160 :     pl_log_cpu_time(gpu->log, start, after_compilation, "compiling shader");
     567                 :            : 
     568                 :            :     // Update cache entries on successful compilation
     569                 :        160 :     pl_cache_steal(cache, &vert);
     570                 :        160 :     pl_cache_steal(cache, &frag);
     571                 :        160 :     pl_cache_steal(cache, &comp);
     572                 :            : 
     573                 :            :     // Create the graphics/compute pipeline
     574         [ +  + ]:        160 :     VkPipeline *pipe = has_spec ? &pass_vk->base : &pass_vk->pipe;
     575         [ -  + ]:        160 :     VK(vk_recreate_pipelines(vk, pass, has_spec, VK_NULL_HANDLE, pipe));
     576                 :        160 :     pl_log_cpu_time(gpu->log, after_compilation, pl_clock_now(), "creating pipeline");
     577                 :            : 
     578                 :            :     // Update pipeline cache
     579         [ +  + ]:        160 :     if (cache) {
     580                 :          2 :         size_t size = 0;
     581         [ -  + ]:          2 :         VK(vk->GetPipelineCacheData(vk->dev, pass_vk->cache, &size, NULL));
     582                 :          2 :         pl_cache_obj_resize(tmp, &pipecache, size);
     583         [ -  + ]:          2 :         VK(vk->GetPipelineCacheData(vk->dev, pass_vk->cache, &size, pipecache.data));
     584                 :          2 :         pl_cache_steal(cache, &pipecache);
     585                 :            :     }
     586                 :            : 
     587         [ +  + ]:        160 :     if (!has_spec) {
     588                 :            :         // We can free these if we no longer need them for specialization
     589                 :          6 :         pl_free_ptr(&pass_vk->attrs);
     590                 :          6 :         vk->DestroyShaderModule(vk->dev, pass_vk->vert, PL_VK_ALLOC);
     591                 :          6 :         vk->DestroyShaderModule(vk->dev, pass_vk->shader, PL_VK_ALLOC);
     592                 :          6 :         vk->DestroyPipelineCache(vk->dev, pass_vk->cache, PL_VK_ALLOC);
     593                 :          6 :         pass_vk->vert = VK_NULL_HANDLE;
     594                 :          6 :         pass_vk->shader = VK_NULL_HANDLE;
     595                 :          6 :         pass_vk->cache = VK_NULL_HANDLE;
     596                 :            :     }
     597                 :            : 
     598                 :        160 :     PL_DEBUG(vk, "Pass statistics: size %zu, SPIR-V: vert %zu frag %zu comp %zu",
     599                 :            :              pipecache.size, vert.size, frag.size, comp.size);
     600                 :            : 
     601                 :            :     success = true;
     602                 :            : 
     603                 :            : error:
     604                 :            :     if (!success) {
     605                 :          0 :         pass_destroy_cb(gpu, pass);
     606                 :            :         pass = NULL;
     607                 :            :     }
     608                 :            : 
     609                 :            : #undef NUM_DS
     610                 :            : 
     611                 :        160 :     pl_free(tmp);
     612                 :        160 :     return pass;
     613                 :            : }
     614                 :            : 
     615                 :            : static const VkPipelineStageFlags2 shaderStages[] = {
     616                 :            :     [PL_PASS_RASTER]  = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
     617                 :            :     [PL_PASS_COMPUTE] = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
     618                 :            : };
     619                 :            : 
     620                 :        887 : static void vk_update_descriptor(pl_gpu gpu, struct vk_cmd *cmd, pl_pass pass,
     621                 :            :                                  struct pl_desc_binding db,
     622                 :            :                                  VkDescriptorSet ds, int idx)
     623                 :            : {
     624                 :        887 :     struct pl_vk *p = PL_PRIV(gpu);
     625                 :        887 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
     626                 :        887 :     struct pl_desc *desc = &pass->params.descriptors[idx];
     627                 :            : 
     628                 :        887 :     VkWriteDescriptorSet *wds = &pass_vk->dswrite[idx];
     629                 :        887 :     *wds = (VkWriteDescriptorSet) {
     630                 :            :         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
     631                 :            :         .dstSet = ds,
     632                 :        887 :         .dstBinding = desc->binding,
     633                 :            :         .descriptorCount = 1,
     634                 :        887 :         .descriptorType = dsType[desc->type],
     635                 :            :     };
     636                 :            : 
     637                 :            :     static const VkAccessFlags2 storageAccess[PL_DESC_ACCESS_COUNT] = {
     638                 :            :         [PL_DESC_ACCESS_READONLY]   = VK_ACCESS_2_SHADER_STORAGE_READ_BIT,
     639                 :            :         [PL_DESC_ACCESS_WRITEONLY]  = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
     640                 :            :         [PL_DESC_ACCESS_READWRITE]  = VK_ACCESS_2_SHADER_STORAGE_READ_BIT |
     641                 :            :                                       VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT,
     642                 :            :     };
     643                 :            : 
     644   [ +  +  +  +  :        887 :     switch (desc->type) {
                      - ]
     645                 :        779 :     case PL_DESC_SAMPLED_TEX: {
     646                 :            :         pl_tex tex = db.object;
     647                 :        779 :         struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     648                 :            : 
     649                 :        779 :         vk_tex_barrier(gpu, cmd, tex, shaderStages[pass->params.type],
     650                 :            :                        VK_ACCESS_2_SHADER_SAMPLED_READ_BIT,
     651                 :            :                        VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
     652                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     653                 :            : 
     654                 :        779 :         VkDescriptorImageInfo *iinfo = &pass_vk->dsiinfo[idx];
     655                 :        779 :         *iinfo = (VkDescriptorImageInfo) {
     656                 :        779 :             .sampler = p->samplers[db.sample_mode][db.address_mode],
     657                 :        779 :             .imageView = tex_vk->view,
     658                 :        779 :             .imageLayout = tex_vk->layout,
     659                 :            :         };
     660                 :            : 
     661                 :        779 :         wds->pImageInfo = iinfo;
     662                 :            :         return;
     663                 :            :     }
     664                 :         66 :     case PL_DESC_STORAGE_IMG: {
     665                 :            :         pl_tex tex = db.object;
     666                 :         66 :         struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     667                 :            : 
     668                 :         66 :         vk_tex_barrier(gpu, cmd, tex, shaderStages[pass->params.type],
     669                 :         66 :                        storageAccess[desc->access], VK_IMAGE_LAYOUT_GENERAL,
     670                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     671                 :            : 
     672                 :         66 :         VkDescriptorImageInfo *iinfo = &pass_vk->dsiinfo[idx];
     673                 :         66 :         *iinfo = (VkDescriptorImageInfo) {
     674                 :         66 :             .imageView = tex_vk->view,
     675                 :         66 :             .imageLayout = tex_vk->layout,
     676                 :            :         };
     677                 :            : 
     678                 :         66 :         wds->pImageInfo = iinfo;
     679                 :            :         return;
     680                 :            :     }
     681                 :         24 :     case PL_DESC_BUF_UNIFORM:
     682                 :            :     case PL_DESC_BUF_STORAGE: {
     683                 :            :         pl_buf buf = db.object;
     684                 :         24 :         struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     685                 :            : 
     686                 :            :         VkAccessFlags2 access = VK_ACCESS_2_UNIFORM_READ_BIT;
     687         [ +  + ]:         24 :         if (desc->type == PL_DESC_BUF_STORAGE)
     688                 :         11 :             access = storageAccess[desc->access];
     689                 :            : 
     690                 :         24 :         vk_buf_barrier(gpu, cmd, buf, shaderStages[pass->params.type],
     691                 :         24 :                        access, 0, buf->params.size, false);
     692                 :            : 
     693                 :         24 :         VkDescriptorBufferInfo *binfo = &pass_vk->dsbinfo[idx];
     694                 :         24 :         *binfo = (VkDescriptorBufferInfo) {
     695                 :         24 :             .buffer = buf_vk->mem.buf,
     696                 :         24 :             .offset = buf_vk->mem.offset,
     697                 :         24 :             .range = buf->params.size,
     698                 :            :         };
     699                 :            : 
     700                 :         24 :         wds->pBufferInfo = binfo;
     701                 :            :         return;
     702                 :            :     }
     703                 :         18 :     case PL_DESC_BUF_TEXEL_UNIFORM:
     704                 :            :     case PL_DESC_BUF_TEXEL_STORAGE: {
     705                 :            :         pl_buf buf = db.object;
     706                 :         18 :         struct pl_buf_vk *buf_vk = PL_PRIV(buf);
     707                 :            : 
     708                 :            :         VkAccessFlags2 access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
     709         [ +  - ]:         18 :         if (desc->type == PL_DESC_BUF_TEXEL_STORAGE)
     710                 :         18 :             access = storageAccess[desc->access];
     711                 :            : 
     712                 :         18 :         vk_buf_barrier(gpu, cmd, buf, shaderStages[pass->params.type],
     713                 :         18 :                        access, 0, buf->params.size, false);
     714                 :            : 
     715                 :         18 :         wds->pTexelBufferView = &buf_vk->view;
     716                 :         18 :         return;
     717                 :            :     }
     718                 :            :     case PL_DESC_INVALID:
     719                 :            :     case PL_DESC_TYPE_COUNT:
     720                 :            :         break;
     721                 :            :     }
     722                 :            : 
     723                 :          0 :     pl_unreachable();
     724                 :            : }
     725                 :            : 
     726                 :        887 : static void vk_release_descriptor(pl_gpu gpu, struct vk_cmd *cmd, pl_pass pass,
     727                 :            :                                   struct pl_desc_binding db, int idx)
     728                 :            : {
     729                 :        887 :     const struct pl_desc *desc = &pass->params.descriptors[idx];
     730                 :            : 
     731      [ +  -  + ]:        887 :     switch (desc->type) {
     732                 :         42 :     case PL_DESC_BUF_UNIFORM:
     733                 :            :     case PL_DESC_BUF_STORAGE:
     734                 :            :     case PL_DESC_BUF_TEXEL_UNIFORM:
     735                 :            :     case PL_DESC_BUF_TEXEL_STORAGE:
     736         [ +  + ]:         42 :         if (desc->access != PL_DESC_ACCESS_READONLY) {
     737                 :         41 :             pl_buf buf = db.object;
     738                 :         41 :             vk_buf_flush(gpu, cmd, buf, 0, buf->params.size);
     739                 :            :         }
     740                 :            :         return;
     741                 :            :     case PL_DESC_SAMPLED_TEX:
     742                 :            :     case PL_DESC_STORAGE_IMG:
     743                 :            :         return;
     744                 :            :     case PL_DESC_INVALID:
     745                 :            :     case PL_DESC_TYPE_COUNT:
     746                 :            :         break;
     747                 :            :     }
     748                 :            : 
     749                 :          0 :     pl_unreachable();
     750                 :            : }
     751                 :            : 
     752                 :            : static void set_ds(struct pl_pass_vk *pass_vk, void *dsbit)
     753                 :            : {
     754                 :          6 :     pass_vk->dmask |= (uintptr_t) dsbit;
     755                 :            : }
     756                 :            : 
     757                 :          6 : VK_CB_FUNC_DEF(set_ds);
     758                 :            : 
     759                 :        536 : static bool need_respec(pl_pass pass, const struct pl_pass_run_params *params)
     760                 :            : {
     761                 :        536 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
     762   [ +  +  +  - ]:        536 :     if (!pass_vk->spec_size || !params->constant_data)
     763                 :            :         return false;
     764                 :            : 
     765                 :            :     VkSpecializationInfo *specInfo = &pass_vk->specInfo;
     766                 :            :     size_t size = pass_vk->spec_size;
     767         [ -  + ]:        528 :     if (!specInfo->pData) {
     768                 :            :         // Shader was never specialized before
     769                 :          0 :         specInfo->pData = pl_memdup((void *) pass, params->constant_data, size);
     770                 :          0 :         specInfo->dataSize = size;
     771                 :          0 :         return true;
     772                 :            :     }
     773                 :            : 
     774                 :            :     // Shader is being re-specialized with new values
     775         [ +  + ]:        528 :     if (memcmp(specInfo->pData, params->constant_data, size) != 0) {
     776                 :            :         memcpy((void *) specInfo->pData, params->constant_data, size);
     777                 :          1 :         return true;
     778                 :            :     }
     779                 :            : 
     780                 :            :     return false;
     781                 :            : }
     782                 :            : 
     783                 :       1005 : void vk_pass_run(pl_gpu gpu, const struct pl_pass_run_params *params)
     784                 :            : {
     785                 :       1005 :     struct pl_vk *p = PL_PRIV(gpu);
     786                 :       1005 :     struct vk_ctx *vk = p->vk;
     787                 :       1005 :     pl_pass pass = params->pass;
     788                 :       1005 :     struct pl_pass_vk *pass_vk = PL_PRIV(pass);
     789                 :            : 
     790   [ +  +  -  + ]:       1005 :     if (params->vertex_data || params->index_data)
     791                 :        469 :         return pl_pass_run_vbo(gpu, params);
     792                 :            : 
     793                 :            :     // Check if we need to re-specialize this pipeline
     794         [ +  + ]:        536 :     if (need_respec(pass, params)) {
     795                 :            :         pl_clock_t start = pl_clock_now();
     796         [ -  + ]:          1 :         VK(vk_recreate_pipelines(vk, pass, false, pass_vk->base, &pass_vk->pipe));
     797                 :          1 :         pl_log_cpu_time(gpu->log, start, pl_clock_now(), "re-specializing shader");
     798                 :            :     }
     799                 :            : 
     800         [ +  + ]:        536 :     if (!pass_vk->use_pushd) {
     801                 :            :         // Wait for a free descriptor set
     802         [ -  + ]:          6 :         while (!pass_vk->dmask) {
     803                 :          0 :             PL_TRACE(gpu, "No free descriptor sets! ...blocking (slow path)");
     804                 :          0 :             vk_poll_commands(vk, 10000000); // 10 ms
     805                 :            :         }
     806                 :            :     }
     807                 :            : 
     808                 :            :     static const enum queue_type types[] = {
     809                 :            :         [PL_PASS_RASTER]  = GRAPHICS,
     810                 :            :         [PL_PASS_COMPUTE] = COMPUTE,
     811                 :            :     };
     812                 :            : 
     813                 :        536 :     struct vk_cmd *cmd = CMD_BEGIN_TIMED(types[pass->params.type], params->timer);
     814         [ -  + ]:        536 :     if (!cmd)
     815                 :          0 :         goto error;
     816                 :            : 
     817                 :            :     // Find a descriptor set to use
     818                 :        536 :     VkDescriptorSet ds = VK_NULL_HANDLE;
     819         [ +  + ]:        536 :     if (!pass_vk->use_pushd) {
     820         [ +  - ]:          6 :         for (int i = 0; i < PL_ARRAY_SIZE(pass_vk->dss); i++) {
     821                 :          6 :             uint16_t dsbit = 1u << i;
     822         [ +  - ]:          6 :             if (pass_vk->dmask & dsbit) {
     823                 :          6 :                 ds = pass_vk->dss[i];
     824                 :          6 :                 pass_vk->dmask &= ~dsbit; // unset
     825                 :          6 :                 vk_cmd_callback(cmd, VK_CB_FUNC(set_ds), pass_vk,
     826                 :          6 :                                 (void *)(uintptr_t) dsbit);
     827                 :          6 :                 break;
     828                 :            :             }
     829                 :            :         }
     830                 :            :     }
     831                 :            : 
     832                 :            :     // Update the dswrite structure with all of the new values
     833         [ +  + ]:       1423 :     for (int i = 0; i < pass->params.num_descriptors; i++)
     834                 :        887 :         vk_update_descriptor(gpu, cmd, pass, params->desc_bindings[i], ds, i);
     835                 :            : 
     836         [ +  + ]:        536 :     if (!pass_vk->use_pushd) {
     837                 :          6 :         vk->UpdateDescriptorSets(vk->dev, pass->params.num_descriptors,
     838                 :          6 :                                  pass_vk->dswrite, 0, NULL);
     839                 :            :     }
     840                 :            : 
     841                 :            :     // Bind the pipeline, descriptor set, etc.
     842                 :            :     static const VkPipelineBindPoint bindPoint[] = {
     843                 :            :         [PL_PASS_RASTER]  = VK_PIPELINE_BIND_POINT_GRAPHICS,
     844                 :            :         [PL_PASS_COMPUTE] = VK_PIPELINE_BIND_POINT_COMPUTE,
     845                 :            :     };
     846                 :            : 
     847                 :        536 :     vk->CmdBindPipeline(cmd->buf, bindPoint[pass->params.type],
     848         [ +  + ]:        536 :                         PL_DEF(pass_vk->pipe, pass_vk->base));
     849                 :            : 
     850         [ -  + ]:        536 :     if (ds) {
     851                 :          0 :         vk->CmdBindDescriptorSets(cmd->buf, bindPoint[pass->params.type],
     852                 :            :                                   pass_vk->pipeLayout, 0, 1, &ds, 0, NULL);
     853                 :            :     }
     854                 :            : 
     855         [ +  + ]:        536 :     if (pass_vk->use_pushd) {
     856                 :        530 :         vk->CmdPushDescriptorSetKHR(cmd->buf, bindPoint[pass->params.type],
     857                 :            :                                     pass_vk->pipeLayout, 0,
     858                 :        530 :                                     pass->params.num_descriptors,
     859                 :        530 :                                     pass_vk->dswrite);
     860                 :            :     }
     861                 :            : 
     862         [ +  + ]:        536 :     if (pass->params.push_constants_size) {
     863                 :        488 :         vk->CmdPushConstants(cmd->buf, pass_vk->pipeLayout,
     864                 :        488 :                              stageFlags[pass->params.type], 0,
     865                 :            :                              pass->params.push_constants_size,
     866                 :        488 :                              params->push_constants);
     867                 :            :     }
     868                 :            : 
     869   [ +  +  -  - ]:        536 :     switch (pass->params.type) {
     870                 :        470 :     case PL_PASS_RASTER: {
     871                 :        470 :         pl_tex tex = params->target;
     872                 :        470 :         struct pl_tex_vk *tex_vk = PL_PRIV(tex);
     873                 :        470 :         pl_buf vert = params->vertex_buf;
     874                 :        470 :         struct pl_buf_vk *vert_vk = PL_PRIV(vert);
     875                 :        470 :         pl_buf index = params->index_buf;
     876         [ +  + ]:        470 :         struct pl_buf_vk *index_vk = index ? PL_PRIV(index) : NULL;
     877         [ -  + ]:        470 :         pl_assert(vert);
     878                 :            : 
     879                 :            :         // In the edge case that vert = index buffer, we need to synchronize
     880                 :            :         // for both flags simultaneously
     881                 :            :         VkPipelineStageFlags2 vbo_stage = VK_PIPELINE_STAGE_2_VERTEX_ATTRIBUTE_INPUT_BIT;
     882                 :            :         VkAccessFlags2 vbo_flags = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT;
     883         [ -  + ]:        470 :         if (index == vert) {
     884                 :            :             vbo_stage |= VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT;
     885                 :            :             vbo_flags |= VK_ACCESS_2_INDEX_READ_BIT;
     886                 :            :         }
     887                 :            : 
     888                 :        470 :         vk_buf_barrier(gpu, cmd, vert, vbo_stage, vbo_flags, 0, vert->params.size, false);
     889                 :            : 
     890                 :        470 :         VkDeviceSize offset = vert_vk->mem.offset + params->buf_offset;
     891                 :        470 :         vk->CmdBindVertexBuffers(cmd->buf, 0, 1, &vert_vk->mem.buf, &offset);
     892                 :            : 
     893         [ +  + ]:        470 :         if (index) {
     894         [ +  - ]:          5 :             if (index != vert) {
     895                 :          5 :                 vk_buf_barrier(gpu, cmd, index, VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT,
     896                 :          5 :                                VK_ACCESS_2_INDEX_READ_BIT, 0, index->params.size,
     897                 :            :                                false);
     898                 :            :             }
     899                 :            : 
     900                 :            :             static const VkIndexType index_fmts[PL_INDEX_FORMAT_COUNT] = {
     901                 :            :                 [PL_INDEX_UINT16] = VK_INDEX_TYPE_UINT16,
     902                 :            :                 [PL_INDEX_UINT32] = VK_INDEX_TYPE_UINT32,
     903                 :            :             };
     904                 :            : 
     905                 :          5 :             vk->CmdBindIndexBuffer(cmd->buf, index_vk->mem.buf,
     906                 :          5 :                                    index_vk->mem.offset + params->index_offset,
     907                 :          5 :                                    index_fmts[params->index_fmt]);
     908                 :            :         }
     909                 :            : 
     910                 :            : 
     911                 :            :         VkAccessFlags2 fbo_access = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
     912         [ +  + ]:        470 :         if (pass->params.load_target)
     913                 :            :             fbo_access |= VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT;
     914                 :            : 
     915                 :        470 :         vk_tex_barrier(gpu, cmd, tex, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
     916                 :            :                        fbo_access, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     917                 :            :                        VK_QUEUE_FAMILY_IGNORED);
     918                 :            : 
     919                 :        470 :         VkViewport viewport = {
     920                 :        470 :             .x = params->viewport.x0,
     921                 :        470 :             .y = params->viewport.y0,
     922                 :        470 :             .width  = pl_rect_w(params->viewport),
     923                 :        470 :             .height = pl_rect_h(params->viewport),
     924                 :            :         };
     925                 :            : 
     926                 :        470 :         VkRect2D scissor = {
     927                 :        470 :             .offset = {params->scissors.x0, params->scissors.y0},
     928                 :        470 :             .extent = {pl_rect_w(params->scissors), pl_rect_h(params->scissors)},
     929                 :            :         };
     930                 :            : 
     931                 :        470 :         vk->CmdSetViewport(cmd->buf, 0, 1, &viewport);
     932                 :        470 :         vk->CmdSetScissor(cmd->buf, 0, 1, &scissor);
     933                 :            : 
     934                 :        470 :         VkRenderPassBeginInfo binfo = {
     935                 :            :             .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
     936                 :        470 :             .renderPass = pass_vk->renderPass,
     937                 :        470 :             .framebuffer = tex_vk->framebuffer,
     938                 :        470 :             .renderArea.extent = {tex->params.w, tex->params.h},
     939                 :            :         };
     940                 :            : 
     941                 :        470 :         vk->CmdBeginRenderPass(cmd->buf, &binfo, VK_SUBPASS_CONTENTS_INLINE);
     942                 :            : 
     943         [ +  + ]:        470 :         if (index) {
     944                 :          5 :             vk->CmdDrawIndexed(cmd->buf, params->vertex_count, 1, 0, 0, 0);
     945                 :            :         } else {
     946                 :        465 :             vk->CmdDraw(cmd->buf, params->vertex_count, 1, 0, 0);
     947                 :            :         }
     948                 :            : 
     949                 :        470 :         vk->CmdEndRenderPass(cmd->buf);
     950                 :            :         break;
     951                 :            :     }
     952                 :         66 :     case PL_PASS_COMPUTE:
     953                 :         66 :         vk->CmdDispatch(cmd->buf, params->compute_groups[0],
     954                 :         66 :                         params->compute_groups[1],
     955                 :         66 :                         params->compute_groups[2]);
     956                 :         66 :         break;
     957                 :            :     case PL_PASS_INVALID:
     958                 :            :     case PL_PASS_TYPE_COUNT:
     959                 :          0 :         pl_unreachable();
     960                 :            :     };
     961                 :            : 
     962         [ +  + ]:       1423 :     for (int i = 0; i < pass->params.num_descriptors; i++)
     963                 :        887 :         vk_release_descriptor(gpu, cmd, pass, params->desc_bindings[i], i);
     964                 :            : 
     965                 :            :     // submit this command buffer for better intra-frame granularity
     966                 :        536 :     CMD_SUBMIT(&cmd);
     967                 :            : 
     968                 :       1005 : error:
     969                 :            :     return;
     970                 :            : }

Generated by: LCOV version 1.16