Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
99fc4fa61b | ||
|
f6d5499a16 | ||
|
26bf13a65d | ||
|
96cf242bcf |
@@ -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.UpdateRenderTargets();
|
||||||
{
|
|
||||||
_channel.TextureManager.UpdateRenderTarget(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_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>
|
||||||
|
@@ -33,10 +33,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class AutoDeleteCache : IEnumerable<Texture>
|
class AutoDeleteCache : IEnumerable<Texture>
|
||||||
{
|
{
|
||||||
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
private const ulong MaxTextureSizeCapacity = 512 * 1024 * 1024; // MB;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private readonly ConcurrentQueue<Texture> _deferredRemovals;
|
private ulong _totalSize;
|
||||||
|
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
private HashSet<ShortTextureCacheEntry> _shortCacheBuilder;
|
||||||
private HashSet<ShortTextureCacheEntry> _shortCache;
|
private HashSet<ShortTextureCacheEntry> _shortCache;
|
||||||
@@ -49,7 +51,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public AutoDeleteCache()
|
public AutoDeleteCache()
|
||||||
{
|
{
|
||||||
_textures = new LinkedList<Texture>();
|
_textures = new LinkedList<Texture>();
|
||||||
_deferredRemovals = new ConcurrentQueue<Texture>();
|
|
||||||
|
|
||||||
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
_shortCacheBuilder = new HashSet<ShortTextureCacheEntry>();
|
||||||
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
_shortCache = new HashSet<ShortTextureCacheEntry>();
|
||||||
@@ -67,37 +68,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="texture">The texture to be added to the cache</param>
|
/// <param name="texture">The texture to be added to the cache</param>
|
||||||
public void Add(Texture texture)
|
public void Add(Texture texture)
|
||||||
{
|
{
|
||||||
texture.IncrementReferenceCount();
|
_totalSize += texture.Size;
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
if (_textures.Count > MaxCapacity)
|
if (_textures.Count > MaxCapacity ||
|
||||||
|
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||||
{
|
{
|
||||||
Texture oldestTexture = _textures.First.Value;
|
RemoveLeastUsedTexture();
|
||||||
|
|
||||||
if (!oldestTexture.CheckModified(false))
|
|
||||||
{
|
|
||||||
// The texture must be flushed if it falls out of the auto delete cache.
|
|
||||||
// Flushes out of the auto delete cache do not trigger write tracking,
|
|
||||||
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
|
||||||
|
|
||||||
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
|
||||||
oldestTexture.FlushModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_textures.RemoveFirst();
|
|
||||||
|
|
||||||
oldestTexture.DecrementReferenceCount();
|
|
||||||
|
|
||||||
oldestTexture.CacheNode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_deferredRemovals.Count > 0)
|
|
||||||
{
|
|
||||||
while (_deferredRemovals.TryDequeue(out Texture textureToRemove))
|
|
||||||
{
|
|
||||||
Remove(textureToRemove, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +99,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
texture.CacheNode = _textures.AddLast(texture);
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||||
|
{
|
||||||
|
RemoveLeastUsedTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -127,6 +111,31 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the least used texture from the cache.
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveLeastUsedTexture()
|
||||||
|
{
|
||||||
|
Texture oldestTexture = _textures.First.Value;
|
||||||
|
|
||||||
|
_totalSize -= oldestTexture.Size;
|
||||||
|
|
||||||
|
if (!oldestTexture.CheckModified(false))
|
||||||
|
{
|
||||||
|
// The texture must be flushed if it falls out of the auto delete cache.
|
||||||
|
// Flushes out of the auto delete cache do not trigger write tracking,
|
||||||
|
// as it is expected that other overlapping textures exist that have more up-to-date contents.
|
||||||
|
|
||||||
|
oldestTexture.Group.SynchronizeDependents(oldestTexture);
|
||||||
|
oldestTexture.FlushModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textures.RemoveFirst();
|
||||||
|
|
||||||
|
oldestTexture.DecrementReferenceCount();
|
||||||
|
oldestTexture.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a texture from the cache.
|
/// Removes a texture from the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -148,20 +157,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
_textures.Remove(texture.CacheNode);
|
_textures.Remove(texture.CacheNode);
|
||||||
|
|
||||||
|
_totalSize -= texture.Size;
|
||||||
|
|
||||||
texture.CacheNode = null;
|
texture.CacheNode = null;
|
||||||
|
|
||||||
return texture.DecrementReferenceCount();
|
return texture.DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues removal of a texture from the cache in a thread safe way.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be removed from the cache</param>
|
|
||||||
public void RemoveDeferred(Texture texture)
|
|
||||||
{
|
|
||||||
_deferredRemovals.Enqueue(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to find a texture on the short duration cache.
|
/// Attempt to find a texture on the short duration cache.
|
||||||
/// </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;
|
||||||
@@ -1750,13 +1637,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveFromPools(true);
|
RemoveFromPools(true);
|
||||||
|
|
||||||
// We only want to remove if there's no mapped region of the texture that was modified by the GPU,
|
|
||||||
// otherwise we could lose data.
|
|
||||||
if (!Group.AnyModified(this))
|
|
||||||
{
|
|
||||||
_physicalMemory.TextureCache.QueueAutoDeleteCacheRemoval(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -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.
|
||||||
@@ -1175,19 +1079,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queues the removal of a texture from the auto delete cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This function is thread safe and can be called from any thread.
|
|
||||||
/// The texture will be deleted on the next time the cache is used.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="texture">The texture to be removed</param>
|
|
||||||
public void QueueAutoDeleteCacheRemoval(Texture texture)
|
|
||||||
{
|
|
||||||
_cache.RemoveDeferred(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
/// Adds a texture to the short duration cache. This typically keeps it alive for two ticks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
else if (lhs.IsLinear && rhs.IsLinear)
|
else if (lhs.IsLinear && rhs.IsLinear && lhsSize.Height == rhsSize.Height)
|
||||||
{
|
{
|
||||||
// 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
|
||||||
|
@@ -434,32 +434,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a texture was modified by the GPU.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="texture">The texture to be checked</param>
|
|
||||||
/// <returns>True if any region of the texture was modified by the GPU, false otherwise</returns>
|
|
||||||
public bool AnyModified(Texture texture)
|
|
||||||
{
|
|
||||||
bool anyModified = false;
|
|
||||||
|
|
||||||
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < regionCount; i++)
|
|
||||||
{
|
|
||||||
TextureGroupHandle group = _handles[baseHandle + i];
|
|
||||||
|
|
||||||
if (group.Modified)
|
|
||||||
{
|
|
||||||
anyModified = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return anyModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flush modified ranges for a given texture.
|
/// Flush modified ranges for a given texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int attachmentCount = 0;
|
int attachmentCount = 0;
|
||||||
int colorCount = 0;
|
int colorCount = 0;
|
||||||
|
int maxColorAttachmentIndex = -1;
|
||||||
|
|
||||||
for (int i = 0; i < state.AttachmentEnable.Length; i++)
|
for (int i = 0; i < state.AttachmentEnable.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -36,6 +37,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
attachmentIndices[attachmentCount++] = i;
|
attachmentIndices[attachmentCount++] = i;
|
||||||
colorCount++;
|
colorCount++;
|
||||||
|
maxColorAttachmentIndex = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +75,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (colorAttachmentsCount != 0)
|
if (colorAttachmentsCount != 0)
|
||||||
{
|
{
|
||||||
int maxAttachmentIndex = Constants.MaxRenderTargets - 1;
|
subpass.ColorAttachmentCount = (uint)maxColorAttachmentIndex + 1;
|
||||||
subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
|
|
||||||
subpass.PColorAttachments = &attachmentReferences[0];
|
subpass.PColorAttachments = &attachmentReferences[0];
|
||||||
|
|
||||||
// Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
|
// Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
|
||||||
for (int i = 0; i <= maxAttachmentIndex; i++)
|
for (int i = 0; i <= maxColorAttachmentIndex; i++)
|
||||||
{
|
{
|
||||||
subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
|
subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
private const ulong MinByteWeightForFlush = 256 * 1024 * 1024; // MiB
|
||||||
|
|
||||||
private readonly List<QueryPool> _activeQueries;
|
private readonly List<(QueryPool, bool)> _activeQueries;
|
||||||
private CounterQueueEvent _activeConditionalRender;
|
private CounterQueueEvent _activeConditionalRender;
|
||||||
|
|
||||||
private readonly List<BufferedQuery> _pendingQueryCopies;
|
private readonly List<BufferedQuery> _pendingQueryCopies;
|
||||||
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
public PipelineFull(VulkanRenderer gd, Device device) : base(gd, device)
|
||||||
{
|
{
|
||||||
_activeQueries = new List<QueryPool>();
|
_activeQueries = new List<(QueryPool, bool)>();
|
||||||
_pendingQueryCopies = new();
|
_pendingQueryCopies = new();
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
@@ -202,7 +202,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AutoFlush.RegisterFlush(DrawCount);
|
AutoFlush.RegisterFlush(DrawCount);
|
||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
foreach (var queryPool in _activeQueries)
|
foreach ((var queryPool, _) in _activeQueries)
|
||||||
{
|
{
|
||||||
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
Gd.Api.CmdEndQuery(CommandBuffer, queryPool, 0);
|
||||||
}
|
}
|
||||||
@@ -220,10 +220,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
// Restore per-command buffer state.
|
// Restore per-command buffer state.
|
||||||
|
|
||||||
foreach (var queryPool in _activeQueries)
|
foreach ((var queryPool, var isOcclusion) in _activeQueries)
|
||||||
{
|
{
|
||||||
|
bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion;
|
||||||
|
|
||||||
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
|
Gd.Api.CmdResetQueryPool(CommandBuffer, queryPool, 0, 1);
|
||||||
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.ResetCounterPool();
|
Gd.ResetCounterPool();
|
||||||
@@ -231,7 +233,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Restore();
|
Restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
|
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool isOcclusion, bool fromSamplePool)
|
||||||
{
|
{
|
||||||
if (needsReset)
|
if (needsReset)
|
||||||
{
|
{
|
||||||
@@ -247,16 +249,24 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
|
bool isPrecise = Gd.Capabilities.SupportsPreciseOcclusionQueries && isOcclusion;
|
||||||
|
Gd.Api.CmdBeginQuery(CommandBuffer, pool, 0, isPrecise ? QueryControlFlags.PreciseBit : 0);
|
||||||
|
|
||||||
_activeQueries.Add(pool);
|
_activeQueries.Add((pool, isOcclusion));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndQuery(QueryPool pool)
|
public void EndQuery(QueryPool pool)
|
||||||
{
|
{
|
||||||
Gd.Api.CmdEndQuery(CommandBuffer, pool, 0);
|
Gd.Api.CmdEndQuery(CommandBuffer, pool, 0);
|
||||||
|
|
||||||
_activeQueries.Remove(pool);
|
for (int i = 0; i < _activeQueries.Count; i++)
|
||||||
|
{
|
||||||
|
if (_activeQueries[i].Item1.Handle == pool.Handle)
|
||||||
|
{
|
||||||
|
_activeQueries.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyQueryResults(BufferedQuery query)
|
public void CopyQueryResults(BufferedQuery query)
|
||||||
|
@@ -100,7 +100,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||||||
if (_isSupported)
|
if (_isSupported)
|
||||||
{
|
{
|
||||||
bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
|
bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
|
||||||
_pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null);
|
bool isOcclusion = _type == CounterType.SamplesPassed;
|
||||||
|
_pipeline.BeginQuery(this, _queryPool, needsReset, isOcclusion, isOcclusion && resetSequence != null);
|
||||||
}
|
}
|
||||||
_resetSequence = null;
|
_resetSequence = null;
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
|
var componentMapping = new ComponentMapping(swizzleR, swizzleG, swizzleB, swizzleA);
|
||||||
|
|
||||||
var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
|
var aspectFlags = info.Format.ConvertAspectFlags(info.DepthStencilMode);
|
||||||
var aspectFlagsDepth = info.Format.ConvertAspectFlags(DepthStencilMode.Depth);
|
var aspectFlagsDepth = info.Format.ConvertAspectFlags();
|
||||||
|
|
||||||
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
||||||
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
|
||||||
|
@@ -229,7 +229,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ncaId = BitConverter.ToString(cnmt.ContentEntries[0].NcaId).Replace("-", "").ToLower();
|
string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower();
|
||||||
|
|
||||||
AddAocItem(cnmt.TitleId, containerPath, $"{ncaId}.nca", true);
|
AddAocItem(cnmt.TitleId, containerPath, $"{ncaId}.nca", true);
|
||||||
}
|
}
|
||||||
|
@@ -696,8 +696,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
var buildIds = programs.Select(p => p switch
|
var buildIds = programs.Select(p => p switch
|
||||||
{
|
{
|
||||||
NsoExecutable nso => BitConverter.ToString(nso.BuildId.ItemsRo.ToArray()).Replace("-", "").TrimEnd('0'),
|
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'),
|
||||||
NroExecutable nro => BitConverter.ToString(nro.Header.BuildId).Replace("-", "").TrimEnd('0'),
|
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'),
|
||||||
_ => string.Empty
|
_ => string.Empty
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
@@ -51,11 +51,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
|||||||
|
|
||||||
var payload = new JwtPayload
|
var payload = new JwtPayload
|
||||||
{
|
{
|
||||||
{ "sub", BitConverter.ToString(rawUserId).Replace("-", "").ToLower() },
|
{ "sub", Convert.ToHexString(rawUserId).ToLower() },
|
||||||
{ "aud", "ed9e2f05d286f7b8" },
|
{ "aud", "ed9e2f05d286f7b8" },
|
||||||
{ "di", BitConverter.ToString(deviceId).Replace("-", "").ToLower() },
|
{ "di", Convert.ToHexString(deviceId).ToLower() },
|
||||||
{ "sn", "XAW10000000000" },
|
{ "sn", "XAW10000000000" },
|
||||||
{ "bs:did", BitConverter.ToString(deviceAccountId).Replace("-", "").ToLower() },
|
{ "bs:did", Convert.ToHexString(deviceAccountId).ToLower() },
|
||||||
{ "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" },
|
{ "iss", "https://e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com" },
|
||||||
{ "typ", "id_token" },
|
{ "typ", "id_token" },
|
||||||
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
||||||
|
@@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
||||||
string hash = BitConverter.ToString(SHA256.HashData(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20);
|
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId))).Remove(0x20);
|
||||||
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
|
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
|
||||||
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user