blob: 4d1fe671d0a8130c0c0c77bfa214afa39a5dcccb [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 Forbesfcd05f12015-04-08 10:36:37 +1200298static void
299validate_vi_against_vs_inputs(VkPipelineVertexInputCreateInfo const *vi, shader_source const *vs)
300{
301 std::map<uint32_t, interface_var> inputs;
302 /* we collect builtin inputs, but they will never appear in the VI state --
303 * the vs builtin inputs are generated in the pipeline, not sourced from buffers (VertexID, etc)
304 */
305 std::map<uint32_t, interface_var> builtin_inputs;
306
307 printf("Begin validate_vi_against_vs_inputs\n");
308
309 collect_interface_by_location(vs, spv::StorageInput, inputs, builtin_inputs);
310
311 /* Build index by location */
312 std::map<uint32_t, VkVertexInputAttributeDescription const *> attribs;
313 for (int i = 0; i < vi->attributeCount; i++)
314 attribs[vi->pVertexAttributeDescriptions[i].location] = &vi->pVertexAttributeDescriptions[i];
315
316 auto it_a = attribs.begin();
317 auto it_b = inputs.begin();
318
319 while (it_a != attribs.end() || it_b != inputs.end()) {
320 if (it_b == inputs.end() || it_a->first < it_b->first) {
321 printf(" WARN: attribute at location %d not consumed by the vertex shader\n",
322 it_a->first);
323 it_a++;
324 }
325 else if (it_a == attribs.end() || it_b->first < it_a->first) {
326 printf(" ERR: vertex shader consumes input at location %d but not provided\n",
327 it_b->first);
328 it_b++;
329 }
330 else {
331 /* TODO: type check */
332 printf(" OK: match on attribute location %d\n",
333 it_a->first);
334 it_a++;
335 it_b++;
336 }
337 }
338
339 printf("End validate_vi_against_vs_inputs\n");
340}
341
342
Chris Forbes60540932015-04-08 10:15:35 +1200343VK_LAYER_EXPORT VkResult VKAPI vkCreateGraphicsPipeline(VkDevice device,
344 const VkGraphicsPipelineCreateInfo *pCreateInfo,
345 VkPipeline *pPipeline)
346{
347 /* TODO: run cross-stage validation */
Chris Forbes60540932015-04-08 10:15:35 +1200348 /* - Validate FS output -> CB */
349 /* - Support GS, TCS, TES stages */
350
Chris Forbes8f600932015-04-08 10:16:45 +1200351 /* We seem to allow pipeline stages to be specified out of order, so collect and identify them
352 * before trying to do anything more: */
353
354 shader_source const *vs_source = 0;
355 shader_source const *fs_source = 0;
356 VkPipelineCbStateCreateInfo const *cb = 0;
357 VkPipelineVertexInputCreateInfo const *vi = 0;
358
359 for (auto stage = pCreateInfo; stage; stage = (decltype(stage))stage->pNext) {
360 if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO) {
361 auto shader_stage = (VkPipelineShaderStageCreateInfo const *)stage;
362
363 if (shader_stage->shader.stage == VK_SHADER_STAGE_VERTEX)
364 vs_source = shader_map[(void *)(shader_stage->shader.shader)];
365 else if (shader_stage->shader.stage == VK_SHADER_STAGE_FRAGMENT)
366 fs_source = shader_map[(void *)(shader_stage->shader.shader)];
367 else
368 printf("Unknown shader stage %d\n", shader_stage->shader.stage);
369 }
370 else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO) {
371 cb = (VkPipelineCbStateCreateInfo const *)stage;
372 }
373 else if (stage->sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO) {
374 vi = (VkPipelineVertexInputCreateInfo const *)stage;
375 }
376 }
377
378 printf("Pipeline: vi=%p vs=%p fs=%p cb=%p\n", vi, vs_source, fs_source, cb);
379
Chris Forbesfcd05f12015-04-08 10:36:37 +1200380 if (vi && vs_source) {
381 validate_vi_against_vs_inputs(vi, vs_source);
382 }
383
Chris Forbesbb164b62015-04-08 10:19:16 +1200384 if (vs_source && fs_source) {
385 validate_interface_between_stages(vs_source, "vertex shader",
386 fs_source, "fragment shader");
387 }
388
Chris Forbes60540932015-04-08 10:15:35 +1200389 VkLayerDispatchTable *pTable = tableMap[(VkBaseLayerObject *)device];
390 VkResult res = pTable->CreateGraphicsPipeline(device, pCreateInfo, pPipeline);
391 return res;
392}
393
394
Chris Forbesaab9d112015-04-02 13:22:31 +1300395VK_LAYER_EXPORT void * VKAPI vkGetProcAddr(VkPhysicalGpu gpu, const char* pName)
396{
397 if (gpu == NULL)
398 return NULL;
399
400 initLayerTable((const VkBaseLayerObject *) gpu);
401
402#define ADD_HOOK(fn) \
403 if (!strncmp(#fn, pName, sizeof(#fn))) \
404 return (void *) fn
405
406 ADD_HOOK(vkGetProcAddr);
407 ADD_HOOK(vkEnumerateLayers);
408 ADD_HOOK(vkCreateDevice);
409 ADD_HOOK(vkCreateShader);
Chris Forbes60540932015-04-08 10:15:35 +1200410 ADD_HOOK(vkCreateGraphicsPipeline);
Chris Forbesaab9d112015-04-02 13:22:31 +1300411
412 VkBaseLayerObject* gpuw = (VkBaseLayerObject *) gpu;
413 if (gpuw->pGPA == NULL)
414 return NULL;
415 return gpuw->pGPA((VkPhysicalGpu) gpuw->nextObject, pName);
416}