mirror of
https://github.com/libretro/RetroArch
synced 2025-02-07 03:40:24 +00:00
533 lines
17 KiB
C++
533 lines
17 KiB
C++
|
/**************************************************************
|
||
|
|
||
|
custom_video_adl.cpp - ATI/AMD ADL library
|
||
|
|
||
|
---------------------------------------------------------
|
||
|
|
||
|
Switchres Modeline generation engine for emulation
|
||
|
|
||
|
License GPL-2.0+
|
||
|
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
|
||
|
Alexandre Wodarczyk, Gil Delescluse
|
||
|
|
||
|
**************************************************************/
|
||
|
|
||
|
// Constants and structures ported from AMD ADL SDK files
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <stdio.h>
|
||
|
#include "custom_video_adl.h"
|
||
|
#include "log.h"
|
||
|
|
||
|
|
||
|
//============================================================
|
||
|
// memory allocation callbacks
|
||
|
//============================================================
|
||
|
|
||
|
void* __stdcall ADL_Main_Memory_Alloc(int iSize)
|
||
|
{
|
||
|
void* lpBuffer = malloc(iSize);
|
||
|
return lpBuffer;
|
||
|
}
|
||
|
|
||
|
void __stdcall ADL_Main_Memory_Free(void** lpBuffer)
|
||
|
{
|
||
|
if (NULL != *lpBuffer)
|
||
|
{
|
||
|
free(*lpBuffer);
|
||
|
*lpBuffer = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::adl_timing
|
||
|
//============================================================
|
||
|
|
||
|
adl_timing::adl_timing(char *display_name, custom_video_settings *vs)
|
||
|
{
|
||
|
m_vs = *vs;
|
||
|
strcpy (m_display_name, display_name);
|
||
|
strcpy (m_device_key, m_vs.device_reg_key);
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::~adl_timing
|
||
|
//============================================================
|
||
|
|
||
|
adl_timing::~adl_timing()
|
||
|
{
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::init
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::init()
|
||
|
{
|
||
|
int ADL_Err = ADL_ERR;
|
||
|
|
||
|
log_verbose("ATI/AMD ADL init\n");
|
||
|
|
||
|
ADL_Err = open();
|
||
|
if (ADL_Err != ADL_OK)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL Initialization error!\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ADL2_Adapter_NumberOfAdapters_Get = (ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_NumberOfAdapters_Get");
|
||
|
if (ADL2_Adapter_NumberOfAdapters_Get == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Adapter_NumberOfAdapters_Get not available!");
|
||
|
return false;
|
||
|
}
|
||
|
ADL2_Adapter_AdapterInfo_Get = (ADL2_ADAPTER_ADAPTERINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_AdapterInfo_Get");
|
||
|
if (ADL2_Adapter_AdapterInfo_Get == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Adapter_AdapterInfo_Get not available!");
|
||
|
return false;
|
||
|
}
|
||
|
ADL2_Display_DisplayInfo_Get = (ADL2_DISPLAY_DISPLAYINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_DisplayInfo_Get");
|
||
|
if (ADL2_Display_DisplayInfo_Get == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Display_DisplayInfo_Get not available!");
|
||
|
return false;
|
||
|
}
|
||
|
ADL2_Display_ModeTimingOverride_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Get");
|
||
|
if (ADL2_Display_ModeTimingOverride_Get == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Get not available!");
|
||
|
return false;
|
||
|
}
|
||
|
ADL2_Display_ModeTimingOverride_Set = (ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Set");
|
||
|
if (ADL2_Display_ModeTimingOverride_Set == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Set not available!");
|
||
|
return false;
|
||
|
}
|
||
|
ADL2_Display_ModeTimingOverrideList_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverrideList_Get");
|
||
|
if (ADL2_Display_ModeTimingOverrideList_Get == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Display_ModeTimingOverrideList_Get not available!");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ADL2_Flush_Driver_Data = (ADL2_FLUSH_DRIVER_DATA) (void *) GetProcAddress(hDLL,"ADL2_Flush_Driver_Data");
|
||
|
if (ADL2_Flush_Driver_Data == NULL)
|
||
|
{
|
||
|
log_verbose("ERROR: ADL2_Flush_Driver_Data not available!");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!enum_displays())
|
||
|
{
|
||
|
log_error("ADL error enumerating displays.\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!get_device_mapping_from_display_name())
|
||
|
{
|
||
|
log_error("ADL error mapping display.\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!get_driver_version(m_device_key))
|
||
|
{
|
||
|
log_error("ADL driver version unknown!.\n");
|
||
|
}
|
||
|
|
||
|
if (!get_timing_list())
|
||
|
{
|
||
|
log_error("ADL error getting list of timing overrides.\n");
|
||
|
}
|
||
|
|
||
|
log_verbose("ADL functions retrieved successfully.\n");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::adl_open
|
||
|
//============================================================
|
||
|
|
||
|
int adl_timing::open()
|
||
|
{
|
||
|
ADL2_MAIN_CONTROL_CREATE ADL2_Main_Control_Create;
|
||
|
int ADL_Err = ADL_ERR;
|
||
|
|
||
|
hDLL = LoadLibraryA("atiadlxx.dll");
|
||
|
if (hDLL == NULL) hDLL = LoadLibraryA("atiadlxy.dll");
|
||
|
|
||
|
if (hDLL != NULL)
|
||
|
{
|
||
|
ADL2_Main_Control_Create = (ADL2_MAIN_CONTROL_CREATE) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Create");
|
||
|
if (ADL2_Main_Control_Create != NULL)
|
||
|
ADL_Err = ADL2_Main_Control_Create(ADL_Main_Memory_Alloc, 1, &m_adl);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
log_verbose("ADL Library not found!\n");
|
||
|
}
|
||
|
|
||
|
return ADL_Err;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::close
|
||
|
//============================================================
|
||
|
|
||
|
void adl_timing::close()
|
||
|
{
|
||
|
ADL2_MAIN_CONTROL_DESTROY ADL2_Main_Control_Destroy;
|
||
|
|
||
|
log_verbose("ATI/AMD ADL close\n");
|
||
|
|
||
|
for (int i = 0; i <= iNumberAdapters - 1; i++)
|
||
|
ADL_Main_Memory_Free((void **)&lpAdapter[i].m_display_list);
|
||
|
|
||
|
ADL_Main_Memory_Free((void **)&lpAdapterInfo);
|
||
|
ADL_Main_Memory_Free((void **)&lpAdapter);
|
||
|
|
||
|
ADL2_Main_Control_Destroy = (ADL2_MAIN_CONTROL_DESTROY) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Destroy");
|
||
|
if (ADL2_Main_Control_Destroy != NULL)
|
||
|
ADL2_Main_Control_Destroy(m_adl);
|
||
|
|
||
|
FreeLibrary(hDLL);
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::get_driver_version
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::get_driver_version(char *device_key)
|
||
|
{
|
||
|
HKEY hkey;
|
||
|
bool found = false;
|
||
|
|
||
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, device_key, 0, KEY_READ , &hkey) == ERROR_SUCCESS)
|
||
|
{
|
||
|
BYTE cat_ver[32];
|
||
|
DWORD length = sizeof(cat_ver);
|
||
|
if ((RegQueryValueExA(hkey, "Catalyst_Version", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
|
||
|
(RegQueryValueExA(hkey, "RadeonSoftwareVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
|
||
|
(RegQueryValueExA(hkey, "DriverVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS))
|
||
|
{
|
||
|
found = true;
|
||
|
is_patched = (RegQueryValueExA(hkey, "CalamityRelease", NULL, NULL, NULL, NULL) == ERROR_SUCCESS);
|
||
|
sscanf((char *)cat_ver, "%d.%d", &cat_version, &sub_version);
|
||
|
log_verbose("AMD driver version %d.%d%s\n", cat_version, sub_version, is_patched? "(patched)":"");
|
||
|
}
|
||
|
RegCloseKey(hkey);
|
||
|
}
|
||
|
return found;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::enum_displays
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::enum_displays()
|
||
|
{
|
||
|
ADL2_Adapter_NumberOfAdapters_Get(m_adl, &iNumberAdapters);
|
||
|
|
||
|
lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
|
||
|
memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
|
||
|
ADL2_Adapter_AdapterInfo_Get(m_adl, lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
|
||
|
|
||
|
lpAdapter = (LPAdapterList)malloc(sizeof(AdapterList) * iNumberAdapters);
|
||
|
for (int i = 0; i <= iNumberAdapters - 1; i++)
|
||
|
{
|
||
|
lpAdapter[i].m_index = lpAdapterInfo[i].iAdapterIndex;
|
||
|
lpAdapter[i].m_bus = lpAdapterInfo[i].iBusNumber;
|
||
|
memcpy(&lpAdapter[i].m_name, &lpAdapterInfo[i].strAdapterName, ADL_MAX_PATH);
|
||
|
memcpy(&lpAdapter[i].m_display_name, &lpAdapterInfo[i].strDisplayName, ADL_MAX_PATH);
|
||
|
lpAdapter[i].m_num_of_displays = 0;
|
||
|
lpAdapter[i].m_display_list = 0;
|
||
|
|
||
|
// Only get display info from target adapter (this api is very slow!)
|
||
|
if (!strcmp(lpAdapter[i].m_display_name, m_display_name))
|
||
|
ADL2_Display_DisplayInfo_Get(m_adl, lpAdapter[i].m_index, &lpAdapter[i].m_num_of_displays, &lpAdapter[i].m_display_list, 1);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::get_device_mapping_from_display_name
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::get_device_mapping_from_display_name()
|
||
|
{
|
||
|
for (int i = 0; i <= iNumberAdapters -1; i++)
|
||
|
{
|
||
|
if (!strcmp(m_display_name, lpAdapter[i].m_display_name))
|
||
|
{
|
||
|
ADLDisplayInfo *display_list;
|
||
|
display_list = lpAdapter[i].m_display_list;
|
||
|
|
||
|
for (int j = 0; j <= lpAdapter[i].m_num_of_displays - 1; j++)
|
||
|
{
|
||
|
if (lpAdapter[i].m_index == display_list[j].displayID.iDisplayLogicalAdapterIndex)
|
||
|
{
|
||
|
m_adapter_index = lpAdapter[i].m_index;
|
||
|
m_display_index = display_list[j].displayID.iDisplayLogicalIndex;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::display_mode_info_to_modeline
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m)
|
||
|
{
|
||
|
if (dmi->sDetailedTiming.sHTotal == 0) return false;
|
||
|
|
||
|
ADLDetailedTiming dt;
|
||
|
memcpy(&dt, &dmi->sDetailedTiming, sizeof(ADLDetailedTiming));
|
||
|
|
||
|
if (dt.sHTotal == 0) return false;
|
||
|
|
||
|
m->htotal = dt.sHTotal;
|
||
|
m->hactive = dt.sHDisplay;
|
||
|
m->hbegin = dt.sHSyncStart;
|
||
|
m->hend = dt.sHSyncWidth + m->hbegin;
|
||
|
m->vtotal = dt.sVTotal;
|
||
|
m->vactive = dt.sVDisplay;
|
||
|
m->vbegin = dt.sVSyncStart;
|
||
|
m->vend = dt.sVSyncWidth + m->vbegin;
|
||
|
m->interlace = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED)? 1 : 0;
|
||
|
m->doublescan = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_DOUBLE_SCAN)? 1 : 0;
|
||
|
m->hsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_H_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1);
|
||
|
m->vsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_V_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1) ;
|
||
|
m->pclock = dt.sPixelClock * 10000;
|
||
|
|
||
|
m->height = m->height? m->height : dmi->iPelsHeight;
|
||
|
m->width = m->width? m->width : dmi->iPelsWidth;
|
||
|
m->refresh = m->refresh? m->refresh : dmi->iRefreshRate / interlace_factor(m->interlace, 1);;
|
||
|
m->hfreq = float(m->pclock / m->htotal);
|
||
|
m->vfreq = float(m->hfreq / m->vtotal) * (m->interlace? 2 : 1);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::get_timing_list
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::get_timing_list()
|
||
|
{
|
||
|
if (ADL2_Display_ModeTimingOverrideList_Get(m_adl, m_adapter_index, m_display_index, MAX_MODELINES, adl_mode, &m_num_of_adl_modes) != ADL_OK) return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::get_timing_from_cache
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::get_timing_from_cache(modeline *m)
|
||
|
{
|
||
|
ADLDisplayModeInfo *mode = 0;
|
||
|
|
||
|
for (int i = 0; i < m_num_of_adl_modes; i++)
|
||
|
{
|
||
|
mode = &adl_mode[i];
|
||
|
if (mode->iPelsWidth == m->width && mode->iPelsHeight == m->height && mode->iRefreshRate == m->refresh)
|
||
|
{
|
||
|
if ((m->interlace) && !(mode->sDetailedTiming.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED))
|
||
|
continue;
|
||
|
goto found;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
|
||
|
found:
|
||
|
if (display_mode_info_to_modeline(mode, m)) return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::get_timing
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::get_timing(modeline *m)
|
||
|
{
|
||
|
ADLDisplayMode mode_in;
|
||
|
ADLDisplayModeInfo mode_info_out;
|
||
|
modeline m_temp = *m;
|
||
|
|
||
|
//modeline to ADLDisplayMode
|
||
|
mode_in.iPelsHeight = m->height;
|
||
|
mode_in.iPelsWidth = m->width;
|
||
|
mode_in.iBitsPerPel = 32;
|
||
|
mode_in.iDisplayFrequency = m->refresh * interlace_factor(m->interlace, 1);
|
||
|
|
||
|
if (ADL2_Display_ModeTimingOverride_Get(m_adl, m_adapter_index, m_display_index, &mode_in, &mode_info_out) != ADL_OK) goto not_found;
|
||
|
if (display_mode_info_to_modeline(&mode_info_out, &m_temp))
|
||
|
{
|
||
|
if (m_temp.interlace == m->interlace)
|
||
|
{
|
||
|
memcpy(m, &m_temp, sizeof(modeline));
|
||
|
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
not_found:
|
||
|
|
||
|
// Try to get timing from our cache (interlaced modes are not properly retrieved by ADL_Display_ModeTimingOverride_Get)
|
||
|
if (get_timing_from_cache(m))
|
||
|
{
|
||
|
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::set_timing
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::set_timing(modeline *m)
|
||
|
{
|
||
|
return set_timing_override(m, TIMING_UPDATE);
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::set_timing_override
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::set_timing_override(modeline *m, int update_mode)
|
||
|
{
|
||
|
ADLDisplayModeInfo mode_info = {};
|
||
|
ADLDetailedTiming *dt;
|
||
|
modeline m_temp;
|
||
|
|
||
|
//modeline to ADLDisplayModeInfo
|
||
|
mode_info.iTimingStandard = (update_mode & TIMING_DELETE)? ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT : ADL_DL_MODETIMING_STANDARD_CUSTOM;
|
||
|
mode_info.iPossibleStandard = 0;
|
||
|
mode_info.iRefreshRate = m->refresh * interlace_factor(m->interlace, 0);
|
||
|
mode_info.iPelsWidth = m->width;
|
||
|
mode_info.iPelsHeight = m->height;
|
||
|
|
||
|
//modeline to ADLDetailedTiming
|
||
|
dt = &mode_info.sDetailedTiming;
|
||
|
dt->sTimingFlags = (m->interlace? ADL_DL_TIMINGFLAG_INTERLACED : 0) |
|
||
|
(m->doublescan? ADL_DL_TIMINGFLAG_DOUBLE_SCAN: 0) |
|
||
|
(m->hsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_H_SYNC_POLARITY : 0) |
|
||
|
(m->vsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_V_SYNC_POLARITY : 0);
|
||
|
dt->sHTotal = m->htotal;
|
||
|
dt->sHDisplay = m->hactive;
|
||
|
dt->sHSyncStart = m->hbegin;
|
||
|
dt->sHSyncWidth = m->hend - m->hbegin;
|
||
|
dt->sVTotal = m->vtotal;
|
||
|
dt->sVDisplay = m->vactive;
|
||
|
dt->sVSyncStart = m->vbegin;
|
||
|
dt->sVSyncWidth = m->vend - m->vbegin;
|
||
|
dt->sPixelClock = m->pclock / 10000;
|
||
|
dt->sHOverscanRight = 0;
|
||
|
dt->sHOverscanLeft = 0;
|
||
|
dt->sVOverscanBottom = 0;
|
||
|
dt->sVOverscanTop = 0;
|
||
|
|
||
|
if (ADL2_Display_ModeTimingOverride_Set(m_adl, m_adapter_index, m_display_index, &mode_info, (update_mode & TIMING_UPDATE_LIST)? 1 : 0) != ADL_OK) return false;
|
||
|
|
||
|
//ADL2_Flush_Driver_Data(m_adl, m_adapter_index);
|
||
|
|
||
|
// read modeline to trigger timing refresh on modded drivers
|
||
|
memcpy(&m_temp, m, sizeof(modeline));
|
||
|
if (update_mode & TIMING_UPDATE) get_timing(&m_temp);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::add_mode
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::add_mode(modeline *mode)
|
||
|
{
|
||
|
if (!set_timing_override(mode, TIMING_UPDATE_LIST))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_resync.wait();
|
||
|
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::delete_mode
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::delete_mode(modeline *mode)
|
||
|
{
|
||
|
if (!set_timing_override(mode, TIMING_DELETE | TIMING_UPDATE_LIST))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_resync.wait();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::update_mode
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::update_mode(modeline *mode)
|
||
|
{
|
||
|
bool refresh_required = !is_patched || (mode->type & MODE_DESKTOP);
|
||
|
|
||
|
if (!set_timing_override(mode, refresh_required? TIMING_UPDATE_LIST : TIMING_UPDATE))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (refresh_required) m_resync.wait();
|
||
|
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//============================================================
|
||
|
// adl_timing::process_modelist
|
||
|
//============================================================
|
||
|
|
||
|
bool adl_timing::process_modelist(std::vector<modeline *> modelist)
|
||
|
{
|
||
|
bool refresh_required = false;
|
||
|
bool error = false;
|
||
|
|
||
|
for (auto &mode : modelist)
|
||
|
{
|
||
|
if (mode->type & MODE_DELETE || mode->type & MODE_ADD || (mode->type & MODE_UPDATE && (!is_patched || (mode->type & MODE_DESKTOP))))
|
||
|
refresh_required = true;
|
||
|
|
||
|
bool is_last = (mode == modelist.back());
|
||
|
|
||
|
if (!set_timing_override(mode, (mode->type & MODE_DELETE? TIMING_DELETE : TIMING_UPDATE) | (is_last && refresh_required? TIMING_UPDATE_LIST : 0)))
|
||
|
{
|
||
|
mode->type |= MODE_ERROR;
|
||
|
error = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mode->type &= ~MODE_ERROR;
|
||
|
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (refresh_required) m_resync.wait();
|
||
|
return !error;
|
||
|
}
|