Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/winemac.drv/cocoa_display.m | 101 +++++++++++++++++++++++++++++ dlls/winemac.drv/display.c | 134 +++++++++++++++++++++++++++++++++++++-- dlls/winemac.drv/macdrv_cocoa.h | 15 +++++ 3 files changed, 243 insertions(+), 7 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_display.m b/dlls/winemac.drv/cocoa_display.m index 82c9e3fcc3..53b1fb0a68 100644 --- a/dlls/winemac.drv/cocoa_display.m +++ b/dlls/winemac.drv/cocoa_display.m @@ -26,6 +26,8 @@ #endif #include "macdrv_cocoa.h"
+static uint64_t dedicated_gpu_id; +static uint64_t integrated_gpu_id;
/*********************************************************************** * convert_display_rect @@ -286,6 +288,7 @@ static int macdrv_get_gpus_from_metal_devices(struct macdrv_gpu** new_gpus, int* /* Hide the integrated GPU if the system default device is a dedicated GPU */ if (!primary_device.isLowPower) { + dedicated_gpu_id = primary_gpu.id; hide_integrated = TRUE; }
@@ -296,6 +299,7 @@ static int macdrv_get_gpus_from_metal_devices(struct macdrv_gpu** new_gpus, int*
if (hide_integrated && devices[i].isLowPower) { + integrated_gpu_id = gpus[gpu_count].id; continue; }
@@ -415,6 +419,7 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun * Assuming integrated GPU vendor is Intel for now */ if (gpus[gpu_count].vendor_id == 0x8086) { + integrated_gpu_id = gpus[gpu_count].id; integrated_index = gpu_count; }
@@ -433,7 +438,10 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun * Choose the first dedicated GPU as primary */ if (primary_index == integrated_index) primary_index = 0; + else if (primary_index == gpu_count - 1) + primary_index = integrated_index;
+ dedicated_gpu_id = gpus[primary_index].id; gpu_count--; }
@@ -465,6 +473,9 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun */ int macdrv_get_gpus(struct macdrv_gpu** new_gpus, int* count) { + integrated_gpu_id = 0; + dedicated_gpu_id = 0; + if (is_metal_available()) return macdrv_get_gpus_from_metal_devices(new_gpus, count); else @@ -481,3 +492,93 @@ void macdrv_free_gpus(struct macdrv_gpu* gpus) if (gpus) free(gpus); } + +/*********************************************************************** + * macdrv_get_adapters + * + * Get a list of adapters under gpu_id. The first adapter is primary if GPU is primary. + * Call macdrv_free_adapters() when you are done using the data. + * + * Returns non-zero value on failure with parameters unchanged and zero on success. + */ +int macdrv_get_adapters(uint64_t gpu_id, struct macdrv_adapter** new_adapters, int* count) +{ + CGDirectDisplayID display_ids[16]; + uint32_t display_id_count; + struct macdrv_adapter* adapters; + struct macdrv_gpu gpu; + int primary_index = 0; + int adapter_count = 0; + int ret = -1; + uint32_t i; + + if (CGGetOnlineDisplayList(sizeof(display_ids) / sizeof(display_ids[0]), display_ids, &display_id_count) + != kCGErrorSuccess) + return -1; + + if (!display_id_count) + { + *new_adapters = NULL; + *count = 0; + return 0; + } + + /* Actual adapter count may be less */ + adapters = calloc(display_id_count, sizeof(*adapters)); + if (!adapters) + return -1; + + for (i = 0; i < display_id_count; i++) + { + /* Mirrored displays are under the same adapter with primary display, so they doesn't increase adapter count */ + if (CGDisplayMirrorsDisplay(display_ids[i]) != kCGNullDirectDisplay) + continue; + + if (macdrv_get_gpu_info_from_display_id(&gpu, display_ids[i])) + goto done; + + if (gpu.id == gpu_id || (gpu_id == dedicated_gpu_id && gpu.id == integrated_gpu_id)) + { + adapters[adapter_count].id = display_ids[i]; + + if (CGDisplayIsMain(display_ids[i])) + { + adapters[adapter_count].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE; + primary_index = adapter_count; + } + + if (CGDisplayIsActive(display_ids[i])) + adapters[adapter_count].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP; + + adapter_count++; + } + } + + /* Make sure the first adapter is primary if the GPU is primary */ + if (primary_index) + { + struct macdrv_adapter tmp; + tmp = adapters[0]; + adapters[0] = adapters[primary_index]; + adapters[primary_index] = tmp; + } + + *new_adapters = adapters; + *count = adapter_count; + ret = 0; +done: + if (ret) + macdrv_free_adapters(adapters); + return ret; +} + +/*********************************************************************** + * macdrv_free_adapters + * + * Frees an adapter list allocated from macdrv_get_adapters() + */ +void macdrv_free_adapters(struct macdrv_adapter* adapters) +{ + if (adapters) + free(adapters); +} diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index 3e7335f5aa..6b2eea9ecc 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -57,6 +57,9 @@ static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d',' static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0}; static const WCHAR pciW[] = {'P','C','I',0}; static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0}; +static const WCHAR symbolic_link_valueW[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0}; +static const WCHAR gpu_idW[] = {'G','P','U','I','D',0}; +static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0}; static const WCHAR guid_fmtW[] = { '{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-', '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0}; @@ -77,6 +80,26 @@ static const WCHAR video_keyW[] = { 'H','A','R','D','W','A','R','E','\', 'D','E','V','I','C','E','M','A','P','\', 'V','I','D','E','O',0}; +static const WCHAR adapter_key_fmtW[] = { + 'S','y','s','t','e','m','\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\', + 'C','o','n','t','r','o','l','\', + 'V','i','d','e','o','\', + '%','s','\', + '%','0','4','x',0}; +static const WCHAR device_video_fmtW[] = { + '\','D','e','v','i','c','e','\', + 'V','i','d','e','o','%','d',0}; +static const WCHAR machine_prefixW[] = { + '\','R','e','g','i','s','t','r','y','\', + 'M','a','c','h','i','n','e','\',0}; +static const WCHAR nt_classW[] = { + '\','R','e','g','i','s','t','r','y','\', + 'M','a','c','h','i','n','e','\', + 'S','y','s','t','e','m','\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\', + 'C','o','n','t','r','o','l','\', + 'C','l','a','s','s','\',0};
static CFArrayRef modes; @@ -1431,11 +1454,12 @@ void macdrv_displays_changed(const macdrv_event *event) /*********************************************************************** * macdrv_init_gpu * - * Initialize a GPU instance. + * Initialize a GPU instance and return its GUID string in guid_string and driver value in driver parameter. * * Return FALSE on failure and TRUE on success. */ -static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int gpu_index) +static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int gpu_index, WCHAR *guid_string, + WCHAR *driver) { static const BOOL present = TRUE; SP_DEVINFO_DATA device_data = {sizeof(device_data)}; @@ -1478,6 +1502,13 @@ static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int goto done; RegCloseKey(hkey);
+ /* Retrieve driver value for adapters */ + if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW), + NULL)) + goto done; + lstrcpyW(driver, nt_classW); + lstrcatW(driver, bufferW); + /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */ hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
@@ -1490,6 +1521,7 @@ static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR))) goto done; } + lstrcpyW(guid_string, bufferW);
ret = TRUE; done: @@ -1499,13 +1531,80 @@ done: return ret; }
-static void prepare_devices(void) +/*********************************************************************** + * macdrv_init_adapter + * + * Initialize an adapter. + * + * Return FALSE on failure and TRUE on success. + */ +static BOOL macdrv_init_adapter(HKEY video_hkey, int video_index, int gpu_index, int adapter_index, + const struct macdrv_gpu *gpu, const WCHAR *guid_string, const WCHAR *gpu_driver, + const struct macdrv_adapter *adapter) +{ + WCHAR adapter_keyW[MAX_PATH]; + WCHAR key_nameW[MAX_PATH]; + WCHAR bufferW[1024]; + HKEY hkey = NULL; + BOOL ret = FALSE; + LSTATUS ls; + + sprintfW(key_nameW, device_video_fmtW, video_index); + lstrcpyW(bufferW, machine_prefixW); + sprintfW(adapter_keyW, adapter_key_fmtW, guid_string, adapter_index); + lstrcatW(bufferW, adapter_keyW); + + /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */ + if (RegSetValueExW(video_hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR))) + goto done; + + /* Create HKLM\System\CurrentControlSet\Control\Video{GPU GUID}{Adapter Index} link to GPU driver */ + ls = RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, + KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (ls == ERROR_ALREADY_EXISTS) + RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK, + KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (RegSetValueExW(hkey, symbolic_link_valueW, 0, REG_LINK, (const BYTE *)gpu_driver, + lstrlenW(gpu_driver) * sizeof(WCHAR))) + goto done; + RegCloseKey(hkey); + hkey = NULL; + + /* FIXME: + * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can + * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the + * device driver on Windows */ + RegCreateKeyExW(HKEY_CURRENT_CONFIG, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL); + + /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match + * them via the GUID in Device Paramters/VideoID, but it would required enumrating all GPU instances */ + sprintfW(bufferW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index); + if (RegSetValueExW(hkey, gpu_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR))) + goto done; + + /* Write StateFlags */ + if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags, + sizeof(adapter->state_flags))) + goto done; + + ret = TRUE; +done: + RegCloseKey(hkey); + if (!ret) + ERR("Failed to initialize adapter\n"); + return ret; +} + +static void prepare_devices(HKEY video_hkey) { static const BOOL not_present = FALSE; SP_DEVINFO_DATA device_data = {sizeof(device_data)}; HDEVINFO devinfo; DWORD i = 0;
+ /* Clean up old adapter keys for reinitialization */ + RegDeleteTreeW(video_hkey, NULL); + /* FIXME: * Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in * case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result @@ -1551,11 +1650,15 @@ void macdrv_init_display_devices(void) static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0}; HANDLE mutex; struct macdrv_gpu *gpus = NULL; - INT gpu_count; - INT gpu; + struct macdrv_adapter *adapters = NULL; + INT gpu_count, adapter_count; + INT gpu, adapter; HDEVINFO gpu_devinfo = NULL; HKEY video_hkey = NULL; + INT video_index = 0; DWORD disposition = 0; + WCHAR guidW[40]; + WCHAR driverW[1024];
mutex = CreateMutexW(NULL, FALSE, init_mutexW); WaitForSingleObject(mutex, INFINITE); @@ -1573,7 +1676,7 @@ void macdrv_init_display_devices(void)
TRACE("\n");
- prepare_devices(); + prepare_devices(video_hkey);
gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
@@ -1583,8 +1686,24 @@ void macdrv_init_display_devices(void)
for (gpu = 0; gpu < gpu_count; gpu++) { - if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu)) + if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW)) goto done; + + /* Initialize adapters */ + if (macdrv_get_adapters(gpus[gpu].id, &adapters, &adapter_count)) + goto done; + + for (adapter = 0; adapter < adapter_count; adapter++) + { + if (!macdrv_init_adapter(video_hkey, video_index, gpu, adapter, &gpus[gpu], guidW, driverW, + &adapters[adapter])) + goto done; + + video_index++; + } + + macdrv_free_adapters(adapters); + adapters = NULL; }
done: @@ -1596,4 +1715,5 @@ done: CloseHandle(mutex);
macdrv_free_gpus(gpus); + macdrv_free_adapters(adapters); } diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 513746fa0e..b960e902e3 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -258,6 +258,10 @@ static inline CGPoint cgpoint_win_from_mac(CGPoint point)
/* display */
+/* Used DISPLAY_DEVICE.StateFlags for adapters */ +#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001 +#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004 + /* Represent a physical GPU in the PCI slots */ struct macdrv_gpu { @@ -272,12 +276,23 @@ static inline CGPoint cgpoint_win_from_mac(CGPoint point) uint32_t revision_id; };
+/* Represent an adapter in EnumDisplayDevices context */ +struct macdrv_adapter +{ + /* ID to uniquely identify an adapter. Currently it's a CGDirectDisplayID */ + uint32_t id; + /* as StateFlags in DISPLAY_DEVICE struct */ + uint32_t state_flags; +}; + extern int macdrv_get_displays(struct macdrv_display** displays, int* count) DECLSPEC_HIDDEN; extern void macdrv_free_displays(struct macdrv_display* displays) DECLSPEC_HIDDEN; extern int macdrv_set_display_mode(const struct macdrv_display* display, CGDisplayModeRef display_mode) DECLSPEC_HIDDEN; extern int macdrv_get_gpus(struct macdrv_gpu** gpus, int* count) DECLSPEC_HIDDEN; extern void macdrv_free_gpus(struct macdrv_gpu* gpus) DECLSPEC_HIDDEN; +extern int macdrv_get_adapters(uint64_t gpu_id, struct macdrv_adapter** adapters, int* count) DECLSPEC_HIDDEN; +extern void macdrv_free_adapters(struct macdrv_adapter* adapters) DECLSPEC_HIDDEN;
/* event */