Do naming refactoring on Ryujinx.Graphics (#611)

* Renaming part 1

* Renaming part 2

* Renaming part 3

* Renaming part 4

* Renaming part 5

* Renaming part 6

* Renaming part 7

* Renaming part 8

* Renaming part 9

* Renaming part 10

* General cleanup

* Thought I got all of these

* Apply #595

* Additional renaming

* Tweaks from feedback

* Rename files
This commit is contained in:
Alex Barney 2019-03-03 19:45:25 -06:00 committed by jduncanator
parent 8e71ea0812
commit 1f554c1093
125 changed files with 9121 additions and 9120 deletions

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gal.OpenGL
{
delegate void DeleteValue<T>(T Value);
delegate void DeleteValue<T>(T value);
}

View file

@ -18,10 +18,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public bool HasDepth => ImageUtils.HasDepth(Image.Format);
public bool HasStencil => ImageUtils.HasStencil(Image.Format);
public ImageHandler(int Handle, GalImage Image)
public ImageHandler(int handle, GalImage image)
{
this.Handle = Handle;
this.Image = Image;
Handle = handle;
Image = image;
}
}
}

View file

@ -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;
}
}
}

View file

@ -1,74 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLConstBuffer : IGalConstBuffer
{
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024;
private OGLCachedResource<OGLStreamBuffer> Cache;
public OGLConstBuffer()
{
Cache = new OGLCachedResource<OGLStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize);
}
public void LockCache()
{
Cache.Lock();
}
public void UnlockCache()
{
Cache.Unlock();
}
public void Create(long Key, long Size)
{
OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
Cache.AddOrUpdate(Key, Buffer, Size);
}
public bool IsCached(long Key, long Size)
{
return Cache.TryGetSize(Key, out long CachedSize) && CachedSize == Size;
}
public void SetData(long Key, long Size, IntPtr HostAddress)
{
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;
return true;
}
UboHandle = 0;
return false;
}
private static void DeleteBuffer(OGLStreamBuffer Buffer)
{
Buffer.Dispose();
}
}
}

View file

@ -1,70 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLExtension
{
// Private lazy backing variables
private static Lazy<bool> s_EnhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static Lazy<bool> s_NvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
// Public accessors
public static bool EnhancedLayouts => s_EnhancedLayouts.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
public static bool ViewportArray => s_ViewportArray.Value;
public static bool NvidiaDrvier => s_NvidiaDriver.Value;
private static bool HasExtension(string Name)
{
int NumExtensions = GL.GetInteger(GetPName.NumExtensions);
for (int Extension = 0; Extension < NumExtensions; Extension++)
{
if (GL.GetString(StringNameIndexed.Extensions, Extension) == Name)
{
return true;
}
}
Logger.PrintInfo(LogClass.Gpu, $"OpenGL extension {Name} unavailable. You may experience some performance degredation");
return false;
}
private static bool IsNvidiaDriver()
{
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
}
public static class Required
{
// Public accessors
public static bool EnhancedLayouts => s_EnhancedLayoutsRequired.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClampRequired.Value;
public static bool ViewportArray => s_ViewportArrayRequired.Value;
// Private lazy backing variables
private static Lazy<bool> s_EnhancedLayoutsRequired = new Lazy<bool>(() => HasExtensionRequired(OGLExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts"));
private static Lazy<bool> s_TextureMirrorClampRequired = new Lazy<bool>(() => HasExtensionRequired(OGLExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArrayRequired = new Lazy<bool>(() => HasExtensionRequired(OGLExtension.ViewportArray, "GL_ARB_viewport_array"));
private static bool HasExtensionRequired(bool Value, string Name)
{
if (Value)
{
return true;
}
Logger.PrintWarning(LogClass.Gpu, $"Required OpenGL extension {Name} unavailable. You may experience some rendering issues");
return false;
}
}
}
}

View file

@ -1,12 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLLimit
{
private static Lazy<int> s_MaxUboSize = new Lazy<int>(() => GL.GetInteger(GetPName.MaxUniformBlockSize));
public static int MaxUboSize => s_MaxUboSize.Value;
}
}

View file

@ -1,207 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRasterizer : IGalRasterizer
{
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] VertexBuffers;
private OGLCachedResource<int> VboCache;
private OGLCachedResource<int> IboCache;
private struct IbInfo
{
public int Count;
public int ElemSizeLog2;
public DrawElementsType Type;
}
private IbInfo IndexBuffer;
public OGLRasterizer()
{
VertexBuffers = new int[32];
VboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxVertexBufferCacheSize);
IboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxIndexBufferCacheSize);
IndexBuffer = new IbInfo();
}
public void LockCaches()
{
VboCache.Lock();
IboCache.Lock();
}
public void UnlockCaches()
{
VboCache.Unlock();
IboCache.Unlock();
}
public void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red,
float Green,
float Blue,
float Alpha,
float Depth,
int Stencil)
{
GL.ColorMask(
Attachment,
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
Flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha });
GL.ColorMask(Attachment, true, true, true, true);
GL.DepthMask(true);
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth);
}
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil);
}
}
public bool IsVboCached(long Key, long DataSize)
{
return VboCache.TryGetSize(Key, out long Size) && Size == DataSize;
}
public bool IsIboCached(long Key, long DataSize)
{
return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
}
public void CreateVbo(long Key, int DataSize, IntPtr HostAddress)
{
int Handle = GL.GenBuffer();
VboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(DataSize);
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
public void CreateVbo(long Key, byte[] Data)
{
int Handle = GL.GenBuffer();
VboCache.AddOrUpdate(Key, Handle, Data.Length);
IntPtr Length = new IntPtr(Data.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Data, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
{
int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
IntPtr Length = new IntPtr(DataSize);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long Key, int DataSize, byte[] Buffer)
{
int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
}
public void SetIndexArray(int Size, GalIndexFormat Format)
{
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
IndexBuffer.Count = Size >> (int)Format;
IndexBuffer.ElemSizeLog2 = (int)Format;
}
public void DrawArrays(int First, int Count, GalPrimitiveType PrimType)
{
if (Count == 0)
{
return;
}
if (PrimType == GalPrimitiveType.Quads)
{
for (int Offset = 0; Offset < Count; Offset += 4)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else if (PrimType == GalPrimitiveType.QuadStrip)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First, 4);
for (int Offset = 2; Offset < Count; Offset += 2)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else
{
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
}
}
public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)
{
if (!IboCache.TryGetValue(IboKey, out int IboHandle))
{
return;
}
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle);
First <<= IndexBuffer.ElemSizeLog2;
if (VertexBase != 0)
{
IntPtr Indices = new IntPtr(First);
GL.DrawElementsBaseVertex(Mode, IndexBuffer.Count, IndexBuffer.Type, Indices, VertexBase);
}
else
{
GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
}
}
public bool TryGetVbo(long VboKey, out int VboHandle)
{
return VboCache.TryGetValue(VboKey, out VboHandle);
}
}
}

View file

@ -1,549 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRenderTarget : IGalRenderTarget
{
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount;
private struct Rect
{
public int X { get; private set; }
public int Y { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public Rect(int X, int Y, int Width, int Height)
{
this.X = X;
this.Y = Y;
this.Width = Width;
this.Height = Height;
}
}
private class FrameBufferAttachments
{
public int MapCount { get; set; }
public DrawBuffersEnum[] Map { get; private set; }
public long[] Colors { get; private set; }
public long Zeta { get; set; }
public FrameBufferAttachments()
{
Colors = new long[RenderTargetsCount];
Map = new DrawBuffersEnum[RenderTargetsCount];
}
public void Update(FrameBufferAttachments Source)
{
for (int Index = 0; Index < RenderTargetsCount; Index++)
{
Map[Index] = Source.Map[Index];
Colors[Index] = Source.Colors[Index];
}
MapCount = Source.MapCount;
Zeta = Source.Zeta;
}
}
private int[] ColorHandles;
private int ZetaHandle;
private OGLTexture Texture;
private ImageHandler ReadTex;
private Rect Window;
private float[] Viewports;
private bool FlipX;
private bool FlipY;
private int CropTop;
private int CropLeft;
private int CropRight;
private int CropBottom;
//This framebuffer is used to attach guest rendertargets,
//think of it as a dummy OpenGL VAO
private int DummyFrameBuffer;
//These framebuffers are used to blit images
private int SrcFb;
private int DstFb;
private FrameBufferAttachments Attachments;
private FrameBufferAttachments OldAttachments;
private int CopyPBO;
public bool FramebufferSrgb { get; set; }
public OGLRenderTarget(OGLTexture Texture)
{
Attachments = new FrameBufferAttachments();
OldAttachments = new FrameBufferAttachments();
ColorHandles = new int[RenderTargetsCount];
Viewports = new float[RenderTargetsCount * 4];
this.Texture = Texture;
Texture.TextureDeleted += TextureDeletionHandler;
}
private void TextureDeletionHandler(object Sender, int Handle)
{
//Texture was deleted, the handle is no longer valid, so
//reset all uses of this handle on a render target.
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
if (ColorHandles[Attachment] == Handle)
{
ColorHandles[Attachment] = 0;
}
}
if (ZetaHandle == Handle)
{
ZetaHandle = 0;
}
}
public void Bind()
{
if (DummyFrameBuffer == 0)
{
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
ImageHandler CachedImage;
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
long Key = Attachments.Colors[Attachment];
int Handle = 0;
if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage))
{
Handle = CachedImage.Handle;
}
if (Handle == ColorHandles[Attachment])
{
continue;
}
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Handle,
0);
ColorHandles[Attachment] = Handle;
}
if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage))
{
if (CachedImage.Handle != ZetaHandle)
{
if (CachedImage.HasDepth && CachedImage.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
CachedImage.Handle,
0);
}
else if (CachedImage.HasDepth)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
CachedImage.Handle,
0);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else
{
throw new InvalidOperationException("Invalid image format \"" + CachedImage.Format + "\" used as Zeta!");
}
ZetaHandle = CachedImage.Handle;
}
}
else if (ZetaHandle != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
ZetaHandle = 0;
}
if (OGLExtension.ViewportArray)
{
GL.ViewportArray(0, RenderTargetsCount, Viewports);
}
else
{
GL.Viewport(
(int)Viewports[0],
(int)Viewports[1],
(int)Viewports[2],
(int)Viewports[3]);
}
if (Attachments.MapCount > 1)
{
GL.DrawBuffers(Attachments.MapCount, Attachments.Map);
}
else if (Attachments.MapCount == 1)
{
GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]);
}
else
{
GL.DrawBuffer(DrawBufferMode.None);
}
OldAttachments.Update(Attachments);
}
public void BindColor(long Key, int Attachment)
{
Attachments.Colors[Attachment] = Key;
}
public void UnbindColor(int Attachment)
{
Attachments.Colors[Attachment] = 0;
}
public void BindZeta(long Key)
{
Attachments.Zeta = Key;
}
public void UnbindZeta()
{
Attachments.Zeta = 0;
}
public void Present(long Key)
{
Texture.TryGetImageHandler(Key, out ReadTex);
}
public void SetMap(int[] Map)
{
if (Map != null)
{
Attachments.MapCount = Map.Length;
for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++)
{
Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment];
}
}
else
{
Attachments.MapCount = 0;
}
}
public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
{
this.FlipX = FlipX;
this.FlipY = FlipY;
CropTop = Top;
CropLeft = Left;
CropRight = Right;
CropBottom = Bottom;
}
public void SetWindowSize(int Width, int Height)
{
Window = new Rect(0, 0, Width, Height);
}
public void SetViewport(int Attachment, int X, int Y, int Width, int Height)
{
int Offset = Attachment * 4;
Viewports[Offset + 0] = X;
Viewports[Offset + 1] = Y;
Viewports[Offset + 2] = Width;
Viewports[Offset + 3] = Height;
}
public void Render()
{
if (ReadTex == null)
{
return;
}
int SrcX0, SrcX1, SrcY0, SrcY1;
if (CropLeft == 0 && CropRight == 0)
{
SrcX0 = 0;
SrcX1 = ReadTex.Width;
}
else
{
SrcX0 = CropLeft;
SrcX1 = CropRight;
}
if (CropTop == 0 && CropBottom == 0)
{
SrcY0 = 0;
SrcY1 = ReadTex.Height;
}
else
{
SrcY0 = CropTop;
SrcY1 = CropBottom;
}
float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width));
float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height));
int DstWidth = (int)(Window.Width * RatioX);
int DstHeight = (int)(Window.Height * RatioY);
int DstPaddingX = (Window.Width - DstWidth) / 2;
int DstPaddingY = (Window.Height - DstHeight) / 2;
int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX;
int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX;
int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
GL.Viewport(0, 0, Window.Width, Window.Height);
if (SrcFb == 0)
{
SrcFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Disable(EnableCap.FramebufferSrgb);
GL.BlitFramebuffer(
SrcX0,
SrcY0,
SrcX1,
SrcY1,
DstX0,
DstY0,
DstX1,
DstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
if (FramebufferSrgb)
{
GL.Enable(EnableCap.FramebufferSrgb);
}
}
public void Copy(
GalImage SrcImage,
GalImage DstImage,
long SrcKey,
long DstKey,
int SrcLayer,
int DstLayer,
int SrcX0,
int SrcY0,
int SrcX1,
int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1)
{
if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
{
if (SrcTex.HasColor != DstTex.HasColor ||
SrcTex.HasDepth != DstTex.HasDepth ||
SrcTex.HasStencil != DstTex.HasStencil)
{
throw new NotImplementedException();
}
if (SrcFb == 0)
{
SrcFb = GL.GenFramebuffer();
}
if (DstFb == 0)
{
DstFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
FramebufferAttachment Attachment = GetAttachment(SrcTex);
if (ImageUtils.IsArray(SrcImage.TextureTarget) && SrcLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0, SrcLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
}
if (ImageUtils.IsArray(DstImage.TextureTarget) && DstLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0, DstLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
}
BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
if (SrcTex.HasColor)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Filter = BlitFramebufferFilter.Linear;
}
ClearBufferMask Mask = GetClearMask(SrcTex);
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
}
}
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 &&
NewImage.Depth == OldImage.Depth &&
NewImage.LayerCount == OldImage.LayerCount &&
NewImage.TextureTarget == OldImage.TextureTarget)
{
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);
TextureTarget Target = ImageUtils.GetTextureTarget(NewImage.TextureTarget);
GL.BindTexture(Target, CachedImage.Handle);
GL.GetTexImage(Target, 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)
{
return FramebufferAttachment.ColorAttachment0;
}
else if (CachedImage.HasDepth && CachedImage.HasStencil)
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (CachedImage.HasDepth)
{
return FramebufferAttachment.DepthAttachment;
}
else if (CachedImage.HasStencil)
{
return FramebufferAttachment.StencilAttachment;
}
else
{
throw new InvalidOperationException();
}
}
private static ClearBufferMask GetClearMask(ImageHandler CachedImage)
{
return (CachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) |
(CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
(CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
}
}
}

