/// <summary> /// Function to render a scene that does not use an effect. /// </summary> /// <param name="pass">The current pass.</param> /// <param name="currentTarget">The currently active render target.</param> /// <param name="currentTexture">The texture for the previously active render target.</param> private void RenderNoEffectPass(Gorgon2DCompositionPass pass, GorgonRenderTargetView currentTarget, GorgonTexture2DView currentTexture) { if (pass.RenderMethod == null) { return; } // If we changed the states, then apply them now. if ((_noEffectBatchState.BlendState != pass.BlendOverride) || (_noEffectBatchState.RasterState != pass.RasterOverride) || (_noEffectBatchState.DepthStencilState != pass.DepthStencilOverride)) { _noEffectBatchState = _noEffectBatchStateBuilder.BlendState(pass.BlendOverride) .DepthStencilState(pass.DepthStencilOverride) .RasterState(pass.RasterOverride) .Build(); } if (Graphics.RenderTargets[0] != currentTarget) { Graphics.SetRenderTarget(currentTarget, Graphics.DepthStencilView); } Renderer.Begin(_noEffectBatchState, pass.Camera); pass.RenderMethod(currentTexture, 0, 0, new DX.Size2(currentTarget.Width, currentTarget.Height)); Renderer.End(); }
/// <summary> /// Function to retrieve a draw indexed call from the pool and, initialize it. /// </summary> /// <param name="renderable">The renderable to evaluate.</param> /// <param name="batchState">The current global state for the batch.</param> /// <param name="indexBuffer">The index buffer to use when creating the draw call.</param> /// <param name="vertexBuffer">The vertex buffer binding to use when creating the draw call.</param> /// <param name="layout">The vertex input layout.</param> /// <returns>The draw call.</returns> public GorgonDrawIndexCall GetDrawIndexCall(BatchRenderable renderable, Gorgon2DBatchState batchState, GorgonIndexBuffer indexBuffer, GorgonVertexBufferBinding vertexBuffer, GorgonInputLayout layout) { SetCommonStates(_drawIndexBuilder, renderable, batchState, null); return(_drawIndexBuilder.VertexBuffer(layout, vertexBuffer) .IndexBuffer(indexBuffer) .Build(_drawIndexAllocator)); }
/// <summary> /// Function called to build a new (or return an existing) 2D batch state. /// </summary> /// <param name="passIndex">The index of the current rendering pass.</param> /// <param name="statesChanged"><b>true</b> if the blend, raster, or depth/stencil state was changed. <b>false</b> if not.</param> /// <returns>The 2D batch state.</returns> protected override Gorgon2DBatchState OnGetBatchState(int passIndex, bool statesChanged) { if (statesChanged) { _batchState = BatchStateBuilder.Build(); } return(_batchState); }
/// <summary> /// Function to build up the render targets. /// </summary> /// <param name="width">The width of the render targets.</param> /// <param name="height">The height of the render targets.</param> /// <param name="format">The format of the buffer.</param> private void BuildRenderTargets(int width, int height, BufferFormat format) { // For the lighting effect, we use a deferred rendering technique where we have 3 render targets for diffuse, specularity, and normal mapping. // These targets are sub resources of the same texture resource (array indices). // Diffuse. _rtvInfo.Width = width; _rtvInfo.Height = height; _rtvInfo.Format = format; _rtvInfo.ArrayCount = 2; _gbufferTargets[0] = Graphics.TemporaryTargets.Rent(_rtvInfo, "Gorgon 2D GBuffer - Diffuse/Specular", false); _gbufferTargets[0].Clear(GorgonColor.Black); // Specular. _gbufferTargets[1] = _gbufferTargets[0].Texture.GetRenderTargetView(arrayIndex: 1, arrayCount: 1); _rtvInfo.Format = BufferFormat.R8G8B8A8_UNorm; _rtvInfo.ArrayCount = 1; // Normals. // We'll clear it before the pass, the default color is insufficient. _gbufferTargets[2] = Graphics.TemporaryTargets.Rent(_rtvInfo, "Gorgon 2D Buffer - Normals", false); GorgonTexture2DView normalSrv = _gbufferTargets[2].GetShaderResourceView(); if ((_pixelLitShaderState.ShaderResources[1] != normalSrv) || (_diffuseFilter != DiffuseFiltering) || (_normalFilter != NormalFiltering) || (_specularFilter != SpecularFiltering)) { _diffuseFilter = DiffuseFiltering ?? GorgonSamplerState.Default; _normalFilter = NormalFiltering ?? GorgonSamplerState.PointFiltering; _specularFilter = SpecularFiltering ?? GorgonSamplerState.Default; _pixelDeferShaderState = PixelShaderBuilder.ResetTo(_pixelDeferShaderState) .SamplerState(_diffuseFilter, 0) .SamplerState(_normalFilter, 1) .SamplerState(_specularFilter, 2) .Build(); _pixelLitShaderState = PixelShaderBuilder .ResetTo(_pixelLitShaderState) .ShaderResource(normalSrv, 1) .SamplerState(_diffuseFilter, 0) .SamplerState(_normalFilter, 1) .SamplerState(_specularFilter, 2) .Build(); _lightingState = BatchStateBuilder .ResetTo(_lightingState) .PixelShaderState(_pixelLitShaderState) .Build(); } _gbufferTexture = _gbufferTargets[0].Texture.GetShaderResourceView(); }
/// <summary> /// Function to clear the effects from the compositor. /// </summary> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor Clear() { _batchState = Gorgon2DBatchState.NoBlend; _batchStateBuilder.Clear(); _batchStateBuilder.BlendState(GorgonBlendState.NoBlending); _effectList.Clear(); _effects.Clear(); _hasBatchStateChanged = false; return(this); }
/// <summary> /// Function called when the effect is being initialized. /// </summary> /// <remarks> /// Use this method to set up the effect upon its creation. For example, this method could be used to create the required shaders for the effect. /// </remarks> protected override void OnInitialize() { // Compile our blur shader. _grayScaleShader = CompileShader <GorgonPixelShader>(Resources.BasicSprite, "GorgonPixelShaderGrayScale"); _grayScaleState = PixelShaderBuilder .Shader(_grayScaleShader) .Build(); _batchState = BatchStateBuilder .PixelShaderState(_grayScaleState) .Build(); }
/// <summary> /// Function called to initialize the effect. /// </summary> /// <remarks>Applications must implement this method to ensure that any required resources are created, and configured for the effect.</remarks> protected override void OnInitialize() { var globalData = new GlobalEffectData { CameraPosition = DX.Vector3.Zero, FlipYNormal = 0 }; _globalData = GorgonConstantBufferView.CreateConstantBuffer(Graphics, ref globalData, "Global deferred light effect data.", ResourceUsage.Default); _lightData = GorgonConstantBufferView.CreateConstantBuffer(Graphics, new GorgonConstantBufferInfo("Deferred Lighting Light Data Buffer") { SizeInBytes = Unsafe.SizeOf <PointLightData>(), Usage = ResourceUsage.Dynamic }); Macros.Add(new GorgonShaderMacro("DEFERRED_LIGHTING")); _vertexDeferShader = CompileShader <GorgonVertexShader>(Resources.Lighting, "GorgonVertexLightingShader"); _vertexDeferShaderState = VertexShaderBuilder.Shader(_vertexDeferShader) .Build(); _pixelDeferShader = CompileShader <GorgonPixelShader>(Resources.Lighting, "GorgonPixelShaderDeferred"); _pixelDeferShaderState = PixelShaderBuilder.Shader(_pixelDeferShader) .SamplerState(_diffuseFilter, 0) .SamplerState(_normalFilter, 1) .SamplerState(_specularFilter, 2) .Build(); Macros.Clear(); Macros.Add(new GorgonShaderMacro("LIGHTS")); _vertexLitShader = CompileShader <GorgonVertexShader>(Resources.Lighting, "GorgonVertexLitShader"); _vertexLitShaderState = VertexShaderBuilder.Shader(_vertexLitShader) .Build(); _pixelLitShader = CompileShader <GorgonPixelShader>(Resources.Lighting, "GorgonPixelShaderLighting"); _pixelLitShaderState = PixelShaderBuilder.Shader(_pixelLitShader) .ConstantBuffer(_lightData, 1) .ConstantBuffer(_globalData, 2) .SamplerState(_diffuseFilter, 0) .SamplerState(_normalFilter, 1) .SamplerState(_specularFilter, 2) .Build(); // Rebuild our states for the new pixel shaders. _lightingState = BatchStateBuilder.PixelShaderState(_pixelLitShaderState) .VertexShaderState(_vertexLitShaderState) .BlendState(GorgonBlendState.Additive) .Build(); }
/// <summary> /// Function called to build a new (or return an existing) 2D batch state. /// </summary> /// <param name="passIndex">The index of the current rendering pass.</param> /// <param name="statesChanged"><b>true</b> if the blend, raster, or depth/stencil state was changed. <b>false</b> if not.</param> /// <returns>The 2D batch state.</returns> protected override Gorgon2DBatchState OnGetBatchState(int passIndex, bool statesChanged) { switch (passIndex) { case 0: if ((statesChanged) || (_deferredState == null)) { _deferredState = BatchStateBuilder .PixelShaderState(_pixelDeferShaderState) .VertexShaderState(_vertexDeferShaderState) .BlendState(GorgonBlendState.NoBlending) .Build(); } return(_deferredState); default: return(_lightingState); } }
/// <summary> /// Function called to render the effect. /// </summary> /// <param name="renderMethod">The method used to render the scene that will be used in the effect.</param> /// <param name="output">The render target that will receive the results of rendering the effect.</param> /// <param name="blendStateOverride">[Optional] An override for the current blending state.</param> /// <param name="depthStencilStateOverride">[Optional] An override for the current depth/stencil state.</param> /// <param name="rasterStateOverride">[Optional] An override for the current raster state.</param> /// <param name="camera">[Optional] The camera to use when rendering.</param> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="renderMethod"/>, or the <paramref name="output"/> parameter is <b>null</b>.</exception> /// <remarks> /// <para> /// The <paramref name="renderMethod"/> is a callback to a method with 3 parameters: /// <list type="number"> /// <item> /// <description>The current pass index.</description> /// </item> /// <item> /// <description>The total number of passes.</description> /// </item> /// <item> /// <description>The size of the current render target.</description> /// </item> /// </list> /// Users should pass a method that will render the items they want to use with this effect. /// </para> /// <para> /// <para> /// If the <paramref name="blendStateOverride"/>, parameter is omitted, then the <see cref="GorgonBlendState.Default"/> is used. When provided, this will override the current blending state. /// </para> /// <para> /// If the <paramref name="depthStencilStateOverride"/> parameter is omitted, then the <see cref="GorgonDepthStencilState.Default"/> is used. When provided, this will override the current /// depth/stencil state. /// </para> /// <para> /// If the <paramref name="rasterStateOverride"/> parameter is omitted, then the <see cref="GorgonRasterState.Default"/> is used. When provided, this will override the current raster state. /// </para> /// <para> /// The <paramref name="camera"/> parameter is used to render the texture using a different view, and optionally, a different coordinate set. /// </para> /// <para> /// <note type="important"> /// <para> /// For performance reasons, any exceptions thrown by this method will only be thrown when Gorgon is compiled as DEBUG. /// </para> /// </note> /// </para> /// </para> /// </remarks> public void Render(Action <int, int, DX.Size2> renderMethod, GorgonRenderTargetView output, GorgonBlendState blendStateOverride = null, GorgonDepthStencilState depthStencilStateOverride = null, GorgonRasterState rasterStateOverride = null, IGorgon2DCamera camera = null) { renderMethod.ValidateObject(nameof(renderMethod)); output.ValidateObject(nameof(output)); bool stateChanged = SetupStates(blendStateOverride, depthStencilStateOverride, rasterStateOverride, output, camera); for (int i = 0; i < PassCount; ++i) { // Batch state should be cached on the implementation side, otherwise the GC could be impacted by a lot of dead objects per frame. Gorgon2DBatchState batchState = OnGetBatchState(i, stateChanged); switch (OnBeforeRenderPass(i, output, camera)) { case PassContinuationState.Continue: Renderer.Begin(batchState, camera); OnRenderPass(i, renderMethod, output); Renderer.End(); OnAfterRenderPass(i, output); break; case PassContinuationState.Skip: continue; default: OnAfterRender(output); return; } } OnAfterRender(output); }
/// <summary>Function called to build a new (or return an existing) 2D batch state.</summary> /// <param name="passIndex">The index of the current rendering pass.</param> /// <param name="statesChanged"> /// <b>true</b> if the blend, raster, or depth/stencil state was changed. <b>false</b> if not.</param> /// <returns>The 2D batch state.</returns> protected override Gorgon2DBatchState OnGetBatchState(int passIndex, bool statesChanged) { if ((_useSimple != FullScreen) || ((LookupTexture == null) && (_currentLut != _defaultLut)) || ((LookupTexture != null) && (LookupTexture != _currentLut))) { GorgonPixelShader current = FullScreen ? _simpleChromeAbShader : _chromeAbShader; _currentLut = LookupTexture ?? _defaultLut; _chromeAbBatchState = BatchStateBuilder.Clear() .BlendState(GorgonBlendState.NoBlending) .PixelShaderState(_shaderStateBuilder .Clear() .Shader(current) .ShaderResource(!FullScreen ? _currentLut : null, 1) .SamplerState(!FullScreen ? GorgonSamplerState.Default : null, 1) .ConstantBuffer(_settings, 1)) .Build(); _useSimple = FullScreen; } return(_chromeAbBatchState); }
/// <summary> /// Function to set up common states for a draw call type. /// </summary> /// <typeparam name="TB">The type of draw call builder.</typeparam> /// <typeparam name="TD">The type of draw call to return.</typeparam> /// <param name="builder">The builder to pass in.</param> /// <param name="renderable">The renderable being rendered.</param> /// <param name="batchState">The current batch level state.</param> /// <param name="renderer">The renderer used to send renderable data to the GPU.</param> private void SetCommonStates <TB, TD>(GorgonDrawCallBuilderCommon <TB, TD> builder, BatchRenderable renderable, Gorgon2DBatchState batchState, ObjectRenderer renderer) where TB : GorgonDrawCallBuilderCommon <TB, TD> where TD : GorgonDrawCallCommon { // If we haven't specified values for our first texture and sampler, then do so now. batchState.PixelShaderState.RwSrvs[0] = renderable.Texture ?? _defaultTexture; batchState.PixelShaderState.RwSamplers[0] = renderable.TextureSampler ?? GorgonSamplerState.Default; builder.ConstantBuffers(ShaderType.Vertex, batchState.VertexShaderState.RwConstantBuffers) .ConstantBuffers(ShaderType.Pixel, batchState.PixelShaderState.RwConstantBuffers) .ShaderResources(ShaderType.Vertex, batchState.VertexShaderState.RwSrvs) .ShaderResources(ShaderType.Pixel, batchState.PixelShaderState.RwSrvs) .SamplerStates(ShaderType.Vertex, batchState.VertexShaderState.RwSamplers) .SamplerStates(ShaderType.Pixel, batchState.PixelShaderState.RwSamplers) .PipelineState(_stateBuilder.PixelShader(batchState.PixelShaderState.Shader) .VertexShader(batchState.VertexShaderState.Shader) .DepthStencilState(batchState.DepthStencilState) .RasterState(batchState.RasterState) .BlendState(batchState.BlendState) .PrimitiveType(renderable.PrimitiveType)); // If we don't supply a renderer type, then don't assign a vertex buffer. if (renderer == null) { return; } builder.VertexBuffer(_inputLayout, renderer.VertexBuffer); }
/// <summary> /// Function to retrieve a draw indexed call from the pool and, initialize it. /// </summary> /// <param name="renderable">The renderable to evaluate.</param> /// <param name="batchState">The current global state for the batch.</param> /// <param name="renderer">The renderer that will be </param> /// <returns>The draw call.</returns> public GorgonDrawIndexCall GetDrawIndexCall(BatchRenderable renderable, Gorgon2DBatchState batchState, ObjectRenderer renderer) { SetCommonStates(_drawIndexBuilder, renderable, batchState, renderer); return(_drawIndexBuilder.IndexBuffer(renderer.IndexBuffer) .Build(_drawIndexAllocator)); }
/// <summary> /// Function to render the scene for the compositor effects. /// </summary> /// <param name="output">The final output render target for the compositor.</param> /// <param name="renderMethod">The method used to render the initial scene.</param> /// <returns>The fluent interface for the effects processor.</returns> public Gorgon2DCompositor Render(GorgonRenderTargetView output, Action renderMethod) { output.ValidateObject(nameof(output)); renderMethod.ValidateObject(nameof(renderMethod)); // Create or update our resources if ((_final != output) || (NeedsResourceUpdate(output))) { FreeResources(); CreateResources(output); } // Create the batch state that we'll use for our initial scene. if (_hasBatchStateChanged) { _batchState = _batchStateBuilder.Build(); _hasBatchStateChanged = false; } if (_hasFinalBatchStateChanged) { _finalBatchState = _finalBatchStateBuilder.Build(); _hasFinalBatchStateChanged = false; } RenderInitalScene(renderMethod); // If we have no effects, then, just output the scene to the render target as-is. if (_effects.Count == 0) { CopyToFinal(_originalTexture); return(this); } (GorgonRenderTargetView target, GorgonTexture2DView texture)current = (_pingTarget, _originalTexture); // Iterate through our items. for (int i = 0; i < _effectList.Count; ++i) { Gorgon2DCompositionPass pass = _effectList[i]; if (!pass.Enabled) { continue; } GorgonRenderTargetView currentTarget = current.target; GorgonTexture2DView currentTexture = current.texture; GorgonTexture2DView nextTexture = ((currentTexture == _originalTexture) || (currentTexture == _pongTexture)) ? _pingTexture : _pongTexture; GorgonRenderTarget2DView nextTarget = current.target == _pingTarget ? _pongTarget : _pingTarget; if (pass.ClearColor != null) { currentTarget.Clear(pass.ClearColor.Value); } if (pass.Effect != null) { RenderEffectPass(pass, currentTarget, currentTexture); } else { RenderNoEffectPass(pass, currentTarget, currentTexture); } current = (nextTarget, nextTexture); } CopyToFinal(current.texture); return(this); }