File: sl_utils.cpp

package info (click to toggle)
vulkan-validationlayers 1.4.321.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,412 kB
  • sloc: cpp: 594,175; python: 11,321; sh: 24; makefile: 20; xml: 14
file content (465 lines) | stat: -rw-r--r-- 23,084 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/* Copyright (c) 2015-2025 The Khronos Group Inc.
 * Copyright (c) 2015-2025 Valve Corporation
 * Copyright (c) 2015-2025 LunarG, Inc.
 * Copyright (C) 2015-2024 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "error_message/error_location.h"
#include "stateless/stateless_validation.h"

namespace stateless {
bool Instance::CheckPromotedApiAgainstVulkanVersion(VkInstance instance, const Location &loc,
                                                    const uint32_t promoted_version) const {
    bool skip = false;
    if (api_version < promoted_version) {
        skip |= LogError("UNASSIGNED-API-Version-Violation", instance, loc,
                         "Attempted to call with an effective API version of %s"
                         "but this API was not promoted until version %s.",
                         StringAPIVersion(api_version).c_str(), StringAPIVersion(promoted_version).c_str());
    }
    return skip;
}

bool Instance::CheckPromotedApiAgainstVulkanVersion(VkPhysicalDevice pdev, const Location &loc,
                                                    const uint32_t promoted_version) const {
    bool skip = false;
    const auto &target_pdev = physical_device_properties_map.find(pdev);
    if (target_pdev != physical_device_properties_map.end()) {
        auto effective_api_version = std::min(APIVersion(target_pdev->second->apiVersion), api_version);
        if (effective_api_version < promoted_version) {
            skip |= LogError(
                "UNASSIGNED-API-Version-Violation", instance, loc,
                "Attempted to call with an effective API version of %s, "
                "which is the minimum of version requested in pApplicationInfo (%s) and supported by this physical device (%s), "
                "but this API was not promoted until version %s.",
                StringAPIVersion(effective_api_version).c_str(), StringAPIVersion(api_version).c_str(),
                StringAPIVersion(target_pdev->second->apiVersion).c_str(), StringAPIVersion(promoted_version).c_str());
        }
    }
    return skip;
}

bool Instance::OutputExtensionError(const Location &loc, const vvl::Extensions &exentsions) const {
    return LogError("UNASSIGNED-GeneralParameterError-ExtensionNotEnabled", instance, loc,
                    "function required extension %s which has not been enabled.\n", String(exentsions).c_str());
}
bool Device::OutputExtensionError(const Location &loc, const vvl::Extensions &exentsions) const {
    return LogError("UNASSIGNED-GeneralParameterError-ExtensionNotEnabled", device, loc,
                    "function required extension %s which has not been enabled.\n", String(exentsions).c_str());
}

static const uint8_t kUtF8OneByteCode = 0xC0;
static const uint8_t kUtF8OneByteMask = 0xE0;
static const uint8_t kUtF8TwoByteCode = 0xE0;
static const uint8_t kUtF8TwoByteMask = 0xF0;
static const uint8_t kUtF8ThreeByteCode = 0xF0;
static const uint8_t kUtF8ThreeByteMask = 0xF8;
static const uint8_t kUtF8DataByteCode = 0x80;
static const uint8_t kUtF8DataByteMask = 0xC0;

typedef enum VkStringErrorFlagBits {
    VK_STRING_ERROR_NONE = 0x00000000,
    VK_STRING_ERROR_LENGTH = 0x00000001,
    VK_STRING_ERROR_BAD_DATA = 0x00000002,
} VkStringErrorFlagBits;
typedef VkFlags VkStringErrorFlags;

static VkStringErrorFlags ValidateVkString(const int max_length, const char *utf8) {
    VkStringErrorFlags result = VK_STRING_ERROR_NONE;
    int num_char_bytes = 0;
    int i, j;

    for (i = 0; i <= max_length; i++) {
        if (utf8[i] == 0) {
            break;
        } else if (i == max_length) {
            result |= VK_STRING_ERROR_LENGTH;
            break;
        } else if ((utf8[i] >= 0xa) && (utf8[i] < 0x7f)) {
            num_char_bytes = 0;
        } else if ((utf8[i] & kUtF8OneByteMask) == kUtF8OneByteCode) {
            num_char_bytes = 1;
        } else if ((utf8[i] & kUtF8TwoByteMask) == kUtF8TwoByteCode) {
            num_char_bytes = 2;
        } else if ((utf8[i] & kUtF8ThreeByteMask) == kUtF8ThreeByteCode) {
            num_char_bytes = 3;
        } else {
            result |= VK_STRING_ERROR_BAD_DATA;
            break;
        }

        // Validate the following num_char_bytes of data
        for (j = 0; (j < num_char_bytes) && (i < max_length); j++) {
            if (++i == max_length) {
                result |= VK_STRING_ERROR_LENGTH;
                break;
            }
            if ((utf8[i] & kUtF8DataByteMask) != kUtF8DataByteCode) {
                result |= VK_STRING_ERROR_BAD_DATA;
                break;
            }
        }
        if (result != VK_STRING_ERROR_NONE) break;
    }
    return result;
}

static const int kMaxParamCheckerStringLength = 256;
bool Context::ValidateString(const Location &loc, const char *vuid, const char *validate_string) const {
    bool skip = false;

    VkStringErrorFlags result = ValidateVkString(kMaxParamCheckerStringLength, validate_string);

    if (result == VK_STRING_ERROR_NONE) {
        return skip;
    } else if (result & VK_STRING_ERROR_LENGTH) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "exceeds max length %" PRIu32 ".", kMaxParamCheckerStringLength);
    } else if (result & VK_STRING_ERROR_BAD_DATA) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "contains invalid characters or is badly formed.");
    }
    return skip;
}

bool Context::ValidateNotZero(bool is_zero, const char *vuid, const Location &loc) const {
    bool skip = false;
    if (is_zero) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "is zero.");
    }
    return skip;
}

bool Context::ValidateRequiredPointer(const Location &loc, const void *value, const char *vuid) const {
    bool skip = false;
    if (value == nullptr) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "is NULL.");
    }
    return skip;
}

bool Context::ValidateAllocationCallbacks(const VkAllocationCallbacks &callback, const Location &loc) const {
    bool skip = false;
    skip |= ValidateRequiredPointer(loc.dot(Field::pfnAllocation), reinterpret_cast<const void *>(callback.pfnAllocation),
                                    "VUID-VkAllocationCallbacks-pfnAllocation-00632");

    skip |= ValidateRequiredPointer(loc.dot(Field::pfnReallocation), reinterpret_cast<const void *>(callback.pfnReallocation),
                                    "VUID-VkAllocationCallbacks-pfnReallocation-00633");

    skip |= ValidateRequiredPointer(loc.dot(Field::pfnFree), reinterpret_cast<const void *>(callback.pfnFree),
                                    "VUID-VkAllocationCallbacks-pfnFree-00634");

    if (callback.pfnInternalAllocation) {
        skip |=
            ValidateRequiredPointer(loc.dot(Field::pfnInternalAllocation), reinterpret_cast<const void *>(callback.pfnInternalFree),
                                    "VUID-VkAllocationCallbacks-pfnInternalAllocation-00635");
    }

    if (callback.pfnInternalFree) {
        skip |=
            ValidateRequiredPointer(loc.dot(Field::pfnInternalFree), reinterpret_cast<const void *>(callback.pfnInternalAllocation),
                                    "VUID-VkAllocationCallbacks-pfnInternalAllocation-00635");
    }
    return skip;
}

bool Context::ValidateStringArray(const Location &count_loc, const Location &array_loc, uint32_t count, const char *const *array,
                                  bool count_required, bool array_required, const char *count_required_vuid,
                                  const char *array_required_vuid) const {
    bool skip = false;

    if ((array == nullptr) || (count == 0)) {
        skip |= ValidateArray(count_loc, array_loc, count, &array, count_required, array_required, count_required_vuid,
                              array_required_vuid);
    } else {
        // Verify that strings in the array are not NULL
        for (uint32_t i = 0; i < count; ++i) {
            if (array[i] == nullptr) {
                skip |= log.LogError(array_required_vuid, error_obj.handle, array_loc.dot(i), "is NULL.");
            }
        }
    }

    return skip;
}

// We decided in https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/9578 that structs in pNext chains are allowed
// regardless of the extension. pNext structs all are based on a sType/pNext system (VkBaseInStructure/VkBaseOutStructure) and they
// can be safely ignored.
//
// However.. some structs we determined to warn the user because it might produce subtle effects, but this is the edge case, not the
// normal case.
bool Context::ValidatePnextStructExtension(const Location &loc, const VkBaseOutStructure *header) const {
    bool skip = false;
    switch (header->sType) {
        case VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO: {
            auto buffer_flags2_ci = (const VkBufferUsageFlags2CreateInfo *)header;
            if (buffer_flags2_ci->usage != 0 && !IsExtEnabled(extensions.vk_khr_maintenance5)) {
                skip |= log.LogWarning(
                    "WARNING-VkBufferUsageFlags2CreateInfo-Extension", error_obj.handle, loc.dot(Field::pNext),
                    "points to a VkBufferUsageFlags2CreateInfo struct but VK_KHR_maintenance5 has not been enabled. "
                    "Without checking for and enabling the extension, you might have a situation where your buffer usage "
                    "flags are ignored if the driver doesn't support VK_KHR_maintenance5");
            }
        } break;

        case VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO: {
            auto pipeline_flags2_ci = (const VkPipelineCreateFlags2CreateInfo *)header;
            if (pipeline_flags2_ci->flags != 0 && !IsExtEnabled(extensions.vk_khr_maintenance5)) {
                skip |= log.LogWarning(
                    "WARNING-VkPipelineCreateFlags2CreateInfo-Extension", error_obj.handle, loc.dot(Field::pNext),
                    "points to a VkPipelineCreateFlags2CreateInfo struct but VK_KHR_maintenance5 has not been enabled. "
                    "Without checking for and enabling the extension, you might have a situation where your buffer usage "
                    "flags are ignored if the driver doesn't support VK_KHR_maintenance5");
            }
        } break;

        default:
            break;
    }
    return skip;
}

bool Context::ValidateStructPnext(const Location &loc, const void *next, size_t allowed_type_count,
                                  const VkStructureType *allowed_types, uint32_t header_version, const char *pnext_vuid,
                                  const char *stype_vuid, const bool is_const_param) const {
    bool skip = false;

    if (next != nullptr) {
        vvl::unordered_set<const void *> cycle_check;
        vvl::unordered_set<VkStructureType, vvl::hash<int>> unique_stype_check;
        const char *disclaimer =
            "This error is based on the Valid Usage documentation for version %" PRIu32
            " of the Vulkan header.  It is possible that "
            "you are using a struct from a private extension or an extension that was added to a later version of the Vulkan "
            "header, in which case the use of %s is undefined and may not work correctly with validation enabled";

        const Location pNext_loc = loc.dot(Field::pNext);
        if ((allowed_type_count == 0) && (GetCustomStypeInfo().empty())) {
            std::string message = "must be NULL.\n%s\n";
            message += disclaimer;
            skip |= log.LogError(pnext_vuid, error_obj.handle, pNext_loc, message.c_str(),
                                 PrintPNextChain(Struct::Empty, next).c_str(), header_version, pNext_loc.Fields().c_str());
        } else {
            const VkStructureType *start = allowed_types;
            const VkStructureType *end = allowed_types + allowed_type_count;
            const VkBaseOutStructure *current = reinterpret_cast<const VkBaseOutStructure *>(next);

            while (current != nullptr) {
                if ((loc.function != Func::vkCreateInstance || (current->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO)) &&
                    (loc.function != Func::vkCreateDevice || (current->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO))) {
                    if (unique_stype_check.find(current->sType) != unique_stype_check.end() && !IsDuplicatePnext(current->sType)) {
                        // stype_vuid will only be null if there are no listed pNext and will hit disclaimer check
                        skip |= log.LogError(stype_vuid, error_obj.handle, pNext_loc,
                                             "chain contains duplicate structure types: %s appears multiple times.\n%s",
                                             string_VkStructureName(current->sType), PrintPNextChain(loc.structure, next).c_str());
                    } else {
                        unique_stype_check.insert(current->sType);
                    }

                    // Search custom stype list -- if sType found, skip this entirely
                    bool custom = false;
                    for (const auto &item : GetCustomStypeInfo()) {
                        if (item.first == current->sType) {
                            custom = true;
                            break;
                        }
                    }
                    if (!custom) {
                        if (std::find(start, end, current->sType) == end) {
                            const std::string type_name = string_VkStructureType(current->sType);
                            if (type_name.compare("Unhandled VkStructureType") == 0) {
                                std::string message =
                                    "chain includes a structure with unknown VkStructureType (%" PRIu32 ").\n%s\n";
                                message += disclaimer;
                                skip |= log.LogError(pnext_vuid, error_obj.handle, pNext_loc, message.c_str(), current->sType,
                                                     PrintPNextChain(Struct::Empty, next).c_str(), header_version,
                                                     pNext_loc.Fields().c_str());
                            } else {
                                std::string message = "chain includes a structure with unexpected VkStructureType %s.\n%s\n";
                                message += disclaimer;
                                skip |= log.LogError(pnext_vuid, error_obj.handle, pNext_loc, message.c_str(), type_name.c_str(),
                                                     PrintPNextChain(Struct::Empty, next).c_str(), header_version,
                                                     pNext_loc.Fields().c_str());
                            }
                        }
                        // Send Location without pNext field so the pNext() connector can be used
                        skip |= ValidatePnextStructContents(loc, current, pnext_vuid, is_const_param);
                        skip |= ValidatePnextStructExtension(loc, current);
                        // pNext contents for vkGetPhysicalDeviceProperties2KHR() is no longer checked.
                        if (loc.function == Func::vkGetPhysicalDeviceFeatures2 ||
                            loc.function == Func::vkGetPhysicalDeviceFeatures2KHR || loc.function == Func::vkCreateDevice) {
                            skip |= ValidatePnextFeatureStructContents(loc, current, pnext_vuid, is_const_param);
                        }
                    }
                }
                current = reinterpret_cast<const VkBaseOutStructure *>(current->pNext);
            }
        }
    }

    return skip;
}

bool Context::ValidateBool32(const Location &loc, VkBool32 value) const {
    bool skip = false;
    if ((value != VK_TRUE) && (value != VK_FALSE)) {
        skip |= log.LogError("UNASSIGNED-GeneralParameterError-UnrecognizedBool32", error_obj.handle, loc,
                             "(%" PRIu32
                             ") is neither VK_TRUE nor VK_FALSE. Applications MUST not pass any other "
                             "values than VK_TRUE or VK_FALSE into a Vulkan implementation where a VkBool32 is expected.",
                             value);
    }
    return skip;
}

bool Context::ValidateBool32Array(const Location &count_loc, const Location &array_loc, uint32_t count, const VkBool32 *array,
                                  bool count_required, bool array_required, const char *count_required_vuid,
                                  const char *array_required_vuid) const {
    bool skip = false;

    if ((array == nullptr) || (count == 0)) {
        skip |= ValidateArray(count_loc, array_loc, count, &array, count_required, array_required, count_required_vuid,
                              array_required_vuid);
    } else {
        for (uint32_t i = 0; i < count; ++i) {
            if ((array[i] != VK_TRUE) && (array[i] != VK_FALSE)) {
                skip |= log.LogError(array_required_vuid, error_obj.handle, array_loc.dot(i),
                                     "(%" PRIu32
                                     ") is neither VK_TRUE nor VK_FALSE. Applications MUST not pass any other "
                                     "values than VK_TRUE or VK_FALSE into a Vulkan implementation where a VkBool32 is expected.",
                                     array[i]);
            }
        }
    }

    return skip;
}

bool Context::ValidateReservedFlags(const Location &loc, VkFlags value, const char *vuid) const {
    bool skip = false;
    if (value != 0) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "is %" PRIu32 ", but must be 0.", value);
    }
    return skip;
}

bool Context::ValidateReservedFlags(const Location &loc, VkFlags64 value, const char *vuid) const {
    bool skip = false;
    if (value != 0) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "is %" PRIu64 ", but must be 0.", value);
    }
    return skip;
}

// helper to implement validation of both 32 bit and 64 bit flags.
template <typename FlagTypedef>
bool Context::ValidateFlagsImplementation(const Location &loc, vvl::FlagBitmask flag_bitmask, FlagTypedef all_flags,
                                          FlagTypedef value, const FlagType flag_type, const char *vuid,
                                          const char *flags_zero_vuid) const {
    bool skip = false;

    const bool required = flag_type == kRequiredFlags || flag_type == kRequiredSingleBit;
    const char *zero_vuid = flag_type == kRequiredFlags ? flags_zero_vuid : vuid;
    if (required && value == 0) {
        skip |= log.LogError(zero_vuid, error_obj.handle, loc, "is zero.");
    }

    const auto HasMaxOneBitSet = [](const FlagTypedef f) {
        // Decrement flips bits from right upto first 1.
        // Rest stays same, and if there was any other 1s &ded together they would be non-zero. QED
        return f == 0 || !(f & (f - 1));
    };

    const bool is_bits_type = flag_type == kRequiredSingleBit || flag_type == kOptionalSingleBit;
    if (is_bits_type && !HasMaxOneBitSet(value)) {
        skip |= log.LogError(vuid, error_obj.handle, loc, "contains multiple members of %s when only a single value is allowed.",
                             String(flag_bitmask));
    }

    return skip;
}

bool Context::ValidateFlags(const Location &loc, vvl::FlagBitmask flag_bitmask, VkFlags all_flags, VkFlags value,
                            const FlagType flag_type, const char *vuid, const char *flags_zero_vuid) const {
    bool skip = false;
    skip |= ValidateFlagsImplementation<VkFlags>(loc, flag_bitmask, all_flags, value, flag_type, vuid, flags_zero_vuid);

    if (ignore_unknown_enums) {
        return skip;
    }

    if ((value & ~all_flags) != 0) {
        skip |=
            log.LogError(vuid, error_obj.handle, loc, "contains flag bits (0x%" PRIx32 ") which are not recognized members of %s.",
                         value, String(flag_bitmask));
    }

    if (!skip && value != 0) {
        vvl::Extensions required = IsValidFlagValue(flag_bitmask, value);
        if (!required.empty()) {
            skip |=
                log.LogError(vuid, error_obj.handle, loc, "has %s values (%s) that requires the extensions %s.",
                             String(flag_bitmask), DescribeFlagBitmaskValue(flag_bitmask, value).c_str(), String(required).c_str());
        }
    }
    return skip;
}

bool Context::ValidateFlags(const Location &loc, vvl::FlagBitmask flag_bitmask, VkFlags64 all_flags, VkFlags64 value,
                            const FlagType flag_type, const char *vuid, const char *flags_zero_vuid) const {
    bool skip = false;
    skip |= ValidateFlagsImplementation<VkFlags64>(loc, flag_bitmask, all_flags, value, flag_type, vuid, flags_zero_vuid);

    if (ignore_unknown_enums) {
        return skip;
    }

    if ((value & ~all_flags) != 0) {
        skip |=
            log.LogError(vuid, error_obj.handle, loc, "contains flag bits (0x%" PRIx64 ") which are not recognized members of %s.",
                         value, String(flag_bitmask));
    }

    if (!skip && value != 0) {
        vvl::Extensions required = IsValidFlag64Value(flag_bitmask, value);
        if (!required.empty()) {
            skip |= log.LogError(vuid, error_obj.handle, loc, "has %s values (%s) that requires the extensions %s.",
                                 String(flag_bitmask), DescribeFlagBitmaskValue64(flag_bitmask, value).c_str(),
                                 String(required).c_str());
        }
    }
    return skip;
}

bool Context::ValidateFlagsArray(const Location &count_loc, const Location &array_loc, vvl::FlagBitmask flag_bitmask,
                                 VkFlags all_flags, uint32_t count, const VkFlags *array, bool count_required,
                                 const char *count_required_vuid, const char *array_required_vuid) const {
    bool skip = false;

    if ((array == nullptr) || (count == 0)) {
        // Flag arrays always need to have a valid array
        skip |= ValidateArray(count_loc, array_loc, count, &array, count_required, true, count_required_vuid, array_required_vuid);
    } else {
        // Verify that all VkFlags values in the array
        for (uint32_t i = 0; i < count; ++i) {
            if ((array[i] & (~all_flags)) != 0) {
                skip |= log.LogError(array_required_vuid, error_obj.handle, array_loc.dot(i),
                                     "contains flag bits that are not recognized members of %s.", String(flag_bitmask));
            }
        }
    }

    return skip;
}
}  // namespace stateless