View file

@ -1,58 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLRenderer : IGalRenderer
{
public IGalConstBuffer Buffer { get; private set; }
public IGalRenderTarget RenderTarget { get; private set; }
public IGalRasterizer Rasterizer { get; private set; }
public IGalShader Shader { get; private set; }
public IGalPipeline Pipeline { get; private set; }
public IGalTexture Texture { get; private set; }
private ConcurrentQueue<Action> ActionsQueue;
public OGLRenderer()
{
Buffer = new OGLConstBuffer();
Texture = new OGLTexture();
RenderTarget = new OGLRenderTarget(Texture as OGLTexture);
Rasterizer = new OGLRasterizer();
Shader = new OGLShader(Buffer as OGLConstBuffer);
Pipeline = new OGLPipeline(
Buffer as OGLConstBuffer,
RenderTarget as OGLRenderTarget,
Rasterizer as OGLRasterizer,
Shader as OGLShader);
ActionsQueue = new ConcurrentQueue<Action>();
}
public void QueueAction(Action ActionMthd)
{
ActionsQueue.Enqueue(ActionMthd);
}
public void RunActions()
{
int Count = ActionsQueue.Count;
while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
{
RenderAction();
}
}
}
}

View file

@ -1,298 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Gal.Shader;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLShader : IGalShader
{
public const int ReservedCbufCount = 1;
private const int ExtraDataSize = 4;
public OGLShaderProgram Current;
private ConcurrentDictionary<long, OGLShaderStage> Stages;
private Dictionary<OGLShaderProgram, int> Programs;
public int CurrentProgramHandle { get; private set; }
private OGLConstBuffer Buffer;
private int ExtraUboHandle;
public OGLShader(OGLConstBuffer Buffer)
{
this.Buffer = Buffer;
Stages = new ConcurrentDictionary<long, OGLShaderStage>();
Programs = new Dictionary<OGLShaderProgram, int>();
}
public void Create(IGalMemory Memory, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type));
}
public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
}
private OGLShaderStage ShaderStageFactory(
IGalMemory Memory,
long Position,
long PositionB,
bool IsDualVp,
GalShaderType Type)
{
GlslProgram Program;
GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize, OGLExtension.NvidiaDrvier);
int ShaderDumpIndex = ShaderDumper.DumpIndex;
if (IsDualVp)
{
ShaderDumper.Dump(Memory, Position, Type, "a");
ShaderDumper.Dump(Memory, PositionB, Type, "b");
Program = Decompiler.Decompile(Memory, Position, PositionB, Type);
}
else
{
ShaderDumper.Dump(Memory, Position, Type);
Program = Decompiler.Decompile(Memory, Position, Type);
}
string Code = Program.Code;
if (ShaderDumper.IsDumpEnabled())
{
Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code;
}
return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures);
}
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.ConstBufferUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.TextureUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
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);
}
public void Bind(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
Bind(Stage);
}
}
private void Bind(OGLShaderStage Stage)
{
if (Stage.Type == GalShaderType.Geometry)
{
//Enhanced layouts are required for Geometry shaders
//skip this stage if current driver has no ARB_enhanced_layouts
if (!OGLExtension.EnhancedLayouts)
{
return;
}
}
switch (Stage.Type)
{
case GalShaderType.Vertex: Current.Vertex = Stage; break;
case GalShaderType.TessControl: Current.TessControl = Stage; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
case GalShaderType.Geometry: Current.Geometry = Stage; break;
case GalShaderType.Fragment: Current.Fragment = Stage; break;
}
}
public void Unbind(GalShaderType Type)
{
switch (Type)
{
case GalShaderType.Vertex: Current.Vertex = null; break;
case GalShaderType.TessControl: Current.TessControl = null; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break;
case GalShaderType.Geometry: Current.Geometry = null; break;
case GalShaderType.Fragment: Current.Fragment = null; break;
}
}
public void BindProgram()
{
if (Current.Vertex == null ||
Current.Fragment == null)
{
return;
}
if (!Programs.TryGetValue(Current, out int Handle))
{
Handle = GL.CreateProgram();
AttachIfNotNull(Handle, Current.Vertex);
AttachIfNotNull(Handle, Current.TessControl);
AttachIfNotNull(Handle, Current.TessEvaluation);
AttachIfNotNull(Handle, Current.Geometry);
AttachIfNotNull(Handle, Current.Fragment);
GL.LinkProgram(Handle);
CheckProgramLink(Handle);
BindUniformBlocks(Handle);
BindTextureLocations(Handle);
Programs.Add(Current, Handle);
}
GL.UseProgram(Handle);
CurrentProgramHandle = Handle;
}
private void EnsureExtraBlock()
{
if (ExtraUboHandle == 0)
{
ExtraUboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
}
}
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);
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
int FreeBinding = ReservedCbufCount;
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
{
int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
if (BlockIndex < 0)
{
//It is expected that its found, if it's not then driver might be in a malfunction
throw new InvalidOperationException();
}
GL.UniformBlockBinding(ProgramHandle, BlockIndex, FreeBinding);
FreeBinding++;
}
}
}
BindUniformBlocksIfNotNull(Current.Vertex);
BindUniformBlocksIfNotNull(Current.TessControl);
BindUniformBlocksIfNotNull(Current.TessEvaluation);
BindUniformBlocksIfNotNull(Current.Geometry);
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindTextureLocations(int ProgramHandle)
{
int Index = 0;
void BindTexturesIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
{
int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
GL.Uniform1(Location, Index);
Index++;
}
}
}
GL.UseProgram(ProgramHandle);
BindTexturesIfNotNull(Current.Vertex);
BindTexturesIfNotNull(Current.TessControl);
BindTexturesIfNotNull(Current.TessEvaluation);
BindTexturesIfNotNull(Current.Geometry);
BindTexturesIfNotNull(Current.Fragment);
}
private static void CheckProgramLink(int Handle)
{
int Status = 0;
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status);
if (Status == 0)
{
throw new ShaderException(GL.GetProgramInfoLog(Handle));
}
}
}
}

View file

@ -1,86 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
struct OGLShaderProgram
{
public OGLShaderStage Vertex;
public OGLShaderStage TessControl;
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)
{
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));
}
}
}
}

View file

