From 740d07c0fd90e35a233e3a5ce09049280ced3023 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 4 Oct 2026 14:20:11 +0200 Subject: [PATCH] Add `DrawMaterial` feature to `RenderToTextureControl` for custom post-processing the cached UI image --- .../UI/GUI/Common/RenderToTextureControl.cs | 94 +++++++++++++++++-- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs index 4f45c6951..f1a9c69a5 100644 --- a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs +++ b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs @@ -12,6 +12,9 @@ namespace FlaxEngine.GUI private bool _autoSize = true; private GPUTexture _texture; private Float2 _textureSize; + private MaterialBase _drawMaterial; + private MaterialInstance _drawMaterialInstance; + private string _drawTextureParameterName = "Input"; /// /// Gets the texture with cached children controls. @@ -21,7 +24,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets a value indicating whether automatically update size of texture when control dimensions gets changed. /// - [EditorOrder(10), Tooltip("If checked, size of the texture will be automatically updated when control dimensions gets changed.")] + [EditorOrder(10)] public bool AutomaticTextureSize { get => _autoSize; @@ -38,7 +41,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the size of the texture (in pixels). /// - [EditorOrder(20), VisibleIf("CanEditTextureSize"), Limit(0, 4096), Tooltip("The size of the texture (in pixels).")] + [EditorOrder(20), VisibleIf(nameof(AutomaticTextureSize), true), Limit(0, 4096)] public Float2 TextureSize { get => _textureSize; @@ -54,15 +57,52 @@ namespace FlaxEngine.GUI /// /// Gets or sets the value whether cached texture data should be invalidated automatically (eg. when child control changes). /// + [EditorOrder(30)] public bool AutomaticInvalidate { get; set; } = true; + /// + /// Gets or sets the GUI material used to draw the cached texture to the screen. + /// Can be used to post-process underlying GUI with a custom shader (eg. chromatic-aberration, blur or tint). If not set, simple texture copy is performed. + /// Materials has to be created with GUI domain and a GPUTexture parameter (default name is "Input") to be used as a source texture. + /// + [EditorOrder(100)] + public MaterialBase DrawMaterial + { + get => _drawMaterial; + set + { + if (_drawMaterial != value) + { + _drawMaterial = value; + Invalidate(); + } + } + } + + /// + /// Gets or sets the name of the GPUTexture parameter on the to use as a source texture. + /// + [EditorOrder(110), VisibleIf("HasDrawMaterial")] + public string DrawTextureParameterName + { + get => _drawTextureParameterName; + set + { + if (_drawTextureParameterName != value) + { + _drawTextureParameterName = value; + Invalidate(); + } + } + } + #if FLAX_EDITOR - private bool CanEditTextureSize => !_autoSize; + private bool HasDrawMaterial => _drawMaterial != null; #endif + /// /// Invalidates the cached image of children controls and invokes the redraw to the texture. /// - [Tooltip("Invalidates the cached image of children controls and invokes the redraw to the texture.")] public void Invalidate() { _invalid = true; @@ -86,7 +126,12 @@ namespace FlaxEngine.GUI _invalid = false; if (!_texture) + { _texture = new GPUTexture(); +#if !BUILD_RELEASE + _texture.Name = nameof(RenderToTextureControl); +#endif + } if (_texture.Size != _textureSize) { var desc = GPUTextureDescription.New2D((int)_textureSize.X, (int)_textureSize.Y, PixelFormat.R8G8B8A8_UNorm); @@ -115,9 +160,9 @@ namespace FlaxEngine.GUI finally { Render2D.End(); + _isDuringTextureDraw = false; + Profiler.EndEventGPU(); } - _isDuringTextureDraw = false; - Profiler.EndEventGPU(); } /// @@ -127,10 +172,43 @@ namespace FlaxEngine.GUI if (_texture && !_invalid && !_isDuringTextureDraw) { var bounds = new Rectangle(Float2.Zero, Size); + + // Background var backgroundColor = BackgroundColor; if (backgroundColor.A > 0.0f) Render2D.FillRectangle(bounds, backgroundColor); - Render2D.DrawTexture(_texture, bounds); + + if (_drawMaterial && !_drawMaterial.WaitForLoaded()) + { + // Blit with a custom material + if (!_drawMaterialInstance) + _drawMaterialInstance = Content.CreateVirtualAsset(); + _drawMaterialInstance.BaseMaterial = _drawMaterial; + if (!_drawMaterial.IsGUI) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Cannot draw RenderToTextureControl contents because material '{_drawMaterial}' isn't GUI domain"); + return; + } + var textureParam = _drawMaterialInstance.GetParameter(_drawTextureParameterName); + if (!textureParam) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Cannot draw RenderToTextureControl contents because material '{_drawMaterial}' doesn't have parameter '{_drawTextureParameterName}'"); + return; + } + if (textureParam.ParameterType != MaterialParameterType.GPUTexture) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Cannot draw RenderToTextureControl contents because material '{_drawMaterial}''s parameter '{_drawTextureParameterName}' is not a GPUTexture"); + return; + } + textureParam.Value = _texture; + Render2D.DrawMaterial(_drawMaterialInstance, bounds); + } + else + { + // Simple texture draw + Render2D.DrawTexture(_texture, bounds); + } + return; } @@ -183,7 +261,9 @@ namespace FlaxEngine.GUI _redrawRegistered = false; Scripting.Draw -= OnDraw; } + _drawMaterial = null; Object.Destroy(ref _texture); + Object.Destroy(ref _drawMaterialInstance); base.OnDestroy(); }