Compare commits

...

2 Commits

Author SHA1 Message Date
gdkchan
633c5ec330 Extend uses count from ushort to uint on Operand Data structure (#3374) 2022-06-05 14:15:27 -03:00
gdkchan
a3e7bb8eb4 Copy dependency for multisample and non-multisample textures (#3382)
* Use copy dependency for textures that differs in multisample but are otherwise compatible

* Remove allowMs flag as it's no longer required for correctness, it's just an optimization now

* Dispose intermmediate pool
2022-06-05 14:06:47 -03:00
8 changed files with 292 additions and 34 deletions

View File

@@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation
public byte Kind; public byte Kind;
public byte Type; public byte Type;
public byte SymbolType; public byte SymbolType;
public byte Padding; // Unused space.
public ushort AssignmentsCount; public ushort AssignmentsCount;
public ushort AssignmentsCapacity; public ushort AssignmentsCapacity;
public ushort UsesCount; public uint UsesCount;
public ushort UsesCapacity; public uint UsesCapacity;
public Operation* Assignments; public Operation* Assignments;
public Operation* Uses; public Operation* Uses;
public ulong Value; public ulong Value;
@@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation
{ {
Debug.Assert(Kind != OperandKind.Memory); Debug.Assert(Kind != OperandKind.Memory);
return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount); return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
} }
} }
public int UsesCount => _data->UsesCount; public int UsesCount => (int)_data->UsesCount;
public int AssignmentsCount => _data->AssignmentsCount; public int AssignmentsCount => _data->AssignmentsCount;
public bool Relocatable => Symbol.Type != SymbolType.None; public bool Relocatable => Symbol.Type != SymbolType.None;
@@ -178,7 +179,7 @@ namespace ARMeilleure.IntermediateRepresentation
{ {
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity); Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
} }
if (index != default) if (index != default)
{ {
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity); Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
@@ -265,6 +266,13 @@ namespace ARMeilleure.IntermediateRepresentation
data = Allocators.References.Allocate<T>(initialCapacity); data = Allocators.References.Allocate<T>(initialCapacity);
} }
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
{
count = 0;
capacity = initialCapacity;
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
{ {
if (count < capacity) if (count < capacity)
@@ -294,6 +302,40 @@ namespace ARMeilleure.IntermediateRepresentation
} }
} }
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
{
if (count < capacity)
{
data[count++] = item;
return;
}
// Could not add item in the fast path, fallback onto the slow path.
ExpandAdd(item, ref data, ref count, ref capacity);
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
{
uint newCount = checked(count + 1);
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
if (newCapacity <= capacity)
{
throw new OverflowException();
}
var oldSpan = new Span<T>(data, (int)count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
oldSpan.CopyTo(new Span<T>(data, (int)count));
data[count] = item;
count = newCount;
}
}
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
{ {
var span = new Span<T>(data, count); var span = new Span<T>(data, count);
@@ -314,6 +356,26 @@ namespace ARMeilleure.IntermediateRepresentation
} }
} }
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
{
var span = new Span<T>(data, (int)count);
for (int i = 0; i < span.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(span[i], item))
{
if (i + 1 < count)
{
span.Slice(i + 1).CopyTo(span.Slice(i));
}
count--;
return;
}
}
}
public override int GetHashCode() public override int GetHashCode()
{ {
if (Kind == OperandKind.LocalVariable) if (Kind == OperandKind.LocalVariable)

View File

@@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL
CubemapArray, CubemapArray,
TextureBuffer TextureBuffer
} }
public static class TargetExtensions
{
public static bool IsMultisample(this Target target)
{
return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray;
}
}
} }

View File

@@ -1136,32 +1136,22 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="range">Texture view physical memory ranges</param> /// <param name="range">Texture view physical memory ranges</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="allowMs">Indicates that multisample textures are allowed to match non-multisample requested textures</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, bool allowMs, out int firstLayer, out int firstLevel) public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
{ {
TextureViewCompatibility result = TextureViewCompatibility.Full; TextureViewCompatibility result = TextureViewCompatibility.Full;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
if (result != TextureViewCompatibility.Incompatible) if (result != TextureViewCompatibility.Incompatible)
{ {
bool msTargetCompatible = false; result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (allowMs) bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
{ {
msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D; result = TextureViewCompatibility.Incompatible;
}
if (!msTargetCompatible)
{
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
{
result = TextureViewCompatibility.Incompatible;
}
} }
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat) if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)

View File

@@ -547,7 +547,6 @@ namespace Ryujinx.Graphics.Gpu.Image
range.Value, range.Value,
sizeInfo.LayerSize, sizeInfo.LayerSize,
_context.Capabilities, _context.Capabilities,
flags.HasFlag(TextureSearchFlags.ForCopy),
out int firstLayer, out int firstLayer,
out int firstLevel); out int firstLevel);
@@ -662,7 +661,6 @@ namespace Ryujinx.Graphics.Gpu.Image
overlap.Range, overlap.Range,
overlap.LayerSize, overlap.LayerSize,
_context.Capabilities, _context.Capabilities,
false,
out int firstLayer, out int firstLayer,
out int firstLevel); out int firstLevel);

