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