@ -1,381 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLTexture : IGalTexture
{
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private OGLCachedResource<ImageHandler> TextureCache;
public EventHandler<int> TextureDeleted { get; set; }
public OGLTexture()
{
TextureCache = new OGLCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
}
public void LockCache()
{
TextureCache.Lock();
}
public void UnlockCache()
{
TextureCache.Unlock();
}
private void DeleteTexture(ImageHandler CachedImage)
{
TextureDeleted?.Invoke(this, CachedImage.Handle);
GL.DeleteTexture(CachedImage.Handle);
}
public void Create(long Key, int Size, GalImage Image)
{
int Handle = GL.GenTexture();
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, 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!");
}
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
switch (Target)
{
case TextureTarget.Texture1D:
GL.TexImage1D(
Target,
Level,
InternalFmt,
Image.Width,
Border,
Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Format,
Type,
IntPtr.Zero);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Format,
Type,
IntPtr.Zero);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
}
public void Create(long Key, byte[] Data, GalImage Image)
{
int Handle = GL.GenTexture();
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, Handle);
const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0;
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
{
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
switch (Target)
{
case TextureTarget.Texture1D:
GL.CompressedTexImage1D(
Target,
Level,
InternalFmt,
Image.Width,
Border,
Data.Length,
Data);
break;
case TextureTarget.Texture2D:
GL.CompressedTexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Data.Length,
Data);
break;
case TextureTarget.Texture3D:
GL.CompressedTexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Data.Length,
Data);
break;
case TextureTarget.Texture2DArray:
GL.CompressedTexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Data.Length,
Data);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
}
else
{
//TODO: Use KHR_texture_compression_astc_hdr when available
if (IsAstc(Image.Format))
{
int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format);
int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
int TextureBlockDepth = ImageUtils.GetBlockDepth(Image.Format);
Data = ASTCDecoder.DecodeToRGBA8888(
Data,
TextureBlockWidth,
TextureBlockHeight,
TextureBlockDepth,
Image.Width,
Image.Height,
Image.Depth);
Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask);
}
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
switch (Target)
{
case TextureTarget.Texture1D:
GL.TexImage1D(
Target,
Level,
InternalFmt,
Image.Width,
Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.Depth,
Border,
Format,
Type,
Data);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
Target,
Level,
InternalFmt,
Image.Width,
Image.Height,
Image.LayerCount,
Border,
Format,
Type,
Data);
break;
case TextureTarget.TextureCubeMap:
Span<byte> Array = new Span<byte>(Data);
int FaceSize = ImageUtils.GetSize(Image) / 6;
for (int Face = 0; Face < 6; Face++)
{
GL.TexImage2D(
TextureTarget.TextureCubeMapPositiveX + Face,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
Array.Slice(Face * FaceSize, FaceSize).ToArray());
}
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {Target}");
}
}
}
private static bool IsAstc(GalImageFormat Format)
{
Format &= GalImageFormat.FormatMask;
return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd;
}
public bool TryGetImage(long Key, out GalImage Image)
{
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
Image = CachedImage.Image;
return true;
}
Image = default(GalImage);
return false;
}
public bool TryGetImageHandler(long Key, out ImageHandler CachedImage)
{
if (TextureCache.TryGetValue(Key, out CachedImage))
{
return true;
}
CachedImage = null;
return false;
}
public void Bind(long Key, int Index, GalImage Image)
{
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.BindTexture(Target, CachedImage.Handle);
int[] SwizzleRgba = new int[]
{
(int)OGLEnumConverter.GetTextureSwizzle(Image.XSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.YSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
};
GL.TexParameter(Target, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
}
}
public void SetSampler(GalImage Image, GalTextureSampler Sampler)
{
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
int WrapR = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressP);
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
TextureTarget Target = ImageUtils.GetTextureTarget(Image.TextureTarget);
GL.TexParameter(Target, TextureParameterName.TextureWrapS, WrapS);
GL.TexParameter(Target, TextureParameterName.TextureWrapT, WrapT);
GL.TexParameter(Target, TextureParameterName.TextureWrapR, WrapR);
GL.TexParameter(Target, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(Target, TextureParameterName.TextureMagFilter, MagFilter);
float[] Color = new float[]
{
Sampler.BorderColor.Red,
Sampler.BorderColor.Green,
Sampler.BorderColor.Blue,
Sampler.BorderColor.Alpha
};
GL.TexParameter(Target, TextureParameterName.TextureBorderColor, Color);
if (Sampler.DepthCompare)
{
GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)OGLEnumConverter.GetDepthCompareFunc(Sampler.DepthCompareFunc));
}
else
{
GL.TexParameter(Target, TextureParameterName.TextureCompareMode, (int)All.None);
GL.TexParameter(Target, TextureParameterName.TextureCompareFunc, (int)All.Never);
}
}
}
}

View file

@ -0,0 +1,191 @@
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)
{
Value = value;
DataSize = dataSize;
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)
{
_maxSize = maxSize;
if (deleteValueCallback == null)
{
throw new ArgumentNullException(nameof(deleteValueCallback));
}
_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;
}
}
}

View file

@ -0,0 +1,74 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglConstBuffer : IGalConstBuffer
{
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024;
private OglCachedResource<OglStreamBuffer> _cache;
public OglConstBuffer()
{
_cache = new OglCachedResource<OglStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize);
}
public void LockCache()
{
_cache.Lock();
}
public void UnlockCache()
{
_cache.Unlock();
}
public void Create(long key, long size)
{
OglStreamBuffer buffer = new OglStreamBuffer(BufferTarget.UniformBuffer, size);
_cache.AddOrUpdate(key, buffer, size);
}
public bool IsCached(long key, long size)
{
return _cache.TryGetSize(key, out long cachedSize) && cachedSize == size;
}
public void SetData(long key, long size, IntPtr hostAddress)
{
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;
return true;
}
uboHandle = 0;
return false;
}
private static void DeleteBuffer(OglStreamBuffer buffer)
{
buffer.Dispose();
}
}
}

View file

@ -3,34 +3,34 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLEnumConverter
static class OglEnumConverter
{
public static FrontFaceDirection GetFrontFace(GalFrontFace FrontFace)
public static FrontFaceDirection GetFrontFace(GalFrontFace frontFace)
{
switch (FrontFace)
switch (frontFace)
{
case GalFrontFace.CW: return FrontFaceDirection.Cw;
case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
case GalFrontFace.Cw: return FrontFaceDirection.Cw;
case GalFrontFace.Ccw: return FrontFaceDirection.Ccw;
}
throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!");
throw new ArgumentException(nameof(frontFace) + " \"" + frontFace + "\" is not valid!");
}
public static CullFaceMode GetCullFace(GalCullFace CullFace)
public static CullFaceMode GetCullFace(GalCullFace cullFace)
{
switch (CullFace)
switch (cullFace)
{
case GalCullFace.Front: return CullFaceMode.Front;
case GalCullFace.Back: return CullFaceMode.Back;
case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
}
throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!");
throw new ArgumentException(nameof(cullFace) + " \"" + cullFace + "\" is not valid!");
}
public static StencilOp GetStencilOp(GalStencilOp Op)
public static StencilOp GetStencilOp(GalStencilOp op)
{
switch (Op)
switch (op)
{
case GalStencilOp.Keep: return StencilOp.Keep;
case GalStencilOp.Zero: return StencilOp.Zero;
@ -42,28 +42,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
}
throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!");
throw new ArgumentException(nameof(op) + " \"" + op + "\" is not valid!");
}
public static DepthFunction GetDepthFunc(GalComparisonOp Func)
public static DepthFunction GetDepthFunc(GalComparisonOp func)
{
return (DepthFunction)GetFunc(Func);
return (DepthFunction)GetFunc(func);
}
public static StencilFunction GetStencilFunc(GalComparisonOp Func)
public static StencilFunction GetStencilFunc(GalComparisonOp func)
{
return (StencilFunction)GetFunc(Func);
return (StencilFunction)GetFunc(func);
}
private static All GetFunc(GalComparisonOp Func)
private static All GetFunc(GalComparisonOp func)
{
if ((int)Func >= (int)All.Never &&
(int)Func <= (int)All.Always)
if ((int)func >= (int)All.Never &&
(int)func <= (int)All.Always)
{
return (All)Func;
return (All)func;
}
switch (Func)
switch (func)
{
case GalComparisonOp.Never: return All.Never;
case GalComparisonOp.Less: return All.Less;
@ -75,24 +75,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalComparisonOp.Always: return All.Always;
}
throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!");
throw new ArgumentException(nameof(func) + " \"" + func + "\" is not valid!");
}
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
public static DrawElementsType GetDrawElementsType(GalIndexFormat format)
{
switch (Format)
switch (format)
{
case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte;
case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!");
throw new ArgumentException(nameof(format) + " \"" + format + "\" is not valid!");
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
public static PrimitiveType GetPrimitiveType(GalPrimitiveType type)
{
switch (Type)
switch (type)
{
case GalPrimitiveType.Points: return PrimitiveType.Points;
case GalPrimitiveType.Lines: return PrimitiveType.Lines;
@ -109,12 +109,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!");
}
public static ShaderType GetShaderType(GalShaderType Type)
public static ShaderType GetShaderType(GalShaderType type)
{
switch (Type)
switch (type)
{
case GalShaderType.Vertex: return ShaderType.VertexShader;
case GalShaderType.TessControl: return ShaderType.TessControlShader;
@ -123,50 +123,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalShaderType.Fragment: return ShaderType.FragmentShader;
}
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!");
}
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat format)
{
switch (Format)
switch (format)
{
case GalImageFormat.RGBA32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.RGBA32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.RGBA32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBA16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.RGBA16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.RGBA16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.RGBA16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort);
case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBX8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.BGRA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.BGRA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.RGBA4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.Rgba32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.Rgba32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.Rgba32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.Rgba16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.Rgba16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.Rgba16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.Rgba16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort);
case GalImageFormat.Rg32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.Rg32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.Rg32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.Rgbx8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.Rgba8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.Rgba8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Bgra8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.Bgra8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.Rgba4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.Rgb10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.Rgb10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float);
case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int);
case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt);
case GalImageFormat.BGR5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalImageFormat.RGB5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.RGB565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.BGR565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalImageFormat.RG16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.RG16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort);
case GalImageFormat.RG16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.RG8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.RG8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.Bgr5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalImageFormat.Rgb5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.Rgb565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.Bgr565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalImageFormat.Rg16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.Rg16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.Rg16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short);
case GalImageFormat.Rg16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort);
case GalImageFormat.Rg16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.Rg8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.Rg8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.Rg8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.Rg8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat);
case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short);
case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short);
@ -186,12 +186,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}");
}
public static All GetDepthCompareFunc(DepthCompareFunc DepthCompareFunc)
public static All GetDepthCompareFunc(DepthCompareFunc depthCompareFunc)
{
switch (DepthCompareFunc)
switch (depthCompareFunc)
{
case DepthCompareFunc.LEqual:
return All.Lequal;
@ -210,13 +210,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case DepthCompareFunc.Never:
return All.Never;
default:
throw new ArgumentException(nameof(DepthCompareFunc) + " \"" + DepthCompareFunc + "\" is not valid!");
throw new ArgumentException(nameof(depthCompareFunc) + " \"" + depthCompareFunc + "\" is not valid!");
}
}
public static InternalFormat GetCompressedImageFormat(GalImageFormat Format)
public static InternalFormat GetCompressedImageFormat(GalImageFormat format)
{
switch (Format)
switch (format)
{
case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat;
@ -234,12 +234,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2;
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}");
}
public static All GetTextureSwizzle(GalTextureSource Source)
public static All GetTextureSwizzle(GalTextureSource source)
{
switch (Source)
switch (source)
{
case GalTextureSource.Zero: return All.Zero;
case GalTextureSource.Red: return All.Red;
@ -250,12 +250,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureSource.OneFloat: return All.One;
}
throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!");
throw new ArgumentException(nameof(source) + " \"" + source + "\" is not valid!");
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap wrap)
{
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
@ -264,9 +264,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
}
if (OGLExtension.TextureMirrorClamp)
if (OglExtension.TextureMirrorClamp)
{
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.MirrorClampToEdge: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt;
case GalTextureWrap.MirrorClampToBorder: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt;
@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
else
{
//Fallback to non-mirrored clamps
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
@ -284,37 +284,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!");
throw new ArgumentException(nameof(wrap) + " \"" + wrap + "\" is not valid!");
}
public static TextureMinFilter GetTextureMinFilter(
GalTextureFilter MinFilter,
GalTextureMipFilter MipFilter)
GalTextureFilter minFilter,
GalTextureMipFilter mipFilter)
{
//TODO: Mip (needs mipmap support first).
switch (MinFilter)
switch (minFilter)
{
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!");
throw new ArgumentException(nameof(minFilter) + " \"" + minFilter + "\" is not valid!");
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter filter)
{
switch (Filter)
switch (filter)
{
case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!");
throw new ArgumentException(nameof(filter) + " \"" + filter + "\" is not valid!");
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
public static BlendEquationMode GetBlendEquation(GalBlendEquation blendEquation)
{
switch (BlendEquation)
switch (blendEquation)
{
case GalBlendEquation.FuncAdd:
case GalBlendEquation.FuncAddGl:
@ -337,12 +337,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendEquationMode.Max;
}
throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!");
throw new ArgumentException(nameof(blendEquation) + " \"" + blendEquation + "\" is not valid!");
}
public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor)
public static BlendingFactor GetBlendFactor(GalBlendFactor blendFactor)
{
switch (BlendFactor)
switch (blendFactor)
{
case GalBlendFactor.Zero:
case GalBlendFactor.ZeroGl:
@ -421,7 +421,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendingFactor.ConstantColor;
}
throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!");
throw new ArgumentException(nameof(blendFactor) + " \"" + blendFactor + "\" is not valid!");
}
}
}

