blob: f288ee28e1b329fac423e70e77ebb150c0cf0154 [file] [log] [blame]
sfricke-samsungef15e482022-01-26 11:32:49 -08001/* Copyright (c) 2020-2022 The Khronos Group Inc.
2 * Copyright (c) 2020-2022 Valve Corporation
3 * Copyright (c) 2020-2022 LunarG, Inc.
Tony-LunarG1dce2392019-10-23 16:49:29 -06004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * Author: Tony Barbour <tony@lunarg.com>
18 */
19
Jeremy Gebben5160e032022-03-28 14:57:43 -060020#include "gpu_utils.h"
Jeremy Gebben159b3cc2021-06-03 09:09:03 -060021#include "descriptor_sets.h"
Tony-LunarG1dce2392019-10-23 16:49:29 -060022#include "spirv-tools/libspirv.h"
23#include "spirv-tools/optimizer.hpp"
24#include "spirv-tools/instrument.hpp"
Mark Lobodzinski102687e2020-04-28 11:03:28 -060025#include <spirv/unified1/spirv.hpp>
Tony-LunarG1dce2392019-10-23 16:49:29 -060026#include <algorithm>
27#include <regex>
28
29#define VMA_IMPLEMENTATION
30// This define indicates that we will supply Vulkan function pointers at initialization
31#define VMA_STATIC_VULKAN_FUNCTIONS 0
32#include "vk_mem_alloc.h"
33
Tony-LunarG1dce2392019-10-23 16:49:29 -060034// Implementation for Descriptor Set Manager class
Tony-LunarGb5fae462020-03-05 12:43:25 -070035UtilDescriptorSetManager::UtilDescriptorSetManager(VkDevice device, uint32_t numBindingsInSet)
Tony-LunarG1dce2392019-10-23 16:49:29 -060036 : device(device), numBindingsInSet(numBindingsInSet) {}
37
Tony-LunarGb5fae462020-03-05 12:43:25 -070038UtilDescriptorSetManager::~UtilDescriptorSetManager() {
Tony-LunarG1dce2392019-10-23 16:49:29 -060039 for (auto &pool : desc_pool_map_) {
40 DispatchDestroyDescriptorPool(device, pool.first, NULL);
41 }
42 desc_pool_map_.clear();
43}
44
Tony-LunarGb5fae462020-03-05 12:43:25 -070045VkResult UtilDescriptorSetManager::GetDescriptorSet(VkDescriptorPool *desc_pool, VkDescriptorSetLayout ds_layout,
46 VkDescriptorSet *desc_set) {
Tony-LunarG1dce2392019-10-23 16:49:29 -060047 std::vector<VkDescriptorSet> desc_sets;
48 VkResult result = GetDescriptorSets(1, desc_pool, ds_layout, &desc_sets);
49 if (result == VK_SUCCESS) {
50 *desc_set = desc_sets[0];
51 }
52 return result;
53}
54
Tony-LunarGb5fae462020-03-05 12:43:25 -070055VkResult UtilDescriptorSetManager::GetDescriptorSets(uint32_t count, VkDescriptorPool *pool, VkDescriptorSetLayout ds_layout,
56 std::vector<VkDescriptorSet> *desc_sets) {
Jeremy Gebbenfcfc33c2022-03-28 15:31:29 -060057 auto guard = Lock();
Tony-LunarG1dce2392019-10-23 16:49:29 -060058 const uint32_t default_pool_size = kItemsPerChunk;
59 VkResult result = VK_SUCCESS;
60 VkDescriptorPool pool_to_use = VK_NULL_HANDLE;
61
62 if (0 == count) {
63 return result;
64 }
65 desc_sets->clear();
66 desc_sets->resize(count);
67
68 for (auto &pool : desc_pool_map_) {
69 if (pool.second.used + count < pool.second.size) {
70 pool_to_use = pool.first;
71 break;
72 }
73 }
74 if (VK_NULL_HANDLE == pool_to_use) {
75 uint32_t pool_count = default_pool_size;
76 if (count > default_pool_size) {
77 pool_count = count;
78 }
79 const VkDescriptorPoolSize size_counts = {
80 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
81 pool_count * numBindingsInSet,
82 };
Nathaniel Cesariofc6291e2021-04-06 00:22:15 -060083 auto desc_pool_info = LvlInitStruct<VkDescriptorPoolCreateInfo>();
Tony-LunarG1dce2392019-10-23 16:49:29 -060084 desc_pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
85 desc_pool_info.maxSets = pool_count;
86 desc_pool_info.poolSizeCount = 1;
87 desc_pool_info.pPoolSizes = &size_counts;
88 result = DispatchCreateDescriptorPool(device, &desc_pool_info, NULL, &pool_to_use);
89 assert(result == VK_SUCCESS);
90 if (result != VK_SUCCESS) {
91 return result;
92 }
93 desc_pool_map_[pool_to_use].size = desc_pool_info.maxSets;
94 desc_pool_map_[pool_to_use].used = 0;
95 }
96 std::vector<VkDescriptorSetLayout> desc_layouts(count, ds_layout);
97
98 VkDescriptorSetAllocateInfo alloc_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, NULL, pool_to_use, count,
99 desc_layouts.data()};
100
101 result = DispatchAllocateDescriptorSets(device, &alloc_info, desc_sets->data());
102 assert(result == VK_SUCCESS);
103 if (result != VK_SUCCESS) {
104 return result;
105 }
106 *pool = pool_to_use;
107 desc_pool_map_[pool_to_use].used += count;
108 return result;
109}
110
Tony-LunarGb5fae462020-03-05 12:43:25 -0700111void UtilDescriptorSetManager::PutBackDescriptorSet(VkDescriptorPool desc_pool, VkDescriptorSet desc_set) {
Jeremy Gebbenfcfc33c2022-03-28 15:31:29 -0600112 auto guard = Lock();
Tony-LunarG1dce2392019-10-23 16:49:29 -0600113 auto iter = desc_pool_map_.find(desc_pool);
114 if (iter != desc_pool_map_.end()) {
115 VkResult result = DispatchFreeDescriptorSets(device, desc_pool, 1, &desc_set);
116 assert(result == VK_SUCCESS);
117 if (result != VK_SUCCESS) {
118 return;
119 }
120 desc_pool_map_[desc_pool].used--;
121 if (0 == desc_pool_map_[desc_pool].used) {
122 DispatchDestroyDescriptorPool(device, desc_pool, NULL);
123 desc_pool_map_.erase(desc_pool);
124 }
125 }
126 return;
127}
128
129// Trampolines to make VMA call Dispatch for Vulkan calls
130static VKAPI_ATTR void VKAPI_CALL gpuVkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
131 VkPhysicalDeviceProperties *pProperties) {
132 DispatchGetPhysicalDeviceProperties(physicalDevice, pProperties);
133}
134static VKAPI_ATTR void VKAPI_CALL gpuVkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
135 VkPhysicalDeviceMemoryProperties *pMemoryProperties) {
136 DispatchGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
137}
138static VKAPI_ATTR VkResult VKAPI_CALL gpuVkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo,
139 const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory) {
140 return DispatchAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
141}
142static VKAPI_ATTR void VKAPI_CALL gpuVkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks *pAllocator) {
143 DispatchFreeMemory(device, memory, pAllocator);
144}
145static VKAPI_ATTR VkResult VKAPI_CALL gpuVkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size,
146 VkMemoryMapFlags flags, void **ppData) {
147 return DispatchMapMemory(device, memory, offset, size, flags, ppData);
148}
149static VKAPI_ATTR void VKAPI_CALL gpuVkUnmapMemory(VkDevice device, VkDeviceMemory memory) { DispatchUnmapMemory(device, memory); }
150static VKAPI_ATTR VkResult VKAPI_CALL gpuVkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount,
151 const VkMappedMemoryRange *pMemoryRanges) {
152 return DispatchFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
153}
154static VKAPI_ATTR VkResult VKAPI_CALL gpuVkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount,
155 const VkMappedMemoryRange *pMemoryRanges) {
156 return DispatchInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
157}
158static VKAPI_ATTR VkResult VKAPI_CALL gpuVkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory,
159 VkDeviceSize memoryOffset) {
160 return DispatchBindBufferMemory(device, buffer, memory, memoryOffset);
161}
162static VKAPI_ATTR VkResult VKAPI_CALL gpuVkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory,
163 VkDeviceSize memoryOffset) {
164 return DispatchBindImageMemory(device, image, memory, memoryOffset);
165}
166static VKAPI_ATTR void VKAPI_CALL gpuVkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer,
167 VkMemoryRequirements *pMemoryRequirements) {
168 DispatchGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
169}
170static VKAPI_ATTR void VKAPI_CALL gpuVkGetImageMemoryRequirements(VkDevice device, VkImage image,
171 VkMemoryRequirements *pMemoryRequirements) {
172 DispatchGetImageMemoryRequirements(device, image, pMemoryRequirements);
173}
174static VKAPI_ATTR VkResult VKAPI_CALL gpuVkCreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo,
175 const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) {
176 return DispatchCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
177}
178static VKAPI_ATTR void VKAPI_CALL gpuVkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks *pAllocator) {
179 return DispatchDestroyBuffer(device, buffer, pAllocator);
180}
181static VKAPI_ATTR VkResult VKAPI_CALL gpuVkCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo,
182 const VkAllocationCallbacks *pAllocator, VkImage *pImage) {
183 return DispatchCreateImage(device, pCreateInfo, pAllocator, pImage);
184}
185static VKAPI_ATTR void VKAPI_CALL gpuVkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) {
186 DispatchDestroyImage(device, image, pAllocator);
187}
188static VKAPI_ATTR void VKAPI_CALL gpuVkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer,
189 uint32_t regionCount, const VkBufferCopy *pRegions) {
190 DispatchCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions);
191}
192
Tony-LunarGb5fae462020-03-05 12:43:25 -0700193VkResult UtilInitializeVma(VkPhysicalDevice physical_device, VkDevice device, VmaAllocator *pAllocator) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600194 VmaVulkanFunctions functions;
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700195 VmaAllocatorCreateInfo allocator_info = {};
196 allocator_info.device = device;
197 allocator_info.physicalDevice = physical_device;
Tony-LunarG1dce2392019-10-23 16:49:29 -0600198
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700199 functions.vkGetPhysicalDeviceProperties = static_cast<PFN_vkGetPhysicalDeviceProperties>(gpuVkGetPhysicalDeviceProperties);
200 functions.vkGetPhysicalDeviceMemoryProperties =
201 static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(gpuVkGetPhysicalDeviceMemoryProperties);
202 functions.vkAllocateMemory = static_cast<PFN_vkAllocateMemory>(gpuVkAllocateMemory);
203 functions.vkFreeMemory = static_cast<PFN_vkFreeMemory>(gpuVkFreeMemory);
204 functions.vkMapMemory = static_cast<PFN_vkMapMemory>(gpuVkMapMemory);
205 functions.vkUnmapMemory = static_cast<PFN_vkUnmapMemory>(gpuVkUnmapMemory);
206 functions.vkFlushMappedMemoryRanges = static_cast<PFN_vkFlushMappedMemoryRanges>(gpuVkFlushMappedMemoryRanges);
207 functions.vkInvalidateMappedMemoryRanges = static_cast<PFN_vkInvalidateMappedMemoryRanges>(gpuVkInvalidateMappedMemoryRanges);
208 functions.vkBindBufferMemory = static_cast<PFN_vkBindBufferMemory>(gpuVkBindBufferMemory);
209 functions.vkBindImageMemory = static_cast<PFN_vkBindImageMemory>(gpuVkBindImageMemory);
210 functions.vkGetBufferMemoryRequirements = static_cast<PFN_vkGetBufferMemoryRequirements>(gpuVkGetBufferMemoryRequirements);
211 functions.vkGetImageMemoryRequirements = static_cast<PFN_vkGetImageMemoryRequirements>(gpuVkGetImageMemoryRequirements);
212 functions.vkCreateBuffer = static_cast<PFN_vkCreateBuffer>(gpuVkCreateBuffer);
213 functions.vkDestroyBuffer = static_cast<PFN_vkDestroyBuffer>(gpuVkDestroyBuffer);
214 functions.vkCreateImage = static_cast<PFN_vkCreateImage>(gpuVkCreateImage);
215 functions.vkDestroyImage = static_cast<PFN_vkDestroyImage>(gpuVkDestroyImage);
216 functions.vkCmdCopyBuffer = static_cast<PFN_vkCmdCopyBuffer>(gpuVkCmdCopyBuffer);
217 allocator_info.pVulkanFunctions = &functions;
Tony-LunarG1dce2392019-10-23 16:49:29 -0600218
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700219 return vmaCreateAllocator(&allocator_info, pAllocator);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600220}
221
Jeremy Gebben33717862022-03-28 15:53:56 -0600222void GpuAssistedBase::PreCallRecordCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo *pCreateInfo,
223 const VkAllocationCallbacks *pAllocator, VkDevice *pDevice, void *modified_ci) {
224 ValidationStateTracker::PreCallRecordCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, modified_ci);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600225 VkPhysicalDeviceFeatures *features = nullptr;
Jeremy Gebben33717862022-03-28 15:53:56 -0600226 // Use a local variable to query features since this method runs in the instance validation object.
227 // To avoid confusion and race conditions about which physical device's features are stored in the
228 // 'supported_devices' member variable, it will only be set in the device validation objects.
229 // See CreateDevice() below.
230 VkPhysicalDeviceFeatures gpu_supported_features;
231 DispatchGetPhysicalDeviceFeatures(gpu, &gpu_supported_features);
232 auto modified_create_info = static_cast<VkDeviceCreateInfo *>(modified_ci);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600233 if (modified_create_info->pEnabledFeatures) {
234 // If pEnabledFeatures, VkPhysicalDeviceFeatures2 in pNext chain is not allowed
235 features = const_cast<VkPhysicalDeviceFeatures *>(modified_create_info->pEnabledFeatures);
236 } else {
237 VkPhysicalDeviceFeatures2 *features2 = nullptr;
Mark Lobodzinski1f887d32020-12-30 15:31:33 -0700238 features2 = const_cast<VkPhysicalDeviceFeatures2 *>(LvlFindInChain<VkPhysicalDeviceFeatures2>(modified_create_info->pNext));
Tony-LunarG1dce2392019-10-23 16:49:29 -0600239 if (features2) features = &features2->features;
240 }
Tony-LunarGf0634eb2021-01-05 15:11:12 -0700241 VkPhysicalDeviceFeatures new_features = {};
242 VkBool32 *desired = reinterpret_cast<VkBool32 *>(&desired_features);
243 VkBool32 *feature_ptr;
Tony-LunarG1dce2392019-10-23 16:49:29 -0600244 if (features) {
Tony-LunarGf0634eb2021-01-05 15:11:12 -0700245 feature_ptr = reinterpret_cast<VkBool32 *>(features);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600246 } else {
Tony-LunarGf0634eb2021-01-05 15:11:12 -0700247 feature_ptr = reinterpret_cast<VkBool32 *>(&new_features);
248 }
249 VkBool32 *supported = reinterpret_cast<VkBool32 *>(&supported_features);
250 for (size_t i = 0; i < sizeof(VkPhysicalDeviceFeatures); i += (sizeof(VkBool32))) {
251 if (*supported && *desired) {
252 *feature_ptr = true;
253 }
254 supported++;
255 desired++;
256 feature_ptr++;
257 }
258 if (!features) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600259 delete modified_create_info->pEnabledFeatures;
260 modified_create_info->pEnabledFeatures = new VkPhysicalDeviceFeatures(new_features);
261 }
262}
263
Jeremy Gebben33717862022-03-28 15:53:56 -0600264void GpuAssistedBase::CreateDevice(const VkDeviceCreateInfo *pCreateInfo) {
265 ValidationStateTracker::CreateDevice(pCreateInfo);
266 // If api version 1.1 or later, SetDeviceLoaderData will be in the loader
267 auto chain_info = get_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
268 assert(chain_info->u.pfnSetDeviceLoaderData);
269 vkSetDeviceLoaderData = chain_info->u.pfnSetDeviceLoaderData;
270
271 // Some devices have extremely high limits here, so set a reasonable max because we have to pad
272 // the pipeline layout with dummy descriptor set layouts.
273 adjusted_max_desc_sets = phys_dev_props.limits.maxBoundDescriptorSets;
274 adjusted_max_desc_sets = std::min(33U, adjusted_max_desc_sets);
275
276 // We can't do anything if there is only one.
277 // Device probably not a legit Vulkan device, since there should be at least 4. Protect ourselves.
278 if (adjusted_max_desc_sets == 1) {
279 ReportSetupProblem(device, "Device can bind only a single descriptor set.");
280 aborted = true;
281 return;
282 }
283 desc_set_bind_index = adjusted_max_desc_sets - 1;
284
285 VkResult result1 = UtilInitializeVma(physical_device, device, &vmaAllocator);
286 assert(result1 == VK_SUCCESS);
287 desc_set_manager = layer_data::make_unique<UtilDescriptorSetManager>(device, static_cast<uint32_t>(bindings_.size()));
288
289 const VkDescriptorSetLayoutCreateInfo debug_desc_layout_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, NULL, 0,
290 static_cast<uint32_t>(bindings_.size()), bindings_.data()};
291
292 const VkDescriptorSetLayoutCreateInfo dummy_desc_layout_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, NULL, 0, 0,
293 NULL};
294
295 result1 = DispatchCreateDescriptorSetLayout(device, &debug_desc_layout_info, NULL, &debug_desc_layout);
296
297 // This is a layout used to "pad" a pipeline layout to fill in any gaps to the selected bind index.
298 VkResult result2 = DispatchCreateDescriptorSetLayout(device, &dummy_desc_layout_info, NULL, &dummy_desc_layout);
299
300 assert((result1 == VK_SUCCESS) && (result2 == VK_SUCCESS));
301 if ((result1 != VK_SUCCESS) || (result2 != VK_SUCCESS)) {
302 ReportSetupProblem(device, "Unable to create descriptor set layout.");
303 if (result1 == VK_SUCCESS) {
304 DispatchDestroyDescriptorSetLayout(device, debug_desc_layout, NULL);
305 }
306 if (result2 == VK_SUCCESS) {
307 DispatchDestroyDescriptorSetLayout(device, dummy_desc_layout, NULL);
308 }
309 debug_desc_layout = VK_NULL_HANDLE;
310 dummy_desc_layout = VK_NULL_HANDLE;
311 aborted = true;
312 return;
313 }
314}
315
316void GpuAssistedBase::PreCallRecordDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) {
Jeremy Gebben33717862022-03-28 15:53:56 -0600317 if (debug_desc_layout) {
318 DispatchDestroyDescriptorSetLayout(device, debug_desc_layout, NULL);
319 debug_desc_layout = VK_NULL_HANDLE;
320 }
321 if (dummy_desc_layout) {
322 DispatchDestroyDescriptorSetLayout(device, dummy_desc_layout, NULL);
323 dummy_desc_layout = VK_NULL_HANDLE;
324 }
325 ValidationStateTracker::PreCallRecordDestroyDevice(device, pAllocator);
326 // State Tracker can end up making vma calls through callbacks - don't destroy allocator until ST is done
327 if (vmaAllocator) {
328 vmaDestroyAllocator(vmaAllocator);
329 }
330 desc_set_manager.reset();
331}
332
Jeremy Gebbenfcfc33c2022-03-28 15:31:29 -0600333gpu_utils_state::Queue::Queue(GpuAssistedBase &state, VkQueue q, uint32_t index, VkDeviceQueueCreateFlags flags)
334 : QUEUE_STATE(q, index, flags), state_(state) {}
335
336gpu_utils_state::Queue::~Queue() {
337 if (barrier_command_buffer_) {
338 DispatchFreeCommandBuffers(state_.device, barrier_command_pool_, 1, &barrier_command_buffer_);
339 barrier_command_buffer_ = VK_NULL_HANDLE;
340 }
341 if (barrier_command_pool_) {
342 DispatchDestroyCommandPool(state_.device, barrier_command_pool_, NULL);
343 barrier_command_pool_ = VK_NULL_HANDLE;
344 }
345}
346
347// Submit a memory barrier on graphics queues.
348// Lazy-create and record the needed command buffer.
349void gpu_utils_state::Queue::SubmitBarrier() {
350 if (barrier_command_pool_ == VK_NULL_HANDLE) {
351 VkResult result = VK_SUCCESS;
352
353 auto pool_create_info = LvlInitStruct<VkCommandPoolCreateInfo>();
354 pool_create_info.queueFamilyIndex = queueFamilyIndex;
355 result = DispatchCreateCommandPool(state_.device, &pool_create_info, nullptr, &barrier_command_pool_);
356 if (result != VK_SUCCESS) {
357 state_.ReportSetupProblem(state_.device, "Unable to create command pool for barrier CB.");
358 barrier_command_pool_ = VK_NULL_HANDLE;
359 return;
360 }
361
362 auto buffer_alloc_info = LvlInitStruct<VkCommandBufferAllocateInfo>();
363 buffer_alloc_info.commandPool = barrier_command_pool_;
364 buffer_alloc_info.commandBufferCount = 1;
365 buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
366 result = DispatchAllocateCommandBuffers(state_.device, &buffer_alloc_info, &barrier_command_buffer_);
367 if (result != VK_SUCCESS) {
368 state_.ReportSetupProblem(state_.device, "Unable to create barrier command buffer.");
369 DispatchDestroyCommandPool(state_.device, barrier_command_pool_, nullptr);
370 barrier_command_pool_ = VK_NULL_HANDLE;
371 barrier_command_buffer_ = VK_NULL_HANDLE;
372 return;
373 }
374
375 // Hook up command buffer dispatch
376 state_.vkSetDeviceLoaderData(state_.device, barrier_command_buffer_);
377
378 // Record a global memory barrier to force availability of device memory operations to the host domain.
379 auto command_buffer_begin_info = LvlInitStruct<VkCommandBufferBeginInfo>();
380 result = DispatchBeginCommandBuffer(barrier_command_buffer_, &command_buffer_begin_info);
381 if (result == VK_SUCCESS) {
382 auto memory_barrier = LvlInitStruct<VkMemoryBarrier>();
383 memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
384 memory_barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
385 DispatchCmdPipelineBarrier(barrier_command_buffer_, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0,
386 1, &memory_barrier, 0, nullptr, 0, nullptr);
387 DispatchEndCommandBuffer(barrier_command_buffer_);
388 }
389 }
390 if (barrier_command_buffer_ != VK_NULL_HANDLE) {
391 auto submit_info = LvlInitStruct<VkSubmitInfo>();
392 submit_info.commandBufferCount = 1;
393 submit_info.pCommandBuffers = &barrier_command_buffer_;
394 DispatchQueueSubmit(QUEUE_STATE::Queue(), 1, &submit_info, VK_NULL_HANDLE);
395 }
396}
397
398// Issue a memory barrier to make GPU-written data available to host.
399// Wait for the queue to complete execution.
400// Check the debug buffers for all the command buffers that were submitted.
401void GpuAssistedBase::PostCallRecordQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence,
402 VkResult result) {
403 ValidationStateTracker::PostCallRecordQueueSubmit(queue, submitCount, pSubmits, fence, result);
404
405 if (aborted || (result != VK_SUCCESS)) return;
406 bool buffers_present = false;
407 // Don't QueueWaitIdle if there's nothing to process
408 for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) {
409 const VkSubmitInfo *submit = &pSubmits[submit_idx];
410 for (uint32_t i = 0; i < submit->commandBufferCount; i++) {
411 buffers_present |= CommandBufferNeedsProcessing(submit->pCommandBuffers[i]);
412 }
413 }
414 if (!buffers_present) return;
415
416 SubmitBarrier(queue);
417
418 DispatchQueueWaitIdle(queue);
419
420 for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) {
421 const VkSubmitInfo *submit = &pSubmits[submit_idx];
422 for (uint32_t i = 0; i < submit->commandBufferCount; i++) {
423 ProcessCommandBuffer(queue, submit->pCommandBuffers[i]);
424 }
425 }
426}
427
428void GpuAssistedBase::RecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence,
429 VkResult result) {
430 if (aborted || (result != VK_SUCCESS)) return;
431 bool buffers_present = false;
432 // Don't QueueWaitIdle if there's nothing to process
433 for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) {
434 const VkSubmitInfo2 *submit = &pSubmits[submit_idx];
435 for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++) {
436 buffers_present |= CommandBufferNeedsProcessing(submit->pCommandBufferInfos[i].commandBuffer);
437 }
438 }
439 if (!buffers_present) return;
440
441 SubmitBarrier(queue);
442
443 DispatchQueueWaitIdle(queue);
444
445 for (uint32_t submit_idx = 0; submit_idx < submitCount; submit_idx++) {
446 const VkSubmitInfo2 *submit = &pSubmits[submit_idx];
447 for (uint32_t i = 0; i < submit->commandBufferInfoCount; i++) {
448 ProcessCommandBuffer(queue, submit->pCommandBufferInfos[i].commandBuffer);
449 }
450 }
451}
452
453void GpuAssistedBase::PostCallRecordQueueSubmit2KHR(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2KHR *pSubmits,
454 VkFence fence, VkResult result) {
455 ValidationStateTracker::PostCallRecordQueueSubmit2KHR(queue, submitCount, pSubmits, fence, result);
456 RecordQueueSubmit2(queue, submitCount, pSubmits, fence, result);
457}
458
459void GpuAssistedBase::PostCallRecordQueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2 *pSubmits, VkFence fence,
460 VkResult result) {
461 ValidationStateTracker::PostCallRecordQueueSubmit2(queue, submitCount, pSubmits, fence, result);
462 RecordQueueSubmit2(queue, submitCount, pSubmits, fence, result);
463}
464
Tony-LunarG1dce2392019-10-23 16:49:29 -0600465// Generate the stage-specific part of the message.
Tony-LunarGb5fae462020-03-05 12:43:25 -0700466void UtilGenerateStageMessage(const uint32_t *debug_record, std::string &msg) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600467 using namespace spvtools;
468 std::ostringstream strm;
469 switch (debug_record[kInstCommonOutStageIdx]) {
470 case spv::ExecutionModelVertex: {
471 strm << "Stage = Vertex. Vertex Index = " << debug_record[kInstVertOutVertexIndex]
472 << " Instance Index = " << debug_record[kInstVertOutInstanceIndex] << ". ";
473 } break;
474 case spv::ExecutionModelTessellationControl: {
475 strm << "Stage = Tessellation Control. Invocation ID = " << debug_record[kInstTessCtlOutInvocationId]
476 << ", Primitive ID = " << debug_record[kInstTessCtlOutPrimitiveId];
477 } break;
478 case spv::ExecutionModelTessellationEvaluation: {
479 strm << "Stage = Tessellation Eval. Primitive ID = " << debug_record[kInstTessEvalOutPrimitiveId]
480 << ", TessCoord (u, v) = (" << debug_record[kInstTessEvalOutTessCoordU] << ", "
481 << debug_record[kInstTessEvalOutTessCoordV] << "). ";
482 } break;
483 case spv::ExecutionModelGeometry: {
484 strm << "Stage = Geometry. Primitive ID = " << debug_record[kInstGeomOutPrimitiveId]
485 << " Invocation ID = " << debug_record[kInstGeomOutInvocationId] << ". ";
486 } break;
487 case spv::ExecutionModelFragment: {
488 strm << "Stage = Fragment. Fragment coord (x,y) = ("
489 << *reinterpret_cast<const float *>(&debug_record[kInstFragOutFragCoordX]) << ", "
490 << *reinterpret_cast<const float *>(&debug_record[kInstFragOutFragCoordY]) << "). ";
491 } break;
492 case spv::ExecutionModelGLCompute: {
493 strm << "Stage = Compute. Global invocation ID (x, y, z) = (" << debug_record[kInstCompOutGlobalInvocationIdX] << ", "
494 << debug_record[kInstCompOutGlobalInvocationIdY] << ", " << debug_record[kInstCompOutGlobalInvocationIdZ] << " )";
495 } break;
496 case spv::ExecutionModelRayGenerationNV: {
497 strm << "Stage = Ray Generation. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
498 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
499 } break;
500 case spv::ExecutionModelIntersectionNV: {
501 strm << "Stage = Intersection. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
502 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
503 } break;
504 case spv::ExecutionModelAnyHitNV: {
505 strm << "Stage = Any Hit. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
506 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
507 } break;
508 case spv::ExecutionModelClosestHitNV: {
509 strm << "Stage = Closest Hit. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
510 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
511 } break;
512 case spv::ExecutionModelMissNV: {
513 strm << "Stage = Miss. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
514 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
515 } break;
516 case spv::ExecutionModelCallableNV: {
517 strm << "Stage = Callable. Global Launch ID (x,y,z) = (" << debug_record[kInstRayTracingOutLaunchIdX] << ", "
518 << debug_record[kInstRayTracingOutLaunchIdY] << ", " << debug_record[kInstRayTracingOutLaunchIdZ] << "). ";
519 } break;
Tony-LunarGc7ed2082020-06-11 14:00:04 -0600520 case spv::ExecutionModelTaskNV: {
521 strm << "Stage = Task. Global invocation ID (x, y, z) = (" << debug_record[kInstTaskOutGlobalInvocationIdX] << ", "
522 << debug_record[kInstTaskOutGlobalInvocationIdY] << ", " << debug_record[kInstTaskOutGlobalInvocationIdZ] << " )";
523 } break;
524 case spv::ExecutionModelMeshNV: {
525 strm << "Stage = Mesh.Global invocation ID (x, y, z) = (" << debug_record[kInstMeshOutGlobalInvocationIdX] << ", "
526 << debug_record[kInstMeshOutGlobalInvocationIdY] << ", " << debug_record[kInstMeshOutGlobalInvocationIdZ] << " )";
527 } break;
Tony-LunarG1dce2392019-10-23 16:49:29 -0600528 default: {
529 strm << "Internal Error (unexpected stage = " << debug_record[kInstCommonOutStageIdx] << "). ";
530 assert(false);
531 } break;
532 }
533 msg = strm.str();
534}
535
536std::string LookupDebugUtilsName(const debug_report_data *report_data, const uint64_t object) {
537 auto object_label = report_data->DebugReportGetUtilsObjectName(object);
538 if (object_label != "") {
539 object_label = "(" + object_label + ")";
540 }
541 return object_label;
542}
543
544// Generate message from the common portion of the debug report record.
Tony-LunarGb5fae462020-03-05 12:43:25 -0700545void UtilGenerateCommonMessage(const debug_report_data *report_data, const VkCommandBuffer commandBuffer,
546 const uint32_t *debug_record, const VkShaderModule shader_module_handle,
547 const VkPipeline pipeline_handle, const VkPipelineBindPoint pipeline_bind_point,
548 const uint32_t operation_index, std::string &msg) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600549 using namespace spvtools;
550 std::ostringstream strm;
551 if (shader_module_handle == VK_NULL_HANDLE) {
552 strm << std::hex << std::showbase << "Internal Error: Unable to locate information for shader used in command buffer "
553 << LookupDebugUtilsName(report_data, HandleToUint64(commandBuffer)) << "(" << HandleToUint64(commandBuffer) << "). ";
554 assert(true);
555 } else {
556 strm << std::hex << std::showbase << "Command buffer " << LookupDebugUtilsName(report_data, HandleToUint64(commandBuffer))
557 << "(" << HandleToUint64(commandBuffer) << "). ";
558 if (pipeline_bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) {
559 strm << "Draw ";
560 } else if (pipeline_bind_point == VK_PIPELINE_BIND_POINT_COMPUTE) {
561 strm << "Compute ";
562 } else if (pipeline_bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_NV) {
563 strm << "Ray Trace ";
564 } else {
565 assert(false);
566 strm << "Unknown Pipeline Operation ";
567 }
568 strm << "Index " << operation_index << ". "
569 << "Pipeline " << LookupDebugUtilsName(report_data, HandleToUint64(pipeline_handle)) << "("
570 << HandleToUint64(pipeline_handle) << "). "
571 << "Shader Module " << LookupDebugUtilsName(report_data, HandleToUint64(shader_module_handle)) << "("
572 << HandleToUint64(shader_module_handle) << "). ";
573 }
574 strm << std::dec << std::noshowbase;
575 strm << "Shader Instruction Index = " << debug_record[kInstCommonOutInstructionIdx] << ". ";
576 msg = strm.str();
577}
578
579// Read the contents of the SPIR-V OpSource instruction and any following continuation instructions.
580// Split the single string into a vector of strings, one for each line, for easier processing.
sfricke-samsungef15e482022-01-26 11:32:49 -0800581void ReadOpSource(const SHADER_MODULE_STATE &module_state, const uint32_t reported_file_id,
582 std::vector<std::string> &opsource_lines) {
583 for (auto insn : module_state) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600584 if ((insn.opcode() == spv::OpSource) && (insn.len() >= 5) && (insn.word(3) == reported_file_id)) {
585 std::istringstream in_stream;
586 std::string cur_line;
587 in_stream.str((char *)&insn.word(4));
588 while (std::getline(in_stream, cur_line)) {
589 opsource_lines.push_back(cur_line);
590 }
591 while ((++insn).opcode() == spv::OpSourceContinued) {
592 in_stream.str((char *)&insn.word(1));
593 while (std::getline(in_stream, cur_line)) {
594 opsource_lines.push_back(cur_line);
595 }
596 }
597 break;
598 }
599 }
600}
601
602// The task here is to search the OpSource content to find the #line directive with the
603// line number that is closest to, but still prior to the reported error line number and
604// still within the reported filename.
605// From this known position in the OpSource content we can add the difference between
606// the #line line number and the reported error line number to determine the location
607// in the OpSource content of the reported error line.
608//
609// Considerations:
610// - Look only at #line directives that specify the reported_filename since
611// the reported error line number refers to its location in the reported filename.
612// - If a #line directive does not have a filename, the file is the reported filename, or
613// the filename found in a prior #line directive. (This is C-preprocessor behavior)
614// - It is possible (e.g., inlining) for blocks of code to get shuffled out of their
615// original order and the #line directives are used to keep the numbering correct. This
616// is why we need to examine the entire contents of the source, instead of leaving early
617// when finding a #line line number larger than the reported error line number.
618//
619
620// GCC 4.8 has a problem with std::regex that is fixed in GCC 4.9. Provide fallback code for 4.8
621#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
622
623#if defined(__GNUC__) && GCC_VERSION < 40900
624bool GetLineAndFilename(const std::string string, uint32_t *linenumber, std::string &filename) {
625 // # line <linenumber> "<filename>" or
626 // #line <linenumber> "<filename>"
627 std::vector<std::string> tokens;
628 std::stringstream stream(string);
629 std::string temp;
630 uint32_t line_index = 0;
631
632 while (stream >> temp) tokens.push_back(temp);
633 auto size = tokens.size();
634 if (size > 1) {
635 if (tokens[0] == "#" && tokens[1] == "line") {
636 line_index = 2;
637 } else if (tokens[0] == "#line") {
638 line_index = 1;
639 }
640 }
641 if (0 == line_index) return false;
Mark Young0ec6b062020-11-19 15:32:17 -0700642 *linenumber = static_cast<uint32_t>(std::stoul(tokens[line_index]));
Tony-LunarG1dce2392019-10-23 16:49:29 -0600643 uint32_t filename_index = line_index + 1;
644 // Remove enclosing double quotes around filename
645 if (size > filename_index) filename = tokens[filename_index].substr(1, tokens[filename_index].size() - 2);
646 return true;
647}
648#else
649bool GetLineAndFilename(const std::string string, uint32_t *linenumber, std::string &filename) {
650 static const std::regex line_regex( // matches #line directives
651 "^" // beginning of line
652 "\\s*" // optional whitespace
653 "#" // required text
654 "\\s*" // optional whitespace
655 "line" // required text
656 "\\s+" // required whitespace
657 "([0-9]+)" // required first capture - line number
658 "(\\s+)?" // optional second capture - whitespace
659 "(\".+\")?" // optional third capture - quoted filename with at least one char inside
660 ".*"); // rest of line (needed when using std::regex_match since the entire line is tested)
661
662 std::smatch captures;
663
664 bool found_line = std::regex_match(string, captures, line_regex);
665 if (!found_line) return false;
666
667 // filename is optional and considered found only if the whitespace and the filename are captured
668 if (captures[2].matched && captures[3].matched) {
669 // Remove enclosing double quotes. The regex guarantees the quotes and at least one char.
670 filename = captures[3].str().substr(1, captures[3].str().size() - 2);
671 }
Artem Bolgar82d08362021-06-03 13:11:13 -0700672 *linenumber = (uint32_t)std::stoul(captures[1]);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600673 return true;
674}
675#endif // GCC_VERSION
676
677// Extract the filename, line number, and column number from the correct OpLine and build a message string from it.
678// Scan the source (from OpSource) to find the line of source at the reported line number and place it in another message string.
sfricke-samsung7fac88a2022-01-26 11:44:22 -0800679void UtilGenerateSourceMessages(const std::vector<uint32_t> &pgm, const uint32_t *debug_record, bool from_printf,
Tony-LunarGb5fae462020-03-05 12:43:25 -0700680 std::string &filename_msg, std::string &source_msg) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600681 using namespace spvtools;
682 std::ostringstream filename_stream;
683 std::ostringstream source_stream;
Nathaniel Cesario77cd59b2021-10-11 23:52:24 -0600684 SHADER_MODULE_STATE shader(pgm);
Tony-LunarG1dce2392019-10-23 16:49:29 -0600685 // Find the OpLine just before the failing instruction indicated by the debug info.
686 // SPIR-V can only be iterated in the forward direction due to its opcode/length encoding.
687 uint32_t instruction_index = 0;
688 uint32_t reported_file_id = 0;
689 uint32_t reported_line_number = 0;
690 uint32_t reported_column_number = 0;
691 if (shader.words.size() > 0) {
John Zulauf79f06582021-02-27 18:38:39 -0700692 for (const auto &insn : shader) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600693 if (insn.opcode() == spv::OpLine) {
694 reported_file_id = insn.word(1);
695 reported_line_number = insn.word(2);
696 reported_column_number = insn.word(3);
697 }
698 if (instruction_index == debug_record[kInstCommonOutInstructionIdx]) {
699 break;
700 }
701 instruction_index++;
702 }
703 }
704 // Create message with file information obtained from the OpString pointed to by the discovered OpLine.
705 std::string reported_filename;
706 if (reported_file_id == 0) {
707 filename_stream
708 << "Unable to find SPIR-V OpLine for source information. Build shader with debug info to get source information.";
709 } else {
710 bool found_opstring = false;
711 std::string prefix;
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700712 if (from_printf) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600713 prefix = "Debug shader printf message generated ";
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700714 } else {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600715 prefix = "Shader validation error occurred ";
Nathaniel Cesarioce9b4812020-12-17 08:55:28 -0700716 }
John Zulauf79f06582021-02-27 18:38:39 -0700717 for (const auto &insn : shader) {
Tony-LunarG1dce2392019-10-23 16:49:29 -0600718 if ((insn.opcode() == spv::OpString) && (insn.len() >= 3) && (insn.word(1) == reported_file_id)) {
719 found_opstring = true;
720 reported_filename = (char *)&insn.word(2);
721 if (reported_filename.empty()) {
722 filename_stream << prefix << "at line " << reported_line_number;
723 } else {
724 filename_stream << prefix << "in file " << reported_filename << " at line " << reported_line_number;
725 }
726 if (reported_column_number > 0) {
727 filename_stream << ", column " << reported_column_number;
728 }
729 filename_stream << ".";
730 break;
731 }
732 }
733 if (!found_opstring) {
Tony-LunarG6d195e12020-10-27 16:54:14 -0600734 filename_stream << "Unable to find SPIR-V OpString for file id " << reported_file_id << " from OpLine instruction."
735 << std::endl;
736 filename_stream << "File ID = " << reported_file_id << ", Line Number = " << reported_line_number
737 << ", Column = " << reported_column_number << std::endl;
Tony-LunarG1dce2392019-10-23 16:49:29 -0600738 }
739 }
740 filename_msg = filename_stream.str();
741
742 // Create message to display source code line containing error.
743 if ((reported_file_id != 0)) {
744 // Read the source code and split it up into separate lines.
745 std::vector<std::string> opsource_lines;
746 ReadOpSource(shader, reported_file_id, opsource_lines);
747 // Find the line in the OpSource content that corresponds to the reported error file and line.
748 if (!opsource_lines.empty()) {
749 uint32_t saved_line_number = 0;
750 std::string current_filename = reported_filename; // current "preprocessor" filename state.
751 std::vector<std::string>::size_type saved_opsource_offset = 0;
752 bool found_best_line = false;
753 for (auto it = opsource_lines.begin(); it != opsource_lines.end(); ++it) {
754 uint32_t parsed_line_number;
755 std::string parsed_filename;
756 bool found_line = GetLineAndFilename(*it, &parsed_line_number, parsed_filename);
757 if (!found_line) continue;
758
759 bool found_filename = parsed_filename.size() > 0;
760 if (found_filename) {
761 current_filename = parsed_filename;
762 }
763 if ((!found_filename) || (current_filename == reported_filename)) {
764 // Update the candidate best line directive, if the current one is prior and closer to the reported line
765 if (reported_line_number >= parsed_line_number) {
766 if (!found_best_line ||
767 (reported_line_number - parsed_line_number <= reported_line_number - saved_line_number)) {
768 saved_line_number = parsed_line_number;
769 saved_opsource_offset = std::distance(opsource_lines.begin(), it);
770 found_best_line = true;
771 }
772 }
773 }
774 }
775 if (found_best_line) {
776 assert(reported_line_number >= saved_line_number);
777 std::vector<std::string>::size_type opsource_index =
778 (reported_line_number - saved_line_number) + 1 + saved_opsource_offset;
779 if (opsource_index < opsource_lines.size()) {
780 source_stream << "\n" << reported_line_number << ": " << opsource_lines[opsource_index].c_str();
781 } else {
782 source_stream << "Internal error: calculated source line of " << opsource_index << " for source size of "
783 << opsource_lines.size() << " lines.";
784 }
785 } else {
786 source_stream << "Unable to find suitable #line directive in SPIR-V OpSource.";
787 }
788 } else {
789 source_stream << "Unable to find SPIR-V OpSource.";
790 }
791 }
792 source_msg = source_stream.str();
793}