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 ;
2019-10-18 04:41:18 +02:00
using Ryujinx.Graphics.Shader ;
2021-06-09 01:00:28 +02:00
using System ;
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 ;
2020-05-27 16:07:10 +02:00
private const int HandleHigh = 16 ;
private const int HandleMask = ( 1 < < HandleHigh ) - 1 ;
2021-06-09 00:42:25 +02:00
private const int SlotHigh = 16 ;
private const int SlotMask = ( 1 < < SlotHigh ) - 1 ;
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
private struct TextureStatePerStage
{
public ITexture Texture ;
public ISampler Sampler ;
}
2021-06-24 01:51:41 +02:00
private readonly TextureStatePerStage [ ] [ ] _textureState ;
private readonly TextureStatePerStage [ ] [ ] _imageState ;
2019-10-18 04:41:18 +02:00
2021-09-19 14:03:05 +02:00
private int [ ] _textureBindingsCount ;
private int [ ] _imageBindingsCount ;
2019-10-18 04:41:18 +02:00
private int _textureBufferIndex ;
private bool _rebind ;
2021-06-24 01:51:41 +02:00
private readonly float [ ] _scales ;
2020-11-02 20:53:23 +01:00
private bool _scaleChanged ;
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 ] [ ] ;
_textureState = new TextureStatePerStage [ stages ] [ ] ;
_imageState = new TextureStatePerStage [ stages ] [ ] ;
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 ] ;
_textureState [ stage ] = new TextureStatePerStage [ InitialTextureStateSize ] ;
_imageState [ stage ] = new TextureStatePerStage [ 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 ) ;
Array . Resize ( ref _textureState [ stage ] , count ) ;
}
int toClear = Math . Max ( _textureBindingsCount [ stage ] , count ) ;
TextureStatePerStage [ ] state = _textureState [ stage ] ;
for ( int i = 0 ; i < toClear ; i + + )
{
state [ i ] = new TextureStatePerStage ( ) ;
}
_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 ) ;
Array . Resize ( ref _imageState [ stage ] , count ) ;
}
int toClear = Math . Max ( _imageBindingsCount [ stage ] , count ) ;
TextureStatePerStage [ ] state = _imageState [ stage ] ;
for ( int i = 0 ; i < toClear ; i + + )
{
state [ i ] = new TextureStatePerStage ( ) ;
}
_imageBindingsCount [ stage ] = count ;
return _imageBindings [ stage ] ;
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
}
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>
/// <param name="binding">The related texture binding</param>
/// <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>
private bool UpdateScale ( Texture texture , TextureBindingInfo binding , int index , ShaderStage stage )
{
float result = 1f ;
bool changed = false ;
if ( ( binding . Flags & TextureUsageFlags . NeedsScaleValue ) ! = 0 & & texture ! = null )
{
switch ( stage )
{
case ShaderStage . Fragment :
if ( ( binding . Flags & TextureUsageFlags . ResScaleUnsupported ) ! = 0 )
{
changed | = texture . ScaleMode ! = TextureScaleMode . Blacklisted ;
texture . BlacklistScale ( ) ;
break ;
}
float scale = texture . ScaleFactor ;
if ( scale ! = 1 )
{
2021-06-24 01:51:41 +02:00
Texture activeTarget = _channel . TextureManager . GetAnyRenderTarget ( ) ;
2020-11-02 20:53:23 +01:00
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 ;
}
}
result = scale ;
break ;
case ShaderStage . Compute :
if ( ( binding . Flags & TextureUsageFlags . ResScaleUnsupported ) ! = 0 )
{
changed | = texture . ScaleMode ! = TextureScaleMode . Blacklisted ;
texture . BlacklistScale ( ) ;
}
result = texture . ScaleFactor ;
break ;
}
}
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>
/// <param name="stage">Current shader stage</param>
/// <param name="stageIndex">Shader stage index</param>
private void CommitRenderScale ( ShaderStage stage , int stageIndex )
{
if ( _scaleChanged )
{
2021-09-19 14:03:05 +02:00
_context . Renderer . Pipeline . UpdateRenderScale ( stage , _scales , _textureBindingsCount [ stageIndex ] , _imageBindingsCount [ stageIndex ] ) ;
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>
2019-10-18 04:41:18 +02:00
public void CommitBindings ( )
{
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
if ( _isCompute )
{
CommitTextureBindings ( texturePool , ShaderStage . Compute , 0 ) ;
CommitImageBindings ( texturePool , ShaderStage . Compute , 0 ) ;
2020-11-02 20:53:23 +01:00
CommitRenderScale ( ShaderStage . Compute , 0 ) ;
2019-10-18 04:41:18 +02:00
}
else
{
for ( ShaderStage stage = ShaderStage . Vertex ; stage < = ShaderStage . Fragment ; stage + + )
{
int stageIndex = ( int ) stage - 1 ;
CommitTextureBindings ( texturePool , stage , stageIndex ) ;
CommitImageBindings ( texturePool , stage , stageIndex ) ;
2020-11-02 20:53:23 +01:00
CommitRenderScale ( stage , stageIndex ) ;
2019-10-18 04:41:18 +02:00
}
}
_rebind = false ;
}
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>
/// <param name="stageIndex">The stage number of the specified shader stage</param>
2019-10-18 04:41:18 +02:00
private void CommitTextureBindings ( TexturePool pool , ShaderStage stage , int stageIndex )
{
2021-09-19 14:03:05 +02:00
int textureCount = _textureBindingsCount [ stageIndex ] ;
if ( textureCount = = 0 )
2021-07-14 19:27:22 +02:00
{
return ;
}
var samplerPool = _samplerPool ;
if ( pool = = null )
{
Logger . Error ? . Print ( LogClass . Gpu , $"Shader stage \" { stage } \ " uses textures, but texture pool was not set." ) ;
return ;
}
if ( samplerPool = = null )
2019-10-18 04:41:18 +02:00
{
2021-07-14 19:27:22 +02:00
Logger . Error ? . Print ( LogClass . Gpu , $"Shader stage \" { stage } \ " uses textures, but sampler pool was not set." ) ;
2019-10-18 04:41:18 +02:00
return ;
}
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 ] ;
2019-10-18 04:41:18 +02:00
2021-06-09 00:42:25 +02:00
int textureBufferIndex ;
int samplerBufferIndex ;
2019-10-18 04:41:18 +02:00
2021-06-09 00:42:25 +02:00
if ( bindingInfo . CbufSlot < 0 )
{
textureBufferIndex = _textureBufferIndex ;
samplerBufferIndex = textureBufferIndex ;
}
else
{
textureBufferIndex = bindingInfo . CbufSlot & SlotMask ;
samplerBufferIndex = ( ( bindingInfo . CbufSlot > > SlotHigh ) ! = 0 ) ? ( bindingInfo . CbufSlot > > SlotHigh ) - 1 : textureBufferIndex ;
}
int packedId = ReadPackedId ( stageIndex , bindingInfo . Handle , textureBufferIndex , samplerBufferIndex ) ;
2019-10-18 04:41:18 +02:00
int textureId = UnpackTextureId ( packedId ) ;
2019-12-05 21:34:47 +01:00
int samplerId ;
if ( _samplerIndex = = SamplerIndex . ViaHeaderIndex )
{
samplerId = textureId ;
}
else
{
samplerId = UnpackSamplerId ( packedId ) ;
}
2019-10-18 04:41:18 +02:00
Texture texture = pool . Get ( textureId ) ;
2020-11-08 12:10:00 +01:00
ITexture hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2019-10-18 04:41:18 +02:00
if ( _textureState [ stageIndex ] [ index ] . Texture ! = hostTexture | | _rebind )
{
2020-11-08 12:10:00 +01:00
if ( UpdateScale ( texture , bindingInfo , index , stage ) )
2020-11-02 20:53:23 +01:00
{
2020-11-08 12:10:00 +01:00
hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2020-11-02 20:53:23 +01:00
}
2019-10-18 04:41:18 +02:00
_textureState [ stageIndex ] [ index ] . Texture = hostTexture ;
2020-11-08 12:10:00 +01:00
_context . Renderer . Pipeline . SetTexture ( bindingInfo . Binding , hostTexture ) ;
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-07-14 19:27:22 +02:00
Sampler sampler = samplerPool . Get ( samplerId ) ;
2019-10-18 04:41:18 +02:00
ISampler hostSampler = sampler ? . HostSampler ;
if ( _textureState [ stageIndex ] [ index ] . Sampler ! = hostSampler | | _rebind )
{
_textureState [ stageIndex ] [ index ] . Sampler = hostSampler ;
2020-11-08 12:10:00 +01:00
_context . Renderer . Pipeline . SetSampler ( bindingInfo . Binding , hostSampler ) ;
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>
2019-10-18 04:41:18 +02:00
private void CommitImageBindings ( TexturePool pool , ShaderStage stage , int stageIndex )
{
2021-09-19 14:03:05 +02:00
int imageCount = _imageBindingsCount [ stageIndex ] ;
if ( imageCount = = 0 )
2019-10-18 04:41:18 +02:00
{
return ;
}
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." ) ;
return ;
}
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
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 ] ;
2019-10-18 04:41:18 +02:00
2021-06-09 00:42:25 +02:00
int textureBufferIndex ;
int samplerBufferIndex ;
if ( bindingInfo . CbufSlot < 0 )
{
textureBufferIndex = _textureBufferIndex ;
samplerBufferIndex = textureBufferIndex ;
}
else
{
textureBufferIndex = bindingInfo . CbufSlot & SlotMask ;
samplerBufferIndex = ( ( bindingInfo . CbufSlot > > SlotHigh ) ! = 0 ) ? ( bindingInfo . CbufSlot > > SlotHigh ) - 1 : textureBufferIndex ;
}
2020-11-09 23:35:04 +01:00
2021-06-09 00:42:25 +02:00
int packedId = ReadPackedId ( stageIndex , bindingInfo . Handle , textureBufferIndex , samplerBufferIndex ) ;
2019-10-18 04:41:18 +02:00
int textureId = UnpackTextureId ( packedId ) ;
Texture texture = pool . Get ( textureId ) ;
2020-11-08 12:10:00 +01:00
ITexture hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2019-10-18 04:41:18 +02:00
2021-03-08 22:43:39 +01:00
bool isStore = bindingInfo . Flags . HasFlag ( TextureUsageFlags . ImageStore ) ;
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-03-08 22:43:39 +01:00
else if ( isStore )
{
texture ? . SignalModified ( ) ;
2020-10-20 23:56:23 +02:00
}
2019-10-18 04:41:18 +02:00
if ( _imageState [ stageIndex ] [ index ] . Texture ! = hostTexture | | _rebind )
{
2020-11-08 12:10:00 +01:00
if ( UpdateScale ( texture , bindingInfo , baseScaleIndex + index , stage ) )
2020-11-02 20:53:23 +01:00
{
2020-11-08 12:10:00 +01:00
hostTexture = texture ? . GetTargetTexture ( bindingInfo . Target ) ;
2020-11-02 20:53:23 +01:00
}
2019-10-18 04:41:18 +02:00
_imageState [ stageIndex ] [ index ] . Texture = hostTexture ;
2020-11-08 12:10:00 +01:00
Format format = bindingInfo . Format ;
2020-10-21 00:03:20 +02:00
2020-10-21 14:06:13 +02:00
if ( format = = 0 & & texture ! = null )
2020-10-21 00:03:20 +02:00
{
format = texture . Format ;
}
2020-11-08 12:10:00 +01:00
_context . Renderer . Pipeline . SetImage ( bindingInfo . Binding , hostTexture , format ) ;
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-07-08 01:56:06 +02:00
int textureBufferIndex = cbufSlot < 0 ? bufferIndex : cbufSlot & SlotMask ;
2021-06-09 00:42:25 +02:00
int packedId = ReadPackedId ( stageIndex , handle , textureBufferIndex , textureBufferIndex ) ;
2019-12-16 05:59:46 +01:00
int textureId = UnpackTextureId ( packedId ) ;
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
return texturePool . GetDescriptor ( textureId ) ;
}
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>
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-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-06-29 19:32:02 +02:00
int handle = _channel . MemoryManager . Physical . Read < int > ( textureBufferAddress + ( ulong ) ( wordOffset & HandleMask ) * 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.
if ( wordOffset > > HandleHigh ! = 0 )
{
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-06-29 19:32:02 +02:00
handle | = _channel . MemoryManager . Physical . Read < int > ( samplerBufferAddress + ( ulong ) ( ( wordOffset > > HandleHigh ) - 1 ) * 4 ) ;
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>
/// Unpacks the texture ID from the real texture handle.
/// </summary>
/// <param name="packedId">The real texture handle</param>
/// <returns>The texture ID</returns>
2019-10-18 04:41:18 +02:00
private static int UnpackTextureId ( int packedId )
{
return ( packedId > > 0 ) & 0xfffff ;
}
2019-12-30 00:26:37 +01:00
/// <summary>
/// Unpacks the sampler ID from the real texture handle.
/// </summary>
/// <param name="packedId">The real texture handle</param>
/// <returns>The sampler ID</returns>
2019-10-18 04:41:18 +02:00
private static int UnpackSamplerId ( int packedId )
{
return ( packedId > > 20 ) & 0xfff ;
}
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 ( )
{
_rebind = true ;
}
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
}
}