View file

@ -0,0 +1,70 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OglExtension
{
// Private lazy backing variables
private static Lazy<bool> _enhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> _textureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> _viewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static Lazy<bool> _nvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
// Public accessors
public static bool EnhancedLayouts => _enhancedLayouts.Value;
public static bool TextureMirrorClamp => _textureMirrorClamp.Value;
public static bool ViewportArray => _viewportArray.Value;
public static bool NvidiaDriver => _nvidiaDriver.Value;
private static bool HasExtension(string name)
{
int numExtensions = GL.GetInteger(GetPName.NumExtensions);
for (int extension = 0; extension < numExtensions; extension++)
{
if (GL.GetString(StringNameIndexed.Extensions, extension) == name)
{
return true;
}
}
Logger.PrintInfo(LogClass.Gpu, $"OpenGL extension {name} unavailable. You may experience some performance degradation");
return false;
}
private static bool IsNvidiaDriver()
{
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
}
public static class Required
{
// Public accessors
public static bool EnhancedLayouts => _enhancedLayoutsRequired.Value;
public static bool TextureMirrorClamp => _textureMirrorClampRequired.Value;
public static bool ViewportArray => _viewportArrayRequired.Value;
// Private lazy backing variables
private static Lazy<bool> _enhancedLayoutsRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts"));
private static Lazy<bool> _textureMirrorClampRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> _viewportArrayRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.ViewportArray, "GL_ARB_viewport_array"));
private static bool HasExtensionRequired(bool value, string name)
{
if (value)
{
return true;
}
Logger.PrintWarning(LogClass.Gpu, $"Required OpenGL extension {name} unavailable. You may experience some rendering issues");
return false;
}
}
}
}

View file

@ -0,0 +1,12 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OglLimit
{
private static Lazy<int> _sMaxUboSize = new Lazy<int>(() => GL.GetInteger(GetPName.MaxUniformBlockSize));
public static int MaxUboSize => _sMaxUboSize.Value;
}
}

View file

