blob: d3d7ff4d72697a07da4e8a6d6c43417089b7911d [file] [log] [blame]
Mark Lobodzinski91e50bf2020-01-14 09:55:11 -07001/* Copyright (c) 2015-2020 The Khronos Group Inc.
2 * Copyright (c) 2015-2020 Valve Corporation
3 * Copyright (c) 2015-2020 LunarG, Inc.
Camdeneaa86ea2019-07-26 11:00:09 -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: Camden Stocker <camden@lunarg.com>
18 */
19
Mark Lobodzinski57b8ae82020-02-20 16:37:14 -070020#include "best_practices_validation.h"
Camden5b184be2019-08-13 07:50:19 -060021#include "layer_chassis_dispatch.h"
Camden Stocker0a660ce2019-08-27 15:30:40 -060022#include "best_practices_error_enums.h"
Camden5b184be2019-08-13 07:50:19 -060023
24#include <string>
25#include <iomanip>
Sam Walls8e77e4f2020-03-16 20:47:40 +000026#include <bitset>
Camden5b184be2019-08-13 07:50:19 -060027
28// get the API name is proper format
Jeff Bolz46c0ea02019-10-09 13:06:29 -050029std::string BestPractices::GetAPIVersionName(uint32_t version) const {
Camden5b184be2019-08-13 07:50:19 -060030 std::stringstream version_name;
31 uint32_t major = VK_VERSION_MAJOR(version);
32 uint32_t minor = VK_VERSION_MINOR(version);
33 uint32_t patch = VK_VERSION_PATCH(version);
34
35 version_name << major << "." << minor << "." << patch << " (0x" << std::setfill('0') << std::setw(8) << std::hex << version
36 << ")";
37
38 return version_name.str();
39}
40
Attilio Provenzano19d6a982020-02-27 12:41:41 +000041struct VendorSpecificInfo {
Mark Lobodzinski90eea5b2020-05-15 12:54:00 -060042 EnableFlags vendor_id;
Attilio Provenzano19d6a982020-02-27 12:41:41 +000043 std::string name;
44};
45
46const std::map<BPVendorFlagBits, VendorSpecificInfo> vendor_info = {
Mark Lobodzinski90eea5b2020-05-15 12:54:00 -060047 {kBPVendorArm, {vendor_specific_arm, "Arm"}},
Attilio Provenzano19d6a982020-02-27 12:41:41 +000048};
49
50bool BestPractices::VendorCheckEnabled(BPVendorFlags vendors) const {
51 for (const auto& vendor : vendor_info) {
Mark Lobodzinski90eea5b2020-05-15 12:54:00 -060052 if (vendors & vendor.first && enabled[vendor.second.vendor_id]) {
Attilio Provenzano19d6a982020-02-27 12:41:41 +000053 return true;
54 }
55 }
56 return false;
57}
58
59const char* VendorSpecificTag(BPVendorFlags vendors) {
60 // Cache built vendor tags in a map
61 static std::unordered_map<BPVendorFlags, std::string> tag_map;
62
63 auto res = tag_map.find(vendors);
64 if (res == tag_map.end()) {
65 // Build the vendor tag string
66 std::stringstream vendor_tag;
67
68 vendor_tag << "[";
69 bool first_vendor = true;
70 for (const auto& vendor : vendor_info) {
71 if (vendors & vendor.first) {
72 if (!first_vendor) {
73 vendor_tag << ", ";
74 }
75 vendor_tag << vendor.second.name;
76 first_vendor = false;
77 }
78 }
79 vendor_tag << "]";
80
81 tag_map[vendors] = vendor_tag.str();
82 res = tag_map.find(vendors);
83 }
84
85 return res->second.c_str();
86}
87
Mark Lobodzinski6167e102020-02-24 17:03:55 -070088const char* DepReasonToString(ExtDeprecationReason reason) {
89 switch (reason) {
90 case kExtPromoted:
91 return "promoted to";
92 break;
93 case kExtObsoleted:
94 return "obsoleted by";
95 break;
96 case kExtDeprecated:
97 return "deprecated by";
98 break;
99 default:
100 return "";
101 break;
102 }
103}
104
105bool BestPractices::ValidateDeprecatedExtensions(const char* api_name, const char* extension_name, uint32_t version,
106 const char* vuid) const {
107 bool skip = false;
108 auto dep_info_it = deprecated_extensions.find(extension_name);
109 if (dep_info_it != deprecated_extensions.end()) {
110 auto dep_info = dep_info_it->second;
Mark Lobodzinski6a149702020-05-14 12:21:34 -0600111 if (((dep_info.target.compare("VK_VERSION_1_1") == 0) && (version >= VK_API_VERSION_1_1)) ||
112 ((dep_info.target.compare("VK_VERSION_1_2") == 0) && (version >= VK_API_VERSION_1_2))) {
Mark Lobodzinski6167e102020-02-24 17:03:55 -0700113 skip |=
114 LogWarning(instance, vuid, "%s(): Attempting to enable deprecated extension %s, but this extension has been %s %s.",
115 api_name, extension_name, DepReasonToString(dep_info.reason), (dep_info.target).c_str());
Mark Lobodzinski6a149702020-05-14 12:21:34 -0600116 } else if (dep_info.target.find("VK_VERSION") == std::string::npos) {
Mark Lobodzinski6167e102020-02-24 17:03:55 -0700117 if (dep_info.target.length() == 0) {
118 skip |= LogWarning(instance, vuid,
119 "%s(): Attempting to enable deprecated extension %s, but this extension has been deprecated "
120 "without replacement.",
121 api_name, extension_name);
122 } else {
123 skip |= LogWarning(instance, vuid,
124 "%s(): Attempting to enable deprecated extension %s, but this extension has been %s %s.",
125 api_name, extension_name, DepReasonToString(dep_info.reason), (dep_info.target).c_str());
126 }
127 }
128 }
129 return skip;
130}
131
Camden5b184be2019-08-13 07:50:19 -0600132bool BestPractices::PreCallValidateCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500133 VkInstance* pInstance) const {
Camden5b184be2019-08-13 07:50:19 -0600134 bool skip = false;
135
136 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
137 if (white_list(pCreateInfo->ppEnabledExtensionNames[i], kDeviceExtensionNames)) {
Camden Stocker11ecf512020-01-21 16:06:49 -0800138 skip |= LogWarning(instance, kVUID_BestPractices_CreateInstance_ExtensionMismatch,
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700139 "vkCreateInstance(): Attempting to enable Device Extension %s at CreateInstance time.",
140 pCreateInfo->ppEnabledExtensionNames[i]);
Camden5b184be2019-08-13 07:50:19 -0600141 }
Mark Lobodzinski17d8dc62020-06-03 08:48:58 -0600142 uint32_t specified_version =
143 (pCreateInfo->pApplicationInfo ? pCreateInfo->pApplicationInfo->apiVersion : VK_API_VERSION_1_0);
144 skip |= ValidateDeprecatedExtensions("CreateInstance", pCreateInfo->ppEnabledExtensionNames[i], specified_version,
Mark Lobodzinski6167e102020-02-24 17:03:55 -0700145 kVUID_BestPractices_CreateInstance_DeprecatedExtension);
Camden5b184be2019-08-13 07:50:19 -0600146 }
147
148 return skip;
149}
150
151void BestPractices::PreCallRecordCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator,
152 VkInstance* pInstance) {
Mark Lobodzinski97484d62020-03-03 11:57:41 -0700153 ValidationStateTracker::PreCallRecordCreateInstance(pCreateInfo, pAllocator, pInstance);
Sam Walls53bf7652020-04-21 17:35:15 +0100154
155 if (pCreateInfo != nullptr && pCreateInfo->pApplicationInfo != nullptr)
156 instance_api_version = pCreateInfo->pApplicationInfo->apiVersion;
157 else
158 instance_api_version = 0;
Camden5b184be2019-08-13 07:50:19 -0600159}
160
161bool BestPractices::PreCallValidateCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500162 const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) const {
Camden5b184be2019-08-13 07:50:19 -0600163 bool skip = false;
164
165 // get API version of physical device passed when creating device.
166 VkPhysicalDeviceProperties physical_device_properties{};
167 DispatchGetPhysicalDeviceProperties(physicalDevice, &physical_device_properties);
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500168 auto device_api_version = physical_device_properties.apiVersion;
Camden5b184be2019-08-13 07:50:19 -0600169
170 // check api versions and warn if instance api Version is higher than version on device.
171 if (instance_api_version > device_api_version) {
172 std::string inst_api_name = GetAPIVersionName(instance_api_version);
173 std::string dev_api_name = GetAPIVersionName(device_api_version);
174
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700175 skip |= LogWarning(device, kVUID_BestPractices_CreateDevice_API_Mismatch,
176 "vkCreateDevice(): API Version of current instance, %s is higher than API Version on device, %s",
177 inst_api_name.c_str(), dev_api_name.c_str());
Camden5b184be2019-08-13 07:50:19 -0600178 }
179
180 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
181 if (white_list(pCreateInfo->ppEnabledExtensionNames[i], kInstanceExtensionNames)) {
Camden Stocker11ecf512020-01-21 16:06:49 -0800182 skip |= LogWarning(instance, kVUID_BestPractices_CreateDevice_ExtensionMismatch,
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700183 "vkCreateDevice(): Attempting to enable Instance Extension %s at CreateDevice time.",
184 pCreateInfo->ppEnabledExtensionNames[i]);
Camden5b184be2019-08-13 07:50:19 -0600185 }
Mark Lobodzinski6167e102020-02-24 17:03:55 -0700186 skip |= ValidateDeprecatedExtensions("CreateDevice", pCreateInfo->ppEnabledExtensionNames[i], instance_api_version,
187 kVUID_BestPractices_CreateDevice_DeprecatedExtension);
Camden5b184be2019-08-13 07:50:19 -0600188 }
189
Camden83a9c372019-08-14 11:41:38 -0600190 auto pd_state = GetPhysicalDeviceState(physicalDevice);
Corta48da1d2019-09-20 18:59:07 +0200191 if ((pd_state->vkGetPhysicalDeviceFeaturesState == UNCALLED) && (pCreateInfo->pEnabledFeatures != NULL)) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700192 skip |= LogWarning(device, kVUID_BestPractices_CreateDevice_PDFeaturesNotCalled,
193 "vkCreateDevice() called before getting physical device features from vkGetPhysicalDeviceFeatures().");
Camden83a9c372019-08-14 11:41:38 -0600194 }
195
Camden5b184be2019-08-13 07:50:19 -0600196 return skip;
197}
198
199bool BestPractices::PreCallValidateCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500200 const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) const {
Camden5b184be2019-08-13 07:50:19 -0600201 bool skip = false;
202
203 if ((pCreateInfo->queueFamilyIndexCount > 1) && (pCreateInfo->sharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
204 std::stringstream bufferHex;
205 bufferHex << "0x" << std::hex << HandleToUint64(pBuffer);
206
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700207 skip |= LogWarning(
208 device, kVUID_BestPractices_SharingModeExclusive,
209 "Warning: Buffer (%s) specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE while specifying multiple queues "
210 "(queueFamilyIndexCount of %" PRIu32 ").",
211 bufferHex.str().c_str(), pCreateInfo->queueFamilyIndexCount);
Camden5b184be2019-08-13 07:50:19 -0600212 }
213
214 return skip;
215}
216
217bool BestPractices::PreCallValidateCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500218 const VkAllocationCallbacks* pAllocator, VkImage* pImage) const {
Camden5b184be2019-08-13 07:50:19 -0600219 bool skip = false;
220
221 if ((pCreateInfo->queueFamilyIndexCount > 1) && (pCreateInfo->sharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
222 std::stringstream imageHex;
223 imageHex << "0x" << std::hex << HandleToUint64(pImage);
224
225 skip |=
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700226 LogWarning(device, kVUID_BestPractices_SharingModeExclusive,
227 "Warning: Image (%s) specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE while specifying multiple queues "
228 "(queueFamilyIndexCount of %" PRIu32 ").",
229 imageHex.str().c_str(), pCreateInfo->queueFamilyIndexCount);
Camden5b184be2019-08-13 07:50:19 -0600230 }
231
Attilio Provenzano02859b22020-02-27 14:17:28 +0000232 if (VendorCheckEnabled(kBPVendorArm)) {
233 if (pCreateInfo->samples > kMaxEfficientSamplesArm) {
234 skip |= LogPerformanceWarning(
235 device, kVUID_BestPractices_CreateImage_TooLargeSampleCount,
236 "%s vkCreateImage(): Trying to create an image with %u samples. "
237 "The hardware revision may not have full throughput for framebuffers with more than %u samples.",
238 VendorSpecificTag(kBPVendorArm), static_cast<uint32_t>(pCreateInfo->samples), kMaxEfficientSamplesArm);
239 }
240
241 if (pCreateInfo->samples > VK_SAMPLE_COUNT_1_BIT && !(pCreateInfo->usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT)) {
242 skip |= LogPerformanceWarning(
243 device, kVUID_BestPractices_CreateImage_NonTransientMSImage,
244 "%s vkCreateImage(): Trying to create a multisampled image, but createInfo.usage did not have "
245 "VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT set. Multisampled images may be resolved on-chip, "
246 "and do not need to be backed by physical storage. "
247 "TRANSIENT_ATTACHMENT allows tiled GPUs to not back the multisampled image with physical memory.",
248 VendorSpecificTag(kBPVendorArm));
249 }
250 }
251
Camden5b184be2019-08-13 07:50:19 -0600252 return skip;
253}
254
255bool BestPractices::PreCallValidateCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500256 const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) const {
Camden5b184be2019-08-13 07:50:19 -0600257 bool skip = false;
258
Camden83a9c372019-08-14 11:41:38 -0600259 auto physical_device_state = GetPhysicalDeviceState();
260
261 if (physical_device_state->vkGetPhysicalDeviceSurfaceCapabilitiesKHRState == UNCALLED) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700262 skip |= LogWarning(
263 device, kVUID_BestPractices_Swapchain_GetSurfaceNotCalled,
Camden83a9c372019-08-14 11:41:38 -0600264 "vkCreateSwapchainKHR() called before getting surface capabilities from vkGetPhysicalDeviceSurfaceCapabilitiesKHR().");
265 }
266
267 if (physical_device_state->vkGetPhysicalDeviceSurfacePresentModesKHRState != QUERY_DETAILS) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700268 skip |= LogWarning(device, kVUID_BestPractices_Swapchain_GetSurfaceNotCalled,
269 "vkCreateSwapchainKHR() called before getting surface present mode(s) from "
270 "vkGetPhysicalDeviceSurfacePresentModesKHR().");
Camden83a9c372019-08-14 11:41:38 -0600271 }
272
273 if (physical_device_state->vkGetPhysicalDeviceSurfaceFormatsKHRState != QUERY_DETAILS) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700274 skip |= LogWarning(
275 device, kVUID_BestPractices_Swapchain_GetSurfaceNotCalled,
276 "vkCreateSwapchainKHR() called before getting surface format(s) from vkGetPhysicalDeviceSurfaceFormatsKHR().");
Camden83a9c372019-08-14 11:41:38 -0600277 }
278
Camden5b184be2019-08-13 07:50:19 -0600279 if ((pCreateInfo->queueFamilyIndexCount > 1) && (pCreateInfo->imageSharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700280 skip |=
281 LogWarning(device, kVUID_BestPractices_SharingModeExclusive,
Mark Lobodzinski019f4e32020-04-13 11:01:35 -0600282 "Warning: A Swapchain is being created which specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE while "
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700283 "specifying multiple queues (queueFamilyIndexCount of %" PRIu32 ").",
284 pCreateInfo->queueFamilyIndexCount);
Camden5b184be2019-08-13 07:50:19 -0600285 }
286
Szilard Papp48a6da32020-06-10 14:41:59 +0100287 if (pCreateInfo->minImageCount == 2) {
288 skip |= LogPerformanceWarning(
289 device, kVUID_BestPractices_SuboptimalSwapchainImageCount,
290 "Warning: A Swapchain is being created with minImageCount set to %" PRIu32
291 ", which means double buffering is going "
292 "to be used. Using double buffering and vsync locks rendering to an integer fraction of the vsync rate. In turn, "
293 "reducing the performance of the application if rendering is slower than vsync. Consider setting minImageCount to "
294 "3 to use triple buffering to maximize performance in such cases.",
295 pCreateInfo->minImageCount);
296 }
297
Camden5b184be2019-08-13 07:50:19 -0600298 return skip;
299}
300
301bool BestPractices::PreCallValidateCreateSharedSwapchainsKHR(VkDevice device, uint32_t swapchainCount,
302 const VkSwapchainCreateInfoKHR* pCreateInfos,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500303 const VkAllocationCallbacks* pAllocator,
304 VkSwapchainKHR* pSwapchains) const {
Camden5b184be2019-08-13 07:50:19 -0600305 bool skip = false;
306
307 for (uint32_t i = 0; i < swapchainCount; i++) {
308 if ((pCreateInfos[i].queueFamilyIndexCount > 1) && (pCreateInfos[i].imageSharingMode == VK_SHARING_MODE_EXCLUSIVE)) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700309 skip |= LogWarning(
310 device, kVUID_BestPractices_SharingModeExclusive,
311 "Warning: A shared swapchain (index %" PRIu32
312 ") is being created which specifies a sharing mode of VK_SHARING_MODE_EXCLUSIVE while specifying multiple "
313 "queues (queueFamilyIndexCount of %" PRIu32 ").",
314 i, pCreateInfos[i].queueFamilyIndexCount);
Camden5b184be2019-08-13 07:50:19 -0600315 }
316 }
317
318 return skip;
319}
320
321bool BestPractices::PreCallValidateCreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500322 const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) const {
Camden5b184be2019-08-13 07:50:19 -0600323 bool skip = false;
324
325 for (uint32_t i = 0; i < pCreateInfo->attachmentCount; ++i) {
326 VkFormat format = pCreateInfo->pAttachments[i].format;
327 if (pCreateInfo->pAttachments[i].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
328 if ((FormatIsColor(format) || FormatHasDepth(format)) &&
329 pCreateInfo->pAttachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700330 skip |= LogWarning(device, kVUID_BestPractices_RenderPass_Attatchment,
331 "Render pass has an attachment with loadOp == VK_ATTACHMENT_LOAD_OP_LOAD and "
332 "initialLayout == VK_IMAGE_LAYOUT_UNDEFINED. This is probably not what you "
333 "intended. Consider using VK_ATTACHMENT_LOAD_OP_DONT_CARE instead if the "
334 "image truely is undefined at the start of the render pass.");
Camden5b184be2019-08-13 07:50:19 -0600335 }
336 if (FormatHasStencil(format) && pCreateInfo->pAttachments[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700337 skip |= LogWarning(device, kVUID_BestPractices_RenderPass_Attatchment,
338 "Render pass has an attachment with stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD "
339 "and initialLayout == VK_IMAGE_LAYOUT_UNDEFINED. This is probably not what you "
340 "intended. Consider using VK_ATTACHMENT_LOAD_OP_DONT_CARE instead if the "
341 "image truely is undefined at the start of the render pass.");
Camden5b184be2019-08-13 07:50:19 -0600342 }
343 }
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000344
345 const auto& attachment = pCreateInfo->pAttachments[i];
346 if (attachment.samples > VK_SAMPLE_COUNT_1_BIT) {
347 bool access_requires_memory =
348 attachment.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD || attachment.storeOp == VK_ATTACHMENT_STORE_OP_STORE;
349
350 if (FormatHasStencil(format)) {
351 access_requires_memory |= attachment.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD ||
352 attachment.stencilStoreOp == VK_ATTACHMENT_STORE_OP_STORE;
353 }
354
355 if (access_requires_memory) {
356 skip |= LogPerformanceWarning(
357 device, kVUID_BestPractices_CreateRenderPass_ImageRequiresMemory,
358 "Attachment %u in the VkRenderPass is a multisampled image with %u samples, but it uses loadOp/storeOp "
359 "which requires accessing data from memory. Multisampled images should always be loadOp = CLEAR or DONT_CARE, "
360 "storeOp = DONT_CARE. This allows the implementation to use lazily allocated memory effectively.",
361 i, static_cast<uint32_t>(attachment.samples));
362 }
363 }
Camden5b184be2019-08-13 07:50:19 -0600364 }
365
366 for (uint32_t dependency = 0; dependency < pCreateInfo->dependencyCount; dependency++) {
367 skip |= CheckPipelineStageFlags("vkCreateRenderPass", pCreateInfo->pDependencies[dependency].srcStageMask);
368 skip |= CheckPipelineStageFlags("vkCreateRenderPass", pCreateInfo->pDependencies[dependency].dstStageMask);
369 }
370
371 return skip;
372}
373
Tony-LunarG767180f2020-04-23 14:03:59 -0600374bool BestPractices::ValidateAttachments(const VkRenderPassCreateInfo2* rpci, uint32_t attachmentCount,
375 const VkImageView* image_views) const {
376 bool skip = false;
377
378 // Check for non-transient attachments that should be transient and vice versa
379 for (uint32_t i = 0; i < attachmentCount; ++i) {
380 auto& attachment = rpci->pAttachments[i];
381 bool attachment_should_be_transient =
382 (attachment.loadOp != VK_ATTACHMENT_LOAD_OP_LOAD && attachment.storeOp != VK_ATTACHMENT_STORE_OP_STORE);
383
384 if (FormatHasStencil(attachment.format)) {
385 attachment_should_be_transient &= (attachment.stencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD &&
386 attachment.stencilStoreOp != VK_ATTACHMENT_STORE_OP_STORE);
387 }
388
389 auto view_state = GetImageViewState(image_views[i]);
390 if (view_state) {
391 auto& ivci = view_state->create_info;
392 auto& ici = GetImageState(ivci.image)->createInfo;
393
394 bool image_is_transient = (ici.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0;
395
396 // The check for an image that should not be transient applies to all GPUs
397 if (!attachment_should_be_transient && image_is_transient) {
398 skip |= LogPerformanceWarning(
399 device, kVUID_BestPractices_CreateFramebuffer_AttachmentShouldNotBeTransient,
400 "Attachment %u in VkFramebuffer uses loadOp/storeOps which need to access physical memory, "
401 "but the image backing the image view has VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT set. "
402 "Physical memory will need to be backed lazily to this image, potentially causing stalls.",
403 i);
404 }
405
406 bool supports_lazy = false;
407 for (uint32_t j = 0; j < phys_dev_mem_props.memoryTypeCount; j++) {
408 if (phys_dev_mem_props.memoryTypes[j].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
409 supports_lazy = true;
410 }
411 }
412
413 // The check for an image that should be transient only applies to GPUs supporting
414 // lazily allocated memory
415 if (supports_lazy && attachment_should_be_transient && !image_is_transient) {
416 skip |= LogPerformanceWarning(
417 device, kVUID_BestPractices_CreateFramebuffer_AttachmentShouldBeTransient,
418 "Attachment %u in VkFramebuffer uses loadOp/storeOps which never have to be backed by physical memory, "
419 "but the image backing the image view does not have VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT set. "
420 "You can save physical memory by using transient attachment backed by lazily allocated memory here.",
421 i);
422 }
423 }
424 }
425 return skip;
426}
427
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000428bool BestPractices::PreCallValidateCreateFramebuffer(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo,
429 const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer) const {
430 bool skip = false;
431
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000432 auto rp_state = GetRenderPassState(pCreateInfo->renderPass);
Tony-LunarG767180f2020-04-23 14:03:59 -0600433 if (rp_state && !(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR)) {
434 skip = ValidateAttachments(rp_state->createInfo.ptr(), pCreateInfo->attachmentCount, pCreateInfo->pAttachments);
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000435 }
436
437 return skip;
438}
439
Sam Wallse746d522020-03-16 21:20:23 +0000440bool BestPractices::PreCallValidateAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo,
441 VkDescriptorSet* pDescriptorSets, void* ads_state_data) const {
442 bool skip = false;
443 skip |= ValidationStateTracker::PreCallValidateAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, ads_state_data);
444
445 if (!skip) {
446 const auto& pool_handle = pAllocateInfo->descriptorPool;
447 auto iter = descriptor_pool_freed_count.find(pool_handle);
448 // if the number of freed sets > 0, it implies they could be recycled instead if desirable
449 // this warning is specific to Arm
450 if (VendorCheckEnabled(kBPVendorArm) && iter != descriptor_pool_freed_count.end() && iter->second > 0) {
451 skip |= LogPerformanceWarning(
452 device, kVUID_BestPractices_AllocateDescriptorSets_SuboptimalReuse,
453 "%s Descriptor set memory was allocated via vkAllocateDescriptorSets() for sets which were previously freed in the "
454 "same logical device. On some drivers or architectures it may be most optimal to re-use existing descriptor sets.",
455 VendorSpecificTag(kBPVendorArm));
456 }
457 }
458
459 return skip;
460}
461
Mark Lobodzinski84101d72020-04-24 09:43:48 -0600462void BestPractices::ManualPostCallRecordAllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo,
463 VkDescriptorSet* pDescriptorSets, VkResult result, void* ads_state) {
Sam Wallse746d522020-03-16 21:20:23 +0000464 if (result == VK_SUCCESS) {
465 // find the free count for the pool we allocated into
466 auto iter = descriptor_pool_freed_count.find(pAllocateInfo->descriptorPool);
467 if (iter != descriptor_pool_freed_count.end()) {
468 // we record successful allocations by subtracting the allocation count from the last recorded free count
469 const auto alloc_count = pAllocateInfo->descriptorSetCount;
470 // clamp the unsigned subtraction to the range [0, last_free_count]
471 if (iter->second > alloc_count)
472 iter->second -= alloc_count;
473 else
474 iter->second = 0;
475 }
476 }
477}
478
479void BestPractices::PostCallRecordFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount,
480 const VkDescriptorSet* pDescriptorSets, VkResult result) {
481 ValidationStateTracker::PostCallRecordFreeDescriptorSets(device, descriptorPool, descriptorSetCount, pDescriptorSets, result);
482 if (result == VK_SUCCESS) {
483 // we want to track frees because we're interested in suggesting re-use
484 auto iter = descriptor_pool_freed_count.find(descriptorPool);
485 if (iter == descriptor_pool_freed_count.end()) {
486 descriptor_pool_freed_count.insert(std::make_pair(descriptorPool, descriptorSetCount));
487 } else {
488 iter->second += descriptorSetCount;
489 }
490 }
491}
492
Camden5b184be2019-08-13 07:50:19 -0600493bool BestPractices::PreCallValidateAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500494 const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) const {
Camden5b184be2019-08-13 07:50:19 -0600495 bool skip = false;
496
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500497 if (num_mem_objects + 1 > kMemoryObjectWarningLimit) {
Mark Lobodzinskif95a2662020-01-29 15:43:32 -0700498 skip |= LogPerformanceWarning(device, kVUID_BestPractices_AllocateMemory_TooManyObjects,
499 "Performance Warning: This app has > %" PRIu32 " memory objects.", kMemoryObjectWarningLimit);
Camden5b184be2019-08-13 07:50:19 -0600500 }
501
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000502 if (pAllocateInfo->allocationSize < kMinDeviceAllocationSize) {
503 skip |= LogPerformanceWarning(
504 device, kVUID_BestPractices_AllocateMemory_SmallAllocation,
505 "vkAllocateMemory(): Allocating a VkDeviceMemory of size %llu. This is a very small allocation (current "
506 "threshold is %llu bytes). "
507 "You should make large allocations and sub-allocate from one large VkDeviceMemory.",
508 pAllocateInfo->allocationSize, kMinDeviceAllocationSize);
509 }
510
Camden83a9c372019-08-14 11:41:38 -0600511 // TODO: Insert get check for GetPhysicalDeviceMemoryProperties once the state is tracked in the StateTracker
512
513 return skip;
514}
515
Mark Lobodzinski84101d72020-04-24 09:43:48 -0600516void BestPractices::ManualPostCallRecordAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
517 const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory,
518 VkResult result) {
Mark Lobodzinski205b7a02020-02-21 13:23:17 -0700519 if (result != VK_SUCCESS) {
520 static std::vector<VkResult> error_codes = {VK_ERROR_OUT_OF_HOST_MEMORY, VK_ERROR_OUT_OF_DEVICE_MEMORY,
521 VK_ERROR_TOO_MANY_OBJECTS, VK_ERROR_INVALID_EXTERNAL_HANDLE,
522 VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR};
523 static std::vector<VkResult> success_codes = {};
524 ValidateReturnCodes("vkReleaseFullScreenExclusiveModeEXT", result, error_codes, success_codes);
525 return;
526 }
527 num_mem_objects++;
528}
Camden Stocker9738af92019-10-16 13:54:03 -0700529
Mark Lobodzinskide15e582020-04-29 08:06:00 -0600530void BestPractices::ValidateReturnCodes(const char* api_name, VkResult result, const std::vector<VkResult>& error_codes,
531 const std::vector<VkResult>& success_codes) const {
Mark Lobodzinski205b7a02020-02-21 13:23:17 -0700532 auto error = std::find(error_codes.begin(), error_codes.end(), result);
533 if (error != error_codes.end()) {
Mark Lobodzinski629defa2020-04-29 12:00:23 -0600534 LogWarning(instance, kVUID_BestPractices_Error_Result, "%s(): Returned error %s.", api_name, string_VkResult(result));
Mark Lobodzinski205b7a02020-02-21 13:23:17 -0700535 return;
536 }
537 auto success = std::find(success_codes.begin(), success_codes.end(), result);
538 if (success != success_codes.end()) {
Mark Lobodzinskie7215152020-05-11 08:21:23 -0600539 LogInfo(instance, kVUID_BestPractices_NonSuccess_Result, "%s(): Returned non-success return code %s.", api_name,
540 string_VkResult(result));
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500541 }
542}
543
Jeff Bolz5c801d12019-10-09 10:38:45 -0500544bool BestPractices::PreCallValidateFreeMemory(VkDevice device, VkDeviceMemory memory,
545 const VkAllocationCallbacks* pAllocator) const {
Mark Lobodzinski91e50bf2020-01-14 09:55:11 -0700546 if (memory == VK_NULL_HANDLE) return false;
Camden83a9c372019-08-14 11:41:38 -0600547 bool skip = false;
548
Camden Stocker9738af92019-10-16 13:54:03 -0700549 const DEVICE_MEMORY_STATE* mem_info = ValidationStateTracker::GetDevMemState(memory);
Camden83a9c372019-08-14 11:41:38 -0600550
551 for (auto& obj : mem_info->obj_bindings) {
Mark Lobodzinski818425a2020-03-16 18:19:03 -0600552 LogObjectList objlist(device);
553 objlist.add(obj);
554 objlist.add(mem_info->mem);
555 skip |= LogWarning(objlist, layer_name.c_str(), "VK Object %s still has a reference to mem obj %s.",
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700556 report_data->FormatHandle(obj).c_str(), report_data->FormatHandle(mem_info->mem).c_str());
Camden83a9c372019-08-14 11:41:38 -0600557 }
558
Camden5b184be2019-08-13 07:50:19 -0600559 return skip;
560}
561
562void BestPractices::PreCallRecordFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator) {
Mark Lobodzinski97484d62020-03-03 11:57:41 -0700563 ValidationStateTracker::PreCallRecordFreeMemory(device, memory, pAllocator);
Camden5b184be2019-08-13 07:50:19 -0600564 if (memory != VK_NULL_HANDLE) {
565 num_mem_objects--;
566 }
567}
568
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000569bool BestPractices::ValidateBindBufferMemory(VkBuffer buffer, VkDeviceMemory memory, const char* api_name) const {
Camden Stockerb603cc82019-09-03 10:09:02 -0600570 bool skip = false;
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500571 const BUFFER_STATE* buffer_state = GetBufferState(buffer);
Camden Stockerb603cc82019-09-03 10:09:02 -0600572
sfricke-samsunge2441192019-11-06 14:07:57 -0800573 if (!buffer_state->memory_requirements_checked && !buffer_state->external_memory_handle) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700574 skip |= LogWarning(device, kVUID_BestPractices_BufferMemReqNotCalled,
575 "%s: Binding memory to %s but vkGetBufferMemoryRequirements() has not been called on that buffer.",
576 api_name, report_data->FormatHandle(buffer).c_str());
Camden Stockerb603cc82019-09-03 10:09:02 -0600577 }
578
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000579 const DEVICE_MEMORY_STATE* mem_state = GetDevMemState(memory);
580
581 if (mem_state->alloc_info.allocationSize == buffer_state->createInfo.size &&
582 mem_state->alloc_info.allocationSize < kMinDedicatedAllocationSize) {
583 skip |= LogPerformanceWarning(
584 device, kVUID_BestPractices_SmallDedicatedAllocation,
585 "%s: Trying to bind %s to a memory block which is fully consumed by the buffer. "
586 "The required size of the allocation is %llu, but smaller buffers like this should be sub-allocated from "
587 "larger memory blocks. (Current threshold is %llu bytes.)",
588 api_name, report_data->FormatHandle(buffer).c_str(), mem_state->alloc_info.allocationSize, kMinDedicatedAllocationSize);
589 }
590
Camden Stockerb603cc82019-09-03 10:09:02 -0600591 return skip;
592}
593
594bool BestPractices::PreCallValidateBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500595 VkDeviceSize memoryOffset) const {
Camden Stockerb603cc82019-09-03 10:09:02 -0600596 bool skip = false;
597 const char* api_name = "BindBufferMemory()";
598
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000599 skip |= ValidateBindBufferMemory(buffer, memory, api_name);
Camden Stockerb603cc82019-09-03 10:09:02 -0600600
601 return skip;
602}
603
604bool BestPractices::PreCallValidateBindBufferMemory2(VkDevice device, uint32_t bindInfoCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500605 const VkBindBufferMemoryInfo* pBindInfos) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600606 char api_name[64];
607 bool skip = false;
608
609 for (uint32_t i = 0; i < bindInfoCount; i++) {
610 sprintf(api_name, "vkBindBufferMemory2() pBindInfos[%u]", i);
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000611 skip |= ValidateBindBufferMemory(pBindInfos[i].buffer, pBindInfos[i].memory, api_name);
Camden Stocker8b798ab2019-09-03 10:33:28 -0600612 }
613
614 return skip;
615}
Camden Stockerb603cc82019-09-03 10:09:02 -0600616
617bool BestPractices::PreCallValidateBindBufferMemory2KHR(VkDevice device, uint32_t bindInfoCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500618 const VkBindBufferMemoryInfo* pBindInfos) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600619 char api_name[64];
620 bool skip = false;
Camden Stockerb603cc82019-09-03 10:09:02 -0600621
Camden Stocker8b798ab2019-09-03 10:33:28 -0600622 for (uint32_t i = 0; i < bindInfoCount; i++) {
623 sprintf(api_name, "vkBindBufferMemory2KHR() pBindInfos[%u]", i);
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000624 skip |= ValidateBindBufferMemory(pBindInfos[i].buffer, pBindInfos[i].memory, api_name);
Camden Stocker8b798ab2019-09-03 10:33:28 -0600625 }
626
627 return skip;
628}
629
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000630bool BestPractices::ValidateBindImageMemory(VkImage image, VkDeviceMemory memory, const char* api_name) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600631 bool skip = false;
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500632 const IMAGE_STATE* image_state = GetImageState(image);
Camden Stocker8b798ab2019-09-03 10:33:28 -0600633
sfricke-samsung71bc6572020-04-29 15:49:43 -0700634 if (image_state->disjoint == false) {
sfricke-samsungd7ea5de2020-04-08 09:19:18 -0700635 if (!image_state->memory_requirements_checked && !image_state->external_memory_handle) {
636 skip |= LogWarning(device, kVUID_BestPractices_ImageMemReqNotCalled,
637 "%s: Binding memory to %s but vkGetImageMemoryRequirements() has not been called on that image.",
638 api_name, report_data->FormatHandle(image).c_str());
639 }
640 } else {
641 // TODO If binding disjoint image then this needs to check that VkImagePlaneMemoryRequirementsInfo was called for each
642 // plane.
Camden Stocker8b798ab2019-09-03 10:33:28 -0600643 }
644
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000645 const DEVICE_MEMORY_STATE* mem_state = GetDevMemState(memory);
646
647 if (mem_state->alloc_info.allocationSize == image_state->requirements.size &&
648 mem_state->alloc_info.allocationSize < kMinDedicatedAllocationSize) {
649 skip |= LogPerformanceWarning(
650 device, kVUID_BestPractices_SmallDedicatedAllocation,
651 "%s: Trying to bind %s to a memory block which is fully consumed by the image. "
652 "The required size of the allocation is %llu, but smaller images like this should be sub-allocated from "
653 "larger memory blocks. (Current threshold is %llu bytes.)",
654 api_name, report_data->FormatHandle(image).c_str(), mem_state->alloc_info.allocationSize, kMinDedicatedAllocationSize);
655 }
656
657 // If we're binding memory to a image which was created as TRANSIENT and the image supports LAZY allocation,
658 // make sure this type is actually used.
659 // This warning will only trigger if this layer is run on a platform that supports LAZILY_ALLOCATED_BIT
660 // (i.e.most tile - based renderers)
661 if (image_state->createInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) {
662 bool supports_lazy = false;
663 uint32_t suggested_type = 0;
664
665 for (uint32_t i = 0; i < phys_dev_mem_props.memoryTypeCount; i++) {
666 if ((1u << i) & image_state->requirements.memoryTypeBits) {
667 if (phys_dev_mem_props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
668 supports_lazy = true;
669 suggested_type = i;
670 break;
671 }
672 }
673 }
674
675 uint32_t allocated_properties = phys_dev_mem_props.memoryTypes[mem_state->alloc_info.memoryTypeIndex].propertyFlags;
676
677 if (supports_lazy && (allocated_properties & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) == 0) {
678 skip |= LogPerformanceWarning(
679 device, kVUID_BestPractices_NonLazyTransientImage,
680 "%s: Attempting to bind memory type % u to VkImage which was created with TRANSIENT_ATTACHMENT_BIT,"
681 "but this memory type is not LAZILY_ALLOCATED_BIT. You should use memory type %u here instead to save "
682 "%llu bytes of physical memory.",
683 api_name, mem_state->alloc_info.memoryTypeIndex, suggested_type, image_state->requirements.size);
684 }
685 }
686
Camden Stocker8b798ab2019-09-03 10:33:28 -0600687 return skip;
688}
689
690bool BestPractices::PreCallValidateBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500691 VkDeviceSize memoryOffset) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600692 bool skip = false;
693 const char* api_name = "vkBindImageMemory()";
694
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000695 skip |= ValidateBindImageMemory(image, memory, api_name);
Camden Stocker8b798ab2019-09-03 10:33:28 -0600696
697 return skip;
698}
699
700bool BestPractices::PreCallValidateBindImageMemory2(VkDevice device, uint32_t bindInfoCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500701 const VkBindImageMemoryInfo* pBindInfos) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600702 char api_name[64];
703 bool skip = false;
704
705 for (uint32_t i = 0; i < bindInfoCount; i++) {
706 sprintf(api_name, "vkBindImageMemory2() pBindInfos[%u]", i);
Tony-LunarG5e60b852020-04-27 11:27:54 -0600707 if (!lvl_find_in_chain<VkBindImageMemorySwapchainInfoKHR>(pBindInfos[i].pNext)) {
708 skip |= ValidateBindImageMemory(pBindInfos[i].image, pBindInfos[i].memory, api_name);
709 }
Camden Stocker8b798ab2019-09-03 10:33:28 -0600710 }
711
712 return skip;
713}
714
715bool BestPractices::PreCallValidateBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500716 const VkBindImageMemoryInfo* pBindInfos) const {
Camden Stocker8b798ab2019-09-03 10:33:28 -0600717 char api_name[64];
718 bool skip = false;
719
720 for (uint32_t i = 0; i < bindInfoCount; i++) {
721 sprintf(api_name, "vkBindImageMemory2KHR() pBindInfos[%u]", i);
Attilio Provenzanof31788e2020-02-27 12:00:36 +0000722 skip |= ValidateBindImageMemory(pBindInfos[i].image, pBindInfos[i].memory, api_name);
Camden Stocker8b798ab2019-09-03 10:33:28 -0600723 }
724
725 return skip;
726}
Camden83a9c372019-08-14 11:41:38 -0600727
Attilio Provenzano02859b22020-02-27 14:17:28 +0000728static inline bool FormatHasFullThroughputBlendingArm(VkFormat format) {
729 switch (format) {
730 case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
731 case VK_FORMAT_R16_SFLOAT:
732 case VK_FORMAT_R16G16_SFLOAT:
733 case VK_FORMAT_R16G16B16_SFLOAT:
734 case VK_FORMAT_R16G16B16A16_SFLOAT:
735 case VK_FORMAT_R32_SFLOAT:
736 case VK_FORMAT_R32G32_SFLOAT:
737 case VK_FORMAT_R32G32B32_SFLOAT:
738 case VK_FORMAT_R32G32B32A32_SFLOAT:
739 return false;
740
741 default:
742 return true;
743 }
744}
745
746bool BestPractices::ValidateMultisampledBlendingArm(uint32_t createInfoCount,
747 const VkGraphicsPipelineCreateInfo* pCreateInfos) const {
748 bool skip = false;
749
750 for (uint32_t i = 0; i < createInfoCount; i++) {
751 auto pCreateInfo = &pCreateInfos[i];
752
753 if (!pCreateInfo->pColorBlendState || !pCreateInfo->pMultisampleState ||
754 pCreateInfo->pMultisampleState->rasterizationSamples == VK_SAMPLE_COUNT_1_BIT ||
755 pCreateInfo->pMultisampleState->sampleShadingEnable) {
756 return skip;
757 }
758
759 auto rp_state = GetRenderPassState(pCreateInfo->renderPass);
760 auto& subpass = rp_state->createInfo.pSubpasses[pCreateInfo->subpass];
761
762 for (uint32_t j = 0; j < pCreateInfo->pColorBlendState->attachmentCount; j++) {
763 auto& blend_att = pCreateInfo->pColorBlendState->pAttachments[j];
764 uint32_t att = subpass.pColorAttachments[j].attachment;
765
766 if (att != VK_ATTACHMENT_UNUSED && blend_att.blendEnable && blend_att.colorWriteMask) {
767 if (!FormatHasFullThroughputBlendingArm(rp_state->createInfo.pAttachments[att].format)) {
768 skip |= LogPerformanceWarning(device, kVUID_BestPractices_CreatePipelines_MultisampledBlending,
769 "%s vkCreateGraphicsPipelines() - createInfo #%u: Pipeline is multisampled and "
770 "color attachment #%u makes use "
771 "of a format which cannot be blended at full throughput when using MSAA.",
772 VendorSpecificTag(kBPVendorArm), i, j);
773 }
774 }
775 }
776 }
777
778 return skip;
779}
780
Camden5b184be2019-08-13 07:50:19 -0600781bool BestPractices::PreCallValidateCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount,
782 const VkGraphicsPipelineCreateInfo* pCreateInfos,
Mark Lobodzinski2a162a02019-09-06 11:02:12 -0600783 const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500784 void* cgpl_state_data) const {
Mark Lobodzinski8317a3e2019-09-20 10:07:08 -0600785 bool skip = StateTracker::PreCallValidateCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos,
786 pAllocator, pPipelines, cgpl_state_data);
Mark Lobodzinski8dd14d82020-04-10 14:16:33 -0600787 create_graphics_pipeline_api_state* cgpl_state = reinterpret_cast<create_graphics_pipeline_api_state*>(cgpl_state_data);
Camden5b184be2019-08-13 07:50:19 -0600788
789 if ((createInfoCount > 1) && (!pipelineCache)) {
Mark Lobodzinskif95a2662020-01-29 15:43:32 -0700790 skip |= LogPerformanceWarning(
791 device, kVUID_BestPractices_CreatePipelines_MultiplePipelines,
792 "Performance Warning: This vkCreateGraphicsPipelines call is creating multiple pipelines but is not using a "
793 "pipeline cache, which may help with performance");
Camden5b184be2019-08-13 07:50:19 -0600794 }
795
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000796 for (uint32_t i = 0; i < createInfoCount; i++) {
797 auto& createInfo = pCreateInfos[i];
798
Mark Lobodzinski8dd14d82020-04-10 14:16:33 -0600799 if (!(cgpl_state->pipe_state[i]->active_shaders & VK_SHADER_STAGE_MESH_BIT_NV)) {
800 auto& vertexInput = *createInfo.pVertexInputState;
801 uint32_t count = 0;
802 for (uint32_t j = 0; j < vertexInput.vertexBindingDescriptionCount; j++) {
803 if (vertexInput.pVertexBindingDescriptions[j].inputRate == VK_VERTEX_INPUT_RATE_INSTANCE) {
804 count++;
805 }
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000806 }
Mark Lobodzinski8dd14d82020-04-10 14:16:33 -0600807 if (count > kMaxInstancedVertexBuffers) {
808 skip |= LogPerformanceWarning(
809 device, kVUID_BestPractices_CreatePipelines_TooManyInstancedVertexBuffers,
810 "The pipeline is using %u instanced vertex buffers (current limit: %u), but this can be inefficient on the "
811 "GPU. If using instanced vertex attributes prefer interleaving them in a single buffer.",
812 count, kMaxInstancedVertexBuffers);
813 }
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000814 }
Attilio Provenzano02859b22020-02-27 14:17:28 +0000815
816 skip |= VendorCheckEnabled(kBPVendorArm) && ValidateMultisampledBlendingArm(createInfoCount, pCreateInfos);
Attilio Provenzano1d9a8362020-02-27 12:23:51 +0000817 }
818
Camden5b184be2019-08-13 07:50:19 -0600819 return skip;
820}
821
822bool BestPractices::PreCallValidateCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount,
823 const VkComputePipelineCreateInfo* pCreateInfos,
Mark Lobodzinski2a162a02019-09-06 11:02:12 -0600824 const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500825 void* ccpl_state_data) const {
Mark Lobodzinski8317a3e2019-09-20 10:07:08 -0600826 bool skip = StateTracker::PreCallValidateCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos,
827 pAllocator, pPipelines, ccpl_state_data);
Camden5b184be2019-08-13 07:50:19 -0600828
829 if ((createInfoCount > 1) && (!pipelineCache)) {
Mark Lobodzinskif95a2662020-01-29 15:43:32 -0700830 skip |= LogPerformanceWarning(
831 device, kVUID_BestPractices_CreatePipelines_MultiplePipelines,
832 "Performance Warning: This vkCreateComputePipelines call is creating multiple pipelines but is not using a "
833 "pipeline cache, which may help with performance");
Camden5b184be2019-08-13 07:50:19 -0600834 }
835
836 return skip;
837}
838
Jeff Bolz46c0ea02019-10-09 13:06:29 -0500839bool BestPractices::CheckPipelineStageFlags(std::string api_name, const VkPipelineStageFlags flags) const {
Camden5b184be2019-08-13 07:50:19 -0600840 bool skip = false;
841
842 if (flags & VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700843 skip |= LogWarning(device, kVUID_BestPractices_PipelineStageFlags,
844 "You are using VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT when %s is called\n", api_name.c_str());
Camden5b184be2019-08-13 07:50:19 -0600845 } else if (flags & VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -0700846 skip |= LogWarning(device, kVUID_BestPractices_PipelineStageFlags,
847 "You are using VK_PIPELINE_STAGE_ALL_COMMANDS_BIT when %s is called\n", api_name.c_str());
Camden5b184be2019-08-13 07:50:19 -0600848 }
849
850 return skip;
851}
852
Mark Lobodzinski84101d72020-04-24 09:43:48 -0600853void BestPractices::ManualPostCallRecordQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo, VkResult result) {
Mark Lobodzinski9b133c12020-03-10 10:42:56 -0600854 for (uint32_t i = 0; i < pPresentInfo->swapchainCount; ++i) {
855 auto swapchains_result = pPresentInfo->pResults ? pPresentInfo->pResults[i] : result;
856 if (swapchains_result == VK_SUBOPTIMAL_KHR) {
857 LogPerformanceWarning(
858 pPresentInfo->pSwapchains[i], kVUID_BestPractices_SuboptimalSwapchain,
859 "vkQueuePresentKHR: %s :VK_SUBOPTIMAL_KHR was returned. VK_SUBOPTIMAL_KHR - Presentation will still succeed, "
860 "subject to the window resize behavior, but the swapchain is no longer configured optimally for the surface it "
861 "targets. Applications should query updated surface information and recreate their swapchain at the next "
862 "convenient opportunity.",
863 report_data->FormatHandle(pPresentInfo->pSwapchains[i]).c_str());
864 }
865 }
866}
867
Jeff Bolz5c801d12019-10-09 10:38:45 -0500868bool BestPractices::PreCallValidateQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits,
869 VkFence fence) const {
Camden5b184be2019-08-13 07:50:19 -0600870 bool skip = false;
871
872 for (uint32_t submit = 0; submit < submitCount; submit++) {
873 for (uint32_t semaphore = 0; semaphore < pSubmits[submit].waitSemaphoreCount; semaphore++) {
874 skip |= CheckPipelineStageFlags("vkQueueSubmit", pSubmits[submit].pWaitDstStageMask[semaphore]);
875 }
876 }
877
878 return skip;
879}
880
Attilio Provenzano746e43e2020-02-27 11:23:50 +0000881bool BestPractices::PreCallValidateCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo,
882 const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) const {
883 bool skip = false;
884
885 if (pCreateInfo->flags & VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) {
886 skip |= LogPerformanceWarning(
887 device, kVUID_BestPractices_CreateCommandPool_CommandBufferReset,
888 "vkCreateCommandPool(): VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT is set. Consider resetting entire "
889 "pool instead.");
890 }
891
892 return skip;
893}
894
895bool BestPractices::PreCallValidateBeginCommandBuffer(VkCommandBuffer commandBuffer,
896 const VkCommandBufferBeginInfo* pBeginInfo) const {
897 bool skip = false;
898
899 if (pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT) {
900 skip |= LogPerformanceWarning(device, kVUID_BestPractices_BeginCommandBuffer_SimultaneousUse,
901 "vkBeginCommandBuffer(): VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT is set.");
902 }
903
Attilio Provenzano02859b22020-02-27 14:17:28 +0000904 if (!(pBeginInfo->flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) {
905 skip |= VendorCheckEnabled(kBPVendorArm) &&
906 LogPerformanceWarning(device, kVUID_BestPractices_BeginCommandBuffer_OneTimeSubmit,
907 "%s vkBeginCommandBuffer(): VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT is not set. "
908 "For best performance on Mali GPUs, consider setting ONE_TIME_SUBMIT by default.",
909 VendorSpecificTag(kBPVendorArm));
910 }
911
Attilio Provenzano746e43e2020-02-27 11:23:50 +0000912 return skip;
913}
914
Jeff Bolz5c801d12019-10-09 10:38:45 -0500915bool BestPractices::PreCallValidateCmdSetEvent(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask) const {
Camden5b184be2019-08-13 07:50:19 -0600916 bool skip = false;
917
918 skip |= CheckPipelineStageFlags("vkCmdSetEvent", stageMask);
919
920 return skip;
921}
922
Jeff Bolz5c801d12019-10-09 10:38:45 -0500923bool BestPractices::PreCallValidateCmdResetEvent(VkCommandBuffer commandBuffer, VkEvent event,
924 VkPipelineStageFlags stageMask) const {
Camden5b184be2019-08-13 07:50:19 -0600925 bool skip = false;
926
927 skip |= CheckPipelineStageFlags("vkCmdResetEvent", stageMask);
928
929 return skip;
930}
931
932bool BestPractices::PreCallValidateCmdWaitEvents(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents,
933 VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
934 uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers,
935 uint32_t bufferMemoryBarrierCount,
936 const VkBufferMemoryBarrier* pBufferMemoryBarriers,
937 uint32_t imageMemoryBarrierCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500938 const VkImageMemoryBarrier* pImageMemoryBarriers) const {
Camden5b184be2019-08-13 07:50:19 -0600939 bool skip = false;
940
941 skip |= CheckPipelineStageFlags("vkCmdWaitEvents", srcStageMask);
942 skip |= CheckPipelineStageFlags("vkCmdWaitEvents", dstStageMask);
943
944 return skip;
945}
946
947bool BestPractices::PreCallValidateCmdPipelineBarrier(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
948 VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags,
949 uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers,
950 uint32_t bufferMemoryBarrierCount,
951 const VkBufferMemoryBarrier* pBufferMemoryBarriers,
952 uint32_t imageMemoryBarrierCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500953 const VkImageMemoryBarrier* pImageMemoryBarriers) const {
Camden5b184be2019-08-13 07:50:19 -0600954 bool skip = false;
955
956 skip |= CheckPipelineStageFlags("vkCmdPipelineBarrier", srcStageMask);
957 skip |= CheckPipelineStageFlags("vkCmdPipelineBarrier", dstStageMask);
958
959 return skip;
960}
961
962bool BestPractices::PreCallValidateCmdWriteTimestamp(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage,
Jeff Bolz5c801d12019-10-09 10:38:45 -0500963 VkQueryPool queryPool, uint32_t query) const {
Camden5b184be2019-08-13 07:50:19 -0600964 bool skip = false;
965
966 skip |= CheckPipelineStageFlags("vkCmdWriteTimestamp", pipelineStage);
967
968 return skip;
969}
970
Attilio Provenzano02859b22020-02-27 14:17:28 +0000971static inline bool RenderPassUsesAttachmentOnTile(const safe_VkRenderPassCreateInfo2& createInfo, uint32_t attachment) {
972 for (uint32_t subpass = 0; subpass < createInfo.subpassCount; subpass++) {
973 auto& subpassInfo = createInfo.pSubpasses[subpass];
974
975 // If an attachment is ever used as a color attachment,
976 // resolve attachment or depth stencil attachment,
977 // it needs to exist on tile at some point.
978
979 for (uint32_t i = 0; i < subpassInfo.colorAttachmentCount; i++)
980 if (subpassInfo.pColorAttachments[i].attachment == attachment) return true;
981
982 if (subpassInfo.pResolveAttachments) {
983 for (uint32_t i = 0; i < subpassInfo.colorAttachmentCount; i++)
984 if (subpassInfo.pResolveAttachments[i].attachment == attachment) return true;
985 }
986
987 if (subpassInfo.pDepthStencilAttachment && subpassInfo.pDepthStencilAttachment->attachment == attachment) return true;
988 }
989
990 return false;
991}
992
993bool BestPractices::ValidateCmdBeginRenderPass(VkCommandBuffer commandBuffer, RenderPassCreateVersion rp_version,
994 const VkRenderPassBeginInfo* pRenderPassBegin) const {
995 bool skip = false;
996
997 if (!pRenderPassBegin) {
998 return skip;
999 }
1000
1001 auto rp_state = GetRenderPassState(pRenderPassBegin->renderPass);
1002 if (rp_state) {
Tony-LunarG767180f2020-04-23 14:03:59 -06001003 if (rp_state->createInfo.flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR) {
1004 const VkRenderPassAttachmentBeginInfo* rpabi =
1005 lvl_find_in_chain<VkRenderPassAttachmentBeginInfo>(pRenderPassBegin->pNext);
1006 if (rpabi) {
1007 skip = ValidateAttachments(rp_state->createInfo.ptr(), rpabi->attachmentCount, rpabi->pAttachments);
1008 }
1009 }
Attilio Provenzano02859b22020-02-27 14:17:28 +00001010 // Check if any attachments have LOAD operation on them
1011 for (uint32_t att = 0; att < rp_state->createInfo.attachmentCount; att++) {
1012 auto& attachment = rp_state->createInfo.pAttachments[att];
1013
1014 bool attachmentHasReadback = false;
1015 if (!FormatHasStencil(attachment.format) && attachment.loadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
1016 attachmentHasReadback = true;
1017 }
1018
1019 if (FormatHasStencil(attachment.format) && attachment.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
1020 attachmentHasReadback = true;
1021 }
1022
1023 bool attachmentNeedsReadback = false;
1024
1025 // Check if the attachment is actually used in any subpass on-tile
1026 if (attachmentHasReadback && RenderPassUsesAttachmentOnTile(rp_state->createInfo, att)) {
1027 attachmentNeedsReadback = true;
1028 }
1029
1030 // Using LOAD_OP_LOAD is expensive on tiled GPUs, so flag it as a potential improvement
1031 if (attachmentNeedsReadback) {
1032 skip |= VendorCheckEnabled(kBPVendorArm) &&
1033 LogPerformanceWarning(
1034 device, kVUID_BestPractices_BeginRenderPass_AttachmentNeedsReadback,
1035 "%s Attachment #%u in render pass has begun with VK_ATTACHMENT_LOAD_OP_LOAD.\n"
1036 "Submitting this renderpass will cause the driver to inject a readback of the attachment "
1037 "which will copy in total %u pixels (renderArea = { %d, %d, %u, %u }) to the tile buffer.",
1038 VendorSpecificTag(kBPVendorArm), att,
1039 pRenderPassBegin->renderArea.extent.width * pRenderPassBegin->renderArea.extent.height,
1040 pRenderPassBegin->renderArea.offset.x, pRenderPassBegin->renderArea.offset.y,
1041 pRenderPassBegin->renderArea.extent.width, pRenderPassBegin->renderArea.extent.height);
1042 }
1043 }
1044 }
1045
1046 return skip;
1047}
1048
1049bool BestPractices::PreCallValidateCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin,
1050 VkSubpassContents contents) const {
1051 bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_1, pRenderPassBegin);
1052 return skip;
1053}
1054
1055bool BestPractices::PreCallValidateCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer,
1056 const VkRenderPassBeginInfo* pRenderPassBegin,
1057 const VkSubpassBeginInfoKHR* pSubpassBeginInfo) const {
1058 bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_2, pRenderPassBegin);
1059 return skip;
1060}
1061
1062bool BestPractices::PreCallValidateCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin,
1063 const VkSubpassBeginInfoKHR* pSubpassBeginInfo) const {
1064 bool skip = ValidateCmdBeginRenderPass(commandBuffer, RENDER_PASS_VERSION_2, pRenderPassBegin);
1065 return skip;
1066}
1067
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001068// Generic function to handle validation for all CmdDraw* type functions
1069bool BestPractices::ValidateCmdDrawType(VkCommandBuffer cmd_buffer, const char* caller) const {
1070 bool skip = false;
1071 const CMD_BUFFER_STATE* cb_state = GetCBState(cmd_buffer);
1072 if (cb_state) {
1073 const auto last_bound_it = cb_state->lastBound.find(VK_PIPELINE_BIND_POINT_GRAPHICS);
1074 const PIPELINE_STATE* pipeline_state = nullptr;
1075 if (last_bound_it != cb_state->lastBound.cend()) {
1076 pipeline_state = last_bound_it->second.pipeline_state;
1077 }
1078 const auto& current_vtx_bfr_binding_info = cb_state->current_vertex_buffer_binding_info.vertex_buffer_bindings;
1079 // Verify vertex binding
1080 if (pipeline_state->vertex_binding_descriptions_.size() <= 0) {
1081 if ((!current_vtx_bfr_binding_info.empty()) && (!cb_state->vertex_buffer_used)) {
Mark Lobodzinskif95a2662020-01-29 15:43:32 -07001082 skip |= LogPerformanceWarning(cb_state->commandBuffer, kVUID_BestPractices_DrawState_VtxIndexOutOfBounds,
1083 "Vertex buffers are bound to %s but no vertex buffers are attached to %s.",
1084 report_data->FormatHandle(cb_state->commandBuffer).c_str(),
1085 report_data->FormatHandle(pipeline_state->pipeline).c_str());
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001086 }
1087 }
1088 }
1089 return skip;
1090}
1091
Camden5b184be2019-08-13 07:50:19 -06001092bool BestPractices::PreCallValidateCmdDraw(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001093 uint32_t firstVertex, uint32_t firstInstance) const {
Camden5b184be2019-08-13 07:50:19 -06001094 bool skip = false;
1095
1096 if (instanceCount == 0) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001097 skip |= LogWarning(device, kVUID_BestPractices_CmdDraw_InstanceCountZero,
1098 "Warning: You are calling vkCmdDraw() with an instanceCount of Zero.");
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001099 skip |= ValidateCmdDrawType(commandBuffer, "vkCmdDraw()");
Camden5b184be2019-08-13 07:50:19 -06001100 }
1101
1102 return skip;
1103}
1104
1105bool BestPractices::PreCallValidateCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001106 uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) const {
Camden5b184be2019-08-13 07:50:19 -06001107 bool skip = false;
1108
1109 if (instanceCount == 0) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001110 skip |= LogWarning(device, kVUID_BestPractices_CmdDraw_InstanceCountZero,
1111 "Warning: You are calling vkCmdDrawIndexed() with an instanceCount of Zero.");
Camden5b184be2019-08-13 07:50:19 -06001112 }
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001113 skip |= ValidateCmdDrawType(commandBuffer, "vkCmdDrawIndexed()");
1114
Attilio Provenzano02859b22020-02-27 14:17:28 +00001115 // Check if we reached the limit for small indexed draw calls.
1116 // Note that we cannot update the draw call count here, so we do it in PreCallRecordCmdDrawIndexed.
1117 const CMD_BUFFER_STATE* cmd_state = GetCBState(commandBuffer);
1118 if ((indexCount * instanceCount) <= kSmallIndexedDrawcallIndices &&
1119 (cmd_state->small_indexed_draw_call_count == kMaxSmallIndexedDrawcalls - 1)) {
1120 skip |= VendorCheckEnabled(kBPVendorArm) &&
1121 LogPerformanceWarning(device, kVUID_BestPractices_CmdDrawIndexed_ManySmallIndexedDrawcalls,
1122 "The command buffer contains many small indexed drawcalls "
1123 "(at least %u drawcalls with less than %u indices each). This may cause pipeline bubbles. "
1124 "You can try batching drawcalls or instancing when applicable.",
1125 VendorSpecificTag(kBPVendorArm), kMaxSmallIndexedDrawcalls, kSmallIndexedDrawcallIndices);
1126 }
1127
Sam Walls8e77e4f2020-03-16 20:47:40 +00001128 if (VendorCheckEnabled(kBPVendorArm)) {
1129 ValidateIndexBufferArm(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
1130 }
1131
1132 return skip;
1133}
1134
1135bool BestPractices::ValidateIndexBufferArm(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount,
1136 uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) const {
1137 bool skip = false;
1138
1139 // check for sparse/underutilised index buffer, and post-transform cache thrashing
1140 const auto* cmd_state = GetCBState(commandBuffer);
1141 if (cmd_state == nullptr) return skip;
1142
1143 const auto* ib_state = GetBufferState(cmd_state->index_buffer_binding.buffer);
1144 if (ib_state == nullptr) return skip;
1145
1146 const VkIndexType ib_type = cmd_state->index_buffer_binding.index_type;
1147 const auto& ib_mem_state = *ib_state->binding.mem_state;
1148 const VkDeviceSize ib_mem_offset = ib_mem_state.mapped_range.offset;
1149 const void* ib_mem = ib_mem_state.p_driver_data;
1150 bool primitive_restart_enable = false;
1151
1152 const auto& pipeline_binding_iter = cmd_state->lastBound.find(VK_PIPELINE_BIND_POINT_GRAPHICS);
1153
1154 if (pipeline_binding_iter != cmd_state->lastBound.end()) {
1155 const auto* pipeline_state = pipeline_binding_iter->second.pipeline_state;
1156 if (pipeline_state != nullptr && pipeline_state->graphicsPipelineCI.pInputAssemblyState != nullptr)
1157 primitive_restart_enable = pipeline_state->graphicsPipelineCI.pInputAssemblyState->primitiveRestartEnable == VK_TRUE;
1158 }
1159
1160 // no point checking index buffer if the memory is nonexistant/unmapped, or if there is no graphics pipeline bound to this CB
1161 if (ib_mem && pipeline_binding_iter != cmd_state->lastBound.end()) {
1162 uint32_t scan_stride;
1163 if (ib_type == VK_INDEX_TYPE_UINT8_EXT) {
1164 scan_stride = sizeof(uint8_t);
1165 } else if (ib_type == VK_INDEX_TYPE_UINT16) {
1166 scan_stride = sizeof(uint16_t);
1167 } else {
1168 scan_stride = sizeof(uint32_t);
1169 }
1170
1171 const uint8_t* scan_begin = static_cast<const uint8_t*>(ib_mem) + ib_mem_offset + firstIndex * scan_stride;
1172 const uint8_t* scan_end = scan_begin + indexCount * scan_stride;
1173
1174 // Min and max are important to track for some Mali architectures. In older Mali devices without IDVS, all
1175 // vertices corresponding to indices between the minimum and maximum may be loaded, and possibly shaded,
1176 // irrespective of whether or not they're part of the draw call.
1177
1178 // start with minimum as 0xFFFFFFFF and adjust to indices in the buffer
1179 uint32_t min_index = ~0u;
1180 // start with maximum as 0 and adjust to indices in the buffer
1181 uint32_t max_index = 0u;
1182
1183 // first scan-through, we're looking to simulate a model LRU post-transform cache, estimating the number of vertices shaded
1184 // for the given index buffer
1185 uint32_t vertex_shade_count = 0;
1186
1187 PostTransformLRUCacheModel post_transform_cache;
1188
1189 // The size of the cache being modelled positively correlates with how much behaviour it can capture about
1190 // arbitrary ground-truth hardware/architecture cache behaviour. I.e. it's a good solution when we don't know the
1191 // target architecture.
1192 // However, modelling a post-transform cache with more than 32 elements gives diminishing returns in practice.
1193 // http://eelpi.gotdns.org/papers/fast_vert_cache_opt.html
1194 post_transform_cache.resize(32);
1195
1196 for (const uint8_t* scan_ptr = scan_begin; scan_ptr < scan_end; scan_ptr += scan_stride) {
1197 uint32_t scan_index;
1198 uint32_t primitive_restart_value;
1199 if (ib_type == VK_INDEX_TYPE_UINT8_EXT) {
1200 scan_index = *reinterpret_cast<const uint8_t*>(scan_ptr);
1201 primitive_restart_value = 0xFF;
1202 } else if (ib_type == VK_INDEX_TYPE_UINT16) {
1203 scan_index = *reinterpret_cast<const uint16_t*>(scan_ptr);
1204 primitive_restart_value = 0xFFFF;
1205 } else {
1206 scan_index = *reinterpret_cast<const uint32_t*>(scan_ptr);
1207 primitive_restart_value = 0xFFFFFFFF;
1208 }
1209
1210 max_index = std::max(max_index, scan_index);
1211 min_index = std::min(min_index, scan_index);
1212
1213 if (!primitive_restart_enable || scan_index != primitive_restart_value) {
1214 bool in_cache = post_transform_cache.query_cache(scan_index);
1215 // if the shaded vertex corresponding to the index is not in the PT-cache, we need to shade again
1216 if (!in_cache) vertex_shade_count++;
1217 }
1218 }
1219
1220 // if the max and min values were not set, then we either have no indices, or all primitive restarts, exit...
1221 if (max_index < min_index) return skip;
1222
1223 if (max_index - min_index >= indexCount) {
1224 skip |= LogPerformanceWarning(
1225 device, kVUID_BestPractices_CmdDrawIndexed_SparseIndexBuffer,
1226 "%s The indices which were specified for the draw call only utilise approximately %.02f%% of "
1227 "index buffer value range. Arm Mali architectures before G71 do not have IDVS (Index-Driven "
1228 "Vertex Shading), meaning all vertices corresponding to indices between the minimum and "
1229 "maximum would be loaded, and possibly shaded, whether or not they are used.",
1230 VendorSpecificTag(kBPVendorArm), (static_cast<float>(indexCount) / (max_index - min_index)) * 100.0f);
1231 return skip;
1232 }
1233
1234 // use a dynamic vector of bitsets as a memory-compact representation of which indices are included in the draw call
1235 // each bit of the n-th bucket contains the inclusion information for indices (n*n_buckets) to ((n+1)*n_buckets)
1236 const size_t n_buckets = 64;
1237 std::vector<std::bitset<n_buckets>> vertex_reference_buckets;
1238 vertex_reference_buckets.resize((max_index - min_index + 1) / n_buckets);
1239
1240 // To avoid using too much memory, we run over the indices again.
1241 // Knowing the size from the last scan allows us to record index usage with bitsets
1242 for (const uint8_t* scan_ptr = scan_begin; scan_ptr < scan_end; scan_ptr += scan_stride) {
1243 uint32_t scan_index;
1244 if (ib_type == VK_INDEX_TYPE_UINT8_EXT) {
1245 scan_index = *reinterpret_cast<const uint8_t*>(scan_ptr);
1246 } else if (ib_type == VK_INDEX_TYPE_UINT16) {
1247 scan_index = *reinterpret_cast<const uint16_t*>(scan_ptr);
1248 } else {
1249 scan_index = *reinterpret_cast<const uint32_t*>(scan_ptr);
1250 }
1251 // keep track of the set of all indices used to reference vertices in the draw call
1252 size_t index_offset = scan_index - min_index;
1253 size_t bitset_bucket_index = index_offset / n_buckets;
1254 uint64_t used_indices = 1ull << ((index_offset % n_buckets) & 0xFFFFFFFFu);
1255 vertex_reference_buckets[bitset_bucket_index] |= used_indices;
1256 }
1257
1258 uint32_t vertex_reference_count = 0;
1259 for (const auto& bitset : vertex_reference_buckets) {
1260 vertex_reference_count += static_cast<uint32_t>(bitset.count());
1261 }
1262
1263 // low index buffer utilization implies that: of the vertices available to the draw call, not all are utilized
1264 float utilization = static_cast<float>(vertex_reference_count) / (max_index - min_index + 1);
1265 // low hit rate (high miss rate) implies the order of indices in the draw call may be possible to improve
1266 float cache_hit_rate = static_cast<float>(vertex_reference_count) / vertex_shade_count;
1267
1268 if (utilization < 0.5f) {
1269 skip |= LogPerformanceWarning(device, kVUID_BestPractices_CmdDrawIndexed_SparseIndexBuffer,
1270 "%s The indices which were specified for the draw call only utilise approximately "
1271 "%.02f%% of the bound vertex buffer.",
1272 VendorSpecificTag(kBPVendorArm), utilization);
1273 }
1274
1275 if (cache_hit_rate <= 0.5f) {
1276 skip |=
1277 LogPerformanceWarning(device, kVUID_BestPractices_CmdDrawIndexed_PostTransformCacheThrashing,
1278 "%s The indices which were specified for the draw call are estimated to cause thrashing of "
1279 "the post-transform vertex cache, with a hit-rate of %.02f%%. "
1280 "I.e. the ordering of the index buffer may not make optimal use of indices associated with "
1281 "recently shaded vertices.",
1282 VendorSpecificTag(kBPVendorArm), cache_hit_rate * 100.0f);
1283 }
1284 }
1285
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001286 return skip;
1287}
1288
Attilio Provenzano02859b22020-02-27 14:17:28 +00001289void BestPractices::PreCallRecordCmdDrawIndexed(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount,
1290 uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) {
1291 ValidationStateTracker::PreCallRecordCmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset,
1292 firstInstance);
1293
1294 CMD_BUFFER_STATE* cmd_state = GetCBState(commandBuffer);
1295 if ((indexCount * instanceCount) <= kSmallIndexedDrawcallIndices) {
1296 cmd_state->small_indexed_draw_call_count++;
1297 }
1298}
1299
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001300bool BestPractices::PreCallValidateCmdDrawIndexedIndirectCountKHR(VkCommandBuffer commandBuffer, VkBuffer buffer,
1301 VkDeviceSize offset, VkBuffer countBuffer,
1302 VkDeviceSize countBufferOffset, uint32_t maxDrawCount,
1303 uint32_t stride) const {
1304 bool skip = ValidateCmdDrawType(commandBuffer, "vkCmdDrawIndexedIndirectCountKHR()");
Camden5b184be2019-08-13 07:50:19 -06001305
1306 return skip;
1307}
1308
1309bool BestPractices::PreCallValidateCmdDrawIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001310 uint32_t drawCount, uint32_t stride) const {
Camden5b184be2019-08-13 07:50:19 -06001311 bool skip = false;
1312
1313 if (drawCount == 0) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001314 skip |= LogWarning(device, kVUID_BestPractices_CmdDraw_DrawCountZero,
1315 "Warning: You are calling vkCmdDrawIndirect() with a drawCount of Zero.");
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001316 skip |= ValidateCmdDrawType(commandBuffer, "vkCmdDrawIndirect()");
Camden5b184be2019-08-13 07:50:19 -06001317 }
1318
1319 return skip;
1320}
1321
1322bool BestPractices::PreCallValidateCmdDrawIndexedIndirect(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001323 uint32_t drawCount, uint32_t stride) const {
Camden5b184be2019-08-13 07:50:19 -06001324 bool skip = false;
1325
1326 if (drawCount == 0) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001327 skip |= LogWarning(device, kVUID_BestPractices_CmdDraw_DrawCountZero,
1328 "Warning: You are calling vkCmdDrawIndexedIndirect() with a drawCount of Zero.");
Mark Lobodzinski4c4cf942019-12-20 11:09:51 -07001329 skip |= ValidateCmdDrawType(commandBuffer, "vkCmdDrawIndexedIndirect()");
Camden5b184be2019-08-13 07:50:19 -06001330 }
1331
1332 return skip;
1333}
1334
1335bool BestPractices::PreCallValidateCmdDispatch(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001336 uint32_t groupCountZ) const {
Camden5b184be2019-08-13 07:50:19 -06001337 bool skip = false;
1338
1339 if ((groupCountX == 0) || (groupCountY == 0) || (groupCountZ == 0)) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001340 skip |= LogWarning(device, kVUID_BestPractices_CmdDispatch_GroupCountZero,
1341 "Warning: You are calling vkCmdDispatch() while one or more groupCounts are zero (groupCountX = %" PRIu32
1342 ", groupCountY = %" PRIu32 ", groupCountZ = %" PRIu32 ").",
1343 groupCountX, groupCountY, groupCountZ);
Camden5b184be2019-08-13 07:50:19 -06001344 }
1345
1346 return skip;
1347}
Camden83a9c372019-08-14 11:41:38 -06001348
Camden Stocker9c051442019-11-06 14:28:43 -08001349bool BestPractices::ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(VkPhysicalDevice physicalDevice,
1350 const char* api_name) const {
1351 bool skip = false;
1352 const auto physical_device_state = GetPhysicalDeviceState(physicalDevice);
1353
1354 if (physical_device_state->vkGetPhysicalDeviceDisplayPlanePropertiesKHRState == UNCALLED) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001355 skip |= LogWarning(physicalDevice, kVUID_BestPractices_DisplayPlane_PropertiesNotCalled,
1356 "Potential problem with calling %s() without first retrieving properties from "
1357 "vkGetPhysicalDeviceDisplayPlanePropertiesKHR or vkGetPhysicalDeviceDisplayPlaneProperties2KHR.",
1358 api_name);
Camden Stocker9c051442019-11-06 14:28:43 -08001359 }
1360
1361 return skip;
1362}
1363
Camden83a9c372019-08-14 11:41:38 -06001364bool BestPractices::PreCallValidateGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice, uint32_t planeIndex,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001365 uint32_t* pDisplayCount, VkDisplayKHR* pDisplays) const {
Camden83a9c372019-08-14 11:41:38 -06001366 bool skip = false;
1367
Camden Stocker9c051442019-11-06 14:28:43 -08001368 skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, "vkGetDisplayPlaneSupportedDisplaysKHR");
Camden83a9c372019-08-14 11:41:38 -06001369
Camden Stocker9c051442019-11-06 14:28:43 -08001370 return skip;
1371}
1372
1373bool BestPractices::PreCallValidateGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode,
1374 uint32_t planeIndex,
1375 VkDisplayPlaneCapabilitiesKHR* pCapabilities) const {
1376 bool skip = false;
1377
1378 skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, "vkGetDisplayPlaneCapabilitiesKHR");
1379
1380 return skip;
1381}
1382
1383bool BestPractices::PreCallValidateGetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice,
1384 const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo,
1385 VkDisplayPlaneCapabilities2KHR* pCapabilities) const {
1386 bool skip = false;
1387
1388 skip |= ValidateGetPhysicalDeviceDisplayPlanePropertiesKHRQuery(physicalDevice, "vkGetDisplayPlaneCapabilities2KHR");
Camden83a9c372019-08-14 11:41:38 -06001389
1390 return skip;
1391}
Camden05de2d42019-08-19 10:23:56 -06001392
1393bool BestPractices::PreCallValidateGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001394 VkImage* pSwapchainImages) const {
Camden05de2d42019-08-19 10:23:56 -06001395 bool skip = false;
1396
1397 auto swapchain_state = GetSwapchainState(swapchain);
1398
1399 if (swapchain_state && pSwapchainImages) {
1400 // Compare the preliminary value of *pSwapchainImageCount with the value this time:
1401 if (swapchain_state->vkGetSwapchainImagesKHRState == UNCALLED) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001402 skip |=
1403 LogWarning(device, kVUID_Core_Swapchain_PriorCount,
1404 "vkGetSwapchainImagesKHR() called with non-NULL pSwapchainImageCount; but no prior positive value has "
1405 "been seen for pSwapchainImages.");
Camden05de2d42019-08-19 10:23:56 -06001406 }
1407 }
1408
1409 return skip;
1410}
1411
1412// Common function to handle validation for GetPhysicalDeviceQueueFamilyProperties & 2KHR version
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001413bool BestPractices::ValidateCommonGetPhysicalDeviceQueueFamilyProperties(const PHYSICAL_DEVICE_STATE* pd_state,
1414 uint32_t requested_queue_family_property_count,
1415 bool qfp_null, const char* caller_name) const {
Camden05de2d42019-08-19 10:23:56 -06001416 bool skip = false;
1417 if (!qfp_null) {
1418 // Verify that for each physical device, this command is called first with NULL pQueueFamilyProperties in order to get count
1419 if (UNCALLED == pd_state->vkGetPhysicalDeviceQueueFamilyPropertiesState) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001420 skip |= LogWarning(
1421 pd_state->phys_device, kVUID_Core_DevLimit_MissingQueryCount,
Camden05de2d42019-08-19 10:23:56 -06001422 "%s is called with non-NULL pQueueFamilyProperties before obtaining pQueueFamilyPropertyCount. It is recommended "
1423 "to first call %s with NULL pQueueFamilyProperties in order to obtain the maximal pQueueFamilyPropertyCount.",
1424 caller_name, caller_name);
1425 // Then verify that pCount that is passed in on second call matches what was returned
1426 } else if (pd_state->queue_family_known_count != requested_queue_family_property_count) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001427 skip |= LogWarning(
1428 pd_state->phys_device, kVUID_Core_DevLimit_CountMismatch,
Camden05de2d42019-08-19 10:23:56 -06001429 "%s is called with non-NULL pQueueFamilyProperties and pQueueFamilyPropertyCount value %" PRIu32
1430 ", but the largest previously returned pQueueFamilyPropertyCount for this physicalDevice is %" PRIu32
1431 ". It is recommended to instead receive all the properties by calling %s with pQueueFamilyPropertyCount that was "
1432 "previously obtained by calling %s with NULL pQueueFamilyProperties.",
1433 caller_name, requested_queue_family_property_count, pd_state->queue_family_known_count, caller_name, caller_name);
1434 }
1435 }
1436
1437 return skip;
1438}
1439
Jeff Bolz5c801d12019-10-09 10:38:45 -05001440bool BestPractices::PreCallValidateBindAccelerationStructureMemoryNV(
1441 VkDevice device, uint32_t bindInfoCount, const VkBindAccelerationStructureMemoryInfoNV* pBindInfos) const {
Camden Stocker82510582019-09-03 14:00:16 -06001442 bool skip = false;
1443
1444 for (uint32_t i = 0; i < bindInfoCount; i++) {
1445 const ACCELERATION_STRUCTURE_STATE* as_state = GetAccelerationStructureState(pBindInfos[i].accelerationStructure);
1446 if (!as_state->memory_requirements_checked) {
1447 // There's not an explicit requirement in the spec to call vkGetImageMemoryRequirements() prior to calling
1448 // BindAccelerationStructureMemoryNV but it's implied in that memory being bound must conform with
1449 // VkAccelerationStructureMemoryRequirementsInfoNV from vkGetAccelerationStructureMemoryRequirementsNV
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001450 skip |= LogWarning(
1451 device, kVUID_BestPractices_BindAccelNV_NoMemReqQuery,
Camden Stocker82510582019-09-03 14:00:16 -06001452 "vkBindAccelerationStructureMemoryNV(): "
1453 "Binding memory to %s but vkGetAccelerationStructureMemoryRequirementsNV() has not been called on that structure.",
1454 report_data->FormatHandle(pBindInfos[i].accelerationStructure).c_str());
1455 }
1456 }
1457
1458 return skip;
1459}
1460
Camden05de2d42019-08-19 10:23:56 -06001461bool BestPractices::PreCallValidateGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice,
1462 uint32_t* pQueueFamilyPropertyCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001463 VkQueueFamilyProperties* pQueueFamilyProperties) const {
Camden05de2d42019-08-19 10:23:56 -06001464 const auto physical_device_state = GetPhysicalDeviceState(physicalDevice);
1465 assert(physical_device_state);
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001466 return ValidateCommonGetPhysicalDeviceQueueFamilyProperties(physical_device_state, *pQueueFamilyPropertyCount,
Camden05de2d42019-08-19 10:23:56 -06001467 (nullptr == pQueueFamilyProperties),
1468 "vkGetPhysicalDeviceQueueFamilyProperties()");
1469}
1470
Jeff Bolz5c801d12019-10-09 10:38:45 -05001471bool BestPractices::PreCallValidateGetPhysicalDeviceQueueFamilyProperties2(
1472 VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount,
1473 VkQueueFamilyProperties2KHR* pQueueFamilyProperties) const {
Camden05de2d42019-08-19 10:23:56 -06001474 const auto physical_device_state = GetPhysicalDeviceState(physicalDevice);
1475 assert(physical_device_state);
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001476 return ValidateCommonGetPhysicalDeviceQueueFamilyProperties(physical_device_state, *pQueueFamilyPropertyCount,
Camden05de2d42019-08-19 10:23:56 -06001477 (nullptr == pQueueFamilyProperties),
1478 "vkGetPhysicalDeviceQueueFamilyProperties2()");
1479}
1480
Jeff Bolz5c801d12019-10-09 10:38:45 -05001481bool BestPractices::PreCallValidateGetPhysicalDeviceQueueFamilyProperties2KHR(
1482 VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount,
1483 VkQueueFamilyProperties2KHR* pQueueFamilyProperties) const {
Camden05de2d42019-08-19 10:23:56 -06001484 auto physical_device_state = GetPhysicalDeviceState(physicalDevice);
1485 assert(physical_device_state);
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001486 return ValidateCommonGetPhysicalDeviceQueueFamilyProperties(physical_device_state, *pQueueFamilyPropertyCount,
Camden05de2d42019-08-19 10:23:56 -06001487 (nullptr == pQueueFamilyProperties),
1488 "vkGetPhysicalDeviceQueueFamilyProperties2KHR()");
1489}
1490
1491bool BestPractices::PreCallValidateGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
1492 uint32_t* pSurfaceFormatCount,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001493 VkSurfaceFormatKHR* pSurfaceFormats) const {
Camden05de2d42019-08-19 10:23:56 -06001494 if (!pSurfaceFormats) return false;
1495 const auto physical_device_state = GetPhysicalDeviceState(physicalDevice);
1496 const auto& call_state = physical_device_state->vkGetPhysicalDeviceSurfaceFormatsKHRState;
1497 bool skip = false;
1498 if (call_state == UNCALLED) {
1499 // Since we haven't recorded a preliminary value of *pSurfaceFormatCount, that likely means that the application didn't
1500 // previously call this function with a NULL value of pSurfaceFormats:
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001501 skip |= LogWarning(physicalDevice, kVUID_Core_DevLimit_MustQueryCount,
1502 "vkGetPhysicalDeviceSurfaceFormatsKHR() called with non-NULL pSurfaceFormatCount; but no prior "
1503 "positive value has been seen for pSurfaceFormats.");
Camden05de2d42019-08-19 10:23:56 -06001504 } else {
1505 auto prev_format_count = (uint32_t)physical_device_state->surface_formats.size();
Peter Chene191bd72019-09-16 13:04:37 -04001506 if (*pSurfaceFormatCount > prev_format_count) {
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001507 skip |= LogWarning(physicalDevice, kVUID_Core_DevLimit_CountMismatch,
1508 "vkGetPhysicalDeviceSurfaceFormatsKHR() called with non-NULL pSurfaceFormatCount, and with "
1509 "pSurfaceFormats set to a value (%u) that is greater than the value (%u) that was returned "
1510 "when pSurfaceFormatCount was NULL.",
1511 *pSurfaceFormatCount, prev_format_count);
Camden05de2d42019-08-19 10:23:56 -06001512 }
1513 }
1514 return skip;
1515}
Camden Stocker23cc47d2019-09-03 14:53:57 -06001516
1517bool BestPractices::PreCallValidateQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo,
Jeff Bolz5c801d12019-10-09 10:38:45 -05001518 VkFence fence) const {
Camden Stocker23cc47d2019-09-03 14:53:57 -06001519 bool skip = false;
1520
1521 for (uint32_t bindIdx = 0; bindIdx < bindInfoCount; bindIdx++) {
1522 const VkBindSparseInfo& bindInfo = pBindInfo[bindIdx];
1523 // Store sparse binding image_state and after binding is complete make sure that any requiring metadata have it bound
Jeff Bolz46c0ea02019-10-09 13:06:29 -05001524 std::unordered_set<const IMAGE_STATE*> sparse_images;
1525 // Track images getting metadata bound by this call in a set, it'll be recorded into the image_state
1526 // in RecordQueueBindSparse.
1527 std::unordered_set<const IMAGE_STATE*> sparse_images_with_metadata;
Camden Stocker23cc47d2019-09-03 14:53:57 -06001528 // If we're binding sparse image memory make sure reqs were queried and note if metadata is required and bound
1529 for (uint32_t i = 0; i < bindInfo.imageBindCount; ++i) {
1530 const auto& image_bind = bindInfo.pImageBinds[i];
1531 auto image_state = GetImageState(image_bind.image);
1532 if (!image_state)
1533 continue; // Param/Object validation should report image_bind.image handles being invalid, so just skip here.
1534 sparse_images.insert(image_state);
1535 if (image_state->createInfo.flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) {
1536 if (!image_state->get_sparse_reqs_called || image_state->sparse_requirements.empty()) {
1537 // For now just warning if sparse image binding occurs without calling to get reqs first
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001538 skip |= LogWarning(image_state->image, kVUID_Core_MemTrack_InvalidState,
1539 "vkQueueBindSparse(): Binding sparse memory to %s without first calling "
1540 "vkGetImageSparseMemoryRequirements[2KHR]() to retrieve requirements.",
1541 report_data->FormatHandle(image_state->image).c_str());
Camden Stocker23cc47d2019-09-03 14:53:57 -06001542 }
1543 }
1544 if (!image_state->memory_requirements_checked) {
1545 // For now just warning if sparse image binding occurs without calling to get reqs first
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001546 skip |= LogWarning(image_state->image, kVUID_Core_MemTrack_InvalidState,
1547 "vkQueueBindSparse(): Binding sparse memory to %s without first calling "
1548 "vkGetImageMemoryRequirements() to retrieve requirements.",
1549 report_data->FormatHandle(image_state->image).c_str());
Camden Stocker23cc47d2019-09-03 14:53:57 -06001550 }
1551 }
1552 for (uint32_t i = 0; i < bindInfo.imageOpaqueBindCount; ++i) {
1553 const auto& image_opaque_bind = bindInfo.pImageOpaqueBinds[i];
1554 auto image_state = GetImageState(bindInfo.pImageOpaqueBinds[i].image);
1555 if (!image_state)
1556 continue; // Param/Object validation should report image_bind.image handles being invalid, so just skip here.
1557 sparse_images.insert(image_state);
1558 if (image_state->createInfo.flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) {
1559 if (!image_state->get_sparse_reqs_called || image_state->sparse_requirements.empty()) {
1560 // For now just warning if sparse image binding occurs without calling to get reqs first
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001561 skip |= LogWarning(image_state->image, kVUID_Core_MemTrack_InvalidState,
1562 "vkQueueBindSparse(): Binding opaque sparse memory to %s without first calling "
1563 "vkGetImageSparseMemoryRequirements[2KHR]() to retrieve requirements.",
1564 report_data->FormatHandle(image_state->image).c_str());
Camden Stocker23cc47d2019-09-03 14:53:57 -06001565 }
1566 }
1567 if (!image_state->memory_requirements_checked) {
1568 // For now just warning if sparse image binding occurs without calling to get reqs first
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001569 skip |= LogWarning(image_state->image, kVUID_Core_MemTrack_InvalidState,
1570 "vkQueueBindSparse(): Binding opaque sparse memory to %s without first calling "
1571 "vkGetImageMemoryRequirements() to retrieve requirements.",
1572 report_data->FormatHandle(image_state->image).c_str());
Camden Stocker23cc47d2019-09-03 14:53:57 -06001573 }
1574 for (uint32_t j = 0; j < image_opaque_bind.bindCount; ++j) {
1575 if (image_opaque_bind.pBinds[j].flags & VK_SPARSE_MEMORY_BIND_METADATA_BIT) {
Jeff Bolz46c0ea02019-10-09 13:06:29 -05001576 sparse_images_with_metadata.insert(image_state);
Camden Stocker23cc47d2019-09-03 14:53:57 -06001577 }
1578 }
1579 }
1580 for (const auto& sparse_image_state : sparse_images) {
Jeff Bolz46c0ea02019-10-09 13:06:29 -05001581 if (sparse_image_state->sparse_metadata_required && !sparse_image_state->sparse_metadata_bound &&
1582 sparse_images_with_metadata.find(sparse_image_state) == sparse_images_with_metadata.end()) {
Camden Stocker23cc47d2019-09-03 14:53:57 -06001583 // Warn if sparse image binding metadata required for image with sparse binding, but metadata not bound
Mark Lobodzinskib6e2a282020-01-29 16:03:26 -07001584 skip |= LogWarning(sparse_image_state->image, kVUID_Core_MemTrack_InvalidState,
1585 "vkQueueBindSparse(): Binding sparse memory to %s which requires a metadata aspect but no "
1586 "binding with VK_SPARSE_MEMORY_BIND_METADATA_BIT set was made.",
1587 report_data->FormatHandle(sparse_image_state->image).c_str());
Camden Stocker23cc47d2019-09-03 14:53:57 -06001588 }
1589 }
1590 }
1591
1592 return skip;
1593}
Jeff Bolz46c0ea02019-10-09 13:06:29 -05001594
Mark Lobodzinski84101d72020-04-24 09:43:48 -06001595void BestPractices::ManualPostCallRecordQueueBindSparse(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo,
1596 VkFence fence, VkResult result) {
Mark Lobodzinski205b7a02020-02-21 13:23:17 -07001597 if (result != VK_SUCCESS) {
Mark Lobodzinski205b7a02020-02-21 13:23:17 -07001598 return;
1599 }
Jeff Bolz46c0ea02019-10-09 13:06:29 -05001600
1601 for (uint32_t bindIdx = 0; bindIdx < bindInfoCount; bindIdx++) {
1602 const VkBindSparseInfo& bindInfo = pBindInfo[bindIdx];
1603 for (uint32_t i = 0; i < bindInfo.imageOpaqueBindCount; ++i) {
1604 const auto& image_opaque_bind = bindInfo.pImageOpaqueBinds[i];
1605 auto image_state = GetImageState(bindInfo.pImageOpaqueBinds[i].image);
1606 if (!image_state)
1607 continue; // Param/Object validation should report image_bind.image handles being invalid, so just skip here.
1608 for (uint32_t j = 0; j < image_opaque_bind.bindCount; ++j) {
1609 if (image_opaque_bind.pBinds[j].flags & VK_SPARSE_MEMORY_BIND_METADATA_BIT) {
1610 image_state->sparse_metadata_bound = true;
1611 }
1612 }
1613 }
1614 }
1615}
Camden Stocker0e0f89b2019-10-16 12:24:31 -07001616
1617bool BestPractices::PreCallValidateCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
Camden Stockerf55721f2019-09-09 11:04:49 -06001618 const VkClearAttachment* pAttachments, uint32_t rectCount,
1619 const VkClearRect* pRects) const {
Camden Stocker0e0f89b2019-10-16 12:24:31 -07001620 bool skip = false;
1621 const CMD_BUFFER_STATE* cb_node = GetCBState(commandBuffer);
1622 if (!cb_node) return skip;
1623
Camden Stockerf55721f2019-09-09 11:04:49 -06001624 // Warn if this is issued prior to Draw Cmd and clearing the entire attachment
Camden Stocker0e0f89b2019-10-16 12:24:31 -07001625 if (!cb_node->hasDrawCmd && (cb_node->activeRenderPassBeginInfo.renderArea.extent.width == pRects[0].rect.extent.width) &&
1626 (cb_node->activeRenderPassBeginInfo.renderArea.extent.height == pRects[0].rect.extent.height)) {
1627 // There are times where app needs to use ClearAttachments (generally when reusing a buffer inside of a render pass)
1628 // This warning should be made more specific. It'd be best to avoid triggering this test if it's a use that must call
1629 // CmdClearAttachments.
Mark Lobodzinskif95a2662020-01-29 15:43:32 -07001630 skip |= LogPerformanceWarning(commandBuffer, kVUID_BestPractices_DrawState_ClearCmdBeforeDraw,
1631 "vkCmdClearAttachments() issued on %s prior to any Draw Cmds. It is recommended you "
1632 "use RenderPass LOAD_OP_CLEAR on Attachments prior to any Draw.",
1633 report_data->FormatHandle(commandBuffer).c_str());
Camden Stocker0e0f89b2019-10-16 12:24:31 -07001634 }
1635
Attilio Provenzano1d9a8362020-02-27 12:23:51 +00001636 // Check for uses of ClearAttachments along with LOAD_OP_LOAD,
1637 // as it can be more efficient to just use LOAD_OP_CLEAR
1638 const RENDER_PASS_STATE* rp = cb_node->activeRenderPass;
1639 if (rp) {
1640 const auto& subpass = rp->createInfo.pSubpasses[cb_node->activeSubpass];
1641
1642 for (uint32_t i = 0; i < attachmentCount; i++) {
1643 auto& attachment = pAttachments[i];
1644 if (attachment.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
1645 uint32_t color_attachment = attachment.colorAttachment;
1646 uint32_t fb_attachment = subpass.pColorAttachments[color_attachment].attachment;
1647
1648 if (fb_attachment != VK_ATTACHMENT_UNUSED) {
1649 if (rp->createInfo.pAttachments[fb_attachment].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
1650 skip |= LogPerformanceWarning(
1651 device, kVUID_BestPractices_ClearAttachments_ClearAfterLoad,
1652 "vkCmdClearAttachments() issued on %s for color attachment #%u in this subpass, "
1653 "but LOAD_OP_LOAD was used. If you need to clear the framebuffer, always use LOAD_OP_CLEAR as "
1654 "it is more efficient.",
1655 report_data->FormatHandle(commandBuffer).c_str(), color_attachment);
1656 }
1657 }
1658 }
1659
1660 if (subpass.pDepthStencilAttachment && attachment.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) {
1661 uint32_t fb_attachment = subpass.pDepthStencilAttachment->attachment;
1662
1663 if (fb_attachment != VK_ATTACHMENT_UNUSED) {
1664 if (rp->createInfo.pAttachments[fb_attachment].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
1665 skip |= LogPerformanceWarning(
1666 device, kVUID_BestPractices_ClearAttachments_ClearAfterLoad,
1667 "vkCmdClearAttachments() issued on %s for the depth attachment in this subpass, "
1668 "but LOAD_OP_LOAD was used. If you need to clear the framebuffer, always use LOAD_OP_CLEAR as "
1669 "it is more efficient.",
1670 report_data->FormatHandle(commandBuffer).c_str());
1671 }
1672 }
1673 }
1674
1675 if (subpass.pDepthStencilAttachment && attachment.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) {
1676 uint32_t fb_attachment = subpass.pDepthStencilAttachment->attachment;
1677
1678 if (fb_attachment != VK_ATTACHMENT_UNUSED) {
1679 if (rp->createInfo.pAttachments[fb_attachment].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) {
1680 skip |= LogPerformanceWarning(
1681 device, kVUID_BestPractices_ClearAttachments_ClearAfterLoad,
1682 "vkCmdClearAttachments() issued on %s for the stencil attachment in this subpass, "
1683 "but LOAD_OP_LOAD was used. If you need to clear the framebuffer, always use LOAD_OP_CLEAR as "
1684 "it is more efficient.",
1685 report_data->FormatHandle(commandBuffer).c_str());
1686 }
1687 }
1688 }
1689 }
1690 }
1691
Camden Stockerf55721f2019-09-09 11:04:49 -06001692 return skip;
Camden Stocker0e0f89b2019-10-16 12:24:31 -07001693}
Attilio Provenzano02859b22020-02-27 14:17:28 +00001694
1695bool BestPractices::PreCallValidateCmdResolveImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout,
1696 VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount,
1697 const VkImageResolve* pRegions) const {
1698 bool skip = false;
1699
1700 skip |= VendorCheckEnabled(kBPVendorArm) &&
1701 LogPerformanceWarning(device, kVUID_BestPractices_CmdResolveImage_ResolvingImage,
1702 "%s Attempting to use vkCmdResolveImage to resolve a multisampled image. "
1703 "This is a very slow and extremely bandwidth intensive path. "
1704 "You should always resolve multisampled images on-tile with pResolveAttachments in VkRenderPass.",
1705 VendorSpecificTag(kBPVendorArm));
1706
1707 return skip;
1708}
1709
1710bool BestPractices::PreCallValidateCreateSampler(VkDevice device, const VkSamplerCreateInfo* pCreateInfo,
1711 const VkAllocationCallbacks* pAllocator, VkSampler* pSampler) const {
1712 bool skip = false;
1713
1714 if (VendorCheckEnabled(kBPVendorArm)) {
1715 if ((pCreateInfo->addressModeU != pCreateInfo->addressModeV) || (pCreateInfo->addressModeV != pCreateInfo->addressModeW)) {
1716 skip |= LogPerformanceWarning(
1717 device, kVUID_BestPractices_CreateSampler_DifferentWrappingModes,
1718 "%s Creating a sampler object with wrapping modes which do not match (U = %u, V = %u, W = %u). "
1719 "This may cause reduced performance even if only U (1D image) or U/V wrapping modes (2D "
1720 "image) are actually used. If you need different wrapping modes, disregard this warning.",
1721 VendorSpecificTag(kBPVendorArm));
1722 }
1723
1724 if ((pCreateInfo->minLod != 0.0f) || (pCreateInfo->maxLod < VK_LOD_CLAMP_NONE)) {
1725 skip |= LogPerformanceWarning(
1726 device, kVUID_BestPractices_CreateSampler_LodClamping,
1727 "%s Creating a sampler object with LOD clamping (minLod = %f, maxLod = %f). This may cause reduced performance. "
1728 "Instead of clamping LOD in the sampler, consider using an VkImageView which restricts the mip-levels, set minLod "
1729 "to 0.0, and maxLod to VK_LOD_CLAMP_NONE.",
1730 VendorSpecificTag(kBPVendorArm), pCreateInfo->minLod, pCreateInfo->maxLod);
1731 }
1732
1733 if (pCreateInfo->mipLodBias != 0.0f) {
1734 skip |=
1735 LogPerformanceWarning(device, kVUID_BestPractices_CreateSampler_LodBias,
1736 "%s Creating a sampler object with LOD bias != 0.0 (%f). This will lead to less efficient "
1737 "descriptors being created and may cause reduced performance.",
1738 VendorSpecificTag(kBPVendorArm), pCreateInfo->mipLodBias);
1739 }
1740
1741 if ((pCreateInfo->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
1742 pCreateInfo->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
1743 pCreateInfo->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) &&
1744 (pCreateInfo->borderColor != VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK)) {
1745 skip |= LogPerformanceWarning(
1746 device, kVUID_BestPractices_CreateSampler_BorderClampColor,
1747 "%s Creating a sampler object with border clamping and borderColor != VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK. "
1748 "This will lead to less efficient descriptors being created and may cause reduced performance. "
1749 "If possible, use VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK as the border color.",
1750 VendorSpecificTag(kBPVendorArm));
1751 }
1752
1753 if (pCreateInfo->unnormalizedCoordinates) {
1754 skip |= LogPerformanceWarning(
1755 device, kVUID_BestPractices_CreateSampler_UnnormalizedCoordinates,
1756 "%s Creating a sampler object with unnormalized coordinates. This will lead to less efficient "
1757 "descriptors being created and may cause reduced performance.",
1758 VendorSpecificTag(kBPVendorArm));
1759 }
1760
1761 if (pCreateInfo->anisotropyEnable) {
1762 skip |= LogPerformanceWarning(
1763 device, kVUID_BestPractices_CreateSampler_Anisotropy,
1764 "%s Creating a sampler object with anisotropy. This will lead to less efficient descriptors being created "
1765 "and may cause reduced performance.",
1766 VendorSpecificTag(kBPVendorArm));
1767 }
1768 }
1769
1770 return skip;
1771}
Sam Walls8e77e4f2020-03-16 20:47:40 +00001772
1773void BestPractices::PostTransformLRUCacheModel::resize(size_t size) { _entries.resize(size); }
1774
1775bool BestPractices::PostTransformLRUCacheModel::query_cache(uint32_t value) {
1776 // look for a cache hit
1777 auto hit = std::find_if(_entries.begin(), _entries.end(), [value](const CacheEntry& entry) { return entry.value == value; });
1778 if (hit != _entries.end()) {
1779 // mark the cache hit as being most recently used
1780 hit->age = iteration++;
1781 return true;
1782 }
1783
1784 // if there's no cache hit, we need to model the entry being inserted into the cache
1785 CacheEntry new_entry = {value, iteration};
1786 if (iteration < static_cast<uint32_t>(std::distance(_entries.begin(), _entries.end()))) {
1787 // if there is still space left in the cache, use the next available slot
1788 *(_entries.begin() + iteration) = new_entry;
1789 } else {
1790 // otherwise replace the least recently used cache entry
1791 auto lru = std::min_element(_entries.begin(), hit, [](const CacheEntry& a, const CacheEntry& b) { return a.age < b.age; });
1792 *lru = new_entry;
1793 }
1794 iteration++;
1795 return false;
1796}