aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs498
1 files changed, 498 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
new file mode 100644
index 00000000..266f6285
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -0,0 +1,498 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Shader;
+using System;
+
+namespace Ryujinx.Graphics.Gpu.Image
+{
+ /// <summary>
+ /// Texture manager.
+ /// </summary>
+ class TextureManager : IDisposable
+ {
+ private readonly GpuContext _context;
+ private readonly GpuChannel _channel;
+
+ private readonly TextureBindingsManager _cpBindingsManager;
+ private readonly TextureBindingsManager _gpBindingsManager;
+ private readonly TexturePoolCache _texturePoolCache;
+ private readonly SamplerPoolCache _samplerPoolCache;
+
+ private readonly Texture[] _rtColors;
+ private readonly ITexture[] _rtHostColors;
+ private Texture _rtDepthStencil;
+ private ITexture _rtHostDs;
+
+ public int ClipRegionWidth { get; private set; }
+ public int ClipRegionHeight { get; private set; }
+
+ /// <summary>
+ /// The scaling factor applied to all currently bound render targets.
+ /// </summary>
+ public float RenderTargetScale { get; private set; } = 1f;
+
+ /// <summary>
+ /// Creates a new instance of the texture manager.
+ /// </summary>
+ /// <param name="context">GPU context that the texture manager belongs to</param>
+ /// <param name="channel">GPU channel that the texture manager belongs to</param>
+ public TextureManager(GpuContext context, GpuChannel channel)
+ {
+ _context = context;
+ _channel = channel;
+
+ TexturePoolCache texturePoolCache = new TexturePoolCache(context);
+ SamplerPoolCache samplerPoolCache = new SamplerPoolCache(context);
+
+ float[] scales = new float[64];
+ new Span<float>(scales).Fill(1f);
+
+ _cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
+ _gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
+ _texturePoolCache = texturePoolCache;
+ _samplerPoolCache = samplerPoolCache;
+
+ _rtColors = new Texture[Constants.TotalRenderTargets];
+ _rtHostColors = new ITexture[Constants.TotalRenderTargets];
+ }
+
+ /// <summary>
+ /// Sets the texture and image bindings for the compute pipeline.
+ /// </summary>
+ /// <param name="bindings">Bindings for the active shader</param>
+ public void SetComputeBindings(CachedShaderBindings bindings)
+ {
+ _cpBindingsManager.SetBindings(bindings);
+ }
+
+ /// <summary>
+ /// Sets the texture and image bindings for the graphics pipeline.
+ /// </summary>
+ /// <param name="bindings">Bindings for the active shader</param>
+ public void SetGraphicsBindings(CachedShaderBindings bindings)
+ {
+ _gpBindingsManager.SetBindings(bindings);
+ }
+
+ /// <summary>
+ /// Sets the texture constant buffer index on the compute pipeline.
+ /// </summary>
+ /// <param name="index">The texture constant buffer index</param>
+ public void SetComputeTextureBufferIndex(int index)
+ {
+ _cpBindingsManager.SetTextureBufferIndex(index);
+ }
+
+ /// <summary>
+ /// Sets the texture constant buffer index on the graphics pipeline.
+ /// </summary>
+ /// <param name="index">The texture constant buffer index</param>
+ public void SetGraphicsTextureBufferIndex(int index)
+ {
+ _gpBindingsManager.SetTextureBufferIndex(index);
+ }
+
+ /// <summary>
+ /// Sets the current sampler pool on the compute pipeline.
+ /// </summary>
+ /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
+ /// <param name="maximumId">The maximum ID of the sampler pool</param>
+ /// <param name="samplerIndex">The indexing type of the sampler pool</param>
+ public void SetComputeSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
+ {
+ _cpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
+ }
+
+ /// <summary>
+ /// Sets the current sampler pool on the graphics pipeline.
+ /// </summary>
+ /// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
+ /// <param name="maximumId">The maximum ID of the sampler pool</param>
+ /// <param name="samplerIndex">The indexing type of the sampler pool</param>
+ public void SetGraphicsSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
+ {
+ _gpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
+ }
+
+ /// <summary>
+ /// Sets the current texture pool on the compute pipeline.
+ /// </summary>
+ /// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
+ /// <param name="maximumId">The maximum ID of the texture pool</param>
+ public void SetComputeTexturePool(ulong gpuVa, int maximumId)
+ {
+ _cpBindingsManager.SetTexturePool(gpuVa, maximumId);
+ }
+
+ /// <summary>
+ /// Sets the current texture pool on the graphics pipeline.
+ /// </summary>
+ /// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
+ /// <param name="maximumId">The maximum ID of the texture pool</param>
+ public void SetGraphicsTexturePool(ulong gpuVa, int maximumId)
+ {
+ _gpBindingsManager.SetTexturePool(gpuVa, maximumId);
+ }
+
+ /// <summary>
+ /// Check if a texture's scale must be updated to match the configured resolution scale.
+ /// </summary>
+ /// <param name="texture">The texture to check</param>
+ /// <returns>True if the scale needs updating, false if the scale is up to date</returns>
+ private bool ScaleNeedsUpdated(Texture texture)
+ {
+ return texture != null && !(texture.ScaleMode == TextureScaleMode.Blacklisted || texture.ScaleMode == TextureScaleMode.Undesired) && texture.ScaleFactor != GraphicsConfig.ResScale;
+ }
+
+ /// <summary>
+ /// Sets the render target color buffer.
+ /// </summary>
+ /// <param name="index">The index of the color buffer to set (up to 8)</param>
+ /// <param name="color">The color buffer texture</param>
+ /// <returns>True if render target scale must be updated.</returns>
+ public bool SetRenderTargetColor(int index, Texture color)
+ {
+ bool hasValue = color != null;
+ bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
+
+ if (_rtColors[index] != color)
+ {
+ _rtColors[index]?.SignalModifying(false);
+
+ if (color != null)
+ {
+ color.SynchronizeMemory();
+ color.SignalModifying(true);
+ }
+
+ _rtColors[index] = color;
+ }
+
+ return changesScale || ScaleNeedsUpdated(color);
+ }
+
+ /// <summary>
+ /// Sets the render target depth-stencil buffer.
+ /// </summary>
+ /// <param name="depthStencil">The depth-stencil buffer texture</param>
+ /// <returns>True if render target scale must be updated.</returns>
+ public bool SetRenderTargetDepthStencil(Texture depthStencil)
+ {
+ bool hasValue = depthStencil != null;
+ bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
+
+ if (_rtDepthStencil != depthStencil)
+ {
+ _rtDepthStencil?.SignalModifying(false);
+
+ if (depthStencil != null)
+ {
+ depthStencil.SynchronizeMemory();
+ depthStencil.SignalModifying(true);
+ }
+
+ _rtDepthStencil = depthStencil;
+ }
+
+ return changesScale || ScaleNeedsUpdated(depthStencil);
+ }
+
+ /// <summary>
+ /// Sets the host clip region, which should be the intersection of all render target texture sizes.
+ /// </summary>
+ /// <param name="width">Width of the clip region, defined as the minimum width across all bound textures</param>
+ /// <param name="height">Height of the clip region, defined as the minimum height across all bound textures</param>
+ public void SetClipRegion(int width, int height)
+ {
+ ClipRegionWidth = width;
+ ClipRegionHeight = height;
+ }
+
+ /// <summary>
+ /// Gets the first available bound colour target, or the depth stencil target if not present.
+ /// </summary>
+ /// <returns>The first bound colour target, otherwise the depth stencil target</returns>
+ public Texture GetAnyRenderTarget()
+ {
+ return _rtColors[0] ?? _rtDepthStencil;
+ }
+
+ /// <summary>
+ /// Updates the Render Target scale, given the currently bound render targets.
+ /// This will update scale to match the configured scale, scale textures that are eligible but not scaled,
+ /// and propagate blacklisted status from one texture to the ones bound with it.
+ /// </summary>
+ /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
+ public void UpdateRenderTargetScale(int singleUse)
+ {
+ // Make sure all scales for render targets are at the highest they should be. Blacklisted targets should propagate their scale to the other targets.
+ bool mismatch = false;
+ bool blacklisted = false;
+ bool hasUpscaled = false;
+ bool hasUndesired = false;
+ float targetScale = GraphicsConfig.ResScale;
+
+ void ConsiderTarget(Texture target)
+ {
+ if (target == null) return;
+ float scale = target.ScaleFactor;
+
+ switch (target.ScaleMode)
+ {
+ case TextureScaleMode.Blacklisted:
+ mismatch |= scale != 1f;
+ blacklisted = true;
+ break;
+ case TextureScaleMode.Eligible:
+ mismatch = true; // We must make a decision.
+ break;
+ case TextureScaleMode.Undesired:
+ hasUndesired = true;
+ mismatch |= scale != 1f || hasUpscaled; // If another target is upscaled, scale this one up too.
+ break;
+ case TextureScaleMode.Scaled:
+ hasUpscaled = true;
+ mismatch |= hasUndesired || scale != targetScale; // If the target scale has changed, reset the scale for all targets.
+ break;
+ }
+ }
+
+ if (singleUse != -1)
+ {
+ // If only one target is in use (by a clear, for example) the others do not need to be checked for mismatching scale.
+ ConsiderTarget(_rtColors[singleUse]);
+ }
+ else
+ {
+ foreach (Texture color in _rtColors)
+ {
+ ConsiderTarget(color);
+ }
+ }
+
+ ConsiderTarget(_rtDepthStencil);
+
+ mismatch |= blacklisted && hasUpscaled;
+
+ if (blacklisted || (hasUndesired && !hasUpscaled))
+ {
+ targetScale = 1f;
+ }
+
+ if (mismatch)
+ {
+ if (blacklisted)
+ {
+ // Propagate the blacklisted state to the other textures.
+ foreach (Texture color in _rtColors)
+ {
+ color?.BlacklistScale();
+ }
+
+ _rtDepthStencil?.BlacklistScale();
+ }
+ else
+ {
+ // Set the scale of the other textures.
+ foreach (Texture color in _rtColors)
+ {
+ color?.SetScale(targetScale);
+ }
+
+ _rtDepthStencil?.SetScale(targetScale);
+ }
+ }
+
+ RenderTargetScale = targetScale;
+ }
+
+ /// <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) GetGraphicsTextureAndSampler(int textureId, int samplerId)
+ {
+ return _gpBindingsManager.GetTextureAndSampler(textureId, samplerId);
+ }
+
+ /// <summary>
+ /// Commits bindings on the compute pipeline.
+ /// </summary>
+ /// <param name="specState">Specialization state for the bound shader</param>
+ /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
+ public bool CommitComputeBindings(ShaderSpecializationState specState)
+ {
+ // Every time we switch between graphics and compute work,
+ // we must rebind everything.
+ // Since compute work happens less often, we always do that
+ // before and after the compute dispatch.
+
+ _texturePoolCache.Tick();
+ _samplerPoolCache.Tick();
+
+ _cpBindingsManager.Rebind();
+ bool result = _cpBindingsManager.CommitBindings(specState);
+ _gpBindingsManager.Rebind();
+
+ return result;
+ }
+
+ /// <summary>
+ /// Commits bindings on the graphics pipeline.
+ /// </summary>
+ /// <param name="specState">Specialization state for the bound shader</param>
+ /// <returns>True if all bound textures match the current shader specialization state, false otherwise</returns>
+ public bool CommitGraphicsBindings(ShaderSpecializationState specState)
+ {
+ _texturePoolCache.Tick();
+ _samplerPoolCache.Tick();
+
+ bool result = _gpBindingsManager.CommitBindings(specState);
+
+ UpdateRenderTargets();
+
+ return result;
+ }
+
+ /// <summary>
+ /// Returns a texture pool from the cache, with the given address and maximum id.
+ /// </summary>
+ /// <param name="poolGpuVa">GPU virtual address of the texture pool</param>
+ /// <param name="maximumId">Maximum ID of the texture pool</param>
+ /// <returns>The texture pool</returns>
+ public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
+ {
+ ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
+
+ TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
+
+ return texturePool;
+ }
+
+ /// <summary>
+ /// Gets a texture descriptor used on the compute pipeline.
+ /// </summary>
+ /// <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>
+ /// <param name="handle">Shader "fake" handle of the texture</param>
+ /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
+ /// <returns>The texture descriptor</returns>
+ public TextureDescriptor GetComputeTextureDescriptor(ulong poolGpuVa, int bufferIndex, int maximumId, int handle, int cbufSlot)
+ {
+ return _cpBindingsManager.GetTextureDescriptor(poolGpuVa, bufferIndex, maximumId, 0, handle, cbufSlot);
+ }
+
+ /// <summary>
+ /// Gets a texture descriptor used on the graphics pipeline.
+ /// </summary>
+ /// <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>
+ /// <param name="stageIndex">Index of the shader stage where the texture is bound</param>
+ /// <param name="handle">Shader "fake" handle of the texture</param>
+ /// <param name="cbufSlot">Shader constant buffer slot of the texture</param>
+ /// <returns>The texture descriptor</returns>
+ public TextureDescriptor GetGraphicsTextureDescriptor(
+ ulong poolGpuVa,
+ int bufferIndex,
+ int maximumId,
+ int stageIndex,
+ int handle,
+ int cbufSlot)
+ {
+ return _gpBindingsManager.GetTextureDescriptor(poolGpuVa, bufferIndex, maximumId, stageIndex, handle, cbufSlot);
+ }
+
+ /// <summary>
+ /// Update host framebuffer attachments based on currently bound render target buffers.
+ /// </summary>
+ public void UpdateRenderTargets()
+ {
+ bool anyChanged = false;
+
+ if (_rtHostDs != _rtDepthStencil?.HostTexture)
+ {
+ _rtHostDs = _rtDepthStencil?.HostTexture;
+
+ anyChanged = true;
+ }
+
+ for (int index = 0; index < _rtColors.Length; index++)
+ {
+ ITexture hostTexture = _rtColors[index]?.HostTexture;
+
+ if (_rtHostColors[index] != hostTexture)
+ {
+ _rtHostColors[index] = hostTexture;
+
+ anyChanged = true;
+ }
+ }
+
+ if (anyChanged)
+ {
+ _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
+ }
+ }
+
+ /// <summary>
+ /// Update host framebuffer attachments based on currently bound render target buffers.
+ /// </summary>
+ /// <remarks>
+ /// All color attachments will be unbound.
+ /// </remarks>
+ public void UpdateRenderTargetDepthStencil()
+ {
+ new Span<ITexture>(_rtHostColors).Fill(null);
+ _rtHostDs = _rtDepthStencil?.HostTexture;
+
+ _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
+ }
+
+ /// <summary>
+ /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
+ /// </summary>
+ public void ReloadPools()
+ {
+ _cpBindingsManager.ReloadPools();
+ _gpBindingsManager.ReloadPools();
+ }
+
+ /// <summary>
+ /// Forces all textures, samplers, images and render targets to be rebound the next time
+ /// CommitGraphicsBindings is called.
+ /// </summary>
+ public void Rebind()
+ {
+ _gpBindingsManager.Rebind();
+
+ for (int index = 0; index < _rtHostColors.Length; index++)
+ {
+ _rtHostColors[index] = null;
+ }
+
+ _rtHostDs = null;
+ }
+
+ /// <summary>
+ /// Disposes the texture manager.
+ /// It's an error to use the texture manager after disposal.
+ /// </summary>
+ public void Dispose()
+ {
+ // Textures are owned by the texture cache, so we shouldn't dispose the texture pool cache.
+ _samplerPoolCache.Dispose();
+
+ for (int i = 0; i < _rtColors.Length; i++)
+ {
+ _rtColors[i]?.DecrementReferenceCount();
+ _rtColors[i] = null;
+ }
+
+ _rtDepthStencil?.DecrementReferenceCount();
+ _rtDepthStencil = null;
+ }
+ }
+}