@ -4,9 +4,9 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLPipeline : IGalPipeline
class OglPipeline : IGalPipeline
{
private static Dictionary<GalVertexAttribSize, int> AttribElements =
private static Dictionary<GalVertexAttribSize, int> _attribElements =
new Dictionary<GalVertexAttribSize, int>()
{
{ GalVertexAttribSize._32_32_32_32, 4 },
@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, 3 }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> FloatAttribTypes =
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> _floatAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float },
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> SignedAttribTypes =
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> _signedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int },
@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int2101010Rev }
};
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> UnsignedAttribTypes =
private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> _unsignedAttribTypes =
new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
{
{ GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt },
@ -75,30 +75,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev }
};
private GalPipelineState Old;
private GalPipelineState _old;
private OGLConstBuffer Buffer;
private OGLRenderTarget RenderTarget;
private OGLRasterizer Rasterizer;
private OGLShader Shader;
private OglConstBuffer _buffer;
private OglRenderTarget _renderTarget;
private OglRasterizer _rasterizer;
private OglShader _shader;
private int VaoHandle;
private int _vaoHandle;
public OGLPipeline(
OGLConstBuffer Buffer,
OGLRenderTarget RenderTarget,
OGLRasterizer Rasterizer,
OGLShader Shader)
public OglPipeline(
OglConstBuffer buffer,
OglRenderTarget renderTarget,
OglRasterizer rasterizer,
OglShader shader)
{
this.Buffer = Buffer;
this.RenderTarget = RenderTarget;
this.Rasterizer = Rasterizer;
this.Shader = Shader;
_buffer = buffer;
_renderTarget = renderTarget;
_rasterizer = rasterizer;
_shader = shader;
//These values match OpenGL's defaults
Old = new GalPipelineState
_old = new GalPipelineState
{
FrontFace = GalFrontFace.CCW,
FrontFace = GalFrontFace.Ccw,
CullFaceEnabled = false,
CullFace = GalCullFace.Back,
@ -133,11 +133,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
PrimitiveRestartIndex = 0
};
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
{
Old.Blends[Index] = BlendState.Default;
_old.Blends[index] = BlendState.Default;
Old.ColorMasks[Index] = ColorMaskState.Default;
_old.ColorMasks[index] = ColorMaskState.Default;
}
}
@ -147,122 +147,122 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BindVertexLayout(New);
if (New.FramebufferSrgb != Old.FramebufferSrgb)
if (New.FramebufferSrgb != _old.FramebufferSrgb)
{
Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb);
RenderTarget.FramebufferSrgb = New.FramebufferSrgb;
_renderTarget.FramebufferSrgb = New.FramebufferSrgb;
}
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance)
if (New.FlipX != _old.FlipX || New.FlipY != _old.FlipY || New.Instance != _old.Instance)
{
Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
_shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
}
if (New.FrontFace != Old.FrontFace)
if (New.FrontFace != _old.FrontFace)
{
GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace));
GL.FrontFace(OglEnumConverter.GetFrontFace(New.FrontFace));
}
if (New.CullFaceEnabled != Old.CullFaceEnabled)
if (New.CullFaceEnabled != _old.CullFaceEnabled)
{
Enable(EnableCap.CullFace, New.CullFaceEnabled);
}
if (New.CullFaceEnabled)
{
if (New.CullFace != Old.CullFace)
if (New.CullFace != _old.CullFace)
{
GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace));
GL.CullFace(OglEnumConverter.GetCullFace(New.CullFace));
}
}
if (New.DepthTestEnabled != Old.DepthTestEnabled)
if (New.DepthTestEnabled != _old.DepthTestEnabled)
{
Enable(EnableCap.DepthTest, New.DepthTestEnabled);
}
if (New.DepthWriteEnabled != Old.DepthWriteEnabled)
if (New.DepthWriteEnabled != _old.DepthWriteEnabled)
{
GL.DepthMask(New.DepthWriteEnabled);
}
if (New.DepthTestEnabled)
{
if (New.DepthFunc != Old.DepthFunc)
if (New.DepthFunc != _old.DepthFunc)
{
GL.DepthFunc(OGLEnumConverter.GetDepthFunc(New.DepthFunc));
GL.DepthFunc(OglEnumConverter.GetDepthFunc(New.DepthFunc));
}
}
if (New.DepthRangeNear != Old.DepthRangeNear ||
New.DepthRangeFar != Old.DepthRangeFar)
if (New.DepthRangeNear != _old.DepthRangeNear ||
New.DepthRangeFar != _old.DepthRangeFar)
{
GL.DepthRange(New.DepthRangeNear, New.DepthRangeFar);
}
if (New.StencilTestEnabled != Old.StencilTestEnabled)
if (New.StencilTestEnabled != _old.StencilTestEnabled)
{
Enable(EnableCap.StencilTest, New.StencilTestEnabled);
}
if (New.StencilTwoSideEnabled != Old.StencilTwoSideEnabled)
if (New.StencilTwoSideEnabled != _old.StencilTwoSideEnabled)
{
Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled);
}
if (New.StencilTestEnabled)
{
if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc ||
New.StencilBackFuncRef != Old.StencilBackFuncRef ||
New.StencilBackFuncMask != Old.StencilBackFuncMask)
if (New.StencilBackFuncFunc != _old.StencilBackFuncFunc ||
New.StencilBackFuncRef != _old.StencilBackFuncRef ||
New.StencilBackFuncMask != _old.StencilBackFuncMask)
{
GL.StencilFuncSeparate(
StencilFace.Back,
OGLEnumConverter.GetStencilFunc(New.StencilBackFuncFunc),
OglEnumConverter.GetStencilFunc(New.StencilBackFuncFunc),
New.StencilBackFuncRef,
New.StencilBackFuncMask);
}
if (New.StencilBackOpFail != Old.StencilBackOpFail ||
New.StencilBackOpZFail != Old.StencilBackOpZFail ||
New.StencilBackOpZPass != Old.StencilBackOpZPass)
if (New.StencilBackOpFail != _old.StencilBackOpFail ||
New.StencilBackOpZFail != _old.StencilBackOpZFail ||
New.StencilBackOpZPass != _old.StencilBackOpZPass)
{
GL.StencilOpSeparate(
StencilFace.Back,
OGLEnumConverter.GetStencilOp(New.StencilBackOpFail),
OGLEnumConverter.GetStencilOp(New.StencilBackOpZFail),
OGLEnumConverter.GetStencilOp(New.StencilBackOpZPass));
OglEnumConverter.GetStencilOp(New.StencilBackOpFail),
OglEnumConverter.GetStencilOp(New.StencilBackOpZFail),
OglEnumConverter.GetStencilOp(New.StencilBackOpZPass));
}
if (New.StencilBackMask != Old.StencilBackMask)
if (New.StencilBackMask != _old.StencilBackMask)
{
GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask);
}
if (New.StencilFrontFuncFunc != Old.StencilFrontFuncFunc ||
New.StencilFrontFuncRef != Old.StencilFrontFuncRef ||
New.StencilFrontFuncMask != Old.StencilFrontFuncMask)
if (New.StencilFrontFuncFunc != _old.StencilFrontFuncFunc ||
New.StencilFrontFuncRef != _old.StencilFrontFuncRef ||
New.StencilFrontFuncMask != _old.StencilFrontFuncMask)
{
GL.StencilFuncSeparate(
StencilFace.Front,
OGLEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc),
OglEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc),
New.StencilFrontFuncRef,
New.StencilFrontFuncMask);
}
if (New.StencilFrontOpFail != Old.StencilFrontOpFail ||
New.StencilFrontOpZFail != Old.StencilFrontOpZFail ||
New.StencilFrontOpZPass != Old.StencilFrontOpZPass)
if (New.StencilFrontOpFail != _old.StencilFrontOpFail ||
New.StencilFrontOpZFail != _old.StencilFrontOpZFail ||
New.StencilFrontOpZPass != _old.StencilFrontOpZPass)
{
GL.StencilOpSeparate(
StencilFace.Front,
OGLEnumConverter.GetStencilOp(New.StencilFrontOpFail),
OGLEnumConverter.GetStencilOp(New.StencilFrontOpZFail),
OGLEnumConverter.GetStencilOp(New.StencilFrontOpZPass));
OglEnumConverter.GetStencilOp(New.StencilFrontOpFail),
OglEnumConverter.GetStencilOp(New.StencilFrontOpZFail),
OglEnumConverter.GetStencilOp(New.StencilFrontOpZPass));
}
if (New.StencilFrontMask != Old.StencilFrontMask)
if (New.StencilFrontMask != _old.StencilFrontMask)
{
GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask);
}
@ -277,42 +277,42 @@ namespace Ryujinx.Graphics.Gal.OpenGL
int scissorsApplied = 0;
bool applyToAll = false;
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
{
if (New.ScissorTestEnabled[Index])
if (New.ScissorTestEnabled[index])
{
// If viewport arrays are unavailable apply first scissor test to all or
// there is only 1 scissor test and it's the first, the scissor test applies to all viewports
if (!OGLExtension.Required.ViewportArray || (Index == 0 && New.ScissorTestCount == 1))
if (!OglExtension.Required.ViewportArray || (index == 0 && New.ScissorTestCount == 1))
{
GL.Enable(EnableCap.ScissorTest);
applyToAll = true;
}
else
{
GL.Enable(IndexedEnableCap.ScissorTest, Index);
GL.Enable(IndexedEnableCap.ScissorTest, index);
}
if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index] ||
New.ScissorTestX[Index] != Old.ScissorTestX[Index] ||
New.ScissorTestY[Index] != Old.ScissorTestY[Index] ||
New.ScissorTestWidth[Index] != Old.ScissorTestWidth[Index] ||
New.ScissorTestHeight[Index] != Old.ScissorTestHeight[Index])
if (New.ScissorTestEnabled[index] != _old.ScissorTestEnabled[index] ||
New.ScissorTestX[index] != _old.ScissorTestX[index] ||
New.ScissorTestY[index] != _old.ScissorTestY[index] ||
New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] ||
New.ScissorTestHeight[index] != _old.ScissorTestHeight[index])
{
if (applyToAll)
{
GL.Scissor(New.ScissorTestX[Index], New.ScissorTestY[Index],
New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]);
GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index],
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
}
else
{
GL.ScissorIndexed(Index, New.ScissorTestX[Index], New.ScissorTestY[Index],
New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]);
GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index],
New.ScissorTestWidth[index], New.ScissorTestHeight[index]);
}
}
// If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining itterations
if (!OGLExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount)
// If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining iterations
if (!OglExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount)
{
break;
}
@ -323,26 +323,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (New.BlendIndependent)
{
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
{
SetBlendState(Index, New.Blends[Index], Old.Blends[Index]);
SetBlendState(index, New.Blends[index], _old.Blends[index]);
}
}
else
{
if (New.BlendIndependent != Old.BlendIndependent)
if (New.BlendIndependent != _old.BlendIndependent)
{
SetAllBlendState(New.Blends[0]);
}
else
{
SetBlendState(New.Blends[0], Old.Blends[0]);
SetBlendState(New.Blends[0], _old.Blends[0]);
}
}
if (New.ColorMaskCommon)
{
if (New.ColorMaskCommon != Old.ColorMaskCommon || !New.ColorMasks[0].Equals(Old.ColorMasks[0]))
if (New.ColorMaskCommon != _old.ColorMaskCommon || !New.ColorMasks[0].Equals(_old.ColorMasks[0]))
{
GL.ColorMask(
New.ColorMasks[0].Red,
@ -353,39 +353,39 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
else
{
for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++)
{
if (!New.ColorMasks[Index].Equals(Old.ColorMasks[Index]))
if (!New.ColorMasks[index].Equals(_old.ColorMasks[index]))
{
GL.ColorMask(
Index,
New.ColorMasks[Index].Red,
New.ColorMasks[Index].Green,
New.ColorMasks[Index].Blue,
New.ColorMasks[Index].Alpha);
index,
New.ColorMasks[index].Red,
New.ColorMasks[index].Green,
New.ColorMasks[index].Blue,
New.ColorMasks[index].Alpha);
}
}
}
if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled)
if (New.PrimitiveRestartEnabled != _old.PrimitiveRestartEnabled)
{
Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled);
}
if (New.PrimitiveRestartEnabled)
{
if (New.PrimitiveRestartIndex != Old.PrimitiveRestartIndex)
if (New.PrimitiveRestartIndex != _old.PrimitiveRestartIndex)
{
GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex);
}
}
Old = New;
_old = New;
}
public void Unbind(GalPipelineState State)
public void Unbind(GalPipelineState state)
{
if (State.ScissorTestCount > 0)
if (state.ScissorTestCount > 0)
{
GL.Disable(EnableCap.ScissorTest);
}
@ -400,29 +400,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL
if (New.SeparateAlpha)
{
GL.BlendEquationSeparate(
OGLEnumConverter.GetBlendEquation(New.EquationRgb),
OGLEnumConverter.GetBlendEquation(New.EquationAlpha));
OglEnumConverter.GetBlendEquation(New.EquationRgb),
OglEnumConverter.GetBlendEquation(New.EquationAlpha));
GL.BlendFuncSeparate(
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha));
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha));
}
else
{
GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb));
GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb));
GL.BlendFunc(
OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
OGLEnumConverter.GetBlendFactor(New.FuncDstRgb));
OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
OglEnumConverter.GetBlendFactor(New.FuncDstRgb));
}
}
}
private void SetBlendState(BlendState New, BlendState Old)
private void SetBlendState(BlendState New, BlendState old)
{
if (New.Enabled != Old.Enabled)
if (New.Enabled != old.Enabled)
{
Enable(EnableCap.Blend, New.Enabled);
}
@ -431,91 +431,91 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
if (New.SeparateAlpha)
{
if (New.EquationRgb != Old.EquationRgb ||
New.EquationAlpha != Old.EquationAlpha)
if (New.EquationRgb != old.EquationRgb ||
New.EquationAlpha != old.EquationAlpha)
{
GL.BlendEquationSeparate(
OGLEnumConverter.GetBlendEquation(New.EquationRgb),
OGLEnumConverter.GetBlendEquation(New.EquationAlpha));
OglEnumConverter.GetBlendEquation(New.EquationRgb),
OglEnumConverter.GetBlendEquation(New.EquationAlpha));
}
if (New.FuncSrcRgb != Old.FuncSrcRgb ||
New.FuncDstRgb != Old.FuncDstRgb ||
New.FuncSrcAlpha != Old.FuncSrcAlpha ||
New.FuncDstAlpha != Old.FuncDstAlpha)
if (New.FuncSrcRgb != old.FuncSrcRgb ||
New.FuncDstRgb != old.FuncDstRgb ||
New.FuncSrcAlpha != old.FuncSrcAlpha ||
New.FuncDstAlpha != old.FuncDstAlpha)
{
GL.BlendFuncSeparate(
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha));
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha));
}
}
else
{
if (New.EquationRgb != Old.EquationRgb)
if (New.EquationRgb != old.EquationRgb)
{
GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb));
GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb));
}
if (New.FuncSrcRgb != Old.FuncSrcRgb ||
New.FuncDstRgb != Old.FuncDstRgb)
if (New.FuncSrcRgb != old.FuncSrcRgb ||
New.FuncDstRgb != old.FuncDstRgb)
{
GL.BlendFunc(
OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
OGLEnumConverter.GetBlendFactor(New.FuncDstRgb));
OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
OglEnumConverter.GetBlendFactor(New.FuncDstRgb));
}
}
}
}
private void SetBlendState(int Index, BlendState New, BlendState Old)
private void SetBlendState(int index, BlendState New, BlendState old)
{
if (New.Enabled != Old.Enabled)
if (New.Enabled != old.Enabled)
{
Enable(IndexedEnableCap.Blend, Index, New.Enabled);
Enable(IndexedEnableCap.Blend, index, New.Enabled);
}
if (New.Enabled)
{
if (New.SeparateAlpha)
{
if (New.EquationRgb != Old.EquationRgb ||
New.EquationAlpha != Old.EquationAlpha)
if (New.EquationRgb != old.EquationRgb ||
New.EquationAlpha != old.EquationAlpha)
{
GL.BlendEquationSeparate(
Index,
OGLEnumConverter.GetBlendEquation(New.EquationRgb),
OGLEnumConverter.GetBlendEquation(New.EquationAlpha));
index,
OglEnumConverter.GetBlendEquation(New.EquationRgb),
OglEnumConverter.GetBlendEquation(New.EquationAlpha));
}
if (New.FuncSrcRgb != Old.FuncSrcRgb ||
New.FuncDstRgb != Old.FuncDstRgb ||
New.FuncSrcAlpha != Old.FuncSrcAlpha ||
New.FuncDstAlpha != Old.FuncDstAlpha)
if (New.FuncSrcRgb != old.FuncSrcRgb ||
New.FuncDstRgb != old.FuncDstRgb ||
New.FuncSrcAlpha != old.FuncSrcAlpha ||
New.FuncDstAlpha != old.FuncDstAlpha)
{
GL.BlendFuncSeparate(
Index,
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha));
index,
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb),
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha));
}
}
else
{
if (New.EquationRgb != Old.EquationRgb)
if (New.EquationRgb != old.EquationRgb)
{
GL.BlendEquation(Index, OGLEnumConverter.GetBlendEquation(New.EquationRgb));
GL.BlendEquation(index, OglEnumConverter.GetBlendEquation(New.EquationRgb));
}
if (New.FuncSrcRgb != Old.FuncSrcRgb ||
New.FuncDstRgb != Old.FuncDstRgb)
if (New.FuncSrcRgb != old.FuncSrcRgb ||
New.FuncDstRgb != old.FuncDstRgb)
{
GL.BlendFunc(
Index,
(BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb));
index,
(BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb),
(BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb));
}
}
}
@ -523,310 +523,310 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private void BindConstBuffers(GalPipelineState New)
{
int FreeBinding = OGLShader.ReservedCbufCount;
int freeBinding = OglShader.ReservedCbufCount;
void BindIfNotNull(OGLShaderStage Stage)
void BindIfNotNull(OglShaderStage stage)
{
if (Stage != null)
if (stage != null)
{
foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
foreach (ShaderDeclInfo declInfo in stage.ConstBufferUsage)
{
long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf];
long key = New.ConstBufferKeys[(int)stage.Type][declInfo.Cbuf];
if (Key != 0 && Buffer.TryGetUbo(Key, out int UboHandle))
if (key != 0 && _buffer.TryGetUbo(key, out int uboHandle))
{
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, UboHandle);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, freeBinding, uboHandle);
}
FreeBinding++;
freeBinding++;
}
}
}
BindIfNotNull(Shader.Current.Vertex);
BindIfNotNull(Shader.Current.TessControl);
BindIfNotNull(Shader.Current.TessEvaluation);
BindIfNotNull(Shader.Current.Geometry);
BindIfNotNull(Shader.Current.Fragment);
BindIfNotNull(_shader.Current.Vertex);
BindIfNotNull(_shader.Current.TessControl);
BindIfNotNull(_shader.Current.TessEvaluation);
BindIfNotNull(_shader.Current.Geometry);
BindIfNotNull(_shader.Current.Fragment);
}
private void BindVertexLayout(GalPipelineState New)
{
foreach (GalVertexBinding Binding in New.VertexBindings)
foreach (GalVertexBinding binding in New.VertexBindings)
{
if (!Binding.Enabled || !Rasterizer.TryGetVbo(Binding.VboKey, out int VboHandle))
if (!binding.Enabled || !_rasterizer.TryGetVbo(binding.VboKey, out int vboHandle))
{
continue;
}
if (VaoHandle == 0)
if (_vaoHandle == 0)
{
VaoHandle = GL.GenVertexArray();
_vaoHandle = GL.GenVertexArray();
//Vertex arrays shouldn't be used anywhere else in OpenGL's backend
//if you want to use it, move this line out of the if
GL.BindVertexArray(VaoHandle);
GL.BindVertexArray(_vaoHandle);
}
foreach (GalVertexAttrib Attrib in Binding.Attribs)
foreach (GalVertexAttrib attrib in binding.Attribs)
{
//Skip uninitialized attributes.
if (Attrib.Size == 0)
if (attrib.Size == 0)
{
continue;
}
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.BindBuffer(BufferTarget.ArrayBuffer, vboHandle);
bool Unsigned =
Attrib.Type == GalVertexAttribType.Unorm ||
Attrib.Type == GalVertexAttribType.Uint ||
Attrib.Type == GalVertexAttribType.Uscaled;
bool unsigned =
attrib.Type == GalVertexAttribType.Unorm ||
attrib.Type == GalVertexAttribType.Uint ||
attrib.Type == GalVertexAttribType.Uscaled;
bool Normalize =
Attrib.Type == GalVertexAttribType.Snorm ||
Attrib.Type == GalVertexAttribType.Unorm;
bool normalize =
attrib.Type == GalVertexAttribType.Snorm ||
attrib.Type == GalVertexAttribType.Unorm;
VertexAttribPointerType Type = 0;
VertexAttribPointerType type = 0;
if (Attrib.Type == GalVertexAttribType.Float)
if (attrib.Type == GalVertexAttribType.Float)
{
Type = GetType(FloatAttribTypes, Attrib);
type = GetType(_floatAttribTypes, attrib);
}
else
{
if (Unsigned)
if (unsigned)
{
Type = GetType(UnsignedAttribTypes, Attrib);
type = GetType(_unsignedAttribTypes, attrib);
}
else
{
Type = GetType(SignedAttribTypes, Attrib);
type = GetType(_signedAttribTypes, attrib);
}
}
if (!AttribElements.TryGetValue(Attrib.Size, out int Size))
if (!_attribElements.TryGetValue(attrib.Size, out int size))
{
throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!");
throw new InvalidOperationException("Invalid attribute size \"" + attrib.Size + "\"!");
}
int Offset = Attrib.Offset;
int offset = attrib.Offset;
if (Binding.Stride != 0)
if (binding.Stride != 0)
{
GL.EnableVertexAttribArray(Attrib.Index);
GL.EnableVertexAttribArray(attrib.Index);
if (Attrib.Type == GalVertexAttribType.Sint ||
Attrib.Type == GalVertexAttribType.Uint)
if (attrib.Type == GalVertexAttribType.Sint ||
attrib.Type == GalVertexAttribType.Uint)
{
IntPtr Pointer = new IntPtr(Offset);
IntPtr pointer = new IntPtr(offset);
VertexAttribIntegerType IType = (VertexAttribIntegerType)Type;
VertexAttribIntegerType iType = (VertexAttribIntegerType)type;
GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer);
GL.VertexAttribIPointer(attrib.Index, size, iType, binding.Stride, pointer);
}
else
{
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
GL.VertexAttribPointer(attrib.Index, size, type, normalize, binding.Stride, offset);
}
}
else
{
GL.DisableVertexAttribArray(Attrib.Index);
GL.DisableVertexAttribArray(attrib.Index);
SetConstAttrib(Attrib);
SetConstAttrib(attrib);
}
if (Binding.Instanced && Binding.Divisor != 0)
if (binding.Instanced && binding.Divisor != 0)
{
GL.VertexAttribDivisor(Attrib.Index, 1);
GL.VertexAttribDivisor(attrib.Index, 1);
}
else
{
GL.VertexAttribDivisor(Attrib.Index, 0);
GL.VertexAttribDivisor(attrib.Index, 0);
}
}
}
}
private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> Dict, GalVertexAttrib Attrib)
private static VertexAttribPointerType GetType(Dictionary<GalVertexAttribSize, VertexAttribPointerType> dict, GalVertexAttrib attrib)
{
if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type))
if (!dict.TryGetValue(attrib.Size, out VertexAttribPointerType type))
{
ThrowUnsupportedAttrib(Attrib);
ThrowUnsupportedAttrib(attrib);
}
return Type;
return type;
}
private unsafe static void SetConstAttrib(GalVertexAttrib Attrib)
private static unsafe void SetConstAttrib(GalVertexAttrib attrib)
{
if (Attrib.Size == GalVertexAttribSize._10_10_10_2 ||
Attrib.Size == GalVertexAttribSize._11_11_10)
if (attrib.Size == GalVertexAttribSize._10_10_10_2 ||
attrib.Size == GalVertexAttribSize._11_11_10)
{
ThrowUnsupportedAttrib(Attrib);
ThrowUnsupportedAttrib(attrib);
}
fixed (byte* Ptr = Attrib.Data)
fixed (byte* ptr = attrib.Data)
{
if (Attrib.Type == GalVertexAttribType.Unorm)
if (attrib.Type == GalVertexAttribType.Unorm)
{
switch (Attrib.Size)
switch (attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, Ptr);
GL.VertexAttrib4N((uint)attrib.Index, ptr);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Ptr);
GL.VertexAttrib4N((uint)attrib.Index, (ushort*)ptr);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Ptr);
GL.VertexAttrib4N((uint)attrib.Index, (uint*)ptr);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Snorm)
else if (attrib.Type == GalVertexAttribType.Snorm)
{
switch (Attrib.Size)
switch (attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr);
GL.VertexAttrib4N((uint)attrib.Index, (sbyte*)ptr);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttrib4N((uint)Attrib.Index, (short*)Ptr);
GL.VertexAttrib4N((uint)attrib.Index, (short*)ptr);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4N((uint)Attrib.Index, (int*)Ptr);
GL.VertexAttrib4N((uint)attrib.Index, (int*)ptr);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Uint)
else if (attrib.Type == GalVertexAttribType.Uint)
{
switch (Attrib.Size)
switch (attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, Ptr);
GL.VertexAttribI4((uint)attrib.Index, ptr);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Ptr);
GL.VertexAttribI4((uint)attrib.Index, (ushort*)ptr);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttribI4((uint)Attrib.Index, (uint*)Ptr);
GL.VertexAttribI4((uint)attrib.Index, (uint*)ptr);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Sint)
else if (attrib.Type == GalVertexAttribType.Sint)
{
switch (Attrib.Size)
switch (attrib.Size)
{
case GalVertexAttribSize._8:
case GalVertexAttribSize._8_8:
case GalVertexAttribSize._8_8_8:
case GalVertexAttribSize._8_8_8_8:
GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr);
GL.VertexAttribI4((uint)attrib.Index, (sbyte*)ptr);
break;
case GalVertexAttribSize._16:
case GalVertexAttribSize._16_16:
case GalVertexAttribSize._16_16_16:
case GalVertexAttribSize._16_16_16_16:
GL.VertexAttribI4((uint)Attrib.Index, (short*)Ptr);
GL.VertexAttribI4((uint)attrib.Index, (short*)ptr);
break;
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttribI4((uint)Attrib.Index, (int*)Ptr);
GL.VertexAttribI4((uint)attrib.Index, (int*)ptr);
break;
}
}
else if (Attrib.Type == GalVertexAttribType.Float)
else if (attrib.Type == GalVertexAttribType.Float)
{
switch (Attrib.Size)
switch (attrib.Size)
{
case GalVertexAttribSize._32:
case GalVertexAttribSize._32_32:
case GalVertexAttribSize._32_32_32:
case GalVertexAttribSize._32_32_32_32:
GL.VertexAttrib4(Attrib.Index, (float*)Ptr);
GL.VertexAttrib4(attrib.Index, (float*)ptr);
break;
default: ThrowUnsupportedAttrib(Attrib); break;
default: ThrowUnsupportedAttrib(attrib); break;
}
}
}
}
private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib)
private static void ThrowUnsupportedAttrib(GalVertexAttrib attrib)
{
throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!");
throw new NotImplementedException("Unsupported size \"" + attrib.Size + "\" on type \"" + attrib.Type + "\"!");
}
private void Enable(EnableCap Cap, bool Enabled)
private void Enable(EnableCap cap, bool enabled)
{
if (Enabled)
if (enabled)
{
GL.Enable(Cap);
GL.Enable(cap);
}
else
{
GL.Disable(Cap);
GL.Disable(cap);
}
}
private void Enable(IndexedEnableCap Cap, int Index, bool Enabled)
private void Enable(IndexedEnableCap cap, int index, bool enabled)
{
if (Enabled)
if (enabled)
{
GL.Enable(Cap, Index);
GL.Enable(cap, index);
}
else
{
GL.Disable(Cap, Index);
GL.Disable(cap, index);
}
}
public void ResetDepthMask()
{
Old.DepthWriteEnabled = true;
_old.DepthWriteEnabled = true;
}
public void ResetColorMask(int Index)
public void ResetColorMask(int index)
{
Old.ColorMasks[Index] = ColorMaskState.Default;
_old.ColorMasks[index] = ColorMaskState.Default;
}
}
}

