/// <summary> /// Function to retrieve the outputs attached to a video adapter. /// </summary> /// <param name="device">The Direct 3D device used to filter display modes.</param> /// <param name="adapter">The adapter to retrieve the outputs from.</param> /// <param name="outputCount">The number of outputs for the device.</param> /// <param name="log">The logging interface used to capture debug messages.</param> /// <returns>A list if video output info values.</returns> private static Dictionary <string, VideoOutputInfo> GetOutputs(D3D11.Device5 device, Adapter4 adapter, int outputCount, IGorgonLog log) { var result = new Dictionary <string, VideoOutputInfo>(StringComparer.OrdinalIgnoreCase); // Devices created under RDP/TS do not support output selection. if (SystemInformation.TerminalServerSession) { log.Print("Devices under terminal services and software devices devices do not use outputs, no outputs enumerated.", LoggingLevel.Intermediate); return(result); } for (int i = 0; i < outputCount; ++i) { using (Output output = adapter.GetOutput(i)) using (Output6 output6 = output.QueryInterface <Output6>()) { var outputInfo = new VideoOutputInfo(i, output6, GetVideoModes(device, output6)); if (outputInfo.VideoModes.Count == 0) { log.Print($"Output '{output.Description.DeviceName}' on adapter '{adapter.Description1.Description}' has no full screen video modes.", LoggingLevel.Intermediate); } result.Add(output.Description.DeviceName, outputInfo); } } return(result); }
/// <summary>Imports the data.</summary> /// <param name="temporaryDirectory">The temporary directory for writing any transitory data.</param> /// <param name="cancelToken">The cancel token.</param> /// <remarks> /// <para> /// The <paramref name="temporaryDirectory"/> should be used to write any working/temporary data used by the import. Note that all data written into this directory will be deleted when the /// project is unloaded from memory. /// </para> /// </remarks> public FileInfo ImportData(DirectoryInfo temporaryDirectory, CancellationToken cancelToken) { var spriteCodec = new GorgonV3SpriteBinaryCodec(_renderer); _log.Print("Importing associated texture for sprite...", LoggingLevel.Simple); GorgonTexture2DView texture = GetTexture(); try { _log.Print($"Importing file '{SourceFile.FullName}' (Codec: {_codec.Name})...", LoggingLevel.Verbose); GorgonSprite sprite = _codec.FromFile(SourceFile.FullName); if (sprite.Texture == null) { sprite.Texture = texture; } var tempFile = new FileInfo(Path.Combine(temporaryDirectory.FullName, Path.GetFileNameWithoutExtension(SourceFile.Name))); _log.Print($"Converting '{SourceFile.FullName}' to Gorgon v3 Sprite file format.", LoggingLevel.Verbose); spriteCodec.Save(sprite, tempFile.FullName); return(tempFile); } finally { texture?.Dispose(); } }
/// <summary>Imports the data.</summary> /// <param name="temporaryDirectory">The temporary directory for writing any transitory data.</param> /// <param name="cancelToken">The cancel token.</param> /// <remarks> /// <para> /// The <paramref name="temporaryDirectory"/> should be used to write any working/temporary data used by the import. Note that all data written into this directory will be deleted when the /// project is unloaded from memory. /// </para> /// </remarks> public FileInfo ImportData(DirectoryInfo temporaryDirectory, CancellationToken cancelToken) { var ddsCodec = new GorgonCodecDds(); _log.Print($"Importing file '{SourceFile.FullName}' (Codec: {_codec.Name})...", LoggingLevel.Verbose); using (IGorgonImage image = _codec.LoadFromFile(SourceFile.FullName)) { var tempFile = new FileInfo(Path.Combine(temporaryDirectory.FullName, Path.GetFileNameWithoutExtension(SourceFile.Name))); _log.Print($"Converting '{SourceFile.FullName}' to DDS file format. Image format [{image.Format}].", LoggingLevel.Verbose); ddsCodec.SaveToFile(image, tempFile.FullName); return(tempFile); } }
/// <summary>Function to clear the undo/redo stacks.</summary> public void ClearStack() { Cancel(); // Perform any clean up required for the undo/redo arguments. foreach (IDisposable command in _undoStack.OfType <IDisposable>()) { command.Dispose(); } _undoStack.Clear(); _undoIndex = -1; _log.Print("Undo commands cleared.", LoggingLevel.Simple); }
/// <summary> /// Function to retrieve the description of the raw input device from the registry. /// </summary> /// <param name="deviceName">Path to the registry key that holds the device description.</param> /// <param name="log">The debug log file to use when logging issues.</param> /// <returns>The device description.</returns> public static string GetDeviceDescription(string deviceName, IGorgonLog log) { if (string.IsNullOrWhiteSpace(deviceName)) { throw new ArgumentException(Resources.GORINP_RAW_ERR_CANNOT_READ_DEVICE_DATA, nameof(deviceName)); } string[] regValue = deviceName.Split('#'); regValue[0] = regValue[0].Substring(4); // Don't add RDP devices. if ((log != null) && (regValue.Length > 0) && (regValue[1].StartsWith("RDP_", StringComparison.OrdinalIgnoreCase))) { log.Print("WARNING: This is an RDP device. Raw input in Gorgon is not supported under RDP. Skipping this device.", LoggingLevel.Verbose); return(string.Empty); } using (RegistryKey deviceKey = Registry.LocalMachine.OpenSubKey($@"System\CurrentControlSet\Enum\{regValue[0]}\{regValue[1]}\{regValue[2]}", false)) { if (deviceKey?.GetValue("DeviceDesc") == null) { return(string.Empty); } regValue = deviceKey.GetValue("DeviceDesc").ToString().Split(';'); return(regValue[regValue.Length - 1]); } }
/// <summary> /// Function to add the WARP software device. /// </summary> /// <param name="index">Index of the device.</param> /// <param name="factory">The factory used to query the adapter.</param> /// <param name="log">The log interface used to send messages to a debug log.</param> /// <returns>The video adapter used for WARP software rendering.</returns> private static VideoAdapterInfo GetWARPSoftwareDevice(int index, Factory5 factory, IGorgonLog log) { D3D11.DeviceCreationFlags flags = D3D11.DeviceCreationFlags.None; if (GorgonGraphics.IsDebugEnabled) { flags = D3D11.DeviceCreationFlags.Debug; } using (Adapter warp = factory.GetWarpAdapter()) using (Adapter4 warpAdapter4 = warp.QueryInterface <Adapter4>()) using (var D3DDevice = new D3D11.Device(warpAdapter4, flags)) using (D3D11.Device5 D3DDevice5 = D3DDevice.QueryInterface <D3D11.Device5>()) { FeatureSet?featureSet = GetFeatureLevel(D3DDevice5); if (featureSet == null) { log.Print("WARNING: The WARP software adapter does not support the minimum feature set of 12.0. This device will be excluded.", LoggingLevel.All); return(null); } var result = new VideoAdapterInfo(index, warpAdapter4, featureSet.Value, new Dictionary <string, VideoOutputInfo>(), VideoDeviceType.Software); PrintLog(result, log); return(result); } }
/// <summary> /// Function to return the class name for the device. /// </summary> /// <param name="deviceName">The name of the device from <see cref="RawInputApi.GetDeviceName"/>.</param> /// <param name="log">The debug log file to use when logging issues.</param> /// <returns>The device class name.</returns> public static string GetDeviceClass(string deviceName, IGorgonLog log) { if (string.IsNullOrWhiteSpace(deviceName)) { throw new ArgumentException(Resources.GORINP_RAW_ERR_CANNOT_READ_DEVICE_DATA, nameof(deviceName)); } string[] regValue = deviceName.Split('#'); regValue[0] = regValue[0].Substring(4); // Don't add RDP devices. if ((log != null) && (regValue.Length > 0) && (regValue[1].StartsWith("RDP_", StringComparison.OrdinalIgnoreCase))) { log.Print("WARNING: This is an RDP device. Raw input in Gorgon is not supported under RDP. Skipping this device.", LoggingLevel.Verbose); return(string.Empty); } using (RegistryKey deviceKey = Registry.LocalMachine.OpenSubKey($@"System\CurrentControlSet\Enum\{regValue[0]}\{regValue[1]}\{regValue[2]}", false)) { if (deviceKey?.GetValue("DeviceDesc") == null) { return(string.Empty); } if (deviceKey.GetValue("Class") != null) { return(deviceKey.GetValue("Class").ToString()); } // Windows 8 no longer has a "Class" value in this area, so we need to go elsewhere to get it. if (deviceKey.GetValue("ClassGUID") == null) { return(string.Empty); } string classGUID = deviceKey.GetValue("ClassGUID").ToString(); if (string.IsNullOrWhiteSpace(classGUID)) { return(string.Empty); } using (RegistryKey classKey = Registry.LocalMachine.OpenSubKey($@"System\CurrentControlSet\Control\Class\{classGUID}")) { return(classKey?.GetValue("Class") == null ? string.Empty : classKey.GetValue("Class").ToString()); } } }
/// <summary> /// Function to unload a plugin by its name. /// </summary> /// <param name="plugin">The plugin to remove.</param> private void DisposePlugIn(GorgonPlugIn plugin) { if (!(plugin is IDisposable disposable)) { return; } disposable.Dispose(); _log.Print($"PlugIn '{plugin.Name}' disposed.", LoggingLevel.Verbose); }
/// <summary> /// Function to locate the application that can edit the specified file. /// </summary> /// <param name="workingFile">The file to edit.</param> /// <returns>The path to the executable.</returns> private string GetExecutable(IGorgonVirtualFile workingFile) { _log.Print($"Retrieving associated executable for files of type {workingFile.Extension}.", LoggingLevel.Verbose); string exePath = Win32API.GetAssociatedExecutable(workingFile.PhysicalFile.FullPath); if (string.IsNullOrWhiteSpace(exePath)) { _log.Print($"No executable found for files of type {workingFile.Extension}.", LoggingLevel.Verbose); return(null); } _log.Print($"Found executable path {exePath}.", LoggingLevel.Verbose); return(exePath); }
/// <summary> /// Function to create a new file system provider. /// </summary> /// <param name="providerPlugInName">The fully qualified type name of the plugin that contains the file system provider.</param> /// <returns>The new file system provider object, or if it was previously created, the previously created instance.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="providerPlugInName"/> is <b>null</b></exception> /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="providerPlugInName"/> is empty.</exception> /// <exception cref="GorgonException">Thrown when the plugin specified by the <paramref name="providerPlugInName"/> parameter was not found.</exception> public GorgonFileSystemProvider CreateProvider(string providerPlugInName) { if (providerPlugInName == null) { throw new ArgumentNullException(nameof(providerPlugInName)); } if (string.IsNullOrWhiteSpace(providerPlugInName)) { throw new ArgumentEmptyException(nameof(providerPlugInName)); } _log.Print("Creating file system provider '{0}'.", LoggingLevel.Simple, providerPlugInName); GorgonFileSystemProvider plugin = _pluginService.GetPlugIn <GorgonFileSystemProvider>(providerPlugInName); if (plugin == null) { throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GORFS_ERR_NO_PROVIDER_PLUGIN, providerPlugInName)); } return(plugin); }
/// <summary> /// Function to retrieve a list of mice. /// </summary> /// <returns>A read only list containing information about each mouse.</returns> public IReadOnlyList <IGorgonMouseInfo> EnumerateMice() { RAWINPUTDEVICELIST[] devices = RawInputApi.EnumerateRawInputDevices(RawInputType.Mouse); var result = new List <RawMouseInfo>(); for (int i = 0; i < devices.Length; i++) { RawMouseInfo info = GetDeviceInfo <RawMouseInfo>(ref devices[i]); if (info == null) { _log.Print("WARNING: Could not retrieve the class and device name. Skipping this device.", LoggingLevel.Verbose); continue; } _log.Print("Found mouse: '{0}' on HID path {1}, class {2}.", LoggingLevel.Verbose, info.Description, info.HIDPath, info.DeviceClass); result.Add(info); } return(result); }
/// <summary> /// Function that's called during idle time. /// </summary> /// <returns><b>true</b> to continue execution, <b>false</b> to stop.</returns> /// <remarks>This is the secondary default idle loop.</remarks> public static bool NewIdle() { if (_currentIdle != NewIdle) { _currentIdle = NewIdle; _log.Print("In new idle loop.", LoggingLevel.All); } var form = (formMain)GorgonApplication.ApplicationContext.MainForm; // Get our main form from the context. // Draw some bars every 16 ms. if ((GorgonTiming.MillisecondsSinceStart - _lastTime) >= 16.6f) { Color newColor = Color.Transparent; switch (_component) { case 0: newColor = Color.FromArgb(_color, 0, 0); break; case 1: newColor = Color.FromArgb(0, _color, 0); break; case 2: newColor = Color.FromArgb(0, 0, _color); break; } _lastTime = GorgonTiming.MillisecondsSinceStart; form.Draw(_lastX, _lastY, _lastX, (form.GraphicsSize.Height - 1) - (_lastY), newColor); _color += 3; _lastX++; if (_color >= 255) { _color = 0; _component++; if (_component > 2) { _component = 0; } } if (_lastX >= form.GraphicsSize.Width) { _lastX = 0; } _lastY = _rnd.Next(0, form.GraphicsSize.Height / 4); } form.Flip(); form.DrawFPS("Secondary Idle Loop - FPS: " + GorgonTiming.FPS.ToString("0.0")); return(true); }
/// <summary> /// Function to import an image file from the physical file system into the current image. /// </summary> /// <param name="codec">The codec used to open the file.</param> /// <param name="filePath">The path to the file to import.</param> /// <returns>The source file information, image data, the virtual file entry for the working file and the original pixel format of the file.</returns> public (FileInfo file, IGorgonImage image, IGorgonVirtualFile workingFile, BufferFormat originalFormat) ImportImage(IGorgonImageCodec codec, string filePath) { var file = new FileInfo(filePath); IGorgonImageCodec importCodec = codec; IGorgonImageInfo metaData = null; IGorgonVirtualFile workFile = null; IGorgonImage importImage = null; string workFilePath = $"{Path.GetFileNameWithoutExtension(filePath)}_import_{Guid.NewGuid().ToString("N")}"; // Try to determine if we can actually read the file using an installed codec, if we can't, then try to find a suitable codec. using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { if ((importCodec == null) || (!importCodec.IsReadable(stream))) { importCodec = null; foreach (IGorgonImageCodec newCodec in InstalledCodecs.Codecs.Where(item => (item.CodecCommonExtensions.Count > 0) && (item.CanDecode))) { if (newCodec.IsReadable(stream)) { importCodec = newCodec; break; } } } if (importCodec == null) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_NO_CODEC, filePath)); } metaData = importCodec.GetMetaData(stream); // We absolutely need to have an extension, or else the texconv tool will not work. var codecExtension = new GorgonFileExtension(importCodec.CodecCommonExtensions[0]); _log.Print($"Adding {codecExtension.Extension} extension to working file or else external tools may not be able to read it.", LoggingLevel.Verbose); workFilePath = $"{workFilePath}.{codecExtension.Extension}"; using (Stream outStream = ScratchArea.OpenStream(workFilePath, FileMode.Create)) { stream.CopyTo(outStream); } } workFile = ScratchArea.FileSystem.GetFile(workFilePath); var formatInfo = new GorgonFormatInfo(metaData.Format); // This is always in DDS format. if (formatInfo.IsCompressed) { _log.Print($"Image is compressed using [{formatInfo.Format}] as its pixel format.", LoggingLevel.Intermediate); if (_compressor == null) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format)); } _log.Print($"Loading image '{workFile.FullPath}'...", LoggingLevel.Simple); importImage = _compressor.Decompress(ref workFile, metaData); if (importImage == null) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format)); } _log.Print($"Loaded compressed ([{formatInfo.Format}]) image data as [{importImage.Format}]", LoggingLevel.Intermediate); } else { using (Stream workStream = workFile.OpenStream()) { importImage = importCodec.LoadFromStream(workStream); } } return(file, importImage, workFile, metaData.Format); }
/// <summary>Function to load all the specified plug in assemblies.</summary> /// <param name="pluginCache">The plugin cache that will hold the plug in assembies.</param> /// <param name="pluginAssemblyFiles">The list of plug in assembly paths to load.</param> /// <param name="log">The application logging interface.</param> /// <returns>A list of <see cref="PlugInAssemblyState"/> objects for each plug in assembly loaded.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="pluginAssemblyFiles" /> parameter is <b>null</b></exception> public static IReadOnlyList <PlugInAssemblyState> ValidateAndLoadAssemblies(this GorgonMefPlugInCache pluginCache, IEnumerable <FileInfo> pluginAssemblyFiles, IGorgonLog log) { if (pluginAssemblyFiles == null) { throw new ArgumentNullException(nameof(pluginAssemblyFiles)); } var records = new List <PlugInAssemblyState>(); // We use this to determine whether the plug in can be loaded into the current platform. AssemblyPlatformType currentPlatform = IntPtr.Size == 8 ? AssemblyPlatformType.x64 : AssemblyPlatformType.x86; foreach (FileInfo file in pluginAssemblyFiles) { // If we've already got the assembly loaded into this cache, then there's no need to try and load it. if (pluginCache.PlugInAssemblies?.Any(item => string.Equals(item, file.FullName, StringComparison.OrdinalIgnoreCase)) ?? false) { continue; } if (!file.Exists) { log.Print($"[ERROR] Plug in '{file.FullName}' was not found.", LoggingLevel.Verbose); records.Add(new PlugInAssemblyState(file.FullName, Resources.GOREDIT_PLUGIN_LOAD_FAIL_NOT_FOUND, false)); continue; } (bool isManaged, AssemblyPlatformType platformType) = GorgonMefPlugInCache.IsManagedAssembly(file.FullName); if ((!isManaged) || (platformType == AssemblyPlatformType.Unknown)) { log.Print($"[WARNING] Skipping '{file.FullName}'. Not a valid .NET assembly.", LoggingLevel.Verbose); records.Add(new PlugInAssemblyState(file.FullName, string.Format(Resources.GOREDIT_PLUGIN_LOAD_FAIL_NOT_DOT_NET, file.Name), false)); continue; } // Ensure that our platform type matches (AnyCPU is exempt and will always run, and DLLs don't allow Prefer 32 bit). if ((currentPlatform == AssemblyPlatformType.x86) && (platformType == AssemblyPlatformType.x64)) { log.Print($"[ERROR] Cannot load assembly '{file.FullName}', currently executing in an x86 environment, but the assembly is x64.", LoggingLevel.Simple); records.Add(new PlugInAssemblyState(file.FullName, string.Format(Resources.GOREDIT_PLUGIN_LOAD_FAIL_PLATFORM_MISMATCH, file.Name, platformType, currentPlatform), true)); continue; } if ((currentPlatform == AssemblyPlatformType.x64) && (platformType == AssemblyPlatformType.x86)) { log.Print($"[ERROR] Cannot load assembly '{file.FullName}', currently executing in an x64 environment, but the assembly is x86.", LoggingLevel.Simple); records.Add(new PlugInAssemblyState(file.FullName, string.Format(Resources.GOREDIT_PLUGIN_LOAD_FAIL_PLATFORM_MISMATCH, file.Name, platformType, currentPlatform), true)); continue; } try { log.Print($"Loading plug in assembly '{file.FullName}'...", LoggingLevel.Simple); pluginCache.LoadPlugInAssemblies(file.DirectoryName, file.Name); records.Add(new PlugInAssemblyState(file.FullName, string.Empty, true)); } catch (Exception ex) { log.Print($"ERROR: Cannot load plug in assembly '{file.FullName}'.", LoggingLevel.Simple); log.LogException(ex); records.Add(new PlugInAssemblyState(file.FullName, string.Format(Resources.GOREDIT_PLUGIN_LOAD_FAIL_EXCEPTION, file.Name, ex.Message), true)); } } return(records); }
/// <summary> /// Function to print device log information. /// </summary> /// <param name="device">Device to print.</param> /// <param name="log">The log interface to output debug messages.</param> private static void PrintLog(VideoAdapterInfo device, IGorgonLog log) { log.Print($"Device found: {device.Name}", LoggingLevel.Simple); log.Print("===================================================================", LoggingLevel.Simple); log.Print($"Supported feature set: {device.FeatureSet}", LoggingLevel.Simple); log.Print($"Video memory: {(device.Memory.Video).FormatMemory()}", LoggingLevel.Simple); log.Print($"System memory: {(device.Memory.System).FormatMemory()}", LoggingLevel.Intermediate); log.Print($"Shared memory: {(device.Memory.Shared).FormatMemory()}", LoggingLevel.Intermediate); log.Print($"Device ID: 0x{device.PciInfo.DeviceID.FormatHex()}", LoggingLevel.Verbose); log.Print($"Sub-system ID: 0x{device.PciInfo.SubSystemID.FormatHex()}", LoggingLevel.Verbose); log.Print($"Vendor ID: 0x{device.PciInfo.VendorID.FormatHex()}", LoggingLevel.Verbose); log.Print($"Revision: {device.PciInfo.Revision}", LoggingLevel.Verbose); log.Print($"Unique ID: 0x{device.Luid.FormatHex()}", LoggingLevel.Verbose); log.Print("===================================================================", LoggingLevel.Simple); foreach (IGorgonVideoOutputInfo output in device.Outputs) { log.Print($"Found output '{output.Name}'.", LoggingLevel.Simple); log.Print("===================================================================", LoggingLevel.Verbose); log.Print($"Output bounds: ({output.DesktopBounds.Left}x{output.DesktopBounds.Top})-({output.DesktopBounds.Right}x{output.DesktopBounds.Bottom})", LoggingLevel.Verbose); log.Print($"Monitor handle: 0x{output.MonitorHandle.FormatHex()}", LoggingLevel.Verbose); log.Print($"Attached to desktop: {output.IsAttachedToDesktop}", LoggingLevel.Verbose); log.Print($"Monitor rotation: {output.Rotation}", LoggingLevel.Verbose); log.Print("===================================================================", LoggingLevel.Simple); log.Print($"Retrieving video modes for output '{output.Name}'...", LoggingLevel.Simple); log.Print("===================================================================", LoggingLevel.Simple); foreach (GorgonVideoMode mode in output.VideoModes) { log.Print($"{mode.ToString().PadRight(70)}\tScaling: {mode.Scaling.ToString().PadRight(20)}Scanline Order: {mode.ScanlineOrder.ToString().PadRight(25)}Stereo: {mode.SupportsStereo}", LoggingLevel.Verbose); } log.Print("===================================================================", LoggingLevel.Verbose); log.Print($"Found {output.VideoModes.Count} video modes for output '{output.Name}'.", LoggingLevel.Simple); log.Print("===================================================================", LoggingLevel.Simple); } }
/// <summary> /// Function to perform an enumeration of the video adapters attached to the system and populate this list. /// </summary> /// <param name="enumerateWARPDevice"><b>true</b> to enumerate the WARP software device, or <b>false</b> to exclude it.</param> /// <param name="log">The log that will capture debug logging messages.</param> /// <remarks> /// <para> /// Use this method to populate a list with information about the video adapters installed in the system. /// </para> /// <para> /// You may include the WARP device, which is a software based device that emulates most of the functionality of a video adapter, by setting the <paramref name="enumerateWARPDevice"/> to <b>true</b>. /// </para> /// <para> /// Gorgon requires a video adapter that is capable of supporting Direct 3D 12.0 at minimum. If no suitable devices are found installed in the computer, then the resulting list will be empty. /// </para> /// </remarks> public static IReadOnlyList <IGorgonVideoAdapterInfo> Enumerate(bool enumerateWARPDevice, IGorgonLog log) { var devices = new List <IGorgonVideoAdapterInfo>(); if (log == null) { log = GorgonLog.NullLog; } using (var factory2 = new Factory2(GorgonGraphics.IsDebugEnabled)) using (Factory5 factory5 = factory2.QueryInterface <Factory5>()) { int adapterCount = factory5.GetAdapterCount1(); log.Print("Enumerating video adapters...", LoggingLevel.Simple); // Begin gathering device information. for (int i = 0; i < adapterCount; i++) { // Get the video adapter information. using (Adapter1 adapter1 = factory5.GetAdapter1(i)) using (Adapter4 adapter = adapter1.QueryInterface <Adapter4>()) { // ReSharper disable BitwiseOperatorOnEnumWithoutFlags if (((adapter.Desc3.Flags & AdapterFlags3.Remote) == AdapterFlags3.Remote) || ((adapter.Desc3.Flags & AdapterFlags3.Software) == AdapterFlags3.Software)) { continue; } // ReSharper restore BitwiseOperatorOnEnumWithoutFlags D3D11.DeviceCreationFlags flags = D3D11.DeviceCreationFlags.None; if (GorgonGraphics.IsDebugEnabled) { flags = D3D11.DeviceCreationFlags.Debug; } // We create a D3D device here to filter out unsupported video modes from the format list. using (var D3DDevice = new D3D11.Device(adapter, flags, D3D.FeatureLevel.Level_12_1, D3D.FeatureLevel.Level_12_0)) using (D3D11.Device5 D3DDevice5 = D3DDevice.QueryInterface <D3D11.Device5>()) { D3DDevice5.DebugName = "Output enumerator device."; FeatureSet?featureSet = GetFeatureLevel(D3DDevice5); // Do not enumerate this device if its feature set is not supported. if (featureSet == null) { log.Print("This video adapter is not supported by Gorgon and will be skipped.", LoggingLevel.Verbose); continue; } Dictionary <string, VideoOutputInfo> outputs = GetOutputs(D3DDevice5, adapter, adapter.GetOutputCount(), log); if (outputs.Count <= 0) { log.Print($"WARNING: Video adapter {adapter.Description1.Description.Replace("\0", string.Empty)} has no outputs. Full screen mode will not be possible.", LoggingLevel.Verbose); } var videoAdapter = new VideoAdapterInfo(i, adapter, featureSet.Value, outputs, VideoDeviceType.Hardware); devices.Add(videoAdapter); PrintLog(videoAdapter, log); } } } // Get software devices. if (!enumerateWARPDevice) { return(devices); } VideoAdapterInfo device = GetWARPSoftwareDevice(devices.Count, factory5, log); if (device != null) { devices.Add(device); } } log.Print("Found {0} video adapters.", LoggingLevel.Simple, devices.Count); return(devices); }