2021-07-14 19:27:22 +02:00
using Ryujinx.Common.Logging ;
2019-10-18 04:41:18 +02:00
using Ryujinx.Graphics.GAL ;
2021-07-11 22:20:40 +02:00
using Ryujinx.Graphics.Gpu.Engine.Types ;
2022-06-17 18:09:14 +02:00
using Ryujinx.Graphics.Gpu.Memory ;
using Ryujinx.Graphics.Gpu.Shader ;
2019-10-18 04:41:18 +02:00
using Ryujinx.Graphics.Shader ;
2021-06-09 01:00:28 +02:00
using System ;
2022-06-17 18:09:14 +02:00
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
2019-10-18 04:41:18 +02:00
namespace Ryujinx.Graphics.Gpu.Image
{
2019-12-30 00:26:37 +01:00
/// <summary>
/// Texture bindings manager.
/// </summary>
2021-06-09 01:00:28 +02:00
class TextureBindingsManager : IDisposable
2019-10-18 04:41:18 +02:00
{
2021-09-19 14:03:05 +02:00
private const int InitialTextureStateSize = 32 ;
private const int InitialImageStateSize = 8 ;
2021-06-24 01:51:41 +02:00
private readonly GpuContext _context ;
2019-10-18 04:41:18 +02:00
2021-06-24 01:51:41 +02:00
private readonly bool _isCompute ;
2019-10-18 04:41:18 +02:00
private SamplerPool _samplerPool ;
2019-12-05 21:34:47 +01:00
private SamplerIndex _samplerIndex ;
2019-10-18 04:41:18 +02:00
private ulong _texturePoolAddress ;
private int _texturePoolMaximumId ;
2021-06-24 01:51:41 +02:00
private readonly GpuChannel _channel ;
private readonly TexturePoolCache _texturePoolCache ;
2019-10-18 04:41:18 +02:00
2021-06-24 01:51:41 +02:00
private readonly TextureBindingInfo [ ] [ ] _textureBindings ;
private readonly TextureBindingInfo [ ] [ ] _imageBindings ;
2019-10-18 04:41:18 +02:00
2022-06-17 22:41:38 +02:00
private struct TextureState
2019-10-18 04:41:18 +02:00
{
public ITexture Texture ;
public ISampler Sampler ;
2022-06-17 18:09:14 +02:00
public int TextureHandle ;
public int SamplerHandle ;
public int InvalidatedSequence ;
public Texture CachedTexture ;
public Sampler CachedSampler ;
2022-06-17 22:41:38 +02:00
public int ScaleIndex ;
public TextureUsageFlags UsageFlags ;
2019-10-18 04:41:18 +02:00
}
2022-06-17 22:41:38 +02:00
private TextureState [ ] _textureState ;
private TextureState [ ] _imageState ;
2019-10-18 04:41:18 +02:00
2021-09-19 14:03:05 +02:00
private int [ ] _textureBindingsCount ;
private int [ ] _imageBindingsCount ;
2022-06-17 18:09:14 +02:00
private int _texturePoolSequence ;
private int _samplerPoolSequence ;
2019-10-18 04:41:18 +02:00
2022-06-17 18:09:14 +02:00
private int _textureBufferIndex ;
2019-10-18 04:41:18 +02:00
2021-06-24 01:51:41 +02:00
private readonly float [ ] _scales ;
2020-11-02 20:53:23 +01:00
private bool _scaleChanged ;
2022-01-27 18:17:13 +01:00
private int _lastFragmentTotal ;
2020-11-02 20:53:23 +01:00
2019-12-30 00:26:37 +01:00
/// <summary>
/// Constructs a new instance of the texture bindings manager.
/// </summary>
/// <param name="context">The GPU context that the texture bindings manager belongs to</param>
2021-06-24 01:51:41 +02:00
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
/// <param name="poolCache">Texture pools cache used to get texture pools from</param>
2021-09-28 23:52:27 +02:00
/// <param name="scales">Array where the scales for the currently bound textures are stored</param>
2019-12-30 00:26:37 +01:00
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
2021-09-28 23:52:27 +02:00
public TextureBindingsManager ( GpuContext context , GpuChannel channel , TexturePoolCache poolCache , float [ ] scales , bool isCompute )
2019-10-18 04:41:18 +02:00
{
2019-11-22 18:17:06 +01:00
_context = context ;
2021-06-24 01:51:41 +02:00
_channel = channel ;
_texturePoolCache = poolCache ;
2021-09-28 23:52:27 +02:00
_scales = scales ;
2019-11-22 18:17:06 +01:00
_isCompute = isCompute ;
2019-10-18 04:41:18 +02:00
2020-01-01 16:39:09 +01:00
int stages = isCompute ? 1 : Constants . ShaderStages ;
2019-10-18 04:41:18 +02:00
_textureBindings = new TextureBindingInfo [ stages ] [ ] ;
_imageBindings = new TextureBindingInfo [ stages ] [ ] ;
2022-06-17 22:41:38 +02:00
_textureState = new TextureState [ InitialTextureStateSize ] ;
_imageState = new TextureState [ InitialImageStateSize ] ;
2020-11-02 20:53:23 +01:00
2021-09-19 14:03:05 +02:00
_textureBindingsCount = new int [ stages ] ;
_imageBindingsCount = new int [ stages ] ;
for ( int stage = 0 ; stage < stages ; stage + + )
{
_textureBindings [ stage ] = new TextureBindingInfo [ InitialTextureStateSize ] ;
_imageBindings [ stage ] = new TextureBindingInfo [ InitialImageStateSize ] ;
}
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
2021-09-19 14:03:05 +02:00
/// Rents the texture bindings array for a given stage, so that they can be modified.
2019-12-30 00:26:37 +01:00
/// </summary>
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
2021-09-19 14:03:05 +02:00
/// <param name="count">The number of bindings needed</param>
/// <returns>The texture bindings array</returns>
public TextureBindingInfo [ ] RentTextureBindings ( int stage , int count )
2019-10-18 04:41:18 +02:00
{
2021-09-19 14:03:05 +02:00
if ( count > _textureBindings [ stage ] . Length )
{
Array . Resize ( ref _textureBindings [ stage ] , count ) ;
}
_textureBindingsCount [ stage ] = count ;
return _textureBindings [ stage ] ;
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
2021-09-19 14:03:05 +02:00
/// Rents the image bindings array for a given stage, so that they can be modified.
2019-12-30 00:26:37 +01:00
/// </summary>
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
2021-09-19 14:03:05 +02:00
/// <param name="count">The number of bindings needed</param>
/// <returns>The image bindings array</returns>
public TextureBindingInfo [ ] RentImageBindings ( int stage , int count )
2019-10-18 04:41:18 +02:00
{
2021-09-19 14:03:05 +02:00
if ( count > _imageBindings [ stage ] . Length )
{
Array . Resize ( ref _imageBindings [ stage ] , count ) ;
}
2022-06-17 18:09:14 +02:00
_imageBindingsCount [ stage ] = count ;
return _imageBindings [ stage ] ;
}
2021-09-19 14:03:05 +02:00
2022-06-17 18:09:14 +02:00
/// <summary>
/// Sets the max binding indexes for textures and images.
/// </summary>
/// <param name="maxTextureBinding">The maximum texture binding</param>
/// <param name="maxImageBinding">The maximum image binding</param>
public void SetMaxBindings ( int maxTextureBinding , int maxImageBinding )
{
if ( maxTextureBinding > = _textureState . Length )
2021-09-19 14:03:05 +02:00
{
2022-06-17 18:09:14 +02:00
Array . Resize ( ref _textureState , maxTextureBinding + 1 ) ;
2021-09-19 14:03:05 +02:00
}
2022-06-17 18:09:14 +02:00
if ( maxImageBinding > = _imageState . Length )
{
Array . Resize ( ref _imageState , maxImageBinding + 1 ) ;
}
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Sets the textures constant buffer index.
/// The constant buffer specified holds the texture handles.
/// </summary>
/// <param name="index">Constant buffer index</param>
2019-10-18 04:41:18 +02:00
public void SetTextureBufferIndex ( int index )
{
_textureBufferIndex = index ;
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Sets the current texture sampler pool to be used.
/// </summary>
/// <param name="gpuVa">Start GPU virtual address of the pool</param>
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
2019-12-05 21:34:47 +01:00
public void SetSamplerPool ( ulong gpuVa , int maximumId , SamplerIndex samplerIndex )
2019-10-18 04:41:18 +02:00
{
2021-07-14 19:27:22 +02:00
if ( gpuVa ! = 0 )
2019-10-18 04:41:18 +02:00
{
2021-07-14 19:27:22 +02:00
ulong address = _channel . MemoryManager . Translate ( gpuVa ) ;
if ( _samplerPool ! = null & & _samplerPool . Address = = address & & _samplerPool . MaximumId > = maximumId )
2019-10-18 04:41:18 +02:00
{
return ;
}
2021-07-14 19:27:22 +02:00
_samplerPool ? . Dispose ( ) ;
_samplerPool = new SamplerPool ( _context , _channel . MemoryManager . Physical , address , maximumId ) ;
}
else
{
_samplerPool ? . Dispose ( ) ;
_samplerPool = null ;
2019-10-18 04:41:18 +02:00
}
2019-12-05 21:34:47 +01:00
_samplerIndex = samplerIndex ;
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Sets the current texture pool to be used.
/// </summary>
/// <param name="gpuVa">Start GPU virtual address of the pool</param>
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
2019-10-18 04:41:18 +02:00
public void SetTexturePool ( ulong gpuVa , int maximumId )
{
2021-07-14 19:27:22 +02:00
if ( gpuVa ! = 0 )
{
ulong address = _channel . MemoryManager . Translate ( gpuVa ) ;
2019-10-18 04:41:18 +02:00
2021-07-14 19:27:22 +02:00
_texturePoolAddress = address ;
_texturePoolMaximumId = maximumId ;
}
else
{
_texturePoolAddress = 0 ;
_texturePoolMaximumId = 0 ;
}
2019-10-18 04:41:18 +02:00
}
2021-11-10 19:37:49 +01:00
/// <summary>
/// Gets a texture and a sampler from their respective pools from a texture ID and a sampler ID.
/// </summary>
/// <param name="textureId">ID of the texture</param>
/// <param name="samplerId">ID of the sampler</param>
public ( Texture , Sampler ) GetTextureAndSampler ( int textureId , int samplerId )
{
ulong texturePoolAddress = _texturePoolAddress ;
TexturePool texturePool = texturePoolAddress ! = 0
? _texturePoolCache . FindOrCreate ( _channel , texturePoolAddress , _texturePoolMaximumId )
: null ;
return ( texturePool . Get ( textureId ) , _samplerPool . Get ( samplerId ) ) ;
}
2020-11-02 20:53:23 +01:00
/// <summary>
/// Updates the texture scale for a given texture or image.
/// </summary>
/// <param name="texture">Start GPU virtual address of the pool</param>
2022-06-17 22:41:38 +02:00
/// <param name="usageFlags">The related texture usage flags</param>
2020-11-02 20:53:23 +01:00
/// <param name="index">The texture/image binding index</param>
/// <param name="stage">The active shader stage</param>
/// <returns>True if the given texture has become blacklisted, indicating that its host texture may have changed.</returns>
2022-06-17 22:41:38 +02:00
private bool UpdateScale ( Texture texture , TextureUsageFlags usageFlags , int index , ShaderStage stage )
2020-11-02 20:53:23 +01:00
{
float result = 1f ;
bool changed = false ;
2022-06-17 22:41:38 +02:00
if ( ( usageFlags & TextureUsageFlags . NeedsScaleValue ) ! = 0 & & texture ! = null )
2020-11-02 20:53:23 +01:00
{
2022-06-17 22:41:38 +02:00
if ( ( usageFlags & TextureUsageFlags . ResScaleUnsupported ) ! = 0 )
2020-11-02 20:53:23 +01:00
{
2022-01-08 18:48:48 +01:00
changed = texture . ScaleMode ! = TextureScaleMode . Blacklisted ;
texture . BlacklistScale ( ) ;
}
else
{
switch ( stage )
{
case ShaderStage . Fragment :
float scale = texture . ScaleFactor ;
2020-11-02 20:53:23 +01:00
2022-01-08 18:48:48 +01:00
if ( scale ! = 1 )
2020-11-02 20:53:23 +01:00
{
2022-01-08 18:48:48 +01:00
Texture activeTarget = _channel . TextureManager . GetAnyRenderTarget ( ) ;
if ( activeTarget ! = null & & ( activeTarget . Info . Width / ( float ) texture . Info . Width ) = = ( activeTarget . Info . Height / ( float ) texture . Info . Height ) )
{
// If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels)
result = - scale ;
break ;
}
2020-11-02 20:53:23 +01:00
}
2022-01-08 18:48:48 +01:00
result = scale ;
break ;
case ShaderStage . Vertex :
int fragmentIndex = ( int ) ShaderStage . Fragment - 1 ;
index + = _textureBindingsCount [ fragmentIndex ] + _imageBindingsCount [ fragmentIndex ] ;
2020-11-02 20:53:23 +01:00
2022-01-08 18:48:48 +01:00
result = texture . ScaleFactor ;
break ;
2020-11-02 20:53:23 +01:00
2022-01-08 18:48:48 +01:00
case ShaderStage . Compute :
result = texture . ScaleFactor ;
break ;
}
2020-11-02 20:53:23 +01:00
}
}
2021-08-27 22:08:30 +02:00
if ( result ! = _scales [ index ] )
{
_scaleChanged = true ;
_scales [ index ] = result ;
}
2020-11-02 20:53:23 +01:00
return changed ;
}
/// <summary>
/// Uploads texture and image scales to the backend when they are used.
/// </summary>
2022-01-08 18:48:48 +01:00
private void CommitRenderScale ( )
2020-11-02 20:53:23 +01:00
{
2022-01-27 18:17:13 +01:00
// Stage 0 total: Compute or Vertex.
int total = _textureBindingsCount [ 0 ] + _imageBindingsCount [ 0 ] ;
2022-01-08 18:48:48 +01:00
2022-01-27 18:17:13 +01:00
int fragmentIndex = ( int ) ShaderStage . Fragment - 1 ;
int fragmentTotal = _isCompute ? 0 : ( _textureBindingsCount [ fragmentIndex ] + _imageBindingsCount [ fragmentIndex ] ) ;
2022-01-08 18:48:48 +01:00
2022-01-27 18:17:13 +01:00
if ( total ! = 0 & & fragmentTotal ! = _lastFragmentTotal )
{
// Must update scales in the support buffer if:
// - Vertex stage has bindings.
// - Fragment stage binding count has been updated since last render scale update.
2022-01-08 18:48:48 +01:00
2022-01-27 18:17:13 +01:00
_scaleChanged = true ;
}
if ( _scaleChanged )
{
if ( ! _isCompute )
2022-01-08 18:48:48 +01:00
{
2022-01-27 18:17:13 +01:00
total + = fragmentTotal ; // Add the fragment bindings to the total.
2022-01-08 18:48:48 +01:00
}
2022-01-27 18:17:13 +01:00
_lastFragmentTotal = fragmentTotal ;
2022-01-08 18:48:48 +01:00
_context . Renderer . Pipeline . UpdateRenderScale ( _scales , total , fragmentTotal ) ;
2021-08-27 22:08:30 +02:00
_scaleChanged = false ;
2020-11-02 20:53:23 +01:00
}
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Ensures that the bindings are visible to the host GPU.
2020-01-01 16:39:09 +01:00
/// Note: this actually performs the binding using the host graphics API.
2019-12-30 00:26:37 +01:00
/// </summary>
2022-06-17 18:09:14 +02:00
/// <param name="specState">Specialization state for the bound shader</param>
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
public bool CommitBindings ( ShaderSpecializationState specState )
2019-10-18 04:41:18 +02:00
{
2021-07-14 19:27:22 +02:00
ulong texturePoolAddress = _texturePoolAddress ;
TexturePool texturePool = texturePoolAddress ! = 0
? _texturePoolCache . FindOrCreate ( _channel , texturePoolAddress , _texturePoolMaximumId )
: null ;
2019-10-18 04:41:18 +02:00
2022-06-17 18:09:14 +02:00
// Check if the texture pool has been modified since bindings were last committed.
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
bool poolModified = false ;
if ( texturePool ! = null )
{
int texturePoolSequence = texturePool . CheckModified ( ) ;
if ( _texturePoolSequence ! = texturePoolSequence )
{
poolModified = true ;
_texturePoolSequence = texturePoolSequence ;
}
}
if ( _samplerPool ! = null )
{
int samplerPoolSequence = _samplerPool . CheckModified ( ) ;
if ( _samplerPoolSequence ! = samplerPoolSequence )
{
poolModified = true ;
_samplerPoolSequence = samplerPoolSequence ;
}
}
bool specStateMatches = true ;
2019-10-18 04:41:18 +02:00
if ( _isCompute )
{
2022-06-17 18:09:14 +02:00
specStateMatches & = CommitTextureBindings ( texturePool , ShaderStage . Compute , 0 , poolModified , specState ) ;
specStateMatches & = CommitImageBindings ( texturePool , ShaderStage . Compute , 0 , poolModified , specState ) ;
2019-10-18 04:41:18 +02:00
}
else
{
for ( ShaderStage stage = ShaderStage . Vertex ; stage < = ShaderStage . Fragment ; stage + + )
{
int stageIndex = ( int ) stage - 1 ;
2022-06-17 18:09:14 +02:00
specStateMatches & = CommitTextureBindings ( texturePool , stage , stageIndex , poolModified , specState ) ;
specStateMatches & = CommitImageBindings ( texturePool , stage , stageIndex , poolModified , specState ) ;
2019-10-18 04:41:18 +02:00
}
}
2022-01-08 18:48:48 +01:00
CommitRenderScale ( ) ;
2022-06-17 18:09:14 +02:00
return specStateMatches ;
}
/// <summary>
/// Fetch the constant buffers used for a texture to cache.
/// </summary>
/// <param name="stageIndex">Stage index of the constant buffer</param>
/// <param name="cachedTextureBufferIndex">The currently cached texture buffer index</param>
/// <param name="cachedSamplerBufferIndex">The currently cached sampler buffer index</param>
/// <param name="cachedTextureBuffer">The currently cached texture buffer data</param>
/// <param name="cachedSamplerBuffer">The currently cached sampler buffer data</param>
/// <param name="textureBufferIndex">The new texture buffer index</param>
/// <param name="samplerBufferIndex">The new sampler buffer index</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateCachedBuffer (
int stageIndex ,
ref int cachedTextureBufferIndex ,
ref int cachedSamplerBufferIndex ,
ref ReadOnlySpan < int > cachedTextureBuffer ,
ref ReadOnlySpan < int > cachedSamplerBuffer ,
int textureBufferIndex ,
int samplerBufferIndex )
{
if ( textureBufferIndex ! = cachedTextureBufferIndex )
{
ref BufferBounds bounds = ref _channel . BufferManager . GetUniformBufferBounds ( _isCompute , stageIndex , textureBufferIndex ) ;
cachedTextureBuffer = MemoryMarshal . Cast < byte , int > ( _channel . MemoryManager . Physical . GetSpan ( bounds . Address , ( int ) bounds . Size ) ) ;
cachedTextureBufferIndex = textureBufferIndex ;
if ( samplerBufferIndex = = textureBufferIndex )
{
cachedSamplerBuffer = cachedTextureBuffer ;
cachedSamplerBufferIndex = samplerBufferIndex ;
}
}
if ( samplerBufferIndex ! = cachedSamplerBufferIndex )
{
ref BufferBounds bounds = ref _channel . BufferManager . GetUniformBufferBounds ( _isCompute , stageIndex , samplerBufferIndex ) ;
cachedSamplerBuffer = MemoryMarshal . Cast < byte , int > ( _channel . MemoryManager . Physical . GetSpan ( bounds . Address , ( int ) bounds . Size ) ) ;
cachedSamplerBufferIndex = samplerBufferIndex ;
}
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Ensures that the texture bindings are visible to the host GPU.
2020-01-01 16:39:09 +01:00
/// Note: this actually performs the binding using the host graphics API.
2019-12-30 00:26:37 +01:00
/// </summary>
/// <param name="pool">The current texture pool</param>
/// <param name="stage">The shader stage using the textures to be bound</param>
2022-06-17 18:09:14 +02:00
/// <param name="stageIndex">The stage number of the specified shader stage</param
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
/// <param name="specState">Specialization state for the bound shader</param>
/// <returns>True if all bound textures match the current shader specialiation state, false otherwise</returns>
private bool CommitTextureBindings ( TexturePool pool , ShaderStage stage , int stageIndex , bool poolModified , ShaderSpecializationState specState )
2019-10-18 04:41:18 +02:00
{
2021-09-19 14:03:05 +02:00
int textureCount = _textureBindingsCount [ stageIndex ] ;
if ( textureCount = = 0 )
2021-07-14 19:27:22 +02:00
{
2022-06-17 18:09:14 +02:00
return true ;
2021-07-14 19:27:22 +02:00
}
var samplerPool = _samplerPool ;
if ( pool = = null )
{
Logger . Error ? . Print ( LogClass . Gpu , $"Shader stage \" { stage } \ " uses textures, but texture pool was not set." ) ;
2022-06-17 18:09:14 +02:00
return true ;
2021-07-14 19:27:22 +02:00
}
2022-06-17 18:09:14 +02:00
bool specStateMatches = true ;
int cachedTextureBufferIndex = - 1 ;
int cachedSamplerBufferIndex = - 1 ;
ReadOnlySpan < int > cachedTextureBuffer = Span < int > . Empty ;
ReadOnlySpan < int > cachedSamplerBuffer = Span < int > . Empty ;
2021-09-19 14:03:05 +02:00
for ( int index = 0 ; index < textureCount ; index + + )
2019-10-18 04:41:18 +02:00
{
2020-11-08 12:10:00 +01:00
TextureBindingInfo bindingInfo = _textureBindings [ stageIndex ] [ index ] ;
2022-06-17 22:41:38 +02:00
TextureUsageFlags usageFlags = bindingInfo . Flags ;
2019-10-18 04:41:18 +02:00
2021-10-17 22:28:18 +02:00
( int textureBufferIndex , int samplerBufferIndex ) = TextureHandle . UnpackSlots ( bindingInfo . CbufSlot , _textureBufferIndex ) ;
2021-06-09 00:42:25 +02:00
2022-06-17 18:09:14 +02:00
UpdateCachedBuffer ( stageIndex , ref cachedTextureBufferIndex , ref cachedSamplerBufferIndex , ref cachedTextureBuffer , ref cachedSamplerBuffer , textureBufferIndex , samplerBufferIndex ) ;
int packedId = TextureHandle . ReadPackedId ( bindingInfo . Handle , cachedTextureBuffer , cachedSamplerBuffer ) ;
int textureId = TextureHandle . UnpackTextureId ( packedId ) ;
2019-12-05 21:34:47 +01:00
int samplerId ;
if ( _samplerIndex = = SamplerIndex . ViaHeaderIndex )
{
samplerId = textureId ;
}
else
{
2022-06-17 18:09:14 +02:00
samplerId = TextureHandle . UnpackSamplerId ( packedId ) ;
2019-12-05 21:34:47 +01:00
}
2019-10-18 04:41:18 +02:00
2022-06-17 22:41:38 +02:00
ref TextureState state = ref _textureState [ bindingInfo . Binding ] ;
2022-06-17 18:09:14 +02:00
if ( ! poolModified & &
state . TextureHandle = = textureId & &
state . SamplerHandle = = samplerId & &
state . CachedTexture ! = null & &
state . CachedTexture . InvalidatedSequence = = state . InvalidatedSequence & &
state . CachedSampler ? . IsDisposed ! = true )
{
// The texture is already bound.
state . CachedTexture . SynchronizeMemory ( ) ;
2022-06-17 22:41:38 +02:00
if ( ( state . ScaleIndex ! = index | | state . UsageFlags ! = usageFlags ) & &
UpdateScale ( state . CachedTexture , usageFlags , index , stage ) )
{
ITexture hostTextureRebind = state . CachedTexture . GetTargetTexture ( bindingInfo . Target ) ;
state . Texture = hostTextureRebind ;
state . ScaleIndex = index ;
state . UsageFlags = usageFlags ;
_context . Renderer . Pipeline . SetTexture ( bindingInfo . Binding , hostTextureRebind ) ;
}
2022-06-17 18:09:14 +02:00
continue ;
}
state . TextureHandle = textureId ;
state . SamplerHandle = samplerId ;
ref readonly TextureDescriptor descriptor = ref pool . GetForBinding ( textureId , out Texture texture ) ;
specStateMatches & = specState . MatchesTexture ( stage , index , descriptor ) ;
2019-10-18 04:41:18 +02:00
2020-11-08 12:10:00 +01:00
ITexture hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2019-10-18 04:41:18 +02:00
2020-12-03 20:34:27 +01:00
if ( hostTexture ! = null & & texture . Target = = Target . TextureBuffer )
2020-04-25 15:02:18 +02:00
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
2021-06-24 01:51:41 +02:00
_channel . BufferManager . SetBufferTextureStorage ( hostTexture , texture . Range . GetSubRange ( 0 ) . Address , texture . Size , bindingInfo , bindingInfo . Format , false ) ;
2020-04-25 15:02:18 +02:00
}
2021-12-19 15:50:44 +01:00
else
{
2022-06-17 18:09:14 +02:00
if ( state . Texture ! = hostTexture )
2021-12-19 15:50:44 +01:00
{
2022-06-17 22:41:38 +02:00
if ( UpdateScale ( texture , usageFlags , index , stage ) )
2021-12-19 15:50:44 +01:00
{
hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
}
2022-06-17 18:09:14 +02:00
state . Texture = hostTexture ;
2022-06-17 22:41:38 +02:00
state . ScaleIndex = index ;
state . UsageFlags = usageFlags ;
2020-04-25 15:02:18 +02:00
2021-12-19 15:50:44 +01:00
_context . Renderer . Pipeline . SetTexture ( bindingInfo . Binding , hostTexture ) ;
}
2019-10-18 04:41:18 +02:00
2021-12-19 15:50:44 +01:00
Sampler sampler = samplerPool ? . Get ( samplerId ) ;
2022-06-17 18:09:14 +02:00
state . CachedSampler = sampler ;
2019-10-18 04:41:18 +02:00
2021-12-19 15:50:44 +01:00
ISampler hostSampler = sampler ? . GetHostSampler ( texture ) ;
2022-06-17 18:09:14 +02:00
if ( state . Sampler ! = hostSampler )
2021-12-19 15:50:44 +01:00
{
2022-06-17 18:09:14 +02:00
state . Sampler = hostSampler ;
2019-10-18 04:41:18 +02:00
2021-12-19 15:50:44 +01:00
_context . Renderer . Pipeline . SetSampler ( bindingInfo . Binding , hostSampler ) ;
}
2022-06-17 18:09:14 +02:00
state . CachedTexture = texture ;
state . InvalidatedSequence = texture ? . InvalidatedSequence ? ? 0 ;
2019-10-18 04:41:18 +02:00
}
}
2022-06-17 18:09:14 +02:00
return specStateMatches ;
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Ensures that the image bindings are visible to the host GPU.
2020-01-01 16:39:09 +01:00
/// Note: this actually performs the binding using the host graphics API.
2019-12-30 00:26:37 +01:00
/// </summary>
/// <param name="pool">The current texture pool</param>
/// <param name="stage">The shader stage using the textures to be bound</param>
/// <param name="stageIndex">The stage number of the specified shader stage</param>
2022-06-17 18:09:14 +02:00
/// <param name="poolModified">True if either the texture or sampler pool was modified, false otherwise</param>
/// <param name="specState">Specialization state for the bound shader</param>
/// <returns>True if all bound images match the current shader specialiation state, false otherwise</returns>
private bool CommitImageBindings ( TexturePool pool , ShaderStage stage , int stageIndex , bool poolModified , ShaderSpecializationState specState )
2019-10-18 04:41:18 +02:00
{
2021-09-19 14:03:05 +02:00
int imageCount = _imageBindingsCount [ stageIndex ] ;
if ( imageCount = = 0 )
2019-10-18 04:41:18 +02:00
{
2022-06-17 18:09:14 +02:00
return true ;
2019-10-18 04:41:18 +02:00
}
2021-09-19 14:03:05 +02:00
if ( pool = = null )
2021-07-14 19:27:22 +02:00
{
Logger . Error ? . Print ( LogClass . Gpu , $"Shader stage \" { stage } \ " uses images, but texture pool was not set." ) ;
2022-06-17 18:09:14 +02:00
return true ;
2021-07-14 19:27:22 +02:00
}
2020-11-02 20:53:23 +01:00
// Scales for images appear after the texture ones.
2021-09-19 14:03:05 +02:00
int baseScaleIndex = _textureBindingsCount [ stageIndex ] ;
2020-11-02 20:53:23 +01:00
2022-06-17 18:09:14 +02:00
int cachedTextureBufferIndex = - 1 ;
int cachedSamplerBufferIndex = - 1 ;
ReadOnlySpan < int > cachedTextureBuffer = Span < int > . Empty ;
ReadOnlySpan < int > cachedSamplerBuffer = Span < int > . Empty ;
bool specStateMatches = true ;
2021-09-19 14:03:05 +02:00
for ( int index = 0 ; index < imageCount ; index + + )
2019-10-18 04:41:18 +02:00
{
2020-11-08 12:10:00 +01:00
TextureBindingInfo bindingInfo = _imageBindings [ stageIndex ] [ index ] ;
2022-06-17 22:41:38 +02:00
TextureUsageFlags usageFlags = bindingInfo . Flags ;
int scaleIndex = baseScaleIndex + index ;
2019-10-18 04:41:18 +02:00
2021-10-17 22:28:18 +02:00
( int textureBufferIndex , int samplerBufferIndex ) = TextureHandle . UnpackSlots ( bindingInfo . CbufSlot , _textureBufferIndex ) ;
2020-11-09 23:35:04 +01:00
2022-06-17 18:09:14 +02:00
UpdateCachedBuffer ( stageIndex , ref cachedTextureBufferIndex , ref cachedSamplerBufferIndex , ref cachedTextureBuffer , ref cachedSamplerBuffer , textureBufferIndex , samplerBufferIndex ) ;
2019-10-18 04:41:18 +02:00
2022-06-17 18:09:14 +02:00
int packedId = TextureHandle . ReadPackedId ( bindingInfo . Handle , cachedTextureBuffer , cachedSamplerBuffer ) ;
int textureId = TextureHandle . UnpackTextureId ( packedId ) ;
2019-10-18 04:41:18 +02:00
2022-06-17 22:41:38 +02:00
ref TextureState state = ref _imageState [ bindingInfo . Binding ] ;
2019-10-18 04:41:18 +02:00
2021-03-08 22:43:39 +01:00
bool isStore = bindingInfo . Flags . HasFlag ( TextureUsageFlags . ImageStore ) ;
2022-06-17 18:09:14 +02:00
if ( ! poolModified & &
state . TextureHandle = = textureId & &
state . CachedTexture ! = null & &
state . CachedTexture . InvalidatedSequence = = state . InvalidatedSequence )
{
2022-06-17 22:41:38 +02:00
Texture cachedTexture = state . CachedTexture ;
2022-06-17 18:09:14 +02:00
// The texture is already bound.
2022-06-17 22:41:38 +02:00
cachedTexture . SynchronizeMemory ( ) ;
2022-06-17 18:09:14 +02:00
if ( isStore )
{
2022-06-17 22:41:38 +02:00
cachedTexture ? . SignalModified ( ) ;
}
if ( ( state . ScaleIndex ! = index | | state . UsageFlags ! = usageFlags ) & &
UpdateScale ( state . CachedTexture , usageFlags , scaleIndex , stage ) )
{
ITexture hostTextureRebind = state . CachedTexture . GetTargetTexture ( bindingInfo . Target ) ;
Format format = bindingInfo . Format = = 0 ? cachedTexture . Format : bindingInfo . Format ;
state . Texture = hostTextureRebind ;
state . ScaleIndex = scaleIndex ;
state . UsageFlags = usageFlags ;
_context . Renderer . Pipeline . SetImage ( bindingInfo . Binding , hostTextureRebind , format ) ;
2022-06-17 18:09:14 +02:00
}
continue ;
}
state . TextureHandle = textureId ;
ref readonly TextureDescriptor descriptor = ref pool . GetForBinding ( textureId , out Texture texture ) ;
specStateMatches & = specState . MatchesImage ( stage , index , descriptor ) ;
ITexture hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2020-12-03 20:34:27 +01:00
if ( hostTexture ! = null & & texture . Target = = Target . TextureBuffer )
2020-10-20 23:56:23 +02:00
{
// Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accomodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted.
2021-03-08 22:43:39 +01:00
Format format = bindingInfo . Format ;
if ( format = = 0 & & texture ! = null )
{
format = texture . Format ;
}
2021-06-24 01:51:41 +02:00
_channel . BufferManager . SetBufferTextureStorage ( hostTexture , texture . Range . GetSubRange ( 0 ) . Address , texture . Size , bindingInfo , format , true ) ;
2021-05-31 21:59:23 +02:00
}
2021-12-19 15:50:44 +01:00
else
2019-10-18 04:41:18 +02:00
{
2021-12-19 15:50:44 +01:00
if ( isStore )
2020-11-02 20:53:23 +01:00
{
2021-12-19 15:50:44 +01:00
texture ? . SignalModified ( ) ;
2020-11-02 20:53:23 +01:00
}
2022-06-17 18:09:14 +02:00
if ( state . Texture ! = hostTexture )
2021-12-19 15:50:44 +01:00
{
2022-06-17 22:41:38 +02:00
if ( UpdateScale ( texture , usageFlags , scaleIndex , stage ) )
2021-12-19 15:50:44 +01:00
{
hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
}
2019-10-18 04:41:18 +02:00
2022-06-17 18:09:14 +02:00
state . Texture = hostTexture ;
2022-06-17 22:41:38 +02:00
state . ScaleIndex = scaleIndex ;
state . UsageFlags = usageFlags ;
2020-10-21 00:03:20 +02:00
2021-12-19 15:50:44 +01:00
Format format = bindingInfo . Format ;
2020-10-21 00:03:20 +02:00
2021-12-19 15:50:44 +01:00
if ( format = = 0 & & texture ! = null )
{
format = texture . Format ;
}
_context . Renderer . Pipeline . SetImage ( bindingInfo . Binding , hostTexture , format ) ;
}
2022-06-17 18:09:14 +02:00
state . CachedTexture = texture ;
state . InvalidatedSequence = texture ? . InvalidatedSequence ? ? 0 ;
2019-10-18 04:41:18 +02:00
}
}
2022-06-17 18:09:14 +02:00
return specStateMatches ;
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Gets the texture descriptor for a given texture handle.
/// </summary>
2021-07-08 01:56:06 +02:00
/// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
/// <param name="bufferIndex">Index of the constant buffer with texture handles</param>
/// <param name="maximumId">Maximum ID of the texture pool</param>
2019-12-30 00:26:37 +01:00
/// <param name="stageIndex">The stage number where the texture is bound</param>
/// <param name="handle">The texture handle</param>
2021-05-19 20:05:43 +02:00
/// <param name="cbufSlot">The texture handle's constant buffer slot</param>
2019-12-30 00:26:37 +01:00
/// <returns>The texture descriptor for the specified texture</returns>
2021-07-08 01:56:06 +02:00
public TextureDescriptor GetTextureDescriptor (
ulong poolGpuVa ,
int bufferIndex ,
int maximumId ,
int stageIndex ,
int handle ,
int cbufSlot )
2019-12-16 05:59:46 +01:00
{
2021-10-17 22:28:18 +02:00
( int textureBufferIndex , int samplerBufferIndex ) = TextureHandle . UnpackSlots ( cbufSlot , bufferIndex ) ;
int packedId = ReadPackedId ( stageIndex , handle , textureBufferIndex , samplerBufferIndex ) ;
2022-06-17 18:09:14 +02:00
int textureId = TextureHandle . UnpackTextureId ( packedId ) ;
2019-12-16 05:59:46 +01:00
2021-07-08 01:56:06 +02:00
ulong poolAddress = _channel . MemoryManager . Translate ( poolGpuVa ) ;
2019-12-16 05:59:46 +01:00
2021-07-08 01:56:06 +02:00
TexturePool texturePool = _texturePoolCache . FindOrCreate ( _channel , poolAddress , maximumId ) ;
2019-12-16 05:59:46 +01:00
2022-06-24 02:41:57 +02:00
TextureDescriptor descriptor ;
if ( texturePool . IsValidId ( textureId ) )
{
descriptor = texturePool . GetDescriptor ( textureId ) ;
}
else
{
// If the ID is not valid, we just return a default descriptor with the most common state.
// Since this is used for shader specialization, doing so might avoid the need for recompilations.
descriptor = new TextureDescriptor ( ) ;
descriptor . Word4 | = ( uint ) TextureTarget . Texture2D < < 23 ;
descriptor . Word5 | = 1 u < < 31 ; // Coords normalized.
}
return descriptor ;
2019-12-16 05:59:46 +01:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Reads a packed texture and sampler ID (basically, the real texture handle)
/// from the texture constant buffer.
/// </summary>
/// <param name="stageIndex">The number of the shader stage where the texture is bound</param>
/// <param name="wordOffset">A word offset of the handle on the buffer (the "fake" shader handle)</param>
2020-04-25 15:02:18 +02:00
/// <param name="textureBufferIndex">Index of the constant buffer holding the texture handles</param>
2021-06-09 00:42:25 +02:00
/// <param name="samplerBufferIndex">Index of the constant buffer holding the sampler handles</param>
2019-12-30 00:26:37 +01:00
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
2022-06-17 18:09:14 +02:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2021-06-09 00:42:25 +02:00
private int ReadPackedId ( int stageIndex , int wordOffset , int textureBufferIndex , int samplerBufferIndex )
2019-10-18 04:41:18 +02:00
{
2021-10-17 22:28:18 +02:00
( int textureWordOffset , int samplerWordOffset , TextureHandleType handleType ) = TextureHandle . UnpackOffsets ( wordOffset ) ;
2021-06-09 00:42:25 +02:00
ulong textureBufferAddress = _isCompute
2021-06-24 01:51:41 +02:00
? _channel . BufferManager . GetComputeUniformBufferAddress ( textureBufferIndex )
: _channel . BufferManager . GetGraphicsUniformBufferAddress ( stageIndex , textureBufferIndex ) ;
2019-10-18 04:41:18 +02:00
2021-10-17 22:28:18 +02:00
int handle = _channel . MemoryManager . Physical . Read < int > ( textureBufferAddress + ( uint ) textureWordOffset * 4 ) ;
2020-05-27 16:07:10 +02:00
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
// bindless textures on the shader), we extend it with another value on the higher 16 bits with
// another offset for the sampler.
// The shader translator has code to detect separate texture and sampler uses with a bindless texture,
// turn that into a regular texture access and produce those special handles with values on the higher 16 bits.
2021-10-17 22:28:18 +02:00
if ( handleType ! = TextureHandleType . CombinedSampler )
2020-05-27 16:07:10 +02:00
{
2021-06-09 00:42:25 +02:00
ulong samplerBufferAddress = _isCompute
2021-06-24 01:51:41 +02:00
? _channel . BufferManager . GetComputeUniformBufferAddress ( samplerBufferIndex )
: _channel . BufferManager . GetGraphicsUniformBufferAddress ( stageIndex , samplerBufferIndex ) ;
2021-06-09 00:42:25 +02:00
2021-10-17 22:28:18 +02:00
int samplerHandle = _channel . MemoryManager . Physical . Read < int > ( samplerBufferAddress + ( uint ) samplerWordOffset * 4 ) ;
if ( handleType = = TextureHandleType . SeparateSamplerId )
{
samplerHandle < < = 20 ;
}
handle | = samplerHandle ;
2020-05-27 16:07:10 +02:00
}
2019-10-18 04:41:18 +02:00
2020-05-27 16:07:10 +02:00
return handle ;
2019-10-18 04:41:18 +02:00
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
/// </summary>
2019-10-18 04:41:18 +02:00
public void Rebind ( )
{
2022-06-17 18:09:14 +02:00
Array . Clear ( _textureState ) ;
Array . Clear ( _imageState ) ;
2019-10-18 04:41:18 +02:00
}
2021-06-09 01:00:28 +02:00
/// <summary>
/// Disposes all textures and samplers in the cache.
/// </summary>
public void Dispose ( )
{
_samplerPool ? . Dispose ( ) ;
}
2019-10-18 04:41:18 +02:00
}
}