blob: a15a0ee1cfd97ac67b3fca3169e36a013260dbbc [file] [log] [blame]
Chris Forbesaab9d112015-04-02 13:22:31 +13001/*
2 * Vulkan
3 *
4 * Copyright (C) 2015 LunarG, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24#include <string.h>
25#include <stdlib.h>
26#include <assert.h>
Chris Forbes67cc36f2015-04-13 12:14:52 +120027#include <map>
Chris Forbesaab9d112015-04-02 13:22:31 +130028#include <unordered_map>
Chris Forbesbb164b62015-04-08 10:19:16 +120029#include <map>
Chris Forbes4396ff52015-04-08 10:11:59 +120030#include <vector>
Chris Forbesaab9d112015-04-02 13:22:31 +130031#include "loader_platform.h"
32#include "vk_dispatch_table_helper.h"
33#include "vkLayer.h"
34// The following is #included again to catch certain OS-specific functions
35// being used:
36#include "loader_platform.h"
37
38#include "SPIRV/spirv.h"
39
40static std::unordered_map<void *, VkLayerDispatchTable *> tableMap;
41
Chris Forbes4396ff52015-04-08 10:11:59 +120042
43struct shader_source {
44 std::vector<uint32_t> words;
45
46 shader_source(VkShaderCreateInfo const *pCreateInfo) :
47 words((uint32_t *)pCreateInfo->pCode, (uint32_t *)pCreateInfo->pCode + pCreateInfo->codeSize / sizeof(uint32_t)) {
48 }
49};
50
51
52static std::unordered_map<void *, shader_source *> shader_map;
53
54
Chris Forbesaab9d112015-04-02 13:22:31 +130055static VkLayerDispatchTable * initLayerTable(const VkBaseLayerObject *gpuw)
56{
57 VkLayerDispatchTable *pTable;
58
59 assert(gpuw);
60 std::unordered_map<void *, VkLayerDispatchTable *>::const_iterator it = tableMap.find((void *) gpuw->baseObject);
61 if (it == tableMap.end())
62 {
63 pTable = new VkLayerDispatchTable;
64 tableMap[(void *) gpuw->baseObject] = pTable;
65 } else
66 {
67 return it->second;
68 }
69
70 layer_initialize_dispatch_table(pTable, gpuw->pGPA, (VkPhysicalGpu) gpuw->nextObject);
71
72 return pTable;
73}
74
75
76VK_LAYER_EXPORT VkResult VKAPI vkCreateDevice(VkPhysicalGpu gpu, const VkDeviceCreateInfo* pCreateInfo, VkDevice* pDevice)
77{
78 VkLayerDispatchTable* pTable = tableMap[gpu];
79 VkResult result = pTable->CreateDevice(gpu, pCreateInfo, pDevice);
80 // create a mapping for the device object into the dispatch table
81 tableMap.emplace(*pDevice, pTable);
82 return result;
83}
84
85
86VK_LAYER_EXPORT VkResult VKAPI vkEnumerateLayers(VkPhysicalGpu gpu, size_t maxLayerCount, size_t maxStringSize, size_t* pOutLayerCount, char* const* pOutLayers, void* pReserved)
87{
88 if (pOutLayerCount == NULL || pOutLayers == NULL || pOutLayers[0] == NULL || pOutLayers[1] == NULL || pReserved == NULL)
89 return VK_ERROR_INVALID_POINTER;
90
91 if (maxLayerCount < 1)
92 return VK_ERROR_INITIALIZATION_FAILED;
93 *pOutLayerCount = 1;
94 strncpy((char *) pOutLayers[0], "ShaderChecker", maxStringSize);
95 return VK_SUCCESS;
96}
97
98
99struct extProps {
100 uint32_t version;
101 const char * const name;
102};
103#define SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE 1
104static const struct extProps shaderCheckerExts[SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE] = {
105 // TODO what is the version?
106 0x10, "ShaderChecker",
107};
108
109
110VK_LAYER_EXPORT VkResult VKAPI vkGetGlobalExtensionInfo(
111 VkExtensionInfoType infoType,
112 uint32_t extensionIndex,
113 size_t* pDataSize,
114 void* pData)
115{
116 VkResult result;
117
118 /* This entrypoint is NOT going to init it's own dispatch table since loader calls here early */
119 VkExtensionProperties *ext_props;
120 uint32_t *count;
121
122 if (pDataSize == NULL)
123 return VK_ERROR_INVALID_POINTER;
124
125 switch (infoType) {
126 case VK_EXTENSION_INFO_TYPE_COUNT:
127 *pDataSize = sizeof(uint32_t);
128 if (pData == NULL)
129 return VK_SUCCESS;
130 count = (uint32_t *) pData;
131 *count = SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE;
132 break;
133 case VK_EXTENSION_INFO_TYPE_PROPERTIES:
134 *pDataSize = sizeof(VkExtensionProperties);
135 if (pData == NULL)
136 return VK_SUCCESS;
137 if (extensionIndex >= SHADER_CHECKER_LAYER_EXT_ARRAY_SIZE)
138 return VK_ERROR_INVALID_VALUE;
139 ext_props = (VkExtensionProperties *) pData;
140 ext_props->version = shaderCheckerExts[extensionIndex].version;
141 strncpy(ext_props->extName, shaderCheckerExts[extensionIndex].name,
142 VK_MAX_EXTENSION_NAME);
143 ext_props->extName[VK_MAX_EXTENSION_NAME - 1] = '\0';
144 break;
145 default:
146 return VK_ERROR_INVALID_VALUE;
147 };
148
149 return VK_SUCCESS;
150}
151
152
Chris Forbes67cc36f2015-04-13 12:14:52 +1200153static int
154value_or_default(std::unordered_map<unsigned, unsigned> const &map, unsigned id, int def)
155{
156 auto it = map.find(id);
157 if (it == map.end())
158 return def;
159 else
160 return it->second;
161}
162
163
164struct interface_var {
165 uint32_t id;
166 uint32_t type_id;
167 /* TODO: collect the name, too? Isn't required to be present. */
168};
169
170
171static void
172collect_interface_by_location(shader_source const *src, spv::StorageClass interface,
173 std::map<uint32_t, interface_var> &out,
174 std::map<uint32_t, interface_var> &builtins_out)
175{
176 unsigned int const *code = (unsigned int const *)&src->words[0];
177 size_t size = src->words.size();
178
179 if (code[0] != spv::MagicNumber) {
180 printf("Invalid magic.\n");
181 return;
182 }
183
184 std::unordered_map<unsigned, unsigned> var_locations;
185 std::unordered_map<unsigned, unsigned> var_builtins;
186
187 unsigned word = 5;
188 while (word < size) {
189
190 unsigned opcode = code[word] & 0x0ffffu;
191 unsigned oplen = (code[word] & 0xffff0000u) >> 16;
192
193 /* We consider two interface models: SSO rendezvous-by-location, and
194 * builtins. Complain about anything that fits neither model.
195 */
196 if (opcode == spv::OpDecorate) {
197 if (code[word+2] == spv::DecLocation) {
198 var_locations[code[word+1]] = code[word+3];
199 }
200
201 if (code[word+2] == spv::DecBuiltIn) {
202 var_builtins[code[word+1]] = code[word+3];
203 }
204 }
205
206 /* TODO: handle grouped decorations */
207 /* TODO: handle index=1 dual source outputs from FS -- two vars will
208 * have the same location, and we DONT want to clobber. */
209
210 if (opcode == spv::OpVariable && code[word+3] == interface) {
211 int location = value_or_default(var_locations, code[word+2], -1);
212 int builtin = value_or_default(var_builtins, code[word+2], -1);
213
214 if (location == -1 && builtin == -1) {
215 /* No location defined, and not bound to an API builtin.
216 * The spec says nothing about how this case works (or doesn't)
217 * for interface matching.
218 */
219 printf("WARN: var %d (type %d) in %s interface has no Location or Builtin decoration\n",
220 code[word+2], code[word+1], interface == spv::StorageInput ? "input" : "output");
221 }
222 else if (location != -1) {
223 /* A user-defined interface variable, with a location. */
224 interface_var v;
225 v.id = code[word+2];
226 v.type_id = code[word+1];
227 out[location] = v;
228 }
229 else {
230 /* A builtin interface variable */
231 interface_var v;
232 v.id = code[word+2];
233 v.type_id = code[word+1];
234 builtins_out[builtin] = v;
235 }
236 }
237
238 word += oplen;
239 }
240}
241
242
Chris Forbesaab9d112015-04-02 13:22:31 +1300243VK_LAYER_EXPORT VkResult VKAPI vkCreateShader(VkDevice device, const VkShaderCreateInfo *pCreateInfo,
244 VkShader *pShader)
245{
246 VkLayerDispatchTable* pTable = tableMap[(VkBaseLayerObject *)device];
247 VkResult res = pTable->CreateShader(device, pCreateInfo, pShader);
Chris Forbes4396ff52015-04-08 10:11:59 +1200248
249 shader_map[(VkBaseLayerObject *) *pShader] = new shader_source(pCreateInfo);
Chris Forbesaab9d112015-04-02 13:22:31 +1300250 return res;
251}
252
253
Chris Forbesbb164b62015-04-08 10:19:16 +1200254static void
255validate_interface_between_stages(shader_source const *producer, char const *producer_name,
256 shader_source const *consumer, char const *consumer_name)
257{
258 std::map<uint32_t, interface_var> outputs;
259 std::map<uint32_t, interface_var> inputs;
260
261 std::map<uint32_t, interface_var> builtin_outputs;
262 std::map<uint32_t, interface_var> builtin_inputs;
263
264 printf("Begin validate_interface_between_stages %s -> %s\n",
265 producer_name, consumer_name);
266
267 collect_interface_by_location(producer, spv::StorageOutput, outputs, builtin_outputs);
268 collect_interface_by_location(consumer, spv::StorageInput, inputs, builtin_inputs);
269
270 auto a_it = outputs.begin();
271 auto b_it = inputs.begin();
272
273 /* maps sorted by key (location); walk them together to find mismatches */
274 while (a_it != outputs.end() || b_it != inputs.end()) {
275 if (b_it == inputs.end() || a_it->first < b_it->first) {
276 printf(" WARN: %s writes to output location %d which is not consumed by %s\n",
277 producer_name, a_it->first, consumer_name);
278 a_it++;
279 }
280 else if (a_it == outputs.end() || a_it->first > b_it->first) {
281 printf(" ERR: %s consumes input location %d which is not written by %s\n",
282 consumer_name, b_it->first, producer_name);
283 b_it++;
284 }
285 else {
286 printf(" OK: match on location %d\n",
287 a_it->first);
288 /* TODO: typecheck */
289 a_it++;
290 b_it++;
291 }
292 }
293
294 printf("End validate_interface_between_stages\n");
295}
296
297
Chris Forbes9b9f5fe2015-04-08 10:37:20 +1200298enum FORMAT_TYPE {
299 FORMAT_TYPE_UNDEFINED,
300 FORMAT_TYPE_FLOAT, /* UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader */
301 FORMAT_TYPE_SINT,
302 FORMAT_TYPE_UINT,
303};
304
305
306static unsigned
307get_format_type(VkFormat fmt) {
308 switch (fmt) {
309 case VK_FMT_UNDEFINED:
310 return FORMAT_TYPE_UNDEFINED;
311 case VK_FMT_R8_SINT:
312 case VK_FMT_R8G8_SINT:
313 case VK_FMT_R8G8B8_SINT:
314 case VK_FMT_R8G8B8A8_SINT:
315 case VK_FMT_R16_SINT:
316 case VK_FMT_R16G16_SINT:
317 case VK_FMT_R16G16B16_SINT:
318 case VK_FMT_R16G16B16A16_SINT:
319 case VK_FMT_R32_SINT:
320 case VK_FMT_R32G32_SINT:
321 case VK_FMT_R32G32B32_SINT:
322 case VK_FMT_R32G32B32A32_SINT:
323 case VK_FMT_B8G8R8_SINT:
324 case VK_FMT_B8G8R8A8_SINT:
325 case VK_FMT_R10G10B10A2_SINT:
326 case VK_FMT_B10G10R10A2_SINT:
327 return FORMAT_TYPE_SINT;
328 case VK_FMT_R8_UINT:
329 case VK_FMT_R8G8_UINT:
330 case VK_FMT_R8G8B8_UINT:
331 case VK_FMT_R8G8B8A8_UINT:
332 case VK_FMT_R16_UINT:
333 case VK_FMT_R16G16_UINT:
334 case VK_FMT_R16G16B16_UINT:
335 case VK_FMT_R16G16B16A16_UINT:
336 case VK_FMT_R32_UINT:
337 case VK_FMT_R32G32_UINT:
338 case VK_FMT_R32G32B32_UINT:
339 case VK_FMT_R32G32B32A32_UINT:
340 case VK_FMT_B8G8R8_UINT:
341 case VK_FMT_B8G8R8A8_UINT:
342 case VK_FMT_R10G10B10A2_UINT:
343 case VK_FMT_B10G10R10A2_UINT:
344 return FORMAT_TYPE_UINT;
345 default:
346 return FORMAT_TYPE_FLOAT;
347 }
348}
349
350
Chris Forbesfcd05f12015-04-08 10:36:37 +1200351static void
352validate_vi_against_vs_inputs(VkPipelineVertexInputCreateInfo const *vi, shader_source const *vs)
353{
354 std::map<uint32_t, interface_var> inputs;
355 /* we collect builtin inputs, but they will never appear in the VI state --
356 * the vs builtin inputs are generated in the pipeline, not sourced from buffers (VertexID, etc)
357 */
358 std::map<uint32_t, interface_var> builtin_inputs;
359
360 printf("Begin validate_vi_against_vs_inputs\n");
361
362 collect_interface_by_location(vs, spv::StorageInput, inputs, builtin_inputs);
363
364 /* Build index by location */
365 std::map<uint32_t, VkVertexInputAttributeDescription const *> attribs;
366 for (int i = 0; i < vi->attributeCount; i++)
367 attribs[vi->pVertexAttributeDescriptions[i].location] = &vi->pVertexAttributeDescriptions[i];
368
369 auto it_a = attribs.begin();
370 auto it_b = inputs.begin();
371
372 while (it_a != attribs.end() || it_b != inputs.end()) {
373 if (it_b == inputs.end() || it_a->first < it_b->first) {
374 printf(" WARN: attribute at location %d not consumed by the vertex shader\n",
375 it_a->first);
376 it_a++;
377 }
378 else if (it_a == attribs.end() || it_b->first < it_a->first) {
379 printf(" ERR: vertex shader consumes input at location %d but not provided\n",
380 it_b->first);
381 it_b++;
382 }
383 else {
384 /* TODO: type check */
385 printf(" OK: match on attribute location %d\n",
386 it_a->first);
387 it_a++;
388 it_b++;
389 }
390 }
391
392 printf("End validate_vi_against_vs_inputs\n");
393}
394
395
Chris Forbes9b9f5fe2015-04-08 10:37:20 +1200396static void
397validate_fs_outputs_against_cb(shader_source const *fs, VkPipelineCbStateCreateInfo const *cb)
398{
399 std::map<uint32_t, interface_var> outputs;
400 std::map<uint32_t, interface_var> builtin_outputs;
401
402 printf("Begin validate_fs_outputs_against_cb\n");
403
404 /* TODO: dual source blend index (spv::DecIndex, zero if not provided) */
405
406 collect_interface_by_location(fs, spv::StorageOutput, outputs, builtin_outputs);
407
408 /* Check for legacy gl_FragColor broadcast: In this case, we should have no user-defined outputs,
409 * and all color attachment should be UNORM/SNORM/FLOAT.
410 */
411 if (builtin_outputs.find(spv::BuiltInFragColor) != builtin_outputs.end()) {
412 bool broadcast_err = false;
413 if (outputs.size()) {
414 printf(" ERR: should not have user-defined FS outputs when using broadcast\n");
415 broadcast_err = true;
416 }
417
418 for (int i = 0; i < cb->attachmentCount; i++) {
419 unsigned attachmentType = get_format_type(cb->pAttachments[i].format);
420 if (attachmentType == FORMAT_TYPE_SINT || attachmentType == FORMAT_TYPE_UINT) {
421 printf(" ERR: CB fomat should not be SINT or UINT when using broadcast\n");
422 broadcast_err = true;
423 }
424 }
425
426 if (!broadcast_err)
427 printf(" OK: FS broadcast to all color attachments\n");
428
429 /* Skip the usual matching -- all attachments are considered written to. */
430 printf("End validate_fs_outputs_against_cb\n");
431 return;
432 }
433
434 auto it = outputs.begin();
435 uint32_t attachment = 0;
436
437 /* Walk attachment list and outputs together -- this is a little overpowered since attachments
438 * are currently dense, but the parallel with matching between shader stages is nice.
439 */
440
441 while (it != outputs.end() || attachment < cb->attachmentCount) {
442 if (attachment == cb->attachmentCount || it->first < attachment) {
443 printf(" ERR: fragment shader writes to output location %d with no matching attachment\n",
444 it->first);
445 it++;
446 }
447 else if (it == outputs.end() || it->first > attachment) {
448 printf(" ERR: attachment %d not written by fragment shader\n",
449 attachment);
450 attachment++;
451 }
452 else {
453 printf(" OK: match on attachment index %d\n",
454 it->first);
455 /* TODO: typecheck */
456 it++;
457 attachment++;
458 }
459 }
460
461 printf("End validate_fs_outputs_against_cb\n");
462}
463
464
Chris Forbes60540932015-04-08 10:15:35 +1200465VK_LAYER_EXPORT VkResult VKAPI vkCreateGraphicsPipeline(VkDevice device,
466 const VkGraphicsPipelineCreateInfo *pCreateInfo,
467 VkPipeline *pPipeline)
468{
469 /* TODO: run cross-stage validation */
Chris Forbes60540932015-04-08 10:15:35 +1200470 /* - Support GS, TCS, TES stages */
471
Chris Forbes8f600932015-04-08 10:16:45 +1200472 /* We seem to allow pipeline stages to be specified out of order, so collect and identify them
473 * before trying to do anything more: */
474
475 shader_source const *vs_source = 0;
476 shader_source const *fs_source = 0;
477 VkPipelineCbStateCreateInfo const *cb = 0;
478 VkPipelineVertexInputCreateInfo const *vi = 0;
479
480 for (auto stage = pCreateInfo; stage; stage = (decltype(stage))stage->pNext) {
481 if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO) {
482 auto shader_stage = (VkPipelineShaderStageCreateInfo const *)stage;
483
484 if (shader_stage->shader.stage == VK_SHADER_STAGE_VERTEX)
485 vs_source = shader_map[(void *)(shader_stage->shader.shader)];
486 else if (shader_stage->shader.stage == VK_SHADER_STAGE_FRAGMENT)
487 fs_source = shader_map[(void *)(shader_stage->shader.shader)];
488 else
489 printf("Unknown shader stage %d\n", shader_stage->shader.stage);
490 }
491 else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO) {
492 cb = (VkPipelineCbStateCreateInfo const *)stage;
493 }
494 else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO) {
495 vi = (VkPipelineVertexInputCreateInfo const *)stage;
496 }
497 }
498
499 printf("Pipeline: vi=%p vs=%p fs=%p cb=%p\n", vi, vs_source, fs_source, cb);
500
Chris Forbesfcd05f12015-04-08 10:36:37 +1200501 if (vi && vs_source) {
502 validate_vi_against_vs_inputs(vi, vs_source);
503 }
504
Chris Forbesbb164b62015-04-08 10:19:16 +1200505 if (vs_source && fs_source) {
506 validate_interface_between_stages(vs_source, "vertex shader",
507 fs_source, "fragment shader");
508 }
509
Chris Forbes9b9f5fe2015-04-08 10:37:20 +1200510 if (fs_source && cb) {
511 validate_fs_outputs_against_cb(fs_source, cb);
512 }
513
Chris Forbes60540932015-04-08 10:15:35 +1200514 VkLayerDispatchTable *pTable = tableMap[(VkBaseLayerObject *)device];
515 VkResult res = pTable->CreateGraphicsPipeline(device, pCreateInfo, pPipeline);
516 return res;
517}
518
519
Chris Forbesaab9d112015-04-02 13:22:31 +1300520VK_LAYER_EXPORT void * VKAPI vkGetProcAddr(VkPhysicalGpu gpu, const char* pName)
521{
522 if (gpu == NULL)
523 return NULL;
524
525 initLayerTable((const VkBaseLayerObject *) gpu);
526
527#define ADD_HOOK(fn) \
528 if (!strncmp(#fn, pName, sizeof(#fn))) \
529 return (void *) fn
530
531 ADD_HOOK(vkGetProcAddr);
532 ADD_HOOK(vkEnumerateLayers);
533 ADD_HOOK(vkCreateDevice);
534 ADD_HOOK(vkCreateShader);
Chris Forbes60540932015-04-08 10:15:35 +1200535 ADD_HOOK(vkCreateGraphicsPipeline);
Chris Forbesaab9d112015-04-02 13:22:31 +1300536
537 VkBaseLayerObject* gpuw = (VkBaseLayerObject *) gpu;
538 if (gpuw->pGPA == NULL)
539 return NULL;
540 return gpuw->pGPA((VkPhysicalGpu) gpuw->nextObject, pName);
541}