View file

@ -0,0 +1,207 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglRasterizer : IGalRasterizer
{
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] _vertexBuffers;
private OglCachedResource<int> _vboCache;
private OglCachedResource<int> _iboCache;
private struct IbInfo
{
public int Count;
public int ElemSizeLog2;
public DrawElementsType Type;
}
private IbInfo _indexBuffer;
public OglRasterizer()
{
_vertexBuffers = new int[32];
_vboCache = new OglCachedResource<int>(GL.DeleteBuffer, MaxVertexBufferCacheSize);
_iboCache = new OglCachedResource<int>(GL.DeleteBuffer, MaxIndexBufferCacheSize);
_indexBuffer = new IbInfo();
}
public void LockCaches()
{
_vboCache.Lock();
_iboCache.Lock();
}
public void UnlockCaches()
{
_vboCache.Unlock();
_iboCache.Unlock();
}
public void ClearBuffers(
GalClearBufferFlags flags,
int attachment,
float red,
float green,
float blue,
float alpha,
float depth,
int stencil)
{
GL.ColorMask(
attachment,
flags.HasFlag(GalClearBufferFlags.ColorRed),
flags.HasFlag(GalClearBufferFlags.ColorGreen),
flags.HasFlag(GalClearBufferFlags.ColorBlue),
flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearBuffer(ClearBuffer.Color, attachment, new float[] { red, green, blue, alpha });
GL.ColorMask(attachment, true, true, true, true);
GL.DepthMask(true);
if (flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref depth);
}
if (flags.HasFlag(GalClearBufferFlags.Stencil))
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencil);
}
}
public bool IsVboCached(long key, long dataSize)
{
return _vboCache.TryGetSize(key, out long size) && size == dataSize;
}
public bool IsIboCached(long key, long dataSize)
{
return _iboCache.TryGetSize(key, out long size) && size == dataSize;
}
public void CreateVbo(long key, int dataSize, IntPtr hostAddress)
{
int handle = GL.GenBuffer();
_vboCache.AddOrUpdate(key, handle, dataSize);
IntPtr length = new IntPtr(dataSize);
GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
GL.BufferData(BufferTarget.ArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw);
}
public void CreateVbo(long key, byte[] data)
{
int handle = GL.GenBuffer();
_vboCache.AddOrUpdate(key, handle, data.Length);
IntPtr length = new IntPtr(data.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
GL.BufferData(BufferTarget.ArrayBuffer, length, data, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long key, int dataSize, IntPtr hostAddress)
{
int handle = GL.GenBuffer();
_iboCache.AddOrUpdate(key, handle, (uint)dataSize);
IntPtr length = new IntPtr(dataSize);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long key, int dataSize, byte[] buffer)
{
int handle = GL.GenBuffer();
_iboCache.AddOrUpdate(key, handle, dataSize);
IntPtr length = new IntPtr(buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, length, buffer, BufferUsageHint.StreamDraw);
}
public void SetIndexArray(int size, GalIndexFormat format)
{
_indexBuffer.Type = OglEnumConverter.GetDrawElementsType(format);
_indexBuffer.Count = size >> (int)format;
_indexBuffer.ElemSizeLog2 = (int)format;
}
public void DrawArrays(int first, int count, GalPrimitiveType primType)
{
if (count == 0)
{
return;
}
if (primType == GalPrimitiveType.Quads)
{
for (int offset = 0; offset < count; offset += 4)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4);
}
}
else if (primType == GalPrimitiveType.QuadStrip)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first, 4);
for (int offset = 2; offset < count; offset += 2)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4);
}
}
else
{
GL.DrawArrays(OglEnumConverter.GetPrimitiveType(primType), first, count);
}
}
public void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType)
{
if (!_iboCache.TryGetValue(iboKey, out int iboHandle))
{
return;
}
PrimitiveType mode = OglEnumConverter.GetPrimitiveType(primType);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, iboHandle);
first <<= _indexBuffer.ElemSizeLog2;
if (vertexBase != 0)
{
IntPtr indices = new IntPtr(first);
GL.DrawElementsBaseVertex(mode, _indexBuffer.Count, _indexBuffer.Type, indices, vertexBase);
}
else
{
GL.DrawElements(mode, _indexBuffer.Count, _indexBuffer.Type, first);
}
}
public bool TryGetVbo(long vboKey, out int vboHandle)
{
return _vboCache.TryGetValue(vboKey, out vboHandle);
}
}
}

