Files
FlaxEngine/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs
2021-01-02 14:28:49 +01:00

176 lines
5.5 KiB
C#

// Copyright (c) 2012-2021 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>
public class RenderToTextureControl : ContainerControl
{
private bool _invalid, _redrawRegistered, _isDuringTextureDraw;
private bool _autoSize = true;
private GPUTexture _texture;
private Vector2 _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 Vector2 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;
private bool CanEditTextureSize => !_autoSize;
/// <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;
}
}
Profiler.BeginEventGPU("RenderToTextureControl");
var context = GPUDevice.Instance.MainContext;
_isDuringTextureDraw = true;
context.Clear(_texture.View(), Color.Transparent);
Render2D.CallDrawing(this, context, _texture);
_isDuringTextureDraw = false;
Profiler.EndEventGPU();
}
/// <inheritdoc />
public override void Draw()
{
// Draw cached texture
if (_texture && !_invalid && !_isDuringTextureDraw)
{
var bounds = new Rectangle(Vector2.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();
}
}
}