125 lines
3.5 KiB
C#
125 lines
3.5 KiB
C#
|
|
using System.Collections.Generic;
|
||
|
|
using Godot;
|
||
|
|
using System;
|
||
|
|
|
||
|
|
using Rokojori.Extensions;
|
||
|
|
namespace Rokojori
|
||
|
|
{
|
||
|
|
/** Shared pool of full-size DrawableTexture2D buffers, keyed by size and
|
||
|
|
format.
|
||
|
|
|
||
|
|
Compositing one texture holds up to three full-size buffers at once (the
|
||
|
|
target, the scratch buffer and the ping-pong buffer). At 4096 Rgbah that
|
||
|
|
is roughly 128 MB each, so they are rented for the duration of a
|
||
|
|
composite and returned immediately afterwards - allocating them per
|
||
|
|
composite would thrash VRAM, and keeping them alive per texture would
|
||
|
|
multiply by the number of stacks in a scene.
|
||
|
|
|
||
|
|
Buffers are not cleared on rent. Every consumer either Setup()s them or
|
||
|
|
fully overwrites the rect it reads, so clearing would be wasted work.
|
||
|
|
Session 3's incremental redraw will keep buffers instead of returning
|
||
|
|
them, which is why renting goes through a pool rather than through new().
|
||
|
|
*/
|
||
|
|
public class TextureStackBufferPool
|
||
|
|
{
|
||
|
|
public static readonly TextureStackBufferPool shared = new TextureStackBufferPool();
|
||
|
|
|
||
|
|
class Key
|
||
|
|
{
|
||
|
|
public int width;
|
||
|
|
public int height;
|
||
|
|
public TextureStackFormat format;
|
||
|
|
|
||
|
|
public override int GetHashCode()
|
||
|
|
{
|
||
|
|
return ( width * 397 ) ^ ( height * 31 ) ^ (int) format;
|
||
|
|
}
|
||
|
|
|
||
|
|
public override bool Equals( object other )
|
||
|
|
{
|
||
|
|
var key = other as Key;
|
||
|
|
|
||
|
|
return key != null
|
||
|
|
&& key.width == width
|
||
|
|
&& key.height == height
|
||
|
|
&& key.format == format;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Dictionary<Key,List<DrawableTexture2D>> _free = new Dictionary<Key,List<DrawableTexture2D>>();
|
||
|
|
|
||
|
|
/** The key a rented buffer was created under. Tracked here rather than
|
||
|
|
read back off the buffer, because DrawableTexture2D exposes SetFormat
|
||
|
|
without a matching getter.
|
||
|
|
*/
|
||
|
|
Dictionary<DrawableTexture2D,Key> _keys = new Dictionary<DrawableTexture2D,Key>();
|
||
|
|
|
||
|
|
public int rented => _keys.Count;
|
||
|
|
|
||
|
|
public DrawableTexture2D Rent( int width, int height, TextureStackFormat format, Color clearColor )
|
||
|
|
{
|
||
|
|
var key = new Key(){ width = width, height = height, format = format };
|
||
|
|
|
||
|
|
List<DrawableTexture2D> buffers;
|
||
|
|
|
||
|
|
var buffer = (DrawableTexture2D) null;
|
||
|
|
|
||
|
|
if ( _free.TryGetValue( key, out buffers ) && buffers.Count > 0 )
|
||
|
|
{
|
||
|
|
buffer = buffers[ buffers.Count - 1 ];
|
||
|
|
buffers.RemoveAt( buffers.Count - 1 );
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
buffer = new DrawableTexture2D();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Setup() re-allocates and clears, which is what makes a reused buffer safe.
|
||
|
|
buffer.Setup( width, height, format.ToDrawableFormat(), clearColor, false );
|
||
|
|
|
||
|
|
_keys[ buffer ] = key;
|
||
|
|
|
||
|
|
return buffer;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Return( DrawableTexture2D buffer )
|
||
|
|
{
|
||
|
|
if ( buffer == null )
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
Key key;
|
||
|
|
|
||
|
|
if ( ! _keys.TryGetValue( buffer, out key ) )
|
||
|
|
{
|
||
|
|
// Not ours - the caller allocated it, so it is theirs to keep.
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
_keys.Remove( buffer );
|
||
|
|
|
||
|
|
List<DrawableTexture2D> buffers;
|
||
|
|
|
||
|
|
if ( ! _free.TryGetValue( key, out buffers ) )
|
||
|
|
{
|
||
|
|
buffers = new List<DrawableTexture2D>();
|
||
|
|
_free[ key ] = buffers;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ! buffers.Contains( buffer ) )
|
||
|
|
{
|
||
|
|
buffers.Add( buffer );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Drops every pooled buffer. Nothing calls this automatically - it is the
|
||
|
|
escape hatch for freeing VRAM after a one-off 4k bake.
|
||
|
|
*/
|
||
|
|
public void Clear()
|
||
|
|
{
|
||
|
|
_free.Clear();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|