diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
index 0f249512b0..61f227d936 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -725,10 +725,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return;
}
+ bool clearDepth = (argument & 1) != 0;
+ bool clearStencil = (argument & 2) != 0;
+ uint componentMask = (uint)((argument >> 2) & 0xf);
int index = (argument >> 6) & 0xf;
int layer = (argument >> 10) & 0x3ff;
- engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, singleUse: index);
+ RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
+
+ if (layer != 0 || layerCount > 1)
+ {
+ updateFlags |= RenderTargetUpdateFlags.Layered;
+ }
+
+ if (clearDepth || clearStencil)
+ {
+ updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil;
+ }
+
+ engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1);
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
// on the screen scissor state, then we need to force only one texture to be bound to avoid
@@ -788,18 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.SetScissors(scissors);
}
- if (clipMismatch)
- {
- _channel.TextureManager.UpdateRenderTarget(index);
- }
- else
- {
- _channel.TextureManager.UpdateRenderTargets();
- }
-
- bool clearDepth = (argument & 1) != 0;
- bool clearStencil = (argument & 2) != 0;
- uint componentMask = (uint)((argument >> 2) & 0xf);
+ _channel.TextureManager.UpdateRenderTargets();
if (componentMask != 0)
{
@@ -841,7 +845,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
engine.UpdateScissorState();
}
- engine.UpdateRenderTargetState(useControl: true);
+ engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
if (renderEnable == ConditionalRenderEnabled.Host)
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs
new file mode 100644
index 0000000000..cf2e818ceb
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/RenderTargetUpdateFlags.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ ///
+ /// Flags indicating how the render targets should be updated.
+ ///
+ [Flags]
+ enum RenderTargetUpdateFlags
+ {
+ ///
+ /// No flags.
+ ///
+ None = 0,
+
+ ///
+ /// Get render target index from the control register.
+ ///
+ UseControl = 1 << 0,
+
+ ///
+ /// Indicates that all render targets are 2D array textures.
+ ///
+ Layered = 1 << 1,
+
+ ///
+ /// Indicates that only a single color target will be used.
+ ///
+ SingleColor = 1 << 2,
+
+ ///
+ /// Indicates that the depth-stencil target will be used.
+ ///
+ UpdateDepthStencil = 1 << 3,
+
+ ///
+ /// Default update flags for draw.
+ ///
+ UpdateAll = UseControl | UpdateDepthStencil
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index 9b59009cfb..9b58e0148a 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -402,20 +402,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
private void UpdateRenderTargetState()
{
- UpdateRenderTargetState(true);
+ UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
}
///
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
///
- /// Use draw buffers information from render target control register
- /// Indicates if the texture is layered
+ /// Flags indicating which render targets should be updated and how
/// If this is not -1, it indicates that only the given indexed target will be used.
- public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
+ public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
{
var memoryManager = _channel.MemoryManager;
var rtControl = _state.State.RtControl;
+ bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl);
+ bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered);
+ bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor);
+
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
var msaaMode = _state.State.RtMsaaMode;
@@ -438,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var colorState = _state.State.RtColorState[rtIndex];
- if (index >= count || !IsRtEnabled(colorState))
+ if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
{
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
@@ -478,7 +481,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Image.Texture depthStencil = null;
- if (dsEnable)
+ if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil))
{
var dsState = _state.State.RtDepthStencilState;
var dsSize = _state.State.RtDepthStencilSize;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
index 19eb8b46ee..9a447a0bd9 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -139,12 +139,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
///
- /// Use draw buffers information from render target control register
- /// Indicates if the texture is layered
+ /// Flags indicating which render targets should be updated and how
/// If this is not -1, it indicates that only the given indexed target will be used.
- public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
+ public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
{
- _stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
+ _stateUpdater.UpdateRenderTargetState(updateFlags, singleUse);
}
///
diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs
index 5ed9b2a074..352a828d16 100644
--- a/Ryujinx.Graphics.Gpu/Image/Texture.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs
@@ -1,4 +1,3 @@
-using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
@@ -89,12 +88,6 @@ namespace Ryujinx.Graphics.Gpu.Image
///
public TextureGroup Group { get; private set; }
- ///
- /// Set when a texture has been changed size. This indicates that it may need to be
- /// changed again when obtained as a sampler.
- ///
- public bool ChangedSize { get; private set; }
-
///
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
/// This indicates that the range must be fully checked when matching the texture.
@@ -410,122 +403,6 @@ namespace Ryujinx.Graphics.Gpu.Image
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
}
- ///
- /// Changes the texture size.
- ///
- ///
- /// This operation may also change the size of all mipmap levels, including from the parent
- /// and other possible child textures, to ensure that all sizes are consistent.
- ///
- /// The new texture width
- /// The new texture height
- /// The new texture depth (for 3D textures) or layers (for layered textures)
- public void ChangeSize(int width, int height, int depthOrLayers)
- {
- int blockWidth = Info.FormatInfo.BlockWidth;
- int blockHeight = Info.FormatInfo.BlockHeight;
-
- width <<= FirstLevel;
- height <<= FirstLevel;
-
- if (Target == Target.Texture3D)
- {
- depthOrLayers <<= FirstLevel;
- }
- else
- {
- depthOrLayers = _viewStorage.Info.DepthOrLayers;
- }
-
- _viewStorage.RecreateStorageOrView(width, height, blockWidth, blockHeight, depthOrLayers);
-
- foreach (Texture view in _viewStorage._views)
- {
- int viewWidth = Math.Max(1, width >> view.FirstLevel);
- int viewHeight = Math.Max(1, height >> view.FirstLevel);
-
- int viewDepthOrLayers;
-
- if (view.Info.Target == Target.Texture3D)
- {
- viewDepthOrLayers = Math.Max(1, depthOrLayers >> view.FirstLevel);
- }
- else
- {
- viewDepthOrLayers = view.Info.DepthOrLayers;
- }
-
- view.RecreateStorageOrView(viewWidth, viewHeight, blockWidth, blockHeight, viewDepthOrLayers);
- }
- }
-
- ///
- /// Recreates the texture storage (or view, in the case of child textures) of this texture.
- /// This allows recreating the texture with a new size.
- /// A copy is automatically performed from the old to the new texture.
- ///
- /// The new texture width
- /// The new texture height
- /// The block width related to the given width
- /// The block height related to the given height
- /// The new texture depth (for 3D textures) or layers (for layered textures)
- private void RecreateStorageOrView(int width, int height, int blockWidth, int blockHeight, int depthOrLayers)
- {
- RecreateStorageOrView(
- BitUtils.DivRoundUp(width * Info.FormatInfo.BlockWidth, blockWidth),
- BitUtils.DivRoundUp(height * Info.FormatInfo.BlockHeight, blockHeight),
- depthOrLayers);
- }
-
- ///
- /// Recreates the texture storage (or view, in the case of child textures) of this texture.
- /// This allows recreating the texture with a new size.
- /// A copy is automatically performed from the old to the new texture.
- ///
- /// The new texture width
- /// The new texture height
- /// The new texture depth (for 3D textures) or layers (for layered textures)
- private void RecreateStorageOrView(int width, int height, int depthOrLayers)
- {
- ChangedSize = true;
-
- SetInfo(new TextureInfo(
- Info.GpuAddress,
- width,
- height,
- depthOrLayers,
- Info.Levels,
- Info.SamplesInX,
- Info.SamplesInY,
- Info.Stride,
- Info.IsLinear,
- Info.GobBlocksInY,
- Info.GobBlocksInZ,
- Info.GobBlocksInTileX,
- Info.Target,
- Info.FormatInfo,
- Info.DepthStencilMode,
- Info.SwizzleR,
- Info.SwizzleG,
- Info.SwizzleB,
- Info.SwizzleA));
-
- TextureCreateInfo createInfo = TextureCache.GetCreateInfo(Info, _context.Capabilities, ScaleFactor);
-
- if (_viewStorage != this)
- {
- ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, FirstLayer, FirstLevel));
- }
- else
- {
- ITexture newStorage = _context.Renderer.CreateTexture(createInfo, ScaleFactor);
-
- HostTexture.CopyTo(newStorage, 0, 0);
-
- ReplaceStorage(newStorage);
- }
- }
-
///
/// Registers when a texture has had its data set after being scaled, and
/// determines if it should be blacklisted from scaling to improve performance.
@@ -1215,7 +1092,9 @@ namespace Ryujinx.Graphics.Gpu.Image
/// A value indicating how well this texture matches the given info
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
{
- TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, (flags & TextureSearchFlags.ForSampler) != 0, (flags & TextureSearchFlags.ForCopy) != 0);
+ bool forSampler = (flags & TextureSearchFlags.ForSampler) != 0;
+
+ TextureMatchQuality matchQuality = TextureCompatibility.FormatMatches(Info, info, forSampler, (flags & TextureSearchFlags.ForCopy) != 0);
if (matchQuality == TextureMatchQuality.NoMatch)
{
@@ -1227,12 +1106,12 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureMatchQuality.NoMatch;
}
- if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel))
+ if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
{
return TextureMatchQuality.NoMatch;
}
- if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
+ if ((flags & TextureSearchFlags.ForSampler) != 0)
{
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
{
@@ -1262,12 +1141,20 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Texture view information
/// Texture view physical memory ranges
+ /// Indicates if the texture sizes must be exactly equal, or width is allowed to differ
/// Layer size on the given texture
/// Host GPU capabilities
/// Texture view initial layer on this texture
/// Texture view first mipmap level on this texture
/// The level of compatiblilty a view with the given parameters created from this texture has
- public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
+ public TextureViewCompatibility IsViewCompatible(
+ TextureInfo info,
+ MultiRange range,
+ bool exactSize,
+ int layerSize,
+ Capabilities caps,
+ out int firstLayer,
+ out int firstLevel)
{
TextureViewCompatibility result = TextureViewCompatibility.Full;
@@ -1317,7 +1204,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureViewCompatibility.LayoutIncompatible;
}
- result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, firstLevel));
+ result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSizeMatches(Info, info, exactSize, firstLevel));
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
return result;
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 9802a3dcc1..1d5b1851f5 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -210,8 +210,8 @@ namespace Ryujinx.Graphics.Gpu.Image
ulong offset,
FormatInfo formatInfo,
bool shouldCreate,
- bool preferScaling = true,
- Size? sizeHint = null)
+ bool preferScaling,
+ Size sizeHint)
{
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureInfo info = new TextureInfo(
copyTexture.Address.Pack() + offset,
- width,
+ GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
copyTexture.Height,
copyTexture.Depth,
1,
@@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image
flags |= TextureSearchFlags.NoCreate;
}
- Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
+ Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0);
texture?.SynchronizeMemory();
@@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureInfo info = new TextureInfo(
colorState.Address.Pack(),
- width,
+ GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
colorState.Height,
colorState.Depth,
1,
@@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
- Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
+ Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize);
texture?.SynchronizeMemory();
@@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureInfo info = new TextureInfo(
dsState.Address.Pack(),
- size.Width,
+ GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
size.Height,
size.Depth,
1,
@@ -409,13 +409,41 @@ namespace Ryujinx.Graphics.Gpu.Image
target,
formatInfo);
- Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
+ Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4);
texture?.SynchronizeMemory();
return texture;
}
+ ///
+ /// For block linear textures, gets the minimum width of the texture
+ /// that would still have the same number of GOBs per row as the original width.
+ ///
+ /// The possibly aligned texture width
+ /// The minimum width that the texture may have without losing data
+ /// Bytes per pixel of the texture format
+ /// True if the texture is linear, false for block linear
+ /// The minimum width of the texture with the same amount of GOBs per row
+ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPerPixel, bool isLinear)
+ {
+ if (isLinear || (uint)minimumWidth >= (uint)width)
+ {
+ return width;
+ }
+
+ // Calculate the minimum possible that would not cause data loss
+ // and would be still within the same GOB (aligned size would be the same).
+ // This is useful for render and copy operations, where we don't know the
+ // exact width of the texture, but it doesn't matter, as long the texture is
+ // at least as large as the region being rendered or copied.
+
+ int alignment = 64 / bytesPerPixel;
+ int widthAligned = BitUtils.AlignUp(width, alignment);
+
+ return Math.Clamp(widthAligned - alignment + 1, minimumWidth, widthAligned);
+ }
+
///
/// Tries to find an existing texture, or create a new one if not found.
///
@@ -423,7 +451,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The texture search flags, defines texture comparison rules
/// Texture information of the texture to be found or created
/// Size in bytes of a single texture layer
- /// A hint indicating the minimum used size for the texture
/// Optional ranges of physical memory where the texture data is located
/// The texture
public Texture FindOrCreateTexture(
@@ -431,7 +458,6 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureSearchFlags flags,
TextureInfo info,
int layerSize = 0,
- Size? sizeHint = null,
MultiRange? range = null)
{
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
@@ -512,8 +538,6 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture != null)
{
- ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
-
texture.SynchronizeMemory();
return texture;
@@ -568,6 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
info,
range.Value,
+ isSamplerTexture,
sizeInfo.LayerSize,
_context.Capabilities,
out int firstLayer,
@@ -598,17 +623,15 @@ namespace Ryujinx.Graphics.Gpu.Image
if (oInfo.Compatibility == TextureViewCompatibility.Full)
{
- TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel);
-
if (!isSamplerTexture)
{
- info = adjInfo;
+ // If this is not a sampler texture, the size might be different from the requested size,
+ // so we need to make sure the texture information has the correct size for this base texture,
+ // before creating the view.
+ info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel);
}
- texture = overlap.CreateView(adjInfo, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
-
- ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
-
+ texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
texture.SynchronizeMemory();
break;
}
@@ -682,6 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Image
TextureViewCompatibility compatibility = texture.IsViewCompatible(
overlap.Info,
overlap.Range,
+ exactSize: true,
overlap.LayerSize,
_context.Capabilities,
out int firstLayer,
@@ -792,7 +816,11 @@ namespace Ryujinx.Graphics.Gpu.Image
continue;
}
- TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel);
+ // Note: If we allow different sizes for those overlaps,
+ // we need to make sure that the "info" has the correct size for the parent texture here.
+ // Since this is not allowed right now, we don't need to do it.
+
+ TextureInfo overlapInfo = overlap.Info;
if (texture.ScaleFactor != overlap.ScaleFactor)
{
@@ -856,44 +884,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return texture;
}
- ///
- /// Changes a texture's size to match the desired size for samplers,
- /// or increases a texture's size to fit the region indicated by a size hint.
- ///
- /// The desired texture info
- /// The texture to resize
- /// True if the texture will be used for a sampler, false otherwise
- /// A hint indicating the minimum used size for the texture
- private void ChangeSizeIfNeeded(TextureInfo info, Texture texture, bool isSamplerTexture, Size? sizeHint)
- {
- if (isSamplerTexture)
- {
- // If this is used for sampling, the size must match,
- // otherwise the shader would sample garbage data.
- // To fix that, we create a new texture with the correct
- // size, and copy the data from the old one to the new one.
-
- if (!TextureCompatibility.SizeMatches(texture.Info, info))
- {
- texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
- }
- }
- else if (sizeHint != null)
- {
- // A size hint indicates that data will be used within that range, at least.
- // If the texture is smaller than the size hint, it must be enlarged to meet it.
- // The maximum size is provided by the requested info, which generally has an aligned size.
-
- int width = Math.Max(texture.Info.Width, Math.Min(sizeHint.Value.Width, info.Width));
- int height = Math.Max(texture.Info.Height, Math.Min(sizeHint.Value.Height, info.Height));
-
- if (texture.Info.Width != width || texture.Info.Height != height)
- {
- texture.ChangeSize(width, height, info.DepthOrLayers);
- }
- }
- }
-
///
/// Attempt to find a texture on the short duration cache.
///
@@ -1000,92 +990,6 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- ///
- /// Adjusts the size of the texture information for a given mipmap level,
- /// based on the size of a parent texture.
- ///
- /// The parent texture
- /// The texture information to be adjusted
- /// The first level of the texture view
- /// The adjusted texture information with the new size
- private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
- {
- // When the texture is used as view of another texture, we must
- // ensure that the sizes are valid, otherwise data uploads would fail
- // (and the size wouldn't match the real size used on the host API).
- // Given a parent texture from where the view is created, we have the
- // following rules:
- // - The view size must be equal to the parent size, divided by (2 ^ l),
- // where l is the first mipmap level of the view. The division result must
- // be rounded down, and the result must be clamped to 1.
- // - If the parent format is compressed, and the view format isn't, the
- // view size is calculated as above, but the width and height of the
- // view must be also divided by the compressed format block width and height.
- // - If the parent format is not compressed, and the view is, the view
- // size is calculated as described on the first point, but the width and height
- // of the view must be also multiplied by the block width and height.
- int width = Math.Max(1, parent.Info.Width >> firstLevel);
- int height = Math.Max(1, parent.Info.Height >> firstLevel);
-
- if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
- {
- width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
- height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
- }
- else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
- {
- width *= info.FormatInfo.BlockWidth;
- height *= info.FormatInfo.BlockHeight;
- }
-
- int depthOrLayers;
-
- if (info.Target == Target.Texture3D)
- {
- depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
- }
- else
- {
- depthOrLayers = info.DepthOrLayers;
- }
-
- // 2D and 2D multisample textures are not considered compatible.
- // This specific case is required for copies, where the source texture might be multisample.
- // In this case, we inherit the parent texture multisample state.
- Target target = info.Target;
- int samplesInX = info.SamplesInX;
- int samplesInY = info.SamplesInY;
-
- if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
- {
- target = Target.Texture2DMultisample;
- samplesInX = parent.Info.SamplesInX;
- samplesInY = parent.Info.SamplesInY;
- }
-
- return new TextureInfo(
- info.GpuAddress,
- width,
- height,
- depthOrLayers,
- info.Levels,
- samplesInX,
- samplesInY,
- info.Stride,
- info.IsLinear,
- info.GobBlocksInY,
- info.GobBlocksInZ,
- info.GobBlocksInTileX,
- target,
- info.FormatInfo,
- info.DepthStencilMode,
- info.SwizzleR,
- info.SwizzleG,
- info.SwizzleB,
- info.SwizzleA);
- }
-
-
///
/// Gets a texture creation information from texture information.
/// This can be used to create new host textures.
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
index 7ec4c7ac2b..e8061951be 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
@@ -380,42 +380,37 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Texture information of the texture view
/// Texture information of the texture view to match against
+ /// Indicates if the sizes must be exactly equal
/// Mipmap level of the texture view in relation to this texture
/// The view compatibility level of the view sizes
- public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
+ public static TextureViewCompatibility ViewSizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact, int level)
{
- Size size = GetAlignedSize(lhs, level);
+ Size lhsAlignedSize = GetAlignedSize(lhs, level);
+ Size rhsAlignedSize = GetAlignedSize(rhs);
- Size otherSize = GetAlignedSize(rhs);
+ Size lhsSize = GetSizeInBlocks(lhs, level);
+ Size rhsSize = GetSizeInBlocks(rhs);
TextureViewCompatibility result = TextureViewCompatibility.Full;
// For copies, we can copy a subset of the 3D texture slices,
// so the depth may be different in this case.
- if (rhs.Target == Target.Texture3D && size.Depth != otherSize.Depth)
+ if (rhs.Target == Target.Texture3D && lhsSize.Depth != rhsSize.Depth)
{
result = TextureViewCompatibility.CopyOnly;
}
- if (size.Width == otherSize.Width && size.Height == otherSize.Height)
+ // Some APIs align the width for copy and render target textures,
+ // so the width may not match in this case for different uses of the same texture.
+ // To account for this, we compare the aligned width here.
+ // We expect height to always match exactly, if the texture is the same.
+ if (lhsAlignedSize.Width == rhsAlignedSize.Width && lhsSize.Height == rhsSize.Height)
{
- if (level > 0 && result == TextureViewCompatibility.Full)
- {
- // A resize should not change the aligned size of the largest mip.
- // If it would, then create a copy dependency rather than a full view.
-
- Size mip0SizeLhs = GetAlignedSize(lhs);
- Size mip0SizeRhs = GetLargestAlignedSize(rhs, level);
-
- if (mip0SizeLhs.Width != mip0SizeRhs.Width || mip0SizeLhs.Height != mip0SizeRhs.Height)
- {
- result = TextureViewCompatibility.CopyOnly;
- }
- }
-
- return result;
+ return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
+ ? TextureViewCompatibility.CopyOnly
+ : result;
}
- else if (lhs.IsLinear && rhs.IsLinear)
+ else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
{
// Copy between linear textures with matching stride.
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
@@ -454,57 +449,33 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Texture information to compare
/// Texture information to compare with
- /// True if the size matches, false otherwise
- public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
- {
- return SizeMatches(lhs, rhs, alignSizes: false);
- }
-
- ///
- /// Checks if the texture sizes of the supplied texture informations match the given level
- ///
- /// Texture information to compare
- /// Texture information to compare with
- /// Mipmap level of this texture to compare with
- /// True if the size matches with the level, false otherwise
- public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, int level)
- {
- return Math.Max(1, lhs.Width >> level) == rhs.Width &&
- Math.Max(1, lhs.Height >> level) == rhs.Height &&
- Math.Max(1, lhs.GetDepth() >> level) == rhs.GetDepth();
- }
-
- ///
- /// Checks if the texture sizes of the supplied texture informations match.
- ///
- /// Texture information to compare
- /// Texture information to compare with
- /// True to align the sizes according to the texture layout for comparison
- /// Mip level of the lhs texture. Aligned sizes are compared for the largest mip
+ /// Indicates if the size must be exactly equal between the textures, or if is allowed to be larger
/// True if the sizes matches, false otherwise
- public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool alignSizes, int lhsLevel = 0)
+ public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs, bool exact)
{
if (lhs.GetLayers() != rhs.GetLayers())
{
return false;
}
- bool isTextureBuffer = lhs.Target == Target.TextureBuffer || rhs.Target == Target.TextureBuffer;
+ Size lhsSize = GetSizeInBlocks(lhs);
+ Size rhsSize = GetSizeInBlocks(rhs);
- if (alignSizes && !isTextureBuffer)
+ if (exact || lhs.IsLinear || rhs.IsLinear)
{
- Size size0 = GetLargestAlignedSize(lhs, lhsLevel);
- Size size1 = GetLargestAlignedSize(rhs, lhsLevel);
-
- return size0.Width == size1.Width &&
- size0.Height == size1.Height &&
- size0.Depth == size1.Depth;
+ return lhsSize.Width == rhsSize.Width &&
+ lhsSize.Height == rhsSize.Height &&
+ lhsSize.Depth == rhsSize.Depth;
}
else
{
- return lhs.Width == rhs.Width &&
- lhs.Height == rhs.Height &&
- lhs.GetDepth() == rhs.GetDepth();
+ Size lhsAlignedSize = GetAlignedSize(lhs);
+ Size rhsAlignedSize = GetAlignedSize(rhs);
+
+ return lhsAlignedSize.Width == rhsAlignedSize.Width &&
+ lhsSize.Width >= rhsSize.Width &&
+ lhsSize.Height == rhsSize.Height &&
+ lhsSize.Depth == rhsSize.Depth;
}
}
@@ -543,22 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- ///
- /// Gets the aligned sizes of the specified texture information, shifted to the largest mip from a given level.
- /// The alignment depends on the texture layout and format bytes per pixel.
- ///
- /// Texture information to calculate the aligned size from
- /// Mipmap level for texture views. Shifts the aligned size to represent the largest mip level
- /// The aligned texture size of the largest mip level
- public static Size GetLargestAlignedSize(TextureInfo info, int level)
- {
- int width = info.Width << level;
- int height = info.Height << level;
- int depth = info.GetDepth() << level;
-
- return GetAlignedSize(info, width, height, depth);
- }
-
///
/// Gets the aligned sizes of the specified texture information.
/// The alignment depends on the texture layout and format bytes per pixel.
@@ -575,6 +530,25 @@ namespace Ryujinx.Graphics.Gpu.Image
return GetAlignedSize(info, width, height, depth);
}
+ ///
+ /// Gets the size in blocks for the given texture information.
+ /// For non-compressed formats, that's the same as the regular size.
+ ///
+ /// Texture information to calculate the aligned size from
+ /// Mipmap level for texture views
+ /// The texture size in blocks
+ public static Size GetSizeInBlocks(TextureInfo info, int level = 0)
+ {
+ int width = Math.Max(1, info.Width >> level);
+ int height = Math.Max(1, info.Height >> level);
+ int depth = Math.Max(1, info.GetDepth() >> level);
+
+ return new Size(
+ BitUtils.DivRoundUp(width, info.FormatInfo.BlockWidth),
+ BitUtils.DivRoundUp(height, info.FormatInfo.BlockHeight),
+ depth);
+ }
+
///
/// Check if it's possible to create a view with the layout of the second texture information from the first.
/// The layout information is composed of the Stride for linear textures, or GOB block size
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
index 65cc698b0f..a7ee12bccd 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
@@ -1,5 +1,7 @@
+using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture;
+using System;
namespace Ryujinx.Graphics.Gpu.Image
{
@@ -292,5 +294,88 @@ namespace Ryujinx.Graphics.Gpu.Image
layerSize);
}
}
+
+ ///
+ /// Creates texture information for a given mipmap level of the specified parent texture and this information.
+ ///
+ /// The parent texture
+ /// The first level of the texture view
+ /// The adjusted texture information with the new size
+ public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel)
+ {
+ // When the texture is used as view of another texture, we must
+ // ensure that the sizes are valid, otherwise data uploads would fail
+ // (and the size wouldn't match the real size used on the host API).
+ // Given a parent texture from where the view is created, we have the
+ // following rules:
+ // - The view size must be equal to the parent size, divided by (2 ^ l),
+ // where l is the first mipmap level of the view. The division result must
+ // be rounded down, and the result must be clamped to 1.
+ // - If the parent format is compressed, and the view format isn't, the
+ // view size is calculated as above, but the width and height of the
+ // view must be also divided by the compressed format block width and height.
+ // - If the parent format is not compressed, and the view is, the view
+ // size is calculated as described on the first point, but the width and height
+ // of the view must be also multiplied by the block width and height.
+ int width = Math.Max(1, parent.Info.Width >> firstLevel);
+ int height = Math.Max(1, parent.Info.Height >> firstLevel);
+
+ if (parent.Info.FormatInfo.IsCompressed && !FormatInfo.IsCompressed)
+ {
+ width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
+ height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
+ }
+ else if (!parent.Info.FormatInfo.IsCompressed && FormatInfo.IsCompressed)
+ {
+ width *= FormatInfo.BlockWidth;
+ height *= FormatInfo.BlockHeight;
+ }
+
+ int depthOrLayers;
+
+ if (Target == Target.Texture3D)
+ {
+ depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
+ }
+ else
+ {
+ depthOrLayers = DepthOrLayers;
+ }
+
+ // 2D and 2D multisample textures are not considered compatible.
+ // This specific case is required for copies, where the source texture might be multisample.
+ // In this case, we inherit the parent texture multisample state.
+ Target target = Target;
+ int samplesInX = SamplesInX;
+ int samplesInY = SamplesInY;
+
+ if (target == Target.Texture2D && parent.Target == Target.Texture2DMultisample)
+ {
+ target = Target.Texture2DMultisample;
+ samplesInX = parent.Info.SamplesInX;
+ samplesInY = parent.Info.SamplesInY;
+ }
+
+ return new TextureInfo(
+ GpuAddress,
+ width,
+ height,
+ depthOrLayers,
+ Levels,
+ samplesInX,
+ samplesInY,
+ Stride,
+ IsLinear,
+ GobBlocksInY,
+ GobBlocksInZ,
+ GobBlocksInTileX,
+ target,
+ FormatInfo,
+ DepthStencilMode,
+ SwizzleR,
+ SwizzleG,
+ SwizzleB,
+ SwizzleA);
+ }
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index 083de64c55..266f62856b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -437,22 +437,6 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- ///
- /// Update host framebuffer attachments based on currently bound render target buffers.
- ///
- ///
- /// All attachments other than will be unbound.
- ///
- /// Index of the render target color to be updated
- public void UpdateRenderTarget(int index)
- {
- new Span(_rtHostColors).Fill(null);
- _rtHostColors[index] = _rtColors[index]?.HostTexture;
- _rtHostDs = null;
-
- _context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null);
- }
-
///
/// Update host framebuffer attachments based on currently bound render target buffers.
///
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index fc99fc9977..0348ca014b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -77,22 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
- if (texture.ChangedSize)
- {
- // Texture changed size at one point - it may be a different size than the sampler expects.
- // This can be triggered when the size is changed by a size hint on copy or draw, but the texture has been sampled before.
-
- int baseLevel = descriptor.UnpackBaseLevel();
- int width = Math.Max(1, descriptor.UnpackWidth() >> baseLevel);
- int height = Math.Max(1, descriptor.UnpackHeight() >> baseLevel);
-
- if (texture.Info.Width != width || texture.Info.Height != height)
- {
- texture.ChangeSize(width, height, texture.Info.DepthOrLayers);
- }
- }
-
- // Memory is automatically synchronized on texture creation.
+ // On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
texture.SynchronizeMemory();
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
index aea7b167e6..890bf1736f 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
@@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Image
enum TextureSearchFlags
{
None = 0,
- Strict = 1 << 0,
ForSampler = 1 << 1,
ForCopy = 1 << 2,
WithUpscale = 1 << 3,
diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs
index 90f8e40f92..06d0fddf49 100644
--- a/Ryujinx.Graphics.Gpu/Window.cs
+++ b/Ryujinx.Graphics.Gpu/Window.cs
@@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu
{
pt.AcquireCallback(_context, pt.UserObj);
- Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
+ Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, pt.Range);
pt.Cache.Tick();