1 /* Copyright (c) 2021 The Khronos Group Inc.
2 * Copyright (c) 2021 Valve Corporation
3 * Copyright (c) 2021 LunarG, Inc.
4 * Copyright (C) 2021 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: Jeremy Gebben <jeremyg@lunarg.com>
19 */
20 #pragma once
21
22 #include <cstdint>
23 #include <string>
24 #include <sstream>
25 #include <limits>
26
27 #include "vk_layer_data.h"
28 namespace core_error {
29 // structure to track where a validation error occurs, and capture enough information
30 // to generate the start of a log message and find the correct VUID for many commonvalidity errors.
31 //
32 // usage example:
33 // Location outer(Func::vkCmdPipelineBarrier, Struct::VkImageMemoryBarrier);
34 // auto struct_level = outer.dot(Field::pImageMemoryBarriers, i);
35 // auto field_level = struct_level.dot(Field::srcAccessMask);
36 // std::cout << field_level.Message() << std::endl;
37 // will print:
38 // vkCmdPipelineBarrier(): pImageMemoryBarriers[42].srcAccessMask
39 // VUIDs can be found for an error in generic code using a combination of the
40 // function, structure, and fieldmembers.
41
42 /// TODO: these enums can eventually be autogenerated from vk.xml
43 enum class Func {
44 Empty = 0,
45 vkQueueSubmit,
46 vkQueueSubmit2KHR,
47 vkCmdSetEvent,
48 vkCmdSetEvent2KHR,
49 vkCmdResetEvent,
50 vkCmdResetEvent2KHR,
51 vkCmdPipelineBarrier,
52 vkCmdPipelineBarrier2KHR,
53 vkCmdWaitEvents,
54 vkCmdWaitEvents2KHR,
55 vkCmdWriteTimestamp,
56 vkCmdWriteTimestamp2KHR,
57 vkCreateRenderPass,
58 vkCreateRenderPass2,
59 vkQueueBindSparse,
60 vkSignalSemaphore,
61 };
62
63 const std::string& String(Func func);
64
65 enum class Struct {
66 Empty = 0,
67 VkMemoryBarrier,
68 VkMemoryBarrier2KHR,
69 VkBufferMemoryBarrier,
70 VkImageMemoryBarrier,
71 VkBufferMemoryBarrier2KHR,
72 VkImageMemoryBarrier2KHR,
73 VkSubmitInfo,
74 VkSubmitInfo2KHR,
75 VkCommandBufferSubmitInfoKHR,
76 VkSubpassDependency,
77 VkSubpassDependency2,
78 VkBindSparseInfo,
79 VkSemaphoreSignalInfo,
80 VkSemaphoreSubmitInfoKHR,
81 VkProtectedSubmitInfo,
82 };
83
84 const std::string& String(Struct s);
85
86 enum class Field {
87 Empty = 0,
88 oldLayout,
89 newLayout,
90 image,
91 buffer,
92 pMemoryBarriers,
93 pBufferMemoryBarriers,
94 pImageMemoryBarriers,
95 offset,
96 size,
97 subresourceRange,
98 srcAccessMask,
99 dstAccessMask,
100 srcStageMask,
101 dstStageMask,
102 pNext,
103 pWaitDstStageMask,
104 pWaitSemaphores,
105 pSignalSemaphores,
106 pWaitSemaphoreInfos,
107 pWaitSemaphoreValues,
108 pSignalSemaphoreInfos,
109 pSignalSemaphoreValues,
110 stage,
111 stageMask,
112 value,
113 pCommandBuffers,
114 pSubmits,
115 pCommandBufferInfos,
116 semaphore,
117 commandBuffer,
118 dependencyFlags,
119 pDependencyInfo,
120 pDependencyInfos,
121 srcQueueFamilyIndex,
122 dstQueueFamilyIndex,
123 queryPool,
124 pDependencies,
125 pipelineStage,
126 };
127
128 const std::string& String(Field field);
129
130 struct Location {
131 static const uint32_t kNoIndex = std::numeric_limits<uint32_t>::max();
132
133 // name of the vulkan function we're checking
134 Func function;
135
136 Struct structure;
137 Field field;
138 // optional index if checking an array.
139 uint32_t index;
140 const Location* prev;
141
142 Location(Func func, Struct s, Field f = Field::Empty, uint32_t i = kNoIndex)
functionLocation143 : function(func), structure(s), field(f), index(i), prev(nullptr) {}
144 Location(Func func, Field f = Field::Empty, uint32_t i = kNoIndex)
functionLocation145 : function(func), structure(Struct::Empty), field(f), index(i), prev(nullptr) {}
LocationLocation146 Location(const Location& prev_loc, Struct s, Field f, uint32_t i)
147 : function(prev_loc.function), structure(s), field(f), index(i), prev(&prev_loc) {}
148
149 void AppendFields(std::ostream &out) const;
MessageLocation150 std::string Message() const {
151 std::stringstream out;
152 out << StringFunc() << "(): ";
153 AppendFields(out);
154 return out.str();
155 }
156
157 // the dot() method is for walking down into a structure that is being validated
158 // eg: loc.dot(Field::pMemoryBarriers, 5).dot(Field::srcStagemask)
159 Location dot(Struct s, Field sub_field, uint32_t sub_index = kNoIndex) const {
160 Location result(*this, s, sub_field, sub_index);
161 return result;
162 }
163 Location dot(Field sub_field, uint32_t sub_index = kNoIndex) const {
164 Location result(*this, this->structure, sub_field, sub_index);
165 return result;
166 }
167
StringFuncLocation168 const std::string& StringFunc() const { return String(function); }
StringStructLocation169 const std::string& StringStruct() const { return String(structure); }
StringFieldLocation170 const std::string& StringField() const { return String(field); }
171 };
172
173 template <typename VuidFunctor>
174 struct LocationVuidAdapter {
175 const Location loc;
176 VuidFunctor vuid_functor;
FuncNameLocationVuidAdapter177 const char* FuncName() const {
178 // the returned reference from loc must be valid for lifespan of loc, at least.
179 const std::string& function = loc.StringFunc();
180 return function.c_str();
181 }
VuidLocationVuidAdapter182 const char* Vuid() const {
183 // the returned reference from functor must be valid for lifespan of vuid_functor, at least.
184 const std::string& vuid = vuid_functor(loc);
185 return vuid.c_str();
186 }
187 template <typename... Args>
LocationVuidAdapterLocationVuidAdapter188 LocationVuidAdapter(const Location& loc_, const Args&... args) : loc(loc_), vuid_functor(args...) {}
189 };
190
191 struct LocationCapture {
192 LocationCapture(const Location& loc);
GetLocationCapture193 const Location& Get() const { return capture.back(); }
194
195 protected:
196 // TODO: Optimize this for "new" minimization
197 using CaptureStore = small_vector<Location, 2>;
198 const Location* Capture(const Location& loc, CaptureStore::size_type depth);
199 CaptureStore capture;
200 };
201
202 // Key for use in tables of VUIDs.
203 //
204 // Fuzzy match rules:
205 // key.function OR key.structure may be Empty
206 // loc.structure may be Empty
207 // key.field may be Empty
208 // if key.recurse_field is true, key.field can match loc.field or any fields in loc.prev
209 //
210 struct Key {
211 Func function;
212 Struct structure;
213 Field field;
214 bool recurse_field;
215 Key(Struct r, Field f = Field::Empty, bool recurse = false)
functionKey216 : function(Func::Empty), structure(r), field(f), recurse_field(recurse) {}
217 Key(Func fn, Field f = Field::Empty, bool recurse = false)
functionKey218 : function(fn), structure(Struct::Empty), field(f), recurse_field(recurse) {}
219 };
220
221 bool operator==(const Key& key, const Location& loc);
222
223 // Entry in a VUID lookup table
224 struct Entry {
225 Key k;
226 std::string v;
227 };
228
229 // look for a matching VUID in a vector or array-ish table
230 template <typename Table>
FindVUID(const Location & loc,const Table & table)231 static const std::string& FindVUID(const Location& loc, const Table& table) {
232 static const std::string empty;
233 auto predicate = [&loc](const Entry& entry) { return entry.k == loc; };
234
235 // consistency check: there should never be more than 1 match in a table
236 assert(std::count_if(table.begin(), table.end(), predicate) <= 1);
237
238 const auto pos = std::find_if(table.begin(), table.end(), predicate);
239 return (pos != table.end()) ? pos->v : empty;
240 }
241
242 // 2-level look up where the outer container is a map where we need to find
243 // different VUIDs for different values of an enum or bitfield
244 template <typename OuterKey, typename Table>
FindVUID(OuterKey key,const Location & loc,const Table & table)245 static const std::string& FindVUID(OuterKey key, const Location& loc, const Table& table) {
246 static const std::string empty;
247 const auto entry = table.find(key);
248 if (entry != table.end()) {
249 return FindVUID(loc, entry->second);
250 }
251 return empty;
252 }
253
254 } // namespace core_error
255