2021-06-12 21:44:36 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Runtime.InteropServices ;
using ATI.ADL ;
2021-06-14 09:42:16 +00:00
using Microsoft.Win32.SafeHandles ;
using DisplayMagicianShared ;
2021-06-12 21:44:36 +00:00
namespace DisplayMagicianShared.AMD
{
2021-06-14 09:42:16 +00:00
public class ADLWrapper : IDisposable
2021-06-12 21:44:36 +00:00
{
2021-06-14 09:42:16 +00:00
// Static members are 'eagerly initialized', that is,
// immediately when class is loaded for the first time.
// .NET guarantees thread safety for static initialization
private static ADLWrapper _instance = new ADLWrapper ( ) ;
2021-06-18 00:33:46 +00:00
2021-06-14 09:42:16 +00:00
private bool _initialised = false ;
2021-06-18 00:33:46 +00:00
private bool _initialisedADL2 = false ;
2021-06-14 09:42:16 +00:00
// To detect redundant calls
private bool _disposed = false ;
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle ( IntPtr . Zero , true ) ;
2021-06-18 00:33:46 +00:00
private IntPtr _adlContextHandle = IntPtr . Zero ;
2021-06-14 09:42:16 +00:00
static ADLWrapper ( ) { }
2021-06-12 21:44:36 +00:00
public ADLWrapper ( )
{
2021-06-18 00:33:46 +00:00
int ADLRet = ADL . ADL_ERR ;
2021-06-12 21:44:36 +00:00
2021-06-14 09:42:16 +00:00
SharedLogger . logger . Trace ( "ADLWrapper/ADLWrapper: Intialising ADL library" ) ;
try
2021-06-12 21:44:36 +00:00
{
2021-06-14 09:42:16 +00:00
if ( ADL . ADL_Main_Control_Create ! = null )
2021-06-12 21:44:36 +00:00
{
2021-06-14 09:42:16 +00:00
// Second parameter is 1: Get only the present adapters
ADLRet = ADL . ADL_Main_Control_Create ( ADL . ADL_Main_Memory_Alloc , 1 ) ;
2021-06-12 21:44:36 +00:00
}
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-14 09:42:16 +00:00
{
_initialised = true ;
SharedLogger . logger . Trace ( "ADLWrapper/ADLWrapper: ADL library was initialised successfully" ) ;
}
else
{
SharedLogger . logger . Error ( "ADLWrapper/ADLWrapper: Error intialising ADL library. ADL_Main_Control_Create() returned error code " + ADLRet . ToString ( ) ) ;
}
}
catch ( Exception ex )
{
SharedLogger . logger . Error ( "ADLWrapper/ADLWrapper: Exception intialising ADL library. ADL_Main_Control_Create() caused an exception" ) ;
}
2021-06-18 00:33:46 +00:00
try
{
if ( ADL . ADL2_Main_Control_Create ! = null )
{
// Second parameter is 1: Get only the present adapters
ADLRet = ADL . ADL2_Main_Control_Create ( ADL . ADL_Main_Memory_Alloc , 1 , out _adlContextHandle ) ;
}
if ( ADLRet = = ADL . ADL_OK )
{
_initialisedADL2 = true ;
SharedLogger . logger . Trace ( "ADLWrapper/ADLWrapper: ADL2 library was initialised successfully" ) ;
}
else
{
SharedLogger . logger . Error ( "ADLWrapper/ADLWrapper: Error intialising ADL2 library. ADL2_Main_Control_Create() returned error code " + ADL . ConvertADLReturnValueIntoWords ( ADLRet ) ) ;
}
}
catch ( Exception ex )
{
SharedLogger . logger . Error ( "ADLWrapper/ADLWrapper: Exception intialising ADL2 library. ADL2_Main_Control_Create() caused an exception" ) ;
}
2021-06-14 09:42:16 +00:00
}
~ ADLWrapper ( )
{
// If the ADL library was initialised, then we need to free it up.
if ( _initialised )
{
if ( null ! = ADL . ADL_Main_Control_Destroy )
ADL . ADL_Main_Control_Destroy ( ) ;
2021-06-18 00:33:46 +00:00
}
// If the ADL2 library was initialised, then we need to free it up.
if ( _initialisedADL2 )
{
if ( null ! = ADL . ADL2_Main_Control_Destroy )
ADL . ADL2_Main_Control_Destroy ( _adlContextHandle ) ;
}
2021-06-14 09:42:16 +00:00
}
// Public implementation of Dispose pattern callable by consumers.
public void Dispose ( ) = > Dispose ( true ) ;
// Protected implementation of Dispose pattern.
protected virtual void Dispose ( bool disposing )
{
if ( _disposed )
{
return ;
}
if ( disposing )
{
if ( null ! = ADL . ADL_Main_Control_Destroy )
ADL . ADL_Main_Control_Destroy ( ) ;
// Dispose managed state (managed objects).
_safeHandle ? . Dispose ( ) ;
}
_disposed = true ;
}
public bool IsInstalled
{
get { return _initialised ; }
}
public static ADLWrapper GetLibrary ( )
{
return _instance ;
}
2021-06-15 09:55:15 +00:00
public List < string > GenerateProfileDisplayIdentifiers ( )
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Getting AMD active adapter count" ) ;
2021-06-18 00:33:46 +00:00
int ADLRet = ADL . ADL_ERR ;
2021-06-15 09:55:15 +00:00
int NumberOfAdapters = 0 ;
int NumberOfDisplays = 0 ;
2021-06-17 06:51:48 +00:00
List < string > displayIdentifiers = new List < string > ( ) ;
2021-06-18 00:33:46 +00:00
// Get the DisplayMap info (Desktops) for all adapters on the machine in one go
2021-06-17 09:40:10 +00:00
// Keep a list of things we want to track
2021-06-18 00:33:46 +00:00
/ * List < ADLDisplayMap > allDisplayMaps = new List < ADLDisplayMap > ( ) ;
2021-06-17 09:40:10 +00:00
List < ADLDisplayTarget > allDisplayTargets = new List < ADLDisplayTarget > ( ) ;
if ( ADL . ADL_Display_DisplayMapConfig_Get ! = null )
{
IntPtr DisplayMapBuffer = IntPtr . Zero ;
IntPtr DisplayTargetBuffer = IntPtr . Zero ;
int numDisplayMaps = 0 ;
int numDisplayTargets = 0 ;
2021-06-18 00:33:46 +00:00
// Get the DisplayMap info (Desktops) for all adapters on the machine in one go
2021-06-17 09:40:10 +00:00
ADLRet = ADL . ADL_Display_DisplayMapConfig_Get ( - 1 , out numDisplayMaps , out DisplayMapBuffer , out numDisplayTargets , out DisplayTargetBuffer , 0 ) ;
if ( ADLRet = = ADL . ADL_SUCCESS )
{
2021-06-18 00:33:46 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Number Of DisplayMaps (Desktops): {numDisplayMaps.ToString()} " ) ;
2021-06-17 09:40:10 +00:00
// Marshal the Display Maps
ADLDisplayMap oneDisplayMap = new ADLDisplayMap ( ) ;
for ( int displayMapNum = 0 ; displayMapNum < numDisplayMaps ; displayMapNum + + )
{
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayMap = ( ADLDisplayMap ) Marshal . PtrToStructure ( new IntPtr ( DisplayMapBuffer . ToInt64 ( ) + ( displayMapNum * Marshal . SizeOf ( oneDisplayMap ) ) ) , oneDisplayMap . GetType ( ) ) ;
allDisplayMaps . Add ( oneDisplayMap ) ;
}
//Marshall the DisplayTargets
ADLDisplayTarget oneDisplayTarget = new ADLDisplayTarget ( ) ;
for ( int displayTargetNum = 0 ; displayTargetNum < numDisplayTargets ; displayTargetNum + + )
{
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayTarget = ( ADLDisplayTarget ) Marshal . PtrToStructure ( new IntPtr ( DisplayMapBuffer . ToInt64 ( ) + ( displayTargetNum * Marshal . SizeOf ( oneDisplayTarget ) ) ) , oneDisplayTarget . GetType ( ) ) ;
allDisplayTargets . Add ( oneDisplayTarget ) ;
}
}
}
if ( ADL . ADL_Display_DisplayMapConfig_PossibleAddAndRemove ! = null )
{
int numDisplayMap = 1 ;
int numDisplayTarget = 1 ;
IntPtr PossibleAddTargetBuffer = IntPtr . Zero ;
IntPtr PossibleRemoveTargetBuffer = IntPtr . Zero ;
int numPossibleAddTargets = 0 ;
int numPossibleRemoveTargets = 0 ;
List < ADLDisplayTarget > allPossibleAddDisplayTargets = new List < ADLDisplayTarget > ( ) ;
List < ADLDisplayTarget > allPossibleRemoveDisplayTargets = new List < ADLDisplayTarget > ( ) ;
// Force the display detection and get the Display Info. Use 0 as last parameter to NOT force detection
ADLRet = ADL . ADL_Display_DisplayMapConfig_PossibleAddAndRemove ( 0 , numDisplayMap , allDisplayMaps [ 0 ] , numDisplayTarget , allDisplayTargets [ 0 ] , out numPossibleAddTargets , out PossibleAddTargetBuffer , out numPossibleRemoveTargets , out PossibleRemoveTargetBuffer ) ;
if ( ADLRet = = ADL . ADL_SUCCESS )
{
// Marshal the Possible Add Targets
ADLDisplayTarget oneDisplayTarget = new ADLDisplayTarget ( ) ;
for ( int displayTargetNum = 0 ; displayTargetNum < numPossibleAddTargets ; displayTargetNum + + )
{
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayTarget = ( ADLDisplayTarget ) Marshal . PtrToStructure ( new IntPtr ( PossibleAddTargetBuffer . ToInt64 ( ) + ( displayTargetNum * Marshal . SizeOf ( oneDisplayTarget ) ) ) , oneDisplayTarget . GetType ( ) ) ;
allPossibleAddDisplayTargets . Add ( oneDisplayTarget ) ;
}
2021-06-18 00:33:46 +00:00
// Marshal the Possible Remove Targets too
2021-06-17 09:40:10 +00:00
for ( int displayTargetNum = 0 ; displayTargetNum < numPossibleRemoveTargets ; displayTargetNum + + )
{
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayTarget = ( ADLDisplayTarget ) Marshal . PtrToStructure ( new IntPtr ( PossibleRemoveTargetBuffer . ToInt64 ( ) + ( displayTargetNum * Marshal . SizeOf ( oneDisplayTarget ) ) ) , oneDisplayTarget . GetType ( ) ) ;
allPossibleAddDisplayTargets . Add ( oneDisplayTarget ) ;
}
}
2021-06-18 00:33:46 +00:00
} * /
2021-06-17 09:40:10 +00:00
2021-06-18 06:02:14 +00:00
if ( null ! = ADL . ADL2_Adapter_NumberOfAdapters_Get )
2021-06-15 09:55:15 +00:00
{
2021-06-18 06:02:14 +00:00
ADL . ADL2_Adapter_NumberOfAdapters_Get ( _adlContextHandle , ref NumberOfAdapters ) ;
2021-06-15 09:55:15 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Number Of Adapters: {NumberOfAdapters.ToString()} " ) ;
}
if ( NumberOfAdapters > 0 )
{
// Get OS adpater info from ADL
2021-06-18 06:02:14 +00:00
ADLAdapterInfoX2Array OSAdapterInfoData ;
OSAdapterInfoData = new ADLAdapterInfoX2Array ( ) ;
2021-06-15 09:55:15 +00:00
IntPtr AdapterBuffer = IntPtr . Zero ;
2021-06-18 06:02:14 +00:00
if ( ADL . ADL2_Adapter_AdapterInfoX4_Get ! = null )
2021-06-15 09:55:15 +00:00
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: ADL_Adapter_AdapterInfo_Get DLL function exists." ) ;
// Figure out the size of the AdapterBuffer we need to build
int size = Marshal . SizeOf ( OSAdapterInfoData ) ;
AdapterBuffer = Marshal . AllocCoTaskMem ( ( int ) size ) ;
Marshal . StructureToPtr ( OSAdapterInfoData , AdapterBuffer , false ) ;
// Get the Adapter info and put it in the AdapterBuffer
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Running ADL_Adapter_AdapterInfo_Get to find all known AMD adapters." ) ;
2021-06-18 06:02:14 +00:00
//ADLRet = ADL.ADL2_Adapter_AdapterInfoX4_Get(_adlContextHandle, AdapterBuffer, size);
int numAdapters = 0 ;
ADLRet = ADL . ADL2_Adapter_AdapterInfoX4_Get ( _adlContextHandle , ADL . ADL_ADAPTER_INDEX_ALL , out numAdapters , out AdapterBuffer ) ;
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-15 09:55:15 +00:00
{
// Use the AdapterBuffer pointer to marshal the OS Adapter Info into a structure
2021-06-18 06:02:14 +00:00
OSAdapterInfoData = ( ADLAdapterInfoX2Array ) Marshal . PtrToStructure ( AdapterBuffer , OSAdapterInfoData . GetType ( ) ) ;
2021-06-15 09:55:15 +00:00
int IsActive = ADL . ADL_TRUE ; // We only want to search for active adapters
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Successfully run ADL_Adapter_AdapterInfo_Get to find information about all known AMD adapters." ) ;
// Go through each adapter
for ( int i = 0 ; i < NumberOfAdapters ; i + + )
{
2021-06-18 06:02:14 +00:00
ADLAdapterInfoX2 oneAdapter = OSAdapterInfoData . ADLAdapterInfoX2 [ i ] ;
2021-06-18 00:33:46 +00:00
2021-06-15 09:55:15 +00:00
// Check if the adapter is active
if ( ADL . ADL_Adapter_Active_Get ! = null )
2021-06-18 00:33:46 +00:00
ADLRet = ADL . ADL_Adapter_Active_Get ( oneAdapter . AdapterIndex , ref IsActive ) ;
2021-06-15 09:55:15 +00:00
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-15 09:55:15 +00:00
{
// Only continue if the adapter is enabled
if ( IsActive ! = ADL . ADL_TRUE )
{
2021-06-18 00:33:46 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} isn't active ({oneAdapter.AdapterName})." ) ;
2021-06-15 09:55:15 +00:00
continue ;
}
2021-06-18 00:33:46 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} is active! ({oneAdapter.AdapterName})." ) ;
Console . WriteLine ( $"### Adapter Info for Adapter #{oneAdapter.AdapterIndex} ###" ) ;
Console . WriteLine ( $"Adapter ID = {oneAdapter}" ) ;
2021-06-15 09:55:15 +00:00
// Get the unique identifier from the Adapter
int AdapterID = 0 ;
if ( ADL . ADL_Adapter_ID_Get ! = null )
{
2021-06-18 00:33:46 +00:00
ADLRet = ADL . ADL_Adapter_ID_Get ( oneAdapter . AdapterIndex , ref AdapterID ) ;
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} ({oneAdapter.AdapterName}) AdapterID is {AdapterID.ToString()}" ) ;
2021-06-15 09:55:15 +00:00
}
2021-06-16 10:15:15 +00:00
// Get the Adapter Capabilities
ADLAdapterCapsX2 AdapterCapabilities = new ADLAdapterCapsX2 ( ) ;
if ( ADL . ADL_AdapterX2_Caps ! = null )
2021-06-15 09:55:15 +00:00
{
2021-06-18 00:33:46 +00:00
ADLRet = ADL . ADL_AdapterX2_Caps ( oneAdapter . AdapterIndex , out AdapterCapabilities ) ;
2021-06-15 09:55:15 +00:00
}
2021-06-16 10:15:15 +00:00
//ADLAdapterCapsX2 AdapterCapabilities = (ADLAdapterCapsX2)Marshal.PtrToStructure(AdapterCapabilitiesBuffer, typeof(ADLAdapterCapsX2));
2021-06-18 00:33:46 +00:00
Console . WriteLine ( $"### Adapter Capabilities for Adapter #{oneAdapter.AdapterIndex} ###" ) ;
Console . WriteLine ( $"Adapter ID = {AdapterCapabilities.AdapterID}" ) ;
Console . WriteLine ( $"Adapter Capabilities Mask = {AdapterCapabilities.CapsMask}" ) ;
Console . WriteLine ( $"Adapter Capabilities Value = {AdapterCapabilities.CapsValue}" ) ;
Console . WriteLine ( $"Adapter Num of Connectors = {AdapterCapabilities.NumConnectors}" ) ;
Console . WriteLine ( $"Adapter Num of Controllers = {AdapterCapabilities.NumControllers}" ) ;
Console . WriteLine ( $"Adapter Num of Displays = {AdapterCapabilities.NumDisplays}" ) ;
Console . WriteLine ( $"Adapter Num of GL Sync Connectors = {AdapterCapabilities.NumOfGLSyncConnectors}" ) ;
Console . WriteLine ( $"Adapter Num of Overlays = {AdapterCapabilities.NumOverlays}" ) ;
2021-06-15 09:55:15 +00:00
// Obtain information about displays
ADLDisplayInfo oneDisplayInfo = new ADLDisplayInfo ( ) ;
if ( ADL . ADL_Display_DisplayInfo_Get ! = null )
{
IntPtr DisplayBuffer = IntPtr . Zero ;
int j = 0 ;
// Force the display detection and get the Display Info. Use 0 as last parameter to NOT force detection
2021-06-18 00:33:46 +00:00
ADLRet = ADL . ADL_Display_DisplayInfo_Get ( oneAdapter . AdapterIndex , ref NumberOfDisplays , out DisplayBuffer , 0 ) ;
if ( ADLRet = = ADL . ADL_OK )
2021-06-15 09:55:15 +00:00
{
try
{
for ( j = 0 ; j < NumberOfDisplays ; j + + )
{
2021-06-18 00:33:46 +00:00
// Marshal the returned array of displayinfo into managed objects one by one
2021-06-15 09:55:15 +00:00
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayInfo = ( ADLDisplayInfo ) Marshal . PtrToStructure ( new IntPtr ( DisplayBuffer . ToInt64 ( ) + ( j * Marshal . SizeOf ( oneDisplayInfo ) ) ) , oneDisplayInfo . GetType ( ) ) ;
2021-06-18 00:33:46 +00:00
// Skip non connected displays
Console . WriteLine ( $"### Display Info for Display #{oneDisplayInfo.DisplayID.DisplayLogicalIndex} on Adapter #{oneAdapter.AdapterIndex} ###" ) ;
Console . WriteLine ( $"Display Connector = {oneDisplayInfo.DisplayConnector}" ) ;
Console . WriteLine ( $"Display Controller Index = {oneDisplayInfo.DisplayControllerIndex}" ) ;
Console . WriteLine ( $"Display Logical Adapter Index = {oneDisplayInfo.DisplayID.DisplayLogicalAdapterIndex}" ) ;
Console . WriteLine ( $"Display Logical Index = {oneDisplayInfo.DisplayID.DisplayLogicalIndex}" ) ;
Console . WriteLine ( $"Display Physical Adapter Index = {oneDisplayInfo.DisplayID.DisplayPhysicalAdapterIndex}" ) ;
Console . WriteLine ( $"Display Physical Index = {oneDisplayInfo.DisplayID.DisplayPhysicalIndex}" ) ;
Console . WriteLine ( $"Display Info Mask = {oneDisplayInfo.DisplayInfoMask}" ) ;
Console . WriteLine ( $"Display Info Value = {oneDisplayInfo.DisplayInfoValue}" ) ;
Console . WriteLine ( $"Display Manufacturer Name = {oneDisplayInfo.DisplayManufacturerName}" ) ;
Console . WriteLine ( $"Display Name = {oneDisplayInfo.DisplayName}" ) ;
Console . WriteLine ( $"Display Output Type = {oneDisplayInfo.DisplayOutputType}" ) ;
Console . WriteLine ( $"Display Type = {oneDisplayInfo.DisplayType}" ) ;
// Convert the displayInfoValue to something usable using a library function I made
ConvertedDisplayInfoValue displayInforValue = ADL . ConvertDisplayInfoValue ( oneDisplayInfo . DisplayInfoValue ) ;
Console . WriteLine ( $"Display Info Value DISPLAYCONNECTED = {displayInforValue.DISPLAYCONNECTED}" ) ;
Console . WriteLine ( $"Display Info Value DISPLAYMAPPED = {displayInforValue.DISPLAYMAPPED}" ) ;
Console . WriteLine ( $"Display Info Value FORCIBLESUPPORTED = {displayInforValue.FORCIBLESUPPORTED}" ) ;
Console . WriteLine ( $"Display Info Value GENLOCKSUPPORTED = {displayInforValue.GENLOCKSUPPORTED}" ) ;
Console . WriteLine ( $"Display Info Value LDA_DISPLAY = {displayInforValue.LDA_DISPLAY}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_2HSTRETCH = {displayInforValue.MANNER_SUPPORTED_2HSTRETCH}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_2VSTRETCH = {displayInforValue.MANNER_SUPPORTED_2VSTRETCH}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_CLONE = {displayInforValue.MANNER_SUPPORTED_CLONE}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_EXTENDED = {displayInforValue.MANNER_SUPPORTED_EXTENDED}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_NSTRETCH1GPU = {displayInforValue.MANNER_SUPPORTED_NSTRETCH1GPU}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_NSTRETCHNGPU = {displayInforValue.MANNER_SUPPORTED_NSTRETCHNGPU}" ) ;
Console . WriteLine ( $"Display Info Value MANNER_SUPPORTED_SINGLE = {displayInforValue.MANNER_SUPPORTED_SINGLE}" ) ;
Console . WriteLine ( $"Display Info Value MODETIMING_OVERRIDESSUPPORTED = {displayInforValue.MODETIMING_OVERRIDESSUPPORTED}" ) ;
Console . WriteLine ( $"Display Info Value MULTIVPU_SUPPORTED = {displayInforValue.MULTIVPU_SUPPORTED}" ) ;
Console . WriteLine ( $"Display Info Value NONLOCAL = {displayInforValue.NONLOCAL}" ) ;
Console . WriteLine ( $"Display Info Value SHOWTYPE_PROJECTOR = {displayInforValue.SHOWTYPE_PROJECTOR}" ) ;
if ( ( oneDisplayInfo . DisplayInfoValue & 1 ) ! = 1 )
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} ({oneAdapter.AdapterName}) AdapterID display ID#{j} is not connected" ) ;
continue ;
}
2021-06-15 09:55:15 +00:00
2021-06-18 00:33:46 +00:00
// Skip connected but non-mapped displays (not mapped in windows) - wae want all displays currently visible in the OS
if ( ( oneDisplayInfo . DisplayInfoValue & 2 ) ! = 2 )
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} ({oneAdapter.AdapterName}) AdapterID display ID#{j} is not connected" ) ;
continue ;
}
2021-06-16 10:15:15 +00:00
2021-06-18 00:33:46 +00:00
ADLDisplayConfig displayConfig = new ADLDisplayConfig ( ) ;
if ( ADL . ADL_Display_DeviceConfig_Get ! = null )
2021-06-16 10:15:15 +00:00
{
2021-06-18 00:33:46 +00:00
// Get the DisplayConfig from the Display
ADLRet = ADL . ADL_Display_DeviceConfig_Get ( oneAdapter . AdapterIndex , oneDisplayInfo . DisplayID . DisplayLogicalIndex , out displayConfig ) ;
if ( ADLRet = = ADL . ADL_OK )
{
Console . WriteLine ( $"### Display Device Config for Display #{oneDisplayInfo.DisplayID.DisplayLogicalIndex} on Adapter #{oneAdapter.AdapterIndex} ###" ) ;
Console . WriteLine ( $"Display Connector Type = {displayConfig.ConnectorType}" ) ;
Console . WriteLine ( $"Display Device Data = {displayConfig.DeviceData}" ) ;
Console . WriteLine ( $"Display Overridded Device Data = {displayConfig.OverriddedDeviceData}" ) ;
Console . WriteLine ( $"Display Reserved Data = {displayConfig.Reserved}" ) ;
Console . WriteLine ( $"Display Size = {displayConfig.Size}" ) ;
}
}
ADLDDCInfo2 displayDDCInfo2 = new ADLDDCInfo2 ( ) ;
// Create a stringbuilder buffer that EDID can be loaded into
//displayEDIDData.EDIDData = new StringBuilder(256);
2021-06-16 10:15:15 +00:00
2021-06-18 00:33:46 +00:00
if ( ADL . ADL2_Display_DDCInfo2_Get ! = null )
{
// Get the EDID Data from the Display
ADLRet = ADL . ADL2_Display_DDCInfo2_Get ( _adlContextHandle , oneAdapter . AdapterIndex , oneDisplayInfo . DisplayID . DisplayPhysicalIndex , out displayDDCInfo2 ) ;
if ( ADLRet = = ADL . ADL_OK )
{
Console . WriteLine ( $"### Display EDID Data for Display #{oneDisplayInfo.DisplayID.DisplayLogicalIndex} on Adapter #{oneAdapter.AdapterIndex} ###" ) ;
Console . WriteLine ( $"Display Manufacturer ID = {displayDDCInfo2.ManufacturerID}" ) ;
/ * Console . WriteLine ( $"Display EDID Data = {displayDDCInfo2.EDIDData}" ) ;
Console . WriteLine ( $"Display EDID Size = {displayDDCInfo2.EDIDSize}" ) ;
Console . WriteLine ( $"Display EDID Flag = {displayDDCInfo2.Flag}" ) ;
Console . WriteLine ( $"Display EDID Reserved = {displayDDCInfo2.Reserved}" ) ;
Console . WriteLine ( $"Display EDID Data Size = {displayDDCInfo2.Size}" ) ; * /
} else
{
Console . WriteLine ( $"Error running ADL_Display_EdidData_Get on Display #{oneDisplayInfo.DisplayID.DisplayLogicalIndex} on Adapter #{oneAdapter.AdapterIndex}: {ADL.ConvertADLReturnValueIntoWords(ADLRet)}" ) ;
}
2021-06-16 10:15:15 +00:00
}
2021-06-15 09:55:15 +00:00
2021-06-18 00:33:46 +00:00
// Create an array of all the important display info we need to record
List < string > displayInfoIdentifierSection = new List < string > ( ) ;
displayInfoIdentifierSection . Add ( "AMD" ) ;
try
{
displayInfoIdentifierSection . Add ( oneAdapter . AdapterName ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Adapter Name from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-15 09:55:15 +00:00
2021-06-18 00:33:46 +00:00
try
{
displayInfoIdentifierSection . Add ( oneAdapter . AdapterIndex . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Adapter Index from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-16 10:15:15 +00:00
2021-06-18 00:33:46 +00:00
try
{
displayInfoIdentifierSection . Add ( oneAdapter . VendorID . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD VendorID from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "1002" ) ;
}
2021-06-17 06:51:48 +00:00
2021-06-18 00:33:46 +00:00
try
{
displayInfoIdentifierSection . Add ( AdapterID . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD AdapterID from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-17 06:51:48 +00:00
2021-06-18 00:33:46 +00:00
try
{
ADL . ADLConnectionType connector = ( ADL . ADLConnectionType ) oneDisplayInfo . DisplayOutputType ;
displayInfoIdentifierSection . Add ( connector . ToString ( "G" ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Display Connector from video card to display. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-17 06:51:48 +00:00
2021-06-18 00:33:46 +00:00
try
{
displayInfoIdentifierSection . Add ( oneDisplayInfo . DisplayManufacturerName ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting Display manufacturer from AMD video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-17 06:51:48 +00:00
2021-06-18 00:33:46 +00:00
try
{
displayInfoIdentifierSection . Add ( oneDisplayInfo . DisplayName ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting Display Name from AMD video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
2021-06-17 06:51:48 +00:00
2021-06-18 00:33:46 +00:00
// Create a display identifier out of it
string displayIdentifier = String . Join ( "|" , displayInfoIdentifierSection ) ;
// Add it to the list of display identifiers so we can return it
displayIdentifiers . Add ( displayIdentifier ) ;
SharedLogger . logger . Debug ( $"ProfileRepository/GenerateProfileDisplayIdentifiers: DisplayIdentifier: {displayIdentifier}" ) ;
}
}
catch ( Exception ex )
{
Console . WriteLine ( "Exception caused trying to access attached displays" ) ;
continue ;
2021-06-15 09:55:15 +00:00
}
}
else
{
Console . WriteLine ( "ADL_Display_DisplayInfo_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
// Release the memory for the DisplayInfo structure
if ( IntPtr . Zero ! = DisplayBuffer )
Marshal . FreeCoTaskMem ( DisplayBuffer ) ;
}
}
else
{
Console . WriteLine ( "ADL_Adapter_Active_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
}
}
else
{
Console . WriteLine ( "ADL_Adapter_AdapterInfo_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
}
// Release the memory for the AdapterInfo structure
if ( IntPtr . Zero ! = AdapterBuffer )
{
Marshal . FreeCoTaskMem ( AdapterBuffer ) ;
}
2021-06-17 06:51:48 +00:00
// Return all the identifiers we've found
return displayIdentifiers ;
2021-06-15 09:55:15 +00:00
}
else
{
SharedLogger . logger . Error ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: There were no AMD adapters found by AMD ADL." ) ;
return null ;
}
}
public List < string > GenerateAllAvailableDisplayIdentifiers ( )
2021-06-14 09:42:16 +00:00
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Getting AMD active adapter count" ) ;
2021-06-18 00:33:46 +00:00
int ADLRet = ADL . ADL_ERR ;
2021-06-14 09:42:16 +00:00
int NumberOfAdapters = 0 ;
int NumberOfDisplays = 0 ;
2021-06-17 06:51:48 +00:00
List < string > displayIdentifiers = new List < string > ( ) ;
2021-06-17 09:40:10 +00:00
2021-06-14 09:42:16 +00:00
if ( null ! = ADL . ADL_Adapter_NumberOfAdapters_Get )
{
ADL . ADL_Adapter_NumberOfAdapters_Get ( ref NumberOfAdapters ) ;
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Number Of Adapters: {NumberOfAdapters.ToString()} " ) ;
}
if ( NumberOfAdapters > 0 )
{
// Get OS adpater info from ADL
ADLAdapterInfoArray OSAdapterInfoData ;
OSAdapterInfoData = new ADLAdapterInfoArray ( ) ;
2021-06-17 06:51:48 +00:00
IntPtr AdapterBuffer = IntPtr . Zero ;
2021-06-18 06:02:14 +00:00
if ( ADL . ADL2_Adapter_AdapterInfoX4_Get ! = null )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: ADL_Adapter_AdapterInfo_Get DLL function exists." ) ;
// Figure out the size of the AdapterBuffer we need to build
2021-06-14 09:42:16 +00:00
int size = Marshal . SizeOf ( OSAdapterInfoData ) ;
AdapterBuffer = Marshal . AllocCoTaskMem ( ( int ) size ) ;
Marshal . StructureToPtr ( OSAdapterInfoData , AdapterBuffer , false ) ;
2021-06-12 21:44:36 +00:00
2021-06-17 06:51:48 +00:00
// Get the Adapter info and put it in the AdapterBuffer
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Running ADL_Adapter_AdapterInfo_Get to find all known AMD adapters." ) ;
2021-06-18 06:02:14 +00:00
ADLRet = ADL . ADL_Adapter_AdapterInfo_Get ( out AdapterBuffer , size ) ;
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
// Use the AdapterBuffer pointer to marshal the OS Adapter Info into a structure
OSAdapterInfoData = ( ADLAdapterInfoArray ) Marshal . PtrToStructure ( AdapterBuffer , OSAdapterInfoData . GetType ( ) ) ;
int IsActive = ADL . ADL_TRUE ; // We only want to search for active adapters
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: Successfully run ADL_Adapter_AdapterInfo_Get to find information about all known AMD adapters." ) ;
// Go through each adapter
for ( int i = 0 ; i < NumberOfAdapters ; i + + )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
// Check if the adapter is active
if ( ADL . ADL_Adapter_Active_Get ! = null )
ADLRet = ADL . ADL_Adapter_Active_Get ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex , ref IsActive ) ;
2021-06-14 09:42:16 +00:00
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
// Only continue if the adapter is enabled
if ( IsActive ! = ADL . ADL_TRUE )
{
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} isn't active ({OSAdapterInfoData.ADLAdapterInfo[i].AdapterName})." ) ;
continue ;
}
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} is active! ({OSAdapterInfoData.ADLAdapterInfo[i].AdapterName})." ) ;
2021-06-12 21:44:36 +00:00
2021-06-17 06:51:48 +00:00
// Get the unique identifier from the Adapter
int AdapterID = 0 ;
if ( ADL . ADL_Adapter_ID_Get ! = null )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
ADLRet = ADL . ADL_Adapter_ID_Get ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex , ref AdapterID ) ;
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} ({OSAdapterInfoData.ADLAdapterInfo[i].AdapterName}) AdapterID is {AdapterID.ToString()}" ) ;
}
// Get the Adapter Capabilities
ADLAdapterCapsX2 AdapterCapabilities = new ADLAdapterCapsX2 ( ) ;
if ( ADL . ADL_AdapterX2_Caps ! = null )
{
ADLRet = ADL . ADL_AdapterX2_Caps ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex , out AdapterCapabilities ) ;
}
//ADLAdapterCapsX2 AdapterCapabilities = (ADLAdapterCapsX2)Marshal.PtrToStructure(AdapterCapabilitiesBuffer, typeof(ADLAdapterCapsX2));
Console . Write ( AdapterCapabilities . AdapterID ) ;
// Obtain information about displays
ADLDisplayInfo oneDisplayInfo = new ADLDisplayInfo ( ) ;
if ( ADL . ADL_Display_DisplayInfo_Get ! = null )
{
IntPtr DisplayBuffer = IntPtr . Zero ;
int j = 0 ;
// Force the display detection and get the Display Info. Use 0 as last parameter to NOT force detection
ADLRet = ADL . ADL_Display_DisplayInfo_Get ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex , ref NumberOfDisplays , out DisplayBuffer , 0 ) ;
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
List < ADLDisplayInfo > DisplayInfoData = new List < ADLDisplayInfo > ( ) ;
try
2021-06-14 09:42:16 +00:00
{
2021-06-14 08:37:35 +00:00
2021-06-17 06:51:48 +00:00
for ( j = 0 ; j < NumberOfDisplays ; j + + )
{
// NOTE: the ToInt64 work on 64 bit, need to change to ToInt32 for 32 bit OS
oneDisplayInfo = ( ADLDisplayInfo ) Marshal . PtrToStructure ( new IntPtr ( DisplayBuffer . ToInt64 ( ) + ( j * Marshal . SizeOf ( oneDisplayInfo ) ) ) , oneDisplayInfo . GetType ( ) ) ;
DisplayInfoData . Add ( oneDisplayInfo ) ;
}
}
catch ( Exception ex )
{
Console . WriteLine ( "Exception caused trying to access attached displays" ) ;
continue ;
}
Console . WriteLine ( "\nTotal Number of Displays supported: " + NumberOfDisplays . ToString ( ) ) ;
Console . WriteLine ( "\nDispID AdpID Type OutType CnctType Connected Mapped InfoValue DisplayName " ) ;
2021-06-14 08:37:35 +00:00
2021-06-17 06:51:48 +00:00
for ( j = 0 ; j < NumberOfDisplays ; j + + )
{
2021-06-17 09:40:10 +00:00
// Skip non connected displays - we want only connected displays that could potentially be used now (thats both mapped and non-mapped displays)
2021-06-17 06:51:48 +00:00
if ( ( DisplayInfoData [ j ] . DisplayInfoValue & 1 ) ! = 1 )
2021-06-14 09:42:16 +00:00
{
2021-06-17 06:51:48 +00:00
SharedLogger . logger . Trace ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: AMD Adapter #{i} ({OSAdapterInfoData.ADLAdapterInfo[i].AdapterName}) AdapterID display ID#{j} is not connected" ) ;
continue ;
}
2021-06-12 21:44:36 +00:00
2021-06-17 06:51:48 +00:00
ADLDisplayConfig DisplayConfig = new ADLDisplayConfig ( ) ;
if ( ADL . ADL_Display_DeviceConfig_Get ! = null )
{
2021-06-18 00:33:46 +00:00
// Get the device config
2021-06-17 06:51:48 +00:00
ADLRet = ADL . ADL_Display_DeviceConfig_Get ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex , DisplayInfoData [ j ] . DisplayID . DisplayLogicalIndex , out DisplayConfig ) ;
2021-06-18 00:33:46 +00:00
if ( ADLRet = = ADL . ADL_OK )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
2021-06-12 21:44:36 +00:00
}
}
2021-06-17 06:51:48 +00:00
// Create an array of all the important display info we need to record
List < string > displayInfoIdentifierSection = new List < string > ( ) ;
displayInfoIdentifierSection . Add ( "AMD" ) ;
try
{
displayInfoIdentifierSection . Add ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterName ) ;
}
2021-06-14 09:42:16 +00:00
catch ( Exception ex )
2021-06-12 21:44:36 +00:00
{
2021-06-17 06:51:48 +00:00
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Adapter Name from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
2021-06-12 21:44:36 +00:00
}
2021-06-14 09:42:16 +00:00
2021-06-17 06:51:48 +00:00
try
2021-06-14 09:42:16 +00:00
{
2021-06-17 06:51:48 +00:00
displayInfoIdentifierSection . Add ( OSAdapterInfoData . ADLAdapterInfo [ i ] . AdapterIndex . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Adapter Index from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
try
{
displayInfoIdentifierSection . Add ( OSAdapterInfoData . ADLAdapterInfo [ i ] . VendorID . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD VendorID from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "1002" ) ;
}
try
{
displayInfoIdentifierSection . Add ( AdapterID . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD AdapterID from video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
try
{
2021-06-17 08:10:14 +00:00
ADL . ADLConnectionType connector = ( ADL . ADLConnectionType ) DisplayInfoData [ j ] . DisplayOutputType ;
2021-06-17 06:51:48 +00:00
displayInfoIdentifierSection . Add ( connector . ToString ( ) ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting AMD Display Connector from video card to display. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
try
{
displayInfoIdentifierSection . Add ( DisplayInfoData [ j ] . DisplayManufacturerName ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting Display manufacturer from AMD video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
try
{
displayInfoIdentifierSection . Add ( DisplayInfoData [ j ] . DisplayName ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ADLWrapper/GenerateProfileDisplayIdentifiers: Exception getting Display Name from AMD video card. Substituting with a # instead" ) ;
displayInfoIdentifierSection . Add ( "#" ) ;
}
// Create a display identifier out of it
string displayIdentifier = String . Join ( "|" , displayInfoIdentifierSection ) ;
// Add it to the list of display identifiers so we can return it
displayIdentifiers . Add ( displayIdentifier ) ;
SharedLogger . logger . Debug ( $"ProfileRepository/GenerateProfileDisplayIdentifiers: DisplayIdentifier: {displayIdentifier}" ) ;
2021-06-12 21:44:36 +00:00
}
}
2021-06-17 06:51:48 +00:00
else
{
Console . WriteLine ( "ADL_Display_DisplayInfo_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
// Release the memory for the DisplayInfo structure
if ( IntPtr . Zero ! = DisplayBuffer )
Marshal . FreeCoTaskMem ( DisplayBuffer ) ;
2021-06-12 21:44:36 +00:00
}
}
2021-06-17 06:51:48 +00:00
else
{
Console . WriteLine ( "ADL_Adapter_Active_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
2021-06-14 09:42:16 +00:00
}
2021-06-12 21:44:36 +00:00
}
2021-06-17 06:51:48 +00:00
else
{
Console . WriteLine ( "ADL_Adapter_AdapterInfo_Get() returned error code " + ADLRet . ToString ( ) ) ;
}
2021-06-12 21:44:36 +00:00
}
2021-06-17 06:51:48 +00:00
// Release the memory for the AdapterInfo structure
if ( IntPtr . Zero ! = AdapterBuffer )
{
Marshal . FreeCoTaskMem ( AdapterBuffer ) ;
}
// Return all the identifiers we've found
return displayIdentifiers ;
2021-06-12 21:44:36 +00:00
}
else
{
2021-06-14 09:42:16 +00:00
SharedLogger . logger . Error ( $"ADLWrapper/GenerateProfileDisplayIdentifiers: There were no AMD adapters found by AMD ADL." ) ;
return null ;
2021-06-12 21:44:36 +00:00
}
}
}
}
2021-06-18 00:33:46 +00:00