• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2015-2017 The Khronos Group Inc.
2  * Copyright (c) 2015-2017 Valve Corporation
3  * Copyright (c) 2015-2017 LunarG, Inc.
4  * Copyright (C) 2015-2017 Google Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Author: Chris Forbes <chrisf@ijw.co.nz>
19  */
20 #ifndef VULKAN_SHADER_VALIDATION_H
21 #define VULKAN_SHADER_VALIDATION_H
22 
23 #include <spirv_tools_commit_id.h>
24 
25 // A forward iterator over spirv instructions. Provides easy access to len, opcode, and content words
26 // without the caller needing to care too much about the physical SPIRV module layout.
27 struct spirv_inst_iter {
28     std::vector<uint32_t>::const_iterator zero;
29     std::vector<uint32_t>::const_iterator it;
30 
lenspirv_inst_iter31     uint32_t len() {
32         auto result = *it >> 16;
33         assert(result > 0);
34         return result;
35     }
36 
opcodespirv_inst_iter37     uint32_t opcode() { return *it & 0x0ffffu; }
38 
wordspirv_inst_iter39     uint32_t const &word(unsigned n) {
40         assert(n < len());
41         return it[n];
42     }
43 
offsetspirv_inst_iter44     uint32_t offset() { return (uint32_t)(it - zero); }
45 
spirv_inst_iterspirv_inst_iter46     spirv_inst_iter() {}
47 
spirv_inst_iterspirv_inst_iter48     spirv_inst_iter(std::vector<uint32_t>::const_iterator zero, std::vector<uint32_t>::const_iterator it) : zero(zero), it(it) {}
49 
50     bool operator==(spirv_inst_iter const &other) { return it == other.it; }
51 
52     bool operator!=(spirv_inst_iter const &other) { return it != other.it; }
53 
54     spirv_inst_iter operator++(int) {  // x++
55         spirv_inst_iter ii = *this;
56         it += len();
57         return ii;
58     }
59 
60     spirv_inst_iter operator++() {  // ++x;
61         it += len();
62         return *this;
63     }
64 
65     // The iterator and the value are the same thing.
66     spirv_inst_iter &operator*() { return *this; }
67     spirv_inst_iter const &operator*() const { return *this; }
68 };
69 
70 struct shader_module {
71     // The spirv image itself
72     std::vector<uint32_t> words;
73     // A mapping of <id> to the first word of its def. this is useful because walking type
74     // trees, constant expressions, etc requires jumping all over the instruction stream.
75     std::unordered_map<unsigned, unsigned> def_index;
76     bool has_valid_spirv;
77 
shader_moduleshader_module78     shader_module(VkShaderModuleCreateInfo const *pCreateInfo)
79         : words((uint32_t *)pCreateInfo->pCode, (uint32_t *)pCreateInfo->pCode + pCreateInfo->codeSize / sizeof(uint32_t)),
80           def_index(),
81           has_valid_spirv(true) {
82         build_def_index();
83     }
84 
shader_moduleshader_module85     shader_module() : has_valid_spirv(false) {}
86 
87     // Expose begin() / end() to enable range-based for
beginshader_module88     spirv_inst_iter begin() const { return spirv_inst_iter(words.begin(), words.begin() + 5); }  // First insn
endshader_module89     spirv_inst_iter end() const { return spirv_inst_iter(words.begin(), words.end()); }          // Just past last insn
90     // Given an offset into the module, produce an iterator there.
atshader_module91     spirv_inst_iter at(unsigned offset) const { return spirv_inst_iter(words.begin(), words.begin() + offset); }
92 
93     // Gets an iterator to the definition of an id
get_defshader_module94     spirv_inst_iter get_def(unsigned id) const {
95         auto it = def_index.find(id);
96         if (it == def_index.end()) {
97             return end();
98         }
99         return at(it->second);
100     }
101 
102     void build_def_index();
103 };
104 
105 class ValidationCache {
106     // hashes of shaders that have passed validation before, and can be skipped.
107     // we don't store negative results, as we would have to also store what was
108     // wrong with them; also, we expect they will get fixed, so we're less
109     // likely to see them again.
110     std::unordered_set<uint32_t> good_shader_hashes;
ValidationCache()111     ValidationCache() {}
112 
113    public:
Create(VkValidationCacheCreateInfoEXT const * pCreateInfo)114     static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
115         auto cache = new ValidationCache();
116         cache->Load(pCreateInfo);
117         return VkValidationCacheEXT(cache);
118     }
119 
Load(VkValidationCacheCreateInfoEXT const * pCreateInfo)120     void Load(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
121         const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE;
122         auto size = headerSize;
123         if (!pCreateInfo->pInitialData || pCreateInfo->initialDataSize < size) return;
124 
125         uint32_t const *data = (uint32_t const *)pCreateInfo->pInitialData;
126         if (data[0] != size) return;
127         if (data[1] != VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT) return;
128         uint8_t expected_uuid[VK_UUID_SIZE];
129         Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, expected_uuid);
130         if (memcmp(&data[2], expected_uuid, VK_UUID_SIZE) != 0) return;  // different version
131 
132         data = (uint32_t const *)(reinterpret_cast<uint8_t const *>(data) + headerSize);
133 
134         for (; size < pCreateInfo->initialDataSize; data++, size += sizeof(uint32_t)) {
135             good_shader_hashes.insert(*data);
136         }
137     }
138 
Write(size_t * pDataSize,void * pData)139     void Write(size_t *pDataSize, void *pData) {
140         const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE;  // 4 bytes for header size + 4 bytes for version number + UUID
141         if (!pData) {
142             *pDataSize = headerSize + good_shader_hashes.size() * sizeof(uint32_t);
143             return;
144         }
145 
146         if (*pDataSize < headerSize) {
147             *pDataSize = 0;
148             return;  // Too small for even the header!
149         }
150 
151         uint32_t *out = (uint32_t *)pData;
152         size_t actualSize = headerSize;
153 
154         // Write the header
155         *out++ = headerSize;
156         *out++ = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT;
157         Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, reinterpret_cast<uint8_t *>(out));
158         out = (uint32_t *)(reinterpret_cast<uint8_t *>(out) + VK_UUID_SIZE);
159 
160         for (auto it = good_shader_hashes.begin(); it != good_shader_hashes.end() && actualSize < *pDataSize;
161              it++, out++, actualSize += sizeof(uint32_t)) {
162             *out = *it;
163         }
164 
165         *pDataSize = actualSize;
166     }
167 
Merge(ValidationCache const * other)168     void Merge(ValidationCache const *other) {
169         good_shader_hashes.reserve(good_shader_hashes.size() + other->good_shader_hashes.size());
170         for (auto h : other->good_shader_hashes) good_shader_hashes.insert(h);
171     }
172 
173     static uint32_t MakeShaderHash(VkShaderModuleCreateInfo const *smci);
174 
Contains(uint32_t hash)175     bool Contains(uint32_t hash) { return good_shader_hashes.count(hash) != 0; }
176 
Insert(uint32_t hash)177     void Insert(uint32_t hash) { good_shader_hashes.insert(hash); }
178 
179    private:
Sha1ToVkUuid(const char * sha1_str,uint8_t uuid[VK_UUID_SIZE])180     void Sha1ToVkUuid(const char *sha1_str, uint8_t uuid[VK_UUID_SIZE]) {
181         // Convert sha1_str from a hex string to binary. We only need VK_UUID_BYTES of
182         // output, so pad with zeroes if the input string is shorter than that, and truncate
183         // if it's longer.
184         char padded_sha1_str[2 * VK_UUID_SIZE + 1] = {};
185         strncpy(padded_sha1_str, sha1_str, 2 * VK_UUID_SIZE + 1);
186         char byte_str[3] = {};
187         for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
188             byte_str[0] = padded_sha1_str[2 * i + 0];
189             byte_str[1] = padded_sha1_str[2 * i + 1];
190             uuid[i] = static_cast<uint8_t>(strtol(byte_str, NULL, 16));
191         }
192     }
193 };
194 
195 bool validate_and_capture_pipeline_shader_state(layer_data *dev_data, PIPELINE_STATE *pPipeline);
196 bool validate_compute_pipeline(layer_data *dev_data, PIPELINE_STATE *pPipeline);
197 typedef std::pair<unsigned, unsigned> descriptor_slot_t;
198 bool PreCallValidateCreateShaderModule(layer_data *dev_data, VkShaderModuleCreateInfo const *pCreateInfo, bool *spirv_valid);
199 
200 #endif  // VULKAN_SHADER_VALIDATION_H
201