/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2025 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #include #include "../vk_core.h" #include "../vk_debug.h" #include "../vk_rendertext.h" #include "../vk_replay.h" #include "../vk_shader_cache.h" #include "api/replay/version.h" #include "core/settings.h" #include "driver/ihv/nv/nv_aftermath.h" #include "strings/string_utils.h" RDOC_CONFIG( bool, Vulkan_Debug_ReplaceAppInfo, true, "By default we have no choice but to replace VkApplicationInfo to safely work on all drivers. " "This behaviour can be disabled with this flag, which lets it through both during capture and " "on replay."); // intercept and overwrite the application info if present. We must use the same appinfo on // capture and replay, and the safer default is not to replay as if we were the original app but // with a slightly different workload. So instead we trample what the app reported and put in our // own info. static VkApplicationInfo renderdocAppInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO, NULL, "RenderDoc Capturing App", VK_MAKE_VERSION(RENDERDOC_VERSION_MAJOR, RENDERDOC_VERSION_MINOR, 0), "RenderDoc", VK_MAKE_VERSION(RENDERDOC_VERSION_MAJOR, RENDERDOC_VERSION_MINOR, 0), VK_API_VERSION_1_0, }; static bool equivalent(const VkQueueFamilyProperties &a, const VkQueueFamilyProperties &b) { return a.timestampValidBits == b.timestampValidBits && a.minImageTransferGranularity.width == b.minImageTransferGranularity.width && a.minImageTransferGranularity.height == b.minImageTransferGranularity.height && a.minImageTransferGranularity.depth == b.minImageTransferGranularity.depth && a.queueFlags == b.queueFlags; } // we store the index in the loader table, since it won't be dereferenced and other parts of the // code expect to copy it into a wrapped object static VkPhysicalDevice MakePhysicalDeviceHandleFromIndex(uint32_t physDeviceIndex) { static uintptr_t loaderTable[32]; loaderTable[physDeviceIndex] = (0x100 + physDeviceIndex); return VkPhysicalDevice(&loaderTable[physDeviceIndex]); } static uint32_t GetPhysicalDeviceIndexFromHandle(VkPhysicalDevice physicalDevice) { return uint32_t((uintptr_t)LayerDisp(physicalDevice) - 0x100); } static bool CheckTransferGranularity(VkExtent3D required, VkExtent3D check) { // if the required granularity is (0,0,0) then any is fine - the requirement is always satisfied. if(required.width == required.height && required.height == required.depth && required.depth == 0) return true; // otherwise, each dimension must be <= the required dimension (i.e. more fine-grained) to support // any copies we might do. return check.width <= required.width && check.height <= required.height && check.depth <= required.depth; } // vk_dispatchtables.cpp void InitDeviceTable(VkDevice dev, PFN_vkGetDeviceProcAddr gpa); void InitInstanceTable(VkInstance inst, PFN_vkGetInstanceProcAddr gpa); // Init/shutdown order: // // On capture, WrappedVulkan is new'd and delete'd before vkCreateInstance() and after // vkDestroyInstance() // On replay, WrappedVulkan is new'd and delete'd before Initialise() and after Shutdown() // // The class constructor and destructor handle only *non-API* work. All API objects must be created // and // torn down in the latter functions (vkCreateInstance/vkDestroyInstance during capture, and // Initialise/Shutdown during replay). // // Note that during capture we have vkDestroyDevice before vkDestroyDevice that does most of the // work. // // Also we assume correctness from the application, that all objects are destroyed before the device // and // instance are destroyed. We only clean up after our own objects. static void StripUnwantedLayers(rdcarray &Layers) { Layers.removeIf([](const rdcstr &layer) { // don't try and create our own layer on replay! if(layer == RENDERDOC_VULKAN_LAYER_NAME) { return true; } // don't enable tracing or dumping layers just in case they // came along with the application if(layer == "VK_LAYER_LUNARG_api_dump" || layer == "VK_LAYER_LUNARG_vktrace") { return true; } // also remove the framerate monitor layer as it's buggy and doesn't do anything // in our case if(layer == "VK_LAYER_LUNARG_monitor") { return true; } // remove the optimus layer just in case it was explicitly enabled. if(layer == "VK_LAYER_NV_optimus") { return true; } // filter out validation layers if(layer == "VK_LAYER_LUNARG_standard_validation" || layer == "VK_LAYER_KHRONOS_validation" || layer == "VK_LAYER_LUNARG_core_validation" || layer == "VK_LAYER_LUNARG_device_limits" || layer == "VK_LAYER_LUNARG_image" || layer == "VK_LAYER_LUNARG_object_tracker" || layer == "VK_LAYER_LUNARG_parameter_validation" || layer == "VK_LAYER_LUNARG_swapchain" || layer == "VK_LAYER_GOOGLE_threading" || layer == "VK_LAYER_GOOGLE_unique_objects" || layer == "VK_LAYER_LUNARG_assistant_layer") { return true; } return false; }); } static void StripUnwantedExtensions(rdcarray &Extensions) { // strip out any WSI/direct display extensions. We'll add the ones we want for creating windows // on the current platforms below, and we don't replay any of the WSI functionality // directly so these extensions aren't needed Extensions.removeIf([](const rdcstr &ext) { // remove surface extensions if(ext == "VK_KHR_xlib_surface" || ext == "VK_KHR_xcb_surface" || ext == "VK_KHR_wayland_surface" || ext == "VK_KHR_mir_surface" || ext == "VK_MVK_macos_surface" || ext == "VK_KHR_android_surface" || ext == "VK_KHR_win32_surface" || ext == "VK_EXT_headless_surface") { return true; } // remove direct display extensions if(ext == "VK_KHR_display" || ext == "VK_EXT_direct_mode_display" || ext == "VK_EXT_acquire_xlib_display" || ext == "VK_EXT_display_surface_counter" || ext == "VK_EXT_acquire_drm_display") { return true; } // remove platform-specific external extensions, as we don't replay external objects. We leave // the base extensions since they're widely supported and we don't strip all uses of e.g. // feature structs. if(ext == "VK_KHR_external_fence_fd" || ext == "VK_KHR_external_fence_win32" || ext == "VK_KHR_external_memory_fd" || ext == "VK_KHR_external_memory_win32" || ext == "VK_KHR_external_semaphore_fd" || ext == "VK_KHR_external_semaphore_win32" || ext == "VK_KHR_win32_keyed_mutex") { return true; } // remove WSI-only extensions if(ext == "VK_GOOGLE_display_timing" || ext == "VK_KHR_display_swapchain" || ext == "VK_EXT_display_control" || ext == "VK_KHR_present_id" || ext == "VK_KHR_present_wait" || ext == "VK_EXT_surface_maintenance1" || ext == "VK_EXT_swapchain_maintenance1" || ext == "VK_EXT_hdr_metadata" || ext == "VK_KHR_get_display_properties2") return true; // remove fullscreen exclusive extension if(ext == "VK_EXT_full_screen_exclusive") return true; // this is debug only, nothing to capture, so nothing to replay if(ext == "VK_EXT_tooling_info" || ext == "VK_EXT_private_data" || ext == "VK_EXT_validation_features" || ext == "VK_EXT_validation_cache" || ext == "VK_EXT_validation_flags") return true; // these are debug only and will be added (if supported) as optional if(ext == "VK_EXT_debug_utils" || ext == "VK_EXT_debug_marker") return true; return false; }); } RDResult WrappedVulkan::Initialise(VkInitParams ¶ms, uint64_t sectionVersion, const ReplayOptions &opts) { m_InitParams = params; m_SectionVersion = sectionVersion; m_ReplayOptions = opts; if(!m_Replay->IsRemoteProxy()) Threading::JobSystem::Init(); m_ResourceManager->SetOptimisationLevel(m_ReplayOptions.optimisation); StripUnwantedLayers(params.Layers); StripUnwantedExtensions(params.Extensions); std::set supportedLayers; { uint32_t count = 0; GetInstanceDispatchTable(NULL)->EnumerateInstanceLayerProperties(&count, NULL); VkLayerProperties *props = new VkLayerProperties[count]; GetInstanceDispatchTable(NULL)->EnumerateInstanceLayerProperties(&count, props); for(uint32_t e = 0; e < count; e++) supportedLayers.insert(props[e].layerName); SAFE_DELETE_ARRAY(props); } if(m_ReplayOptions.apiValidation) { const char KhronosValidation[] = "VK_LAYER_KHRONOS_validation"; const char LunarGValidation[] = "VK_LAYER_LUNARG_standard_validation"; if(supportedLayers.find(KhronosValidation) != supportedLayers.end()) { RDCLOG("Enabling %s layer for API validation", KhronosValidation); params.Layers.push_back(KhronosValidation); m_LayersEnabled[VkCheckLayer_unique_objects] = true; } else if(supportedLayers.find(LunarGValidation) != supportedLayers.end()) { RDCLOG("Enabling %s layer for API validation", LunarGValidation); params.Layers.push_back(LunarGValidation); m_LayersEnabled[VkCheckLayer_unique_objects] = true; } else { RDCLOG("API validation layers are not available, check you have the Vulkan SDK installed"); } } // complain about any missing layers, but remove them from the list and continue params.Layers.removeIf([&supportedLayers](const rdcstr &layer) { if(supportedLayers.find(layer) == supportedLayers.end()) { RDCERR("Capture used layer '%s' which is not available, continuing without it", layer.c_str()); return true; } return false; }); std::set supportedExtensions; for(size_t i = 0; i <= params.Layers.size(); i++) { const char *pLayerName = (i == 0 ? NULL : params.Layers[i - 1].c_str()); uint32_t count = 0; GetInstanceDispatchTable(NULL)->EnumerateInstanceExtensionProperties(pLayerName, &count, NULL); VkExtensionProperties *props = new VkExtensionProperties[count]; GetInstanceDispatchTable(NULL)->EnumerateInstanceExtensionProperties(pLayerName, &count, props); for(uint32_t e = 0; e < count; e++) supportedExtensions.insert(props[e].extensionName); SAFE_DELETE_ARRAY(props); } if(!m_Replay->IsRemoteProxy()) { size_t i = 0; for(const rdcstr &ext : supportedExtensions) { RDCLOG("Inst Ext %u: %s", i, ext.c_str()); i++; } i = 0; for(const rdcstr &layer : supportedLayers) { RDCLOG("Inst Layer %u: %s", i, layer.c_str()); i++; } } AddRequiredExtensions(true, params.Extensions, supportedExtensions); // after 1.1, VK_KHR_get_physical_device_properties2 is promoted to core, but enable it if it's // reported as available, just in case. if(params.APIVersion >= VK_API_VERSION_1_1) { if(supportedExtensions.find(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) != supportedExtensions.end()) { if(!params.Extensions.contains(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) params.Extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); } } else { if(supportedExtensions.find(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) == supportedExtensions.end()) { RDCWARN("Unsupported required instance extension for AMD performance counters '%s'", VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); } else { if(!params.Extensions.contains(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) params.Extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); } } // enable device group extension if on 1.0, so we can use BDA. Shuts the validation layers up if(params.APIVersion <= VK_API_VERSION_1_0) { if(supportedExtensions.find(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME) != supportedExtensions.end()) { if(!params.Extensions.contains(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) params.Extensions.push_back(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME); } } // verify that extensions are supported for(size_t i = 0; i < params.Extensions.size(); i++) { if(supportedExtensions.find(params.Extensions[i]) == supportedExtensions.end()) { RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "Capture requires instance extension '%s' which is not supported\n", params.Extensions[i].c_str()); } } // we always want debug extensions if it available, and not already enabled if(supportedExtensions.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != supportedExtensions.end() && !params.Extensions.contains(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { if(!m_Replay->IsRemoteProxy()) RDCLOG("Enabling VK_EXT_debug_utils"); params.Extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } else if(supportedExtensions.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) != supportedExtensions.end() && !params.Extensions.contains(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { if(!m_Replay->IsRemoteProxy()) RDCLOG("Enabling VK_EXT_debug_report"); params.Extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } VkValidationFeaturesEXT featuresEXT = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT}; VkValidationFeatureDisableEXT disableFeatures[] = {VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT}; featuresEXT.disabledValidationFeatureCount = ARRAY_COUNT(disableFeatures); featuresEXT.pDisabledValidationFeatures = disableFeatures; // enable this to get GPU-based validation, where available, whenever we enable API validation #if 0 if(m_ReplayOptions.apiValidation) { VkValidationFeatureEnableEXT enableFeatures[] = { VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT, VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT}; featuresEXT.enabledValidationFeatureCount = ARRAY_COUNT(enableFeatures); featuresEXT.pEnabledValidationFeatures = enableFeatures; } #endif VkValidationFlagsEXT flagsEXT = {VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT}; VkValidationCheckEXT disableChecks[] = {VK_VALIDATION_CHECK_SHADERS_EXT}; flagsEXT.disabledValidationCheckCount = ARRAY_COUNT(disableChecks); flagsEXT.pDisabledValidationChecks = disableChecks; void *instNext = NULL; if(supportedExtensions.find(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) != supportedExtensions.end() && !params.Extensions.contains(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) { if(!m_Replay->IsRemoteProxy()) RDCLOG("Enabling VK_EXT_validation_features"); params.Extensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME); instNext = &featuresEXT; } else if(supportedExtensions.find(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME) != supportedExtensions.end() && !params.Extensions.contains(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME)) { if(!m_Replay->IsRemoteProxy()) RDCLOG("Enabling VK_EXT_validation_flags"); params.Extensions.push_back(VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME); instNext = &flagsEXT; } const char **layerscstr = new const char *[params.Layers.size()]; for(size_t i = 0; i < params.Layers.size(); i++) layerscstr[i] = params.Layers[i].c_str(); const char **extscstr = new const char *[params.Extensions.size()]; for(size_t i = 0; i < params.Extensions.size(); i++) extscstr[i] = params.Extensions[i].c_str(); VkInstanceCreateInfo instinfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, instNext, 0, &renderdocAppInfo, (uint32_t)params.Layers.size(), layerscstr, (uint32_t)params.Extensions.size(), extscstr, }; if(params.APIVersion >= VK_API_VERSION_1_0) renderdocAppInfo.apiVersion = params.APIVersion; m_EnabledExtensions.vulkanVersion = renderdocAppInfo.apiVersion; if(!Vulkan_Debug_ReplaceAppInfo()) { // if we're not replacing the app info, set renderdocAppInfo's parameters to the ones from the // capture renderdocAppInfo.pEngineName = params.EngineName.c_str(); renderdocAppInfo.engineVersion = params.EngineVersion; renderdocAppInfo.pApplicationName = params.AppName.c_str(); renderdocAppInfo.applicationVersion = params.AppVersion; } m_Instance = VK_NULL_HANDLE; VkResult ret = GetInstanceDispatchTable(NULL)->CreateInstance(&instinfo, NULL, &m_Instance); #undef CheckExt #define CheckExt(name, ver) \ if(!strcmp(instinfo.ppEnabledExtensionNames[i], "VK_" #name) || renderdocAppInfo.apiVersion >= ver) \ { \ m_EnabledExtensions.ext_##name = true; \ } for(uint32_t i = 0; i < instinfo.enabledExtensionCount; i++) { CheckInstanceExts(); } SAFE_DELETE_ARRAY(layerscstr); SAFE_DELETE_ARRAY(extscstr); if(ret != VK_SUCCESS) { RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "Vulkan instance creation returned %s", ToStr(ret).c_str()); } RDCASSERTEQUAL(ret, VK_SUCCESS); GetResourceManager()->WrapResource(m_Instance, m_Instance); // we'll add the chunk later when we re-process it. if(params.InstanceID != ResourceId()) { GetResourceManager()->AddLiveResource(params.InstanceID, m_Instance); AddResource(params.InstanceID, ResourceType::Device, "Instance"); GetReplay()->GetResourceDesc(params.InstanceID).initialisationChunks.clear(); } else { GetResourceManager()->AddLiveResource(GetResID(m_Instance), m_Instance); } InitInstanceExtensionTables(m_Instance, &m_EnabledExtensions); m_DbgReportCallback = VK_NULL_HANDLE; m_DbgUtilsCallback = VK_NULL_HANDLE; m_PhysicalDevice = VK_NULL_HANDLE; m_Device = VK_NULL_HANDLE; m_QueueFamilyIdx = ~0U; m_PrevQueue = m_Queue = VK_NULL_HANDLE; m_InternalCmds.Reset(); if(ObjDisp(m_Instance)->CreateDebugUtilsMessengerEXT) { VkDebugUtilsMessengerCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugInfo.pfnUserCallback = &DebugUtilsCallbackStatic; debugInfo.pUserData = this; debugInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; debugInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; ObjDisp(m_Instance) ->CreateDebugUtilsMessengerEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgUtilsCallback); } else if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT) { VkDebugReportCallbackCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debugInfo.pfnCallback = &DebugReportCallbackStatic; debugInfo.pUserData = this; debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT; ObjDisp(m_Instance) ->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgReportCallback); } uint32_t count = 0; VkResult vkr = ObjDisp(m_Instance)->EnumeratePhysicalDevices(Unwrap(m_Instance), &count, NULL); CHECK_VKR(this, vkr); if(count == 0) { RETURN_ERROR_RESULT(ResultCode::APIHardwareUnsupported, "No physical devices exist in this vulkan instance"); } m_ReplayPhysicalDevices.resize(count); m_ReplayPhysicalDevicesUsed.resize(count); m_OriginalPhysicalDevices.resize(count); vkr = ObjDisp(m_Instance) ->EnumeratePhysicalDevices(Unwrap(m_Instance), &count, &m_ReplayPhysicalDevices[0]); CHECK_VKR(this, vkr); for(uint32_t i = 0; i < count; i++) GetResourceManager()->WrapResource(m_Instance, m_ReplayPhysicalDevices[i]); #if ENABLED(RDOC_WIN32) if(GetModuleHandleA("nvoglv64.dll")) LoadLibraryA("nvoglv64.dll"); #endif return ResultCode::Succeeded; } VkResult WrappedVulkan::vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *, VkInstance *pInstance) { RDCASSERT(pCreateInfo); // don't support any extensions for this createinfo RDCASSERT(pCreateInfo->pApplicationInfo == NULL || pCreateInfo->pApplicationInfo->pNext == NULL); const bool internalInstance = (pCreateInfo->pApplicationInfo && pCreateInfo->pApplicationInfo->pApplicationName && rdcstr(pCreateInfo->pApplicationInfo->pApplicationName) == "RenderDoc forced instance"); VkLayerInstanceCreateInfo *layerCreateInfo = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext until we get to the link info while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || layerCreateInfo->function != VK_LAYER_LINK_INFO)) { layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext; } RDCASSERT(layerCreateInfo); if(layerCreateInfo == NULL) { RDCERR("Couldn't find loader instance create info, which is required. Incompatible loader?"); return VK_ERROR_INITIALIZATION_FAILED; } PFN_vkGetInstanceProcAddr gpa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; // move chain on for next layer layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; PFN_vkCreateInstance createFunc = (PFN_vkCreateInstance)gpa(VK_NULL_HANDLE, "vkCreateInstance"); VkInstanceCreateInfo modifiedCreateInfo; modifiedCreateInfo = *pCreateInfo; // Android is a bad platform with a bad loader that it custom wrote, and implicit layers get added // to the create info's layer list even if they weren't specified, so it's impossible to tell if // it's an application making a mistake or the platform messing up. Disable this protection on Android #if DISABLED(RDOC_ANDROID) for(uint32_t i = 0; i < modifiedCreateInfo.enabledLayerCount; i++) { if(rdcstr(modifiedCreateInfo.ppEnabledLayerNames[i]) == RENDERDOC_VULKAN_LAYER_NAME) { // see if any debug report callbacks were passed in the pNext chain VkDebugReportCallbackCreateInfoEXT *report = (VkDebugReportCallbackCreateInfoEXT *)pCreateInfo->pNext; rdcstr msg = "RenderDoc's layer should NEVER be activated manually. Do not include it in " "vkCreateInstance's instance layers."; RDCERR("%s", msg.c_str()); while(report) { if(report->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT) report->pfnCallback(VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, 0, 1, 1, "RDOC", msg.c_str(), report->pUserData); report = (VkDebugReportCallbackCreateInfoEXT *)report->pNext; } // or debug utils callbacks VkDebugUtilsMessengerCreateInfoEXT *messenger = (VkDebugUtilsMessengerCreateInfoEXT *)pCreateInfo->pNext; VkDebugUtilsMessengerCallbackDataEXT messengerData = {}; messengerData.messageIdNumber = 1; messengerData.pMessageIdName = NULL; messengerData.pMessage = msg.c_str(); messengerData.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; while(messenger) { if(messenger->sType == VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) messenger->pfnUserCallback(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, &messengerData, messenger->pUserData); messenger = (VkDebugUtilsMessengerCreateInfoEXT *)messenger->pNext; } return VK_ERROR_INITIALIZATION_FAILED; } } #endif for(uint32_t i = 0; i < modifiedCreateInfo.enabledExtensionCount; i++) { if(!IsSupportedExtension(modifiedCreateInfo.ppEnabledExtensionNames[i])) { RDCERR("RenderDoc does not support instance extension '%s'.", modifiedCreateInfo.ppEnabledExtensionNames[i]); RDCERR( "For KHR/EXT extensions file an issue on github to request support: " "https://github.com/baldurk/renderdoc"); // see if any debug report callbacks were passed in the pNext chain VkDebugReportCallbackCreateInfoEXT *report = (VkDebugReportCallbackCreateInfoEXT *)pCreateInfo->pNext; rdcstr msg = StringFormat::Fmt("RenderDoc does not support requested instance extension: %s.", modifiedCreateInfo.ppEnabledExtensionNames[i]); while(report) { if(report->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT) report->pfnCallback(VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, 0, 1, 1, "RDOC", msg.c_str(), report->pUserData); report = (VkDebugReportCallbackCreateInfoEXT *)report->pNext; } // or debug utils callbacks VkDebugUtilsMessengerCreateInfoEXT *messenger = (VkDebugUtilsMessengerCreateInfoEXT *)pCreateInfo->pNext; VkDebugUtilsMessengerCallbackDataEXT messengerData = {}; messengerData.messageIdNumber = 1; messengerData.pMessageIdName = NULL; messengerData.pMessage = msg.c_str(); messengerData.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; while(messenger) { if(messenger->sType == VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) messenger->pfnUserCallback(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, &messengerData, messenger->pUserData); messenger = (VkDebugUtilsMessengerCreateInfoEXT *)messenger->pNext; } return VK_ERROR_EXTENSION_NOT_PRESENT; } } const char **addedExts = NULL; if(!internalInstance) { addedExts = new const char *[modifiedCreateInfo.enabledExtensionCount + 2]; bool hasDebugReport = false, hasDebugUtils = false, hasGPDP2 = false; for(uint32_t i = 0; i < modifiedCreateInfo.enabledExtensionCount; i++) { addedExts[i] = modifiedCreateInfo.ppEnabledExtensionNames[i]; if(!strcmp(addedExts[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) hasDebugReport = true; if(!strcmp(addedExts[i], VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) hasDebugUtils = true; if(!strcmp(addedExts[i], VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) hasGPDP2 = true; } rdcarray supportedExts; // enumerate what instance extensions are available void *module = LoadVulkanLibrary(); if(module) { PFN_vkEnumerateInstanceExtensionProperties enumInstExts = (PFN_vkEnumerateInstanceExtensionProperties)Process::GetFunctionAddress( module, "vkEnumerateInstanceExtensionProperties"); if(enumInstExts) { uint32_t numSupportedExts = 0; enumInstExts(NULL, &numSupportedExts, NULL); supportedExts.resize(numSupportedExts); enumInstExts(NULL, &numSupportedExts, &supportedExts[0]); } } if(supportedExts.empty()) RDCWARN( "Couldn't load vkEnumerateInstanceExtensionProperties in vkCreateInstance to enumerate " "instance extensions"); // always enable GPDP2 if it's available if(!hasGPDP2) { for(const VkExtensionProperties &ext : supportedExts) { if(!strcmp(ext.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; break; } } } // always enable debug report/utils, if it's available if(!hasDebugUtils) { for(const VkExtensionProperties &ext : supportedExts) { if(!strcmp(ext.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; break; } } } else if(!hasDebugReport) { for(const VkExtensionProperties &ext : supportedExts) { if(!strcmp(ext.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; break; } } } modifiedCreateInfo.ppEnabledExtensionNames = addedExts; } bool brokenGetDeviceProcAddr = false; // override applicationInfo with RenderDoc's, but preserve apiVersion if(modifiedCreateInfo.pApplicationInfo) { if(modifiedCreateInfo.pApplicationInfo->pEngineName && strlower(modifiedCreateInfo.pApplicationInfo->pEngineName) == "idtech") brokenGetDeviceProcAddr = true; if(modifiedCreateInfo.pApplicationInfo->apiVersion >= VK_API_VERSION_1_0) renderdocAppInfo.apiVersion = modifiedCreateInfo.pApplicationInfo->apiVersion; if(Vulkan_Debug_ReplaceAppInfo()) { modifiedCreateInfo.pApplicationInfo = &renderdocAppInfo; } } for(uint32_t i = 0; i < modifiedCreateInfo.enabledLayerCount; i++) { if(!strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_LUNARG_standard_validation") || !strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_KHRONOS_validation") || !strcmp(modifiedCreateInfo.ppEnabledLayerNames[i], "VK_LAYER_GOOGLE_unique_objects")) { m_LayersEnabled[VkCheckLayer_unique_objects] = true; } } // if we forced on API validation, it's also available m_LayersEnabled[VkCheckLayer_unique_objects] |= RenderDoc::Inst().GetCaptureOptions().apiValidation; VkResult ret = createFunc(&modifiedCreateInfo, NULL, pInstance); m_Instance = *pInstance; InitInstanceTable(m_Instance, gpa); GetResourceManager()->WrapResource(m_Instance, m_Instance); *pInstance = m_Instance; // should only be called during capture RDCASSERT(IsCaptureMode(m_State)); m_InitParams.Set(pCreateInfo, GetResID(m_Instance)); VkResourceRecord *record = GetResourceManager()->AddResourceRecord(m_Instance); record->instDevInfo = new InstanceDeviceInfo(); record->instDevInfo->brokenGetDeviceProcAddr = brokenGetDeviceProcAddr; record->instDevInfo->vulkanVersion = VK_API_VERSION_1_0; // whether or not we're using it, we updated the apiVersion in renderdocAppInfo if(renderdocAppInfo.apiVersion > VK_API_VERSION_1_0) record->instDevInfo->vulkanVersion = renderdocAppInfo.apiVersion; std::set availablePhysDeviceFunctions; { uint32_t count = 0; ObjDisp(m_Instance)->EnumeratePhysicalDevices(Unwrap(m_Instance), &count, NULL); rdcarray physDevs; physDevs.resize(count); ObjDisp(m_Instance)->EnumeratePhysicalDevices(Unwrap(m_Instance), &count, physDevs.data()); rdcarray exts; for(VkPhysicalDevice p : physDevs) { ObjDisp(m_Instance)->EnumerateDeviceExtensionProperties(p, NULL, &count, NULL); exts.resize(count); ObjDisp(m_Instance)->EnumerateDeviceExtensionProperties(p, NULL, &count, exts.data()); for(const VkExtensionProperties &e : exts) { availablePhysDeviceFunctions.insert(e.extensionName); } } // we don't bother wrapping these, they're temporary handles } // an extension is available if: // * it's enabled in the instance creation // * it's promoted in the selected vulkan version // * it's a device extension and available on at least one physical device #undef CheckExt #define CheckExt(name, ver) \ if(record->instDevInfo->vulkanVersion >= ver || \ availablePhysDeviceFunctions.find("VK_" #name) != availablePhysDeviceFunctions.end()) \ { \ record->instDevInfo->ext_##name = true; \ } CheckInstanceExts(); #undef CheckExt #define CheckExt(name, ver) \ if(!strcmp(modifiedCreateInfo.ppEnabledExtensionNames[i], "VK_" #name)) \ { \ record->instDevInfo->ext_##name = true; \ } for(uint32_t i = 0; i < modifiedCreateInfo.enabledExtensionCount; i++) { CheckInstanceExts(); } SAFE_DELETE_ARRAY(addedExts); InitInstanceExtensionTables(m_Instance, record->instDevInfo); // don't register a frame capturer for our internal instance on android if(internalInstance) { RDCDEBUG("Not registering internal instance as frame capturer"); } else { RenderDoc::Inst().AddDeviceFrameCapturer(LayerDisp(m_Instance), this); } m_DbgReportCallback = VK_NULL_HANDLE; m_DbgUtilsCallback = VK_NULL_HANDLE; m_PhysicalDevice = VK_NULL_HANDLE; m_Device = VK_NULL_HANDLE; m_QueueFamilyIdx = ~0U; m_PrevQueue = m_Queue = VK_NULL_HANDLE; m_InternalCmds.Reset(); if(ObjDisp(m_Instance)->CreateDebugUtilsMessengerEXT) { VkDebugUtilsMessengerCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugInfo.pfnUserCallback = &DebugUtilsCallbackStatic; debugInfo.pUserData = this; debugInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; debugInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; ObjDisp(m_Instance) ->CreateDebugUtilsMessengerEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgUtilsCallback); } else if(ObjDisp(m_Instance)->CreateDebugReportCallbackEXT) { VkDebugReportCallbackCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debugInfo.pNext = NULL; debugInfo.pfnCallback = &DebugReportCallbackStatic; debugInfo.pUserData = this; debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT; ObjDisp(m_Instance) ->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgReportCallback); } if(ret == VK_SUCCESS) { RDCLOG("Initialised capture layer in Vulkan instance."); } return ret; } void WrappedVulkan::Shutdown() { if(!m_Replay->IsRemoteProxy()) { Threading::JobSystem::Shutdown(); GetResourceManager()->ResolveDeferredWrappers(); } // flush out any pending commands/semaphores SubmitCmds(); SubmitSemaphores(); FlushQ(); // idle the device as well so that external queues are idle. if(m_Device) { VkResult vkr = ObjDisp(m_Device)->DeviceWaitIdle(Unwrap(m_Device)); CHECK_VKR(this, vkr); } // since we didn't create proper registered resources for our command buffers, // they won't be taken down properly with the pool. So we release them (just our // data) here. for(size_t i = 0; i < m_InternalCmds.freecmds.size(); i++) GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.freecmds[i]); if(m_IndirectCommandBuffer != VK_NULL_HANDLE) GetResourceManager()->ReleaseWrappedResource(m_IndirectCommandBuffer); // destroy the pool if(m_Device != VK_NULL_HANDLE && m_InternalCmds.cmdpool != VK_NULL_HANDLE) { ObjDisp(m_Device)->DestroyCommandPool(Unwrap(m_Device), Unwrap(m_InternalCmds.cmdpool), NULL); GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.cmdpool); } for(size_t i = 0; i < m_InternalCmds.freesems.size(); i++) { ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_InternalCmds.freesems[i]), NULL); GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.freesems[i]); } for(size_t i = 0; i < m_ExternalQueues.size(); i++) { if(m_ExternalQueues[i].pool != VK_NULL_HANDLE) { for(size_t x = 0; x < ARRAY_COUNT(m_ExternalQueues[i].ring); x++) { GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].acquire); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].release); ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].fromext), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].fromext); ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].toext), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].toext); ObjDisp(m_Device)->DestroyFence(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].fence), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].fence); } ObjDisp(m_Device)->DestroyCommandPool(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].pool), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].pool); } } FreeAllMemory(MemoryScope::InitialContents); FreeAllMemory(MemoryScope::InitialContentsFirstApplyOnly); if(m_MemoryFreeThread) { Threading::JoinThread(m_MemoryFreeThread); Threading::CloseThread(m_MemoryFreeThread); m_MemoryFreeThread = 0; } // we do more in Shutdown than the equivalent vkDestroyInstance since on replay there's // no explicit vkDestroyDevice, we destroy the device here then the instance // destroy the physical devices manually because due to remapping the may have leftover // refcounts for(size_t i = 0; i < m_ReplayPhysicalDevices.size(); i++) GetResourceManager()->ReleaseWrappedResource(m_ReplayPhysicalDevices[i]); m_ASManager->Cleanup(); m_Replay->DestroyResources(); m_IndirectBuffer.Destroy(); // destroy debug manager and any objects it created SAFE_DELETE(m_DebugManager); SAFE_DELETE(m_ShaderCache); if(m_Instance && ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT && m_DbgReportCallback != VK_NULL_HANDLE) ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgReportCallback, NULL); if(m_Instance && ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT && m_DbgUtilsCallback != VK_NULL_HANDLE) ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT(Unwrap(m_Instance), m_DbgUtilsCallback, NULL); // need to store the unwrapped device and instance to destroy the // API object after resource manager shutdown VkInstance inst = Unwrap(m_Instance); VkDevice dev = Unwrap(m_Device); const VkDevDispatchTable *vt = m_Device != VK_NULL_HANDLE ? ObjDisp(m_Device) : NULL; const VkInstDispatchTable *vit = m_Instance != VK_NULL_HANDLE ? ObjDisp(m_Instance) : NULL; // this destroys the wrapped objects for the devices and instances m_ResourceManager->Shutdown(); delete GetWrapped(m_Device); delete GetWrapped(m_Instance); m_PhysicalDevice = VK_NULL_HANDLE; m_Device = VK_NULL_HANDLE; m_Instance = VK_NULL_HANDLE; m_ReplayPhysicalDevices.clear(); m_PhysicalDevices.clear(); for(size_t i = 0; i < m_QueueFamilies.size(); i++) delete[] m_QueueFamilies[i]; m_QueueFamilies.clear(); // finally destroy device then instance if(vt) vt->DestroyDevice(dev, NULL); if(vit) vit->DestroyInstance(inst, NULL); } void WrappedVulkan::vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *) { if(instance == VK_NULL_HANDLE) return; RDCASSERT(m_Instance == instance); if(ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT && m_DbgReportCallback != VK_NULL_HANDLE) ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgReportCallback, NULL); if(ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT && m_DbgUtilsCallback != VK_NULL_HANDLE) ObjDisp(m_Instance)->DestroyDebugUtilsMessengerEXT(Unwrap(m_Instance), m_DbgUtilsCallback, NULL); // the device should already have been destroyed, assuming that the // application is well behaved. If not, we just leak. ObjDisp(m_Instance)->DestroyInstance(Unwrap(m_Instance), NULL); RenderDoc::Inst().RemoveDeviceFrameCapturer(LayerDisp(m_Instance)); GetResourceManager()->ReleaseWrappedResource(m_Instance); m_Instance = VK_NULL_HANDLE; } template bool WrappedVulkan::Serialise_vkEnumeratePhysicalDevices(SerialiserType &ser, VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { SERIALISE_ELEMENT(instance); SERIALISE_ELEMENT_LOCAL(PhysicalDeviceIndex, *pPhysicalDeviceCount); SERIALISE_ELEMENT_LOCAL(PhysicalDevice, GetResID(*pPhysicalDevices)) .TypedAs("VkPhysicalDevice"_lit) .Important(); uint32_t legacyUnused_memIdxMap[VK_MAX_MEMORY_TYPES] = {0}; // not used at the moment but useful for reference and might be used // in the future VkPhysicalDeviceProperties physProps = {}; VkPhysicalDeviceMemoryProperties memProps = {}; VkPhysicalDeviceFeatures physFeatures = {}; uint32_t queueCount = 0; VkQueueFamilyProperties queueProps[16] = {}; VkPhysicalDeviceDriverProperties driverProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, }; if(ser.IsWriting()) { ObjDisp(instance)->GetPhysicalDeviceProperties(Unwrap(*pPhysicalDevices), &physProps); ObjDisp(instance)->GetPhysicalDeviceMemoryProperties(Unwrap(*pPhysicalDevices), &memProps); ObjDisp(instance)->GetPhysicalDeviceFeatures(Unwrap(*pPhysicalDevices), &physFeatures); ObjDisp(instance)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(*pPhysicalDevices), &queueCount, NULL); if(queueCount > 16) { RDCERR("More than 16 queue families"); queueCount = 16; } ObjDisp(instance)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(*pPhysicalDevices), &queueCount, queueProps); GetPhysicalDeviceDriverProperties(ObjDisp(instance), Unwrap(*pPhysicalDevices), driverProps); } SERIALISE_ELEMENT(legacyUnused_memIdxMap).Hidden(); // was never used SERIALISE_ELEMENT(physProps); SERIALISE_ELEMENT(memProps); SERIALISE_ELEMENT(physFeatures); SERIALISE_ELEMENT(queueCount); SERIALISE_ELEMENT(queueProps); // serialisation of the driver properties was added in 0x10 if(ser.VersionAtLeast(0x10)) { SERIALISE_ELEMENT(driverProps); // we don't need any special handling if this is missing - the properties will be empty which is // the same as a new capture if we can't query the properties } VkPhysicalDevice pd = VK_NULL_HANDLE; SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { bool firstTime = true; for(bool used : m_ReplayPhysicalDevicesUsed) if(used) firstTime = false; { if(PhysicalDeviceIndex >= m_OriginalPhysicalDevices.size()) m_OriginalPhysicalDevices.resize(PhysicalDeviceIndex + 1); m_OriginalPhysicalDevices[PhysicalDeviceIndex].props = physProps; m_OriginalPhysicalDevices[PhysicalDeviceIndex].driverProps = driverProps; m_OriginalPhysicalDevices[PhysicalDeviceIndex].memProps = memProps; m_OriginalPhysicalDevices[PhysicalDeviceIndex].availFeatures = physFeatures; m_OriginalPhysicalDevices[PhysicalDeviceIndex].queueCount = queueCount; memcpy(m_OriginalPhysicalDevices[PhysicalDeviceIndex].queueProps, queueProps, sizeof(queueProps)); } // match up physical devices to those available on replay as best as possible. In general // hopefully the most common case is when there's a precise match, and maybe the order changed. // // If more GPUs were present on replay than during capture, we map many-to-one which might have // bad side-effects, but this is as good as we can do. uint32_t bestIdx = 0; VkPhysicalDeviceProperties bestPhysProps = {}; VkPhysicalDeviceDriverProperties bestDriverProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, }; rdcarray compPhysPropsArray; rdcarray compDriverPropsArray; compPhysPropsArray.resize(m_ReplayPhysicalDevices.size()); compDriverPropsArray.resize(m_ReplayPhysicalDevices.size()); // first cache all the physical device data to compare against for(uint32_t i = 0; i < (uint32_t)m_ReplayPhysicalDevices.size(); i++) { VkPhysicalDeviceProperties &compPhysProps = compPhysPropsArray[i]; RDCEraseEl(compPhysProps); VkPhysicalDeviceDriverProperties &compDriverProps = compDriverPropsArray[i]; RDCEraseEl(compDriverProps); compDriverProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; pd = m_ReplayPhysicalDevices[i]; // find the best possible match for this physical device ObjDisp(pd)->GetPhysicalDeviceProperties(Unwrap(pd), &compPhysProps); GetPhysicalDeviceDriverProperties(ObjDisp(pd), Unwrap(pd), compDriverProps); if(firstTime) { VkDriverInfo runningVersion(compPhysProps, compDriverProps); RDCLOG("Replay has physical device %u available:", i); RDCLOG(" - %s (ver %u.%u patch 0x%x) - %04x:%04x", compPhysProps.deviceName, runningVersion.Major(), runningVersion.Minor(), runningVersion.Patch(), compPhysProps.vendorID, compPhysProps.deviceID); if(compDriverProps.driverID != 0) { RDCLOG( " - %s driver: %s (%s) - CTS %d.%d.%d.%d", ToStr(compDriverProps.driverID).c_str(), compDriverProps.driverName, compDriverProps.driverInfo, compDriverProps.conformanceVersion.major, compDriverProps.conformanceVersion.minor, compDriverProps.conformanceVersion.subminor, compDriverProps.conformanceVersion.patch); } } } // if we're forcing use of a GPU, try to find that one bool forced = false; if(m_ReplayOptions.forceGPUVendor != GPUVendor::Unknown) { for(uint32_t i = 0; i < (uint32_t)m_ReplayPhysicalDevices.size(); i++) { const VkPhysicalDeviceProperties &compPhysProps = compPhysPropsArray[i]; const VkPhysicalDeviceDriverProperties &compDriverProps = compDriverPropsArray[i]; VkDriverInfo bestInfo(bestPhysProps, bestDriverProps); VkDriverInfo compInfo(compPhysProps, compDriverProps); // an exact vendorID match is a better match than not if(compInfo.Vendor() == m_ReplayOptions.forceGPUVendor && bestInfo.Vendor() != m_ReplayOptions.forceGPUVendor) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; forced = true; continue; } else if(compInfo.Vendor() != m_ReplayOptions.forceGPUVendor) { continue; } // ditto deviceID if(compPhysProps.deviceID == m_ReplayOptions.forceGPUDeviceID && bestPhysProps.deviceID != m_ReplayOptions.forceGPUDeviceID) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; forced = true; continue; } else if(compPhysProps.deviceID != m_ReplayOptions.forceGPUDeviceID) { continue; } // driver matching. Only do this if we have a driver name to look at if(compDriverProps.driverID && !m_ReplayOptions.forceGPUDriverName.empty()) { rdcstr compHumanDriverName = HumanDriverName(compDriverProps.driverID); rdcstr bestHumanDriverName = HumanDriverName(bestDriverProps.driverID); // check for a better driverID match if(compHumanDriverName == m_ReplayOptions.forceGPUDriverName && bestHumanDriverName != m_ReplayOptions.forceGPUDriverName) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; forced = true; continue; } } } } if(forced) { RDCLOG("Forcing use of physical device"); } else { bestIdx = 0; RDCEraseEl(bestPhysProps); RDCEraseEl(bestDriverProps); for(uint32_t i = 0; i < (uint32_t)m_ReplayPhysicalDevices.size(); i++) { const VkPhysicalDeviceProperties &compPhysProps = compPhysPropsArray[i]; const VkPhysicalDeviceDriverProperties &compDriverProps = compDriverPropsArray[i]; pd = m_ReplayPhysicalDevices[i]; // the first is the best at the start if(i == 0) { bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; continue; } // an exact vendorID match is a better match than not if(compPhysProps.vendorID == physProps.vendorID && bestPhysProps.vendorID != physProps.vendorID) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; continue; } else if(compPhysProps.vendorID != physProps.vendorID) { continue; } // ditto deviceID if(compPhysProps.deviceID == physProps.deviceID && bestPhysProps.deviceID != physProps.deviceID) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; continue; } else if(compPhysProps.deviceID != physProps.deviceID) { continue; } // driver matching. Only do this if both capture and replay gave us valid driver info to // compare if(compDriverProps.driverID && driverProps.driverID) { // check for a better driverID match if(compDriverProps.driverID == driverProps.driverID && bestDriverProps.driverID != driverProps.driverID) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; continue; } else if(compDriverProps.driverID != driverProps.driverID) { continue; } } // if we have an exact driver version match, prefer that if(compPhysProps.driverVersion == physProps.driverVersion && bestPhysProps.driverVersion != physProps.driverVersion) { bestIdx = i; bestPhysProps = compPhysProps; bestDriverProps = compDriverProps; continue; } else if(compPhysProps.driverVersion != physProps.driverVersion) { continue; } // if we have multiple identical devices, which isn't uncommon, favour the one // that hasn't been assigned if(m_ReplayPhysicalDevicesUsed[bestIdx] && !m_ReplayPhysicalDevicesUsed[i]) { bestIdx = i; bestPhysProps = compPhysProps; continue; } // this device isn't any better, ignore it } } { VkDriverInfo capturedVersion(physProps, driverProps); RDCLOG("Found capture physical device %u:", PhysicalDeviceIndex); RDCLOG(" - %s (ver %u.%u patch 0x%x) - %04x:%04x", physProps.deviceName, capturedVersion.Major(), capturedVersion.Minor(), capturedVersion.Patch(), physProps.vendorID, physProps.deviceID); if(driverProps.driverID != 0) { RDCLOG(" - %s driver: %s (%s) - CTS %d.%d.%d.%d", ToStr(driverProps.driverID).c_str(), driverProps.driverName, driverProps.driverInfo, driverProps.conformanceVersion.major, driverProps.conformanceVersion.minor, driverProps.conformanceVersion.subminor, driverProps.conformanceVersion.patch); } RDCLOG("Mapping during replay to %s physical device %u", forced ? "forced" : "best-match", bestIdx); } pd = m_ReplayPhysicalDevices[bestIdx]; { VkPhysicalDevice fakeDevice = MakePhysicalDeviceHandleFromIndex(PhysicalDeviceIndex); ResourceId id = ResourceIDGen::GetNewUniqueID(); WrappedVkPhysicalDevice *wrapped = new WrappedVkPhysicalDevice(fakeDevice, id); GetResourceManager()->AddCurrentResource(id, wrapped); if(IsReplayMode(m_State)) GetResourceManager()->AddWrapper(wrapped, ToTypedHandle(fakeDevice)); fakeDevice = (VkPhysicalDevice)wrapped; // we want to preserve the separate physical devices until we actually need the real handle, // so don't remap multiple capture-time physical devices to one replay-time physical device // yet. See below in Serialise_vkCreateDevice where this is decoded. // Note this allocation is pooled so we don't have to explicitly delete it. GetResourceManager()->AddLiveResource(PhysicalDevice, fakeDevice); } AddResource(PhysicalDevice, ResourceType::Device, "Physical Device"); DerivedResource(m_Instance, PhysicalDevice); if(PhysicalDeviceIndex >= m_PhysicalDevices.size()) m_PhysicalDevices.resize(PhysicalDeviceIndex + 1); m_PhysicalDevices[PhysicalDeviceIndex] = pd; if(m_ReplayPhysicalDevicesUsed[bestIdx]) { // error if we're remapping multiple physical devices to the same best match RDCWARN( "Mapping multiple capture-time physical devices to a single replay-time physical device." "This means the HW has changed between capture and replay and may cause bugs."); } m_ReplayPhysicalDevicesUsed[bestIdx] = true; } return true; } VkResult WrappedVulkan::vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { uint32_t count; VkResult vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, NULL); if(vkr != VK_SUCCESS) return vkr; VkPhysicalDevice *devices = new VkPhysicalDevice[count]; SERIALISE_TIME_CALL( vkr = ObjDisp(instance)->EnumeratePhysicalDevices(Unwrap(instance), &count, devices)); CHECK_VKR(this, vkr); m_PhysicalDevices.resize(count); for(uint32_t i = 0; i < count; i++) { // it's perfectly valid for enumerate type functions to return the same handle // each time. If that happens, we will already have a wrapper created so just // return the wrapped object to the user and do nothing else if(m_PhysicalDevices[i] != VK_NULL_HANDLE) { GetWrapped(m_PhysicalDevices[i])->RewrapObject(devices[i]); devices[i] = m_PhysicalDevices[i]; } else { GetResourceManager()->WrapResource(instance, devices[i]); if(IsCaptureMode(m_State)) { // add the record first since it's used in the serialise function below to fetch // the memory indices VkResourceRecord *record = GetResourceManager()->AddResourceRecord(devices[i]); RDCASSERT(record); VkResourceRecord *instrecord = GetRecord(instance); VkPhysicalDeviceProperties physProps; ObjDisp(devices[i])->GetPhysicalDeviceProperties(Unwrap(devices[i]), &physProps); VkPhysicalDeviceDriverProperties driverProps = {}; GetPhysicalDeviceDriverProperties(ObjDisp(devices[i]), Unwrap(devices[i]), driverProps); VkDriverInfo capturedVersion(physProps, driverProps); RDCLOG("physical device %u: %s (ver %u.%u patch 0x%x) - %04x:%04x", i, physProps.deviceName, capturedVersion.Major(), capturedVersion.Minor(), capturedVersion.Patch(), physProps.vendorID, physProps.deviceID); m_PhysicalDevices[i] = devices[i]; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CHUNK(VulkanChunk::vkEnumeratePhysicalDevices); Serialise_vkEnumeratePhysicalDevices(ser, instance, &i, &devices[i]); record->AddChunk(scope.Get()); } instrecord->AddParent(record); // copy the instance's setup directly record->instDevInfo = new InstanceDeviceInfo(*instrecord->instDevInfo); // treat physical devices as pool members of the instance (ie. freed when the instance dies) { instrecord->LockChunks(); instrecord->pooledChildren.push_back(record); instrecord->UnlockChunks(); } } } } VkResult result = VK_SUCCESS; if(pPhysicalDevices) { if(count > *pPhysicalDeviceCount) { count = *pPhysicalDeviceCount; result = VK_INCOMPLETE; } memcpy(pPhysicalDevices, devices, count * sizeof(VkPhysicalDevice)); } *pPhysicalDeviceCount = count; SAFE_DELETE_ARRAY(devices); return result; } bool WrappedVulkan::SelectGraphicsComputeQueue(const rdcarray &queueProps, VkDeviceCreateInfo &createInfo, uint32_t &queueFamilyIndex) { // storage for if we need to change the requested queues static rdcarray modQueues; bool found = false; // we need graphics, and if there is a graphics queue there must be a graphics & compute queue. const VkQueueFlags search = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT); // for queue priorities, if we need it static const float one = 1.0f; for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) { uint32_t idx = createInfo.pQueueCreateInfos[i].queueFamilyIndex; RDCASSERT(idx < queueProps.size()); // this requested queue is one we can use too if((queueProps[idx].queueFlags & search) == search && createInfo.pQueueCreateInfos[i].queueCount > 0) { queueFamilyIndex = idx; found = true; break; } } // if we didn't find it, search for which queue family we should add a request for if(!found) { RDCDEBUG("App didn't request a queue family we can use - adding our own"); for(uint32_t i = 0; i < queueProps.size(); i++) { if((queueProps[i].queueFlags & search) == search) { queueFamilyIndex = i; found = true; break; } } if(!found) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Can't add a queue with required properties for RenderDoc! Unsupported configuration"); return false; } // we found the queue family, add it modQueues.resize(createInfo.queueCreateInfoCount + 1); for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) modQueues[i] = createInfo.pQueueCreateInfos[i]; modQueues[createInfo.queueCreateInfoCount].queueFamilyIndex = queueFamilyIndex; modQueues[createInfo.queueCreateInfoCount].queueCount = 1; modQueues[createInfo.queueCreateInfoCount].pQueuePriorities = &one; modQueues[createInfo.queueCreateInfoCount].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; modQueues[createInfo.queueCreateInfoCount].pNext = NULL; modQueues[createInfo.queueCreateInfoCount].flags = 0; createInfo.pQueueCreateInfos = modQueues.data(); createInfo.queueCreateInfoCount++; } return true; } void WrappedVulkan::SendUserDebugMessage(const rdcstr &msg) { VkDebugUtilsMessengerCallbackDataEXT messengerData = {}; messengerData.messageIdNumber = 1; messengerData.pMessageIdName = NULL; messengerData.pMessage = msg.c_str(); messengerData.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; { SCOPED_LOCK(m_CallbacksLock); for(UserDebugReportCallbackData *cb : m_ReportCallbacks) { cb->createInfo.pfnCallback(VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT, 0, 1, 1, "RDOC", msg.c_str(), cb->createInfo.pUserData); } for(UserDebugUtilsCallbackData *cb : m_UtilsCallbacks) { cb->createInfo.pfnUserCallback(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &messengerData, cb->createInfo.pUserData); } } } template bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { SERIALISE_ELEMENT(physicalDevice).Important(); SERIALISE_ELEMENT_LOCAL(CreateInfo, *pCreateInfo).Important(); SERIALISE_ELEMENT_OPT(pAllocator); SERIALISE_ELEMENT_LOCAL(Device, GetResID(*pDevice)).TypedAs("VkDevice"_lit); if(ser.VersionLess(0xD)) { uint32_t supportedQueueFamily; // no longer used SERIALISE_ELEMENT(supportedQueueFamily).Hidden(); } SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { // kept around only to call DerivedResource below, as this is the resource that actually has an // original resource ID. VkPhysicalDevice origPhysDevice = physicalDevice; // see above in Serialise_vkEnumeratePhysicalDevices where this is encoded uint32_t physicalDeviceIndex = GetPhysicalDeviceIndexFromHandle(Unwrap(physicalDevice)); physicalDevice = m_PhysicalDevices[physicalDeviceIndex]; RDCLOG("Creating replay device from physical device %u", physicalDeviceIndex); ObjDisp(physicalDevice) ->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.props); ObjDisp(physicalDevice) ->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.memProps); ObjDisp(physicalDevice) ->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &m_PhysicalDeviceData.availFeatures); GetPhysicalDeviceDriverProperties(ObjDisp(physicalDevice), Unwrap(physicalDevice), m_PhysicalDeviceData.driverProps); m_PhysicalDeviceData.driverInfo = VkDriverInfo(m_PhysicalDeviceData.props, m_PhysicalDeviceData.driverProps, true); rdcarray queuePriorities; for(uint32_t i = 0; i < CreateInfo.queueCreateInfoCount; i++) { VkDeviceQueueGlobalPriorityCreateInfo *queuePrio = (VkDeviceQueueGlobalPriorityCreateInfo *)FindNextStruct( &CreateInfo.pQueueCreateInfos[i], VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO); if(queuePrio) queuePriorities.push_back(queuePrio); } const PhysicalDeviceData &origData = m_OriginalPhysicalDevices[physicalDeviceIndex]; m_OrigPhysicalDeviceData = origData; m_OrigPhysicalDeviceData.driverInfo = VkDriverInfo(origData.props, origData.driverProps, false); // we must make any modifications locally, so the free of pointers // in the serialised VkDeviceCreateInfo don't double-free VkDeviceCreateInfo createInfo = CreateInfo; rdcarray Extensions; for(uint32_t i = 0; i < createInfo.enabledExtensionCount; i++) { Extensions.push_back(createInfo.ppEnabledExtensionNames[i]); } StripUnwantedExtensions(Extensions); std::set supportedExtensions; { uint32_t count = 0; ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &count, NULL); VkExtensionProperties *props = new VkExtensionProperties[count]; ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &count, props); for(uint32_t e = 0; e < count; e++) supportedExtensions.insert(props[e].extensionName); SAFE_DELETE_ARRAY(props); } AddRequiredExtensions(false, Extensions, supportedExtensions); VkPhysicalDeviceProperties physProps; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &physProps); // Drop VK_KHR_driver_properties if it's not available, but add it if it is bool driverPropsSupported = (supportedExtensions.find(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) != supportedExtensions.end()); if(driverPropsSupported) { if(!Extensions.contains(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) Extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); } else { Extensions.removeOne(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); } for(size_t i = 0; i < Extensions.size(); i++) { if(supportedExtensions.find(Extensions[i]) == supportedExtensions.end()) { SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires extension '%s' which is not supported\n" "\n%s", Extensions[i].c_str(), GetPhysDeviceCompatString(false, false).c_str()); return false; } } // enable VK_EXT_debug_marker if it's available, to replay markers to the driver/any other // layers that might be listening if(supportedExtensions.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME); RDCLOG("Enabling VK_EXT_debug_marker"); } // enable VK_AMD_SHADER_INFO_EXTENSION_NAME if it's available, to fetch shader disassembly if(supportedExtensions.find(VK_AMD_SHADER_INFO_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_AMD_SHADER_INFO_EXTENSION_NAME); RDCLOG("Enabling VK_AMD_shader_info"); } // enable VK_AMD_gpa_interface if it's available, for AMD counter support if(supportedExtensions.find("VK_AMD_gpa_interface") != supportedExtensions.end()) { Extensions.push_back("VK_AMD_gpa_interface"); RDCLOG("Enabling VK_AMD_gpa_interface"); } // enable VK_AMD_shader_core_properties if it's available, for AMD counter support if(supportedExtensions.find(VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME); RDCLOG("Enabling VK_AMD_shader_core_properties"); } // enable VK_MVK_moltenvk if it's available, for detecting/controlling moltenvk. // Currently this is used opaquely (extension present or not) rather than using anything the // extension provides. if(supportedExtensions.find("VK_MVK_moltenvk") != supportedExtensions.end()) { Extensions.push_back("VK_MVK_moltenvk"); RDCLOG("Enabling VK_MVK_moltenvk"); } // enable VK_KHR_driver_properties if it's available, to match up to capture-time if(supportedExtensions.find(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_driver_properties"); } // enable VK_KHR_shader_non_semantic_info if it's available, to enable debug printf if(supportedExtensions.find(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_shader_non_semantic_info"); } bool pipeExec = false; // enable VK_KHR_pipeline_executable_properties if it's available, to fetch disassembly and // statistics if(supportedExtensions.find(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME) != supportedExtensions.end()) { pipeExec = true; Extensions.push_back(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_pipeline_executable_properties"); } bool xfb = false; // enable VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME if it's available, to fetch mesh output in // tessellation/geometry stages if(supportedExtensions.find(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME) != supportedExtensions.end()) { xfb = true; Extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); RDCLOG("Enabling VK_EXT_transform_feedback extension"); } else { RDCWARN( "VK_EXT_transform_feedback extension not available, mesh output from " "geometry/tessellation stages will not be available"); } bool scalarBlock = false; // enable VK_EXT_scalar_block_layout if it's available, to fetch mesh output in the mesh stage if(supportedExtensions.find(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME) != supportedExtensions.end()) { scalarBlock = true; Extensions.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); RDCLOG("Enabling VK_EXT_scalar_block_layout extension"); } else { const VkPhysicalDeviceMeshShaderFeaturesEXT *meshFeats = (const VkPhysicalDeviceMeshShaderFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT); if(meshFeats && meshFeats->meshShader) RDCWARN( "VK_EXT_scalar_block_layout extension not available, mesh output from " "mesh stage will not be available"); } // enable VK_KHR_shader_subgroup_uniform_control_flow if it's available, to make subgroup // debugging more reliable/spec-clean. // if we can't get it, we'll just emit the same code anyway and hope it compiles to something sensible if(RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion) >= VK_MAKE_VERSION(1, 1, 0)) { if(supportedExtensions.find(VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_KHR_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_shader_subgroup_uniform_control_flow extension"); } } bool KHRbuffer = false, EXTbuffer = false; if(supportedExtensions.find(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_buffer_device_address"); if(!Extensions.contains(VK_KHR_DEVICE_GROUP_EXTENSION_NAME)) Extensions.push_back(VK_KHR_DEVICE_GROUP_EXTENSION_NAME); KHRbuffer = true; } else if(supportedExtensions.find(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) != supportedExtensions.end()) { Extensions.push_back(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); RDCLOG("Enabling VK_EXT_buffer_device_address"); EXTbuffer = true; } else { RDCWARN( "VK_[KHR|EXT]_buffer_device_address not available, feedback from " "bindless shader access will use less reliable fallback"); } bool perfQuery = false; if(supportedExtensions.find(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME) != supportedExtensions.end()) { perfQuery = true; Extensions.push_back(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME); RDCLOG("Enabling VK_KHR_performance_query"); } VkDevice device; rdcarray queueProps; { uint32_t qCount = 0; ObjDisp(physicalDevice) ->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, NULL); queueProps.resize(qCount); ObjDisp(physicalDevice) ->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, queueProps.data()); } // to aid the search algorithm below, we apply implied transfer bit onto the queue properties. for(VkQueueFamilyProperties &q : queueProps) { if(q.queueFlags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)) q.queueFlags |= VK_QUEUE_TRANSFER_BIT; } uint32_t origQCount = origData.queueCount; const VkQueueFamilyProperties *origprops = origData.queueProps; // create queue remapping for(uint32_t origQIndex = 0; origQIndex < origQCount; origQIndex++) { m_QueueRemapping[origQIndex].resize(origprops[origQIndex].queueCount); RDCLOG("Capture describes queue family %u:", origQIndex); RDCLOG(" - %u queues available with %s", origprops[origQIndex].queueCount, ToStr(VkQueueFlagBits(origprops[origQIndex].queueFlags)).c_str()); RDCLOG(" %u timestamp bits (%u,%u,%u) granularity", origprops[origQIndex].timestampValidBits, origprops[origQIndex].minImageTransferGranularity.width, origprops[origQIndex].minImageTransferGranularity.height, origprops[origQIndex].minImageTransferGranularity.depth); // find the best queue family to map to. We try and find the closest match that is at least // good enough. We want to try and preserve families that were separate before but we need to // ensure the remapped queue family is at least as good as it was at capture time. uint32_t destFamily = 0; if(origQIndex < queueProps.size() && equivalent(origprops[origQIndex], queueProps[origQIndex])) { destFamily = origQIndex; RDCLOG(" (identity match)"); } else { // we categorise the original queue as one of four types: universal // (graphics/compute/transfer), graphics/transfer only (rare), compute-only // (compute/transfer) or transfer-only (transfer). We try first to find an exact match, then // move progressively up the priority list to find a broader and broader match. // We don't care about sparse binding - it's just treated as a requirement. enum class SearchType { Other, Universal, GraphicsTransfer, ComputeTransfer, GraphicsOrComputeTransfer, TransferOnly, } search; VkQueueFlags mask = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT); switch(origprops[origQIndex].queueFlags & mask) { case VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT: case VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT: search = SearchType::Universal; break; case VK_QUEUE_GRAPHICS_BIT: case VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT: search = SearchType::GraphicsTransfer; break; case VK_QUEUE_COMPUTE_BIT: case VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT: search = SearchType::ComputeTransfer; break; case VK_QUEUE_TRANSFER_BIT: search = SearchType::TransferOnly; break; default: search = SearchType::Other; // video queue, NV optical flow, some type of queue we don't handle break; } bool needSparse = (origprops[origQIndex].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) != 0; VkExtent3D needGranularity = origprops[origQIndex].minImageTransferGranularity; while(search != SearchType::Other) { bool found = false; for(uint32_t replayQIndex = 0; replayQIndex < queueProps.size(); replayQIndex++) { // ignore queues that couldn't satisfy the required transfer granularity if(!CheckTransferGranularity(needGranularity, queueProps[replayQIndex].minImageTransferGranularity)) continue; // ignore queues that don't have sparse binding, if we need that if(needSparse && ((queueProps[replayQIndex].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) == 0)) continue; switch(search) { case SearchType::Other: break; case SearchType::Universal: if((queueProps[replayQIndex].queueFlags & mask) == (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) { destFamily = replayQIndex; found = true; } break; case SearchType::GraphicsTransfer: if((queueProps[replayQIndex].queueFlags & mask) == (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT)) { destFamily = replayQIndex; found = true; } break; case SearchType::ComputeTransfer: if((queueProps[replayQIndex].queueFlags & mask) == (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT)) { destFamily = replayQIndex; found = true; } break; case SearchType::GraphicsOrComputeTransfer: if((queueProps[replayQIndex].queueFlags & mask) == (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT) || (queueProps[replayQIndex].queueFlags & mask) == (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT)) { destFamily = replayQIndex; found = true; } break; case SearchType::TransferOnly: if((queueProps[replayQIndex].queueFlags & mask) == VK_QUEUE_TRANSFER_BIT) { destFamily = replayQIndex; found = true; } break; } if(found) break; } if(found) break; // no such queue family found, fall back to the next type of queue to search for switch(search) { case SearchType::Other: break; case SearchType::Universal: search = SearchType::Other; break; case SearchType::GraphicsTransfer: case SearchType::ComputeTransfer: case SearchType::GraphicsOrComputeTransfer: // if we didn't find a graphics or compute (and transfer) queue, we have to look for a // universal one search = SearchType::Universal; break; case SearchType::TransferOnly: // when falling back from looking for a transfer-only queue, we consider either // graphics-only or compute-only as better candidates before universal search = SearchType::GraphicsOrComputeTransfer; break; } } } RDCLOG("Remapping to queue family %u:", destFamily); RDCLOG(" - %u queues available with %s", queueProps[destFamily].queueCount, ToStr(VkQueueFlagBits(queueProps[destFamily].queueFlags)).c_str()); RDCLOG(" %u timestamp bits (%u,%u,%u) granularity", queueProps[destFamily].timestampValidBits, queueProps[destFamily].minImageTransferGranularity.width, queueProps[destFamily].minImageTransferGranularity.height, queueProps[destFamily].minImageTransferGranularity.depth); // loop over the queues, wrapping around if necessary to provide enough queues. The idea being // an application is more likely to use early queues than later ones, so if there aren't // enough queues in the family then we should prioritise giving unique queues to the early // indices for(uint32_t q = 0; q < origprops[origQIndex].queueCount; q++) { m_QueueRemapping[origQIndex][q] = {destFamily, q % queueProps[destFamily].queueCount}; } } VkDeviceQueueCreateInfo *queueCreateInfos = (VkDeviceQueueCreateInfo *)createInfo.pQueueCreateInfos; // now apply the remapping to the requested queues for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) { VkDeviceQueueCreateInfo &queueCreate = (VkDeviceQueueCreateInfo &)queueCreateInfos[i]; uint32_t queueFamily = queueCreate.queueFamilyIndex; queueFamily = m_QueueRemapping[queueFamily][0].family; queueCreate.queueFamilyIndex = queueFamily; uint32_t queueCount = RDCMIN(queueCreate.queueCount, queueProps[queueFamily].queueCount); if(queueCount < queueCreate.queueCount) RDCWARN("Truncating queue family request from %u queues to %u queues", queueCreate.queueCount, queueCount); queueCreate.queueCount = queueCount; } // remove any duplicates that have been created rdcarray queueInfos; for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) { VkDeviceQueueCreateInfo &queue1 = (VkDeviceQueueCreateInfo &)queueCreateInfos[i]; // if we already have this one in the list, continue bool already = false; for(const VkDeviceQueueCreateInfo &queue2 : queueInfos) { if(queue1.queueFamilyIndex == queue2.queueFamilyIndex) { already = true; break; } } if(already) continue; // get the 'biggest' queue allocation from all duplicates. That way we ensure we have enough // queues in the queue family to satisfy any remap. VkDeviceQueueCreateInfo biggest = queue1; for(uint32_t j = i + 1; j < createInfo.queueCreateInfoCount; j++) { VkDeviceQueueCreateInfo &queue2 = (VkDeviceQueueCreateInfo &)queueCreateInfos[j]; if(biggest.queueFamilyIndex == queue2.queueFamilyIndex) { if(queue2.queueCount > biggest.queueCount) biggest = queue2; } } queueInfos.push_back(biggest); } createInfo.queueCreateInfoCount = (uint32_t)queueInfos.size(); createInfo.pQueueCreateInfos = queueInfos.data(); uint32_t qFamilyIdx = 0; if(!SelectGraphicsComputeQueue(queueProps, createInfo, qFamilyIdx)) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Can't add a queue with required properties for RenderDoc! Unsupported configuration"); return false; } // remove structs from extensions that we have stripped but may still be referenced here, // to ensure we don't pass structs for disabled extensions. bool private_data = false; private_data |= RemoveNextStruct(&createInfo, VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO); private_data |= RemoveNextStruct(&createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES); if(private_data) { RDCLOG("Removed VK_EXT_private_data structs from vkCreateDevice pNext chain"); } bool present_id = false; present_id |= RemoveNextStruct(&createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR); present_id |= RemoveNextStruct(&createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR); if(present_id) { RDCLOG("Removed VK_KHR_present_id/wait structs from vkCreateDevice pNext chain"); } VkPhysicalDeviceFeatures enabledFeatures = {0}; if(createInfo.pEnabledFeatures != NULL) enabledFeatures = *createInfo.pEnabledFeatures; VkPhysicalDeviceFeatures2 *enabledFeatures2 = (VkPhysicalDeviceFeatures2 *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); // VkPhysicalDeviceFeatures2 takes priority if(enabledFeatures2) enabledFeatures = enabledFeatures2->features; else if(createInfo.pEnabledFeatures) enabledFeatures = *createInfo.pEnabledFeatures; VkPhysicalDeviceFeatures availFeatures = {0}; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &availFeatures); #define CHECK_PHYS_FEATURE(feature) \ if(enabledFeatures.feature && !availFeatures.feature) \ { \ SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, \ "Capture requires physical device feature '" #feature \ "' which is not supported\n\n%s", \ GetPhysDeviceCompatString(false, false).c_str()); \ return false; \ } CHECK_PHYS_FEATURE(robustBufferAccess); CHECK_PHYS_FEATURE(fullDrawIndexUint32); CHECK_PHYS_FEATURE(imageCubeArray); CHECK_PHYS_FEATURE(independentBlend); CHECK_PHYS_FEATURE(geometryShader); CHECK_PHYS_FEATURE(tessellationShader); CHECK_PHYS_FEATURE(sampleRateShading); CHECK_PHYS_FEATURE(dualSrcBlend); CHECK_PHYS_FEATURE(logicOp); CHECK_PHYS_FEATURE(multiDrawIndirect); CHECK_PHYS_FEATURE(drawIndirectFirstInstance); CHECK_PHYS_FEATURE(depthClamp); CHECK_PHYS_FEATURE(depthBiasClamp); CHECK_PHYS_FEATURE(fillModeNonSolid); CHECK_PHYS_FEATURE(depthBounds); CHECK_PHYS_FEATURE(wideLines); CHECK_PHYS_FEATURE(largePoints); CHECK_PHYS_FEATURE(alphaToOne); CHECK_PHYS_FEATURE(multiViewport); CHECK_PHYS_FEATURE(samplerAnisotropy); CHECK_PHYS_FEATURE(textureCompressionETC2); CHECK_PHYS_FEATURE(textureCompressionASTC_LDR); CHECK_PHYS_FEATURE(textureCompressionBC); CHECK_PHYS_FEATURE(occlusionQueryPrecise); CHECK_PHYS_FEATURE(pipelineStatisticsQuery); CHECK_PHYS_FEATURE(vertexPipelineStoresAndAtomics); CHECK_PHYS_FEATURE(fragmentStoresAndAtomics); CHECK_PHYS_FEATURE(shaderTessellationAndGeometryPointSize); CHECK_PHYS_FEATURE(shaderImageGatherExtended); CHECK_PHYS_FEATURE(shaderStorageImageExtendedFormats); CHECK_PHYS_FEATURE(shaderStorageImageMultisample); CHECK_PHYS_FEATURE(shaderStorageImageReadWithoutFormat); CHECK_PHYS_FEATURE(shaderStorageImageWriteWithoutFormat); CHECK_PHYS_FEATURE(shaderUniformBufferArrayDynamicIndexing); CHECK_PHYS_FEATURE(shaderSampledImageArrayDynamicIndexing); CHECK_PHYS_FEATURE(shaderStorageBufferArrayDynamicIndexing); CHECK_PHYS_FEATURE(shaderStorageImageArrayDynamicIndexing); CHECK_PHYS_FEATURE(shaderClipDistance); CHECK_PHYS_FEATURE(shaderCullDistance); CHECK_PHYS_FEATURE(shaderFloat64); CHECK_PHYS_FEATURE(shaderInt64); CHECK_PHYS_FEATURE(shaderInt16); CHECK_PHYS_FEATURE(shaderResourceResidency); CHECK_PHYS_FEATURE(shaderResourceMinLod); CHECK_PHYS_FEATURE(sparseBinding); CHECK_PHYS_FEATURE(sparseResidencyBuffer); CHECK_PHYS_FEATURE(sparseResidencyImage2D); CHECK_PHYS_FEATURE(sparseResidencyImage3D); CHECK_PHYS_FEATURE(sparseResidency2Samples); CHECK_PHYS_FEATURE(sparseResidency4Samples); CHECK_PHYS_FEATURE(sparseResidency8Samples); CHECK_PHYS_FEATURE(sparseResidency16Samples); CHECK_PHYS_FEATURE(sparseResidencyAliased); CHECK_PHYS_FEATURE(variableMultisampleRate); CHECK_PHYS_FEATURE(inheritedQueries); #define BEGIN_PHYS_EXT_CHECK(struct, stype) \ if(struct *ext = (struct *)FindNextStruct(&createInfo, stype)) \ { \ struct avail = {stype}; \ VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; \ availBase.pNext = &avail; \ ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); \ const char *structName = #struct; #define END_PHYS_EXT_CHECK() } #define CHECK_PHYS_EXT_FEATURE(feature) \ if(ext->feature && !avail.feature) \ { \ SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, \ "Capture requires physical device feature '" #feature \ "' in struct '%s' which is not supported\n\n%s", \ structName, GetPhysDeviceCompatString(false, false).c_str()); \ return false; \ } VkPhysicalDeviceDescriptorIndexingFeatures descIndexingFeatures = {}; VkPhysicalDeviceVulkan12Features vulkan12Features = {}; VkPhysicalDeviceVulkan13Features vulkan13Features = {}; VkPhysicalDeviceSynchronization2Features sync2 = {}; if(ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2) { BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVulkan11Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES); { CHECK_PHYS_EXT_FEATURE(storageBuffer16BitAccess); CHECK_PHYS_EXT_FEATURE(uniformAndStorageBuffer16BitAccess); CHECK_PHYS_EXT_FEATURE(storagePushConstant16); CHECK_PHYS_EXT_FEATURE(storageInputOutput16); CHECK_PHYS_EXT_FEATURE(multiview); CHECK_PHYS_EXT_FEATURE(multiviewGeometryShader); CHECK_PHYS_EXT_FEATURE(multiviewTessellationShader); CHECK_PHYS_EXT_FEATURE(variablePointersStorageBuffer); CHECK_PHYS_EXT_FEATURE(variablePointers); CHECK_PHYS_EXT_FEATURE(protectedMemory); CHECK_PHYS_EXT_FEATURE(samplerYcbcrConversion); CHECK_PHYS_EXT_FEATURE(shaderDrawParameters); m_MultiView |= ext->multiview != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVulkan12Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); { vulkan12Features = *ext; CHECK_PHYS_EXT_FEATURE(samplerMirrorClampToEdge); CHECK_PHYS_EXT_FEATURE(drawIndirectCount); CHECK_PHYS_EXT_FEATURE(storageBuffer8BitAccess); CHECK_PHYS_EXT_FEATURE(uniformAndStorageBuffer8BitAccess); CHECK_PHYS_EXT_FEATURE(storagePushConstant8); CHECK_PHYS_EXT_FEATURE(shaderBufferInt64Atomics); CHECK_PHYS_EXT_FEATURE(shaderSharedInt64Atomics); CHECK_PHYS_EXT_FEATURE(shaderFloat16); CHECK_PHYS_EXT_FEATURE(shaderInt8); CHECK_PHYS_EXT_FEATURE(descriptorIndexing); CHECK_PHYS_EXT_FEATURE(shaderInputAttachmentArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformTexelBufferArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageTexelBufferArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderSampledImageArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageImageArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderInputAttachmentArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformTexelBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageTexelBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(descriptorBindingUniformBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingSampledImageUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageImageUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingUniformTexelBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageTexelBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingUpdateUnusedWhilePending); CHECK_PHYS_EXT_FEATURE(descriptorBindingPartiallyBound); CHECK_PHYS_EXT_FEATURE(descriptorBindingVariableDescriptorCount); CHECK_PHYS_EXT_FEATURE(runtimeDescriptorArray); CHECK_PHYS_EXT_FEATURE(samplerFilterMinmax); CHECK_PHYS_EXT_FEATURE(scalarBlockLayout); CHECK_PHYS_EXT_FEATURE(imagelessFramebuffer); CHECK_PHYS_EXT_FEATURE(uniformBufferStandardLayout); CHECK_PHYS_EXT_FEATURE(shaderSubgroupExtendedTypes); CHECK_PHYS_EXT_FEATURE(separateDepthStencilLayouts); CHECK_PHYS_EXT_FEATURE(hostQueryReset); CHECK_PHYS_EXT_FEATURE(timelineSemaphore); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddress); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressCaptureReplay); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressMultiDevice); CHECK_PHYS_EXT_FEATURE(vulkanMemoryModel); CHECK_PHYS_EXT_FEATURE(vulkanMemoryModelDeviceScope); CHECK_PHYS_EXT_FEATURE(vulkanMemoryModelAvailabilityVisibilityChains); CHECK_PHYS_EXT_FEATURE(shaderOutputViewportIndex); CHECK_PHYS_EXT_FEATURE(shaderOutputLayer); CHECK_PHYS_EXT_FEATURE(subgroupBroadcastDynamicId); m_SeparateDepthStencil |= (ext->separateDepthStencilLayouts != VK_FALSE); if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay\n" "\n%s", GetPhysDeviceCompatString(false, false).c_str()); return false; } } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVulkan13Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES); { vulkan13Features = *ext; CHECK_PHYS_EXT_FEATURE(robustImageAccess); CHECK_PHYS_EXT_FEATURE(inlineUniformBlock); CHECK_PHYS_EXT_FEATURE(descriptorBindingInlineUniformBlockUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(pipelineCreationCacheControl); CHECK_PHYS_EXT_FEATURE(privateData); CHECK_PHYS_EXT_FEATURE(shaderDemoteToHelperInvocation); CHECK_PHYS_EXT_FEATURE(shaderTerminateInvocation); CHECK_PHYS_EXT_FEATURE(subgroupSizeControl); CHECK_PHYS_EXT_FEATURE(computeFullSubgroups); CHECK_PHYS_EXT_FEATURE(synchronization2); CHECK_PHYS_EXT_FEATURE(textureCompressionASTC_HDR); CHECK_PHYS_EXT_FEATURE(shaderZeroInitializeWorkgroupMemory); CHECK_PHYS_EXT_FEATURE(dynamicRendering); CHECK_PHYS_EXT_FEATURE(shaderIntegerDotProduct); CHECK_PHYS_EXT_FEATURE(maintenance4); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDevice8BitStorageFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES); { CHECK_PHYS_EXT_FEATURE(storageBuffer8BitAccess); CHECK_PHYS_EXT_FEATURE(uniformAndStorageBuffer8BitAccess); CHECK_PHYS_EXT_FEATURE(storagePushConstant8); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDevice16BitStorageFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES); { CHECK_PHYS_EXT_FEATURE(storageBuffer16BitAccess); CHECK_PHYS_EXT_FEATURE(uniformAndStorageBuffer16BitAccess); CHECK_PHYS_EXT_FEATURE(storagePushConstant16); CHECK_PHYS_EXT_FEATURE(storageInputOutput16); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceASTCDecodeFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(decodeModeSharedExponent); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(attachmentFeedbackLoopLayout); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(fragmentShaderBarycentric); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMultiviewFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES); { CHECK_PHYS_EXT_FEATURE(multiview); CHECK_PHYS_EXT_FEATURE(multiviewGeometryShader); CHECK_PHYS_EXT_FEATURE(multiviewTessellationShader); m_MultiView |= ext->multiview != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceFragmentDensityMapFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(fragmentDensityMap); CHECK_PHYS_EXT_FEATURE(fragmentDensityMapDynamic); CHECK_PHYS_EXT_FEATURE(fragmentDensityMapNonSubsampledImages); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceFragmentDensityMap2FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(fragmentDensityMapDeferred); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceFragmentDensityMapOffsetFeaturesQCOM, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_OFFSET_FEATURES_QCOM); { CHECK_PHYS_EXT_FEATURE(fragmentDensityMapOffset); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceProtectedMemoryFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES); { CHECK_PHYS_EXT_FEATURE(protectedMemory); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceSamplerYcbcrConversionFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES); { CHECK_PHYS_EXT_FEATURE(samplerYcbcrConversion); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderAtomicInt64Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderBufferInt64Atomics); CHECK_PHYS_EXT_FEATURE(shaderSharedInt64Atomics); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderDrawParametersFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderDrawParameters); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderImageFootprintFeaturesNV, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV); { CHECK_PHYS_EXT_FEATURE(imageFootprint); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceTransformFeedbackFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(transformFeedback); CHECK_PHYS_EXT_FEATURE(geometryStreams); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVariablePointersFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES); { CHECK_PHYS_EXT_FEATURE(variablePointersStorageBuffer); CHECK_PHYS_EXT_FEATURE(variablePointers); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVertexAttributeDivisorFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES); { CHECK_PHYS_EXT_FEATURE(vertexAttributeInstanceRateDivisor); CHECK_PHYS_EXT_FEATURE(vertexAttributeInstanceRateZeroDivisor); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVulkanMemoryModelFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES); { CHECK_PHYS_EXT_FEATURE(vulkanMemoryModel); CHECK_PHYS_EXT_FEATURE(vulkanMemoryModelDeviceScope); CHECK_PHYS_EXT_FEATURE(vulkanMemoryModelAvailabilityVisibilityChains); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceConditionalRenderingFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(conditionalRendering); CHECK_PHYS_EXT_FEATURE(inheritedConditionalRendering); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceHostQueryResetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES); { CHECK_PHYS_EXT_FEATURE(hostQueryReset); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDepthClipControlFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(depthClipControl); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVE_TOPOLOGY_LIST_RESTART_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(primitiveTopologyListRestart); CHECK_PHYS_EXT_FEATURE(primitiveTopologyPatchListRestart); m_ListRestart = ext->primitiveTopologyListRestart != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDevicePrimitivesGeneratedQueryFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIMITIVES_GENERATED_QUERY_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(primitivesGeneratedQuery); CHECK_PHYS_EXT_FEATURE(primitivesGeneratedQueryWithRasterizerDiscard); CHECK_PHYS_EXT_FEATURE(primitivesGeneratedQueryWithNonZeroStreams); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(multisampledRenderToSingleSampled); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(rasterizationOrderColorAttachmentAccess); CHECK_PHYS_EXT_FEATURE(rasterizationOrderDepthAttachmentAccess); CHECK_PHYS_EXT_FEATURE(rasterizationOrderStencilAttachmentAccess); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDepthClipEnableFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(depthClipEnable); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceYcbcrImageArraysFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(ycbcrImageArrays); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceBufferDeviceAddressFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(bufferDeviceAddress); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressCaptureReplay); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressMultiDevice); if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay\n" "\n%s", GetPhysDeviceCompatString(false, false).c_str()); return false; } } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceBufferDeviceAddressFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES); { CHECK_PHYS_EXT_FEATURE(bufferDeviceAddress); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressCaptureReplay); CHECK_PHYS_EXT_FEATURE(bufferDeviceAddressMultiDevice); if(ext->bufferDeviceAddress && !avail.bufferDeviceAddressCaptureReplay) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires bufferDeviceAddress support, which is available, but " "bufferDeviceAddressCaptureReplay support is not available which is required to " "replay\n" "\n%s", GetPhysDeviceCompatString(false, false).c_str()); return false; } } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDescriptorIndexingFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES); { descIndexingFeatures = *ext; CHECK_PHYS_EXT_FEATURE(shaderInputAttachmentArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformTexelBufferArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageTexelBufferArrayDynamicIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderSampledImageArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageImageArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderInputAttachmentArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderUniformTexelBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(shaderStorageTexelBufferArrayNonUniformIndexing); CHECK_PHYS_EXT_FEATURE(descriptorBindingUniformBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingSampledImageUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageImageUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingUniformTexelBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingStorageTexelBufferUpdateAfterBind); CHECK_PHYS_EXT_FEATURE(descriptorBindingUpdateUnusedWhilePending); CHECK_PHYS_EXT_FEATURE(descriptorBindingPartiallyBound); CHECK_PHYS_EXT_FEATURE(descriptorBindingVariableDescriptorCount); CHECK_PHYS_EXT_FEATURE(runtimeDescriptorArray); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceUniformBufferStandardLayoutFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES); { CHECK_PHYS_EXT_FEATURE(uniformBufferStandardLayout); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(fragmentShaderSampleInterlock); CHECK_PHYS_EXT_FEATURE(fragmentShaderPixelInterlock); CHECK_PHYS_EXT_FEATURE(fragmentShaderShadingRateInterlock); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderDemoteToHelperInvocation); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(texelBufferAlignment); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceIndexTypeUint8Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES); { CHECK_PHYS_EXT_FEATURE(indexTypeUint8); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceImagelessFramebufferFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES); { CHECK_PHYS_EXT_FEATURE(imagelessFramebuffer); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceSubgroupSizeControlFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES); { CHECK_PHYS_EXT_FEATURE(subgroupSizeControl); CHECK_PHYS_EXT_FEATURE(computeFullSubgroups); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(pipelineExecutableInfo); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceLineRasterizationFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES); { CHECK_PHYS_EXT_FEATURE(rectangularLines); CHECK_PHYS_EXT_FEATURE(bresenhamLines); CHECK_PHYS_EXT_FEATURE(smoothLines); CHECK_PHYS_EXT_FEATURE(stippledRectangularLines); CHECK_PHYS_EXT_FEATURE(stippledBresenhamLines); CHECK_PHYS_EXT_FEATURE(stippledSmoothLines); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderSubgroupExtendedTypes); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderSubgroupRotateFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_ROTATE_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderSubgroupRotate); CHECK_PHYS_EXT_FEATURE(shaderSubgroupRotateClustered); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceCoherentMemoryFeaturesAMD, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD); { CHECK_PHYS_EXT_FEATURE(deviceCoherentMemory); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderClockFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(shaderSubgroupClock); CHECK_PHYS_EXT_FEATURE(shaderDeviceClock); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMemoryPriorityFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(memoryPriority); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceScalarBlockLayoutFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES); { CHECK_PHYS_EXT_FEATURE(scalarBlockLayout); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderFloat16Int8Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderFloat16); CHECK_PHYS_EXT_FEATURE(shaderInt8); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderFloatControls2Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT_CONTROLS_2_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderFloatControls2); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceTimelineSemaphoreFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES); { CHECK_PHYS_EXT_FEATURE(timelineSemaphore); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES); { CHECK_PHYS_EXT_FEATURE(separateDepthStencilLayouts); m_SeparateDepthStencil |= (ext->separateDepthStencilLayouts != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDevicePerformanceQueryFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(performanceCounterQueryPools); CHECK_PHYS_EXT_FEATURE(performanceCounterMultipleQueryPools); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceInlineUniformBlockFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES); { CHECK_PHYS_EXT_FEATURE(inlineUniformBlock); CHECK_PHYS_EXT_FEATURE(descriptorBindingInlineUniformBlockUpdateAfterBind); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceCustomBorderColorFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(customBorderColors); CHECK_PHYS_EXT_FEATURE(customBorderColorWithoutFormat); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRobustness2FeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(robustBufferAccess2); CHECK_PHYS_EXT_FEATURE(robustImageAccess2); CHECK_PHYS_EXT_FEATURE(nullDescriptor); m_NULLDescriptorsAllowed |= (ext->nullDescriptor != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDevicePipelineCreationCacheControlFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_CREATION_CACHE_CONTROL_FEATURES); { CHECK_PHYS_EXT_FEATURE(pipelineCreationCacheControl); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceComputeShaderDerivativesFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(computeDerivativeGroupQuads); CHECK_PHYS_EXT_FEATURE(computeDerivativeGroupLinear); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceExtendedDynamicStateFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(extendedDynamicState); m_ExtendedDynState = (ext->extendedDynamicState != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderTerminateInvocationFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_TERMINATE_INVOCATION_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderTerminateInvocation); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceImageRobustnessFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES); { CHECK_PHYS_EXT_FEATURE(robustImageAccess); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderAtomicFloatFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(shaderBufferFloat32Atomics); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat32AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat64Atomics); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat64AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat32Atomics); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat32AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat64Atomics); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat64AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderImageFloat32Atomics); CHECK_PHYS_EXT_FEATURE(shaderImageFloat32AtomicAdd); CHECK_PHYS_EXT_FEATURE(sparseImageFloat32Atomics); CHECK_PHYS_EXT_FEATURE(sparseImageFloat32AtomicAdd); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderImageAtomicInt64FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_ATOMIC_INT64_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(shaderImageInt64Atomics); CHECK_PHYS_EXT_FEATURE(sparseImageInt64Atomics); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderZeroInitializeWorkgroupMemory); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(workgroupMemoryExplicitLayout); CHECK_PHYS_EXT_FEATURE(workgroupMemoryExplicitLayoutScalarBlockLayout); CHECK_PHYS_EXT_FEATURE(workgroupMemoryExplicitLayout8BitAccess); CHECK_PHYS_EXT_FEATURE(workgroupMemoryExplicitLayout16BitAccess); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceSynchronization2Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES); { sync2 = *ext; CHECK_PHYS_EXT_FEATURE(synchronization2); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMaintenance4Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES); { CHECK_PHYS_EXT_FEATURE(maintenance4); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderIntegerDotProductFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderIntegerDotProduct); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceShaderSubgroupUniformControlFlowFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_UNIFORM_CONTROL_FLOW_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(shaderSubgroupUniformControlFlow); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderAtomicFloat2FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(shaderBufferFloat16Atomics); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat16AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat16AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat32AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderBufferFloat64AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat16Atomics); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat16AtomicAdd); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat16AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat32AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderSharedFloat64AtomicMinMax); CHECK_PHYS_EXT_FEATURE(shaderImageFloat32AtomicMinMax); CHECK_PHYS_EXT_FEATURE(sparseImageFloat32AtomicMinMax); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_2_PLANE_444_FORMATS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(ycbcr2plane444Formats); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(formatRgba10x6WithoutYCbCrSampler); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceGlobalPriorityQueryFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GLOBAL_PRIORITY_QUERY_FEATURES); { CHECK_PHYS_EXT_FEATURE(globalPriorityQuery); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceColorWriteEnableFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(colorWriteEnable); m_DynColorWrite = (ext->colorWriteEnable != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceExtendedDynamicState2FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(extendedDynamicState2); CHECK_PHYS_EXT_FEATURE(extendedDynamicState2LogicOp); CHECK_PHYS_EXT_FEATURE(extendedDynamicState2PatchControlPoints); m_ExtendedDynState2 = (ext->extendedDynamicState2 != VK_FALSE); m_ExtendedDynState2Logic = (ext->extendedDynamicState2LogicOp != VK_FALSE); m_ExtendedDynState2CPs = (ext->extendedDynamicState2PatchControlPoints != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(vertexInputDynamicState); m_DynVertexInput = (ext->vertexInputDynamicState != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(graphicsPipelineLibrary); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDynamicRenderingFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES); { CHECK_PHYS_EXT_FEATURE(dynamicRendering); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDynamicRenderingLocalReadFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES); { CHECK_PHYS_EXT_FEATURE(dynamicRenderingLocalRead); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDevice4444FormatsFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(formatA4R4G4B4); CHECK_PHYS_EXT_FEATURE(formatA4B4G4R4); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceTextureCompressionASTCHDRFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES); { CHECK_PHYS_EXT_FEATURE(textureCompressionASTC_HDR); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceFragmentShadingRateFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(pipelineFragmentShadingRate); CHECK_PHYS_EXT_FEATURE(primitiveFragmentShadingRate); CHECK_PHYS_EXT_FEATURE(attachmentFragmentShadingRate); m_FragmentShadingRate = (ext->pipelineFragmentShadingRate != VK_FALSE) || (ext->primitiveFragmentShadingRate != VK_FALSE) || (ext->attachmentFragmentShadingRate != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(mutableDescriptorType); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PAGEABLE_DEVICE_LOCAL_MEMORY_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(pageableDeviceLocalMemory); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(swapchainMaintenance1); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceBorderColorSwizzleFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BORDER_COLOR_SWIZZLE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(borderColorSwizzle); CHECK_PHYS_EXT_FEATURE(borderColorSwizzleFromImage); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceNonSeamlessCubeMapFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(nonSeamlessCubeMap); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceDepthClampZeroOneFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLAMP_ZERO_ONE_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(depthClampZeroOne); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceImageViewMinLodFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_MIN_LOD_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(minLod); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceProvokingVertexFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(provokingVertexLast); CHECK_PHYS_EXT_FEATURE(transformFeedbackPreservesProvokingVertex); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(attachmentFeedbackLoopDynamicState); m_DynAttachmentLoop = ext->attachmentFeedbackLoopDynamicState != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceImage2DViewOf3DFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_2D_VIEW_OF_3D_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(image2DViewOf3D); CHECK_PHYS_EXT_FEATURE(sampler2DViewOf3D); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceExtendedDynamicState3FeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(extendedDynamicState3TessellationDomainOrigin); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3DepthClampEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3PolygonMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3RasterizationSamples); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3SampleMask); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3AlphaToCoverageEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3AlphaToOneEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3LogicOpEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ColorBlendEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ColorBlendEquation); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ColorWriteMask); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3RasterizationStream); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ConservativeRasterizationMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ExtraPrimitiveOverestimationSize); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3DepthClipEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3SampleLocationsEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ColorBlendAdvanced); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ProvokingVertexMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3LineRasterizationMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3LineStippleEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3DepthClipNegativeOneToOne); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ViewportWScalingEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ViewportSwizzle); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageToColorEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageToColorLocation); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageModulationMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageModulationTableEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageModulationTable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3CoverageReductionMode); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3RepresentativeFragmentTestEnable); CHECK_PHYS_EXT_FEATURE(extendedDynamicState3ShadingRateImageEnable); m_ExtendedDynState3TesselDomain = (ext->extendedDynamicState3TessellationDomainOrigin != VK_FALSE); m_ExtendedDynState3DepthClampEnable = (ext->extendedDynamicState3DepthClampEnable != VK_FALSE); m_ExtendedDynState3PolyMode = (ext->extendedDynamicState3PolygonMode != VK_FALSE); m_ExtendedDynState3RastSamples = (ext->extendedDynamicState3RasterizationSamples != VK_FALSE); m_ExtendedDynState3SampleMask = (ext->extendedDynamicState3SampleMask != VK_FALSE); m_ExtendedDynState3AlphaToCover = (ext->extendedDynamicState3AlphaToCoverageEnable != VK_FALSE); m_ExtendedDynState3AlphaToOne = (ext->extendedDynamicState3AlphaToOneEnable != VK_FALSE); m_ExtendedDynState3LogicEnable = (ext->extendedDynamicState3LogicOpEnable != VK_FALSE); m_ExtendedDynState3CBEnable = (ext->extendedDynamicState3ColorBlendEnable != VK_FALSE); m_ExtendedDynState3CBEquation = (ext->extendedDynamicState3ColorBlendEquation != VK_FALSE); m_ExtendedDynState3WriteMask = (ext->extendedDynamicState3ColorWriteMask != VK_FALSE); m_ExtendedDynState3RastStream = (ext->extendedDynamicState3RasterizationStream != VK_FALSE); m_ExtendedDynState3ConservRast = (ext->extendedDynamicState3ConservativeRasterizationMode != VK_FALSE); m_ExtendedDynState3PrimOverest = (ext->extendedDynamicState3ExtraPrimitiveOverestimationSize != VK_FALSE); m_ExtendedDynState3DepthClip = (ext->extendedDynamicState3DepthClipEnable != VK_FALSE); m_ExtendedDynState3SampleLoc = (ext->extendedDynamicState3SampleLocationsEnable != VK_FALSE); m_ExtendedDynState3ProvokingVert = (ext->extendedDynamicState3ProvokingVertexMode != VK_FALSE); m_ExtendedDynState3LineRast = (ext->extendedDynamicState3LineRasterizationMode != VK_FALSE); m_ExtendedDynState3LineStipple = (ext->extendedDynamicState3LineStippleEnable != VK_FALSE); m_ExtendedDynState3DepthClipNeg = (ext->extendedDynamicState3DepthClipNegativeOneToOne != VK_FALSE); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMeshShaderFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(taskShader); CHECK_PHYS_EXT_FEATURE(meshShader); CHECK_PHYS_EXT_FEATURE(multiviewMeshShader); CHECK_PHYS_EXT_FEATURE(primitiveFragmentShadingRateMeshShader); CHECK_PHYS_EXT_FEATURE(meshShaderQueries); m_MeshShaders = ext->meshShader != VK_FALSE; m_TaskShaders = ext->taskShader != VK_FALSE; m_MeshQueries = avail.meshShaderQueries != VK_FALSE; if(avail.meshShaderQueries) ext->meshShaderQueries = true; else RDCWARN("meshShaderQueries = false, mesh shader performance counters unavailable"); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceAccelerationStructureFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(accelerationStructure) CHECK_PHYS_EXT_FEATURE(accelerationStructureCaptureReplay) CHECK_PHYS_EXT_FEATURE(accelerationStructureIndirectBuild) CHECK_PHYS_EXT_FEATURE(descriptorBindingAccelerationStructureUpdateAfterBind) if(ext->accelerationStructure && !avail.accelerationStructureCaptureReplay) { SET_ERROR_RESULT( m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires accelerationStructure support, which is available, but " "accelerationStructureCaptureReplay support is not available which is required to " "replay\n" "\n%s", GetPhysDeviceCompatString(false, false).c_str()); return false; } m_AccelerationStructures = ext->accelerationStructure != VK_FALSE; if(m_AccelerationStructures) { RDCLOG( "Ray tracing acceleration structures requested, allocating all device memory with " "VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT"); ext->accelerationStructureCaptureReplay = VK_TRUE; } } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceNestedCommandBufferFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NESTED_COMMAND_BUFFER_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(nestedCommandBuffer); CHECK_PHYS_EXT_FEATURE(nestedCommandBufferRendering); CHECK_PHYS_EXT_FEATURE(nestedCommandBufferSimultaneousUse); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderObjectFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(shaderObject); m_ShaderObject = ext->shaderObject != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceShaderRelaxedExtendedInstructionFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_RELAXED_EXTENDED_INSTRUCTION_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(shaderRelaxedExtendedInstruction); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderExpectAssumeFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_EXPECT_ASSUME_FEATURES); { CHECK_PHYS_EXT_FEATURE(shaderExpectAssume); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceShaderMaximalReconvergenceFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_MAXIMAL_RECONVERGENCE_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(shaderMaximalReconvergence); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceShaderQuadControlFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_QUAD_CONTROL_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(shaderQuadControl); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRayTracingPipelineFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(rayTracingPipeline); CHECK_PHYS_EXT_FEATURE(rayTracingPipelineShaderGroupHandleCaptureReplay); CHECK_PHYS_EXT_FEATURE(rayTracingPipelineShaderGroupHandleCaptureReplayMixed); CHECK_PHYS_EXT_FEATURE(rayTracingPipelineTraceRaysIndirect); CHECK_PHYS_EXT_FEATURE(rayTraversalPrimitiveCulling); VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR, }; VkPhysicalDeviceProperties2 availPropsBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; availPropsBase.pNext = &rayProps; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &availPropsBase); if(ext->rayTracingPipeline && !avail.rayTracingPipelineShaderGroupHandleCaptureReplay) { SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, "Capture requires rayTracingPipeline support, which is available, but " "rayTracingPipelineShaderGroupHandleCaptureReplay support is not " "available which is required to replay\n" "\n%s", GetPhysDeviceCompatString(false, false).c_str()); return false; } if(ext->rayTracingPipeline) ext->rayTracingPipelineShaderGroupHandleCaptureReplay = VK_TRUE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRayTracingMaintenance1FeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_MAINTENANCE_1_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(rayTracingMaintenance1); CHECK_PHYS_EXT_FEATURE(rayTracingPipelineTraceRaysIndirect2); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR); { CHECK_PHYS_EXT_FEATURE(rayTracingPositionFetch); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMaintenance5Features, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES); { CHECK_PHYS_EXT_FEATURE(maintenance5); m_Maintenance5 = ext->maintenance5 != VK_FALSE; } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceImageCompressionControlFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(imageCompressionControl); } END_PHYS_EXT_CHECK(); BEGIN_PHYS_EXT_CHECK( VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT); { CHECK_PHYS_EXT_FEATURE(imageCompressionControlSwapchain); } END_PHYS_EXT_CHECK(); } if(availFeatures.depthClamp) enabledFeatures.depthClamp = true; else RDCWARN( "depthClamp = false, overlays like highlight drawcall won't show depth-clipped pixels."); // we have a fallback for this case, so no warning if(availFeatures.fillModeNonSolid) enabledFeatures.fillModeNonSolid = true; // don't warn if this isn't available, we just won't report the counters if(availFeatures.pipelineStatisticsQuery) enabledFeatures.pipelineStatisticsQuery = true; if(availFeatures.geometryShader) enabledFeatures.geometryShader = true; else RDCWARN( "geometryShader = false, pixel history primitive ID and triangle size overlay will not " "be available, and local rendering on this device will not support lit mesh views."); // enable these features for simplicity, since we use them when available in the shader // debugging to simplify samples. If minlod isn't used then we omit it, and that's fine because // the application's shaders wouldn't have been using minlod. We use gatherExtended for gather // offsets, which means if it's not supported then we can't debug constant offsets properly // (because we pass offsets as uniforms), but that's not a big deal. if(availFeatures.shaderImageGatherExtended) enabledFeatures.shaderImageGatherExtended = true; if(availFeatures.shaderResourceMinLod) enabledFeatures.shaderResourceMinLod = true; if(availFeatures.imageCubeArray) enabledFeatures.imageCubeArray = true; bool descIndexingAllowsRBA = true; if(vulkan12Features.descriptorBindingUniformBufferUpdateAfterBind || vulkan12Features.descriptorBindingStorageBufferUpdateAfterBind || vulkan12Features.descriptorBindingUniformTexelBufferUpdateAfterBind || vulkan12Features.descriptorBindingStorageTexelBufferUpdateAfterBind) { // if any update after bind feature is enabled, check robustBufferAccessUpdateAfterBind VkPhysicalDeviceVulkan12Properties vulkan12Props = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES, }; VkPhysicalDeviceProperties2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; availBase.pNext = &vulkan12Props; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &availBase); descIndexingAllowsRBA = vulkan12Props.robustBufferAccessUpdateAfterBind != VK_FALSE; } if(descIndexingFeatures.descriptorBindingUniformBufferUpdateAfterBind || descIndexingFeatures.descriptorBindingStorageBufferUpdateAfterBind || descIndexingFeatures.descriptorBindingUniformTexelBufferUpdateAfterBind || descIndexingFeatures.descriptorBindingStorageTexelBufferUpdateAfterBind) { // if any update after bind feature is enabled, check robustBufferAccessUpdateAfterBind VkPhysicalDeviceDescriptorIndexingProperties descIndexingProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, }; VkPhysicalDeviceProperties2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; availBase.pNext = &descIndexingProps; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &availBase); descIndexingAllowsRBA = descIndexingProps.robustBufferAccessUpdateAfterBind != VK_FALSE; } if(availFeatures.robustBufferAccess && !descIndexingAllowsRBA) { // if the feature is available but we can't use it, warn RDCWARN( "robustBufferAccess is available, but cannot be enabled due to " "robustBufferAccessUpdateAfterBind not being avilable and some UpdateAfterBind features " "being enabled. " "out of bounds access due to bugs in application or RenderDoc may cause crashes"); } else { // either the feature is available, and we enable it, or it's not available at all. if(availFeatures.robustBufferAccess) enabledFeatures.robustBufferAccess = true; else RDCWARN( "robustBufferAccess = false, out of bounds access due to bugs in application or " "RenderDoc may cause crashes"); } if(availFeatures.shaderInt64) enabledFeatures.shaderInt64 = true; else RDCWARN("shaderInt64 = false, feedback from shaders will use less reliable fallback."); if(availFeatures.shaderStorageImageWriteWithoutFormat) enabledFeatures.shaderStorageImageWriteWithoutFormat = true; else RDCWARN( "shaderStorageImageWriteWithoutFormat = false, save/load from 2DMS textures will not be " "possible"); if(availFeatures.shaderStorageImageMultisample) enabledFeatures.shaderStorageImageMultisample = true; else RDCWARN( "shaderStorageImageMultisample = false, accurately replaying 2DMS textures will not be " "possible"); if(availFeatures.occlusionQueryPrecise) enabledFeatures.occlusionQueryPrecise = true; else RDCWARN("occlusionQueryPrecise = false, samples passed counter will not be available"); if(availFeatures.fragmentStoresAndAtomics) enabledFeatures.fragmentStoresAndAtomics = true; else RDCWARN( "fragmentStoresAndAtomics = false, quad overdraw overlay will not be available and " "feedback from shaders will not be fetched for fragment stage"); if(availFeatures.vertexPipelineStoresAndAtomics) enabledFeatures.vertexPipelineStoresAndAtomics = true; else RDCWARN( "vertexPipelineStoresAndAtomics = false, feedback from shaders will not be fetched for " "vertex stages"); if(availFeatures.sampleRateShading) enabledFeatures.sampleRateShading = true; else RDCWARN( "sampleRateShading = false, save/load from depth 2DMS textures will not be " "possible"); // patch the enabled features if(enabledFeatures2) enabledFeatures2->features = enabledFeatures; else createInfo.pEnabledFeatures = &enabledFeatures; uint32_t numExts = 0; VkResult vkr = ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &numExts, NULL); CHECK_VKR(this, vkr); VkExtensionProperties *exts = new VkExtensionProperties[numExts]; vkr = ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &numExts, exts); CHECK_VKR(this, vkr); for(uint32_t i = 0; i < numExts; i++) RDCLOG("Dev Ext %u: %s (%u)", i, exts[i].extensionName, exts[i].specVersion); SAFE_DELETE_ARRAY(exts); VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR pipeExecFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR, }; if(pipeExec) { VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &pipeExecFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(pipeExecFeatures.pipelineExecutableInfo) { // see if there's an existing struct VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR *existing = (VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR); if(existing) { // if so, make sure the feature is enabled existing->pipelineExecutableInfo = VK_TRUE; } else { // otherwise, add our own, and push it onto the pNext array pipeExecFeatures.pipelineExecutableInfo = VK_TRUE; pipeExecFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &pipeExecFeatures; } } else { RDCWARN( "VK_KHR_pipeline_executable_properties is available, but the physical device feature " "is not. Disabling"); Extensions.removeOne(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); } } VkPhysicalDeviceTransformFeedbackFeaturesEXT xfbFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT, }; // if we're enabling XFB, make sure we can enable the physical device feature if(xfb) { VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &xfbFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(xfbFeatures.transformFeedback) { // see if there's an existing struct VkPhysicalDeviceTransformFeedbackFeaturesEXT *existing = (VkPhysicalDeviceTransformFeedbackFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT); if(existing) { // if so, make sure the feature is enabled existing->transformFeedback = VK_TRUE; } else { // otherwise, add our own, and push it onto the pNext array xfbFeatures.transformFeedback = VK_TRUE; xfbFeatures.geometryStreams = VK_FALSE; xfbFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &xfbFeatures; } } else { RDCWARN( "VK_EXT_transform_feedback is available, but the physical device feature is not. " "Disabling"); Extensions.removeOne(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); } } VkPhysicalDevicePerformanceQueryFeaturesKHR perfFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR, }; if(perfQuery) { VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; m_PhysicalDeviceData.performanceQueryFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR; availBase.pNext = &perfFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); m_PhysicalDeviceData.performanceQueryFeatures = perfFeatures; if(perfFeatures.performanceCounterQueryPools) { VkPhysicalDevicePerformanceQueryFeaturesKHR *existing = (VkPhysicalDevicePerformanceQueryFeaturesKHR *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PERFORMANCE_QUERY_FEATURES_KHR); if(existing) { existing->performanceCounterQueryPools = VK_TRUE; } else { perfFeatures.performanceCounterQueryPools = VK_TRUE; perfFeatures.performanceCounterMultipleQueryPools = VK_FALSE; perfFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &perfFeatures; } } else { Extensions.removeOne(VK_KHR_PERFORMANCE_QUERY_EXTENSION_NAME); } } VkPhysicalDeviceScalarBlockLayoutFeatures scalarBlockFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, }; if(RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion) >= VK_MAKE_VERSION(1, 2, 0)) { VkPhysicalDeviceVulkan12Features avail12Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, }; VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &avail12Features; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(avail12Features.scalarBlockLayout) { VkPhysicalDeviceVulkan12Features *existing = (VkPhysicalDeviceVulkan12Features *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); if(existing) { existing->scalarBlockLayout = VK_TRUE; } else { VkPhysicalDeviceScalarBlockLayoutFeatures *existingEXT = (VkPhysicalDeviceScalarBlockLayoutFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES); if(existingEXT) { existingEXT->scalarBlockLayout = VK_TRUE; } else { // don't add a new VkPhysicalDeviceVulkan12Features to the pNext chain because if we do // we have to remove any components etc. Instead just add the individual // VkPhysicalDeviceScalarBlockLayoutFeaturesEXT scalarBlockFeatures.scalarBlockLayout = VK_TRUE; scalarBlockFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &scalarBlockFeatures; } } } } else if(scalarBlock) { VkPhysicalDeviceScalarBlockLayoutFeatures scalarAvail = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES, }; VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &scalarAvail; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(scalarAvail.scalarBlockLayout) { // see if there's an existing struct VkPhysicalDeviceScalarBlockLayoutFeatures *existing = (VkPhysicalDeviceScalarBlockLayoutFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES); if(existing) { existing->scalarBlockLayout = VK_TRUE; } else { // otherwise, add our own, and push it onto the pNext array scalarBlockFeatures.scalarBlockLayout = VK_TRUE; scalarBlockFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &scalarBlockFeatures; } } else { RDCWARN( "VK_EXT_scalar_block_layout is available, but the physical device feature " "is not. Disabling"); Extensions.removeOne(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME); } } VkPhysicalDeviceBufferDeviceAddressFeaturesEXT bufAddrEXTFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, }; VkPhysicalDeviceBufferDeviceAddressFeatures bufAddrFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, }; if(RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion) >= VK_MAKE_VERSION(1, 3, 0)) { // VK_EXT_extended_dynamic_state and VK_EXT_extended_dynamic_state2 were unconditionally // promoted and considered implicitly enabled in vulkan 1.3 m_ExtendedDynState = true; m_ExtendedDynState2 = true; // logic and patch CPs were not } if(RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion) >= VK_MAKE_VERSION(1, 2, 0)) { VkPhysicalDeviceVulkan12Features avail12Features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, }; VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &avail12Features; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(avail12Features.bufferDeviceAddress) { VkPhysicalDeviceVulkan12Features *existing = (VkPhysicalDeviceVulkan12Features *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); if(existing) { if(existing->bufferDeviceAddress) existing->bufferDeviceAddressCaptureReplay = VK_TRUE; existing->bufferDeviceAddress = VK_TRUE; } else { VkPhysicalDeviceBufferDeviceAddressFeatures *existingKHR = (VkPhysicalDeviceBufferDeviceAddressFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES); VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *existingEXT = (VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT); if(existingKHR) { if(existingKHR->bufferDeviceAddress) existingKHR->bufferDeviceAddressCaptureReplay = VK_TRUE; existingKHR->bufferDeviceAddress = VK_TRUE; } else if(existingEXT) { if(existingEXT->bufferDeviceAddress) existingEXT->bufferDeviceAddressCaptureReplay = VK_TRUE; existingEXT->bufferDeviceAddress = VK_TRUE; } else { // don't add a new VkPhysicalDeviceVulkan12Features to the pNext chain because if we do // we have to remove any components etc. Instead just add the individual // VkPhysicalDeviceBufferDeviceAddressFeaturesKHR bufAddrFeatures.bufferDeviceAddress = VK_TRUE; bufAddrFeatures.bufferDeviceAddressMultiDevice = VK_FALSE; bufAddrFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &bufAddrFeatures; } } } } else if(KHRbuffer) { VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &bufAddrFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(bufAddrFeatures.bufferDeviceAddress) { // see if there's an existing struct VkPhysicalDeviceBufferDeviceAddressFeatures *existing = (VkPhysicalDeviceBufferDeviceAddressFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES); if(existing) { if(existing->bufferDeviceAddress) existing->bufferDeviceAddressCaptureReplay = VK_TRUE; // if so, make sure the feature is enabled existing->bufferDeviceAddress = VK_TRUE; } else { // otherwise, add our own, and push it onto the pNext array bufAddrFeatures.bufferDeviceAddress = VK_TRUE; bufAddrFeatures.bufferDeviceAddressMultiDevice = VK_FALSE; bufAddrFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &bufAddrFeatures; } } else { RDCWARN( "VK_KHR_buffer_device_address is available, but the physical device feature " "is not. Disabling"); Extensions.removeOne(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); } } else if(EXTbuffer) { VkPhysicalDeviceFeatures2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2}; availBase.pNext = &bufAddrEXTFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), &availBase); if(bufAddrEXTFeatures.bufferDeviceAddress) { // see if there's an existing struct VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *existing = (VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT); if(existing) { if(existing->bufferDeviceAddress) existing->bufferDeviceAddressCaptureReplay = VK_TRUE; // if so, make sure the feature is enabled existing->bufferDeviceAddress = VK_TRUE; } else { // otherwise, add our own, and push it onto the pNext array bufAddrEXTFeatures.bufferDeviceAddress = VK_TRUE; bufAddrEXTFeatures.bufferDeviceAddressMultiDevice = VK_FALSE; bufAddrEXTFeatures.pNext = (void *)createInfo.pNext; createInfo.pNext = &bufAddrEXTFeatures; } } else { RDCWARN( "VK_EXT_buffer_device_address is available, but the physical device feature " "is not. Disabling"); Extensions.removeOne(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); } } rdcarray layerArray; layerArray.resize(m_InitParams.Layers.size()); for(size_t i = 0; i < m_InitParams.Layers.size(); i++) layerArray[i] = m_InitParams.Layers[i].c_str(); createInfo.enabledLayerCount = 0; createInfo.ppEnabledLayerNames = NULL; rdcarray extArray; extArray.resize(Extensions.size()); for(size_t i = 0; i < Extensions.size(); i++) extArray[i] = Extensions[i].c_str(); createInfo.enabledExtensionCount = (uint32_t)extArray.size(); createInfo.ppEnabledExtensionNames = extArray.data(); byte *tempMem = GetTempMemory(GetNextPatchSize(createInfo.pNext)); UnwrapNextChain(m_State, "VkDeviceCreateInfo", tempMem, (VkBaseInStructure *)&createInfo); VkDeviceGroupDeviceCreateInfo *device_group_info = (VkDeviceGroupDeviceCreateInfo *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO); // decode physical devices that are actually indices if(device_group_info) { VkPhysicalDevice *physDevs = (VkPhysicalDevice *)device_group_info->pPhysicalDevices; for(uint32_t i = 0; i < device_group_info->physicalDeviceCount; i++) physDevs[i] = Unwrap(m_PhysicalDevices[GetPhysicalDeviceIndexFromHandle(physDevs[i])]); } NVAftermath_Init(); NVAftermath_EnableVK(supportedExtensions, Extensions, &createInfo.pNext); vkr = GetDeviceDispatchTable(NULL)->CreateDevice(Unwrap(physicalDevice), &createInfo, NULL, &device); if(vkr != VK_SUCCESS && !queuePriorities.empty()) { RDCWARN("Failed to create logical device: %s. Reducing queue priorities", ToStr(vkr).c_str()); for(VkDeviceQueueGlobalPriorityCreateInfo *q : queuePriorities) { // medium is considered the default if no priority is set otherwise if(q->globalPriority > VK_QUEUE_GLOBAL_PRIORITY_MEDIUM) q->globalPriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM; } vkr = GetDeviceDispatchTable(NULL)->CreateDevice(Unwrap(physicalDevice), &createInfo, NULL, &device); } if(vkr != VK_SUCCESS) { SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIReplayFailed, "Error creating logical device, VkResult: %s", ToStr(vkr).c_str()); return false; } GetResourceManager()->WrapResource(device, device); GetResourceManager()->AddLiveResource(Device, device); AddResource(Device, ResourceType::Device, "Device"); DerivedResource(origPhysDevice, Device); // we unset the extension because it may be a 'shared' extension that's available at both instance // and device. Only set it to enabled if it's really enabled for this device. This can happen with a // device extension that is reported by another physical device than the one selected - it becomes // available at instance level (e.g. for physical device queries) but is not available at *this* // device level. #undef CheckExt #define CheckExt(name, ver) m_EnabledExtensions.ext_##name = false; CheckDeviceExts(); uint32_t effectiveApiVersion = RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion); #undef CheckExt #define CheckExt(name, ver) \ if(effectiveApiVersion >= ver) \ { \ m_EnabledExtensions.ext_##name = true; \ } CheckDeviceExts(); #undef CheckExt #define CheckExt(name, ver) \ if(!strcmp(createInfo.ppEnabledExtensionNames[i], "VK_" #name)) \ { \ m_EnabledExtensions.ext_##name = true; \ } for(uint32_t i = 0; i < createInfo.enabledExtensionCount; i++) { CheckDeviceExts(); } // for cases where a promoted extension isn't supported as the extension itself, manually // disable them when the feature bit is false. if(effectiveApiVersion >= VK_MAKE_VERSION(1, 2, 0)) { if(supportedExtensions.find(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) == supportedExtensions.end() && !vulkan12Features.bufferDeviceAddress) m_EnabledExtensions.ext_KHR_buffer_device_address = false; if(supportedExtensions.find(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME) == supportedExtensions.end() && !vulkan12Features.scalarBlockLayout) m_EnabledExtensions.ext_EXT_scalar_block_layout = false; if(supportedExtensions.find(VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME) == supportedExtensions.end() && !vulkan12Features.drawIndirectCount) m_EnabledExtensions.ext_KHR_draw_indirect_count = false; if(supportedExtensions.find(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME) == supportedExtensions.end() && !vulkan12Features.samplerFilterMinmax) m_EnabledExtensions.ext_EXT_sampler_filter_minmax = false; // these features are required so this should never happen if(supportedExtensions.find(VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME) == supportedExtensions.end() && !vulkan12Features.separateDepthStencilLayouts) { RDCWARN( "Required feature 'separateDepthStencilLayouts' not supported by 1.2 physical device."); m_EnabledExtensions.ext_KHR_separate_depth_stencil_layouts = false; } } // we also need to check for feature enablement - if an extension is promoted that doesn't mean // it's enabled if(m_EnabledExtensions.ext_KHR_synchronization2) { if(!vulkan13Features.synchronization2 && !sync2.synchronization2) m_EnabledExtensions.ext_KHR_synchronization2 = false; } InitInstanceExtensionTables(m_Instance, &m_EnabledExtensions); InitDeviceExtensionTables(device, &m_EnabledExtensions); RDCASSERT(m_Device == VK_NULL_HANDLE); // MULTIDEVICE m_PhysicalDevice = physicalDevice; m_Device = device; m_QueueFamilyIdx = qFamilyIdx; if(m_InternalCmds.cmdpool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo poolInfo = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qFamilyIdx}; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_InternalCmds.cmdpool); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_InternalCmds.cmdpool); } // for each queue family we've remapped to, ensure we have a command pool and command buffer on // that queue, and we'll also use the first queue that the application creates (or fetch our // own). for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) { uint32_t qidx = createInfo.pQueueCreateInfos[i].queueFamilyIndex; m_ExternalQueues.resize(RDCMAX((uint32_t)m_ExternalQueues.size(), qidx + 1)); // Resize also the image barriers, as ImageBarrierSequence::SetMaxQueueFamilyIndex // just sets the static MaxQueueFamilyIndex m_setupImageBarriers.ResizeForMaxQueueFamilyIndex(qidx); m_cleanupImageBarriers.ResizeForMaxQueueFamilyIndex(qidx); ImageBarrierSequence::SetMaxQueueFamilyIndex(qidx); VkCommandPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qidx, }; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_ExternalQueues[qidx].pool); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].pool); VkCommandBufferAllocateInfo cmdInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL, Unwrap(m_ExternalQueues[qidx].pool), VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, }; VkFenceCreateInfo fenceInfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, VK_FENCE_CREATE_SIGNALED_BIT}; VkSemaphoreCreateInfo semInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; for(size_t x = 0; x < ARRAY_COUNT(m_ExternalQueues[i].ring); x++) { vkr = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &cmdInfo, &m_ExternalQueues[qidx].ring[x].acquire); CHECK_VKR(this, vkr); if(m_SetDeviceLoaderData) m_SetDeviceLoaderData(device, m_ExternalQueues[qidx].ring[x].acquire); else SetDispatchTableOverMagicNumber(device, m_ExternalQueues[qidx].ring[x].acquire); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].acquire); vkr = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &cmdInfo, &m_ExternalQueues[qidx].ring[x].release); CHECK_VKR(this, vkr); if(m_SetDeviceLoaderData) m_SetDeviceLoaderData(device, m_ExternalQueues[qidx].ring[x].release); else SetDispatchTableOverMagicNumber(device, m_ExternalQueues[qidx].ring[x].release); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].release); vkr = ObjDisp(device)->CreateSemaphore(Unwrap(device), &semInfo, NULL, &m_ExternalQueues[qidx].ring[x].fromext); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].fromext); vkr = ObjDisp(device)->CreateSemaphore(Unwrap(device), &semInfo, NULL, &m_ExternalQueues[qidx].ring[x].toext); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].toext); vkr = ObjDisp(device)->CreateFence(Unwrap(device), &fenceInfo, NULL, &m_ExternalQueues[qidx].ring[x].fence); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].fence); } } m_Replay->SetDriverInformation(m_PhysicalDeviceData.props, m_PhysicalDeviceData.driverProps); m_PhysicalDeviceData.enabledFeatures = enabledFeatures; // MoltenVK reports 0x3fffffff for this limit so just ignore that value if it comes up RDCASSERT(m_PhysicalDeviceData.props.limits.maxBoundDescriptorSets < ARRAY_COUNT(BakedCmdBufferInfo::pushDescriptorID[0]) || m_PhysicalDeviceData.props.limits.maxBoundDescriptorSets >= 0x10000000, m_PhysicalDeviceData.props.limits.maxBoundDescriptorSets); m_PhysicalDeviceData.queueCount = (uint32_t)queueProps.size(); for(size_t i = 0; i < queueProps.size(); i++) m_PhysicalDeviceData.queueProps[i] = queueProps[i]; if(RDCMIN(m_EnabledExtensions.vulkanVersion, physProps.apiVersion) >= VK_MAKE_VERSION(1, 1, 0)) { VkPhysicalDeviceVulkan11Properties vulkan11Props = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES, }; VkPhysicalDeviceProperties2 devProps2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; devProps2.pNext = &vulkan11Props; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &devProps2); m_PhysicalDeviceData.maxMemoryAllocationSize = vulkan11Props.maxMemoryAllocationSize; } else { m_PhysicalDeviceData.maxMemoryAllocationSize = 0x80000000U; } ChooseMemoryIndices(); APIProps.vendor = GetDriverInfo().Vendor(); // temporarily disable the debug message sink, to ignore any false positive messages from our // init ScopedDebugMessageSink *sink = GetDebugMessageSink(); SetDebugMessageSink(NULL); m_ShaderCache = new VulkanShaderCache(this); SetDebugMessageSink(sink); } return true; } VkResult WrappedVulkan::vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *, VkDevice *pDevice) { VkDeviceCreateInfo createInfo = *pCreateInfo; for(uint32_t i = 0; i < createInfo.enabledExtensionCount; i++) { if(!IsSupportedExtension(createInfo.ppEnabledExtensionNames[i])) { RDCERR("RenderDoc does not support device extension '%s'.", createInfo.ppEnabledExtensionNames[i]); RDCERR( "For KHR/EXT extensions file an issue on github to request support: " "https://github.com/baldurk/renderdoc"); SendUserDebugMessage( StringFormat::Fmt("RenderDoc does not support requested device extension: %s.", createInfo.ppEnabledExtensionNames[i])); return VK_ERROR_EXTENSION_NOT_PRESENT; } } if(m_Device != VK_NULL_HANDLE) { SendUserDebugMessage("RenderDoc does not support multiple simultaneous logical devices."); return VK_ERROR_INITIALIZATION_FAILED; } rdcarray Extensions(createInfo.ppEnabledExtensionNames, createInfo.enabledExtensionCount); // enable VK_KHR_driver_properties if it's available { uint32_t count = 0; ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &count, NULL); VkExtensionProperties *props = new VkExtensionProperties[count]; ObjDisp(physicalDevice) ->EnumerateDeviceExtensionProperties(Unwrap(physicalDevice), NULL, &count, props); for(uint32_t e = 0; e < count; e++) { if(!strcmp(props[e].extensionName, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) { Extensions.push_back(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); break; } } SAFE_DELETE_ARRAY(props); } createInfo.ppEnabledExtensionNames = Extensions.data(); createInfo.enabledExtensionCount = (uint32_t)Extensions.size(); VkResult vkr = VK_SUCCESS; rdcarray queueProps; { uint32_t qCount = 0; ObjDisp(physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, NULL); queueProps.resize(qCount); ObjDisp(physicalDevice) ->GetPhysicalDeviceQueueFamilyProperties(Unwrap(physicalDevice), &qCount, queueProps.data()); } // find a queue that supports all capabilities, and if one doesn't exist, add it. uint32_t qFamilyIdx = 0; if(!SelectGraphicsComputeQueue(queueProps, createInfo, qFamilyIdx)) return VK_ERROR_INITIALIZATION_FAILED; m_QueueFamilies.resize(createInfo.queueCreateInfoCount); m_QueueFamilyCounts.resize(createInfo.queueCreateInfoCount); m_QueueFamilyIndices.clear(); for(size_t i = 0; i < createInfo.queueCreateInfoCount; i++) { uint32_t family = createInfo.pQueueCreateInfos[i].queueFamilyIndex; uint32_t count = createInfo.pQueueCreateInfos[i].queueCount; m_QueueFamilies.resize(RDCMAX(m_QueueFamilies.size(), size_t(family) + 1)); m_QueueFamilyCounts.resize(RDCMAX(m_QueueFamilies.size(), size_t(family) + 1)); m_QueueFamilies[family] = new VkQueue[count]; m_QueueFamilyCounts[family] = count; for(uint32_t q = 0; q < count; q++) m_QueueFamilies[family][q] = VK_NULL_HANDLE; if(!m_QueueFamilyIndices.contains(family)) m_QueueFamilyIndices.push_back(family); } VkLayerDeviceCreateInfo *layerCreateInfo = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext until we get to the link info while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerCreateInfo->function != VK_LAYER_LINK_INFO)) { layerCreateInfo = (VkLayerDeviceCreateInfo *)layerCreateInfo->pNext; } RDCASSERT(layerCreateInfo); if(layerCreateInfo == NULL) { RDCERR("Couldn't find loader device create info, which is required. Incompatible loader?"); return VK_ERROR_INITIALIZATION_FAILED; } PFN_vkGetDeviceProcAddr gdpa = layerCreateInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr; PFN_vkGetInstanceProcAddr gipa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; // move chain on for next layer layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; PFN_vkCreateDevice createFunc = (PFN_vkCreateDevice)gipa(Unwrap(m_Instance), "vkCreateDevice"); // now search again through for the loader data callback (if it exists) layerCreateInfo = (VkLayerDeviceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerCreateInfo->function != VK_LOADER_DATA_CALLBACK)) { layerCreateInfo = (VkLayerDeviceCreateInfo *)layerCreateInfo->pNext; } // if we found one (we might not - on old loaders), then store the func ptr for // use instead of SetDispatchTableOverMagicNumber if(layerCreateInfo) { RDCASSERT(m_SetDeviceLoaderData == layerCreateInfo->u.pfnSetDeviceLoaderData || m_SetDeviceLoaderData == NULL, (void *)m_SetDeviceLoaderData, (void *)layerCreateInfo->u.pfnSetDeviceLoaderData); m_SetDeviceLoaderData = layerCreateInfo->u.pfnSetDeviceLoaderData; } // patch enabled features needed at capture time VkPhysicalDeviceFeatures availFeatures; ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &availFeatures); // default to all off. This is equivalent to createInfo.pEnabledFeatures == NULL VkPhysicalDeviceFeatures enabledFeatures = {0}; // allocate and unwrap the next chain, so we can patch features if we need to, as well as removing // the loader info later when it comes time to serialise byte *tempMem = GetTempMemory(GetNextPatchSize(createInfo.pNext)); UnwrapNextChain(m_State, "VkDeviceCreateInfo", tempMem, (VkBaseInStructure *)&createInfo); VkPhysicalDeviceFeatures2 *enabledFeatures2 = (VkPhysicalDeviceFeatures2 *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); /* VkPhysicalDeviceVulkan11Features *enabledFeaturesVK11 = (VkPhysicalDeviceVulkan11Features *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES); */ VkPhysicalDeviceVulkan12Features *enabledFeaturesVK12 = (VkPhysicalDeviceVulkan12Features *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); // VkPhysicalDeviceFeatures2 takes priority if(enabledFeatures2) enabledFeatures = enabledFeatures2->features; else if(createInfo.pEnabledFeatures) enabledFeatures = *createInfo.pEnabledFeatures; // enable this feature as it's needed at capture time to save MSAA initial states if(availFeatures.shaderStorageImageWriteWithoutFormat) enabledFeatures.shaderStorageImageWriteWithoutFormat = true; else RDCWARN( "shaderStorageImageWriteWithoutFormat = false, multisampled textures will have empty " "contents at frame start."); // even though we don't actually do any multisampled stores, this is needed to be able to create // MSAA images with STORAGE_BIT usage if(availFeatures.shaderStorageImageMultisample) enabledFeatures.shaderStorageImageMultisample = true; else RDCWARN( "shaderStorageImageMultisample = false, multisampled textures will have empty " "contents at frame start."); // patch the enabled features if(enabledFeatures2) enabledFeatures2->features = enabledFeatures; else createInfo.pEnabledFeatures = &enabledFeatures; // we need to enable non-subsampled images because we're going to remove subsampled bit from // images, and we want to ensure that it's legal! We verified that this is OK before whitelisting // the extension VkPhysicalDeviceFragmentDensityMapFeaturesEXT *fragmentDensityMapFeatures = (VkPhysicalDeviceFragmentDensityMapFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT); if(fragmentDensityMapFeatures && !fragmentDensityMapFeatures->fragmentDensityMapNonSubsampledImages) { fragmentDensityMapFeatures->fragmentDensityMapNonSubsampledImages = true; } VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *bufferAddressFeaturesEXT = (VkPhysicalDeviceBufferDeviceAddressFeaturesEXT *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT); VkPhysicalDeviceBufferDeviceAddressFeatures *bufferAddressFeaturesCoreKHR = (VkPhysicalDeviceBufferDeviceAddressFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES); // we must turn on bufferDeviceAddressCaptureReplay. We verified that this feature was available // before we whitelisted the extension/feature if(enabledFeaturesVK12 && enabledFeaturesVK12->bufferDeviceAddress) enabledFeaturesVK12->bufferDeviceAddressCaptureReplay = VK_TRUE; if(bufferAddressFeaturesCoreKHR) bufferAddressFeaturesCoreKHR->bufferDeviceAddressCaptureReplay = VK_TRUE; if(bufferAddressFeaturesEXT) bufferAddressFeaturesEXT->bufferDeviceAddressCaptureReplay = VK_TRUE; // check features that we care about at capture time const VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures *separateDepthStencilFeatures = (const VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES); if(separateDepthStencilFeatures) m_SeparateDepthStencil |= (separateDepthStencilFeatures->separateDepthStencilLayouts != VK_FALSE); // we need to enable acceleration structure capture/replay. We verified that this is OK before // whitelisting the extension VkPhysicalDeviceAccelerationStructureFeaturesKHR *accFeatures = (VkPhysicalDeviceAccelerationStructureFeaturesKHR *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR); if(accFeatures && accFeatures->accelerationStructure) { accFeatures->accelerationStructureCaptureReplay = VK_TRUE; m_AccelerationStructures = true; RDCLOG("Acceleration structures enabled, ALL MEMORY WILL BE MARKED AS BDA"); } VkPhysicalDeviceRayTracingPipelineFeaturesKHR *rtpFeatures = (VkPhysicalDeviceRayTracingPipelineFeaturesKHR *)FindNextStruct( &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR); if(rtpFeatures && rtpFeatures->rayTracingPipeline) { rtpFeatures->rayTracingPipelineShaderGroupHandleCaptureReplay = VK_TRUE; } VkResult ret; SERIALISE_TIME_CALL(ret = createFunc(Unwrap(physicalDevice), &createInfo, NULL, pDevice)); if(ret == VK_SUCCESS) { InitDeviceTable(*pDevice, gdpa); RDCLOG("Created capture device from physical device %d", m_PhysicalDevices.indexOf(physicalDevice)); ResourceId id = GetResourceManager()->WrapResource(*pDevice, *pDevice); if(IsCaptureMode(m_State)) { Chunk *chunk = NULL; VkDeviceCreateInfo serialiseCreateInfo = *pCreateInfo; // don't serialise out any of the pNext stuff for layer initialisation RemoveNextStruct(&serialiseCreateInfo, VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO); { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCreateDevice); Serialise_vkCreateDevice(ser, physicalDevice, &serialiseCreateInfo, NULL, pDevice); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pDevice); RDCASSERT(record); record->AddChunk(chunk); record->instDevInfo = new InstanceDeviceInfo(); record->instDevInfo->brokenGetDeviceProcAddr = GetRecord(m_Instance)->instDevInfo->brokenGetDeviceProcAddr; VkPhysicalDeviceProperties physProps; ObjDisp(physicalDevice)->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &physProps); record->instDevInfo->vulkanVersion = RDCMIN(physProps.apiVersion, GetRecord(m_Instance)->instDevInfo->vulkanVersion); #undef CheckExt #define CheckExt(name, ver) \ record->instDevInfo->ext_##name = GetRecord(m_Instance)->instDevInfo->ext_##name; // inherit extension enablement from instance, that way GetDeviceProcAddress can check // for enabled extensions for instance functions CheckInstanceExts(); // we unset the extension because it may be a 'shared' extension that's available at both instance // and device. Only set it to enabled if it's really enabled for this device. This can happen with a // device extension that is reported by another physical device than the one selected - it becomes // available at instance level (e.g. for physical device queries) but is not available at *this* // device level. #undef CheckExt #define CheckExt(name, ver) record->instDevInfo->ext_##name = false; CheckDeviceExts(); #undef CheckExt #define CheckExt(name, ver) \ if(!strcmp(createInfo.ppEnabledExtensionNames[i], "VK_" #name) || \ record->instDevInfo->vulkanVersion >= ver) \ { \ record->instDevInfo->ext_##name = true; \ } for(uint32_t i = 0; i < createInfo.enabledExtensionCount; i++) { CheckDeviceExts(); } m_EnabledExtensions = *record->instDevInfo; InitDeviceExtensionTables(*pDevice, record->instDevInfo); } else { GetResourceManager()->AddLiveResource(id, *pDevice); } VkDevice device = *pDevice; RDCASSERT(m_Device == VK_NULL_HANDLE); // MULTIDEVICE m_PhysicalDevice = physicalDevice; m_Device = device; m_QueueFamilyIdx = qFamilyIdx; if(m_InternalCmds.cmdpool == VK_NULL_HANDLE) { VkCommandPoolCreateInfo poolInfo = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qFamilyIdx}; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_InternalCmds.cmdpool); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_InternalCmds.cmdpool); } // for each queue family that isn't our own, create a command pool and command buffer on that // queue for(uint32_t i = 0; i < createInfo.queueCreateInfoCount; i++) { uint32_t qidx = createInfo.pQueueCreateInfos[i].queueFamilyIndex; m_ExternalQueues.resize(RDCMAX((uint32_t)m_ExternalQueues.size(), qidx + 1)); // Resize also the image barriers, as ImageBarrierSequence::SetMaxQueueFamilyIndex // just sets the static MaxQueueFamilyIndex m_setupImageBarriers.ResizeForMaxQueueFamilyIndex(qidx); m_cleanupImageBarriers.ResizeForMaxQueueFamilyIndex(qidx); ImageBarrierSequence::SetMaxQueueFamilyIndex(qidx); VkCommandPoolCreateInfo poolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, NULL, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, qidx, }; vkr = ObjDisp(device)->CreateCommandPool(Unwrap(device), &poolInfo, NULL, &m_ExternalQueues[qidx].pool); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].pool); VkCommandBufferAllocateInfo cmdInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL, Unwrap(m_ExternalQueues[qidx].pool), VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, }; VkFenceCreateInfo fenceInfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, NULL, VK_FENCE_CREATE_SIGNALED_BIT}; VkSemaphoreCreateInfo semInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; for(size_t x = 0; x < ARRAY_COUNT(m_ExternalQueues[i].ring); x++) { vkr = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &cmdInfo, &m_ExternalQueues[qidx].ring[x].acquire); CHECK_VKR(this, vkr); if(m_SetDeviceLoaderData) m_SetDeviceLoaderData(device, m_ExternalQueues[qidx].ring[x].acquire); else SetDispatchTableOverMagicNumber(device, m_ExternalQueues[qidx].ring[x].acquire); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].acquire); vkr = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &cmdInfo, &m_ExternalQueues[qidx].ring[x].release); CHECK_VKR(this, vkr); if(m_SetDeviceLoaderData) m_SetDeviceLoaderData(device, m_ExternalQueues[qidx].ring[x].release); else SetDispatchTableOverMagicNumber(device, m_ExternalQueues[qidx].ring[x].release); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].release); vkr = ObjDisp(device)->CreateSemaphore(Unwrap(device), &semInfo, NULL, &m_ExternalQueues[qidx].ring[x].fromext); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].fromext); vkr = ObjDisp(device)->CreateSemaphore(Unwrap(device), &semInfo, NULL, &m_ExternalQueues[qidx].ring[x].toext); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].toext); vkr = ObjDisp(device)->CreateFence(Unwrap(device), &fenceInfo, NULL, &m_ExternalQueues[qidx].ring[x].fence); CHECK_VKR(this, vkr); GetResourceManager()->WrapResource(Unwrap(device), m_ExternalQueues[qidx].ring[x].fence); } } ObjDisp(physicalDevice) ->GetPhysicalDeviceProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.props); ObjDisp(physicalDevice) ->GetPhysicalDeviceMemoryProperties(Unwrap(physicalDevice), &m_PhysicalDeviceData.memProps); ObjDisp(physicalDevice) ->GetPhysicalDeviceFeatures(Unwrap(physicalDevice), &m_PhysicalDeviceData.availFeatures); m_PhysicalDeviceData.enabledFeatures = enabledFeatures; GetPhysicalDeviceDriverProperties(ObjDisp(physicalDevice), Unwrap(physicalDevice), m_PhysicalDeviceData.driverProps); m_PhysicalDeviceData.driverInfo = VkDriverInfo(m_PhysicalDeviceData.props, m_PhysicalDeviceData.driverProps, true); // hack for steamdeck, set soft memory limit to 200MB if it's not specified if(m_PhysicalDeviceData.driverProps.driverID == VK_DRIVER_ID_MESA_RADV && m_PhysicalDeviceData.props.vendorID == 0x1002 && m_PhysicalDeviceData.props.deviceID == 0x163F) { CaptureOptions opts = RenderDoc::Inst().GetCaptureOptions(); if(opts.softMemoryLimit == 0) opts.softMemoryLimit = 200; RenderDoc::Inst().SetCaptureOptions(opts); RDCLOG("Forcing 200MB soft memory limit"); } m_PhysicalDeviceData.maxMemoryAllocationSize = 0; ChooseMemoryIndices(); m_PhysicalDeviceData.queueCount = (uint32_t)queueProps.size(); for(size_t i = 0; i < queueProps.size(); i++) m_PhysicalDeviceData.queueProps[i] = queueProps[i]; m_ShaderCache = new VulkanShaderCache(this); m_TextRenderer = new VulkanTextRenderer(this); m_DebugManager = new VulkanDebugManager(this); } FirstFrame(); return ret; } void WrappedVulkan::vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *) { if(device == VK_NULL_HANDLE) return; if(m_MemoryFreeThread) { Threading::JoinThread(m_MemoryFreeThread); Threading::CloseThread(m_MemoryFreeThread); m_MemoryFreeThread = 0; } // flush out any pending commands/semaphores SubmitCmds(); SubmitSemaphores(); FlushQ(); { SCOPED_LOCK(m_PendingCmdBufferCallbacksLock); for(VkPendingSubmissionCompleteCallbacks *pending : m_PendingCmdBufferCallbacks) { const VkResult vkr = ObjDisp(m_Device)->GetEventStatus(Unwrap(m_Device), pending->event); if(vkr == VK_EVENT_SET) pending->Release(); } m_PendingCmdBufferCallbacks.clear(); } // idle the device as well so that external queues are idle. VkResult vkr = ObjDisp(m_Device)->DeviceWaitIdle(Unwrap(m_Device)); CHECK_VKR(this, vkr); // MULTIDEVICE this function will need to check if the device is the one we // used for debugmanager/cmd pool etc, and only remove child queues and // resources (instead of doing full resource manager shutdown). // Or will we have a debug manager per-device? RDCASSERT(m_Device == device); // delete all debug manager objects SAFE_DELETE(m_DebugManager); SAFE_DELETE(m_ShaderCache); SAFE_DELETE(m_TextRenderer); m_ASManager->Cleanup(); // since we didn't create proper registered resources for our command buffers, // they won't be taken down properly with the pool. So we release them (just our // data) here. for(size_t i = 0; i < m_InternalCmds.freecmds.size(); i++) GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.freecmds[i]); if(m_IndirectCommandBuffer != VK_NULL_HANDLE) GetResourceManager()->ReleaseWrappedResource(m_IndirectCommandBuffer); // destroy our command pool if(m_InternalCmds.cmdpool != VK_NULL_HANDLE) { ObjDisp(m_Device)->DestroyCommandPool(Unwrap(m_Device), Unwrap(m_InternalCmds.cmdpool), NULL); GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.cmdpool); } for(size_t i = 0; i < m_InternalCmds.freesems.size(); i++) { ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_InternalCmds.freesems[i]), NULL); GetResourceManager()->ReleaseWrappedResource(m_InternalCmds.freesems[i]); } for(size_t i = 0; i < m_ExternalQueues.size(); i++) { if(m_ExternalQueues[i].pool != VK_NULL_HANDLE) { for(size_t x = 0; x < ARRAY_COUNT(m_ExternalQueues[i].ring); x++) { GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].acquire); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].release); ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].fromext), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].fromext); ObjDisp(m_Device)->DestroySemaphore(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].toext), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].toext); ObjDisp(m_Device)->DestroyFence(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].ring[x].fence), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].ring[x].fence); } ObjDisp(m_Device)->DestroyCommandPool(Unwrap(m_Device), Unwrap(m_ExternalQueues[i].pool), NULL); GetResourceManager()->ReleaseWrappedResource(m_ExternalQueues[i].pool); } } m_InternalCmds.Reset(); m_QueueFamilyIdx = ~0U; m_PrevQueue = m_Queue = VK_NULL_HANDLE; m_QueueFamilies.clear(); m_QueueFamilyCounts.clear(); m_QueueFamilyIndices.clear(); m_ExternalQueues.clear(); // destroy the API device immediately. There should be no more // resources left in the resource manager device/physical device/instance. // Anything we created should be gone and anything the application created // should be deleted by now. // If there were any leaks, we will leak them ourselves in vkDestroyInstance // rather than try to delete API objects after the device has gone ObjDisp(m_Device)->DestroyDevice(Unwrap(m_Device), NULL); GetResourceManager()->ReleaseWrappedResource(m_Device); m_Device = VK_NULL_HANDLE; m_PhysicalDevice = VK_NULL_HANDLE; } template bool WrappedVulkan::Serialise_vkDeviceWaitIdle(SerialiserType &ser, VkDevice device) { SERIALISE_ELEMENT(device).Important(); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { ObjDisp(device)->DeviceWaitIdle(Unwrap(device)); } return true; } VkResult WrappedVulkan::vkDeviceWaitIdle(VkDevice device) { VkResult ret; SERIALISE_TIME_CALL(ret = ObjDisp(device)->DeviceWaitIdle(Unwrap(device))); if(IsActiveCapturing(m_State)) { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CHUNK(VulkanChunk::vkDeviceWaitIdle); Serialise_vkDeviceWaitIdle(ser, device); m_FrameCaptureRecord->AddChunk(scope.Get()); } return ret; } INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkEnumeratePhysicalDevices, VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices); INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateDevice, VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *, VkDevice *pDevice); INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkDeviceWaitIdle, VkDevice device);