Files
FlaxEngine/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs

192 lines
5.9 KiB
C#

// Copyright (c) Wojciech Figat. All rights reserved.
namespace FlaxEngine.GUI
{
/// <summary>
/// UI container control that can render children to texture and display pre-cached texture instead of drawing children every frame. It can be also used to render part of UI to texture and use it in material or shader.
/// </summary>
[ActorToolbox("GUI")]
public class RenderToTextureControl : ContainerControl
{
private bool _invalid, _redrawRegistered, _isDuringTextureDraw;
private bool _autoSize = true;
private GPUTexture _texture;
private Float2 _textureSize;
/// <summary>
/// Gets the texture with cached children controls.
/// </summary>
public GPUTexture Texture => _texture;
/// <summary>
/// Gets or sets a value indicating whether automatically update size of texture when control dimensions gets changed.
/// </summary>
[EditorOrder(10), Tooltip("If checked, size of the texture will be automatically updated when control dimensions gets changed.")]
public bool AutomaticTextureSize
{
get => _autoSize;
set
{
if (_autoSize == value)
return;
_autoSize = value;
if (_autoSize)
TextureSize = Size;
}
}
/// <summary>
/// Gets or sets the size of the texture (in pixels).
/// </summary>
[EditorOrder(20), VisibleIf("CanEditTextureSize"), Limit(0, 4096), Tooltip("The size of the texture (in pixels).")]
public Float2 TextureSize
{
get => _textureSize;
set
{
if (_textureSize == value)
return;
_textureSize = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the value whether cached texture data should be invalidated automatically (eg. when child control changes).
/// </summary>
public bool AutomaticInvalidate { get; set; } = true;
#if FLAX_EDITOR
private bool CanEditTextureSize => !_autoSize;
#endif
/// <summary>
/// Invalidates the cached image of children controls and invokes the redraw to the texture.
/// </summary>
[Tooltip("Invalidates the cached image of children controls and invokes the redraw to the texture.")]
public void Invalidate()
{
_invalid = true;
if (!_redrawRegistered)
{
_redrawRegistered = true;
Scripting.Draw += OnDraw;
}
}
private void OnDraw()
{
if (_redrawRegistered)
{
_redrawRegistered = false;
Scripting.Draw -= OnDraw;
}
if (!_invalid)
return;
_invalid = false;
if (!_texture)
_texture = new GPUTexture();
if (_texture.Size != _textureSize)
{
var desc = GPUTextureDescription.New2D((int)_textureSize.X, (int)_textureSize.Y, PixelFormat.R8G8B8A8_UNorm);
if (_texture.Init(ref desc))
{
Debug.Logger.LogHandler.LogWrite(LogType.Error, "Failed to allocate texture for RenderToTextureControl");
return;
}
}
if (!_texture || !_texture.IsAllocated)
return;
Profiler.BeginEventGPU("RenderToTextureControl");
var context = GPUDevice.Instance.MainContext;
_isDuringTextureDraw = true;
context.Clear(_texture.View(), Color.Transparent);
Render2D.Begin(context, _texture);
try
{
var scale = _textureSize / Size;
Matrix3x3.Scaling(scale.X, scale.Y, 1.0f, out var scaleMatrix);
Render2D.PushTransform(ref scaleMatrix);
Draw();
Render2D.PopTransform();
}
finally
{
Render2D.End();
}
_isDuringTextureDraw = false;
Profiler.EndEventGPU();
}
/// <inheritdoc />
public override void Draw()
{
// Draw cached texture
if (_texture && !_invalid && !_isDuringTextureDraw)
{
var bounds = new Rectangle(Float2.Zero, Size);
var backgroundColor = BackgroundColor;
if (backgroundColor.A > 0.0f)
Render2D.FillRectangle(bounds, backgroundColor);
Render2D.DrawTexture(_texture, bounds);
return;
}
// Draw default UI directly
base.Draw();
}
/// <inheritdoc />
protected override void OnSizeChanged()
{
base.OnSizeChanged();
if (_autoSize)
TextureSize = Size;
}
/// <inheritdoc />
public override void OnChildResized(Control control)
{
base.OnChildResized(control);
if (AutomaticInvalidate)
Invalidate();
}
/// <inheritdoc />
public override void OnChildrenChanged()
{
base.OnChildrenChanged();
if (AutomaticInvalidate)
Invalidate();
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
if (AutomaticInvalidate)
Invalidate();
}
/// <inheritdoc />
public override void OnDestroy()
{
_invalid = false;
if (_redrawRegistered)
{
_redrawRegistered = false;
Scripting.Draw -= OnDraw;
}
Object.Destroy(ref _texture);
base.OnDestroy();
}
}
}