1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00
wine/dlls/win32u/d3dkmt.c

547 lines
19 KiB
C

/*
* 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 <pthread.h>
#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;
}