Cache changes
This commit is contained in:
parent
5b49938c9b
commit
b0d953d6bb
18 changed files with 1414 additions and 518 deletions
|
@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
public struct GalVertexAttrib
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
public bool IsConst { get; private set; }
|
||||
public int Offset { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
public bool IsConst { get; private set; }
|
||||
public int ArrayIndex { get; private set; }
|
||||
public int Offset { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
public GalVertexAttribSize Size { get; private set; }
|
||||
public GalVertexAttribType Type { get; private set; }
|
||||
|
@ -13,21 +13,21 @@ namespace Ryujinx.Graphics.Gal
|
|||
public bool IsBgra { get; private set; }
|
||||
|
||||
public GalVertexAttrib(
|
||||
int Index,
|
||||
bool IsConst,
|
||||
int ArrayIndex,
|
||||
int Offset,
|
||||
byte[] Data,
|
||||
GalVertexAttribSize Size,
|
||||
GalVertexAttribType Type,
|
||||
bool IsBgra)
|
||||
{
|
||||
this.Index = Index;
|
||||
this.IsConst = IsConst;
|
||||
this.Data = Data;
|
||||
this.Offset = Offset;
|
||||
this.Size = Size;
|
||||
this.Type = Type;
|
||||
this.IsBgra = IsBgra;
|
||||
this.IsConst = IsConst;
|
||||
this.Data = Data;
|
||||
this.ArrayIndex = ArrayIndex;
|
||||
this.Offset = Offset;
|
||||
this.Size = Size;
|
||||
this.Type = Type;
|
||||
this.IsBgra = IsBgra;
|
||||
}
|
||||
}
|
||||
}
|
43
Ryujinx.Graphics/Gal/GalVertexAttribArray.cs
Normal file
43
Ryujinx.Graphics/Gal/GalVertexAttribArray.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public struct GalVertexAttribArray : IEquatable<GalVertexAttribArray>
|
||||
{
|
||||
public bool Enabled { get; private set; }
|
||||
public long VboKey { get; private set; }
|
||||
public int Stride { get; private set; }
|
||||
public int Divisor { get; private set; }
|
||||
|
||||
public GalVertexAttribArray(long vboKey, int stride, int divisor)
|
||||
{
|
||||
Enabled = true;
|
||||
VboKey = vboKey;
|
||||
Stride = stride;
|
||||
Divisor = divisor;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is GalVertexAttribArray array))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals(array);
|
||||
}
|
||||
|
||||
public bool Equals(GalVertexAttribArray array)
|
||||
{
|
||||
return Enabled == array.Enabled &&
|
||||
VboKey == array.VboKey &&
|
||||
Stride == array.Stride &&
|
||||
Divisor == array.Divisor;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Enabled, VboKey, Stride, Divisor);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,11 +7,9 @@ namespace Ryujinx.Graphics.Gal
|
|||
void LockCache();
|
||||
void UnlockCache();
|
||||
|
||||
void Create(long Key, long Size);
|
||||
void Create(long key, IntPtr hostAddress, int size);
|
||||
void Create(long key, byte[] buffer);
|
||||
|
||||
bool IsCached(long Key, long Size);
|
||||
|
||||
void SetData(long Key, long Size, IntPtr HostAddress);
|
||||
void SetData(long Key, byte[] Data);
|
||||
bool IsCached(long Key, int Size);
|
||||
}
|
||||
}
|
|
@ -17,15 +17,21 @@ namespace Ryujinx.Graphics.Gal
|
|||
float Depth,
|
||||
int Stencil);
|
||||
|
||||
bool IsVboCached(long Key, long DataSize);
|
||||
bool TryBindVao(ReadOnlySpan<int> rawAttributes, GalVertexAttribArray[] arrays);
|
||||
|
||||
bool IsIboCached(long Key, long DataSize);
|
||||
void CreateVao(
|
||||
ReadOnlySpan<int> rawAttributes,
|
||||
GalVertexAttrib[] attributes,
|
||||
GalVertexAttribArray[] arrays);
|
||||
|
||||
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
|
||||
void CreateVbo(long Key, byte[] Data);
|
||||
bool IsVboCached(long key, int size);
|
||||
bool IsIboCached(long key, int size, out long vertexCount);
|
||||
|
||||
void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
|
||||
void CreateIbo(long Key, int DataSize, byte[] Buffer);
|
||||
void CreateVbo(long key, IntPtr hostAddress, int size);
|
||||
void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount);
|
||||
|
||||
void CreateVbo(long key, byte[] buffer);
|
||||
void CreateIbo(long key, byte[] buffer, long vertexCount);
|
||||
|
||||
void SetIndexArray(int Size, GalIndexFormat Format);
|
||||
|
||||
|
|
|
@ -37,7 +37,5 @@ namespace Ryujinx.Graphics.Gal
|
|||
int DstY0,
|
||||
int DstX1,
|
||||
int DstY1);
|
||||
|
||||
void Reinterpret(long Key, GalImage NewImage);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gal
|
|||
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
|
||||
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
|
||||
|
||||
void SetExtraData(float FlipX, float FlipY, int Instance);
|
||||
|
||||
void Bind(long Key);
|
||||
|
||||
void Unbind(GalShaderType Type);
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLCachedResource<T>
|
||||
{
|
||||
public delegate void DeleteValue(T Value);
|
||||
|
||||
private const int MinTimeDelta = 5 * 60000;
|
||||
private const int MaxRemovalsPerRun = 10;
|
||||
|
||||
private struct CacheBucket
|
||||
{
|
||||
public T Value { get; private set; }
|
||||
|
||||
public LinkedListNode<long> Node { get; private set; }
|
||||
|
||||
public long DataSize { get; private set; }
|
||||
|
||||
public long Timestamp { get; private set; }
|
||||
|
||||
public CacheBucket(T Value, long DataSize, LinkedListNode<long> Node)
|
||||
{
|
||||
this.Value = Value;
|
||||
this.DataSize = DataSize;
|
||||
this.Node = Node;
|
||||
|
||||
Timestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<long, CacheBucket> Cache;
|
||||
|
||||
private LinkedList<long> SortedCache;
|
||||
|
||||
private DeleteValue DeleteValueCallback;
|
||||
|
||||
private Queue<T> DeletePending;
|
||||
|
||||
private bool Locked;
|
||||
|
||||
private long MaxSize;
|
||||
private long TotalSize;
|
||||
|
||||
public OGLCachedResource(DeleteValue DeleteValueCallback, long MaxSize)
|
||||
{
|
||||
this.MaxSize = MaxSize;
|
||||
|
||||
if (DeleteValueCallback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(DeleteValueCallback));
|
||||
}
|
||||
|
||||
this.DeleteValueCallback = DeleteValueCallback;
|
||||
|
||||
Cache = new Dictionary<long, CacheBucket>();
|
||||
|
||||
SortedCache = new LinkedList<long>();
|
||||
|
||||
DeletePending = new Queue<T>();
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
Locked = true;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
Locked = false;
|
||||
|
||||
while (DeletePending.TryDequeue(out T Value))
|
||||
{
|
||||
DeleteValueCallback(Value);
|
||||
}
|
||||
|
||||
ClearCacheIfNeeded();
|
||||
}
|
||||
|
||||
public void AddOrUpdate(long Key, T Value, long Size)
|
||||
{
|
||||
if (!Locked)
|
||||
{
|
||||
ClearCacheIfNeeded();
|
||||
}
|
||||
|
||||
LinkedListNode<long> Node = SortedCache.AddLast(Key);
|
||||
|
||||
CacheBucket NewBucket = new CacheBucket(Value, Size, Node);
|
||||
|
||||
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
||||
{
|
||||
if (Locked)
|
||||
{
|
||||
DeletePending.Enqueue(Bucket.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteValueCallback(Bucket.Value);
|
||||
}
|
||||
|
||||
SortedCache.Remove(Bucket.Node);
|
||||
|
||||
TotalSize -= Bucket.DataSize;
|
||||
|
||||
Cache[Key] = NewBucket;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.Add(Key, NewBucket);
|
||||
}
|
||||
|
||||
TotalSize += Size;
|
||||
}
|
||||
|
||||
public bool TryGetValue(long Key, out T Value)
|
||||
{
|
||||
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
||||
{
|
||||
Value = Bucket.Value;
|
||||
|
||||
SortedCache.Remove(Bucket.Node);
|
||||
|
||||
LinkedListNode<long> Node = SortedCache.AddLast(Key);
|
||||
|
||||
Cache[Key] = new CacheBucket(Value, Bucket.DataSize, Node);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Value = default(T);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetSize(long Key, out long Size)
|
||||
{
|
||||
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
|
||||
{
|
||||
Size = Bucket.DataSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Size = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ClearCacheIfNeeded()
|
||||
{
|
||||
long Timestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
int Count = 0;
|
||||
|
||||
while (Count++ < MaxRemovalsPerRun)
|
||||
{
|
||||
LinkedListNode<long> Node = SortedCache.First;
|
||||
|
||||
if (Node == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
CacheBucket Bucket = Cache[Node.Value];
|
||||
|
||||
long TimeDelta = Timestamp - Bucket.Timestamp;
|
||||
|
||||
if (TimeDelta <= MinTimeDelta && !UnderMemoryPressure())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
SortedCache.Remove(Node);
|
||||
|
||||
Cache.Remove(Node.Value);
|
||||
|
||||
DeleteValueCallback(Bucket.Value);
|
||||
|
||||
TotalSize -= Bucket.DataSize;
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnderMemoryPressure()
|
||||
{
|
||||
return TotalSize >= MaxSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLConstBuffer : IGalConstBuffer
|
||||
{
|
||||
|
||||
private OGLCachedResource<OGLStreamBuffer> Cache;
|
||||
private OGLResourceCache<int, OGLStreamBuffer> Cache;
|
||||
|
||||
public OGLConstBuffer()
|
||||
{
|
||||
Cache = new OGLCachedResource<OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.ConstBufferLimit);
|
||||
Cache = new OGLResourceCache<int, OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.ConstBufferLimit);
|
||||
}
|
||||
|
||||
private static void DeleteBuffer(OGLStreamBuffer Buffer)
|
||||
{
|
||||
Buffer.Dispose();
|
||||
}
|
||||
|
||||
public void LockCache()
|
||||
|
@ -23,51 +27,45 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
Cache.Unlock();
|
||||
}
|
||||
|
||||
public void Create(long Key, long Size)
|
||||
public bool IsCached(long key, int size)
|
||||
{
|
||||
OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
|
||||
|
||||
Cache.AddOrUpdate(Key, Buffer, Size);
|
||||
return Cache.TryGetSize(key, out int cbSize) && cbSize >= size;
|
||||
}
|
||||
|
||||
public bool IsCached(long Key, long Size)
|
||||
public void Create(long key, IntPtr hostAddress, int size)
|
||||
{
|
||||
return Cache.TryGetSize(Key, out long CachedSize) && CachedSize == Size;
|
||||
GetBuffer(key, size).SetData(hostAddress, size);
|
||||
}
|
||||
|
||||
public void SetData(long Key, long Size, IntPtr HostAddress)
|
||||
public void Create(long key, byte[] buffer)
|
||||
{
|
||||
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
|
||||
GetBuffer(key, buffer.Length).SetData(buffer);
|
||||
}
|
||||
|
||||
public bool TryGetUbo(long key, out int uboHandle)
|
||||
{
|
||||
if (Cache.TryGetValue(key, out OGLStreamBuffer buffer))
|
||||
{
|
||||
Buffer.SetData(Size, HostAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(long Key, byte[] Data)
|
||||
{
|
||||
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
|
||||
{
|
||||
Buffer.SetData(Data);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetUbo(long Key, out int UboHandle)
|
||||
{
|
||||
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
|
||||
{
|
||||
UboHandle = Buffer.Handle;
|
||||
uboHandle = buffer.Handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UboHandle = 0;
|
||||
uboHandle = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void DeleteBuffer(OGLStreamBuffer Buffer)
|
||||
private OGLStreamBuffer GetBuffer(long Key, int Size)
|
||||
{
|
||||
Buffer.Dispose();
|
||||
if (!Cache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer))
|
||||
{
|
||||
Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
|
||||
|
||||
Cache.AddOrUpdate(Key, Size, Buffer, Size);
|
||||
}
|
||||
|
||||
return Buffer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,116 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLRasterizer : IGalRasterizer
|
||||
{
|
||||
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
|
||||
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
|
||||
private static Dictionary<GalVertexAttribSize, int> AttribElements =
|
||||
new Dictionary<GalVertexAttribSize, int>()
|
||||
{
|
||||
{ GalVertexAttribSize._32_32_32_32, 4 },
|
||||
{ GalVertexAttribSize._32_32_32, 3 },
|
||||
{ GalVertexAttribSize._16_16_16_16, 4 },
|
||||
{ GalVertexAttribSize._32_32, 2 },
|
||||
{ GalVertexAttribSize._16_16_16, 3 },
|
||||
{ GalVertexAttribSize._8_8_8_8, 4 },
|
||||
{ GalVertexAttribSize._16_16, 2 },
|
||||
{ GalVertexAttribSize._32, 1 },
|
||||
{ GalVertexAttribSize._8_8_8, 3 },
|
||||
{ GalVertexAttribSize._8_8, 2 },
|
||||
{ GalVertexAttribSize._16, 1 },
|
||||
{ GalVertexAttribSize._8, 1 },
|
||||
{ GalVertexAttribSize._10_10_10_2, 4 },
|
||||
{ GalVertexAttribSize._11_11_10, 3 }
|
||||
};
|
||||
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> FloatAttribTypes =
|
||||
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
|
||||
{
|
||||
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float },
|
||||
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float },
|
||||
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat },
|
||||
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Float },
|
||||
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat },
|
||||
{ GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat },
|
||||
{ GalVertexAttribSize._32, VertexAttribPointerType.Float },
|
||||
{ GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat }
|
||||
};
|
||||
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
|
||||
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
|
||||
{
|
||||
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int },
|
||||
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int },
|
||||
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short },
|
||||
{ GalVertexAttribSize._32_32, VertexAttribPointerType.Int },
|
||||
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short },
|
||||
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte },
|
||||
{ GalVertexAttribSize._16_16, VertexAttribPointerType.Short },
|
||||
{ GalVertexAttribSize._32, VertexAttribPointerType.Int },
|
||||
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte },
|
||||
{ GalVertexAttribSize._8_8, VertexAttribPointerType.Byte },
|
||||
{ GalVertexAttribSize._16, VertexAttribPointerType.Short },
|
||||
{ GalVertexAttribSize._8, VertexAttribPointerType.Byte },
|
||||
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int2101010Rev }
|
||||
};
|
||||
|
||||
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> UnsignedAttribTypes =
|
||||
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
|
||||
{
|
||||
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._32_32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._32_32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._16_16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._16_16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._32, VertexAttribPointerType.UnsignedInt },
|
||||
{ GalVertexAttribSize._8_8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._8_8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._16, VertexAttribPointerType.UnsignedShort },
|
||||
{ GalVertexAttribSize._8, VertexAttribPointerType.UnsignedByte },
|
||||
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.UnsignedInt2101010Rev },
|
||||
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev }
|
||||
};
|
||||
|
||||
private int[] VertexBuffers;
|
||||
|
||||
private OGLCachedResource<int> VboCache;
|
||||
private OGLCachedResource<int> IboCache;
|
||||
private struct CachedVao
|
||||
{
|
||||
public int[] Attributes { get; }
|
||||
|
||||
public GalVertexAttribArray[] Arrays { get; }
|
||||
|
||||
public int Handle { get; }
|
||||
|
||||
public CachedVao(int[] attributes, GalVertexAttribArray[] arrays)
|
||||
{
|
||||
Attributes = attributes;
|
||||
Arrays = arrays;
|
||||
|
||||
Handle = GL.GenVertexArray();
|
||||
}
|
||||
}
|
||||
|
||||
private OGLResourceCache<int, CachedVao> VaoCache;
|
||||
|
||||
private OGLResourceCache<int, OGLStreamBuffer> VboCache;
|
||||
|
||||
private class CachedIbo
|
||||
{
|
||||
public OGLStreamBuffer Buffer { get; }
|
||||
|
||||
public long VertexCount { get; set; }
|
||||
|
||||
public CachedIbo(OGLStreamBuffer buffer)
|
||||
{
|
||||
Buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
private OGLResourceCache<int, CachedIbo> IboCache;
|
||||
|
||||
private struct IbInfo
|
||||
{
|
||||
|
@ -27,33 +126,53 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{
|
||||
VertexBuffers = new int[32];
|
||||
|
||||
VboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxVertexBufferCacheSize);
|
||||
IboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxIndexBufferCacheSize);
|
||||
VaoCache = new OGLResourceCache<int, CachedVao>(DeleteVao, OGLResourceLimits.VertexArrayLimit);
|
||||
|
||||
VboCache = new OGLResourceCache<int, OGLStreamBuffer>(DeleteBuffer, OGLResourceLimits.VertexBufferLimit);
|
||||
|
||||
IboCache = new OGLResourceCache<int, CachedIbo>(DeleteIbo, OGLResourceLimits.IndexBufferLimit);
|
||||
|
||||
IndexBuffer = new IbInfo();
|
||||
}
|
||||
|
||||
private static void DeleteVao(CachedVao vao)
|
||||
{
|
||||
GL.DeleteVertexArray(vao.Handle);
|
||||
}
|
||||
|
||||
private static void DeleteBuffer(OGLStreamBuffer buffer)
|
||||
{
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
private static void DeleteIbo(CachedIbo ibo)
|
||||
{
|
||||
ibo.Buffer.Dispose();
|
||||
}
|
||||
|
||||
public void LockCaches()
|
||||
{
|
||||
VaoCache.Lock();
|
||||
VboCache.Lock();
|
||||
IboCache.Lock();
|
||||
}
|
||||
|
||||
public void UnlockCaches()
|
||||
{
|
||||
VaoCache.Unlock();
|
||||
VboCache.Unlock();
|
||||
IboCache.Unlock();
|
||||
}
|
||||
|
||||
public void ClearBuffers(
|
||||
GalClearBufferFlags Flags,
|
||||
int Attachment,
|
||||
float Red,
|
||||
float Green,
|
||||
float Blue,
|
||||
float Alpha,
|
||||
float Depth,
|
||||
int Stencil)
|
||||
int Attachment,
|
||||
float Red,
|
||||
float Green,
|
||||
float Blue,
|
||||
float Alpha,
|
||||
float Depth,
|
||||
int Stencil)
|
||||
{
|
||||
GL.ColorMask(
|
||||
Attachment,
|
||||
|
@ -78,62 +197,347 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsVboCached(long Key, long DataSize)
|
||||
public bool IsVboCached(long key, int size)
|
||||
{
|
||||
return VboCache.TryGetSize(Key, out long Size) && Size == DataSize;
|
||||
return VboCache.TryGetSize(key, out int vbSize) && vbSize >= size;
|
||||
}
|
||||
|
||||
public bool IsIboCached(long Key, long DataSize)
|
||||
public bool IsIboCached(long key, int size, out long vertexCount)
|
||||
{
|
||||
return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
|
||||
if (IboCache.TryGetSizeAndValue(key, out int ibSize, out CachedIbo ibo) && ibSize >= size)
|
||||
{
|
||||
vertexCount = ibo.VertexCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
vertexCount = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CreateVbo(long Key, int DataSize, IntPtr HostAddress)
|
||||
public bool TryBindVao(ReadOnlySpan<int> rawAttributes, GalVertexAttribArray[] arrays)
|
||||
{
|
||||
int Handle = GL.GenBuffer();
|
||||
long hash = CalculateHash(arrays);
|
||||
|
||||
VboCache.AddOrUpdate(Key, Handle, DataSize);
|
||||
if (!VaoCache.TryGetValue(hash, out CachedVao vao))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr Length = new IntPtr(DataSize);
|
||||
if (rawAttributes.Length != vao.Attributes.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
|
||||
if (arrays.Length != vao.Arrays.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < rawAttributes.Length; index++)
|
||||
{
|
||||
if (rawAttributes[index] != vao.Attributes[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < arrays.Length; index++)
|
||||
{
|
||||
if (!arrays[index].Equals(vao.Arrays[index]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
GL.BindVertexArray(vao.Handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateVbo(long Key, byte[] Data)
|
||||
public void CreateVao(
|
||||
ReadOnlySpan<int> rawAttributes,
|
||||
GalVertexAttrib[] attributes,
|
||||
GalVertexAttribArray[] arrays)
|
||||
{
|
||||
int Handle = GL.GenBuffer();
|
||||
CachedVao vao = new CachedVao(rawAttributes.ToArray(), arrays);
|
||||
|
||||
VboCache.AddOrUpdate(Key, Handle, Data.Length);
|
||||
long hash = CalculateHash(arrays);
|
||||
|
||||
IntPtr Length = new IntPtr(Data.Length);
|
||||
VaoCache.AddOrUpdate(hash, 1, vao, 1);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, Length, Data, BufferUsageHint.StreamDraw);
|
||||
GL.BindVertexArray(vao.Handle);
|
||||
|
||||
for (int index = 0; index < attributes.Length; index++)
|
||||
{
|
||||
GalVertexAttrib attrib = attributes[index];
|
||||
|
||||
GalVertexAttribArray array = arrays[attrib.ArrayIndex];
|
||||
|
||||
//Skip uninitialized attributes.
|
||||
if (attrib.Size == 0 || !array.Enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!VboCache.TryGetValue(array.VboKey, out OGLStreamBuffer vbo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VboCache.AddDependency(array.VboKey, VaoCache, hash);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo.Handle);
|
||||
|
||||
bool Unsigned =
|
||||
attrib.Type == GalVertexAttribType.Unorm ||
|
||||
attrib.Type == GalVertexAttribType.Uint ||
|
||||
attrib.Type == GalVertexAttribType.Uscaled;
|
||||
|
||||
bool Normalize =
|
||||
attrib.Type == GalVertexAttribType.Snorm ||
|
||||
attrib.Type == GalVertexAttribType.Unorm;
|
||||
|
||||
VertexAttribPointerType Type = 0;
|
||||
|
||||
if (attrib.Type == GalVertexAttribType.Float)
|
||||
{
|
||||
Type = GetType(FloatAttribTypes, attrib);
|
||||
}
|
||||
else if (Unsigned)
|
||||
{
|
||||
Type = GetType(UnsignedAttribTypes, attrib);
|
||||
}
|
||||
else
|
||||
{
|
||||
Type = GetType(SignedAttribTypes, attrib);
|
||||
}
|
||||
|
||||
if (!AttribElements.TryGetValue(attrib.Size, out int Size))
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid attribute size \"{attrib.Size}\".");
|
||||
}
|
||||
|
||||
int Offset = attrib.Offset;
|
||||
|
||||
if (array.Stride != 0)
|
||||
{
|
||||
GL.EnableVertexAttribArray(index);
|
||||
|
||||
if (attrib.Type == GalVertexAttribType.Sint ||
|
||||
attrib.Type == GalVertexAttribType.Uint)
|
||||
{
|
||||
IntPtr Pointer = new IntPtr(Offset);
|
||||
|
||||
VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
|
||||
|
||||
GL.VertexAttribIPointer(index, Size, IType, array.Stride, Pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.VertexAttribPointer(index, Size, Type, Normalize, array.Stride, Offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.DisableVertexAttribArray(index);
|
||||
|
||||
SetConstAttrib(attrib, (uint)index);
|
||||
}
|
||||
|
||||
if (array.Divisor != 0)
|
||||
{
|
||||
GL.VertexAttribDivisor(index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.VertexAttribDivisor(index, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
|
||||
private long CalculateHash(GalVertexAttribArray[] arrays)
|
||||
{
|
||||
int Handle = GL.GenBuffer();
|
||||
if (arrays.Length == 1)
|
||||
{
|
||||
return arrays[0].VboKey;
|
||||
}
|
||||
|
||||
IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
|
||||
long hash = 17;
|
||||
|
||||
IntPtr Length = new IntPtr(DataSize);
|
||||
for (int index = 0; index < arrays.Length; index++)
|
||||
{
|
||||
hash = hash * 23 + arrays[index].VboKey;
|
||||
}
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void CreateIbo(long Key, int DataSize, byte[] Buffer)
|
||||
private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> Dict, GalVertexAttrib Attrib)
|
||||
{
|
||||
int Handle = GL.GenBuffer();
|
||||
if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type))
|
||||
{
|
||||
ThrowUnsupportedAttrib(Attrib);
|
||||
}
|
||||
|
||||
IboCache.AddOrUpdate(Key, Handle, DataSize);
|
||||
return Type;
|
||||
}
|
||||
|
||||
IntPtr Length = new IntPtr(Buffer.Length);
|
||||
private unsafe static void SetConstAttrib(GalVertexAttrib Attrib, uint Index)
|
||||
{
|
||||
if (Attrib.Size == GalVertexAttribSize._10_10_10_2 ||
|
||||
Attrib.Size == GalVertexAttribSize._11_11_10)
|
||||
{
|
||||
ThrowUnsupportedAttrib(Attrib);
|
||||
}
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
||||
fixed (byte* Ptr = Attrib.Data)
|
||||
{
|
||||
if (Attrib.Type == GalVertexAttribType.Unorm)
|
||||
{
|
||||
switch (Attrib.Size)
|
||||
{
|
||||
case GalVertexAttribSize._8:
|
||||
case GalVertexAttribSize._8_8:
|
||||
case GalVertexAttribSize._8_8_8:
|
||||
case GalVertexAttribSize._8_8_8_8:
|
||||
GL.VertexAttrib4N(Index, Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._16:
|
||||
case GalVertexAttribSize._16_16:
|
||||
case GalVertexAttribSize._16_16_16:
|
||||
case GalVertexAttribSize._16_16_16_16:
|
||||
GL.VertexAttrib4N(Index, (ushort*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._32:
|
||||
case GalVertexAttribSize._32_32:
|
||||
case GalVertexAttribSize._32_32_32:
|
||||
case GalVertexAttribSize._32_32_32_32:
|
||||
GL.VertexAttrib4N(Index, (uint*)Ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Attrib.Type == GalVertexAttribType.Snorm)
|
||||
{
|
||||
switch (Attrib.Size)
|
||||
{
|
||||
case GalVertexAttribSize._8:
|
||||
case GalVertexAttribSize._8_8:
|
||||
case GalVertexAttribSize._8_8_8:
|
||||
case GalVertexAttribSize._8_8_8_8:
|
||||
GL.VertexAttrib4N(Index, (sbyte*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._16:
|
||||
case GalVertexAttribSize._16_16:
|
||||
case GalVertexAttribSize._16_16_16:
|
||||
case GalVertexAttribSize._16_16_16_16:
|
||||
GL.VertexAttrib4N(Index, (short*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._32:
|
||||
case GalVertexAttribSize._32_32:
|
||||
case GalVertexAttribSize._32_32_32:
|
||||
case GalVertexAttribSize._32_32_32_32:
|
||||
GL.VertexAttrib4N(Index, (int*)Ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Attrib.Type == GalVertexAttribType.Uint)
|
||||
{
|
||||
switch (Attrib.Size)
|
||||
{
|
||||
case GalVertexAttribSize._8:
|
||||
case GalVertexAttribSize._8_8:
|
||||
case GalVertexAttribSize._8_8_8:
|
||||
case GalVertexAttribSize._8_8_8_8:
|
||||
GL.VertexAttribI4(Index, Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._16:
|
||||
case GalVertexAttribSize._16_16:
|
||||
case GalVertexAttribSize._16_16_16:
|
||||
case GalVertexAttribSize._16_16_16_16:
|
||||
GL.VertexAttribI4(Index, (ushort*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._32:
|
||||
case GalVertexAttribSize._32_32:
|
||||
case GalVertexAttribSize._32_32_32:
|
||||
case GalVertexAttribSize._32_32_32_32:
|
||||
GL.VertexAttribI4(Index, (uint*)Ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Attrib.Type == GalVertexAttribType.Sint)
|
||||
{
|
||||
switch (Attrib.Size)
|
||||
{
|
||||
case GalVertexAttribSize._8:
|
||||
case GalVertexAttribSize._8_8:
|
||||
case GalVertexAttribSize._8_8_8:
|
||||
case GalVertexAttribSize._8_8_8_8:
|
||||
GL.VertexAttribI4(Index, (sbyte*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._16:
|
||||
case GalVertexAttribSize._16_16:
|
||||
case GalVertexAttribSize._16_16_16:
|
||||
case GalVertexAttribSize._16_16_16_16:
|
||||
GL.VertexAttribI4(Index, (short*)Ptr);
|
||||
break;
|
||||
|
||||
case GalVertexAttribSize._32:
|
||||
case GalVertexAttribSize._32_32:
|
||||
case GalVertexAttribSize._32_32_32:
|
||||
case GalVertexAttribSize._32_32_32_32:
|
||||
GL.VertexAttribI4(Index, (int*)Ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Attrib.Type == GalVertexAttribType.Float)
|
||||
{
|
||||
switch (Attrib.Size)
|
||||
{
|
||||
case GalVertexAttribSize._32:
|
||||
case GalVertexAttribSize._32_32:
|
||||
case GalVertexAttribSize._32_32_32:
|
||||
case GalVertexAttribSize._32_32_32_32:
|
||||
GL.VertexAttrib4(Index, (float*)Ptr);
|
||||
break;
|
||||
|
||||
default: ThrowUnsupportedAttrib(Attrib); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib)
|
||||
{
|
||||
throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
|
||||
}
|
||||
|
||||
public void CreateVbo(long key, IntPtr hostAddress, int size)
|
||||
{
|
||||
GetVbo(key, size).SetData(hostAddress, size);
|
||||
}
|
||||
|
||||
public void CreateIbo(long key, IntPtr hostAddress, int size, long vertexCount)
|
||||
{
|
||||
GetIbo(key, size, vertexCount).SetData(hostAddress, size);
|
||||
}
|
||||
|
||||
public void CreateVbo(long key, byte[] buffer)
|
||||
{
|
||||
GetVbo(key, buffer.Length).SetData(buffer);
|
||||
}
|
||||
|
||||
public void CreateIbo(long key, byte[] buffer, long vertexCount)
|
||||
{
|
||||
GetIbo(key, buffer.Length, vertexCount).SetData(buffer);
|
||||
}
|
||||
|
||||
public void SetIndexArray(int Size, GalIndexFormat Format)
|
||||
|
@ -176,14 +580,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)
|
||||
{
|
||||
if (!IboCache.TryGetValue(IboKey, out int IboHandle))
|
||||
if (!IboCache.TryGetValue(IboKey, out CachedIbo Ibo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
|
||||
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle);
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Ibo.Buffer.Handle);
|
||||
|
||||
First <<= IndexBuffer.ElemSizeLog2;
|
||||
|
||||
|
@ -201,7 +605,42 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public bool TryGetVbo(long VboKey, out int VboHandle)
|
||||
{
|
||||
return VboCache.TryGetValue(VboKey, out VboHandle);
|
||||
if (VboCache.TryGetValue(VboKey, out OGLStreamBuffer Vbo))
|
||||
{
|
||||
VboHandle = Vbo.Handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VboHandle = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private OGLStreamBuffer GetVbo(long Key, int Size)
|
||||
{
|
||||
if (!VboCache.TryReuseValue(Key, Size, out OGLStreamBuffer Buffer))
|
||||
{
|
||||
Buffer = new OGLStreamBuffer(BufferTarget.ArrayBuffer, Size);
|
||||
|
||||
VboCache.AddOrUpdate(Key, Size, Buffer, Size);
|
||||
}
|
||||
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
private OGLStreamBuffer GetIbo(long Key, int Size, long VertexCount)
|
||||
{
|
||||
if (!IboCache.TryReuseValue(Key, Size, out CachedIbo Ibo))
|
||||
{
|
||||
OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.ElementArrayBuffer, Size);
|
||||
|
||||
IboCache.AddOrUpdate(Key, Size, Ibo = new CachedIbo(Buffer), Size);
|
||||
}
|
||||
|
||||
Ibo.VertexCount = VertexCount;
|
||||
|
||||
return Ibo.Buffer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
|
@ -88,8 +87,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
private FrameBufferAttachments Attachments;
|
||||
private FrameBufferAttachments OldAttachments;
|
||||
|
||||
private int CopyPBO;
|
||||
|
||||
public bool FramebufferSrgb { get; set; }
|
||||
|
||||
public OGLRenderTarget(OGLTexture Texture)
|
||||
|
@ -440,56 +437,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
public void Reinterpret(long Key, GalImage NewImage)
|
||||
{
|
||||
if (!Texture.TryGetImage(Key, out GalImage OldImage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NewImage.Format == OldImage.Format &&
|
||||
NewImage.Width == OldImage.Width &&
|
||||
NewImage.Height == OldImage.Height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CopyPBO == 0)
|
||||
{
|
||||
CopyPBO = GL.GenBuffer();
|
||||
}
|
||||
|
||||
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyPBO);
|
||||
|
||||
//The buffer should be large enough to hold the largest texture.
|
||||
int BufferSize = Math.Max(ImageUtils.GetSize(OldImage),
|
||||
ImageUtils.GetSize(NewImage));
|
||||
|
||||
GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
|
||||
|
||||
if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
|
||||
|
||||
GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero);
|
||||
|
||||
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
|
||||
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);
|
||||
|
||||
GL.PixelStore(PixelStoreParameter.UnpackRowLength, OldImage.Width);
|
||||
|
||||
Texture.Create(Key, ImageUtils.GetSize(NewImage), NewImage);
|
||||
|
||||
GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
|
||||
|
||||
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
|
||||
}
|
||||
|
||||
private static FramebufferAttachment GetAttachment(ImageHandler CachedImage)
|
||||
{
|
||||
if (CachedImage.HasColor)
|
||||
|
|
393
Ryujinx.Graphics/Gal/OpenGL/OGLResourceCache.cs
Normal file
393
Ryujinx.Graphics/Gal/OpenGL/OGLResourceCache.cs
Normal file
|
@ -0,0 +1,393 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLResourceCache<TPoolKey, TValue>
|
||||
{
|
||||
private const int MinTimeDelta = 5 * 60000;
|
||||
private const int MaxRemovalsPerRun = 10;
|
||||
|
||||
private const int DefaultMinTimeForPoolTransfer = 2500;
|
||||
|
||||
private class CacheBucket
|
||||
{
|
||||
public long Key { get; private set; }
|
||||
public TPoolKey PoolKey { get; private set; }
|
||||
public TValue Value { get; private set; }
|
||||
|
||||
public LinkedListNode<CacheBucket> CacheNode { get; private set; }
|
||||
public LinkedListNode<CacheBucket> PoolNode { get; private set; }
|
||||
|
||||
private Queue<Action> _deleteDeps;
|
||||
|
||||
public int Size { get; private set; }
|
||||
|
||||
public long Timestamp { get; private set; }
|
||||
|
||||
public bool Orphan { get; private set; }
|
||||
|
||||
public CacheBucket(long key, TPoolKey poolKey, TValue value, int size)
|
||||
{
|
||||
Key = key;
|
||||
PoolKey = poolKey;
|
||||
Value = value;
|
||||
Size = size;
|
||||
|
||||
_deleteDeps = new Queue<Action>();
|
||||
}
|
||||
|
||||
public void UpdateCacheNode(LinkedListNode<CacheBucket> newNode)
|
||||
{
|
||||
Timestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
CacheNode = newNode;
|
||||
}
|
||||
|
||||
public void UpdatePoolNode(LinkedListNode<CacheBucket> newNode)
|
||||
{
|
||||
PoolNode = newNode;
|
||||
}
|
||||
|
||||
public void MarkAsOrphan()
|
||||
{
|
||||
Orphan = true;
|
||||
}
|
||||
|
||||
public void AddDependency(Action deleteDep)
|
||||
{
|
||||
_deleteDeps.Enqueue(deleteDep);
|
||||
}
|
||||
|
||||
public void DeleteAllDependencies()
|
||||
{
|
||||
while (_deleteDeps.TryDequeue(out Action deleteDep))
|
||||
{
|
||||
deleteDep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<long, CacheBucket> _cache;
|
||||
|
||||
private Dictionary<TPoolKey, LinkedList<CacheBucket>> _pool;
|
||||
|
||||
private LinkedList<CacheBucket> _sortedCache;
|
||||
|
||||
private LinkedListNode<CacheBucket> _poolTransferNode;
|
||||
|
||||
private Action<TValue> _deleteValueCallback;
|
||||
|
||||
private Queue<TValue> _deletionPending;
|
||||
|
||||
private bool _locked;
|
||||
|
||||
private int _maxSize;
|
||||
private int _totalSize;
|
||||
private int _minTimeForPoolTransfer;
|
||||
|
||||
public OGLResourceCache(
|
||||
Action<TValue> deleteValueCallback,
|
||||
int maxSize,
|
||||
int minTimeForPoolTransfer = DefaultMinTimeForPoolTransfer)
|
||||
{
|
||||
_maxSize = maxSize;
|
||||
|
||||
_deleteValueCallback = deleteValueCallback ?? throw new ArgumentNullException(nameof(deleteValueCallback));
|
||||
|
||||
_cache = new Dictionary<long, CacheBucket>();
|
||||
|
||||
_pool = new Dictionary<TPoolKey, LinkedList<CacheBucket>>();
|
||||
|
||||
_sortedCache = new LinkedList<CacheBucket>();
|
||||
|
||||
_deletionPending = new Queue<TValue>();
|
||||
}
|
||||
|
||||
public void Lock()
|
||||
{
|
||||
//Locking ensure that no resources are deleted while
|
||||
//the cache is locked, this prevent resources from
|
||||
//being deleted or modified while in use.
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
_locked = false;
|
||||
|
||||
while (_deletionPending.TryDequeue(out TValue Value))
|
||||
{
|
||||
_deleteValueCallback(Value);
|
||||
}
|
||||
|
||||
ClearCacheIfNeeded();
|
||||
}
|
||||
|
||||
public void AddOrUpdate(long key, TPoolKey poolKey, TValue value, int size)
|
||||
{
|
||||
if (!_locked)
|
||||
{
|
||||
ClearCacheIfNeeded();
|
||||
}
|
||||
|
||||
CacheBucket newBucket = new CacheBucket(key, poolKey, value, size);
|
||||
|
||||
newBucket.UpdateCacheNode(_sortedCache.AddLast(newBucket));
|
||||
|
||||
if (_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
//A resource is considered orphan when it is no longer bound to
|
||||
//a key, and has been replaced by a newer one. It may still be
|
||||
//re-used. When the time expires, it will be deleted otherwise.
|
||||
bucket.MarkAsOrphan();
|
||||
|
||||
//We need to delete all dependencies, to force them
|
||||
//to use the updated handles, since we are replacing this
|
||||
//resource on the cache with another one.
|
||||
bucket.DeleteAllDependencies();
|
||||
}
|
||||
|
||||
_totalSize += size;
|
||||
|
||||
_cache[key] = newBucket;
|
||||
}
|
||||
|
||||
public void AddDependency<T, U>(long key, OGLResourceCache<T, U> soureCache, long sourceKey)
|
||||
{
|
||||
if (!_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bucket.AddDependency(() => soureCache.Delete(sourceKey));
|
||||
}
|
||||
|
||||
public bool TryGetValue(long key, out TValue value)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
AcquireResource(bucket);
|
||||
|
||||
value = bucket.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default(TValue);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryReuseValue(long key, TPoolKey poolKey, out TValue value)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out CacheBucket bucket) && bucket.PoolKey.Equals(poolKey))
|
||||
{
|
||||
//Value on key is already compatible, we don't need to do anything.
|
||||
AcquireResource(bucket);
|
||||
|
||||
value = bucket.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_pool.TryGetValue(poolKey, out LinkedList<CacheBucket> queue))
|
||||
{
|
||||
LinkedListNode<CacheBucket> node = queue.First;
|
||||
|
||||
bucket = node.Value;
|
||||
|
||||
Remove(bucket);
|
||||
|
||||
AddOrUpdate(key, poolKey, bucket.Value, bucket.Size);
|
||||
|
||||
value = bucket.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default(TValue);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetSize(long key, out int size)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
AcquireResource(bucket);
|
||||
|
||||
size = bucket.Size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetSizeAndValue(long key, out int size, out TValue value)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
AcquireResource(bucket);
|
||||
|
||||
size = bucket.Size;
|
||||
value = bucket.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
value = default(TValue);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AcquireResource(CacheBucket bucket)
|
||||
{
|
||||
RemoveFromSortedCache(bucket.CacheNode);
|
||||
|
||||
bucket.UpdateCacheNode(_sortedCache.AddLast(bucket.CacheNode.Value));
|
||||
|
||||
RemoveFromResourcePool(bucket);
|
||||
}
|
||||
|
||||
private void ClearCacheIfNeeded()
|
||||
{
|
||||
long timestamp = PerformanceCounter.ElapsedMilliseconds;
|
||||
|
||||
for (int count = 0; count < MaxRemovalsPerRun; count++)
|
||||
{
|
||||
LinkedListNode<CacheBucket> node = _sortedCache.First;
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
CacheBucket bucket = node.Value;
|
||||
|
||||
long timeDelta = timestamp - bucket.Timestamp;
|
||||
|
||||
if (timeDelta <= MinTimeDelta && !UnderMemoryPressure())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Delete(bucket);
|
||||
}
|
||||
|
||||
if (_poolTransferNode == null)
|
||||
{
|
||||
_poolTransferNode = _sortedCache.First;
|
||||
}
|
||||
|
||||
while (_poolTransferNode != null)
|
||||
{
|
||||
CacheBucket bucket = _poolTransferNode.Value;
|
||||
|
||||
long timeDelta = timestamp - bucket.Timestamp;
|
||||
|
||||
if (timeDelta <= _minTimeForPoolTransfer)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
AddToResourcePool(bucket);
|
||||
|
||||
_poolTransferNode = _poolTransferNode.Next;
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnderMemoryPressure()
|
||||
{
|
||||
return _totalSize >= _maxSize;
|
||||
}
|
||||
|
||||
private void Delete(long key)
|
||||
{
|
||||
if (!_cache.TryGetValue(key, out CacheBucket bucket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Delete(bucket);
|
||||
}
|
||||
|
||||
private void Delete(CacheBucket bucket)
|
||||
{
|
||||
Remove(bucket);
|
||||
|
||||
if (_locked)
|
||||
{
|
||||
_deletionPending.Enqueue(bucket.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_deleteValueCallback(bucket.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Remove(CacheBucket bucket)
|
||||
{
|
||||
if (!bucket.Orphan)
|
||||
{
|
||||
_cache.Remove(bucket.Key);
|
||||
}
|
||||
|
||||
RemoveFromSortedCache(bucket.CacheNode);
|
||||
RemoveFromResourcePool(bucket);
|
||||
|
||||
bucket.DeleteAllDependencies();
|
||||
|
||||
_totalSize -= bucket.Size;
|
||||
}
|
||||
|
||||
private void RemoveFromSortedCache(LinkedListNode<CacheBucket> node)
|
||||
{
|
||||
if (_poolTransferNode == node)
|
||||
{
|
||||
_poolTransferNode = node.Next;
|
||||
}
|
||||
|
||||
_sortedCache.Remove(node);
|
||||
}
|
||||
|
||||
private bool AddToResourcePool(CacheBucket bucket)
|
||||
{
|
||||
if (bucket.PoolNode == null)
|
||||
{
|
||||
if (!_pool.TryGetValue(bucket.PoolKey, out LinkedList<CacheBucket> queue))
|
||||
{
|
||||
_pool.Add(bucket.PoolKey, queue = new LinkedList<CacheBucket>());
|
||||
}
|
||||
|
||||
bucket.UpdatePoolNode(queue.AddLast(bucket));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RemoveFromResourcePool(CacheBucket bucket)
|
||||
{
|
||||
if (bucket.PoolNode != null)
|
||||
{
|
||||
LinkedList<CacheBucket> queue = bucket.PoolNode.List;
|
||||
|
||||
queue.Remove(bucket.PoolNode);
|
||||
|
||||
bucket.UpdatePoolNode(null);
|
||||
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
_pool.Remove(bucket.PoolKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
private Dictionary<OGLShaderProgram, int> Programs;
|
||||
|
||||
public int CurrentProgramHandle { get; private set; }
|
||||
private int CurrentProgramHandle;
|
||||
|
||||
private OGLConstBuffer Buffer;
|
||||
|
||||
|
@ -103,20 +103,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
|
||||
{
|
||||
BindProgram();
|
||||
|
||||
EnsureExtraBlock();
|
||||
|
||||
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
||||
|
||||
float* Data = stackalloc float[ExtraDataSize];
|
||||
|
||||
Data[0] = FlipX;
|
||||
Data[1] = FlipY;
|
||||
Data[2] = BitConverter.Int32BitsToSingle(Instance);
|
||||
|
||||
//Invalidate buffer
|
||||
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
|
||||
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
|
||||
}
|
||||
|
||||
|
@ -172,7 +168,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
if (!Programs.TryGetValue(Current, out int Handle))
|
||||
{
|
||||
Handle = GL.CreateProgram();
|
||||
CurrentProgramHandle = Handle = GL.CreateProgram();
|
||||
|
||||
AttachIfNotNull(Handle, Current.Vertex);
|
||||
AttachIfNotNull(Handle, Current.TessControl);
|
||||
|
@ -184,15 +180,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
CheckProgramLink(Handle);
|
||||
|
||||
GL.UseProgram(Handle);
|
||||
|
||||
BindUniformBlocks(Handle);
|
||||
BindTextureLocations(Handle);
|
||||
|
||||
Programs.Add(Current, Handle);
|
||||
}
|
||||
else if (CurrentProgramHandle != Handle)
|
||||
{
|
||||
CurrentProgramHandle = Handle;
|
||||
|
||||
GL.UseProgram(Handle);
|
||||
GL.UseProgram(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
CurrentProgramHandle = Handle;
|
||||
private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
|
||||
{
|
||||
if (Stage != null)
|
||||
{
|
||||
Stage.Compile();
|
||||
|
||||
GL.AttachShader(ProgramHandle, Stage.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureExtraBlock()
|
||||
|
@ -209,16 +219,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
|
||||
{
|
||||
if (Stage != null)
|
||||
{
|
||||
Stage.Compile();
|
||||
|
||||
GL.AttachShader(ProgramHandle, Stage.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void BindUniformBlocks(int ProgramHandle)
|
||||
{
|
||||
int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName);
|
||||
|
@ -274,8 +274,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
}
|
||||
}
|
||||
|
||||
GL.UseProgram(ProgramHandle);
|
||||
|
||||
BindTexturesIfNotNull(Current.Vertex);
|
||||
BindTexturesIfNotNull(Current.TessControl);
|
||||
BindTexturesIfNotNull(Current.TessEvaluation);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
|
@ -11,76 +9,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
public OGLShaderStage TessEvaluation;
|
||||
public OGLShaderStage Geometry;
|
||||
public OGLShaderStage Fragment;
|
||||
}
|
||||
|
||||
class OGLShaderStage : IDisposable
|
||||
{
|
||||
public int Handle { get; private set; }
|
||||
|
||||
public bool IsCompiled { get; private set; }
|
||||
|
||||
public GalShaderType Type { get; private set; }
|
||||
|
||||
public string Code { get; private set; }
|
||||
|
||||
public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
|
||||
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
||||
|
||||
public OGLShaderStage(
|
||||
GalShaderType Type,
|
||||
string Code,
|
||||
IEnumerable<ShaderDeclInfo> ConstBufferUsage,
|
||||
IEnumerable<ShaderDeclInfo> TextureUsage)
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
this.Type = Type;
|
||||
this.Code = Code;
|
||||
this.ConstBufferUsage = ConstBufferUsage;
|
||||
this.TextureUsage = TextureUsage;
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
if (Handle == 0)
|
||||
if (!(obj is OGLShaderProgram program))
|
||||
{
|
||||
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
||||
|
||||
CompileAndCheck(Handle, Code);
|
||||
return false;
|
||||
}
|
||||
|
||||
return Vertex == program.Vertex &&
|
||||
TessControl == program.TessControl &&
|
||||
TessEvaluation == program.TessEvaluation &&
|
||||
Geometry == program.Geometry &&
|
||||
Fragment == program.Fragment;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public override int GetHashCode()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing && Handle != 0)
|
||||
{
|
||||
GL.DeleteShader(Handle);
|
||||
|
||||
Handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompileAndCheck(int Handle, string Code)
|
||||
{
|
||||
GL.ShaderSource(Handle, Code);
|
||||
GL.CompileShader(Handle);
|
||||
|
||||
CheckCompilation(Handle);
|
||||
}
|
||||
|
||||
private static void CheckCompilation(int Handle)
|
||||
{
|
||||
int Status = 0;
|
||||
|
||||
GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
|
||||
|
||||
if (Status == 0)
|
||||
{
|
||||
throw new ShaderException(GL.GetShaderInfoLog(Handle));
|
||||
}
|
||||
return HashCode.Combine(
|
||||
Vertex,
|
||||
TessControl,
|
||||
TessEvaluation,
|
||||
Geometry,
|
||||
Fragment);
|
||||
}
|
||||
}
|
||||
}
|
77
Ryujinx.Graphics/Gal/OpenGL/OGLShaderStage.cs
Normal file
77
Ryujinx.Graphics/Gal/OpenGL/OGLShaderStage.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using OpenTK.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLShaderStage : IDisposable
|
||||
{
|
||||
public int Handle { get; private set; }
|
||||
|
||||
public bool IsCompiled { get; private set; }
|
||||
|
||||
public GalShaderType Type { get; private set; }
|
||||
|
||||
public string Code { get; private set; }
|
||||
|
||||
public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
|
||||
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
||||
|
||||
public OGLShaderStage(
|
||||
GalShaderType Type,
|
||||
string Code,
|
||||
IEnumerable<ShaderDeclInfo> ConstBufferUsage,
|
||||
IEnumerable<ShaderDeclInfo> TextureUsage)
|
||||
{
|
||||
this.Type = Type;
|
||||
this.Code = Code;
|
||||
this.ConstBufferUsage = ConstBufferUsage;
|
||||
this.TextureUsage = TextureUsage;
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
if (Handle == 0)
|
||||
{
|
||||
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
||||
|
||||
CompileAndCheck(Handle, Code);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
{
|
||||
if (Disposing && Handle != 0)
|
||||
{
|
||||
GL.DeleteShader(Handle);
|
||||
|
||||
Handle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompileAndCheck(int Handle, string Code)
|
||||
{
|
||||
GL.ShaderSource(Handle, Code);
|
||||
GL.CompileShader(Handle);
|
||||
|
||||
CheckCompilation(Handle);
|
||||
}
|
||||
|
||||
private static void CheckCompilation(int Handle)
|
||||
{
|
||||
int Status = 0;
|
||||
|
||||
GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
|
||||
|
||||
if (Status == 0)
|
||||
{
|
||||
throw new ShaderException(GL.GetShaderInfoLog(Handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,30 +11,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
protected BufferTarget Target { get; private set; }
|
||||
|
||||
public OGLStreamBuffer(BufferTarget Target, long Size)
|
||||
public OGLStreamBuffer(BufferTarget target, int size)
|
||||
{
|
||||
this.Target = Target;
|
||||
this.Size = Size;
|
||||
Target = target;
|
||||
Size = size;
|
||||
|
||||
Handle = GL.GenBuffer();
|
||||
|
||||
GL.BindBuffer(Target, Handle);
|
||||
GL.BindBuffer(target, Handle);
|
||||
|
||||
GL.BufferData(Target, (IntPtr)Size, IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
GL.BufferData(target, new IntPtr(size), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||
}
|
||||
|
||||
public void SetData(long Size, IntPtr HostAddress)
|
||||
public void SetData(IntPtr hostAddress, int size)
|
||||
{
|
||||
GL.BindBuffer(Target, Handle);
|
||||
|
||||
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
|
||||
GL.BufferSubData(Target, IntPtr.Zero, new IntPtr(size), hostAddress);
|
||||
}
|
||||
|
||||
public void SetData(byte[] Data)
|
||||
public void SetData(byte[] buffer)
|
||||
{
|
||||
GL.BindBuffer(Target, Handle);
|
||||
|
||||
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data);
|
||||
GL.BufferSubData(Target, IntPtr.Zero, new IntPtr(buffer.Length), buffer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool Disposing)
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposing && Handle != 0)
|
||||
if (disposing && Handle != 0)
|
||||
{
|
||||
GL.DeleteBuffer(Handle);
|
||||
|
||||
|
|
|
@ -6,14 +6,49 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
{
|
||||
class OGLTexture : IGalTexture
|
||||
{
|
||||
private struct ImageKey
|
||||
{
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
|
||||
private OGLCachedResource<ImageHandler> TextureCache;
|
||||
public GalImageFormat Format { get; private set; }
|
||||
|
||||
public ImageKey(GalImage image)
|
||||
{
|
||||
Width = image.Width;
|
||||
Height = image.Height;
|
||||
Format = image.Format;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is ImageKey imgKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Width == imgKey.Width &&
|
||||
Height == imgKey.Height &&
|
||||
Format == imgKey.Format;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Width, Height, Format);
|
||||
}
|
||||
}
|
||||
|
||||
private OGLResourceCache<ImageKey, ImageHandler> TextureCache;
|
||||
|
||||
private OGLResourceCache<int, int> PboCache;
|
||||
|
||||
public EventHandler<int> TextureDeleted { get; set; }
|
||||
|
||||
public OGLTexture()
|
||||
{
|
||||
TextureCache = new OGLCachedResource<ImageHandler>(DeleteTexture, OGLResourceLimits.TextureLimit);
|
||||
TextureCache = new OGLResourceCache<ImageKey, ImageHandler>(DeleteTexture, OGLResourceLimits.TextureLimit);
|
||||
|
||||
PboCache = new OGLResourceCache<int, int>(GL.DeleteBuffer, OGLResourceLimits.PixelBufferLimit, 0);
|
||||
}
|
||||
|
||||
public void LockCache()
|
||||
|
@ -35,15 +70,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public void Create(long Key, int Size, GalImage Image)
|
||||
{
|
||||
int Handle = GL.GenTexture();
|
||||
CreateFromPboOrEmpty(Key, Size, Image, IsEmpty: true);
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||
private void CreateFromPboOrEmpty(long Key, int Size, GalImage Image, bool IsEmpty = false)
|
||||
{
|
||||
ImageKey imageKey = new ImageKey(Image);
|
||||
|
||||
if (TextureCache.TryReuseValue(Key, imageKey, out ImageHandler CachedImage))
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CachedImage = new ImageHandler(GL.GenTexture(), Image);
|
||||
|
||||
TextureCache.AddOrUpdate(Key, imageKey, CachedImage, Size);
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
|
||||
|
||||
const int Level = 0; //TODO: Support mipmap textures.
|
||||
const int Border = 0;
|
||||
|
||||
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size);
|
||||
|
||||
if (ImageUtils.IsCompressed(Image.Format))
|
||||
{
|
||||
throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
|
||||
|
@ -74,7 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
const int Level = 0; //TODO: Support mipmap textures.
|
||||
const int Border = 0;
|
||||
|
||||
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
|
||||
ImageKey imageKey = new ImageKey(Image);
|
||||
|
||||
TextureCache.AddOrUpdate(Key, imageKey, new ImageHandler(Handle, Image), Data.Length);
|
||||
|
||||
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
|
||||
{
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace Ryujinx.Graphics
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
static class QuadHelper
|
||||
static class IbHelper
|
||||
{
|
||||
public static int ConvertIbSizeQuadsToTris(int Size)
|
||||
{
|
||||
|
@ -77,5 +79,61 @@ namespace Ryujinx.Graphics
|
|||
|
||||
return Output;
|
||||
}
|
||||
|
||||
public static int GetVertexCountFromIb16(byte[] data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ushort max = 0;
|
||||
|
||||
for (int index = 0; index < data.Length; index += 2)
|
||||
{
|
||||
ushort value = BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(index, 2));
|
||||
|
||||
if (max < value)
|
||||
{
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
public static long GetVertexCountFromIb32(byte[] data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint max = 0;
|
||||
|
||||
for (int index = 0; index < data.Length; index += 4)
|
||||
{
|
||||
uint value = BinaryPrimitives.ReadUInt32LittleEndian(data.AsSpan(index, 4));
|
||||
|
||||
if (max < value)
|
||||
{
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
public static long GetIbMaxVertexCount(GalIndexFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case GalIndexFormat.Byte: return 1L << 8;
|
||||
case GalIndexFormat.Int16: return 1L << 16;
|
||||
case GalIndexFormat.Int32: return 1L << 32;
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(format));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,6 +64,53 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
AddMethod(0x8e4, 16, 1, CbData);
|
||||
AddMethod(0x904, 5, 8, CbBind);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.DepthTestEnable, 1, 1, SetDepth);
|
||||
AddMethod((int)NvGpuEngine3dReg.DepthWriteEnable, 1, 1, SetDepth);
|
||||
AddMethod((int)NvGpuEngine3dReg.DepthTestFunction, 1, 1, SetDepth);
|
||||
AddMethod((int)NvGpuEngine3dReg.DepthRangeNNear, 1, 1, SetDepth);
|
||||
AddMethod((int)NvGpuEngine3dReg.DepthRangeNFar, 1, 1, SetDepth);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilEnable, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackFuncFunc, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackFuncRef, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackFuncMask, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackOpFail, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackOpZFail, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackOpZPass, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilBackMask, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontFuncFunc, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontFuncRef, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontFuncMask, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontOpFail, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontOpZFail, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontOpZPass, 1, 1, SetStencil);
|
||||
AddMethod((int)NvGpuEngine3dReg.StencilFrontMask, 1, 1, SetStencil);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendIndependent, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNEnable, 8, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNSeparateAlpha, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNEquationRgb, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNFuncSrcRgb, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNFuncDstRgb, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNEquationAlpha, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNFuncSrcAlpha, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.IBlendNFuncDstAlpha, 8, 8, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendSeparateAlpha, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendEquationRgb, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendFuncSrcRgb, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendFuncDstRgb, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendEquationAlpha, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendFuncSrcAlpha, 1, 1, SetBlend);
|
||||
AddMethod((int)NvGpuEngine3dReg.BlendFuncDstAlpha, 1, 1, SetBlend);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.ColorMaskCommon, 1, 1, SetColorMask);
|
||||
AddMethod((int)NvGpuEngine3dReg.ColorMaskN, 8, 1, SetColorMask);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.PrimRestartEnable, 1, 1, SetPrimRestart);
|
||||
AddMethod((int)NvGpuEngine3dReg.PrimRestartIndex, 1, 1, SetPrimRestart);
|
||||
|
||||
AddMethod((int)NvGpuEngine3dReg.FrameBufferSrgb, 1, 1, SetFramebufferSrgb);
|
||||
|
||||
ConstBuffers = new ConstBuffer[6][];
|
||||
|
||||
for (int Index = 0; Index < ConstBuffers.Length; Index++)
|
||||
|
@ -726,15 +773,17 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
long Key = Vmm.GetPhysicalAddress(Cb.Position);
|
||||
|
||||
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
|
||||
bool CbCached = Gpu.Renderer.Buffer.IsCached(Key, Cb.Size);
|
||||
|
||||
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer) || !CbCached)
|
||||
{
|
||||
if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr))
|
||||
{
|
||||
Gpu.Renderer.Buffer.SetData(Key, Cb.Size, CbPtr);
|
||||
Gpu.Renderer.Buffer.Create(Key, CbPtr, Cb.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.Buffer.SetData(Key, Vmm.ReadBytes(Cb.Position, Cb.Size));
|
||||
Gpu.Renderer.Buffer.Create(Key, Vmm.ReadBytes(Cb.Position, Cb.Size));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -753,7 +802,9 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
long IboKey = Vmm.GetPhysicalAddress(IbPosition);
|
||||
|
||||
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
|
||||
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
|
||||
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
|
||||
int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase);
|
||||
int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
|
||||
|
||||
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
|
||||
|
@ -764,30 +815,61 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
if (IndexEntrySize > 4)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid index entry size \"" + IndexEntrySize + "\"!");
|
||||
throw new InvalidOperationException($"Invalid index entry size \"{IndexEntrySize}\".");
|
||||
}
|
||||
|
||||
long IbVtxCount = 0;
|
||||
|
||||
if (IndexCount != 0)
|
||||
{
|
||||
int IbSize = IndexCount * IndexEntrySize;
|
||||
int IbSize = (IndexFirst + IndexCount) * IndexEntrySize;
|
||||
|
||||
bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize);
|
||||
int HostIbSize = IbSize;
|
||||
|
||||
bool UsesLegacyQuads =
|
||||
PrimType == GalPrimitiveType.Quads ||
|
||||
PrimType == GalPrimitiveType.QuadStrip;
|
||||
|
||||
if (!IboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index))
|
||||
if (UsesLegacyQuads)
|
||||
{
|
||||
if (PrimType == GalPrimitiveType.Quads)
|
||||
{
|
||||
HostIbSize = IbHelper.ConvertIbSizeQuadsToTris(HostIbSize);
|
||||
}
|
||||
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
|
||||
{
|
||||
HostIbSize = IbHelper.ConvertIbSizeQuadStripToTris(HostIbSize);
|
||||
}
|
||||
}
|
||||
|
||||
bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, HostIbSize, out IbVtxCount);
|
||||
|
||||
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index) || !IboCached)
|
||||
{
|
||||
IbVtxCount = IbHelper.GetIbMaxVertexCount(IndexFormat);
|
||||
|
||||
if (!UsesLegacyQuads)
|
||||
{
|
||||
if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr))
|
||||
bool ShallGetVertexCount = IndexFormat != GalIndexFormat.Byte;
|
||||
|
||||
if (!ShallGetVertexCount && Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr))
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr);
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbPtr, IbSize, IbVtxCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize));
|
||||
byte[] Data = Vmm.ReadBytes(IbPosition, IbSize);
|
||||
|
||||
if (IndexFormat == GalIndexFormat.Int16)
|
||||
{
|
||||
IbVtxCount = IbHelper.GetVertexCountFromIb16(Data);
|
||||
}
|
||||
else if (IndexFormat == GalIndexFormat.Int32)
|
||||
{
|
||||
IbVtxCount = IbHelper.GetVertexCountFromIb32(Data);
|
||||
}
|
||||
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, Data, IbVtxCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -796,14 +878,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
if (PrimType == GalPrimitiveType.Quads)
|
||||
{
|
||||
Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount);
|
||||
Buffer = IbHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount);
|
||||
}
|
||||
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
|
||||
{
|
||||
Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount);
|
||||
Buffer = IbHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount);
|
||||
}
|
||||
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer);
|
||||
Gpu.Renderer.Rasterizer.CreateIbo(IboKey, Buffer, IbVtxCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,53 +897,56 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
{
|
||||
if (PrimType == GalPrimitiveType.Quads)
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat);
|
||||
Gpu.Renderer.Rasterizer.SetIndexArray(IbHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat);
|
||||
}
|
||||
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat);
|
||||
Gpu.Renderer.Rasterizer.SetIndexArray(IbHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
|
||||
|
||||
for (int Attr = 0; Attr < 16; Attr++)
|
||||
{
|
||||
int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr);
|
||||
|
||||
int ArrayIndex = Packed & 0x1f;
|
||||
|
||||
if (Attribs[ArrayIndex] == null)
|
||||
{
|
||||
Attribs[ArrayIndex] = new List<GalVertexAttrib>();
|
||||
}
|
||||
|
||||
long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
|
||||
|
||||
bool IsConst = ((Packed >> 6) & 1) != 0;
|
||||
|
||||
int Offset = (Packed >> 7) & 0x3fff;
|
||||
|
||||
GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f);
|
||||
GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7);
|
||||
|
||||
bool IsRgba = ((Packed >> 31) & 1) != 0;
|
||||
|
||||
//Note: 16 is the maximum size of an attribute,
|
||||
//having a component size of 32-bits with 4 elements (a vec4).
|
||||
byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16);
|
||||
|
||||
Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba));
|
||||
}
|
||||
//Get vertex arrays and attributes count from attribute registers.
|
||||
int AttribsCount = 0;
|
||||
int ArraysCount = 0;
|
||||
|
||||
for (int Index = 0; Index < 32; Index++)
|
||||
{
|
||||
if (Attribs[Index] == null)
|
||||
int Packed = Registers[(int)NvGpuEngine3dReg.VertexAttribNFormat + Index];
|
||||
|
||||
//The size is a 3 bits field, starting at bit 27.
|
||||
//If size is 0, then the attribute is unused, skip it.
|
||||
if ((Packed & (7 << 27)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AttribsCount < Index)
|
||||
{
|
||||
AttribsCount = Index;
|
||||
}
|
||||
|
||||
int ArrayIndex = Packed & 0x1f;
|
||||
|
||||
if (ArraysCount < ArrayIndex)
|
||||
{
|
||||
ArraysCount = ArrayIndex;
|
||||
}
|
||||
}
|
||||
|
||||
//Those are actually the last valid indices, so we need
|
||||
//to add 1 to get the count.
|
||||
AttribsCount++;
|
||||
ArraysCount++;
|
||||
|
||||
//Upload vertex buffers, build vertex array info table.
|
||||
GalVertexAttribArray[] Arrays = new GalVertexAttribArray[ArraysCount];
|
||||
|
||||
int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst);
|
||||
int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount);
|
||||
|
||||
for (int Index = 0; Index < ArraysCount; Index++)
|
||||
{
|
||||
int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
|
||||
|
||||
bool Enable = (Control & 0x1000) != 0;
|
||||
|
@ -895,19 +980,71 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
long VbSize = (VbEndPos - VbPosition) + 1;
|
||||
|
||||
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
|
||||
long MaxVbSize = (IndexCount != 0
|
||||
? VertexBase + IbVtxCount
|
||||
: VertexFirst + VertexCount) * Stride;
|
||||
|
||||
if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
|
||||
if (MaxVbSize < VbSize)
|
||||
{
|
||||
VbSize = MaxVbSize;
|
||||
}
|
||||
|
||||
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, (int)VbSize);
|
||||
|
||||
if (Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex) || !VboCached)
|
||||
{
|
||||
if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr))
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr);
|
||||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, VbPtr, (int)VbSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize));
|
||||
}
|
||||
}
|
||||
|
||||
Arrays[Index] = new GalVertexAttribArray(VboKey, Stride, Instanced ? VertexDivisor : 0);
|
||||
}
|
||||
|
||||
//Set vertex attributes.
|
||||
ReadOnlySpan<int> RawAttribs = new ReadOnlySpan<int>(Registers, (int)NvGpuEngine3dReg.VertexAttribNFormat, AttribsCount);
|
||||
|
||||
if (!Gpu.Renderer.Rasterizer.TryBindVao(RawAttribs, Arrays))
|
||||
{
|
||||
GalVertexAttrib[] Attributes = new GalVertexAttrib[AttribsCount];
|
||||
|
||||
for (int Index = 0; Index < AttribsCount; Index++)
|
||||
{
|
||||
int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Index);
|
||||
|
||||
int ArrayIndex = Packed & 0x1f;
|
||||
|
||||
long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4);
|
||||
|
||||
bool IsConst = ((Packed >> 6) & 1) != 0;
|
||||
|
||||
int Offset = (Packed >> 7) & 0x3fff;
|
||||
|
||||
//Note: 16 is the maximum size of an attribute,
|
||||
//having a component size of 32-bits with 4 elements (a vec4).
|
||||
byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16);
|
||||
|
||||
GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f);
|
||||
GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7);
|
||||
|
||||
bool IsRgba = ((Packed >> 31) & 1) != 0;
|
||||
|
||||
Attributes[Index] = new GalVertexAttrib(
|
||||
IsConst,
|
||||
ArrayIndex,
|
||||
Offset,
|
||||
Data,
|
||||
Size,
|
||||
Type,
|
||||
IsRgba);
|
||||
}
|
||||
|
||||
Gpu.Renderer.Rasterizer.CreateVao(RawAttribs, Attributes, Arrays);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -974,11 +1111,11 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
//quad (First % 4 != 0 for Quads) then it will not work properly.
|
||||
if (PrimType == GalPrimitiveType.Quads)
|
||||
{
|
||||
IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst);
|
||||
IndexFirst = IbHelper.ConvertIbSizeQuadsToTris(IndexFirst);
|
||||
}
|
||||
else /* if (PrimType == GalPrimitiveType.QuadStrip) */
|
||||
{
|
||||
IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst);
|
||||
IndexFirst = IbHelper.ConvertIbSizeQuadStripToTris(IndexFirst);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1063,23 +1200,11 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
|
||||
|
||||
long CbKey = Vmm.GetPhysicalAddress(Position);
|
||||
|
||||
int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
|
||||
|
||||
if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size))
|
||||
{
|
||||
Gpu.Renderer.Buffer.Create(CbKey, Size);
|
||||
}
|
||||
|
||||
ConstBuffer Cb = ConstBuffers[Stage][Index];
|
||||
|
||||
if (Cb.Position != Position || Cb.Enabled != Enabled || Cb.Size != Size)
|
||||
{
|
||||
ConstBuffers[Stage][Index].Position = Position;
|
||||
ConstBuffers[Stage][Index].Enabled = Enabled;
|
||||
ConstBuffers[Stage][Index].Size = Size;
|
||||
}
|
||||
ConstBuffers[Stage][Index].Enabled = Enabled;
|
||||
ConstBuffers[Stage][Index].Position = Position;
|
||||
ConstBuffers[Stage][Index].Size = Size;
|
||||
}
|
||||
|
||||
private float GetFlipSign(NvGpuEngine3dReg Reg)
|
||||
|
|
Loading…
Add table
Reference in a new issue