View file

@ -0,0 +1,549 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglRenderTarget : IGalRenderTarget
{
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount;
private struct Rect
{
public int X { get; private set; }
public int Y { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public Rect(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
}
private class FrameBufferAttachments
{
public int MapCount { get; set; }
public DrawBuffersEnum[] Map { get; private set; }
public long[] Colors { get; private set; }
public long Zeta { get; set; }
public FrameBufferAttachments()
{
Colors = new long[RenderTargetsCount];
Map = new DrawBuffersEnum[RenderTargetsCount];
}
public void Update(FrameBufferAttachments source)
{
for (int index = 0; index < RenderTargetsCount; index++)
{
Map[index] = source.Map[index];
Colors[index] = source.Colors[index];
}
MapCount = source.MapCount;
Zeta = source.Zeta;
}
}
private int[] _colorHandles;
private int _zetaHandle;
private OglTexture _texture;
private ImageHandler _readTex;
private Rect _window;
private float[] _viewports;
private bool _flipX;
private bool _flipY;
private int _cropTop;
private int _cropLeft;
private int _cropRight;
private int _cropBottom;
//This framebuffer is used to attach guest rendertargets,
//think of it as a dummy OpenGL VAO
private int _dummyFrameBuffer;
//These framebuffers are used to blit images
private int _srcFb;
private int _dstFb;
private FrameBufferAttachments _attachments;
private FrameBufferAttachments _oldAttachments;
private int _copyPbo;
public bool FramebufferSrgb { get; set; }
public OglRenderTarget(OglTexture texture)
{
_attachments = new FrameBufferAttachments();
_oldAttachments = new FrameBufferAttachments();
_colorHandles = new int[RenderTargetsCount];
_viewports = new float[RenderTargetsCount * 4];
_texture = texture;
texture.TextureDeleted += TextureDeletionHandler;
}
private void TextureDeletionHandler(object sender, int handle)
{
//Texture was deleted, the handle is no longer valid, so
//reset all uses of this handle on a render target.
for (int attachment = 0; attachment < RenderTargetsCount; attachment++)
{
if (_colorHandles[attachment] == handle)
{
_colorHandles[attachment] = 0;
}
}
if (_zetaHandle == handle)
{
_zetaHandle = 0;
}
}
public void Bind()
{
if (_dummyFrameBuffer == 0)
{
_dummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dummyFrameBuffer);
ImageHandler cachedImage;
for (int attachment = 0; attachment < RenderTargetsCount; attachment++)
{
long key = _attachments.Colors[attachment];
int handle = 0;
if (key != 0 && _texture.TryGetImageHandler(key, out cachedImage))
{
handle = cachedImage.Handle;
}
if (handle == _colorHandles[attachment])
{
continue;
}
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + attachment,
handle,
0);
_colorHandles[attachment] = handle;
}
if (_attachments.Zeta != 0 && _texture.TryGetImageHandler(_attachments.Zeta, out cachedImage))
{
if (cachedImage.Handle != _zetaHandle)
{
if (cachedImage.HasDepth && cachedImage.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
cachedImage.Handle,
0);
}
else if (cachedImage.HasDepth)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
cachedImage.Handle,
0);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else
{
throw new InvalidOperationException("Invalid image format \"" + cachedImage.Format + "\" used as Zeta!");
}
_zetaHandle = cachedImage.Handle;
}
}
else if (_zetaHandle != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
_zetaHandle = 0;
}
if (OglExtension.ViewportArray)
{
GL.ViewportArray(0, RenderTargetsCount, _viewports);
}
else
{
GL.Viewport(
(int)_viewports[0],
(int)_viewports[1],
(int)_viewports[2],
(int)_viewports[3]);
}
if (_attachments.MapCount > 1)
{
GL.DrawBuffers(_attachments.MapCount, _attachments.Map);
}
else if (_attachments.MapCount == 1)
{
GL.DrawBuffer((DrawBufferMode)_attachments.Map[0]);
}
else
{
GL.DrawBuffer(DrawBufferMode.None);
}
_oldAttachments.Update(_attachments);
}
public void BindColor(long key, int attachment)
{
_attachments.Colors[attachment] = key;
}
public void UnbindColor(int attachment)
{
_attachments.Colors[attachment] = 0;
}
public void BindZeta(long key)
{
_attachments.Zeta = key;
}
public void UnbindZeta()
{
_attachments.Zeta = 0;
}
public void Present(long key)
{
_texture.TryGetImageHandler(key, out _readTex);
}
public void SetMap(int[] map)
{
if (map != null)
{
_attachments.MapCount = map.Length;
for (int attachment = 0; attachment < _attachments.MapCount; attachment++)
{
_attachments.Map[attachment] = DrawBuffersEnum.ColorAttachment0 + map[attachment];
}
}
else
{
_attachments.MapCount = 0;
}
}
public void SetTransform(bool flipX, bool flipY, int top, int left, int right, int bottom)
{
_flipX = flipX;
_flipY = flipY;
_cropTop = top;
_cropLeft = left;
_cropRight = right;
_cropBottom = bottom;
}
public void SetWindowSize(int width, int height)
{
_window = new Rect(0, 0, width, height);
}
public void SetViewport(int attachment, int x, int y, int width, int height)
{
int offset = attachment * 4;
_viewports[offset + 0] = x;
_viewports[offset + 1] = y;
_viewports[offset + 2] = width;
_viewports[offset + 3] = height;
}
public void Render()
{
if (_readTex == null)
{
return;
}
int srcX0, srcX1, srcY0, srcY1;
if (_cropLeft == 0 && _cropRight == 0)
{
srcX0 = 0;
srcX1 = _readTex.Width;
}
else
{
srcX0 = _cropLeft;
srcX1 = _cropRight;
}
if (_cropTop == 0 && _cropBottom == 0)
{
srcY0 = 0;
srcY1 = _readTex.Height;
}
else
{
srcY0 = _cropTop;
srcY1 = _cropBottom;
}
float ratioX = MathF.Min(1f, (_window.Height * (float)NativeWidth) / ((float)NativeHeight * _window.Width));
float ratioY = MathF.Min(1f, (_window.Width * (float)NativeHeight) / ((float)NativeWidth * _window.Height));
int dstWidth = (int)(_window.Width * ratioX);
int dstHeight = (int)(_window.Height * ratioY);
int dstPaddingX = (_window.Width - dstWidth) / 2;
int dstPaddingY = (_window.Height - dstHeight) / 2;
int dstX0 = _flipX ? _window.Width - dstPaddingX : dstPaddingX;
int dstX1 = _flipX ? dstPaddingX : _window.Width - dstPaddingX;
int dstY0 = _flipY ? dstPaddingY : _window.Height - dstPaddingY;
int dstY1 = _flipY ? _window.Height - dstPaddingY : dstPaddingY;
GL.Viewport(0, 0, _window.Width, _window.Height);
if (_srcFb == 0)
{
_srcFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, _readTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Disable(EnableCap.FramebufferSrgb);
GL.BlitFramebuffer(
srcX0,
srcY0,
srcX1,
srcY1,
dstX0,
dstY0,
dstX1,
dstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
if (FramebufferSrgb)
{
GL.Enable(EnableCap.FramebufferSrgb);
}
}
public void Copy(
GalImage srcImage,
GalImage dstImage,
long srcKey,
long dstKey,
int srcLayer,
int dstLayer,
int srcX0,
int srcY0,
int srcX1,
int srcY1,
int dstX0,
int dstY0,
int dstX1,
int dstY1)
{
if (_texture.TryGetImageHandler(srcKey, out ImageHandler srcTex) &&
_texture.TryGetImageHandler(dstKey, out ImageHandler dstTex))
{
if (srcTex.HasColor != dstTex.HasColor ||
srcTex.HasDepth != dstTex.HasDepth ||
srcTex.HasStencil != dstTex.HasStencil)
{
throw new NotImplementedException();
}
if (_srcFb == 0)
{
_srcFb = GL.GenFramebuffer();
}
if (_dstFb == 0)
{
_dstFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dstFb);
FramebufferAttachment attachment = GetAttachment(srcTex);
if (ImageUtils.IsArray(srcImage.TextureTarget) && srcLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0, srcLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0);
}
if (ImageUtils.IsArray(dstImage.TextureTarget) && dstLayer > 0)
{
GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0, dstLayer);
}
else
{
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0);
}
BlitFramebufferFilter filter = BlitFramebufferFilter.Nearest;
if (srcTex.HasColor)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
filter = BlitFramebufferFilter.Linear;
}
ClearBufferMask mask = GetClearMask(srcTex);
GL.BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
}
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 &&
newImage.Depth == oldImage.Depth &&
newImage.LayerCount == oldImage.LayerCount &&
newImage.TextureTarget == oldImage.TextureTarget)
{
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);
TextureTarget target = ImageUtils.GetTextureTarget(newImage.TextureTarget);
GL.BindTexture(target, cachedImage.Handle);
GL.GetTexImage(target, 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)
{
return FramebufferAttachment.ColorAttachment0;
}
else if (cachedImage.HasDepth && cachedImage.HasStencil)
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (cachedImage.HasDepth)
{
return FramebufferAttachment.DepthAttachment;
}
else if (cachedImage.HasStencil)
{
return FramebufferAttachment.StencilAttachment;
}
else
{
throw new InvalidOperationException();
}
}
private static ClearBufferMask GetClearMask(ImageHandler cachedImage)
{
return (cachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) |
(cachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
(cachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
}
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OglRenderer : IGalRenderer
{
public IGalConstBuffer Buffer { get; private set; }
public IGalRenderTarget RenderTarget { get; private set; }
public IGalRasterizer Rasterizer { get; private set; }
public IGalShader Shader { get; private set; }
public IGalPipeline Pipeline { get; private set; }
public IGalTexture Texture { get; private set; }
private ConcurrentQueue<Action> _actionsQueue;
public OglRenderer()
{
Buffer = new OglConstBuffer();
Texture = new OglTexture();
RenderTarget = new OglRenderTarget(Texture as OglTexture);
Rasterizer = new OglRasterizer();
Shader = new OglShader(Buffer as OglConstBuffer);
Pipeline = new OglPipeline(
Buffer as OglConstBuffer,
RenderTarget as OglRenderTarget,
Rasterizer as OglRasterizer,
Shader as OglShader);
_actionsQueue = new ConcurrentQueue<Action>();
}
public void QueueAction(Action actionMthd)
{
_actionsQueue.Enqueue(actionMthd);
}
public void RunActions()
{
int count = _actionsQueue.Count;
while (count-- > 0 && _actionsQueue.TryDequeue(out Action renderAction))
{
renderAction();
}
}
}
}

View file

@ -0,0 +1,298 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Gal.Shader;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglShader : IGalShader
{
public const int ReservedCbufCount = 1;
private const int ExtraDataSize = 4;
public OglShaderProgram Current;
private ConcurrentDictionary<long, OglShaderStage> _stages;
private Dictionary<OglShaderProgram, int> _programs;
public int CurrentProgramHandle { get; private set; }
private OglConstBuffer _buffer;
private int _extraUboHandle;
public OglShader(OglConstBuffer buffer)
{
_buffer = buffer;
_stages = new ConcurrentDictionary<long, OglShaderStage>();
_programs = new Dictionary<OglShaderProgram, int>();
}
public void Create(IGalMemory memory, long key, GalShaderType type)
{
_stages.GetOrAdd(key, (stage) => ShaderStageFactory(memory, key, 0, false, type));
}
public void Create(IGalMemory memory, long vpAPos, long key, GalShaderType type)
{
_stages.GetOrAdd(key, (stage) => ShaderStageFactory(memory, vpAPos, key, true, type));
}
private OglShaderStage ShaderStageFactory(
IGalMemory memory,
long position,
long positionB,
bool isDualVp,
GalShaderType type)
{
GlslProgram program;
GlslDecompiler decompiler = new GlslDecompiler(OglLimit.MaxUboSize, OglExtension.NvidiaDriver);
int shaderDumpIndex = ShaderDumper.DumpIndex;
if (isDualVp)
{
ShaderDumper.Dump(memory, position, type, "a");
ShaderDumper.Dump(memory, positionB, type, "b");
program = decompiler.Decompile(memory, position, positionB, type);
}
else
{
ShaderDumper.Dump(memory, position, type);
program = decompiler.Decompile(memory, position, type);
}
string code = program.Code;
if (ShaderDumper.IsDumpEnabled())
{
code = "//Shader " + shaderDumpIndex + Environment.NewLine + code;
}
return new OglShaderStage(type, code, program.Uniforms, program.Textures);
}
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long key)
{
if (_stages.TryGetValue(key, out OglShaderStage stage))
{
return stage.ConstBufferUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long key)
{
if (_stages.TryGetValue(key, out OglShaderStage stage))
{
return stage.TextureUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
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);
}
public void Bind(long key)
{
if (_stages.TryGetValue(key, out OglShaderStage stage))
{
Bind(stage);
}
}
private void Bind(OglShaderStage stage)
{
if (stage.Type == GalShaderType.Geometry)
{
//Enhanced layouts are required for Geometry shaders
//skip this stage if current driver has no ARB_enhanced_layouts
if (!OglExtension.EnhancedLayouts)
{
return;
}
}
switch (stage.Type)
{
case GalShaderType.Vertex: Current.Vertex = stage; break;
case GalShaderType.TessControl: Current.TessControl = stage; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = stage; break;
case GalShaderType.Geometry: Current.Geometry = stage; break;
case GalShaderType.Fragment: Current.Fragment = stage; break;
}
}
public void Unbind(GalShaderType type)
{
switch (type)
{
case GalShaderType.Vertex: Current.Vertex = null; break;
case GalShaderType.TessControl: Current.TessControl = null; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break;
case GalShaderType.Geometry: Current.Geometry = null; break;
case GalShaderType.Fragment: Current.Fragment = null; break;
}
}
public void BindProgram()
{
if (Current.Vertex == null ||
Current.Fragment == null)
{
return;
}
if (!_programs.TryGetValue(Current, out int handle))
{
handle = GL.CreateProgram();
AttachIfNotNull(handle, Current.Vertex);
AttachIfNotNull(handle, Current.TessControl);
AttachIfNotNull(handle, Current.TessEvaluation);
AttachIfNotNull(handle, Current.Geometry);
AttachIfNotNull(handle, Current.Fragment);
GL.LinkProgram(handle);
CheckProgramLink(handle);
BindUniformBlocks(handle);
BindTextureLocations(handle);
_programs.Add(Current, handle);
}
GL.UseProgram(handle);
CurrentProgramHandle = handle;
}
private void EnsureExtraBlock()
{
if (_extraUboHandle == 0)
{
_extraUboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.UniformBuffer, _extraUboHandle);
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, _extraUboHandle);
}
}
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);
GL.UniformBlockBinding(programHandle, extraBlockindex, 0);
int freeBinding = ReservedCbufCount;
void BindUniformBlocksIfNotNull(OglShaderStage stage)
{
if (stage != null)
{
foreach (ShaderDeclInfo declInfo in stage.ConstBufferUsage)
{
int blockIndex = GL.GetUniformBlockIndex(programHandle, declInfo.Name);
if (blockIndex < 0)
{
//It is expected that its found, if it's not then driver might be in a malfunction
throw new InvalidOperationException();
}
GL.UniformBlockBinding(programHandle, blockIndex, freeBinding);
freeBinding++;
}
}
}
BindUniformBlocksIfNotNull(Current.Vertex);
BindUniformBlocksIfNotNull(Current.TessControl);
BindUniformBlocksIfNotNull(Current.TessEvaluation);
BindUniformBlocksIfNotNull(Current.Geometry);
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindTextureLocations(int programHandle)
{
int index = 0;
void BindTexturesIfNotNull(OglShaderStage stage)
{
if (stage != null)
{
foreach (ShaderDeclInfo decl in stage.TextureUsage)
{
int location = GL.GetUniformLocation(programHandle, decl.Name);
GL.Uniform1(location, index);
index++;
}
}
}
GL.UseProgram(programHandle);
BindTexturesIfNotNull(Current.Vertex);
BindTexturesIfNotNull(Current.TessControl);
BindTexturesIfNotNull(Current.TessEvaluation);
BindTexturesIfNotNull(Current.Geometry);
BindTexturesIfNotNull(Current.Fragment);
}
private static void CheckProgramLink(int handle)
{
int status = 0;
GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out status);
if (status == 0)
{
throw new ShaderException(GL.GetProgramInfoLog(handle));
}
}
}
}

