using System; using System.Linq; using NvAPIWrapper.Native; using NvAPIWrapper.Native.Display.Structures; using NvAPIWrapper.Native.Exceptions; using NvAPIWrapper.Native.General; using NvAPIWrapper.Native.Interfaces.Mosaic; using NvAPIWrapper.Native.Mosaic; using NvAPIWrapper.Native.Mosaic.Structures; namespace NvAPIWrapper.Mosaic { /// /// Represents a mosaic grid topology /// public class GridTopology : IEquatable { /// /// Creates a new GridTopology /// /// Mosaic rows /// Mosaic columns /// Topology displays public GridTopology(int rows, int columns, GridTopologyDisplay[] displays) { SetDisplays(rows, columns, displays); var possibleDisplaySettings = GetPossibleDisplaySettings(); if (possibleDisplaySettings.Any()) { SetDisplaySettings( possibleDisplaySettings.OrderByDescending( settings => (long) settings.Width * settings.Height * settings.BitsPerPixel * settings.Frequency) .First()); } } /// /// Creates a new GridTopology /// /// A IGridTopology implamented object public GridTopology(IGridTopology gridTopology) { Rows = gridTopology.Rows; Columns = gridTopology.Columns; Displays = gridTopology.Displays.Select(display => new GridTopologyDisplay(display)).ToArray(); SetDisplaySettings(gridTopology.DisplaySettings); ApplyWithBezelCorrectedResolution = gridTopology.ApplyWithBezelCorrectedResolution; ImmersiveGaming = gridTopology.ImmersiveGaming; BaseMosaicPanoramic = gridTopology.BaseMosaicPanoramic; DriverReloadAllowed = gridTopology.DriverReloadAllowed; AcceleratePrimaryDisplay = gridTopology.AcceleratePrimaryDisplay; } /// /// Gets or sets a boolean value enabling SLI acceleration on the primary display while in single-wide mode (For /// Immersive Gaming only). /// public bool AcceleratePrimaryDisplay { get; set; } /// /// Gets or sets a boolean value forcing to the bezel-corrected resolution when enabling and doing the modeset /// public bool ApplyWithBezelCorrectedResolution { get; set; } /// /// Gets or sets a boolean value enabling the Base Mosaic (Panoramic) instead of Mosaic SLI (for NVS and Quadro-boards /// only) /// public bool BaseMosaicPanoramic { get; set; } /// /// Gets the mosaic columns /// public int Columns { get; private set; } /// /// Gets topology displays /// public GridTopologyDisplay[] Displays { get; private set; } /// /// Gets or sets a boolean value allowing the API to, if necessary, realod the driver (for Vista and above only). Will /// not be persisted. Value undefined on get. /// public bool DriverReloadAllowed { get; set; } /// /// Gets the topology Frequency /// public int Frequency { get; private set; } /// /// Gets or sets a boolean value enabling as immersive gaming instead of Mosaic SLI (for Quadro-boards only) /// public bool ImmersiveGaming { get; set; } /// /// Gets the topology resolution /// public Resolution Resolution { get; private set; } /// /// Gets the mosaic rows /// public int Rows { get; private set; } /// public bool Equals(GridTopology other) { if (ReferenceEquals(null, other)) { return false; } if (ReferenceEquals(this, other)) { return true; } return Resolution.Equals(other.Resolution) && Frequency == other.Frequency && Rows == other.Rows && Columns == other.Columns && Displays.SequenceEqual(other.Displays) && ApplyWithBezelCorrectedResolution == other.ApplyWithBezelCorrectedResolution && ImmersiveGaming == other.ImmersiveGaming && BaseMosaicPanoramic == other.BaseMosaicPanoramic && AcceleratePrimaryDisplay == other.AcceleratePrimaryDisplay; } /// /// Retrieves a list of currently active mosaic grid topologies /// /// An array of GridTopology objects public static GridTopology[] GetGridTopologies() { return MosaicApi.EnumDisplayGrids().Select(topology => new GridTopology(topology)).ToArray(); } /// /// Checks for equality between two objects of same type /// /// The first object /// The second object /// true, if both objects are equal, otherwise false public static bool operator ==(GridTopology left, GridTopology right) { return right?.Equals(left) ?? ReferenceEquals(left, null); } /// /// Checks for inequality between two objects of same type /// /// The first object /// The second object /// true, if both objects are not equal, otherwise false public static bool operator !=(GridTopology left, GridTopology right) { return !(left == right); } /// /// Applies the requested grid topologies /// /// An array of grid topologies to apply /// SetDisplayTopologyFlag flag public static void SetGridTopologies( GridTopology[] grids, SetDisplayTopologyFlag flags = SetDisplayTopologyFlag.NoFlag) { var gridTopologyV2 = grids.Select(grid => grid.GetGridTopologyV2()).Cast().ToArray(); try { MosaicApi.SetDisplayGrids(gridTopologyV2, flags); } catch (NVIDIAApiException ex) { if (ex.Status != Status.IncompatibleStructureVersion) { throw; } } catch (NVIDIANotSupportedException) { // ignore } var gridTopologyV1 = grids.Select(grid => grid.GetGridTopologyV1()).Cast().ToArray(); MosaicApi.SetDisplayGrids(gridTopologyV1, flags); } /// /// Validates a list of grid topologies /// /// An array of grid topologies to validate /// SetDisplayTopologyFlag flag /// An array of DisplayTopologyStatus object containing the result of the validation public static DisplayTopologyStatus[] ValidateGridTopologies( GridTopology[] grids, SetDisplayTopologyFlag flags = SetDisplayTopologyFlag.AllowInvalid) { var gridTopologyV2 = grids.Select(grid => grid.GetGridTopologyV2()).Cast().ToArray(); try { return MosaicApi.ValidateDisplayGrids(gridTopologyV2, flags); } catch (NVIDIAApiException ex) { if (ex.Status != Status.IncompatibleStructureVersion) { throw; } } catch (NVIDIANotSupportedException) { // ignore } var gridTopologyV1 = grids.Select(grid => grid.GetGridTopologyV1()).Cast().ToArray(); return MosaicApi.ValidateDisplayGrids(gridTopologyV1, flags); } /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (obj.GetType() != GetType()) { return false; } return Equals((GridTopology) obj); } /// public override int GetHashCode() { unchecked { var hashCode = Resolution.GetHashCode(); hashCode = (hashCode * 397) ^ Frequency; hashCode = (hashCode * 397) ^ Rows; hashCode = (hashCode * 397) ^ Columns; hashCode = (hashCode * 397) ^ (Displays?.GetHashCode() ?? 0); hashCode = (hashCode * 397) ^ ApplyWithBezelCorrectedResolution.GetHashCode(); hashCode = (hashCode * 397) ^ ImmersiveGaming.GetHashCode(); hashCode = (hashCode * 397) ^ BaseMosaicPanoramic.GetHashCode(); hashCode = (hashCode * 397) ^ AcceleratePrimaryDisplay.GetHashCode(); return hashCode; } } /// /// Creates and fills a DisplaySettingsV1 object /// /// The newly created DisplaySettingsV1 object public DisplaySettingsV1 GetDisplaySettingsV1() { return new DisplaySettingsV1(Resolution.Width, Resolution.Height, Resolution.ColorDepth, Frequency); } /// /// Creates and fills a GridTopologyV1 object /// /// The newly created GridTopologyV1 object public GridTopologyV1 GetGridTopologyV1() { var displaySettings = GetDisplaySettingsV1(); return new GridTopologyV1(Rows, Columns, Displays.Select(display => display.GetGridTopologyDisplayV1()).ToArray(), displaySettings, ApplyWithBezelCorrectedResolution, ImmersiveGaming, BaseMosaicPanoramic, DriverReloadAllowed, AcceleratePrimaryDisplay); } /// /// Creates and fills a GridTopologyV2 object /// /// The newly created GridTopologyV2 object public GridTopologyV2 GetGridTopologyV2() { var displaySettings = GetDisplaySettingsV1(); return new GridTopologyV2(Rows, Columns, Displays.Select(display => display.GetGridTopologyDisplayV2()).ToArray(), displaySettings, ApplyWithBezelCorrectedResolution, ImmersiveGaming, BaseMosaicPanoramic, DriverReloadAllowed, AcceleratePrimaryDisplay, Displays.Any(display => display.PixelShiftType != PixelShiftType.NoPixelShift)); } /// /// Retrieves a list of possible display settings for this topology /// /// An array of IDisplaySettings implamented objects public IDisplaySettings[] GetPossibleDisplaySettings() { var gridTopologyV2 = GetGridTopologyV2(); try { return MosaicApi.EnumDisplayModes(gridTopologyV2); } catch (NVIDIAApiException ex) { if (ex.Status != Status.IncompatibleStructureVersion) { throw; } } catch (NVIDIANotSupportedException) { // ignore } var gridTopologyV1 = GetGridTopologyV1(); return MosaicApi.EnumDisplayModes(gridTopologyV1); } /// /// Changes topology arrangement and displays /// /// Mosaic rows /// Mosaic columns /// Topology displays /// Invalid display arrangement. /// Number of displays should match the arrangement. public void SetDisplays(int rows, int columns, GridTopologyDisplay[] displays) { if (rows * columns <= 0) { throw new ArgumentOutOfRangeException($"{nameof(rows)}, {nameof(columns)}", "Invalid display arrangement."); } if (displays.Length != rows * columns) { throw new ArgumentException("Number of displays should match the arrangement.", nameof(displays)); } Rows = rows; Columns = columns; Displays = displays; } /// /// Changes display settings for the topology /// /// Display settings to use public void SetDisplaySettings(IDisplaySettings displaySettings) { Resolution = new Resolution(displaySettings.Width, displaySettings.Height, displaySettings.BitsPerPixel); Frequency = displaySettings.Frequency; } } }