using System; using System.Collections.Generic; using System.Linq; using System.Text; using Aiwaz.Contracts; using System.Runtime.InteropServices; using Aiwaz.Core; using System.Collections.ObjectModel; namespace Aiwaz.Resources { [AiwazResource("SwapChain", "An output target for render operations, the output could be displayed directly into a window.")] public class SwapChain : RenderTarget, ISwapChain { [DllImport("user32.dll")] static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); [Serializable, StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; public int Top; public int Right; public int Bottom; public RECT(int left_, int top_, int right_, int bottom_) { Left = left_; Top = top_; Right = right_; Bottom = bottom_; } public int Height { get { return Bottom - Top; } } public int Width { get { return Right - Left; } } } private SlimDX.DXGI.SwapChainDescription swapChainDescription; private SlimDX.DXGI.SwapChain swapChain; public SwapChain(IntPtr windowHandle) : this(windowHandle, 0, 0, 0, SlimDX.DXGI.Format.Unknown, 0, 0) { } public SwapChain(IntPtr windowHandle, int width, int height, int refreshRate, SlimDX.DXGI.Format format) : this (windowHandle, width, height, refreshRate, format, 0, 0) { } public SwapChain(IntPtr windowHandle, int width, int height, int refreshRate, SlimDX.DXGI.Format format, int multiSampleCount, int multiSampleQuality) : base() { VSync = true; // get missing information if (multiSampleCount <= 0) multiSampleCount = 1; if (width == 0 || height == 0) { RECT windowRect = new RECT(); GetClientRect(windowHandle, out windowRect); if (width == 0) width = windowRect.Right - windowRect.Left; if (height == 0) height = windowRect.Bottom - windowRect.Top; } if (refreshRate == 0) refreshRate = 60; // 60Hz if (format == SlimDX.DXGI.Format.Unknown) format = SlimDX.DXGI.Format.R8G8B8A8_UNorm; // RGBA(X) 8Bit per channel -> 32Bit swapChainDescription = new SlimDX.DXGI.SwapChainDescription() { BufferCount = 1, Usage = SlimDX.DXGI.Usage.RenderTargetOutput, OutputHandle = windowHandle, IsWindowed = true, ModeDescription = new SlimDX.DXGI.ModeDescription() { Width = width, Height = height, Format = (SlimDX.DXGI.Format)format, RefreshRate = new SlimDX.Rational(refreshRate, 1) }, SampleDescription = new SlimDX.DXGI.SampleDescription() { Count = multiSampleCount, Quality = multiSampleQuality } }; this.multiSampleCount = multiSampleCount; this.multiSampleQuality = multiSampleQuality; Console.WriteLine(string.Format("Creating SwapChain ({0}x{1}x{2} {3})..", width, height, refreshRate, format.ToString())); swapChain = new SlimDX.DXGI.SwapChain(Engine.Factory, Engine.Device, swapChainDescription); if (swapChain == null) throw new InitializingFailedException(this.GetType().Name, "Unable to create internal swap chain."); this.RetriveData(); this.ViewPort = new ViewPort() { Width = width, Height = height }; } public override int RenderTargetWidth { get { return swapChainDescription.ModeDescription.Width; } } public override int RenderTargetHeight { get { return swapChainDescription.ModeDescription.Height; } } public override SlimDX.DXGI.Format RenderTargetFormat { get { return swapChainDescription.ModeDescription.Format; } } #region ISwapChain Members public void Resize(int width, int height) { if (width <= 0 || height <= 0) { RECT windowRect = new RECT(); GetClientRect(swapChainDescription.OutputHandle, out windowRect); if (width <= 0) width = windowRect.Right - windowRect.Left; if (height <= 0) height = windowRect.Bottom - windowRect.Top; } if (swapChainDescription.ModeDescription.Width == width && swapChainDescription.ModeDescription.Height == height) return; var depthStencilBuffer = this.HasDepthStencilBuffer; Engine.Device.ClearState(); this.HasDepthStencilBuffer = false; if (this.DX10RenderTargetView != null) this.DX10RenderTargetView.Dispose(); this.DX10RenderTargetView = null; swapChain.ResizeBuffers(swapChainDescription.BufferCount, width, height, swapChainDescription.ModeDescription.Format, SlimDX.DXGI.SwapChainFlags.AllowModeSwitch); var desc = swapChainDescription.ModeDescription; desc.Width = width; desc.Height = height; swapChainDescription.ModeDescription = desc; this.RetriveData(); this.HasDepthStencilBuffer = depthStencilBuffer; this.ViewPort = new ViewPort() { Width = width, Height = height }; } public void Present() { swapChain.Present(VSync ? 1 : 0, SlimDX.DXGI.PresentFlags.None); } public bool Fullscreen { get { return !swapChainDescription.IsWindowed; } set { if (swapChainDescription.IsWindowed == !value) return; swapChainDescription.IsWindowed = !value; var hasDepthStencilBuffer = this.HasDepthStencilBuffer; Engine.Device.ClearState(); this.HasDepthStencilBuffer = false; if (this.DX10RenderTargetView != null) this.DX10RenderTargetView.Dispose(); this.DX10RenderTargetView = null; swapChain.SetFullScreenState(value, Engine.DeviceEnumerator.FindBestOutput(false).Output); this.RetriveData(); this.HasDepthStencilBuffer = hasDepthStencilBuffer; } } public bool VSync { get; set; } private void RetriveData() { using (var backbufferTexture = SlimDX.Direct3D10.Texture2D.FromSwapChain(swapChain, 0)) { DX10RenderTargetView = new SlimDX.Direct3D10.RenderTargetView(Engine.Device, backbufferTexture); } } #endregion #region IDisposable Members public override void Dispose() { if (swapChain != null) swapChain.Dispose(); swapChain = null; base.Dispose(); } #endregion public override ICreationParams CreationParams { get { return null; } } public override ObservableCollection Children { get { return null; } } } }