File: gpuav_features.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 (323 lines) | stat: -rw-r--r-- 17,963 bytes parent folder | download | duplicates (6)
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
/* Copyright (c) 2025 The Khronos Group Inc.
 * Copyright (c) 2025 Valve Corporation
 * Copyright (c) 2025 LunarG, 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 "gpuav/core/gpuav.h"
#include "generated/dispatch_functions.h"

namespace gpuav {

static std::vector<VkExtensionProperties> GetAvailableExtensions(VkPhysicalDevice physical_device) {
    VkResult err;
    uint32_t extension_count = 512;
    std::vector<VkExtensionProperties> extensions(extension_count);
    for (;;) {
        err = DispatchEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, extensions.data());
        if (err == VK_SUCCESS) {
            extensions.resize(extension_count);
            return extensions;
        } else if (err == VK_INCOMPLETE) {
            extension_count *= 2;  // wasn't enough space, increase it
            extensions.resize(extension_count);
        } else {
            return {};
        }
    }
}

static bool IsExtensionAvailable(const char *extension_name, const std::vector<VkExtensionProperties> &available_extensions) {
    for (const VkExtensionProperties &ext : available_extensions) {
        if (strncmp(extension_name, ext.extensionName, VK_MAX_EXTENSION_NAME_SIZE) == 0) {
            return true;
        }
    }

    return false;
}

// We end up printing lots of warning for GPU-AV that really have no use for any object.
// Instead of spamming with a VkInstance, just explicitly don't print anything
static const VulkanTypedHandle kNoObjects;

// In PreCallRecord we try to turn on as many features as possible on behalf of the app (and warn the user if we are doing it).
// Later after device creation, we can decide if the required Vulkan feature for each GPU-AV setting is found and report errors
void Instance::AddFeatures(VkPhysicalDevice physical_device, vku::safe_VkDeviceCreateInfo *modified_create_info,
                           const Location &loc) {
    // Query things here to make sure we don't attempt to add a feature this is just not supported
    VkPhysicalDeviceRobustness2FeaturesKHR supported_robustness2_feature = vku::InitStructHelper();
    VkPhysicalDevice8BitStorageFeatures supported_8bit_feature = vku::InitStructHelper(&supported_robustness2_feature);
    VkPhysicalDeviceBufferDeviceAddressFeatures supported_bda_feature = vku::InitStructHelper(&supported_8bit_feature);
    VkPhysicalDeviceVulkanMemoryModelFeatures supported_memory_model_feature = vku::InitStructHelper(&supported_bda_feature);
    VkPhysicalDeviceTimelineSemaphoreFeatures supported_timeline_feature = vku::InitStructHelper(&supported_memory_model_feature);
    VkPhysicalDeviceFeatures2 features_2 = vku::InitStructHelper(&supported_timeline_feature);
    DispatchGetPhysicalDeviceFeatures2(physical_device, &features_2);

    // First core features
    {
        const VkPhysicalDeviceFeatures &supported_features = features_2.features;

        VkPhysicalDeviceFeatures *modified_features =
            const_cast<VkPhysicalDeviceFeatures *>(modified_create_info->pEnabledFeatures);
        if (!modified_features) {
            if (auto *modified_features_2 = const_cast<VkPhysicalDeviceFeatures2 *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceFeatures2>(modified_create_info->pNext))) {
                modified_features = &modified_features_2->features;
            } else {
                // The user has no VkPhysicalDeviceFeatures, so we are adding it for them
                modified_features = new VkPhysicalDeviceFeatures;
                memset(modified_features, 0, sizeof(VkPhysicalDeviceFeatures));
                modified_create_info->pEnabledFeatures = modified_features;
            }
        }

        if (modified_features) {
            if (supported_features.fragmentStoresAndAtomics && !modified_features->fragmentStoresAndAtomics) {
                InternalWarning(kNoObjects, loc, "Forcing fragmentStoresAndAtomics to VK_TRUE");
                modified_features->fragmentStoresAndAtomics = VK_TRUE;
            }
            if (supported_features.vertexPipelineStoresAndAtomics && !modified_features->vertexPipelineStoresAndAtomics) {
                InternalWarning(kNoObjects, loc, "Forcing vertexPipelineStoresAndAtomics to VK_TRUE");
                modified_features->vertexPipelineStoresAndAtomics = VK_TRUE;
            }
            if (supported_features.shaderInt64 && !modified_features->shaderInt64) {
                InternalWarning(kNoObjects, loc, "Forcing shaderInt64 to VK_TRUE");
                modified_features->shaderInt64 = VK_TRUE;
            }
            if (gpuav_settings.force_on_robustness && supported_features.robustBufferAccess &&
                !modified_features->robustBufferAccess) {
                InternalWarning(kNoObjects, loc, "Forcing robustBufferAccess to VK_TRUE");
                modified_features->robustBufferAccess = VK_TRUE;
            }
        }
    }

    // Build extension list once
    std::vector<VkExtensionProperties> available_extensions = GetAvailableExtensions(physical_device);

    if (supported_timeline_feature.timelineSemaphore) {
        auto add_timeline_semaphore = [this, &loc, modified_create_info]() {
            if (auto *ts_features = const_cast<VkPhysicalDeviceTimelineSemaphoreFeatures *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceTimelineSemaphoreFeatures>(modified_create_info))) {
                if (ts_features->timelineSemaphore == VK_FALSE) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceTimelineSemaphoreFeatures::timelineSemaphore to VK_TRUE");
                    ts_features->timelineSemaphore = VK_TRUE;
                }
            } else {
                InternalWarning(
                    kNoObjects, loc,
                    "Adding a VkPhysicalDeviceTimelineSemaphoreFeatures to pNext with timelineSemaphore set to VK_TRUE");
                VkPhysicalDeviceTimelineSemaphoreFeatures new_ts_features = vku::InitStructHelper();
                new_ts_features.timelineSemaphore = VK_TRUE;
                vku::AddToPnext(*modified_create_info, new_ts_features);
            }
        };

        if (api_version > VK_API_VERSION_1_1) {
            if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
                if (features12->timelineSemaphore == VK_FALSE) {
                    InternalWarning(kNoObjects, loc, "Forcing VkPhysicalDeviceVulkan12Features::timelineSemaphore to VK_TRUE");
                    features12->timelineSemaphore = VK_TRUE;
                }
            } else {
                add_timeline_semaphore();
            }
        } else if (IsExtensionAvailable(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, available_extensions)) {
            // Only adds if not found already
            vku::AddExtension(*modified_create_info, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
            add_timeline_semaphore();
            timeline_khr_ = true;
        }
    }

    if (supported_memory_model_feature.vulkanMemoryModel) {
        auto add_memory_model = [this, &loc, modified_create_info]() {
            if (auto *mm_features = const_cast<VkPhysicalDeviceVulkanMemoryModelFeatures *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceVulkanMemoryModelFeatures>(modified_create_info))) {
                if (mm_features->vulkanMemoryModel == VK_FALSE) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceVulkanMemoryModelFeatures::vulkanMemoryModel to VK_TRUE");
                    mm_features->vulkanMemoryModel = VK_TRUE;
                }
                if (mm_features->vulkanMemoryModelDeviceScope == VK_FALSE) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceVulkanMemoryModelFeatures::vulkanMemoryModelDeviceScope to VK_TRUE");
                    mm_features->vulkanMemoryModelDeviceScope = VK_TRUE;
                }
            } else {
                InternalWarning(kNoObjects, loc,
                                "Adding a VkPhysicalDeviceVulkanMemoryModelFeatures to pNext with vulkanMemoryModel and "
                                "vulkanMemoryModelDeviceScope set to VK_TRUE");
                VkPhysicalDeviceVulkanMemoryModelFeatures new_mm_features = vku::InitStructHelper();
                new_mm_features.vulkanMemoryModel = VK_TRUE;
                new_mm_features.vulkanMemoryModelDeviceScope = VK_TRUE;
                vku::AddToPnext(*modified_create_info, new_mm_features);
            }
        };

        if (api_version > VK_API_VERSION_1_1) {
            if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
                if (features12->vulkanMemoryModel == VK_FALSE) {
                    InternalWarning(kNoObjects, loc, "Forcing VkPhysicalDeviceVulkan12Features::vulkanMemoryModel to VK_TRUE");
                    features12->vulkanMemoryModel = VK_TRUE;
                }
                if (features12->vulkanMemoryModelDeviceScope == VK_FALSE) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceVulkan12Features::vulkanMemoryModelDeviceScope to VK_TRUE");
                    features12->vulkanMemoryModelDeviceScope = VK_TRUE;
                }
            } else {
                add_memory_model();
            }
        } else if (IsExtensionAvailable(VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME, available_extensions)) {
            // Only adds if not found already
            vku::AddExtension(*modified_create_info, VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME);
            add_memory_model();
        }
    }

    if (supported_bda_feature.bufferDeviceAddress) {
        auto add_bda = [this, &loc, modified_create_info]() {
            // Add buffer device address feature
            if (auto *bda_features = const_cast<VkPhysicalDeviceBufferDeviceAddressFeatures *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceBufferDeviceAddressFeatures>(modified_create_info))) {
                if (!bda_features->bufferDeviceAddress) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress to VK_TRUE");
                    bda_features->bufferDeviceAddress = VK_TRUE;
                }
            } else {
                InternalWarning(
                    kNoObjects, loc,
                    "Adding a VkPhysicalDeviceBufferDeviceAddressFeatures to pNext with bufferDeviceAddress set to VK_TRUE");
                VkPhysicalDeviceBufferDeviceAddressFeatures new_bda_features = vku::InitStructHelper();
                new_bda_features.bufferDeviceAddress = VK_TRUE;
                vku::AddToPnext(*modified_create_info, new_bda_features);
            }
        };

        if (api_version >= VK_API_VERSION_1_2) {
            if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
                if (!features12->bufferDeviceAddress) {
                    InternalWarning(kNoObjects, loc, "Forcing VkPhysicalDeviceVulkan12Features::bufferDeviceAddress to VK_TRUE");
                    features12->bufferDeviceAddress = VK_TRUE;
                }
            } else {
                add_bda();
            }
        } else if (IsExtensionAvailable(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, available_extensions)) {
            // Only adds if not found already
            vku::AddExtension(*modified_create_info, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
            add_bda();
        }
    }

    if (supported_8bit_feature.storageBuffer8BitAccess) {
        auto add_8bit_access = [this, &loc, modified_create_info]() {
            // Add storageBuffer8BitAccess feature
            if (auto *eight_bit_access_feature = const_cast<VkPhysicalDevice8BitStorageFeatures *>(
                    vku::FindStructInPNextChain<VkPhysicalDevice8BitStorageFeatures>(modified_create_info))) {
                if (!eight_bit_access_feature->storageBuffer8BitAccess) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDevice8BitStorageFeatures::storageBuffer8BitAccess to VK_TRUE");
                    eight_bit_access_feature->storageBuffer8BitAccess = VK_TRUE;
                }
            } else {
                InternalWarning(kNoObjects, loc,
                                "Adding a VkPhysicalDevice8BitStorageFeatures to pNext with storageBuffer8BitAccess "
                                "set to VK_TRUE");
                VkPhysicalDevice8BitStorageFeatures new_8bit_features = vku::InitStructHelper();
                new_8bit_features.storageBuffer8BitAccess = VK_TRUE;
                vku::AddToPnext(*modified_create_info, new_8bit_features);
            }
        };

        if (api_version >= VK_API_VERSION_1_2) {
            if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
                if (!features12->storageBuffer8BitAccess) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceVulkan12Features::storageBuffer8BitAccess to VK_TRUE");
                    features12->storageBuffer8BitAccess = VK_TRUE;
                }
            } else {
                add_8bit_access();
            }
        } else if (IsExtensionAvailable(VK_KHR_8BIT_STORAGE_EXTENSION_NAME, available_extensions)) {
            // Only adds if not found already
            vku::AddExtension(*modified_create_info, VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
            add_8bit_access();
        }
    }

    if (gpuav_settings.debug_printf_enabled) {
        if (!IsExtensionAvailable(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, available_extensions)) {
            InternalWarning(kNoObjects, loc,
                            "VK_KHR_shader_non_semantic_info is not available on selected device, Debug Printf may produce SPIR-V "
                            "that could fail to compile the shader.");
        } else {
            vku::AddExtension(*modified_create_info, VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME);
        }
    }

    if (gpuav_settings.force_on_robustness &&
        (supported_robustness2_feature.robustBufferAccess2 || supported_robustness2_feature.robustImageAccess2)) {
        const bool has_ext = IsExtensionAvailable(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, available_extensions);
        const bool has_khr = IsExtensionAvailable(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME, available_extensions);
        if (has_ext || has_khr) {
            // Only adds if not found already
            if (has_khr) {
                vku::AddExtension(*modified_create_info, VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
            } else {
                vku::AddExtension(*modified_create_info, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
            }

            if (auto *robust_buffer_2_feature = const_cast<VkPhysicalDeviceRobustness2FeaturesKHR *>(
                    vku::FindStructInPNextChain<VkPhysicalDeviceRobustness2FeaturesKHR>(modified_create_info))) {
                if (!robust_buffer_2_feature->robustBufferAccess2 && supported_robustness2_feature.robustBufferAccess2) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceRobustness2FeaturesKHR::robustBufferAccess2 to VK_TRUE");
                    robust_buffer_2_feature->robustBufferAccess2 = VK_TRUE;
                }
                if (!robust_buffer_2_feature->robustImageAccess2 && supported_robustness2_feature.robustImageAccess2) {
                    InternalWarning(kNoObjects, loc,
                                    "Forcing VkPhysicalDeviceRobustness2FeaturesKHR::robustImageAccess2 to VK_TRUE");
                    robust_buffer_2_feature->robustImageAccess2 = VK_TRUE;
                }
            } else {
                VkPhysicalDeviceRobustness2FeaturesKHR new_robust_buffer_2_feature = vku::InitStructHelper();
                if (supported_robustness2_feature.robustBufferAccess2) {
                    InternalWarning(
                        kNoObjects, loc,
                        "Adding a VkPhysicalDeviceRobustness2FeaturesKHR to pNext with robustBufferAccess2 set to VK_TRUE");
                    new_robust_buffer_2_feature.robustBufferAccess2 = VK_TRUE;
                }
                if (supported_robustness2_feature.robustImageAccess2) {
                    InternalWarning(
                        kNoObjects, loc,
                        "Adding a VkPhysicalDeviceRobustness2FeaturesKHR to pNext with robustImageAccess2 set to VK_TRUE");
                    new_robust_buffer_2_feature.robustImageAccess2 = VK_TRUE;
                }
                vku::AddToPnext(*modified_create_info, new_robust_buffer_2_feature);
            }
        }
    }
}

}  // namespace gpuav