View File

@@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using System; using System;
using System.Numerics;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
@@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image
case Target.Texture2DMultisample: case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray: case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
{
return TextureViewCompatibility.CopyOnly;
}
result = rhs.Target == Target.Texture2DMultisample || result = rhs.Target == Target.Texture2DMultisample ||
rhs.Target == Target.Texture2DMultisampleArray; rhs.Target == Target.Texture2DMultisampleArray;
break; break;

View File

@@ -0,0 +1,98 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL.Image
{
class IntermmediatePool : IDisposable
{
private readonly Renderer _renderer;
private readonly List<TextureView> _entries;
public IntermmediatePool(Renderer renderer)
{
_renderer = renderer;
_entries = new List<TextureView>();
}
public TextureView GetOrCreateWithAtLeast(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
TextureView entry;
for (int i = 0; i < _entries.Count; i++)
{
entry = _entries[i];
if (entry.Target == target && entry.Format == format)
{
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
{
width = Math.Max(width, entry.Width);
height = Math.Max(height, entry.Height);
depth = Math.Max(depth, entry.Info.Depth);
levels = Math.Max(levels, entry.Info.Levels);
entry.Dispose();
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries[i] = entry;
}
return entry;
}
}
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries.Add(entry);
return entry;
}
private TextureView CreateNew(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
width,
height,
depth,
levels,
1,
blockWidth,
blockHeight,
bytesPerPixel,
format,
DepthStencilMode.Depth,
target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 1f);
}
public void Dispose()
{
foreach (TextureView entry in _entries)
{
entry.Dispose();
}
_entries.Clear();
}
}
}

View File

@@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
private readonly Renderer _renderer; private readonly Renderer _renderer;
public IntermmediatePool IntermmediatePool { get; }
private int _srcFramebuffer; private int _srcFramebuffer;
private int _dstFramebuffer; private int _dstFramebuffer;
@@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public TextureCopy(Renderer renderer) public TextureCopy(Renderer renderer)
{ {
_renderer = renderer; _renderer = renderer;
IntermmediatePool = new IntermmediatePool(renderer);
} }
public void Copy( public void Copy(
@@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image
TextureView dst, TextureView dst,
Extents2D srcRegion, Extents2D srcRegion,
Extents2D dstRegion, Extents2D dstRegion,
bool linearFilter) bool linearFilter,
int srcLayer = 0,
int dstLayer = 0,
int srcLevel = 0,
int dstLevel = 0)
{
int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel);
int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer);
Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels);
}
public void Copy(
TextureView src,
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int layers,
int levels)
{ {
TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src; TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
@@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
int levels = Math.Min(src.Info.Levels, dst.Info.Levels); if (srcLevel != 0)
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers()); {
srcRegion = srcRegion.Reduce(srcLevel);
}
if (dstLevel != 0)
{
dstRegion = dstRegion.Reduce(dstLevel);
}
for (int level = 0; level < levels; level++) for (int level = 0; level < levels; level++)
{ {
for (int layer = 0; layer < layers; layer++) for (int layer = 0; layer < layers; layer++)
{ {
if (layers > 1) if ((srcLayer | dstLayer) != 0 || layers > 1)
{ {
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer); Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
} }
else else
{ {
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level); Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
} }
ClearBufferMask mask = GetMask(src.Format); ClearBufferMask mask = GetMask(src.Format);
@@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
_copyPboHandle = 0; _copyPboHandle = 0;
} }
IntermmediatePool.Dispose();
} }
} }
} }

View File

@@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
TextureView destinationView = (TextureView)destination; TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel); if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Width,
Height,
Info.Depth,
Info.Levels);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
}
} }
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
{ {
TextureView destinationView = (TextureView)destination; TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1); if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Math.Max(1, Width >> srcLevel),
Math.Max(1, Height >> srcLevel),
1,
1);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
}
}
private static Target GetIntermmediateTarget(Target srcTarget)
{
return srcTarget switch
{
Target.Texture2D => Target.Texture2DMultisample,
Target.Texture2DArray => Target.Texture2DMultisampleArray,
Target.Texture2DMultisampleArray => Target.Texture2DArray,
_ => Target.Texture2D
};
} }
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)