/* * Copyright 2024 RĂ©mi Bernon 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 */ #if 0 #pragma makedep unix #endif #include "config.h" #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "ntgdi_private.h" #include "win32u_private.h" #include "wine/vulkan.h" #include "wine/vulkan_driver.h" WINE_DEFAULT_DEBUG_CHANNEL(vulkan); struct d3dkmt_adapter { D3DKMT_HANDLE handle; /* Kernel mode graphics adapter handle */ struct list entry; /* List entry */ VkPhysicalDevice vk_device; /* Vulkan physical device */ }; struct d3dkmt_device { D3DKMT_HANDLE handle; /* Kernel mode graphics device handle*/ struct list entry; /* List entry */ }; struct d3dkmt_vidpn_source { D3DKMT_VIDPNSOURCEOWNER_TYPE type; /* VidPN source owner type */ D3DDDI_VIDEO_PRESENT_SOURCE_ID id; /* VidPN present source id */ D3DKMT_HANDLE device; /* Kernel mode device context */ struct list entry; /* List entry */ }; static pthread_mutex_t d3dkmt_lock = PTHREAD_MUTEX_INITIALIZER; static struct list d3dkmt_adapters = LIST_INIT( d3dkmt_adapters ); static struct list d3dkmt_devices = LIST_INIT( d3dkmt_devices ); static struct list d3dkmt_vidpn_sources = LIST_INIT( d3dkmt_vidpn_sources ); /* VidPN source information list */ static VkInstance d3dkmt_vk_instance; /* Vulkan instance for D3DKMT functions */ static PFN_vkGetPhysicalDeviceMemoryProperties2KHR pvkGetPhysicalDeviceMemoryProperties2KHR; static PFN_vkGetPhysicalDeviceProperties2KHR pvkGetPhysicalDeviceProperties2KHR; static PFN_vkEnumeratePhysicalDevices pvkEnumeratePhysicalDevices; static const struct vulkan_funcs *vulkan_funcs; static void d3dkmt_init_vulkan(void) { static const char *extensions[] = { VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, }; VkInstanceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .enabledExtensionCount = ARRAY_SIZE( extensions ), .ppEnabledExtensionNames = extensions, }; VkResult vr; if (!(vulkan_funcs = __wine_get_vulkan_driver( WINE_VULKAN_DRIVER_VERSION ))) { WARN( "Failed to open the Vulkan driver\n" ); return; } if ((vr = vulkan_funcs->p_vkCreateInstance( &create_info, NULL, &d3dkmt_vk_instance ))) { WARN( "Failed to create a Vulkan instance, vr %d.\n", vr ); vulkan_funcs = NULL; return; } #define LOAD_VK_FUNC( f ) \ if (!(p##f = (void *)vulkan_funcs->p_vkGetInstanceProcAddr( d3dkmt_vk_instance, #f ))) \ { \ WARN( "Failed to load " #f ".\n" ); \ vulkan_funcs->p_vkDestroyInstance( d3dkmt_vk_instance, NULL ); \ vulkan_funcs = NULL; \ return; \ } LOAD_VK_FUNC( vkEnumeratePhysicalDevices ) LOAD_VK_FUNC( vkGetPhysicalDeviceProperties2KHR ) LOAD_VK_FUNC( vkGetPhysicalDeviceMemoryProperties2KHR ) #undef LOAD_VK_FUNC } static BOOL d3dkmt_use_vulkan(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once( &once, d3dkmt_init_vulkan ); return !!vulkan_funcs; } /* d3dkmt_lock must be held */ static struct d3dkmt_adapter *find_adapter_from_handle( D3DKMT_HANDLE handle ) { struct d3dkmt_adapter *adapter; LIST_FOR_EACH_ENTRY( adapter, &d3dkmt_adapters, struct d3dkmt_adapter, entry ) if (adapter->handle == handle) return adapter; return NULL; } /****************************************************************************** * NtGdiDdDDIOpenAdapterFromHdc (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIOpenAdapterFromHdc( D3DKMT_OPENADAPTERFROMHDC *desc ) { FIXME( "(%p): stub\n", desc ); return STATUS_NO_MEMORY; } /****************************************************************************** * NtGdiDdDDIEscape (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIEscape( const D3DKMT_ESCAPE *desc ) { FIXME( "(%p): stub\n", desc ); return STATUS_NO_MEMORY; } /****************************************************************************** * NtGdiDdDDICloseAdapter (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDICloseAdapter( const D3DKMT_CLOSEADAPTER *desc ) { NTSTATUS status = STATUS_INVALID_PARAMETER; struct d3dkmt_adapter *adapter; TRACE( "(%p)\n", desc ); if (!desc || !desc->hAdapter) return STATUS_INVALID_PARAMETER; pthread_mutex_lock( &d3dkmt_lock ); if ((adapter = find_adapter_from_handle( desc->hAdapter ))) { list_remove( &adapter->entry ); status = STATUS_SUCCESS; } pthread_mutex_unlock( &d3dkmt_lock ); free( adapter ); return status; } /****************************************************************************** * NtGdiDdDDIOpenAdapterFromDeviceName (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIOpenAdapterFromDeviceName( D3DKMT_OPENADAPTERFROMDEVICENAME *desc ) { D3DKMT_OPENADAPTERFROMLUID desc_luid; NTSTATUS status; FIXME( "desc %p stub.\n", desc ); if (!desc || !desc->pDeviceName) return STATUS_INVALID_PARAMETER; memset( &desc_luid, 0, sizeof(desc_luid) ); if ((status = NtGdiDdDDIOpenAdapterFromLuid( &desc_luid ))) return status; desc->AdapterLuid = desc_luid.AdapterLuid; desc->hAdapter = desc_luid.hAdapter; return STATUS_SUCCESS; } static VkPhysicalDevice get_vulkan_physical_device( const GUID *uuid ) { VkPhysicalDevice *devices, device; UINT device_count, i; VkResult vr; if ((vr = pvkEnumeratePhysicalDevices( d3dkmt_vk_instance, &device_count, NULL ))) { WARN( "vkEnumeratePhysicalDevices returned %d\n", vr ); return VK_NULL_HANDLE; } if (!device_count || !(devices = malloc( device_count * sizeof(*devices) ))) return VK_NULL_HANDLE; if ((vr = pvkEnumeratePhysicalDevices( d3dkmt_vk_instance, &device_count, devices ))) { WARN( "vkEnumeratePhysicalDevices returned %d\n", vr ); free( devices ); return VK_NULL_HANDLE; } for (i = 0, device = VK_NULL_HANDLE; i < device_count; ++i) { VkPhysicalDeviceIDProperties id = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES}; VkPhysicalDeviceProperties2 properties2 = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &id}; pvkGetPhysicalDeviceProperties2KHR( devices[i], &properties2 ); if (IsEqualGUID( uuid, id.deviceUUID )) { device = devices[i]; break; } } free( devices ); return device; } /****************************************************************************** * NtGdiDdDDIOpenAdapterFromLuid (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *desc ) { static D3DKMT_HANDLE handle_start = 0; struct d3dkmt_adapter *adapter; GUID uuid = {0}; if (!(adapter = calloc( 1, sizeof(*adapter) ))) return STATUS_NO_MEMORY; if (!d3dkmt_use_vulkan()) WARN( "Vulkan is unavailable.\n" ); else if (!get_vulkan_uuid_from_luid( &desc->AdapterLuid, &uuid )) WARN( "Failed to find Vulkan device with LUID %08x:%08x.\n", (int)desc->AdapterLuid.HighPart, (int)desc->AdapterLuid.LowPart ); else if (!(adapter->vk_device = get_vulkan_physical_device( &uuid ))) WARN( "Failed to find vulkan device with GUID %s\n", debugstr_guid( &uuid ) ); pthread_mutex_lock( &d3dkmt_lock ); desc->hAdapter = adapter->handle = ++handle_start; list_add_tail( &d3dkmt_adapters, &adapter->entry ); pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_SUCCESS; } /****************************************************************************** * NtGdiDdDDICreateDevice (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDICreateDevice( D3DKMT_CREATEDEVICE *desc ) { static D3DKMT_HANDLE handle_start = 0; struct d3dkmt_device *device; BOOL found = FALSE; TRACE( "(%p)\n", desc ); if (!desc) return STATUS_INVALID_PARAMETER; pthread_mutex_lock( &d3dkmt_lock ); found = !!find_adapter_from_handle( desc->hAdapter ); pthread_mutex_unlock( &d3dkmt_lock ); if (!found) return STATUS_INVALID_PARAMETER; if (desc->Flags.LegacyMode || desc->Flags.RequestVSync || desc->Flags.DisableGpuTimeout) FIXME( "Flags unsupported.\n" ); device = calloc( 1, sizeof(*device) ); if (!device) return STATUS_NO_MEMORY; pthread_mutex_lock( &d3dkmt_lock ); device->handle = ++handle_start; list_add_tail( &d3dkmt_devices, &device->entry ); pthread_mutex_unlock( &d3dkmt_lock ); desc->hDevice = device->handle; return STATUS_SUCCESS; } /****************************************************************************** * NtGdiDdDDIDestroyDevice (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIDestroyDevice( const D3DKMT_DESTROYDEVICE *desc ) { D3DKMT_SETVIDPNSOURCEOWNER set_owner_desc = {0}; struct d3dkmt_device *device, *found = NULL; TRACE( "(%p)\n", desc ); if (!desc || !desc->hDevice) return STATUS_INVALID_PARAMETER; pthread_mutex_lock( &d3dkmt_lock ); LIST_FOR_EACH_ENTRY( device, &d3dkmt_devices, struct d3dkmt_device, entry ) { if (device->handle == desc->hDevice) { list_remove( &device->entry ); found = device; break; } } pthread_mutex_unlock( &d3dkmt_lock ); if (!found) return STATUS_INVALID_PARAMETER; set_owner_desc.hDevice = desc->hDevice; NtGdiDdDDISetVidPnSourceOwner( &set_owner_desc ); free( found ); return STATUS_SUCCESS; } /****************************************************************************** * NtGdiDdDDIQueryAdapterInfo (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ) { if (!desc) return STATUS_INVALID_PARAMETER; FIXME( "desc %p, type %d stub\n", desc, desc->Type ); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtGdiDdDDIQueryStatistics (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIQueryStatistics( D3DKMT_QUERYSTATISTICS *stats ) { FIXME( "(%p): stub\n", stats ); return STATUS_SUCCESS; } /****************************************************************************** * NtGdiDdDDIQueryVideoMemoryInfo (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDIQueryVideoMemoryInfo( D3DKMT_QUERYVIDEOMEMORYINFO *desc ) { VkPhysicalDeviceMemoryBudgetPropertiesEXT budget; VkPhysicalDeviceMemoryProperties2 properties2; struct d3dkmt_adapter *adapter; OBJECT_BASIC_INFORMATION info; NTSTATUS status; unsigned int i; TRACE( "(%p)\n", desc ); if (!desc || !desc->hAdapter || (desc->MemorySegmentGroup != D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL && desc->MemorySegmentGroup != D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL)) return STATUS_INVALID_PARAMETER; /* FIXME: Wine currently doesn't support linked adapters */ if (desc->PhysicalAdapterIndex > 0) return STATUS_INVALID_PARAMETER; status = NtQueryObject( desc->hProcess ? desc->hProcess : GetCurrentProcess(), ObjectBasicInformation, &info, sizeof(info), NULL ); if (status != STATUS_SUCCESS) return status; if (!(info.GrantedAccess & PROCESS_QUERY_INFORMATION)) return STATUS_ACCESS_DENIED; desc->Budget = 0; desc->CurrentUsage = 0; desc->CurrentReservation = 0; desc->AvailableForReservation = 0; pthread_mutex_lock( &d3dkmt_lock ); if ((adapter = find_adapter_from_handle( desc->hAdapter )) && adapter->vk_device) { memset( &budget, 0, sizeof(budget) ); budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; properties2.pNext = &budget; pvkGetPhysicalDeviceMemoryProperties2KHR( adapter->vk_device, &properties2 ); for (i = 0; i < properties2.memoryProperties.memoryHeapCount; ++i) { if ((desc->MemorySegmentGroup == D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL && properties2.memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) || (desc->MemorySegmentGroup == D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL && !(properties2.memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT))) { desc->Budget += budget.heapBudget[i]; desc->CurrentUsage += budget.heapUsage[i]; } } desc->AvailableForReservation = desc->Budget / 2; } pthread_mutex_unlock( &d3dkmt_lock ); return adapter ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER; } /****************************************************************************** * NtGdiDdDDISetQueuedLimit (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDISetQueuedLimit( D3DKMT_SETQUEUEDLIMIT *desc ) { FIXME( "(%p): stub\n", desc ); return STATUS_NOT_IMPLEMENTED; } /****************************************************************************** * NtGdiDdDDISetVidPnSourceOwner (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDISetVidPnSourceOwner( const D3DKMT_SETVIDPNSOURCEOWNER *desc ) { struct d3dkmt_vidpn_source *source, *source2; BOOL found; UINT i; TRACE( "(%p)\n", desc ); if (!desc || !desc->hDevice || (desc->VidPnSourceCount && (!desc->pType || !desc->pVidPnSourceId))) return STATUS_INVALID_PARAMETER; pthread_mutex_lock( &d3dkmt_lock ); /* Check parameters */ for (i = 0; i < desc->VidPnSourceCount; ++i) { LIST_FOR_EACH_ENTRY( source, &d3dkmt_vidpn_sources, struct d3dkmt_vidpn_source, entry ) { if (source->id == desc->pVidPnSourceId[i]) { /* Same device */ if (source->device == desc->hDevice) { if ((source->type == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE && (desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_SHARED || desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_EMULATED)) || (source->type == D3DKMT_VIDPNSOURCEOWNER_EMULATED && desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE)) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_INVALID_PARAMETER; } } /* Different devices */ else { if ((source->type == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE || source->type == D3DKMT_VIDPNSOURCEOWNER_EMULATED) && (desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE || desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_EMULATED)) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE; } } } } /* On Windows, it seems that all video present sources are owned by DMM clients, so any attempt to set * D3DKMT_VIDPNSOURCEOWNER_SHARED come back STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE */ if (desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_SHARED) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE; } /* FIXME: D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVEGDI unsupported */ if (desc->pType[i] == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVEGDI || desc->pType[i] > D3DKMT_VIDPNSOURCEOWNER_EMULATED) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_INVALID_PARAMETER; } } /* Remove owner */ if (!desc->VidPnSourceCount && !desc->pType && !desc->pVidPnSourceId) { LIST_FOR_EACH_ENTRY_SAFE( source, source2, &d3dkmt_vidpn_sources, struct d3dkmt_vidpn_source, entry ) { if (source->device == desc->hDevice) { list_remove( &source->entry ); free( source ); } } pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_SUCCESS; } /* Add owner */ for (i = 0; i < desc->VidPnSourceCount; ++i) { found = FALSE; LIST_FOR_EACH_ENTRY( source, &d3dkmt_vidpn_sources, struct d3dkmt_vidpn_source, entry ) { if (source->device == desc->hDevice && source->id == desc->pVidPnSourceId[i]) { found = TRUE; break; } } if (found) source->type = desc->pType[i]; else { source = malloc( sizeof(*source) ); if (!source) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_NO_MEMORY; } source->id = desc->pVidPnSourceId[i]; source->type = desc->pType[i]; source->device = desc->hDevice; list_add_tail( &d3dkmt_vidpn_sources, &source->entry ); } } pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_SUCCESS; } /****************************************************************************** * NtGdiDdDDICheckVidPnExclusiveOwnership (win32u.@) */ NTSTATUS WINAPI NtGdiDdDDICheckVidPnExclusiveOwnership( const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *desc ) { struct d3dkmt_vidpn_source *source; TRACE( "(%p)\n", desc ); if (!desc || !desc->hAdapter) return STATUS_INVALID_PARAMETER; pthread_mutex_lock( &d3dkmt_lock ); LIST_FOR_EACH_ENTRY( source, &d3dkmt_vidpn_sources, struct d3dkmt_vidpn_source, entry ) { if (source->id == desc->VidPnSourceId && source->type == D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE) { pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_GRAPHICS_PRESENT_OCCLUDED; } } pthread_mutex_unlock( &d3dkmt_lock ); return STATUS_SUCCESS; }