View file

@ -0,0 +1,86 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
struct OglShaderProgram
{
public OglShaderStage Vertex;
public OglShaderStage TessControl;
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)
{
Type = type;
Code = code;
ConstBufferUsage = constBufferUsage;
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));
}
}
}
}

View file

@ -3,7 +3,7 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLStreamBuffer : IDisposable
class OglStreamBuffer : IDisposable
{
public int Handle { get; protected set; }
@ -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, long 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, (IntPtr)size, IntPtr.Zero, BufferUsageHint.StreamDraw);
}
public void SetData(long Size, IntPtr HostAddress)
public void SetData(long size, IntPtr hostAddress)
{
GL.BindBuffer(Target, Handle);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)size, hostAddress);
}
public void SetData(byte[] Data)
public void SetData(byte[] data)
{
GL.BindBuffer(Target, Handle);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data);
GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)data.Length, data);
}
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);

View file

@ -0,0 +1,381 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglTexture : IGalTexture
{
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private OglCachedResource<ImageHandler> _textureCache;
public EventHandler<int> TextureDeleted { get; set; }
public OglTexture()
{
_textureCache = new OglCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
}
public void LockCache()
{
_textureCache.Lock();
}
public void UnlockCache()
{
_textureCache.Unlock();
}
private void DeleteTexture(ImageHandler cachedImage)
{
TextureDeleted?.Invoke(this, cachedImage.Handle);
GL.DeleteTexture(cachedImage.Handle);
}
public void Create(long key, int size, GalImage image)
{
int handle = GL.GenTexture();
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, 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!");
}
(PixelInternalFormat internalFmt,
PixelFormat format,
PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
switch (target)
{
case TextureTarget.Texture1D:
GL.TexImage1D(
target,
level,
internalFmt,
image.Width,
border,
format,
type,
IntPtr.Zero);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
target,
level,
internalFmt,
image.Width,
image.Height,
border,
format,
type,
IntPtr.Zero);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.Depth,
border,
format,
type,
IntPtr.Zero);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.LayerCount,
border,
format,
type,
IntPtr.Zero);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {target}");
}
}
public void Create(long key, byte[] data, GalImage image)
{
int handle = GL.GenTexture();
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, handle);
const int level = 0; //TODO: Support mipmap textures.
const int border = 0;
_textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length);
if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format))
{
InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format);
switch (target)
{
case TextureTarget.Texture1D:
GL.CompressedTexImage1D(
target,
level,
internalFmt,
image.Width,
border,
data.Length,
data);
break;
case TextureTarget.Texture2D:
GL.CompressedTexImage2D(
target,
level,
internalFmt,
image.Width,
image.Height,
border,
data.Length,
data);
break;
case TextureTarget.Texture3D:
GL.CompressedTexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.Depth,
border,
data.Length,
data);
break;
case TextureTarget.Texture2DArray:
GL.CompressedTexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.LayerCount,
border,
data.Length,
data);
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {target}");
}
}
else
{
//TODO: Use KHR_texture_compression_astc_hdr when available
if (IsAstc(image.Format))
{
int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format);
int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format);
int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format);
data = AstcDecoder.DecodeToRgba8888(
data,
textureBlockWidth,
textureBlockHeight,
textureBlockDepth,
image.Width,
image.Height,
image.Depth);
image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask);
}
(PixelInternalFormat internalFmt,
PixelFormat format,
PixelType type) = OglEnumConverter.GetImageFormat(image.Format);
switch (target)
{
case TextureTarget.Texture1D:
GL.TexImage1D(
target,
level,
internalFmt,
image.Width,
border,
format,
type,
data);
break;
case TextureTarget.Texture2D:
GL.TexImage2D(
target,
level,
internalFmt,
image.Width,
image.Height,
border,
format,
type,
data);
break;
case TextureTarget.Texture3D:
GL.TexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.Depth,
border,
format,
type,
data);
break;
case TextureTarget.Texture2DArray:
GL.TexImage3D(
target,
level,
internalFmt,
image.Width,
image.Height,
image.LayerCount,
border,
format,
type,
data);
break;
case TextureTarget.TextureCubeMap:
Span<byte> array = new Span<byte>(data);
int faceSize = ImageUtils.GetSize(image) / 6;
for (int face = 0; face < 6; face++)
{
GL.TexImage2D(
TextureTarget.TextureCubeMapPositiveX + face,
level,
internalFmt,
image.Width,
image.Height,
border,
format,
type,
array.Slice(face * faceSize, faceSize).ToArray());
}
break;
default:
throw new NotImplementedException($"Unsupported texture target type: {target}");
}
}
}
private static bool IsAstc(GalImageFormat format)
{
format &= GalImageFormat.FormatMask;
return format > GalImageFormat.Astc2DStart && format < GalImageFormat.Astc2DEnd;
}
public bool TryGetImage(long key, out GalImage image)
{
if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
{
image = cachedImage.Image;
return true;
}
image = default(GalImage);
return false;
}
public bool TryGetImageHandler(long key, out ImageHandler cachedImage)
{
if (_textureCache.TryGetValue(key, out cachedImage))
{
return true;
}
cachedImage = null;
return false;
}
public void Bind(long key, int index, GalImage image)
{
if (_textureCache.TryGetValue(key, out ImageHandler cachedImage))
{
GL.ActiveTexture(TextureUnit.Texture0 + index);
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.BindTexture(target, cachedImage.Handle);
int[] swizzleRgba = new int[]
{
(int)OglEnumConverter.GetTextureSwizzle(image.XSource),
(int)OglEnumConverter.GetTextureSwizzle(image.YSource),
(int)OglEnumConverter.GetTextureSwizzle(image.ZSource),
(int)OglEnumConverter.GetTextureSwizzle(image.WSource)
};
GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba);
}
}
public void SetSampler(GalImage image, GalTextureSampler sampler)
{
int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU);
int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV);
int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP);
int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter);
int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter);
TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget);
GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS);
GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT);
GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR);
GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter);
GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter);
float[] color = new float[]
{
sampler.BorderColor.Red,
sampler.BorderColor.Green,
sampler.BorderColor.Blue,
sampler.BorderColor.Alpha
};
GL.TexParameter(target, TextureParameterName.TextureBorderColor, color);
if (sampler.DepthCompare)
{
GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture);
GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc));
}
else
{
GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None);
GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never);
}
}
}
}