OSDN Git Service

VKPipeline: Log any errors produced by the SPIR-V optimizer
[android-x86/external-swiftshader.git] / src / Vulkan / VkPipeline.cpp
1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "VkPipeline.hpp"
16 #include "VkPipelineLayout.hpp"
17 #include "VkShaderModule.hpp"
18 #include "Pipeline/SpirvShader.hpp"
19
20 #include "spirv-tools/optimizer.hpp"
21
22 namespace
23 {
24
25 sw::DrawType Convert(VkPrimitiveTopology topology)
26 {
27         switch(topology)
28         {
29         case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
30                 return sw::DRAW_POINTLIST;
31         case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
32                 return sw::DRAW_LINELIST;
33         case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
34                 return sw::DRAW_LINESTRIP;
35         case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
36                 return sw::DRAW_TRIANGLELIST;
37         case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
38                 return sw::DRAW_TRIANGLESTRIP;
39         case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
40                 return sw::DRAW_TRIANGLEFAN;
41         case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
42         case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
43         case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
44         case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
45                 // geometry shader specific
46                 ASSERT(false);
47                 break;
48         case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
49                 // tesselation shader specific
50                 ASSERT(false);
51                 break;
52         default:
53                 UNIMPLEMENTED();
54         }
55
56         return sw::DRAW_TRIANGLELIST;
57 }
58
59 sw::Rect Convert(const VkRect2D& rect)
60 {
61         return sw::Rect(rect.offset.x, rect.offset.y, rect.offset.x + rect.extent.width, rect.offset.y + rect.extent.height);
62 }
63
64 sw::StreamType getStreamType(VkFormat format)
65 {
66         switch(format)
67         {
68         case VK_FORMAT_R8_UNORM:
69         case VK_FORMAT_R8G8_UNORM:
70         case VK_FORMAT_R8G8B8A8_UNORM:
71         case VK_FORMAT_R8_UINT:
72         case VK_FORMAT_R8G8_UINT:
73         case VK_FORMAT_R8G8B8A8_UINT:
74         case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
75         case VK_FORMAT_A8B8G8R8_UINT_PACK32:
76                 return sw::STREAMTYPE_BYTE;
77         case VK_FORMAT_B8G8R8A8_UNORM:
78                 return sw::STREAMTYPE_COLOR;
79         case VK_FORMAT_R8_SNORM:
80         case VK_FORMAT_R8_SINT:
81         case VK_FORMAT_R8G8_SNORM:
82         case VK_FORMAT_R8G8_SINT:
83         case VK_FORMAT_R8G8B8A8_SNORM:
84         case VK_FORMAT_R8G8B8A8_SINT:
85         case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
86         case VK_FORMAT_A8B8G8R8_SINT_PACK32:
87                 return sw::STREAMTYPE_SBYTE;
88         case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
89                 return sw::STREAMTYPE_2_10_10_10_UINT;
90         case VK_FORMAT_R16_UNORM:
91         case VK_FORMAT_R16_UINT:
92         case VK_FORMAT_R16G16_UNORM:
93         case VK_FORMAT_R16G16_UINT:
94         case VK_FORMAT_R16G16B16A16_UNORM:
95         case VK_FORMAT_R16G16B16A16_UINT:
96                 return sw::STREAMTYPE_USHORT;
97         case VK_FORMAT_R16_SNORM:
98         case VK_FORMAT_R16_SINT:
99         case VK_FORMAT_R16G16_SNORM:
100         case VK_FORMAT_R16G16_SINT:
101         case VK_FORMAT_R16G16B16A16_SNORM:
102         case VK_FORMAT_R16G16B16A16_SINT:
103                 return sw::STREAMTYPE_SHORT;
104         case VK_FORMAT_R16_SFLOAT:
105         case VK_FORMAT_R16G16_SFLOAT:
106         case VK_FORMAT_R16G16B16A16_SFLOAT:
107                 return sw::STREAMTYPE_HALF;
108         case VK_FORMAT_R32_UINT:
109         case VK_FORMAT_R32G32_UINT:
110         case VK_FORMAT_R32G32B32_UINT:
111         case VK_FORMAT_R32G32B32A32_UINT:
112                 return sw::STREAMTYPE_UINT;
113         case VK_FORMAT_R32_SINT:
114         case VK_FORMAT_R32G32_SINT:
115         case VK_FORMAT_R32G32B32_SINT:
116         case VK_FORMAT_R32G32B32A32_SINT:
117                 return sw::STREAMTYPE_INT;
118         case VK_FORMAT_R32_SFLOAT:
119         case VK_FORMAT_R32G32_SFLOAT:
120         case VK_FORMAT_R32G32B32_SFLOAT:
121         case VK_FORMAT_R32G32B32A32_SFLOAT:
122                 return sw::STREAMTYPE_FLOAT;
123         default:
124                 UNIMPLEMENTED();
125         }
126
127         return sw::STREAMTYPE_BYTE;
128 }
129
130 uint32_t getNumberOfChannels(VkFormat format)
131 {
132         switch(format)
133         {
134         case VK_FORMAT_R8_UNORM:
135         case VK_FORMAT_R8_SNORM:
136         case VK_FORMAT_R8_UINT:
137         case VK_FORMAT_R8_SINT:
138         case VK_FORMAT_R16_UNORM:
139         case VK_FORMAT_R16_SNORM:
140         case VK_FORMAT_R16_UINT:
141         case VK_FORMAT_R16_SINT:
142         case VK_FORMAT_R16_SFLOAT:
143         case VK_FORMAT_R32_UINT:
144         case VK_FORMAT_R32_SINT:
145         case VK_FORMAT_R32_SFLOAT:
146                 return 1;
147         case VK_FORMAT_R8G8_UNORM:
148         case VK_FORMAT_R8G8_SNORM:
149         case VK_FORMAT_R8G8_UINT:
150         case VK_FORMAT_R8G8_SINT:
151         case VK_FORMAT_R16G16_UNORM:
152         case VK_FORMAT_R16G16_SNORM:
153         case VK_FORMAT_R16G16_UINT:
154         case VK_FORMAT_R16G16_SINT:
155         case VK_FORMAT_R16G16_SFLOAT:
156         case VK_FORMAT_R32G32_UINT:
157         case VK_FORMAT_R32G32_SINT:
158         case VK_FORMAT_R32G32_SFLOAT:
159                 return 2;
160         case VK_FORMAT_R32G32B32_UINT:
161         case VK_FORMAT_R32G32B32_SINT:
162         case VK_FORMAT_R32G32B32_SFLOAT:
163                 return 3;
164         case VK_FORMAT_R8G8B8A8_UNORM:
165         case VK_FORMAT_R8G8B8A8_SNORM:
166         case VK_FORMAT_R8G8B8A8_UINT:
167         case VK_FORMAT_R8G8B8A8_SINT:
168         case VK_FORMAT_B8G8R8A8_UNORM:
169         case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
170         case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
171         case VK_FORMAT_A8B8G8R8_UINT_PACK32:
172         case VK_FORMAT_A8B8G8R8_SINT_PACK32:
173         case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
174         case VK_FORMAT_R16G16B16A16_UNORM:
175         case VK_FORMAT_R16G16B16A16_SNORM:
176         case VK_FORMAT_R16G16B16A16_UINT:
177         case VK_FORMAT_R16G16B16A16_SINT:
178         case VK_FORMAT_R16G16B16A16_SFLOAT:
179         case VK_FORMAT_R32G32B32A32_UINT:
180         case VK_FORMAT_R32G32B32A32_SINT:
181         case VK_FORMAT_R32G32B32A32_SFLOAT:
182                 return 4;
183         default:
184                 UNIMPLEMENTED();
185         }
186
187         return 0;
188 }
189
190 // preprocessSpirv applies and freezes specializations into constants, inlines
191 // all functions and performs constant folding.
192 std::vector<uint32_t> preprocessSpirv(
193                 std::vector<uint32_t> const &code,
194                 VkSpecializationInfo const *specializationInfo)
195 {
196         spvtools::Optimizer opt{SPV_ENV_VULKAN_1_1};
197
198         opt.SetMessageConsumer([](spv_message_level_t level, const char*, const spv_position_t& p, const char* m) {
199                 switch (level)
200                 {
201                 case SPV_MSG_FATAL:
202                 case SPV_MSG_INTERNAL_ERROR:
203                 case SPV_MSG_ERROR:
204                         ERR("%d:%d %s", p.line, p.column, m);
205                         break;
206                 case SPV_MSG_WARNING:
207                 case SPV_MSG_INFO:
208                 case SPV_MSG_DEBUG:
209                         TRACE("%d:%d %s", p.line, p.column, m);
210                         break;
211                 }
212         });
213
214         opt.RegisterPass(spvtools::CreateInlineExhaustivePass());
215
216         // If the pipeline uses specialization, apply the specializations before freezing
217         if (specializationInfo)
218         {
219                 std::unordered_map<uint32_t, std::vector<uint32_t>> specializations;
220                 for (auto i = 0u; i < specializationInfo->mapEntryCount; ++i)
221                 {
222                         auto const &e = specializationInfo->pMapEntries[i];
223                         auto value_ptr =
224                                         static_cast<uint32_t const *>(specializationInfo->pData) + e.offset / sizeof(uint32_t);
225                         specializations.emplace(e.constantID,
226                                                                         std::vector<uint32_t>{value_ptr, value_ptr + e.size / sizeof(uint32_t)});
227                 }
228                 opt.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(specializations));
229         }
230         // Freeze specialization constants into normal constants, and propagate through
231         opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass());
232         opt.RegisterPass(spvtools::CreateFoldSpecConstantOpAndCompositePass());
233
234         std::vector<uint32_t> optimized;
235         opt.Run(code.data(), code.size(), &optimized);
236         return optimized;
237 }
238
239 } // anonymous namespace
240
241 namespace vk
242 {
243
244 Pipeline::Pipeline(PipelineLayout const *layout) : layout(layout) {}
245
246 GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo* pCreateInfo, void* mem)
247         : Pipeline(Cast(pCreateInfo->layout))
248 {
249         if((pCreateInfo->flags != 0) ||
250            (pCreateInfo->stageCount != 2) ||
251            (pCreateInfo->pTessellationState != nullptr) ||
252            (pCreateInfo->pDynamicState != nullptr) ||
253            (pCreateInfo->subpass != 0) ||
254            (pCreateInfo->basePipelineHandle != VK_NULL_HANDLE) ||
255            (pCreateInfo->basePipelineIndex != 0))
256         {
257                 UNIMPLEMENTED();
258         }
259
260         const VkPipelineShaderStageCreateInfo& vertexStage = pCreateInfo->pStages[0];
261         if((vertexStage.stage != VK_SHADER_STAGE_VERTEX_BIT) ||
262            (vertexStage.flags != 0) ||
263            !((vertexStage.pSpecializationInfo == nullptr) ||
264              ((vertexStage.pSpecializationInfo->mapEntryCount == 0) &&
265               (vertexStage.pSpecializationInfo->dataSize == 0))))
266         {
267                 UNIMPLEMENTED();
268         }
269
270         const VkPipelineShaderStageCreateInfo& fragmentStage = pCreateInfo->pStages[1];
271         if((fragmentStage.stage != VK_SHADER_STAGE_FRAGMENT_BIT) ||
272            (fragmentStage.flags != 0) ||
273            !((fragmentStage.pSpecializationInfo == nullptr) ||
274              ((fragmentStage.pSpecializationInfo->mapEntryCount == 0) &&
275               (fragmentStage.pSpecializationInfo->dataSize == 0))))
276         {
277                 UNIMPLEMENTED();
278         }
279
280         const VkPipelineVertexInputStateCreateInfo* vertexInputState = pCreateInfo->pVertexInputState;
281         if(vertexInputState->flags != 0)
282         {
283                 UNIMPLEMENTED();
284         }
285
286         // Context must always have a PipelineLayout set.
287         context.pipelineLayout = layout;
288
289         // Temporary in-binding-order representation of buffer strides, to be consumed below
290         // when considering attributes. TODO: unfuse buffers from attributes in backend, is old GL model.
291         uint32_t bufferStrides[MAX_VERTEX_INPUT_BINDINGS];
292         for(uint32_t i = 0; i < vertexInputState->vertexBindingDescriptionCount; i++)
293         {
294                 auto const & desc = vertexInputState->pVertexBindingDescriptions[i];
295                 bufferStrides[desc.binding] = desc.stride;
296                 if(desc.inputRate != VK_VERTEX_INPUT_RATE_VERTEX)
297                 {
298                         UNIMPLEMENTED();
299                 }
300         }
301
302         for(uint32_t i = 0; i < vertexInputState->vertexAttributeDescriptionCount; i++)
303         {
304                 auto const & desc = vertexInputState->pVertexAttributeDescriptions[i];
305                 sw::Stream& input = context.input[desc.location];
306                 input.count = getNumberOfChannels(desc.format);
307                 input.type = getStreamType(desc.format);
308                 input.normalized = !sw::Surface::isNonNormalizedInteger(desc.format);
309                 input.offset = desc.offset;
310                 input.binding = desc.binding;
311                 input.stride = bufferStrides[desc.binding];
312         }
313
314         const VkPipelineInputAssemblyStateCreateInfo* assemblyState = pCreateInfo->pInputAssemblyState;
315         if((assemblyState->flags != 0) ||
316            (assemblyState->primitiveRestartEnable != 0))
317         {
318                 UNIMPLEMENTED();
319         }
320
321         context.drawType = Convert(assemblyState->topology);
322
323         const VkPipelineViewportStateCreateInfo* viewportState = pCreateInfo->pViewportState;
324         if(viewportState)
325         {
326                 if((viewportState->flags != 0) ||
327                         (viewportState->viewportCount != 1) ||
328                         (viewportState->scissorCount != 1))
329                 {
330                         UNIMPLEMENTED();
331                 }
332
333                 scissor = Convert(viewportState->pScissors[0]);
334                 viewport = viewportState->pViewports[0];
335         }
336
337         const VkPipelineRasterizationStateCreateInfo* rasterizationState = pCreateInfo->pRasterizationState;
338         if((rasterizationState->flags != 0) ||
339            (rasterizationState->depthClampEnable != 0) ||
340            (rasterizationState->polygonMode != VK_POLYGON_MODE_FILL))
341         {
342                 UNIMPLEMENTED();
343         }
344
345         context.rasterizerDiscard = rasterizationState->rasterizerDiscardEnable;
346         context.cullMode = rasterizationState->cullMode;
347         context.frontFacingCCW = rasterizationState->frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE;
348         context.depthBias = (rasterizationState->depthBiasEnable ? rasterizationState->depthBiasConstantFactor : 0.0f);
349         context.slopeDepthBias = (rasterizationState->depthBiasEnable ? rasterizationState->depthBiasSlopeFactor : 0.0f);
350
351         const VkPipelineMultisampleStateCreateInfo* multisampleState = pCreateInfo->pMultisampleState;
352         if(multisampleState)
353         {
354                 switch (multisampleState->rasterizationSamples) {
355                 case VK_SAMPLE_COUNT_1_BIT:
356                         context.sampleCount = 1;
357                         break;
358                 case VK_SAMPLE_COUNT_4_BIT:
359                         context.sampleCount = 4;
360                         break;
361                 default:
362                         UNIMPLEMENTED("Unsupported sample count");
363                 }
364
365                 if((multisampleState->flags != 0) ||
366                         (multisampleState->sampleShadingEnable != 0) ||
367                         !((multisampleState->pSampleMask == nullptr) ||
368                         (*(multisampleState->pSampleMask) == 0xFFFFFFFFu)) ||
369                                 (multisampleState->alphaToCoverageEnable != 0) ||
370                         (multisampleState->alphaToOneEnable != 0))
371                 {
372                         UNIMPLEMENTED();
373                 }
374         }
375         else
376         {
377                 context.sampleCount = 1;
378         }
379
380         const VkPipelineDepthStencilStateCreateInfo* depthStencilState = pCreateInfo->pDepthStencilState;
381         if(depthStencilState)
382         {
383                 if((depthStencilState->flags != 0) ||
384                    (depthStencilState->depthBoundsTestEnable != 0) ||
385                    (depthStencilState->minDepthBounds != 0.0f) ||
386                    (depthStencilState->maxDepthBounds != 1.0f))
387                 {
388                         UNIMPLEMENTED();
389                 }
390
391                 context.depthBufferEnable = depthStencilState->depthTestEnable;
392                 context.depthWriteEnable = depthStencilState->depthWriteEnable;
393                 context.depthCompareMode = depthStencilState->depthCompareOp;
394
395                 context.stencilEnable = context.twoSidedStencil = depthStencilState->stencilTestEnable;
396                 if(context.stencilEnable)
397                 {
398                         context.stencilMask = depthStencilState->front.compareMask;
399                         context.stencilCompareMode = depthStencilState->front.compareOp;
400                         context.stencilZFailOperation = depthStencilState->front.depthFailOp;
401                         context.stencilFailOperation = depthStencilState->front.failOp;
402                         context.stencilPassOperation = depthStencilState->front.passOp;
403                         context.stencilReference = depthStencilState->front.reference;
404                         context.stencilWriteMask = depthStencilState->front.writeMask;
405
406                         context.stencilMaskCCW = depthStencilState->back.compareMask;
407                         context.stencilCompareModeCCW = depthStencilState->back.compareOp;
408                         context.stencilZFailOperationCCW = depthStencilState->back.depthFailOp;
409                         context.stencilFailOperationCCW = depthStencilState->back.failOp;
410                         context.stencilPassOperationCCW = depthStencilState->back.passOp;
411                         context.stencilReferenceCCW = depthStencilState->back.reference;
412                         context.stencilWriteMaskCCW = depthStencilState->back.writeMask;
413                 }
414         }
415
416         const VkPipelineColorBlendStateCreateInfo* colorBlendState = pCreateInfo->pColorBlendState;
417         if(colorBlendState)
418         {
419                 if((colorBlendState->flags != 0) ||
420                    ((colorBlendState->logicOpEnable != 0) &&
421                         (colorBlendState->attachmentCount > 1)))
422                 {
423                         UNIMPLEMENTED();
424                 }
425
426                 blendConstants.r = colorBlendState->blendConstants[0];
427                 blendConstants.g = colorBlendState->blendConstants[1];
428                 blendConstants.b = colorBlendState->blendConstants[2];
429                 blendConstants.a = colorBlendState->blendConstants[3];
430
431                 if(colorBlendState->attachmentCount == 1)
432                 {
433                         const VkPipelineColorBlendAttachmentState& attachment = colorBlendState->pAttachments[0];
434                         if(attachment.colorWriteMask != (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT))
435                         {
436                                 UNIMPLEMENTED();
437                         }
438
439                         context.alphaBlendEnable = attachment.blendEnable;
440                         context.separateAlphaBlendEnable = (attachment.alphaBlendOp != attachment.colorBlendOp) ||
441                                                                                            (attachment.dstAlphaBlendFactor != attachment.dstColorBlendFactor) ||
442                                                                                            (attachment.srcAlphaBlendFactor != attachment.srcColorBlendFactor);
443                         context.blendOperationStateAlpha = attachment.alphaBlendOp;
444                         context.blendOperationState = attachment.colorBlendOp;
445                         context.destBlendFactorStateAlpha = attachment.dstAlphaBlendFactor;
446                         context.destBlendFactorState = attachment.dstColorBlendFactor;
447                         context.sourceBlendFactorStateAlpha = attachment.srcAlphaBlendFactor;
448                         context.sourceBlendFactorState = attachment.srcColorBlendFactor;
449                 }
450         }
451 }
452
453 void GraphicsPipeline::destroyPipeline(const VkAllocationCallbacks* pAllocator)
454 {
455         delete vertexShader;
456         delete fragmentShader;
457 }
458
459 size_t GraphicsPipeline::ComputeRequiredAllocationSize(const VkGraphicsPipelineCreateInfo* pCreateInfo)
460 {
461         return 0;
462 }
463
464 void GraphicsPipeline::compileShaders(const VkAllocationCallbacks* pAllocator, const VkGraphicsPipelineCreateInfo* pCreateInfo)
465 {
466         for (auto pStage = pCreateInfo->pStages; pStage != pCreateInfo->pStages + pCreateInfo->stageCount; pStage++)
467         {
468                 auto module = Cast(pStage->module);
469
470                 auto code = preprocessSpirv(module->getCode(), pStage->pSpecializationInfo);
471
472                 // TODO: also pass in any pipeline state which will affect shader compilation
473                 auto spirvShader = new sw::SpirvShader{code};
474
475                 switch (pStage->stage)
476                 {
477                 case VK_SHADER_STAGE_VERTEX_BIT:
478                         context.vertexShader = vertexShader = spirvShader;
479                         break;
480
481                 case VK_SHADER_STAGE_FRAGMENT_BIT:
482                         context.pixelShader = fragmentShader = spirvShader;
483                         break;
484
485                 default:
486                         UNIMPLEMENTED("Unsupported stage");
487                 }
488         }
489 }
490
491 uint32_t GraphicsPipeline::computePrimitiveCount(uint32_t vertexCount) const
492 {
493         switch(context.drawType)
494         {
495         case sw::DRAW_POINTLIST:
496                 return vertexCount;
497         case sw::DRAW_LINELIST:
498                 return vertexCount / 2;
499         case sw::DRAW_LINESTRIP:
500                 return vertexCount - 1;
501         case sw::DRAW_TRIANGLELIST:
502                 return vertexCount / 3;
503         case sw::DRAW_TRIANGLESTRIP:
504                 return vertexCount - 2;
505         case sw::DRAW_TRIANGLEFAN:
506                 return vertexCount - 2;
507         default:
508                 UNIMPLEMENTED();
509         }
510
511         return 0;
512 }
513
514 const sw::Context& GraphicsPipeline::getContext() const
515 {
516         return context;
517 }
518
519 const sw::Rect& GraphicsPipeline::getScissor() const
520 {
521         return scissor;
522 }
523
524 const VkViewport& GraphicsPipeline::getViewport() const
525 {
526         return viewport;
527 }
528
529 const sw::Color<float>& GraphicsPipeline::getBlendConstants() const
530 {
531         return blendConstants;
532 }
533
534 ComputePipeline::ComputePipeline(const VkComputePipelineCreateInfo* pCreateInfo, void* mem)
535         : Pipeline(Cast(pCreateInfo->layout))
536 {
537 }
538
539 void ComputePipeline::destroyPipeline(const VkAllocationCallbacks* pAllocator)
540 {
541 }
542
543 size_t ComputePipeline::ComputeRequiredAllocationSize(const VkComputePipelineCreateInfo* pCreateInfo)
544 {
545         return 0;
546 }
547
548 } // namespace vk