Handle mismatching texture size with copy dependencies (#4364)
* Handle mismatching texture size with copy dependencies * Create copy and render textures with the minimum possible size * Only align width for comparisons, assume that height is always exact * Fix IsExactMatch size check * Allow sampler and copy textures to match textures with larger width * Delete texture ChangeSize related code * Move AdjustSize to TextureInfo and give it a better name, adjust usages * Fix GetMinimumWidthInGob when minimumWidth > width * Only update render targets that are actually cleared for clear Avoids creating textures with incorrect sizes * Delete UpdateRenderTargetState method that is not needed anymore Clears now only ever sets the render targets that will be cleared rather than all of them
This commit is contained in:
parent
59755818ef
commit
96cf242bcf
12 changed files with 271 additions and 406 deletions
|
@ -725,10 +725,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool clearDepth = (argument & 1) != 0;
|
||||||
|
bool clearStencil = (argument & 2) != 0;
|
||||||
|
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||||
int index = (argument >> 6) & 0xf;
|
int index = (argument >> 6) & 0xf;
|
||||||
int layer = (argument >> 10) & 0x3ff;
|
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
|
// 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
|
// 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);
|
_context.Renderer.Pipeline.SetScissors(scissors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clipMismatch)
|
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTarget(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTargets();
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
}
|
|
||||||
|
|
||||||
bool clearDepth = (argument & 1) != 0;
|
|
||||||
bool clearStencil = (argument & 2) != 0;
|
|
||||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
|
||||||
|
|
||||||
if (componentMask != 0)
|
if (componentMask != 0)
|
||||||
{
|
{
|
||||||
|
@ -841,7 +845,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
engine.UpdateScissorState();
|
engine.UpdateScissorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.UpdateRenderTargetState(useControl: true);
|
engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
|
|
||||||
if (renderEnable == ConditionalRenderEnabled.Host)
|
if (renderEnable == ConditionalRenderEnabled.Host)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Flags indicating how the render targets should be updated.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
enum RenderTargetUpdateFlags
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No flags.
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get render target index from the control register.
|
||||||
|
/// </summary>
|
||||||
|
UseControl = 1 << 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that all render targets are 2D array textures.
|
||||||
|
/// </summary>
|
||||||
|
Layered = 1 << 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that only a single color target will be used.
|
||||||
|
/// </summary>
|
||||||
|
SingleColor = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the depth-stencil target will be used.
|
||||||
|
/// </summary>
|
||||||
|
UpdateDepthStencil = 1 << 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default update flags for draw.
|
||||||
|
/// </summary>
|
||||||
|
UpdateAll = UseControl | UpdateDepthStencil
|
||||||
|
}
|
||||||
|
}
|
|
@ -402,20 +402,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateRenderTargetState()
|
private void UpdateRenderTargetState()
|
||||||
{
|
{
|
||||||
UpdateRenderTargetState(true);
|
UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
|
public void UpdateRenderTargetState(RenderTargetUpdateFlags updateFlags, int singleUse = -1)
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
var rtControl = _state.State.RtControl;
|
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;
|
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
||||||
|
|
||||||
var msaaMode = _state.State.RtMsaaMode;
|
var msaaMode = _state.State.RtMsaaMode;
|
||||||
|
@ -438,7 +441,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
|
||||||
var colorState = _state.State.RtColorState[rtIndex];
|
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);
|
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
|
||||||
|
|
||||||
|
@ -478,7 +481,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
|
||||||
Image.Texture depthStencil = null;
|
Image.Texture depthStencil = null;
|
||||||
|
|
||||||
if (dsEnable)
|
if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil))
|
||||||
{
|
{
|
||||||
var dsState = _state.State.RtDepthStencilState;
|
var dsState = _state.State.RtDepthStencilState;
|
||||||
var dsSize = _state.State.RtDepthStencilSize;
|
var dsSize = _state.State.RtDepthStencilSize;
|
||||||
|
|
|
@ -139,12 +139,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
/// <param name="updateFlags">Flags indicating which render targets should be updated and how</param>
|
||||||
/// <param name="layered">Indicates if the texture is layered</param>
|
|
||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using Ryujinx.Common;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
@ -89,12 +88,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TextureGroup Group { get; private set; }
|
public TextureGroup Group { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set when a texture has been changed size. This indicates that it may need to be
|
|
||||||
/// changed again when obtained as a sampler.
|
|
||||||
/// </summary>
|
|
||||||
public bool ChangedSize { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set when a texture's GPU VA has ever been partially or fully unmapped.
|
/// 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.
|
/// 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);
|
Group.CreateCopyDependency(contained, FirstLayer + layer, FirstLevel + level, copyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Changes the texture size.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 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.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="width">The block width related to the given width</param>
|
|
||||||
/// <param name="height">The block height related to the given height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="width">The new texture width</param>
|
|
||||||
/// <param name="height">The new texture height</param>
|
|
||||||
/// <param name="depthOrLayers">The new texture depth (for 3D textures) or layers (for layered textures)</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers when a texture has had its data set after being scaled, and
|
/// Registers when a texture has had its data set after being scaled, and
|
||||||
/// determines if it should be blacklisted from scaling to improve performance.
|
/// determines if it should be blacklisted from scaling to improve performance.
|
||||||
|
@ -1215,7 +1092,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <returns>A value indicating how well this texture matches the given info</returns>
|
/// <returns>A value indicating how well this texture matches the given info</returns>
|
||||||
public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags flags)
|
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)
|
if (matchQuality == TextureMatchQuality.NoMatch)
|
||||||
{
|
{
|
||||||
|
@ -1227,12 +1106,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TextureCompatibility.SizeMatches(Info, info, (flags & TextureSearchFlags.Strict) == 0, FirstLevel))
|
if (!TextureCompatibility.SizeMatches(Info, info, forSampler))
|
||||||
{
|
{
|
||||||
return TextureMatchQuality.NoMatch;
|
return TextureMatchQuality.NoMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & TextureSearchFlags.ForSampler) != 0 || (flags & TextureSearchFlags.Strict) != 0)
|
if ((flags & TextureSearchFlags.ForSampler) != 0)
|
||||||
{
|
{
|
||||||
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
if (!TextureCompatibility.SamplerParamsMatches(Info, info))
|
||||||
{
|
{
|
||||||
|
@ -1262,12 +1141,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">Texture view information</param>
|
/// <param name="info">Texture view information</param>
|
||||||
/// <param name="range">Texture view physical memory ranges</param>
|
/// <param name="range">Texture view physical memory ranges</param>
|
||||||
|
/// <param name="exactSize">Indicates if the texture sizes must be exactly equal, or width is allowed to differ</param>
|
||||||
/// <param name="layerSize">Layer size on the given texture</param>
|
/// <param name="layerSize">Layer size on the given texture</param>
|
||||||
/// <param name="caps">Host GPU capabilities</param>
|
/// <param name="caps">Host GPU capabilities</param>
|
||||||
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
/// <param name="firstLayer">Texture view initial layer on this texture</param>
|
||||||
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
|
||||||
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
|
||||||
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;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
|
@ -1317,7 +1204,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return TextureViewCompatibility.LayoutIncompatible;
|
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));
|
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewSubImagesInBounds(Info, info, firstLayer, firstLevel));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -210,8 +210,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
ulong offset,
|
ulong offset,
|
||||||
FormatInfo formatInfo,
|
FormatInfo formatInfo,
|
||||||
bool shouldCreate,
|
bool shouldCreate,
|
||||||
bool preferScaling = true,
|
bool preferScaling,
|
||||||
Size? sizeHint = null)
|
Size sizeHint)
|
||||||
{
|
{
|
||||||
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
copyTexture.Address.Pack() + offset,
|
copyTexture.Address.Pack() + offset,
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, copyTexture.LinearLayout),
|
||||||
copyTexture.Height,
|
copyTexture.Height,
|
||||||
copyTexture.Depth,
|
copyTexture.Depth,
|
||||||
1,
|
1,
|
||||||
|
@ -255,7 +255,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
flags |= TextureSearchFlags.NoCreate;
|
flags |= TextureSearchFlags.NoCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
colorState.Address.Pack(),
|
colorState.Address.Pack(),
|
||||||
width,
|
GetMinimumWidthInGob(width, sizeHint.Width, formatInfo.BytesPerPixel, isLinear),
|
||||||
colorState.Height,
|
colorState.Height,
|
||||||
colorState.Depth,
|
colorState.Depth,
|
||||||
1,
|
1,
|
||||||
|
@ -342,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
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();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
|
@ -395,7 +395,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
dsState.Address.Pack(),
|
dsState.Address.Pack(),
|
||||||
size.Width,
|
GetMinimumWidthInGob(size.Width, sizeHint.Width, formatInfo.BytesPerPixel, false),
|
||||||
size.Height,
|
size.Height,
|
||||||
size.Depth,
|
size.Depth,
|
||||||
1,
|
1,
|
||||||
|
@ -409,13 +409,41 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
target,
|
target,
|
||||||
formatInfo);
|
formatInfo);
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The possibly aligned texture width</param>
|
||||||
|
/// <param name="minimumWidth">The minimum width that the texture may have without losing data</param>
|
||||||
|
/// <param name="bytesPerPixel">Bytes per pixel of the texture format</param>
|
||||||
|
/// <param name="isLinear">True if the texture is linear, false for block linear</param>
|
||||||
|
/// <returns>The minimum width of the texture with the same amount of GOBs per row</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -423,7 +451,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
||||||
/// <param name="info">Texture information of the texture to be found or created</param>
|
/// <param name="info">Texture information of the texture to be found or created</param>
|
||||||
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(
|
public Texture FindOrCreateTexture(
|
||||||
|
@ -431,7 +458,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
TextureSearchFlags flags,
|
TextureSearchFlags flags,
|
||||||
TextureInfo info,
|
TextureInfo info,
|
||||||
int layerSize = 0,
|
int layerSize = 0,
|
||||||
Size? sizeHint = null,
|
|
||||||
MultiRange? range = null)
|
MultiRange? range = null)
|
||||||
{
|
{
|
||||||
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
|
@ -512,8 +538,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
|
@ -568,6 +592,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
TextureViewCompatibility overlapCompatibility = overlap.IsViewCompatible(
|
||||||
info,
|
info,
|
||||||
range.Value,
|
range.Value,
|
||||||
|
isSamplerTexture,
|
||||||
sizeInfo.LayerSize,
|
sizeInfo.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
|
@ -598,17 +623,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
if (oInfo.Compatibility == TextureViewCompatibility.Full)
|
||||||
{
|
{
|
||||||
TextureInfo adjInfo = AdjustSizes(overlap, info, oInfo.FirstLevel);
|
|
||||||
|
|
||||||
if (!isSamplerTexture)
|
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);
|
texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel);
|
||||||
|
|
||||||
ChangeSizeIfNeeded(info, texture, isSamplerTexture, sizeHint);
|
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -682,6 +705,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
TextureViewCompatibility compatibility = texture.IsViewCompatible(
|
||||||
overlap.Info,
|
overlap.Info,
|
||||||
overlap.Range,
|
overlap.Range,
|
||||||
|
exactSize: true,
|
||||||
overlap.LayerSize,
|
overlap.LayerSize,
|
||||||
_context.Capabilities,
|
_context.Capabilities,
|
||||||
out int firstLayer,
|
out int firstLayer,
|
||||||
|
@ -792,7 +816,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
continue;
|
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)
|
if (texture.ScaleFactor != overlap.ScaleFactor)
|
||||||
{
|
{
|
||||||
|
@ -856,44 +884,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">The desired texture info</param>
|
|
||||||
/// <param name="texture">The texture to resize</param>
|
|
||||||
/// <param name="isSamplerTexture">True if the texture will be used for a sampler, false otherwise</param>
|
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1000,92 +990,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adjusts the size of the texture information for a given mipmap level,
|
|
||||||
/// based on the size of a parent texture.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parent">The parent texture</param>
|
|
||||||
/// <param name="info">The texture information to be adjusted</param>
|
|
||||||
/// <param name="firstLevel">The first level of the texture view</param>
|
|
||||||
/// <returns>The adjusted texture information with the new size</returns>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a texture creation information from texture information.
|
/// Gets a texture creation information from texture information.
|
||||||
/// This can be used to create new host textures.
|
/// This can be used to create new host textures.
|
||||||
|
|
|
@ -380,42 +380,37 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information of the texture view</param>
|
/// <param name="lhs">Texture information of the texture view</param>
|
||||||
/// <param name="rhs">Texture information of the texture view to match against</param>
|
/// <param name="rhs">Texture information of the texture view to match against</param>
|
||||||
|
/// <param name="exact">Indicates if the sizes must be exactly equal</param>
|
||||||
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
/// <param name="level">Mipmap level of the texture view in relation to this texture</param>
|
||||||
/// <returns>The view compatibility level of the view sizes</returns>
|
/// <returns>The view compatibility level of the view sizes</returns>
|
||||||
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;
|
TextureViewCompatibility result = TextureViewCompatibility.Full;
|
||||||
|
|
||||||
// For copies, we can copy a subset of the 3D texture slices,
|
// For copies, we can copy a subset of the 3D texture slices,
|
||||||
// so the depth may be different in this case.
|
// 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;
|
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)
|
return (exact && lhsSize.Width != rhsSize.Width) || lhsSize.Width < rhsSize.Width
|
||||||
{
|
? TextureViewCompatibility.CopyOnly
|
||||||
// A resize should not change the aligned size of the largest mip.
|
: result;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else if (lhs.IsLinear && rhs.IsLinear)
|
|
||||||
{
|
{
|
||||||
// Copy between linear textures with matching stride.
|
// Copy between linear textures with matching stride.
|
||||||
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
int stride = BitUtils.AlignUp(Math.Max(1, lhs.Stride >> level), Constants.StrideAlignment);
|
||||||
|
@ -454,57 +449,33 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
/// <param name="lhs">Texture information to compare</param>
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
/// <param name="rhs">Texture information to compare with</param>
|
||||||
/// <returns>True if the size matches, false otherwise</returns>
|
/// <param name="exact">Indicates if the size must be exactly equal between the textures, or if <paramref name="rhs"/> is allowed to be larger</param>
|
||||||
public static bool SizeMatches(TextureInfo lhs, TextureInfo rhs)
|
|
||||||
{
|
|
||||||
return SizeMatches(lhs, rhs, alignSizes: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match the given level
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="level">Mipmap level of this texture to compare with</param>
|
|
||||||
/// <returns>True if the size matches with the level, false otherwise</returns>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the texture sizes of the supplied texture informations match.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="lhs">Texture information to compare</param>
|
|
||||||
/// <param name="rhs">Texture information to compare with</param>
|
|
||||||
/// <param name="alignSizes">True to align the sizes according to the texture layout for comparison</param>
|
|
||||||
/// <param name="lhsLevel">Mip level of the lhs texture. Aligned sizes are compared for the largest mip</param>
|
|
||||||
/// <returns>True if the sizes matches, false otherwise</returns>
|
/// <returns>True if the sizes matches, false otherwise</returns>
|
||||||
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())
|
if (lhs.GetLayers() != rhs.GetLayers())
|
||||||
{
|
{
|
||||||
return false;
|
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);
|
return lhsSize.Width == rhsSize.Width &&
|
||||||
Size size1 = GetLargestAlignedSize(rhs, lhsLevel);
|
lhsSize.Height == rhsSize.Height &&
|
||||||
|
lhsSize.Depth == rhsSize.Depth;
|
||||||
return size0.Width == size1.Width &&
|
|
||||||
size0.Height == size1.Height &&
|
|
||||||
size0.Depth == size1.Depth;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return lhs.Width == rhs.Width &&
|
Size lhsAlignedSize = GetAlignedSize(lhs);
|
||||||
lhs.Height == rhs.Height &&
|
Size rhsAlignedSize = GetAlignedSize(rhs);
|
||||||
lhs.GetDepth() == rhs.GetDepth();
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="info">Texture information to calculate the aligned size from</param>
|
|
||||||
/// <param name="level">Mipmap level for texture views. Shifts the aligned size to represent the largest mip level</param>
|
|
||||||
/// <returns>The aligned texture size of the largest mip level</returns>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the aligned sizes of the specified texture information.
|
/// Gets the aligned sizes of the specified texture information.
|
||||||
/// The alignment depends on the texture layout and format bytes per pixel.
|
/// 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);
|
return GetAlignedSize(info, width, height, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size in blocks for the given texture information.
|
||||||
|
/// For non-compressed formats, that's the same as the regular size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="info">Texture information to calculate the aligned size from</param>
|
||||||
|
/// <param name="level">Mipmap level for texture views</param>
|
||||||
|
/// <returns>The texture size in blocks</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if it's possible to create a view with the layout of the second texture information from the first.
|
/// 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
|
/// The layout information is composed of the Stride for linear textures, or GOB block size
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
|
@ -292,5 +294,88 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
layerSize);
|
layerSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates texture information for a given mipmap level of the specified parent texture and this information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">The parent texture</param>
|
||||||
|
/// <param name="firstLevel">The first level of the texture view</param>
|
||||||
|
/// <returns>The adjusted texture information with the new size</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -437,22 +437,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// All attachments other than <paramref name="index"/> will be unbound.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="index">Index of the render target color to be updated</param>
|
|
||||||
public void UpdateRenderTarget(int index)
|
|
||||||
{
|
|
||||||
new Span<ITexture>(_rtHostColors).Fill(null);
|
|
||||||
_rtHostColors[index] = _rtColors[index]?.HostTexture;
|
|
||||||
_rtHostDs = null;
|
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetRenderTargets(_rtHostColors, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -77,22 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (texture.ChangedSize)
|
// On the path above (texture not yet in the pool), memory is automatically synchronized on texture creation.
|
||||||
{
|
|
||||||
// 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.
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
enum TextureSearchFlags
|
enum TextureSearchFlags
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Strict = 1 << 0,
|
|
||||||
ForSampler = 1 << 1,
|
ForSampler = 1 << 1,
|
||||||
ForCopy = 1 << 2,
|
ForCopy = 1 << 2,
|
||||||
WithUpscale = 1 << 3,
|
WithUpscale = 1 << 3,
|
||||||
|
|
|
@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
pt.AcquireCallback(_context, pt.UserObj);
|
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();
|
pt.Cache.Tick();
|
||||||
|
|
||||||
|
|
Reference in a new issue