/* * Copyright 2016-2018 Józef Kucia for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef __VKD3D_D3D12_CROSSTEST_H #define __VKD3D_D3D12_CROSSTEST_H /* Hack for MinGW-w64 headers. * * We want to use WIDL C inline wrappers because some methods * in D3D12 interfaces return aggregate objects. Unfortunately, * WIDL C inline wrappers are broken when used with MinGW-w64 * headers because FORCEINLINE expands to extern inline * which leads to the "multiple storage classes in declaration * specifiers" compiler error. */ #ifdef __MINGW32__ # include <_mingw.h> # ifdef __MINGW64_VERSION_MAJOR # undef __forceinline # define __forceinline __inline__ __attribute__((__always_inline__,__gnu_inline__)) # endif # define _HRESULT_DEFINED typedef int HRESULT; #endif #define COBJMACROS #define INITGUID #include "vkd3d_test.h" #include "vkd3d_windows.h" #define WIDL_C_INLINE_WRAPPERS #include "vkd3d_d3d12.h" #include "vkd3d_d3d12sdklayers.h" #include #include #include #include #if defined(_WIN32) && !defined(VKD3D_FORCE_UTILS_WRAPPER) # include "vkd3d_dxgi1_4.h" #else # include "vkd3d_threads.h" # include "vkd3d.h" # include "vkd3d_utils.h" # include "vkd3d_sonames.h" #endif #if !defined(_WIN32) #include #endif #include "d3d12_test_utils.h" extern PFN_D3D12_CREATE_DEVICE pfn_D3D12CreateDevice; extern PFN_D3D12_GET_DEBUG_INTERFACE pfn_D3D12GetDebugInterface; #if defined(_WIN32) && !defined(VKD3D_FORCE_UTILS_WRAPPER) static inline HANDLE create_event(void) { return CreateEventA(NULL, FALSE, FALSE, NULL); } static inline void signal_event(HANDLE event) { SetEvent(event); } static inline unsigned int wait_event(HANDLE event, unsigned int milliseconds) { return WaitForSingleObject(event, milliseconds); } static inline void destroy_event(HANDLE event) { CloseHandle(event); } #define get_d3d12_pfn(name) get_d3d12_pfn_(#name) static inline void *get_d3d12_pfn_(const char *name) { static HMODULE d3d12_module; if (!d3d12_module) d3d12_module = LoadLibraryA("d3d12.dll"); return GetProcAddress(d3d12_module, name); } #else #define INFINITE VKD3D_INFINITE #define WAIT_OBJECT_0 VKD3D_WAIT_OBJECT_0 #define WAIT_TIMEOUT VKD3D_WAIT_TIMEOUT static inline HANDLE create_event(void) { return vkd3d_create_event(); } static inline void signal_event(HANDLE event) { vkd3d_signal_event(event); } static inline unsigned int wait_event(HANDLE event, unsigned int milliseconds) { return vkd3d_wait_event(event, milliseconds); } static inline void destroy_event(HANDLE event) { vkd3d_destroy_event(event); } #define get_d3d12_pfn(name) (name) #endif typedef void (*thread_main_pfn)(void *data); struct test_thread_data { thread_main_pfn main_pfn; void *user_data; }; #if defined(_WIN32) && !defined(VKD3D_FORCE_UTILS_WRAPPER) static inline DWORD WINAPI test_thread_main(void *untyped_data) { struct test_thread_data *data = untyped_data; data->main_pfn(data->user_data); free(untyped_data); return 0; } static inline HANDLE create_thread(thread_main_pfn main_pfn, void *user_data) { struct test_thread_data *data; if (!(data = malloc(sizeof(*data)))) return NULL; data->main_pfn = main_pfn; data->user_data = user_data; return CreateThread(NULL, 0, test_thread_main, data, 0, NULL); } static inline bool join_thread(HANDLE thread) { unsigned int ret; ret = WaitForSingleObject(thread, INFINITE); CloseHandle(thread); return ret == WAIT_OBJECT_0; } #else static void *test_thread_main(void *untyped_data) { struct test_thread_data *data = untyped_data; data->main_pfn(data->user_data); free(untyped_data); return NULL; } static inline HANDLE create_thread(thread_main_pfn main_pfn, void *user_data) { struct test_thread_data *data; pthread_t *thread; if (!(thread = malloc(sizeof(*thread)))) return NULL; if (!(data = malloc(sizeof(*data)))) { free(thread); return NULL; } data->main_pfn = main_pfn; data->user_data = user_data; if (pthread_create(thread, NULL, test_thread_main, data)) { free(data); free(thread); return NULL; } return thread; } static inline bool join_thread(HANDLE untyped_thread) { pthread_t *thread = untyped_thread; int rc; rc = pthread_join(*thread, NULL); free(thread); return !rc; } #endif static HRESULT wait_for_fence(ID3D12Fence *fence, uint64_t value) { unsigned int ret; HANDLE event; HRESULT hr; if (ID3D12Fence_GetCompletedValue(fence) >= value) return S_OK; if (!(event = create_event())) return E_FAIL; if (FAILED(hr = ID3D12Fence_SetEventOnCompletion(fence, value, event))) { destroy_event(event); return hr; } ret = wait_event(event, INFINITE); destroy_event(event); return ret == WAIT_OBJECT_0 ? S_OK : E_FAIL; } static void wait_queue_idle_(unsigned int line, ID3D12Device *device, ID3D12CommandQueue *queue) { ID3D12Fence *fence; HRESULT hr; hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence); assert_that_(line)(hr == S_OK, "Failed to create fence, hr %#x.\n", hr); hr = ID3D12CommandQueue_Signal(queue, fence, 1); assert_that_(line)(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr); hr = wait_for_fence(fence, 1); assert_that_(line)(hr == S_OK, "Failed to wait for fence, hr %#x.\n", hr); ID3D12Fence_Release(fence); } static bool use_warp_device; static unsigned int use_adapter_idx; #if defined(_WIN32) && !defined(VKD3D_FORCE_UTILS_WRAPPER) static IUnknown *create_warp_adapter(IDXGIFactory4 *factory) { IUnknown *adapter; HRESULT hr; adapter = NULL; hr = IDXGIFactory4_EnumWarpAdapter(factory, &IID_IUnknown, (void **)&adapter); if (FAILED(hr)) trace("Failed to get WARP adapter, hr %#x.\n", hr); return adapter; } static IUnknown *create_adapter(void) { IUnknown *adapter = NULL; IDXGIFactory4 *factory; HRESULT hr; hr = CreateDXGIFactory1(&IID_IDXGIFactory4, (void **)&factory); ok(hr == S_OK, "Failed to create IDXGIFactory4, hr %#x.\n", hr); if (use_warp_device && (adapter = create_warp_adapter(factory))) { IDXGIFactory4_Release(factory); return adapter; } hr = IDXGIFactory4_EnumAdapters(factory, use_adapter_idx, (IDXGIAdapter **)&adapter); IDXGIFactory4_Release(factory); if (FAILED(hr)) trace("Failed to get adapter, hr %#x.\n", hr); return adapter; } static ID3D12Device *create_device(void) { IUnknown *adapter = NULL; ID3D12Device *device; HRESULT hr; if ((use_warp_device || use_adapter_idx) && !(adapter = create_adapter())) { trace("Failed to create adapter.\n"); return NULL; } hr = pfn_D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&device); if (adapter) IUnknown_Release(adapter); return SUCCEEDED(hr) ? device : NULL; } static void init_adapter_info(void) { char name[MEMBER_SIZE(DXGI_ADAPTER_DESC, Description)]; IDXGIAdapter *dxgi_adapter; DXGI_ADAPTER_DESC desc; IUnknown *adapter; unsigned int i; HRESULT hr; if (!(adapter = create_adapter())) return; hr = IUnknown_QueryInterface(adapter, &IID_IDXGIAdapter, (void **)&dxgi_adapter); ok(hr == S_OK, "Failed to query IDXGIAdapter, hr %#x.\n", hr); IUnknown_Release(adapter); hr = IDXGIAdapter_GetDesc(dxgi_adapter, &desc); ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr); /* FIXME: Use debugstr_w(). */ for (i = 0; i < ARRAY_SIZE(desc.Description) && isprint(desc.Description[i]); ++i) name[i] = desc.Description[i]; name[min(i, ARRAY_SIZE(name) - 1)] = '\0'; trace("Adapter: %s, %04x:%04x.\n", name, desc.VendorId, desc.DeviceId); if (desc.VendorId == 0x1414 && desc.DeviceId == 0x008c) { trace("Using WARP device.\n"); use_warp_device = true; } IDXGIAdapter_Release(dxgi_adapter); } static inline bool get_adapter_desc(ID3D12Device *device, DXGI_ADAPTER_DESC *desc) { IDXGIFactory4 *factory; IDXGIAdapter *adapter; HRESULT hr; LUID luid; memset(desc, 0, sizeof(*desc)); if (!vkd3d_test_platform_is_windows()) return false; luid = ID3D12Device_GetAdapterLuid(device); hr = CreateDXGIFactory1(&IID_IDXGIFactory4, (void **)&factory); ok(hr == S_OK, "Failed to create IDXGIFactory4, hr %#x.\n", hr); hr = IDXGIFactory4_EnumAdapterByLuid(factory, luid, &IID_IDXGIAdapter, (void **)&adapter); if (SUCCEEDED(hr)) { hr = IDXGIAdapter_GetDesc(adapter, desc); ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr); IDXGIAdapter_Release(adapter); } IDXGIFactory4_Release(factory); return SUCCEEDED(hr); } static inline bool is_amd_windows_device(ID3D12Device *device) { DXGI_ADAPTER_DESC desc; return get_adapter_desc(device, &desc) && desc.VendorId == 0x1002; } static inline bool is_intel_windows_device(ID3D12Device *device) { DXGI_ADAPTER_DESC desc; return get_adapter_desc(device, &desc) && desc.VendorId == 0x8086; } static inline bool is_mesa_device(ID3D12Device *device) { return false; } static inline bool is_mesa_intel_device(ID3D12Device *device) { return false; } static inline bool is_nvidia_device(ID3D12Device *device) { return false; } static inline bool is_radv_device(ID3D12Device *device) { return false; } static inline bool is_depth_clip_enable_supported(ID3D12Device *device) { return true; } #else static PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr; static bool init_vulkan_loader(void) { if (pfn_vkGetInstanceProcAddr) return true; #ifdef _WIN32 HMODULE mod = LoadLibraryA(SONAME_LIBVULKAN); if (!mod) return false; pfn_vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress(mod, "vkGetInstanceProcAddr"); #else void *mod = dlopen(SONAME_LIBVULKAN, RTLD_LAZY); if (!mod) return false; pfn_vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym(mod, "vkGetInstanceProcAddr"); #endif return pfn_vkGetInstanceProcAddr != NULL; } #define get_vk_instance_proc(instance, sym) (PFN_##sym)get_vk_instance_proc_(instance, #sym) static PFN_vkVoidFunction get_vk_instance_proc_(VkInstance instance, const char *sym) { if (!init_vulkan_loader()) return NULL; return pfn_vkGetInstanceProcAddr(instance, sym); } static bool check_device_extension(VkInstance instance, VkPhysicalDevice vk_physical_device, const char *name) { VkExtensionProperties *properties; bool ret = false; unsigned int i; uint32_t count; VkResult vr; PFN_vkEnumerateDeviceExtensionProperties pfn_vkEnumerateDeviceExtensionProperties; pfn_vkEnumerateDeviceExtensionProperties = get_vk_instance_proc(instance, vkEnumerateDeviceExtensionProperties); if (!pfn_vkEnumerateDeviceExtensionProperties) return false; vr = pfn_vkEnumerateDeviceExtensionProperties(vk_physical_device, NULL, &count, NULL); ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); if (!count) return false; properties = calloc(count, sizeof(*properties)); ok(properties, "Failed to allocate memory.\n"); vr = pfn_vkEnumerateDeviceExtensionProperties(vk_physical_device, NULL, &count, properties); ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); for (i = 0; i < count; ++i) { if (!strcmp(properties[i].extensionName, name)) { ret = true; break; } } free(properties); return ret; } static HRESULT create_vkd3d_instance(struct vkd3d_instance **instance) { struct vkd3d_optional_instance_extensions_info optional_extensions_info; struct vkd3d_instance_create_info instance_create_info; static const char * const optional_instance_extensions[] = { VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, }; memset(&optional_extensions_info, 0, sizeof(optional_extensions_info)); optional_extensions_info.type = VKD3D_STRUCTURE_TYPE_OPTIONAL_INSTANCE_EXTENSIONS_INFO; optional_extensions_info.extensions = optional_instance_extensions; optional_extensions_info.extension_count = ARRAY_SIZE(optional_instance_extensions); memset(&instance_create_info, 0, sizeof(instance_create_info)); instance_create_info.type = VKD3D_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instance_create_info.next = &optional_extensions_info; instance_create_info.pfn_signal_event = vkd3d_signal_event; instance_create_info.wchar_size = sizeof(WCHAR); return vkd3d_create_instance(&instance_create_info, instance); } static VkPhysicalDevice select_physical_device(struct vkd3d_instance *instance) { VkPhysicalDevice *vk_physical_devices; VkPhysicalDevice vk_physical_device; VkInstance vk_instance; uint32_t count; VkResult vr; PFN_vkEnumeratePhysicalDevices pfn_vkEnumeratePhysicalDevices; vk_instance = vkd3d_instance_get_vk_instance(instance); pfn_vkEnumeratePhysicalDevices = get_vk_instance_proc(vk_instance, vkEnumeratePhysicalDevices); if (!pfn_vkEnumeratePhysicalDevices) return VK_NULL_HANDLE; vr = pfn_vkEnumeratePhysicalDevices(vk_instance, &count, NULL); ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); if (use_adapter_idx >= count) { trace("Invalid physical device index %u.\n", use_adapter_idx); return VK_NULL_HANDLE; } vk_physical_devices = calloc(count, sizeof(*vk_physical_devices)); ok(vk_physical_devices, "Failed to allocate memory.\n"); vr = pfn_vkEnumeratePhysicalDevices(vk_instance, &count, vk_physical_devices); ok(vr == VK_SUCCESS, "Got unexpected VkResult %d.\n", vr); vk_physical_device = vk_physical_devices[use_adapter_idx]; free(vk_physical_devices); return vk_physical_device; } static HRESULT create_vkd3d_device(struct vkd3d_instance *instance, D3D_FEATURE_LEVEL minimum_feature_level, REFIID iid, void **device) { static const char * const device_extensions[] = { VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, }; static const struct vkd3d_optional_device_extensions_info optional_extensions = { .type = VKD3D_STRUCTURE_TYPE_OPTIONAL_DEVICE_EXTENSIONS_INFO, .extensions = device_extensions, .extension_count = ARRAY_SIZE(device_extensions), }; struct vkd3d_device_create_info device_create_info; VkPhysicalDevice vk_physical_device; if (!(vk_physical_device = select_physical_device(instance))) return E_INVALIDARG; memset(&device_create_info, 0, sizeof(device_create_info)); device_create_info.type = VKD3D_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device_create_info.next = &optional_extensions; device_create_info.minimum_feature_level = minimum_feature_level; device_create_info.instance = instance; device_create_info.vk_physical_device = vk_physical_device; return vkd3d_create_device(&device_create_info, iid, device); } static ID3D12Device *create_device(void) { struct vkd3d_instance *instance; ID3D12Device *device; HRESULT hr; if (SUCCEEDED(hr = create_vkd3d_instance(&instance))) { hr = create_vkd3d_device(instance, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&device); vkd3d_instance_decref(instance); } return SUCCEEDED(hr) ? device : NULL; } static bool get_driver_properties(ID3D12Device *device, VkPhysicalDeviceDriverPropertiesKHR *driver_properties) { PFN_vkGetPhysicalDeviceProperties2KHR pfn_vkGetPhysicalDeviceProperties2KHR; VkPhysicalDeviceProperties2 device_properties2; VkPhysicalDevice vk_physical_device; VkInstance vk_instance; memset(driver_properties, 0, sizeof(*driver_properties)); driver_properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; vk_physical_device = vkd3d_get_vk_physical_device(device); vk_instance = vkd3d_instance_get_vk_instance(vkd3d_instance_from_device(device)); if (!init_vulkan_loader()) return false; if (check_device_extension(vk_instance, vk_physical_device, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) { struct vkd3d_instance *instance = vkd3d_instance_from_device(device); VkInstance vk_instance = vkd3d_instance_get_vk_instance(instance); pfn_vkGetPhysicalDeviceProperties2KHR = (void *)pfn_vkGetInstanceProcAddr(vk_instance, "vkGetPhysicalDeviceProperties2KHR"); ok(pfn_vkGetPhysicalDeviceProperties2KHR, "vkGetPhysicalDeviceProperties2KHR is NULL.\n"); memset(&device_properties2, 0, sizeof(device_properties2)); device_properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; device_properties2.pNext = driver_properties; pfn_vkGetPhysicalDeviceProperties2KHR(vk_physical_device, &device_properties2); return true; } return false; } static void init_adapter_info(void) { VkPhysicalDeviceDriverPropertiesKHR driver_properties; struct vkd3d_instance *instance; ID3D12Device *device; HRESULT hr; if (FAILED(hr = create_vkd3d_instance(&instance))) return; if (SUCCEEDED(hr = create_vkd3d_device(instance, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&device))) { if (get_driver_properties(device, &driver_properties)) trace("Driver name: %s, driver info: %s.\n", driver_properties.driverName, driver_properties.driverInfo); ID3D12Device_Release(device); } vkd3d_instance_decref(instance); } static inline bool is_amd_windows_device(ID3D12Device *device) { return false; } static inline bool is_intel_windows_device(ID3D12Device *device) { return false; } static inline bool is_mesa_device(ID3D12Device *device) { VkPhysicalDeviceDriverPropertiesKHR properties; get_driver_properties(device, &properties); return properties.driverID == VK_DRIVER_ID_MESA_RADV_KHR || properties.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR; } static inline bool is_mesa_intel_device(ID3D12Device *device) { VkPhysicalDeviceDriverPropertiesKHR properties; get_driver_properties(device, &properties); return properties.driverID == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR; } static inline bool is_nvidia_device(ID3D12Device *device) { VkPhysicalDeviceDriverPropertiesKHR properties; get_driver_properties(device, &properties); return properties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR; } static inline bool is_radv_device(ID3D12Device *device) { VkPhysicalDeviceDriverPropertiesKHR properties; get_driver_properties(device, &properties); return properties.driverID == VK_DRIVER_ID_MESA_RADV_KHR; } static inline bool is_depth_clip_enable_supported(ID3D12Device *device) { struct vkd3d_instance *instance = vkd3d_instance_from_device(device); VkInstance vk_instance = vkd3d_instance_get_vk_instance(instance); VkPhysicalDevice vk_physical_device = vkd3d_get_vk_physical_device(device); return check_device_extension(vk_instance, vk_physical_device, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME); } #endif static void parse_args(int argc, char **argv) { unsigned int i; for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--warp")) use_warp_device = true; else if (!strcmp(argv[i], "--adapter") && i + 1 < argc) use_adapter_idx = atoi(argv[++i]); } } static void enable_d3d12_debug_layer(int argc, char **argv) { bool enable_debug_layer = false, enable_gpu_based_validation = false; ID3D12Debug1 *debug1; ID3D12Debug *debug; unsigned int i; for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--validate")) enable_debug_layer = true; else if (!strcmp(argv[i], "--gbv")) enable_gpu_based_validation = true; } if (enable_gpu_based_validation) { if (SUCCEEDED(pfn_D3D12GetDebugInterface(&IID_ID3D12Debug1, (void **)&debug1))) { ID3D12Debug1_SetEnableGPUBasedValidation(debug1, true); ID3D12Debug1_Release(debug1); enable_debug_layer = true; } else { trace("Failed to enable GPU-based validation.\n"); } } if (enable_debug_layer && SUCCEEDED(pfn_D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug))) { ID3D12Debug_EnableDebugLayer(debug); ID3D12Debug_Release(debug); } } #endif /* __VKD3D_D3D12_CROSSTEST_H */