Home | History | Annotate | Download | only in layers
      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 (at) 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 
     31     uint32_t len() {
     32         auto result = *it >> 16;
     33         assert(result > 0);
     34         return result;
     35     }
     36 
     37     uint32_t opcode() { return *it & 0x0ffffu; }
     38 
     39     uint32_t const &word(unsigned n) {
     40         assert(n < len());
     41         return it[n];
     42     }
     43 
     44     uint32_t offset() { return (uint32_t)(it - zero); }
     45 
     46     spirv_inst_iter() {}
     47 
     48     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 
     78     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 
     85     shader_module() : has_valid_spirv(false) {}
     86 
     87     // Expose begin() / end() to enable range-based for
     88     spirv_inst_iter begin() const { return spirv_inst_iter(words.begin(), words.begin() + 5); }  // First insn
     89     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.
     91     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
     94     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;
    111     ValidationCache() {}
    112 
    113    public:
    114     static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
    115         auto cache = new ValidationCache();
    116         cache->Load(pCreateInfo);
    117         return VkValidationCacheEXT(cache);
    118     }
    119 
    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 
    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 
    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 
    175     bool Contains(uint32_t hash) { return good_shader_hashes.count(hash) != 0; }
    176 
    177     void Insert(uint32_t hash) { good_shader_hashes.insert(hash); }
    178 
    179    private:
    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