forked from KhronosGroup/Vulkan-ValidationLayers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shader_validation.h
141 lines (117 loc) · 5.32 KB
/
shader_validation.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* Copyright (c) 2015-2021 The Khronos Group Inc.
* Copyright (c) 2015-2021 Valve Corporation
* Copyright (c) 2015-2021 LunarG, Inc.
* Copyright (C) 2015-2021 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Chris Forbes <[email protected]>
*
* The Shader Validation file is in charge of taking the Shader Module data and validating it
*/
#ifndef VULKAN_SHADER_VALIDATION_H
#define VULKAN_SHADER_VALIDATION_H
#include <cstdlib>
#include "vulkan/vulkan.h"
#include <generated/spirv_tools_commit_id.h>
#include "shader_module.h"
struct DeviceFeatures;
struct DeviceExtensions;
struct shader_stage_attributes {
char const *const name;
bool arrayed_input;
bool arrayed_output;
VkShaderStageFlags stage;
};
class ValidationCache {
// hashes of shaders that have passed validation before, and can be skipped.
// we don't store negative results, as we would have to also store what was
// wrong with them; also, we expect they will get fixed, so we're less
// likely to see them again.
layer_data::unordered_set<uint32_t> good_shader_hashes;
ValidationCache() {}
public:
static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
auto cache = new ValidationCache();
cache->Load(pCreateInfo);
return VkValidationCacheEXT(cache);
}
void Load(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE;
auto size = headerSize;
if (!pCreateInfo->pInitialData || pCreateInfo->initialDataSize < size) return;
uint32_t const *data = (uint32_t const *)pCreateInfo->pInitialData;
if (data[0] != size) return;
if (data[1] != VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT) return;
uint8_t expected_uuid[VK_UUID_SIZE];
Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, expected_uuid);
if (memcmp(&data[2], expected_uuid, VK_UUID_SIZE) != 0) return; // different version
data = (uint32_t const *)(reinterpret_cast<uint8_t const *>(data) + headerSize);
for (; size < pCreateInfo->initialDataSize; data++, size += sizeof(uint32_t)) {
good_shader_hashes.insert(*data);
}
}
void Write(size_t *pDataSize, void *pData) {
const auto headerSize = 2 * sizeof(uint32_t) + VK_UUID_SIZE; // 4 bytes for header size + 4 bytes for version number + UUID
if (!pData) {
*pDataSize = headerSize + good_shader_hashes.size() * sizeof(uint32_t);
return;
}
if (*pDataSize < headerSize) {
*pDataSize = 0;
return; // Too small for even the header!
}
uint32_t *out = (uint32_t *)pData;
size_t actualSize = headerSize;
// Write the header
*out++ = headerSize;
*out++ = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT;
Sha1ToVkUuid(SPIRV_TOOLS_COMMIT_ID, reinterpret_cast<uint8_t *>(out));
out = (uint32_t *)(reinterpret_cast<uint8_t *>(out) + VK_UUID_SIZE);
for (auto it = good_shader_hashes.begin(); it != good_shader_hashes.end() && actualSize < *pDataSize;
it++, out++, actualSize += sizeof(uint32_t)) {
*out = *it;
}
*pDataSize = actualSize;
}
void Merge(ValidationCache const *other) {
good_shader_hashes.reserve(good_shader_hashes.size() + other->good_shader_hashes.size());
for (auto h : other->good_shader_hashes) good_shader_hashes.insert(h);
}
static uint32_t MakeShaderHash(VkShaderModuleCreateInfo const *smci);
bool Contains(uint32_t hash) { return good_shader_hashes.count(hash) != 0; }
void Insert(uint32_t hash) { good_shader_hashes.insert(hash); }
private:
void Sha1ToVkUuid(const char *sha1_str, uint8_t *uuid) {
// Convert sha1_str from a hex string to binary. We only need VK_UUID_SIZE bytes of
// output, so pad with zeroes if the input string is shorter than that, and truncate
// if it's longer.
#if defined(__GNUC__) && (__GNUC__ > 8)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
#endif
char padded_sha1_str[2 * VK_UUID_SIZE + 1] = {}; // 2 hex digits == 1 byte
std::strncpy(padded_sha1_str, sha1_str, 2 * VK_UUID_SIZE);
#if defined(__GNUC__) && (__GNUC__ > 8)
#pragma GCC diagnostic pop
#endif
for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
const char byte_str[] = {padded_sha1_str[2 * i + 0], padded_sha1_str[2 * i + 1], '\0'};
uuid[i] = static_cast<uint8_t>(std::strtoul(byte_str, nullptr, 16));
}
}
};
spv_target_env PickSpirvEnv(uint32_t api_version, bool spirv_1_4);
void AdjustValidatorOptions(const DeviceExtensions &device_extensions, const DeviceFeatures &enabled_features,
spvtools::ValidatorOptions &options);
#endif // VULKAN_SHADER_VALIDATION_H