using System; using System.Collections.Generic; using System.Linq; using System.Text; using Aiwaz.Contracts; using Aiwaz.Core; namespace Aiwaz.Resources { public abstract class RenderTarget : CommandUser, IRenderTargetBase, IDisposable { public RenderTarget() : base() { Commands.Add(new Command(this, CommandFlags.EndChain | CommandFlags.FlushChain, setRenderTargetCommandType, 0, 0)); this.ClearDepth = 1.0f; } ~RenderTarget() { if (Engine.Initialized) this.Dispose(); } #region IRenderTargetBase Members internal static SlimDX.Direct3D10.ShaderResourceView[] emptyShaderResView = new SlimDX.Direct3D10.ShaderResourceView[128]; private SlimDX.Direct3D10.RenderTargetView[] renderTargetViews; private List additionalRenderTargets = new List(); private SlimDX.Direct3D10.Viewport[] viewports; protected int multiSampleCount = 1; protected int multiSampleQuality = 0; private BindFlags lastBindFlags = BindFlags.Default; public float ClearDepth { get; set; } public byte ClearStencil { get; set; } public SlimDX.Color4 ClearColor { get; set; } private ViewPort viewPort; public ViewPort ViewPort { get { return viewPort; } set { viewPort = value; this.KillViewPortAndRenderTargetData(); this.BuildViewPortAndRenderTargetData(); } } public abstract int RenderTargetWidth { get; } public abstract int RenderTargetHeight { get; } public abstract SlimDX.DXGI.Format RenderTargetFormat { get; } private bool hasDepthStencilBuffer; public bool HasDepthStencilBuffer { get { return hasDepthStencilBuffer; } set { hasDepthStencilBuffer = value; Engine.Device.ClearState(); if (DX10DepthStencilView != null) DX10DepthStencilView.Dispose(); DX10DepthStencilView = null; if (DX10DepthStencilTexture != null) DX10DepthStencilTexture.Dispose(); DX10DepthStencilTexture = null; if (!hasDepthStencilBuffer) return; // no further effort needed here var depthTextureDesc = new SlimDX.Direct3D10.Texture2DDescription() { Width = this.RenderTargetWidth, Height = this.RenderTargetHeight, MipLevels = 1, ArraySize = 1, Format = SlimDX.DXGI.Format.D24_UNorm_S8_UInt, SampleDescription = new SlimDX.DXGI.SampleDescription(multiSampleCount == 0 ? 1 : multiSampleCount, multiSampleQuality), Usage = SlimDX.Direct3D10.ResourceUsage.Default, BindFlags = SlimDX.Direct3D10.BindFlags.DepthStencil, CpuAccessFlags = SlimDX.Direct3D10.CpuAccessFlags.None, OptionFlags = SlimDX.Direct3D10.ResourceOptionFlags.None }; DX10DepthStencilTexture = new SlimDX.Direct3D10.Texture2D(Engine.Device, depthTextureDesc); if (DX10DepthStencilTexture == null) throw new Aiwaz.Core.ActionFailedException("RenderTarget: Unable to create depth stencil texture."); var depthStencilViewDesc = new SlimDX.Direct3D10.DepthStencilViewDescription() { Format = depthTextureDesc.Format, Dimension = multiSampleCount >= 1 ? SlimDX.Direct3D10.DepthStencilViewDimension.Texture2DMultisampled : SlimDX.Direct3D10.DepthStencilViewDimension.Texture2D, MipSlice = 0 }; DX10DepthStencilView = new SlimDX.Direct3D10.DepthStencilView(Engine.Device, DX10DepthStencilTexture, depthStencilViewDesc); if (DX10DepthStencilView == null) throw new Aiwaz.Core.ActionFailedException("RenderTarget: Unable to create depth stencil view."); } } public SlimDX.Direct3D10.RenderTargetView DX10RenderTargetView { get; protected set; } public SlimDX.Direct3D10.DepthStencilView DX10DepthStencilView { get; protected set; } public SlimDX.Direct3D10.Texture2D DX10DepthStencilTexture { get; protected set; } public void AddAdditionalRenderTarget(IRenderTargetBase target) { this.RemoveAdditionalRenderTarget(target); //if (AdditionalRenderTargets.Count + 1 == D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT) // throw new Aiwaz.Core.ActionFailedException("RenderTarget: Unable to add an additional rendering target, list is full."); additionalRenderTargets.Add(target); this.KillViewPortAndRenderTargetData(); this.BuildViewPortAndRenderTargetData(); } public void RemoveAdditionalRenderTarget(IRenderTargetBase target) { if (additionalRenderTargets.Remove(target)) { this.KillViewPortAndRenderTargetData(); this.BuildViewPortAndRenderTargetData(); } } public void Bind(BindFlags bindFlags) { if ((bindFlags & ~ BindFlags.ClearAll) != (lastBindFlags & ~ BindFlags.ClearAll)) { lastBindFlags = bindFlags; this.KillViewPortAndRenderTargetData(); this.BuildViewPortAndRenderTargetData(); } else lastBindFlags = bindFlags; this.Clear(bindFlags); Engine.Device.PixelShader.SetShaderResources(emptyShaderResView, 0, 128); Engine.Device.OutputMerger.SetTargets(DX10DepthStencilView, renderTargetViews); Engine.Device.Rasterizer.SetViewports(viewports); Engine.EngineStates.LastRenderTarget = this; } public void UnbindAllRenderTargets() { if (Engine.EngineStates.LastRenderTarget == this) { Engine.Device.OutputMerger.SetTargets(new SlimDX.Direct3D10.RenderTargetView[0]); Engine.Device.Rasterizer.SetViewports(new SlimDX.Direct3D10.Viewport[0]); Engine.EngineStates.LastRenderTarget = null; } } protected void BuildViewPortAndRenderTargetData() { // Rebuild render targets if (renderTargetViews == null && viewports == null) { if ((lastBindFlags & BindFlags.BindAllTextures) == BindFlags.BindAllTextures) { renderTargetViews = new SlimDX.Direct3D10.RenderTargetView[additionalRenderTargets.Count + 1]; viewports = new SlimDX.Direct3D10.Viewport[additionalRenderTargets.Count + 1]; renderTargetViews[0] = this.DX10RenderTargetView; viewports[0] = this.ViewPort; for (int i = 0; i < additionalRenderTargets.Count; ++i) { renderTargetViews[i + 1] = additionalRenderTargets[i].DX10RenderTargetView; viewports[i + 1] = additionalRenderTargets[i].ViewPort; } } else if ((lastBindFlags & BindFlags.BindAllTextures) != BindFlags.None) { var activeRenderTargetCount = 0; if ((lastBindFlags & BindFlags.BindBaseTexture) != BindFlags.None) ++activeRenderTargetCount; if ((lastBindFlags & BindFlags.BindAdditionalTexture0) != BindFlags.None) ++activeRenderTargetCount; if ((lastBindFlags & BindFlags.BindAdditionalTexture1) != BindFlags.None) ++activeRenderTargetCount; if ((lastBindFlags & BindFlags.BindAdditionalTexture2) != BindFlags.None) ++activeRenderTargetCount; if ((lastBindFlags & BindFlags.BindAdditionalTexture3) != BindFlags.None) ++activeRenderTargetCount; renderTargetViews = new SlimDX.Direct3D10.RenderTargetView[activeRenderTargetCount]; viewports = new SlimDX.Direct3D10.Viewport[activeRenderTargetCount]; int bufferIndex = 0; if ((lastBindFlags & BindFlags.BindBaseTexture) != BindFlags.None) { renderTargetViews[bufferIndex] = this.DX10RenderTargetView; viewports[bufferIndex++] = this.ViewPort; } if ((lastBindFlags & BindFlags.BindAdditionalTexture0) != BindFlags.None) { renderTargetViews[bufferIndex] = additionalRenderTargets[0].DX10RenderTargetView; viewports[bufferIndex++] = additionalRenderTargets[0].ViewPort; } if ((lastBindFlags & BindFlags.BindAdditionalTexture1) != BindFlags.None) { renderTargetViews[bufferIndex] = additionalRenderTargets[1].DX10RenderTargetView; viewports[bufferIndex++] = additionalRenderTargets[1].ViewPort; } if ((lastBindFlags & BindFlags.BindAdditionalTexture2) != BindFlags.None) { renderTargetViews[bufferIndex] = additionalRenderTargets[2].DX10RenderTargetView; viewports[bufferIndex++] = additionalRenderTargets[2].ViewPort; } if ((lastBindFlags & BindFlags.BindAdditionalTexture3) != BindFlags.None) { renderTargetViews[bufferIndex] = additionalRenderTargets[3].DX10RenderTargetView; viewports[bufferIndex++] = additionalRenderTargets[3].ViewPort; } } else throw new ActionFailedException("No render target bindable."); } } protected void KillViewPortAndRenderTargetData() { renderTargetViews = null; viewports = null; if (Engine.EngineStates.LastRenderTarget == this) Engine.EngineStates.LastRenderTarget = null; } #endregion #region IDisposable Members protected void DisposeInternal() { this.KillViewPortAndRenderTargetData(); this.UnbindAllRenderTargets(); if (DX10RenderTargetView != null) DX10RenderTargetView.Dispose(); DX10RenderTargetView = null; if (DX10DepthStencilView != null) DX10DepthStencilView.Dispose(); DX10DepthStencilView = null; if (DX10DepthStencilTexture != null) DX10DepthStencilTexture.Dispose(); DX10DepthStencilTexture = null; } public override void Dispose() { this.DisposeInternal(); base.Dispose(); } #endregion #region CommandUser const byte setRenderTargetCommandType = 0; public override CommandExecuteResult ExecuteCommand(byte commandType, CommandBuffer currentBuffer, int currentPositon) { if (commandType == setRenderTargetCommandType) { if (Engine.EngineStates.LastRenderTarget != this) { this.UnbindAllRenderTargets(); this.Bind(BindFlags.Default); } else this.Clear(BindFlags.Default); } return CommandExecuteResult.None; } protected void Clear(BindFlags bindFlags) { if ((bindFlags & BindFlags.ClearColor) != BindFlags.None) Engine.Device.ClearRenderTargetView(DX10RenderTargetView, ClearColor); if ((bindFlags & BindFlags.ClearDepthStencil) != BindFlags.None && DX10DepthStencilView != null) Engine.Device.ClearDepthStencilView(DX10DepthStencilView, SlimDX.Direct3D10.DepthStencilClearFlags.Depth | SlimDX.Direct3D10.DepthStencilClearFlags.Stencil, ClearDepth, ClearStencil); } #endregion } }