//-------------------------------------------------------------------------------------- // File: DXMUTEnum.cs // // Enumerates D3D adapters, devices, modes, etc. // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- using System; using System.Collections; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace Microsoft.Samples.DirectX.UtilityToolkit { /// /// Enumerates available Direct3D adapters, devices, modes, etc. Singleton. /// public sealed class Enumeration { #region Creation (Not allowed) private Enumeration() {} // Do Not allow Creation /// /// Static constructor to create default lists /// static Enumeration() { // Create the default lists ResetPossibleDepthStencilFormats(); ResetPossibleMultisampleTypeList(); ResetPossiblePresentIntervalList(); } #endregion // Static Data private static bool isPostPixelShaderBlendingRequired = true; private static IDeviceCreation deviceCreationInterface = null; // Vertex processing private static bool isSoftwareVertexProcessing = true; private static bool isHardwareVertexProcessing = true; private static bool isPureHardwareVertexProcessing = true; private static bool isMixedVertexProcessing = false; // Limits private static uint minimumWidth = 0; private static uint maximumWidth = uint.MaxValue; private static uint minimumHeight = 0; private static uint maximumHeight = uint.MaxValue; private static uint minimumRefresh = 0; private static uint maximumRefresh = uint.MaxValue; private static uint multisampleQualityMax = 0xffff; // Lists private static ArrayList depthStencilPossibleList = new ArrayList(); private static ArrayList multiSampleTypeList = new ArrayList(); private static ArrayList presentIntervalList = new ArrayList(); private static ArrayList adapterInformationList = new ArrayList(); // Default arrays private static readonly Format[] allowedFormats = new Format[] { Format.X8R8G8B8, Format.X1R5G5B5, Format.R5G6B5, Format.A2R10G10B10 }; private static readonly Format[] backbufferFormatsArray = new Format[] { Format.A8R8G8B8, Format.X8R8G8B8, Format.A1R5G5B5, Format.X1R5G5B5, Format.R5G6B5, Format.A2R10G10B10 }; private static readonly DeviceType[] deviceTypeArray = new DeviceType[] { DeviceType.Hardware, DeviceType.Software, DeviceType.Reference }; // Implementation /// /// Enumerates available D3D adapters, devices, modes, etc /// public static void Enumerate(IDeviceCreation acceptableCallback) { DisplayModeSorter sorter = new DisplayModeSorter(); try { // Store the callback deviceCreationInterface = acceptableCallback; // Clear the adapter information stored currently adapterInformationList.Clear(); ArrayList adapterFormatList = new ArrayList(); // Look through every adapter on the system foreach(AdapterInformation ai in Manager.Adapters) { EnumAdapterInformation adapterInfo = new EnumAdapterInformation(); // Store some information adapterInfo.AdapterOrdinal = (uint)ai.Adapter; // Ordinal adapterInfo.AdapterInformation = ai.Information; // Information // Get list of all display modes on this adapter. // Also build a temporary list of all display adapter formats. adapterFormatList.Clear(); // Now check to see which formats are supported for(int i = 0; i < allowedFormats.Length; i++) { // Check each of the supported display modes for this format foreach(DisplayMode dm in ai.SupportedDisplayModes[allowedFormats[i]]) { if ( (dm.Width < minimumWidth) || (dm.Height < minimumHeight) || (dm.Width > maximumWidth) || (dm.Height > maximumHeight) || (dm.RefreshRate < minimumRefresh) || (dm.RefreshRate > maximumRefresh) ) { continue; // This format isn't valid } // Add this to the list adapterInfo.displayModeList.Add(dm); // Add this to the format list if it doesn't already exist if (!adapterFormatList.Contains(dm.Format)) { adapterFormatList.Add(dm.Format); } } } // Get the adapter display mode DisplayMode currentAdapterMode = ai.CurrentDisplayMode; // Check to see if this format is in the list if (!adapterFormatList.Contains(currentAdapterMode.Format)) { adapterFormatList.Add(currentAdapterMode.Format); } // Sort the display mode list adapterInfo.displayModeList.Sort(sorter); // Get information for each device with this adapter EnumerateDevices(adapterInfo, adapterFormatList); // If there was at least one device on the adapter, and it's compatible // add it to the list if (adapterInfo.deviceInfoList.Count > 0) { adapterInformationList.Add(adapterInfo); } } // See if all of the descriptions are unique bool isUnique = true; for(int i = 0; i < adapterInformationList.Count; i++) { for (int j = i+1; j < adapterInformationList.Count; j++) { EnumAdapterInformation eai1 = adapterInformationList[i] as EnumAdapterInformation; EnumAdapterInformation eai2 = adapterInformationList[j] as EnumAdapterInformation; if (string.Compare(eai1.AdapterInformation.Description, eai2.AdapterInformation.Description, true) == 0) { isUnique = false; break; } } if (!isUnique) break; } // Now fill the unique description for(int i = 0; i < adapterInformationList.Count; i++) { EnumAdapterInformation eai1 = adapterInformationList[i] as EnumAdapterInformation; eai1.UniqueDescription = eai1.AdapterInformation.Description; // If the descriptions aren't unique, append the adapter ordinal if (!isUnique) eai1.UniqueDescription += string.Format(" (#{0})", eai1.AdapterOrdinal); } } catch (TypeLoadException) { // Couldn't load the manager class, no Direct is available. throw new NoDirect3DException(); } } /// /// Enumerates D3D devices for a particular adapter. /// private static void EnumerateDevices(EnumAdapterInformation adapterInfo, ArrayList adapterFormatList) { // Ignore any exceptions while looking for these device types DirectXException.IgnoreExceptions(); // Enumerate each Direct3D device type for(uint i = 0; i < deviceTypeArray.Length; i++) { // Create a new device information object EnumDeviceInformation deviceInfo = new EnumDeviceInformation(); // Store the type deviceInfo.DeviceType = deviceTypeArray[i]; // Try to get the capabilities deviceInfo.Caps = Manager.GetDeviceCaps((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType); // Get information about each device combination on this device EnumerateDeviceCombos( adapterInfo, deviceInfo, adapterFormatList); // Do we have any device combinations? if (deviceInfo.deviceSettingsList.Count > 0) { // Yes, add it adapterInfo.deviceInfoList.Add(deviceInfo); } } // Turn exception handling back on DirectXException.EnableExceptions(); } /// /// Enumerates device combinations for a particular device. /// private static void EnumerateDeviceCombos(EnumAdapterInformation adapterInfo, EnumDeviceInformation deviceInfo, ArrayList adapterFormatList) { // Find out which adapter formats are supported by this device foreach(Format adapterFormat in adapterFormatList) { for(int i = 0; i < backbufferFormatsArray.Length; i++) { // Go through each windowed mode for (int windowedIndex = 0; windowedIndex < 2; windowedIndex++) { bool isWindowedIndex = (windowedIndex == 1); if ((!isWindowedIndex) && (adapterInfo.displayModeList.Count == 0)) continue; // Nothing here if (!Manager.CheckDeviceType((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType, adapterFormat, backbufferFormatsArray[i], isWindowedIndex)) continue; // Unsupported // Do we require post pixel shader blending? if (isPostPixelShaderBlendingRequired) { // If the backbuffer format doesn't support Usage.QueryPostPixelShaderBlending // then alpha test, pixel fog, render-target blending, color write enable, and dithering // are not supported. if (!Manager.CheckDeviceFormat((int)adapterInfo.AdapterOrdinal, deviceInfo.DeviceType, adapterFormat, Usage.QueryPostPixelShaderBlending, ResourceType.Textures, backbufferFormatsArray[i])) continue; // Unsupported } // If an application callback function has been provided, make sure this device // is acceptable to the app. if (deviceCreationInterface != null) { if (!deviceCreationInterface.IsDeviceAcceptable(deviceInfo.Caps, adapterFormat, backbufferFormatsArray[i],isWindowedIndex)) continue; // Application doesn't like this device } // At this point, we have an adapter/device/adapterformat/backbufferformat/iswindowed // DeviceCombo that is supported by the system and acceptable to the app. We still // need to find one or more suitable depth/stencil buffer format, // multisample type, and present interval. EnumDeviceSettingsCombo deviceCombo = new EnumDeviceSettingsCombo(); // Store the information deviceCombo.AdapterOrdinal = adapterInfo.AdapterOrdinal; deviceCombo.DeviceType = deviceInfo.DeviceType; deviceCombo.AdapterFormat = adapterFormat; deviceCombo.BackBufferFormat = backbufferFormatsArray[i]; deviceCombo.IsWindowed = isWindowedIndex; // Build the depth stencil format and multisample type list BuildDepthStencilFormatList(deviceCombo); BuildMultiSampleTypeList(deviceCombo); if (deviceCombo.multiSampleTypeList.Count == 0) { // Nothing to do continue; } // Build the conflict and present lists BuildConflictList(deviceCombo); BuildPresentIntervalList(deviceInfo, deviceCombo); deviceCombo.adapterInformation = adapterInfo; deviceCombo.deviceInformation = deviceInfo; // Add the combo to the list of devices deviceInfo.deviceSettingsList.Add(deviceCombo); } } } } /// /// Adds all depth/stencil formats that are compatible with the device /// and application to the given device combo /// private static void BuildDepthStencilFormatList(EnumDeviceSettingsCombo deviceCombo) { foreach(DepthFormat depthStencil in depthStencilPossibleList) { if (Manager.CheckDeviceFormat((int)deviceCombo.AdapterOrdinal, deviceCombo.DeviceType, deviceCombo.AdapterFormat, Usage.DepthStencil, ResourceType.Surface, depthStencil)) { // This can be used as a depth stencil, make sure it matches if (Manager.CheckDepthStencilMatch((int)deviceCombo.AdapterOrdinal, deviceCombo.DeviceType, deviceCombo.AdapterFormat, deviceCombo.BackBufferFormat, depthStencil)) { // Yup, add it deviceCombo.depthStencilFormatList.Add(depthStencil); } } } } /// /// Adds all multisample types that are compatible with the device and app to /// the given device combo /// private static void BuildMultiSampleTypeList(EnumDeviceSettingsCombo deviceCombo) { foreach(MultiSampleType msType in multiSampleTypeList) { int result, quality; // Check this if (Manager.CheckDeviceMultiSampleType((int)deviceCombo.AdapterOrdinal, deviceCombo.DeviceType, deviceCombo.BackBufferFormat, deviceCombo.IsWindowed, msType, out result, out quality)) { deviceCombo.multiSampleTypeList.Add(msType); if (quality > multisampleQualityMax + 1) quality = (int)(multisampleQualityMax + 1); deviceCombo.multiSampleQualityList.Add(quality); } } } /// /// Find any conflicts between the available depth/stencil formats and /// multisample types. /// private static void BuildConflictList(EnumDeviceSettingsCombo deviceCombo) { foreach(DepthFormat depthFormat in deviceCombo.depthStencilFormatList) { foreach(MultiSampleType msType in deviceCombo.multiSampleTypeList) { // Check this for conflict if (!Manager.CheckDeviceMultiSampleType((int)deviceCombo.AdapterOrdinal, deviceCombo.DeviceType, (Format)depthFormat, deviceCombo.IsWindowed, msType)) { // Add it to the list EnumDepthStencilMultisampleConflict conflict = new EnumDepthStencilMultisampleConflict(); conflict.DepthStencilFormat = depthFormat; conflict.MultisampleType = msType; deviceCombo.depthStencilConflictList.Add(conflict); } } } } /// /// Adds all present intervals that are compatible with the device and app /// to the given device combo /// private static void BuildPresentIntervalList(EnumDeviceInformation deviceInfo, EnumDeviceSettingsCombo deviceCombo) { for (int i = 0; i < presentIntervalList.Count; i++) { PresentInterval pi = (PresentInterval)presentIntervalList[i]; if (deviceCombo.IsWindowed) { if ( (pi == PresentInterval.Two) || (pi == PresentInterval.Three) || (pi == PresentInterval.Four) ) { // These intervals are never supported in windowed mode continue; } } // Not that PresentInterval.Default is zero so you can't do a bitwise // check for it, it's always available if ( (pi == PresentInterval.Default) || ((deviceInfo.Caps.PresentationIntervals & pi) != 0)) { deviceCombo.presentIntervalList.Add(pi); } } } /// /// Resets the list of possible depth stencil formats /// public static void ResetPossibleDepthStencilFormats() { depthStencilPossibleList.Clear(); depthStencilPossibleList.AddRange(new DepthFormat[] { DepthFormat.D16, DepthFormat.D15S1, DepthFormat.D24X8, DepthFormat.D24S8, DepthFormat.D24X4S4, DepthFormat.D32 }); } /// /// Resets the possible multisample type list /// public static void ResetPossibleMultisampleTypeList() { multiSampleTypeList.Clear(); multiSampleTypeList.AddRange(new MultiSampleType[] { MultiSampleType.None, MultiSampleType.NonMaskable, MultiSampleType.TwoSamples, MultiSampleType.ThreeSamples, MultiSampleType.FourSamples, MultiSampleType.FiveSamples, MultiSampleType.SixSamples, MultiSampleType.SevenSamples, MultiSampleType.EightSamples, MultiSampleType.NineSamples, MultiSampleType.TenSamples, MultiSampleType.ElevenSamples, MultiSampleType.TwelveSamples, MultiSampleType.ThirteenSamples, MultiSampleType.FourteenSamples, MultiSampleType.FifteenSamples, MultiSampleType.SixteenSamples }); } /// /// Resets the possible present interval list /// public static void ResetPossiblePresentIntervalList() { presentIntervalList.Clear(); presentIntervalList.AddRange(new PresentInterval[] { PresentInterval.Immediate, PresentInterval.Default, PresentInterval.One, PresentInterval.Two, PresentInterval.Three, PresentInterval.Four }); } /// /// Set the minimum and maximum resolution items /// public static void SetResolutionMinMax(uint minWidth, uint minHeight, uint maxWidth, uint maxHeight) { minimumWidth = minWidth; minimumHeight = minHeight; maximumWidth = maxHeight; maximumWidth = maxWidth; } /// /// Sets the minimum and maximum refresh /// public static void SetRefreshMinMax(uint minRefresh, uint maxRefresh) { minimumRefresh = minRefresh; maximumRefresh = maxRefresh; } /// /// Property for MultisampleQualityMax /// public static uint MultisampleQualityMax { get { return multisampleQualityMax; } set { if (value > 0xffff) multisampleQualityMax = 0xffff; else multisampleQualityMax = value; } } /// /// Allows the user to set if post pixel shader blending is required /// public static bool IsPostPixelShaderBlendingRequred { get { return isPostPixelShaderBlendingRequired; } set { isPostPixelShaderBlendingRequired = value; } } /// /// Allows the user to set if software vertex processing is available /// public static bool IsSoftwareVertexProcessingPossible { get { return isSoftwareVertexProcessing; } set { isSoftwareVertexProcessing = value; } } /// /// Allows the user to set if hardware vertex processing is available /// public static bool IsHardwareVertexProcessingPossible { get { return isHardwareVertexProcessing; } set { isHardwareVertexProcessing = value; } } /// /// Allows the user to set if pure hardware vertex processing is available /// public static bool IsPureHardwareVertexProcessingPossible { get { return isPureHardwareVertexProcessing; } set { isPureHardwareVertexProcessing = value; } } /// /// Allows the user to set if mixed vertex processing is available /// public static bool IsMixedVertexProcessingPossible { get { return isMixedVertexProcessing; } set { isMixedVertexProcessing = value; } } /// /// Allows the user to get the possible depth stencil formats /// public static ArrayList PossibleDepthStencilFormatList { get { return depthStencilPossibleList; } } /// /// Allows the user to get the possible multisample types /// public static ArrayList PossibleMultisampleTypeList { get { return multiSampleTypeList; } } /// /// Allows the user to get the possible present intervals /// public static ArrayList PossiblePresentIntervalsList { get { return presentIntervalList; } } /// /// Use this after Enumerate to get the list of adapter information /// public static ArrayList AdapterInformationList { get { return adapterInformationList; } } /// /// Get the adapter information for a specific adapter /// public static EnumAdapterInformation GetAdapterInformation(uint ordinal) { foreach(EnumAdapterInformation eai in adapterInformationList) { if (eai.AdapterOrdinal == ordinal) { return eai; } } // Never found it return null; } /// /// Get a specific device information for a device and type /// public static EnumDeviceInformation GetDeviceInfo(uint ordinal, DeviceType deviceType) { EnumAdapterInformation info = GetAdapterInformation(ordinal); if (info != null) { foreach(EnumDeviceInformation edi in info.deviceInfoList) { // Is this the right device type? if (edi.DeviceType == deviceType) { return edi; } } } // Never found it return null; } /// /// Returns a specific device combination /// public static EnumDeviceSettingsCombo GetDeviceSettingsCombo(uint ordinal, DeviceType deviceType, Format adapterFormat, Format backBufferFormat, bool isWindowed) { EnumDeviceInformation info = GetDeviceInfo(ordinal, deviceType); if (info != null) { foreach(EnumDeviceSettingsCombo edsc in info.deviceSettingsList) { // Is this the right settings combo? if ( (edsc.AdapterFormat == adapterFormat) && (edsc.BackBufferFormat == backBufferFormat) && (edsc.IsWindowed == isWindowed) ) { return edsc; } } } // Never found it return null; } /// /// Returns a specific device combination from a device settings object /// public static EnumDeviceSettingsCombo GetDeviceSettingsCombo(DeviceSettings settings) { return GetDeviceSettingsCombo(settings.AdapterOrdinal, settings.DeviceType, settings.AdapterFormat, settings.presentParams.BackBufferFormat, settings.presentParams.Windowed); } } /// /// Class describing an adapter which contains a unique adapter ordinal that /// is installed on the system. /// public class EnumAdapterInformation { public uint AdapterOrdinal; // Ordinal for this adapter public AdapterDetails AdapterInformation; // Information about this adapter public ArrayList displayModeList = new ArrayList(); // Array of display modes public ArrayList deviceInfoList = new ArrayList(); // Array of device information public string UniqueDescription; // Unique description of this device } /// /// Class describing a Direct3D device that contains a /// unique supported device type /// public class EnumDeviceInformation { public uint AdapterOrdinal; // Ordinal for this adapter public DeviceType DeviceType; // Type of the device public Caps Caps; // Capabilities of the device public ArrayList deviceSettingsList = new ArrayList(); // Array with unique set of adapter format, back buffer format, and windowed } /// /// Class describing device settings that contain a unique combination /// of adapter format, back buffer format, and windowed that is compatible /// with a particular Direct3D device and the application /// public class EnumDeviceSettingsCombo { public uint AdapterOrdinal; public DeviceType DeviceType; public Format AdapterFormat; public Format BackBufferFormat; public bool IsWindowed; // Array lists public ArrayList depthStencilFormatList = new ArrayList(); public ArrayList multiSampleTypeList = new ArrayList(); public ArrayList multiSampleQualityList = new ArrayList(); public ArrayList presentIntervalList = new ArrayList(); public ArrayList depthStencilConflictList = new ArrayList(); public EnumAdapterInformation adapterInformation = null; public EnumDeviceInformation deviceInformation = null; } /// /// A depth/stencil buffer format that is incompatible /// with a multisample type /// public struct EnumDepthStencilMultisampleConflict { public DepthFormat DepthStencilFormat; public MultiSampleType MultisampleType; } /// /// Used to sort display modes /// public class DisplayModeSorter : IComparer { #region IComparer Members /// /// Compare two display modes /// public int Compare(object x, object y) { DisplayMode d1 = (DisplayMode)x; DisplayMode d2 = (DisplayMode)y; if (d1.Width > d2.Width) return +1; if (d1.Width < d2.Width) return -1; if (d1.Height > d2.Height) return +1; if (d1.Height < d2.Height) return -1; if (d1.Format > d2.Format) return +1; if (d1.Format < d2.Format) return -1; if (d1.RefreshRate > d2.RefreshRate) return +1; if (d1.RefreshRate < d2.RefreshRate) return -1; // They must be the same, return 0 return 0; } #endregion } #region Helper Utility Class /// /// Helper methods /// class ManagedUtility { private ManagedUtility() { } // No creation /// /// Gets the number of ColorChanelBits from a format /// public static uint GetColorChannelBits(Format format) { switch (format) { case Format.R8G8B8: case Format.A8R8G8B8: case Format.X8R8G8B8: return 8; case Format.R5G6B5: case Format.X1R5G5B5: case Format.A1R5G5B5: return 5; case Format.A4R4G4B4: case Format.X4R4G4B4: return 4; case Format.R3G3B2: case Format.A8R3G3B2: return 2; case Format.A2B10G10R10: case Format.A2R10G10B10: return 10; default: return 0; } } /// /// Gets the number of alpha channel bits /// public static uint GetAlphaChannelBits(Format format) { switch (format) { case Format.X8R8G8B8: case Format.R8G8B8: case Format.R5G6B5: case Format.X1R5G5B5: case Format.R3G3B2: case Format.X4R4G4B4: return 0; case Format.A8R3G3B2: case Format.A8R8G8B8: return 8; case Format.A1R5G5B5: return 1; case Format.A4R4G4B4: return 4; case Format.A2B10G10R10: case Format.A2R10G10B10: return 2; default: return 0; } } /// /// Gets the number of depth bits /// public static uint GetDepthBits(DepthFormat format) { switch (format) { case DepthFormat.D16: case DepthFormat.D16Lockable: return 16; case DepthFormat.D15S1: return 15; case DepthFormat.D24X8: case DepthFormat.D24S8: case DepthFormat.D24X4S4: case DepthFormat.D24SingleS8: return 24; case DepthFormat.D32: case DepthFormat.D32SingleLockable: return 32; default: return 0; } } /// /// Gets the number of stencil bits /// public static uint GetStencilBits(DepthFormat format) { switch (format) { case DepthFormat.D16: case DepthFormat.D16Lockable: case DepthFormat.D24X8: case DepthFormat.D32: case DepthFormat.D32SingleLockable: return 0; case DepthFormat.D15S1: return 1; case DepthFormat.D24X4S4: return 4; case DepthFormat.D24SingleS8: case DepthFormat.D24S8: return 8; default: return 0; } } } #endregion }