/// <summary> /// public helper function to build a device settings structure based upon the match /// options. If the match option is set to ignore, then a optimal default value is used. /// The default value may not exist on the system, but later this will be taken /// into account. /// </summary> private DeviceSettings BuildOptimalDeviceSettings(DeviceSettings settings, MatchOptions match) { DeviceSettings optimal = new DeviceSettings(); // This will be what we return optimal.presentParams = new PresentParameters(); //--------------------- // Adapter ordinal //--------------------- if (match.AdapterOrdinal == MatchType.IgnoreInput) optimal.AdapterOrdinal = 0; else optimal.AdapterOrdinal = settings.AdapterOrdinal; //--------------------- // Device type //--------------------- if (match.DeviceType == MatchType.IgnoreInput) optimal.DeviceType = DeviceType.Hardware; else optimal.DeviceType = settings.DeviceType; //--------------------- // Windowed //--------------------- if (match.Windowed == MatchType.IgnoreInput) optimal.presentParams.Windowed = true; else optimal.presentParams.Windowed = settings.presentParams.Windowed; //--------------------- // Adapter format //--------------------- if (match.AdapterFormat == MatchType.IgnoreInput) { // If windowed, default to the desktop display mode // If fullscreen, default to the desktop display mode for quick mode change or // default to Format.X8R8G8B8 if the desktop display mode is < 32bit DisplayMode adapterDesktopMode = Manager.Adapters[(int)optimal.AdapterOrdinal].CurrentDisplayMode; if (optimal.presentParams.Windowed || ManagedUtility.GetColorChannelBits(adapterDesktopMode.Format) >= 8 ) optimal.AdapterFormat = adapterDesktopMode.Format; else optimal.AdapterFormat = Format.X8R8G8B8; } else { optimal.AdapterFormat = settings.AdapterFormat; } //--------------------- // Vertex processing //--------------------- if (match.VertexProcessing == MatchType.IgnoreInput) optimal.BehaviorFlags = CreateFlags.HardwareVertexProcessing; else optimal.BehaviorFlags = settings.BehaviorFlags; //--------------------- // Resolution //--------------------- if (match.Resolution == MatchType.IgnoreInput) { // If windowed, default to 640x480 // If fullscreen, default to the desktop res for quick mode change if (optimal.presentParams.Windowed ) { optimal.presentParams.BackBufferWidth = DefaultSizeWidth; optimal.presentParams.BackBufferHeight = DefaultSizeHeight; } else { DisplayMode adapterDesktopMode = Manager.Adapters[(int)optimal.AdapterOrdinal].CurrentDisplayMode; optimal.presentParams.BackBufferWidth = adapterDesktopMode.Width; optimal.presentParams.BackBufferHeight = adapterDesktopMode.Height; } } else { optimal.presentParams.BackBufferWidth = settings.presentParams.BackBufferWidth; optimal.presentParams.BackBufferHeight = settings.presentParams.BackBufferHeight; } //--------------------- // Back buffer format //--------------------- if (match.BackBufferFormat == MatchType.IgnoreInput) optimal.presentParams.BackBufferFormat = optimal.AdapterFormat; // Default to match the adapter format else optimal.presentParams.BackBufferFormat = settings.presentParams.BackBufferFormat; //--------------------- // Back buffer count //--------------------- if (match.BackBufferCount == MatchType.IgnoreInput) optimal.presentParams.BackBufferCount = 2; // Default to triple buffering for perf gain else optimal.presentParams.BackBufferCount = settings.presentParams.BackBufferCount; //--------------------- // Multisample //--------------------- if (match.MultiSample == MatchType.IgnoreInput) optimal.presentParams.MultiSampleQuality = 0; // Default to no multisampling else optimal.presentParams.MultiSampleQuality = settings.presentParams.MultiSampleQuality; //--------------------- // Swap effect //--------------------- if (match.SwapEffect == MatchType.IgnoreInput) optimal.presentParams.SwapEffect = SwapEffect.Discard; else optimal.presentParams.SwapEffect = settings.presentParams.SwapEffect; //--------------------- // Depth stencil //--------------------- if (match.DepthFormat == MatchType.IgnoreInput && match.StencilFormat == MatchType.IgnoreInput) { uint backBufferBits = ManagedUtility.GetColorChannelBits(optimal.presentParams.BackBufferFormat); if (backBufferBits >= 8) optimal.presentParams.AutoDepthStencilFormat = DepthFormat.D32; else optimal.presentParams.AutoDepthStencilFormat = DepthFormat.D16; } else { optimal.presentParams.AutoDepthStencilFormat = settings.presentParams.AutoDepthStencilFormat; } //--------------------- // Present flags //--------------------- if (match.PresentFlags == MatchType.IgnoreInput) optimal.presentParams.PresentFlag = PresentFlag.DiscardDepthStencil; else optimal.presentParams.PresentFlag = settings.presentParams.PresentFlag; //--------------------- // Refresh rate //--------------------- if (match.RefreshRate == MatchType.IgnoreInput) optimal.presentParams.FullScreenRefreshRateInHz = 0; else optimal.presentParams.FullScreenRefreshRateInHz = settings.presentParams.FullScreenRefreshRateInHz; //--------------------- // Present interval //--------------------- if (match.PresentInterval == MatchType.IgnoreInput) { // For windowed, default to PresentInterval.Immediate // which will wait not for the vertical retrace period to prevent tearing, // but may introduce tearing. // For full screen, default to PresentInterval.Default // which will wait for the vertical retrace period to prevent tearing. if (optimal.presentParams.Windowed ) optimal.presentParams.PresentationInterval = PresentInterval.Immediate; else optimal.presentParams.PresentationInterval = PresentInterval.Default; } else { optimal.presentParams.PresentationInterval = settings.presentParams.PresentationInterval; } return optimal; }
/// <summary> /// Toggle between Hardware and Reference /// </summary> public void ToggleReference() { // Get the current device settings DeviceSettings currentSettings = State.CurrentDeviceSettings.Clone(); if (currentSettings.DeviceType == DeviceType.Hardware) currentSettings.DeviceType = DeviceType.Reference; else if (currentSettings.DeviceType == DeviceType.Reference) currentSettings.DeviceType = DeviceType.Hardware; MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.PreserveInput; match.DeviceType = MatchType.PreserveInput; match.Windowed = MatchType.ClosestToInput; match.AdapterFormat = MatchType.ClosestToInput; match.VertexProcessing = MatchType.ClosestToInput; match.Resolution = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.ClosestToInput; match.BackBufferCount = MatchType.ClosestToInput; match.MultiSample = MatchType.ClosestToInput; match.SwapEffect = MatchType.ClosestToInput; match.DepthFormat = MatchType.ClosestToInput; match.StencilFormat = MatchType.ClosestToInput; match.PresentFlags = MatchType.ClosestToInput; match.RefreshRate = MatchType.ClosestToInput; match.PresentInterval = MatchType.ClosestToInput; try { currentSettings = FindValidDeviceSettings(currentSettings, match); ChangeDevice(currentSettings, null, false); } #if(DEBUG) catch (Exception e) { // In debug mode show this error (maybe - depending on settings) DisplayErrorMessage(e); #else catch { // In release mode fail silently #endif } finally { // Update the current settings State.CurrentDeviceSettings = currentSettings; } }
/// <summary> /// This function tries to find valid device settings based upon the input device settings /// struct and the match options. For each device setting a match option in the /// MatchOptions struct specifies how the function makes decisions. For example, if /// the caller wants a hardware device with a back buffer format of A2B10G10R10 but the /// hardware device on the system does not support A2B10G10R10 however a reference device is /// installed that does, then the function has a choice to either use the reference device /// or to change to a back buffer format to compatible with the hardware device. The match options lets the /// caller control how these choices are made. /// /// Each match option must be one of the following types: /// MatchType.IgnoreInput: Uses the closest valid value to a default /// MatchType.PreserveInput: Uses the input without change, but may cause no valid device to be found /// MatchType.ClosestToInput: Uses the closest valid value to the input /// </summary> private DeviceSettings FindValidDeviceSettings(DeviceSettings settings, MatchOptions match) { // Build an optimal device settings structure based upon the match // options. If the match option is set to ignore, then a optimal default value is used. // The default value may not exist on the system, but later this will be taken // into account. DeviceSettings optimalSettings = BuildOptimalDeviceSettings(settings, match); float bestRanking = -1.0f; EnumDeviceSettingsCombo bestDeviceSettingsCombo = new EnumDeviceSettingsCombo(); // Find the best combination of: // Adapter Ordinal // Device Type // Adapter Format // Back Buffer Format // Windowed // given what's available on the system and the match options combined with the device settings input. // This combination of settings is encapsulated by the EnumDeviceSettingsCombo class. DisplayMode adapterDesktopDisplayMode; for (int iAdapter = 0; iAdapter < Enumeration.AdapterInformationList.Count; iAdapter++) { EnumAdapterInformation adapterInfo = Enumeration.AdapterInformationList[iAdapter] as EnumAdapterInformation; // Get the desktop display mode of the adapter adapterDesktopDisplayMode = Manager.Adapters[(int)adapterInfo.AdapterOrdinal].CurrentDisplayMode; // Enum all the device types supported by this adapter to find the best device settings for (int iDeviceInfo = 0; iDeviceInfo < adapterInfo.deviceInfoList.Count; iDeviceInfo++) { EnumDeviceInformation deviceInfo = adapterInfo.deviceInfoList[iDeviceInfo] as EnumDeviceInformation; for (int iDeviceCombo = 0; iDeviceCombo<deviceInfo.deviceSettingsList.Count; iDeviceCombo++) { EnumDeviceSettingsCombo deviceSettings = deviceInfo.deviceSettingsList[iDeviceCombo] as EnumDeviceSettingsCombo; // If windowed mode the adapter format has to be the same as the desktop // display mode format so skip any that don't match if (deviceSettings.IsWindowed && (deviceSettings.AdapterFormat != adapterDesktopDisplayMode.Format)) continue; // Skip any combo that doesn't meet the preserve match options if(!DoesDeviceComboMatchPreserveOptions(deviceSettings, settings, match)) continue; // Get a ranking number that describes how closely this device combo matches the optimal combo float curRanking = RankDeviceCombo(deviceSettings, optimalSettings, adapterDesktopDisplayMode); // If this combo better matches the input device settings then save it if (curRanking > bestRanking ) { bestDeviceSettingsCombo = deviceSettings; bestRanking = curRanking; } } } } // If no best device combination was found then fail if (bestRanking == -1.0f) { throw new NoCompatibleDevicesException(); } // Using the best device settings combo found, build valid device settings taking heed of // the match options and the input device settings return BuildValidDeviceSettings(bestDeviceSettingsCombo, settings, match); }
/// <summary> /// Checks to see if the window changed monitors, and if it did it creates a device /// from the monitor's adapter and recreates the scene. /// </summary> private void CheckForWindowMonitorChange() { // Don't do this if the user doesn't want it if (!State.CanAutoChangeAdapter) return; IntPtr monitorHandle = NativeMethods.MonitorFromWindow(Window.Handle, 1); // Primary monitor if (monitorHandle != State.AdapterMonitor) { // Changing device, pause Pause(true, true); // Get the new ordinal uint newOrdinal = GetAdapterOridinalFromMonitor(monitorHandle); // Get the current device settings and flip the ordinal then // find the closest valid device settings with this change DeviceSettings settings = State.CurrentDeviceSettings.Clone(); settings.AdapterOrdinal = newOrdinal; // Set up the match options MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.PreserveInput; match.DeviceType = MatchType.ClosestToInput; match.Windowed = MatchType.ClosestToInput; match.AdapterFormat = MatchType.ClosestToInput; match.VertexProcessing = MatchType.ClosestToInput; match.Resolution = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.ClosestToInput; match.BackBufferCount = MatchType.ClosestToInput; match.MultiSample = MatchType.ClosestToInput; match.SwapEffect = MatchType.ClosestToInput; match.DepthFormat = MatchType.ClosestToInput; match.StencilFormat = MatchType.ClosestToInput; match.PresentFlags = MatchType.ClosestToInput; match.RefreshRate = MatchType.ClosestToInput; match.PresentInterval = MatchType.ClosestToInput; try { // Find some valid settings (if possible) settings = FindValidDeviceSettings(settings, match); try { // Create a Direct3D device using the new device settings. // If there is an existing device, then it will either reset or recreate the scene. ChangeDevice(settings, null, false); } catch (Exception e) { // This is a much worse error.. Shut down and fail Dispose(); Pause(false, false); System.Diagnostics.Debugger.Log(0, string.Empty, e.ToString()); return; } } catch { // No valid device was found for this monitor, that's bad, but not fatal. // Ignore this error } } Pause(false, false); }
/// <summary> /// Toggle between full screen and windowed /// </summary> public void ToggleFullscreen() { // Pause the application Pause(true, true); // Get the current device settings and flip the windowed state then // find the closest valid device settings with this change DeviceSettings currentSettings = State.CurrentDeviceSettings.Clone(); currentSettings.presentParams.Windowed = !currentSettings.presentParams.Windowed; MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.PreserveInput; match.DeviceType = MatchType.ClosestToInput; match.Windowed = MatchType.PreserveInput; match.AdapterFormat = MatchType.IgnoreInput; match.VertexProcessing = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.IgnoreInput; match.BackBufferCount = MatchType.ClosestToInput; match.MultiSample = MatchType.ClosestToInput; match.SwapEffect = MatchType.ClosestToInput; match.DepthFormat = MatchType.ClosestToInput; match.StencilFormat = MatchType.ClosestToInput; match.PresentFlags = MatchType.ClosestToInput; match.RefreshRate = MatchType.IgnoreInput; match.PresentInterval = MatchType.IgnoreInput; System.Drawing.Rectangle windowClient; if (currentSettings.presentParams.Windowed) windowClient = State.ClientRectangle; else windowClient = State.FullScreenClientRectangle; if (windowClient.Width > 0 && windowClient.Height > 0) { match.Resolution = MatchType.ClosestToInput; currentSettings.presentParams.BackBufferWidth = windowClient.Width; currentSettings.presentParams.BackBufferHeight = windowClient.Height; } else { match.Resolution = MatchType.IgnoreInput; } try { if ( (!currentSettings.presentParams.Windowed) && (State.IsMaximized)) { if (WindowForm != null) { WindowForm.WindowState = System.Windows.Forms.FormWindowState.Normal; } toggleMaximized = true; } currentSettings = FindValidDeviceSettings(currentSettings, match); ChangeDevice(currentSettings, null, false); Window.Size = State.WindowBoundsRectangle.Size; Window.Location = State.WindowBoundsRectangle.Location; if (currentSettings.presentParams.Windowed) { if (toggleMaximized) { if (WindowForm != null) { WindowForm.WindowState = System.Windows.Forms.FormWindowState.Maximized; } } toggleMaximized = false; } } finally { // Well, unpause no matter what Pause(false, false); State.CurrentDeviceSettings = currentSettings; } }
/// <summary> /// Tells the framework to change to a device created from the passed in device settings /// If CreateWindow() has not already been called, it will call it with the /// default parameters. Instead of calling this, you can call CreateDevice() /// or SetDevice() /// </summary> public void CreateDeviceFromSettings(DeviceSettings deviceSettings, bool preserveInput) { // Set the state since this was called State.WasDeviceCreateCalled = true; // Was the window created? If not, create it now if (!State.WasWindowCreated) { // If CreateWindow or SetWindow was already called and failed, then fail again. if (State.WasWindowCreateCalled) { throw new InvalidOperationException("CreateWindow was already called and failed."); } // Create a default window CreateWindow("Direct3D Window", null, null, -1, -1); } if (!preserveInput) { // If not preserving the input, the find the closest valid to it MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.ClosestToInput; match.DeviceType = MatchType.ClosestToInput; match.Windowed = MatchType.ClosestToInput; match.AdapterFormat = MatchType.ClosestToInput; match.VertexProcessing = MatchType.ClosestToInput; match.Resolution = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.ClosestToInput; match.BackBufferCount = MatchType.ClosestToInput; match.MultiSample = MatchType.ClosestToInput; match.SwapEffect = MatchType.ClosestToInput; match.DepthFormat = MatchType.ClosestToInput; match.StencilFormat = MatchType.ClosestToInput; match.PresentFlags = MatchType.ClosestToInput; match.RefreshRate = MatchType.ClosestToInput; match.PresentInterval = MatchType.ClosestToInput; try { deviceSettings = FindValidDeviceSettings(deviceSettings, match); } catch(Exception e) { // Display any error message DisplayErrorMessage(e); throw; } // Change to a Direct3D device created from the new device settings. // If there is an existing device, then either reset or recreate the scene ChangeDevice(deviceSettings, null, false); } }
/// <summary> /// Render the 3D environment by: /// - Checking if the device is lost and trying to reset it if it is /// - Get the elapsed time since the last frame /// - Calling the app's framemove and render callback /// - Calling Present() /// If you're not using MainLoop, consider using Render3DEnvironment /// </summary> public void Render3DEnvironment() { Device device = State.Device; if (device == null) return; // Nothing to do if (State.IsDeviceLost || State.IsRenderingPaused) { // Window is minimized or paused so yield // CPU time to other processes System.Threading.Thread.Sleep(100); } if (!State.IsActive) { // Window is not in focus so yield some CPU time to other processes System.Threading.Thread.Sleep(20); } if (State.IsDeviceLost && !State.IsRenderingPaused) { int result; // Check the cooperative level to see if it's ok to render if (!device.CheckCooperativeLevel(out result)) { if (result == (int)ResultCode.DeviceLost) { // The device has been lost but cannot be reset at this time. // So wait until it can be reset. System.Threading.Thread.Sleep(50); return; } // If we are windowed, read the desktop format and // ensure that the Direct3D device is using the same format // since the user could have changed the desktop bitdepth if (IsWindowed) { DisplayMode adapterDesktopMode = Manager.Adapters[(int)State.CurrentDeviceSettings.AdapterOrdinal].CurrentDisplayMode; if (State.CurrentDeviceSettings.AdapterFormat != adapterDesktopMode.Format) { // Set up the match options MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.PreserveInput; match.DeviceType = MatchType.PreserveInput; match.Windowed = MatchType.PreserveInput; match.AdapterFormat = MatchType.PreserveInput; match.VertexProcessing = MatchType.ClosestToInput; match.Resolution = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.ClosestToInput; match.BackBufferCount = MatchType.ClosestToInput; match.MultiSample = MatchType.ClosestToInput; match.SwapEffect = MatchType.ClosestToInput; match.DepthFormat = MatchType.ClosestToInput; match.StencilFormat = MatchType.ClosestToInput; match.PresentFlags = MatchType.ClosestToInput; match.RefreshRate = MatchType.ClosestToInput; match.PresentInterval = MatchType.ClosestToInput; DeviceSettings settings = State.CurrentDeviceSettings; settings.AdapterFormat = adapterDesktopMode.Format; try { settings = FindValidDeviceSettings(settings, match); // Change to a Direct3D device created from the new device settings. // If there is an existing device, then either reset or recreate the scene ChangeDevice(settings, null, false); } catch { DisplayErrorMessage(new NoCompatibleDevicesException()); Dispose(); } return; } } // Try to reset the device try { Reset3DEnvironment(); } catch (DeviceLostException) { // The device was lost again, so continue waiting until it can be reset. System.Threading.Thread.Sleep(50); return; } catch { // Reset failed, but the device wasn't lost so something bad happened, // so recreate the device to try to recover DeviceSettings deviceSettings = State.CurrentDeviceSettings; try { ChangeDevice(deviceSettings, null, false); } catch (Exception e) { ResettingDeviceException rde = new ResettingDeviceException(e); DisplayErrorMessage(rde); Dispose(); throw; } } } State.IsDeviceLost = false; } // Get the app's time, in seconds. Skip rendering if no time elapsed double time = FrameworkTimer.GetTime(); float elapsedTime = (float)FrameworkTimer.GetElapsedTime(); // Store the time for the app if (State.IsUsingConstantFrameTime) { elapsedTime = State.TimePerFrame; time = State.CurrentTime + elapsedTime; } State.CurrentTime = time; State.ElapsedTime = elapsedTime; // Update the FPS stats UpdateFrameStats(); // If the settings dialog exists and is being shown, then // render it instead of rendering the app's scene if (State.Settings != null && State.IsD3DSettingsDialogShowing) { if (!State.IsRenderingPaused) { // Clear the render target and the zbuffer device.Clear(ClearFlags.Target, 0x00003F3F, 1.0f, 0); // Render the scene device.BeginScene(); State.Settings.OnRender(elapsedTime); device.EndScene(); } } else { HandleTimers(); //Animate the scene by calling the app's frame move callback if (State.CallbackInterface != null) { State.CallbackInterface.OnFrameMove(device, time, elapsedTime); device = State.Device; if (device == null) return; } if (!State.IsRenderingPaused) { // Render the scene by calling the app's render callback if (State.CallbackInterface != null) { State.CallbackInterface.OnFrameRender(device, time, elapsedTime); device = State.Device; if (device == null) return; } } } if (!State.IsRenderingPaused) { // Show the frame on the primary surface try { device.Present(); } catch (DeviceLostException) { // Whoops, device is lost now State.IsDeviceLost = true; } catch (DriverInternalErrorException) { // When DriverInternalErrorException is thrown from Present(), // the application can do one of the following: // // - End, with the pop-up window saying that the application cannot continue // because of problems in the display adapter and that the user should // contact the adapter manufacturer. // // - Attempt to restart by calling Device.Reset, which is essentially the same // path as recovering from a lost device. If Device.Reset throws the // DriverInternalErrorException, the application should end immediately with the // message that the user should contact the adapter manufacturer. // // The framework attempts the path of resetting the device // State.IsDeviceLost = true; } } // Update the current frame number State.CurrentFrameNumber++; if (State.OverrideQuitAfterFrame != 0) { if (State.CurrentFrameNumber > State.OverrideQuitAfterFrame) Dispose(); } }
/// <summary> /// Returns false for any device combo that doesn't meet the preserve /// match options /// </summary> private bool DoesDeviceComboMatchPreserveOptions(EnumDeviceSettingsCombo deviceCombo, DeviceSettings settings, MatchOptions match) { //--------------------- // Adapter ordinal //--------------------- if (match.AdapterOrdinal == MatchType.PreserveInput && (deviceCombo.AdapterOrdinal != settings.AdapterOrdinal) ) return false; //--------------------- // Device type //--------------------- if (match.DeviceType == MatchType.PreserveInput && (deviceCombo.DeviceType != settings.DeviceType) ) return false; //--------------------- // Windowed //--------------------- if (match.Windowed == MatchType.PreserveInput && (deviceCombo.IsWindowed != settings.presentParams.Windowed) ) return false; //--------------------- // Adapter format //--------------------- if (match.AdapterFormat == MatchType.PreserveInput && (deviceCombo.AdapterFormat != settings.AdapterFormat) ) return false; //--------------------- // Vertex processing //--------------------- // If keep VP and input has HWVP, then skip if this combo doesn't have HWTL if (match.VertexProcessing == MatchType.PreserveInput && ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0) && (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) ) return false; //--------------------- // Resolution //--------------------- // If keep resolution then check that width and height supported by this combo if (match.Resolution == MatchType.PreserveInput ) { bool bFound = false; for( int i=0; i< deviceCombo.adapterInformation.displayModeList.Count; i++ ) { DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[i]; if (displayMode.Format != deviceCombo.AdapterFormat ) continue; // Skip this display mode if it doesn't match the combo's adapter format if (displayMode.Width == settings.presentParams.BackBufferWidth && displayMode.Height == settings.presentParams.BackBufferHeight ) { bFound = true; break; } } // If the width and height are not supported by this combo, return false if (!bFound ) return false; } //--------------------- // Back buffer format //--------------------- if (match.BackBufferFormat == MatchType.PreserveInput && deviceCombo.BackBufferFormat != settings.presentParams.BackBufferFormat ) return false; //--------------------- // Back buffer count //--------------------- // No caps for the back buffer count //--------------------- // Multisample //--------------------- if (match.MultiSample == MatchType.PreserveInput ) { bool bFound = false; for( int i=0; i<deviceCombo.multiSampleTypeList.Count; i++ ) { MultiSampleType msType = (MultiSampleType)deviceCombo.multiSampleTypeList[i]; uint msQuality = (uint)(int)deviceCombo.multiSampleQualityList[i]; if (msType == settings.presentParams.MultiSample && msQuality >= settings.presentParams.MultiSampleQuality ) { bFound = true; break; } } // If multisample type/quality not supported by this combo, then return false if (!bFound ) return false; } //--------------------- // Swap effect //--------------------- // No caps for swap effects //--------------------- // Depth stencil //--------------------- // If keep depth stencil format then check that the depth stencil format is supported by this combo if (match.DepthFormat == MatchType.PreserveInput && match.StencilFormat == MatchType.PreserveInput ) { if (settings.presentParams.AutoDepthStencilFormat != (DepthFormat)Format.Unknown && !deviceCombo.depthStencilFormatList.Contains( settings.presentParams.AutoDepthStencilFormat ) ) return false; } // If keep depth format then check that the depth format is supported by this combo if (match.DepthFormat == MatchType.PreserveInput && settings.presentParams.AutoDepthStencilFormat != DepthFormat.Unknown ) { bool bFound = false; uint depthBits = ManagedUtility.GetDepthBits( settings.presentParams.AutoDepthStencilFormat ); for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ ) { DepthFormat depthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i]; uint curDepthBits = ManagedUtility.GetDepthBits(depthStencilFmt); if (curDepthBits - depthBits == 0) bFound = true; } if (!bFound ) return false; } // If keep depth format then check that the depth format is supported by this combo if (match.StencilFormat == MatchType.PreserveInput && settings.presentParams.AutoDepthStencilFormat != DepthFormat.Unknown ) { bool bFound = false; uint stencilBits = ManagedUtility.GetStencilBits( settings.presentParams.AutoDepthStencilFormat ); for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ ) { DepthFormat depthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i]; uint curStencilBits = ManagedUtility.GetStencilBits(depthStencilFmt); if (curStencilBits - stencilBits == 0) bFound = true; } if (!bFound ) return false; } //--------------------- // Present flags //--------------------- // No caps for the present flags //--------------------- // Refresh rate //--------------------- // If keep refresh rate then check that the resolution is supported by this combo if (match.RefreshRate == MatchType.PreserveInput ) { bool bFound = false; for( int i=0; i<deviceCombo.adapterInformation.displayModeList.Count; i++ ) { DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[i]; if (displayMode.Format != deviceCombo.AdapterFormat ) continue; if (displayMode.RefreshRate == settings.presentParams.FullScreenRefreshRateInHz ) { bFound = true; break; } } // If refresh rate not supported by this combo, then return false if (!bFound ) return false; } //--------------------- // Present interval //--------------------- // If keep present interval then check that the present interval is supported by this combo if (match.PresentInterval == MatchType.PreserveInput && !deviceCombo.presentIntervalList.Contains( settings.presentParams.PresentationInterval ) ) return false; return true; }
/// <summary> /// Creates a Direct3D device. If CreateWindow or SetWindow has not already /// been called, it will call CreateWindow with default parameters. /// Instead of calling this, you can call SetDevice or CreateDeviceFromSettings /// </summary> public void CreateDevice(uint adapterOridinal, bool windowed, int suggestedWidth, int suggestedHeight, IDeviceCreation callback) { if (State.IsInsideDeviceCallback) throw new InvalidOperationException("You cannot create a window from inside a callback."); // Store callbacks in global state SetDeviceCreationInterface(callback); // Update device create being called State.WasDeviceCreateCalled = true; // Was the window created? If not, create it now if (!State.WasWindowCreated) { // If CreateWindow or SetWindow was already called and failed, then fail again. if (State.WasWindowCreateCalled) { throw new InvalidOperationException("CreateWindow was already called and failed."); } // Create a default window CreateWindow("Direct3D Window", null, null, -1, -1); } // Force an enumeration with the updated Device Acceptable callback Enumeration.Enumerate(State.DeviceCreationInterface); // Set up the match options MatchOptions match = new MatchOptions(); match.AdapterOrdinal = MatchType.PreserveInput; match.DeviceType = MatchType.IgnoreInput; match.Windowed = MatchType.PreserveInput; match.AdapterFormat = MatchType.IgnoreInput; match.VertexProcessing = MatchType.IgnoreInput; match.Resolution = MatchType.ClosestToInput; match.BackBufferFormat = MatchType.IgnoreInput; match.BackBufferCount = MatchType.IgnoreInput; match.MultiSample = MatchType.IgnoreInput; match.SwapEffect = MatchType.IgnoreInput; match.DepthFormat = MatchType.IgnoreInput; match.StencilFormat = MatchType.IgnoreInput; match.PresentFlags = MatchType.IgnoreInput; match.RefreshRate = MatchType.IgnoreInput; match.PresentInterval = MatchType.IgnoreInput; // Get the device settings DeviceSettings settings = new DeviceSettings(); settings.presentParams = new PresentParameters(); settings.AdapterOrdinal = adapterOridinal; settings.presentParams.Windowed = windowed; settings.presentParams.BackBufferWidth = suggestedWidth; settings.presentParams.BackBufferHeight = suggestedHeight; // Override with settings for command line if (State.OverrideWidth != 0) settings.presentParams.BackBufferWidth = State.OverrideWidth; if (State.OverrideHeight != 0) settings.presentParams.BackBufferHeight = State.OverrideHeight; if (State.OverrideAdapterOrdinal != -1) settings.AdapterOrdinal = (uint)State.OverrideAdapterOrdinal; if (State.IsOverridingFullScreen) { settings.presentParams.Windowed = false; if ((State.OverrideWidth == 0) && (State.OverrideHeight == 0)) match.Resolution = MatchType.IgnoreInput; } if (State.IsOverridingWindowed) settings.presentParams.Windowed = true; if (State.IsOverridingForceHardware) { settings.DeviceType = DeviceType.Hardware; match.DeviceType = MatchType.PreserveInput; } if (State.IsOverridingForceReference) { settings.DeviceType = DeviceType.Reference; match.DeviceType = MatchType.PreserveInput; } if (State.IsOverridingForcePureHardwareVertexProcessing) { settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing | CreateFlags.PureDevice; match.VertexProcessing = MatchType.PreserveInput; } if (State.IsOverridingForceHardwareVertexProcessing) { settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing; match.VertexProcessing = MatchType.PreserveInput; } if (State.IsOverridingForceSoftwareVertexProcessing) { settings.BehaviorFlags = CreateFlags.SoftwareVertexProcessing; match.VertexProcessing = MatchType.PreserveInput; } try { settings = FindValidDeviceSettings(settings, match); } catch(Exception e) { DisplayErrorMessage(e); throw; } // If the modify device callback isn't null, call it to // let the app change the settings if (State.DeviceCreationInterface != null) { Caps c = Manager.GetDeviceCaps((int)settings.AdapterOrdinal, settings.DeviceType); State.DeviceCreationInterface.ModifyDeviceSettings(settings, c); } // Change to a Direct3D device created from the new device settings // If there is an existing device, either reset or recreate ChangeDevice(settings, null, false); }
/// <summary> /// Builds valid device settings using the match options, the input device settings, and the /// best device settings combo found. /// </summary> private DeviceSettings BuildValidDeviceSettings(EnumDeviceSettingsCombo deviceCombo, DeviceSettings settings, MatchOptions match) { DeviceSettings validSettings = new DeviceSettings(); DisplayMode adapterDesktopDisplayMode = Manager.Adapters[(int)deviceCombo.AdapterOrdinal].CurrentDisplayMode; // For each setting pick the best, taking into account the match options and // what's supported by the device //--------------------- // Adapter Ordinal //--------------------- // Just using deviceCombo.AdapterOrdinal //--------------------- // Device Type //--------------------- // Just using deviceCombo.DeviceType //--------------------- // Windowed //--------------------- // Just using deviceCombo.Windowed //--------------------- // Adapter Format //--------------------- // Just using deviceCombo.AdapterFormat //--------------------- // Vertex processing //--------------------- CreateFlags bestBehaviorFlags = 0; if (match.VertexProcessing == MatchType.PreserveInput ) { bestBehaviorFlags = settings.BehaviorFlags; } else if (match.VertexProcessing == MatchType.IgnoreInput ) { // The framework defaults to HWVP if available otherwise use SWVP if (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) bestBehaviorFlags |= CreateFlags.HardwareVertexProcessing; else bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing; } else { // Default to input, and fallback to SWVP if HWVP not available bestBehaviorFlags = settings.BehaviorFlags; if ((!deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) && ( (bestBehaviorFlags & CreateFlags.HardwareVertexProcessing ) != 0 || (bestBehaviorFlags & CreateFlags.MixedVertexProcessing) != 0) ) { bestBehaviorFlags &= ~CreateFlags.HardwareVertexProcessing ; bestBehaviorFlags &= ~CreateFlags.MixedVertexProcessing; bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing; } // One of these must be selected if ((bestBehaviorFlags & CreateFlags.HardwareVertexProcessing ) == 0 && (bestBehaviorFlags & CreateFlags.MixedVertexProcessing) == 0 && (bestBehaviorFlags & CreateFlags.SoftwareVertexProcessing) == 0 ) { if (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) bestBehaviorFlags |= CreateFlags.HardwareVertexProcessing ; else bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing; } } //--------------------- // Resolution //--------------------- DisplayMode bestDisplayMode = new DisplayMode(); if (match.Resolution == MatchType.PreserveInput ) { bestDisplayMode.Width = settings.presentParams.BackBufferWidth; bestDisplayMode.Height = settings.presentParams.BackBufferHeight; } else { DisplayMode displayModeIn = new DisplayMode(); if (match.Resolution == MatchType.ClosestToInput && (settings.presentParams.BackBufferWidth != 0 && settings.presentParams.BackBufferWidth != 0) ) { displayModeIn.Width = settings.presentParams.BackBufferWidth; displayModeIn.Height = settings.presentParams.BackBufferHeight; } else // if (match.Resolution == MatchType.IgnoreInput ) { if (deviceCombo.IsWindowed ) { // The framework defaults to 640x480 for windowed displayModeIn.Width = DefaultSizeWidth; displayModeIn.Height = DefaultSizeHeight; } else { // The framework defaults to desktop resolution for fullscreen to try to avoid slow mode change displayModeIn.Width = adapterDesktopDisplayMode.Width; displayModeIn.Height = adapterDesktopDisplayMode.Height; } } // Call a helper function to find the closest valid display mode to the optimal bestDisplayMode = FindValidResolution(deviceCombo, displayModeIn); } //--------------------- // Back Buffer Format //--------------------- // Just using deviceCombo.BackBufferFormat //--------------------- // Back buffer count //--------------------- uint bestBackBufferCount; if (match.BackBufferCount == MatchType.PreserveInput ) { bestBackBufferCount = (uint)settings.presentParams.BackBufferCount; } else if (match.BackBufferCount == MatchType.IgnoreInput ) { // The framework defaults to triple buffering bestBackBufferCount = 2; } else // if (match.BackBufferCount == MatchType.ClosestToInput ) { bestBackBufferCount = (uint)settings.presentParams.BackBufferCount; if (bestBackBufferCount > 3 ) bestBackBufferCount = 3; if (bestBackBufferCount < 1 ) bestBackBufferCount = 1; } //--------------------- // Multisample //--------------------- MultiSampleType bestMultiSampleType; uint bestMultiSampleQuality; if (settings.presentParams.SwapEffect != SwapEffect.Discard) { // Swap effect is not set to discard so multisampling has to off bestMultiSampleType = MultiSampleType.None; bestMultiSampleQuality = 0; } else { if (match.BackBufferCount == MatchType.PreserveInput ) { bestMultiSampleType = settings.presentParams.MultiSample; bestMultiSampleQuality = (uint)settings.presentParams.MultiSampleQuality; } else if (match.BackBufferCount == MatchType.IgnoreInput ) { // Default to no multisampling (always supported) bestMultiSampleType = MultiSampleType.None; bestMultiSampleQuality = 0; } else if (match.BackBufferCount == MatchType.ClosestToInput ) { // Default to no multisampling (always supported) bestMultiSampleType = MultiSampleType.None; bestMultiSampleQuality = 0; for (int i = 0; i < deviceCombo.multiSampleTypeList.Count; i++) { MultiSampleType tempType = (MultiSampleType)deviceCombo.multiSampleTypeList[i]; uint tempQuality = (uint)(int)deviceCombo.multiSampleQualityList[i]; // Check whether supported type is closer to the input than our current best if (Math.Abs((int)tempType - (int)settings.presentParams.MultiSample) < Math.Abs((int)bestMultiSampleType - (int)settings.presentParams.MultiSample) ) { bestMultiSampleType = tempType; bestMultiSampleQuality = (uint)Math.Min(tempQuality-1, settings.presentParams.MultiSampleQuality); } } } else // Error case { // Default to no multisampling (always supported) bestMultiSampleType = MultiSampleType.None; bestMultiSampleQuality = 0; } } //--------------------- // Swap effect //--------------------- SwapEffect bestSwapEffect; if (match.SwapEffect == MatchType.PreserveInput ) { bestSwapEffect = settings.presentParams.SwapEffect; } else if (match.SwapEffect == MatchType.IgnoreInput ) { bestSwapEffect = SwapEffect.Discard; } else // if (match.SwapEffect == MatchType.ClosestToInput ) { bestSwapEffect = settings.presentParams.SwapEffect; // Swap effect has to be one of these 3 if (bestSwapEffect != SwapEffect.Discard && bestSwapEffect != SwapEffect.Flip && bestSwapEffect != SwapEffect.Copy ) { bestSwapEffect = SwapEffect.Discard; } } //--------------------- // Depth stencil //--------------------- DepthFormat bestDepthStencilFormat; bool bestEnableAutoDepthStencil; int[] depthStencilRanking = new int[deviceCombo.depthStencilFormatList.Count]; uint backBufferBitDepth = ManagedUtility.GetColorChannelBits( deviceCombo.BackBufferFormat ); uint inputDepthBitDepth = ManagedUtility.GetDepthBits( settings.presentParams.AutoDepthStencilFormat ); for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ ) { DepthFormat curDepthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i]; uint curDepthBitDepth = ManagedUtility.GetDepthBits( curDepthStencilFmt ); int ranking; if (match.DepthFormat == MatchType.PreserveInput ) { // Need to match bit depth of input if(curDepthBitDepth == inputDepthBitDepth) ranking = 0; else ranking = 10000; } else if (match.DepthFormat == MatchType.IgnoreInput ) { // Prefer match of backbuffer bit depth ranking = Math.Abs((int)curDepthBitDepth - (int)(backBufferBitDepth*4)); } else // if (match.DepthFormat == MatchType.ClosestToInput ) { // Prefer match of input depth format bit depth ranking = Math.Abs((int)curDepthBitDepth - (int)inputDepthBitDepth); } depthStencilRanking[i] = ranking; } uint inputStencilBitDepth = ManagedUtility.GetStencilBits( settings.presentParams.AutoDepthStencilFormat ); for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ ) { DepthFormat curDepthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i]; int ranking = depthStencilRanking[i]; uint curStencilBitDepth = ManagedUtility.GetStencilBits( curDepthStencilFmt ); if (match.StencilFormat == MatchType.PreserveInput ) { // Need to match bit depth of input if(curStencilBitDepth == inputStencilBitDepth) ranking += 0; else ranking += 10000; } else if (match.StencilFormat == MatchType.IgnoreInput ) { // Prefer 0 stencil bit depth ranking += (int)curStencilBitDepth; } else // if (match.StencilFormat == MatchType.ClosestToInput ) { // Prefer match of input stencil format bit depth ranking += Math.Abs((int)curStencilBitDepth - (int)inputStencilBitDepth); } depthStencilRanking[i] = ranking; } int bestRanking = 100000; int bestIndex = -1; for( int i=0; i<depthStencilRanking.Length; i++ ) { if (depthStencilRanking[i] < bestRanking ) { bestRanking = depthStencilRanking[i]; bestIndex = i; } } if (bestIndex >= 0 ) { bestDepthStencilFormat = (DepthFormat)deviceCombo.depthStencilFormatList[bestIndex]; bestEnableAutoDepthStencil = true; } else { bestDepthStencilFormat = DepthFormat.Unknown; bestEnableAutoDepthStencil = false; } //--------------------- // Present flags //--------------------- PresentFlag bestPresentFlag; if (match.PresentFlags == MatchType.PreserveInput ) { bestPresentFlag = settings.presentParams.PresentFlag; } else if (match.PresentFlags == MatchType.IgnoreInput ) { bestPresentFlag = 0; if (bestEnableAutoDepthStencil ) bestPresentFlag = PresentFlag.DiscardDepthStencil; } else // if (match.PresentFlags == MatchType.ClosestToInput ) { bestPresentFlag = settings.presentParams.PresentFlag; if (bestEnableAutoDepthStencil ) bestPresentFlag |= PresentFlag.DiscardDepthStencil; } //--------------------- // Refresh rate //--------------------- if (deviceCombo.IsWindowed ) { // Must be 0 for windowed bestDisplayMode.RefreshRate = 0; } else { if (match.RefreshRate == MatchType.PreserveInput ) { bestDisplayMode.RefreshRate = settings.presentParams.FullScreenRefreshRateInHz; } else { uint refreshRateMatch; if (match.RefreshRate == MatchType.ClosestToInput ) { refreshRateMatch = (uint)settings.presentParams.FullScreenRefreshRateInHz; } else // if (match.RefreshRate == MatchType.IgnoreInput ) { refreshRateMatch = (uint)adapterDesktopDisplayMode.RefreshRate; } bestDisplayMode.RefreshRate = 0; if (refreshRateMatch != 0 ) { int bestRefreshRanking = 100000; for( int iDisplayMode=0; iDisplayMode<deviceCombo.adapterInformation.displayModeList.Count; iDisplayMode++ ) { DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[iDisplayMode]; if (displayMode.Format != deviceCombo.AdapterFormat || displayMode.Height != bestDisplayMode.Height || displayMode.Width != bestDisplayMode.Width ) continue; // Skip display modes that don't match // Find the delta between the current refresh rate and the optimal refresh rate int currentRefreshRanking = Math.Abs((int)displayMode.RefreshRate - (int)refreshRateMatch); if (currentRefreshRanking < bestRefreshRanking ) { bestDisplayMode.RefreshRate = displayMode.RefreshRate; bestRefreshRanking = currentRefreshRanking; // Stop if perfect match found if (bestRefreshRanking == 0 ) break; } } } } } //--------------------- // Present interval //--------------------- PresentInterval bestPresentInterval; if (match.PresentInterval == MatchType.PreserveInput ) { bestPresentInterval = settings.presentParams.PresentationInterval; } else if (match.PresentInterval == MatchType.IgnoreInput ) { if (deviceCombo.IsWindowed ) { // For windowed, the framework defaults to PresentInterval.Immediate // which will wait not for the vertical retrace period to prevent tearing, // but may introduce tearing bestPresentInterval = PresentInterval.Immediate; } else { // For full screen, the framework defaults to PresentInterval.Default // which will wait for the vertical retrace period to prevent tearing bestPresentInterval = PresentInterval.Default; } } else // if (match.PresentInterval == MatchType.ClosestToInput ) { if (deviceCombo.presentIntervalList.Contains( settings.presentParams.PresentationInterval ) ) { bestPresentInterval = settings.presentParams.PresentationInterval; } else { if (deviceCombo.IsWindowed ) bestPresentInterval = PresentInterval.Immediate; else bestPresentInterval = PresentInterval.Default; } } // Fill the device settings struct validSettings.AdapterOrdinal = deviceCombo.AdapterOrdinal; validSettings.DeviceType = deviceCombo.DeviceType; validSettings.AdapterFormat = deviceCombo.AdapterFormat; validSettings.BehaviorFlags = bestBehaviorFlags; validSettings.presentParams = new PresentParameters(); validSettings.presentParams.BackBufferWidth = bestDisplayMode.Width; validSettings.presentParams.BackBufferHeight = bestDisplayMode.Height; validSettings.presentParams.BackBufferFormat = deviceCombo.BackBufferFormat; validSettings.presentParams.BackBufferCount = (int)bestBackBufferCount; validSettings.presentParams.MultiSample = bestMultiSampleType; validSettings.presentParams.MultiSampleQuality = (int)bestMultiSampleQuality; validSettings.presentParams.SwapEffect = bestSwapEffect; validSettings.presentParams.DeviceWindow = deviceCombo.IsWindowed ? State.WindowDeviceWindowed : State.WindowDeviceFullScreen; validSettings.presentParams.Windowed = deviceCombo.IsWindowed; validSettings.presentParams.EnableAutoDepthStencil = bestEnableAutoDepthStencil; validSettings.presentParams.AutoDepthStencilFormat = bestDepthStencilFormat; validSettings.presentParams.PresentFlag = bestPresentFlag; validSettings.presentParams.FullScreenRefreshRateInHz = bestDisplayMode.RefreshRate; validSettings.presentParams.PresentationInterval = bestPresentInterval; validSettings.presentParams.ForceNoMultiThreadedFlag = true; return validSettings; }