diff options
Diffstat (limited to 'Ryujinx.Graphics.OpenGL')
36 files changed, 3367 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs new file mode 100644 index 00000000..b86719ce --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -0,0 +1,74 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Buffer : IBuffer + { + public int Handle { get; } + + public Buffer(int size) + { + Handle = GL.GenBuffer(); + + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.DynamicDraw); + } + + public void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size) + { + GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, ((Buffer)destination).Handle); + + GL.CopyBufferSubData( + BufferTarget.CopyReadBuffer, + BufferTarget.CopyWriteBuffer, + (IntPtr)srcOffset, + (IntPtr)dstOffset, + (IntPtr)size); + } + + public byte[] GetData(int offset, int size) + { + GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + + byte[] data = new byte[size]; + + GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, data); + + return data; + } + + public void SetData(Span<byte> data) + { + unsafe + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + + fixed (byte* ptr = data) + { + GL.BufferData(BufferTarget.CopyWriteBuffer, data.Length, (IntPtr)ptr, BufferUsageHint.DynamicDraw); + } + } + } + + public void SetData(int offset, Span<byte> data) + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + + unsafe + { + fixed (byte* ptr = data) + { + GL.BufferSubData(BufferTarget.CopyWriteBuffer, (IntPtr)offset, data.Length, (IntPtr)ptr); + } + } + } + + public void Dispose() + { + GL.DeleteBuffer(Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/ComputePipeline.cs b/Ryujinx.Graphics.OpenGL/ComputePipeline.cs new file mode 100644 index 00000000..bee96832 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/ComputePipeline.cs @@ -0,0 +1,95 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class ComputePipeline : IComputePipeline + { + private Renderer _renderer; + + private Program _program; + + public ComputePipeline(Renderer renderer) + { + _renderer = renderer; + } + + public void Dispatch(int groupsX, int groupsY, int groupsZ) + { + BindProgram(); + + GL.DispatchCompute(groupsX, groupsY, groupsZ); + + UnbindProgram(); + } + + public void SetProgram(IProgram program) + { + _program = (Program)program; + } + + public void SetStorageBuffer(int index, BufferRange buffer) + { + BindProgram(); + + BindBuffer(index, buffer, isStorage: true); + + UnbindProgram(); + } + + public void SetUniformBuffer(int index, BufferRange buffer) + { + BindProgram(); + + BindBuffer(index, buffer, isStorage: false); + + UnbindProgram(); + } + + private void BindBuffer(int index, BufferRange buffer, bool isStorage) + { + int bindingPoint = isStorage + ? _program.GetStorageBufferBindingPoint(ShaderStage.Compute, index) + : _program.GetUniformBufferBindingPoint(ShaderStage.Compute, index); + + if (bindingPoint == -1) + { + return; + } + + BufferRangeTarget target = isStorage + ? BufferRangeTarget.ShaderStorageBuffer + : BufferRangeTarget.UniformBuffer; + + if (buffer.Buffer == null) + { + GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); + + return; + } + + int bufferHandle = ((Buffer)buffer.Buffer).Handle; + + IntPtr bufferOffset = (IntPtr)buffer.Offset; + + GL.BindBufferRange( + target, + bindingPoint, + bufferHandle, + bufferOffset, + buffer.Size); + } + + private void BindProgram() + { + _program.Bind(); + } + + private void UnbindProgram() + { + ((GraphicsPipeline)_renderer.GraphicsPipeline).RebindProgram(); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs new file mode 100644 index 00000000..8f9b5074 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs @@ -0,0 +1,26 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class AddressModeConverter + { + public static TextureWrapMode Convert(this AddressMode mode) + { + switch (mode) + { + case AddressMode.Clamp : return TextureWrapMode.Clamp; + case AddressMode.Repeat : return TextureWrapMode.Repeat; + case AddressMode.MirrorClamp : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampExt; + case AddressMode.MirrorClampToEdge : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt; + case AddressMode.MirrorClampToBorder : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt; + case AddressMode.ClampToBorder : return TextureWrapMode.ClampToBorder; + case AddressMode.MirroredRepeat : return TextureWrapMode.MirroredRepeat; + case AddressMode.ClampToEdge : return TextureWrapMode.ClampToEdge; + } + + throw new ArgumentException($"Invalid address mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs new file mode 100644 index 00000000..1dc40522 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs @@ -0,0 +1,39 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Blend; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class BlendFactorConverter + { + public static All Convert(this BlendFactor factor) + { + switch (factor) + { + case BlendFactor.Zero: return All.Zero; + case BlendFactor.One: return All.One; + case BlendFactor.SrcColor: return All.SrcColor; + case BlendFactor.OneMinusSrcColor: return All.OneMinusSrcColor; + case BlendFactor.SrcAlpha: return All.SrcAlpha; + case BlendFactor.OneMinusSrcAlpha: return All.OneMinusSrcAlpha; + case BlendFactor.DstAlpha: return All.DstAlpha; + case BlendFactor.OneMinusDstAlpha: return All.OneMinusDstAlpha; + case BlendFactor.DstColor: return All.DstColor; + case BlendFactor.OneMinusDstColor: return All.OneMinusDstColor; + case BlendFactor.SrcAlphaSaturate: return All.SrcAlphaSaturate; + case BlendFactor.Src1Color: return All.Src1Color; + case BlendFactor.OneMinusSrc1Color: return All.OneMinusSrc1Color; + case BlendFactor.Src1Alpha: return All.Src1Alpha; + case BlendFactor.OneMinusSrc1Alpha: return All.OneMinusSrc1Alpha; + case BlendFactor.ConstantColor: return All.ConstantColor; + case BlendFactor.OneMinusConstantColor: return All.OneMinusConstantColor; + case BlendFactor.ConstantAlpha: return All.ConstantAlpha; + case BlendFactor.OneMinusConstantAlpha: return All.OneMinusConstantAlpha; + } + + return All.Zero; + + throw new ArgumentException($"Invalid blend factor \"{factor}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs new file mode 100644 index 00000000..b33a3bf8 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs @@ -0,0 +1,25 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Blend; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class BlendOpConverter + { + public static BlendEquationMode Convert(this BlendOp op) + { + switch (op) + { + case BlendOp.Add: return BlendEquationMode.FuncAdd; + case BlendOp.Subtract: return BlendEquationMode.FuncSubtract; + case BlendOp.ReverseSubtract: return BlendEquationMode.FuncReverseSubtract; + case BlendOp.Minimum: return BlendEquationMode.Min; + case BlendOp.Maximum: return BlendEquationMode.Max; + } + + return BlendEquationMode.FuncAdd; + + throw new ArgumentException($"Invalid blend operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs new file mode 100644 index 00000000..c0287d8a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class CompareModeConverter + { + public static TextureCompareMode Convert(this CompareMode mode) + { + switch (mode) + { + case CompareMode.None: return TextureCompareMode.None; + case CompareMode.CompareRToTexture: return TextureCompareMode.CompareRToTexture; + } + + throw new ArgumentException($"Invalid compare mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs new file mode 100644 index 00000000..f592735b --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs @@ -0,0 +1,28 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class CompareOpConverter + { + public static All Convert(this CompareOp op) + { + switch (op) + { + case CompareOp.Never: return All.Never; + case CompareOp.Less: return All.Less; + case CompareOp.Equal: return All.Equal; + case CompareOp.LessOrEqual: return All.Lequal; + case CompareOp.Greater: return All.Greater; + case CompareOp.NotEqual: return All.Notequal; + case CompareOp.GreaterOrEqual: return All.Gequal; + case CompareOp.Always: return All.Always; + } + + return All.Never; + + throw new ArgumentException($"Invalid compare operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs new file mode 100644 index 00000000..4d14bdea --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class DepthStencilModeConverter + { + public static All Convert(this DepthStencilMode mode) + { + switch (mode) + { + case DepthStencilMode.Depth: return All.Depth; + case DepthStencilMode.Stencil: return All.Stencil; + } + + throw new ArgumentException($"Invalid depth stencil mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs new file mode 100644 index 00000000..5c23b68a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs @@ -0,0 +1,23 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class FaceConverter + { + public static CullFaceMode Convert(this Face face) + { + switch (face) + { + case Face.Back: return CullFaceMode.Back; + case Face.Front: return CullFaceMode.Front; + case Face.FrontAndBack: return CullFaceMode.FrontAndBack; + } + + return CullFaceMode.FrontAndBack; + + throw new ArgumentException($"Invalid face \"{face}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs new file mode 100644 index 00000000..1cae36cd --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs @@ -0,0 +1,22 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class FrontFaceConverter + { + public static FrontFaceDirection Convert(this FrontFace frontFace) + { + switch (frontFace) + { + case FrontFace.Clockwise: return FrontFaceDirection.Cw; + case FrontFace.CounterClockwise: return FrontFaceDirection.Ccw; + } + + return FrontFaceDirection.Cw; + + throw new ArgumentException($"Invalid front face \"{frontFace}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs new file mode 100644 index 00000000..d3100b98 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs @@ -0,0 +1,21 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class IndexTypeConverter + { + public static DrawElementsType Convert(this IndexType type) + { + switch (type) + { + case IndexType.UByte: return DrawElementsType.UnsignedByte; + case IndexType.UShort: return DrawElementsType.UnsignedShort; + case IndexType.UInt: return DrawElementsType.UnsignedInt; + } + + throw new ArgumentException($"Invalid index type \"{type}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs new file mode 100644 index 00000000..d86d8014 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class MagFilterConverter + { + public static TextureMagFilter Convert(this MagFilter filter) + { + switch (filter) + { + case MagFilter.Nearest: return TextureMagFilter.Nearest; + case MagFilter.Linear: return TextureMagFilter.Linear; + } + + throw new ArgumentException($"Invalid filter \"{filter}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs new file mode 100644 index 00000000..128577f8 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs @@ -0,0 +1,24 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class MinFilterConverter + { + public static TextureMinFilter Convert(this MinFilter filter) + { + switch (filter) + { + case MinFilter.Nearest: return TextureMinFilter.Nearest; + case MinFilter.Linear: return TextureMinFilter.Linear; + case MinFilter.NearestMipmapNearest: return TextureMinFilter.NearestMipmapNearest; + case MinFilter.LinearMipmapNearest: return TextureMinFilter.LinearMipmapNearest; + case MinFilter.NearestMipmapLinear: return TextureMinFilter.NearestMipmapLinear; + case MinFilter.LinearMipmapLinear: return TextureMinFilter.LinearMipmapLinear; + } + + throw new ArgumentException($"Invalid filter \"{filter}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs new file mode 100644 index 00000000..fe1dfe39 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs @@ -0,0 +1,33 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class PrimitiveTopologyConverter + { + public static PrimitiveType Convert(this PrimitiveTopology topology) + { + switch (topology) + { + case PrimitiveTopology.Points: return PrimitiveType.Points; + case PrimitiveTopology.Lines: return PrimitiveType.Lines; + case PrimitiveTopology.LineLoop: return PrimitiveType.LineLoop; + case PrimitiveTopology.LineStrip: return PrimitiveType.LineStrip; + case PrimitiveTopology.Triangles: return PrimitiveType.Triangles; + case PrimitiveTopology.TriangleStrip: return PrimitiveType.TriangleStrip; + case PrimitiveTopology.TriangleFan: return PrimitiveType.TriangleFan; + case PrimitiveTopology.Quads: return PrimitiveType.Quads; + case PrimitiveTopology.QuadStrip: return PrimitiveType.QuadStrip; + case PrimitiveTopology.Polygon: return PrimitiveType.Polygon; + case PrimitiveTopology.LinesAdjacency: return PrimitiveType.LinesAdjacency; + case PrimitiveTopology.LineStripAdjacency: return PrimitiveType.LineStripAdjacency; + case PrimitiveTopology.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency; + case PrimitiveTopology.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency; + case PrimitiveTopology.Patches: return PrimitiveType.Patches; + } + + throw new ArgumentException($"Invalid primitive topology \"{topology}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs new file mode 100644 index 00000000..44fbe0a5 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs @@ -0,0 +1,27 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class StencilOpConverter + { + public static StencilOp Convert(this GAL.DepthStencil.StencilOp op) + { + switch (op) + { + case GAL.DepthStencil.StencilOp.Keep: return StencilOp.Keep; + case GAL.DepthStencil.StencilOp.Zero: return StencilOp.Zero; + case GAL.DepthStencil.StencilOp.Replace: return StencilOp.Replace; + case GAL.DepthStencil.StencilOp.IncrementAndClamp: return StencilOp.Incr; + case GAL.DepthStencil.StencilOp.DecrementAndClamp: return StencilOp.Decr; + case GAL.DepthStencil.StencilOp.Invert: return StencilOp.Invert; + case GAL.DepthStencil.StencilOp.IncrementAndWrap: return StencilOp.IncrWrap; + case GAL.DepthStencil.StencilOp.DecrementAndWrap: return StencilOp.DecrWrap; + } + + return StencilOp.Keep; + + throw new ArgumentException($"Invalid stencil operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs new file mode 100644 index 00000000..d9ce650f --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs @@ -0,0 +1,24 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class SwizzleComponentConverter + { + public static All Convert(this SwizzleComponent swizzleComponent) + { + switch (swizzleComponent) + { + case SwizzleComponent.Zero: return All.Zero; + case SwizzleComponent.One: return All.One; + case SwizzleComponent.Red: return All.Red; + case SwizzleComponent.Green: return All.Green; + case SwizzleComponent.Blue: return All.Blue; + case SwizzleComponent.Alpha: return All.Alpha; + } + + throw new ArgumentException($"Invalid swizzle component \"{swizzleComponent}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs new file mode 100644 index 00000000..6b524980 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs @@ -0,0 +1,33 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class TargetConverter + { + public static ImageTarget ConvertToImageTarget(this Target target) + { + return (ImageTarget)target.Convert(); + } + + public static TextureTarget Convert(this Target target) + { + switch (target) + { + case Target.Texture1D: return TextureTarget.Texture1D; + case Target.Texture2D: return TextureTarget.Texture2D; + case Target.Texture3D: return TextureTarget.Texture3D; + case Target.Texture1DArray: return TextureTarget.Texture1DArray; + case Target.Texture2DArray: return TextureTarget.Texture2DArray; + case Target.Texture2DMultisample: return TextureTarget.Texture2DMultisample; + case Target.Rectangle: return TextureTarget.TextureRectangle; + case Target.Cubemap: return TextureTarget.TextureCubeMap; + case Target.CubemapArray: return TextureTarget.TextureCubeMapArray; + case Target.TextureBuffer: return TextureTarget.TextureBuffer; + } + + throw new ArgumentException($"Invalid target \"{target}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Counters.cs b/Ryujinx.Graphics.OpenGL/Counters.cs new file mode 100644 index 00000000..e82a040f --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Counters.cs @@ -0,0 +1,77 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Counters + { + private int[] _queryObjects; + + private ulong[] _accumulatedCounters; + + public Counters() + { + int count = Enum.GetNames(typeof(CounterType)).Length; + + _queryObjects = new int[count]; + + _accumulatedCounters = new ulong[count]; + } + + public void Initialize() + { + for (int index = 0; index < _queryObjects.Length; index++) + { + int handle = GL.GenQuery(); + + _queryObjects[index] = handle; + + CounterType type = (CounterType)index; + + GL.BeginQuery(GetTarget(type), handle); + } + } + + public ulong GetCounter(CounterType type) + { + UpdateAccumulatedCounter(type); + + return _accumulatedCounters[(int)type]; + } + + public void ResetCounter(CounterType type) + { + UpdateAccumulatedCounter(type); + + _accumulatedCounters[(int)type] = 0; + } + + private void UpdateAccumulatedCounter(CounterType type) + { + int handle = _queryObjects[(int)type]; + + QueryTarget target = GetTarget(type); + + GL.EndQuery(target); + + GL.GetQueryObject(handle, GetQueryObjectParam.QueryResult, out long result); + + _accumulatedCounters[(int)type] += (ulong)result; + + GL.BeginQuery(target, handle); + } + + private static QueryTarget GetTarget(CounterType type) + { + switch (type) + { + case CounterType.SamplesPassed: return QueryTarget.SamplesPassed; + case CounterType.PrimitivesGenerated: return QueryTarget.PrimitivesGenerated; + case CounterType.TransformFeedbackPrimitivesWritten: return QueryTarget.TransformFeedbackPrimitivesWritten; + } + + return QueryTarget.SamplesPassed; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Debugger.cs b/Ryujinx.Graphics.OpenGL/Debugger.cs new file mode 100644 index 00000000..9da98ae1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Debugger.cs @@ -0,0 +1,43 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.OpenGL +{ + public static class Debugger + { + private static DebugProc _debugCallback; + + public static void Initialize() + { + GL.Enable(EnableCap.DebugOutputSynchronous); + + int[] array = null; + + GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DontCare, DebugSeverityControl.DontCare, 0, array, true); + + _debugCallback = PrintDbg; + + GL.DebugMessageCallback(_debugCallback, IntPtr.Zero); + } + + private static void PrintDbg( + DebugSource source, + DebugType type, + int id, + DebugSeverity severity, + int length, + IntPtr message, + IntPtr userParam) + { + string msg = Marshal.PtrToStringAnsi(message); + + if (type == DebugType.DebugTypeError && !msg.Contains("link")) + { + throw new Exception(msg); + } + + System.Console.WriteLine("GL message: " + source + " " + type + " " + severity + " " + msg); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs b/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs new file mode 100644 index 00000000..a1fd723f --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs @@ -0,0 +1,45 @@ +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL.Formats +{ + struct FormatInfo + { + public int Components { get; } + public bool Normalized { get; } + public bool Scaled { get; } + + public PixelInternalFormat PixelInternalFormat { get; } + public PixelFormat PixelFormat { get; } + public PixelType PixelType { get; } + + public bool IsCompressed { get; } + + public FormatInfo( + int components, + bool normalized, + bool scaled, + All pixelInternalFormat, + PixelFormat pixelFormat, + PixelType pixelType) + { + Components = components; + Normalized = normalized; + Scaled = scaled; + PixelInternalFormat = (PixelInternalFormat)pixelInternalFormat; + PixelFormat = pixelFormat; + PixelType = pixelType; + IsCompressed = false; + } + + public FormatInfo(int components, bool normalized, bool scaled, All pixelFormat) + { + Components = components; + Normalized = normalized; + Scaled = scaled; + PixelInternalFormat = 0; + PixelFormat = (PixelFormat)pixelFormat; + PixelType = 0; + IsCompressed = true; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs b/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs new file mode 100644 index 00000000..13e88c55 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs @@ -0,0 +1,183 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Formats +{ + struct FormatTable + { + private static FormatInfo[] _table; + + static FormatTable() + { + _table = new FormatInfo[Enum.GetNames(typeof(Format)).Length]; + + Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte)); + Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte)); + Add(Format.R8Uint, new FormatInfo(1, false, false, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte)); + Add(Format.R8Sint, new FormatInfo(1, false, false, All.R8i, PixelFormat.RedInteger, PixelType.Byte)); + Add(Format.R16Float, new FormatInfo(1, false, false, All.R16f, PixelFormat.Red, PixelType.HalfFloat)); + Add(Format.R16Unorm, new FormatInfo(1, true, false, All.R16, PixelFormat.Red, PixelType.UnsignedShort)); + Add(Format.R16Snorm, new FormatInfo(1, true, false, All.R16Snorm, PixelFormat.Red, PixelType.Short)); + Add(Format.R16Uint, new FormatInfo(1, false, false, All.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort)); + Add(Format.R16Sint, new FormatInfo(1, false, false, All.R16i, PixelFormat.RedInteger, PixelType.Short)); + Add(Format.R32Float, new FormatInfo(1, false, false, All.R32f, PixelFormat.Red, PixelType.Float)); + Add(Format.R32Uint, new FormatInfo(1, false, false, All.R32ui, PixelFormat.RedInteger, PixelType.UnsignedInt)); + Add(Format.R32Sint, new FormatInfo(1, false, false, All.R32i, PixelFormat.RedInteger, PixelType.Int)); + Add(Format.R8G8Unorm, new FormatInfo(2, true, false, All.Rg8, PixelFormat.Rg, PixelType.UnsignedByte)); + Add(Format.R8G8Snorm, new FormatInfo(2, true, false, All.Rg8Snorm, PixelFormat.Rg, PixelType.Byte)); + Add(Format.R8G8Uint, new FormatInfo(2, false, false, All.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte)); + Add(Format.R8G8Sint, new FormatInfo(2, false, false, All.Rg8i, PixelFormat.RgInteger, PixelType.Byte)); + Add(Format.R16G16Float, new FormatInfo(2, false, false, All.Rg16f, PixelFormat.Rg, PixelType.HalfFloat)); + Add(Format.R16G16Unorm, new FormatInfo(2, true, false, All.Rg16, PixelFormat.Rg, PixelType.UnsignedShort)); + Add(Format.R16G16Snorm, new FormatInfo(2, true, false, All.Rg16Snorm, PixelFormat.Rg, PixelType.Short)); + Add(Format.R16G16Uint, new FormatInfo(2, false, false, All.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort)); + Add(Format.R16G16Sint, new FormatInfo(2, false, false, All.Rg16i, PixelFormat.RgInteger, PixelType.Short)); + Add(Format.R32G32Float, new FormatInfo(2, false, false, All.Rg32f, PixelFormat.Rg, PixelType.Float)); + Add(Format.R32G32Uint, new FormatInfo(2, false, false, All.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt)); + Add(Format.R32G32Sint, new FormatInfo(2, false, false, All.Rg32i, PixelFormat.RgInteger, PixelType.Int)); + Add(Format.R8G8B8Unorm, new FormatInfo(3, true, false, All.Rgb8, PixelFormat.Rgb, PixelType.UnsignedByte)); + Add(Format.R8G8B8Snorm, new FormatInfo(3, true, false, All.Rgb8Snorm, PixelFormat.Rgb, PixelType.Byte)); + Add(Format.R8G8B8Uint, new FormatInfo(3, false, false, All.Rgb8ui, PixelFormat.RgbInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8Sint, new FormatInfo(3, false, false, All.Rgb8i, PixelFormat.RgbInteger, PixelType.Byte)); + Add(Format.R16G16B16Float, new FormatInfo(3, false, false, All.Rgb16f, PixelFormat.Rgb, PixelType.HalfFloat)); + Add(Format.R16G16B16Unorm, new FormatInfo(3, true, false, All.Rgb16, PixelFormat.Rgb, PixelType.UnsignedShort)); + Add(Format.R16G16B16Snorm, new FormatInfo(3, true, false, All.Rgb16Snorm, PixelFormat.Rgb, PixelType.Short)); + Add(Format.R16G16B16Uint, new FormatInfo(3, false, false, All.Rgb16ui, PixelFormat.RgbInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16Sint, new FormatInfo(3, false, false, All.Rgb16i, PixelFormat.RgbInteger, PixelType.Short)); + Add(Format.R32G32B32Float, new FormatInfo(3, false, false, All.Rgb32f, PixelFormat.Rgb, PixelType.Float)); + Add(Format.R32G32B32Uint, new FormatInfo(3, false, false, All.Rgb32ui, PixelFormat.RgbInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32Sint, new FormatInfo(3, false, false, All.Rgb32i, PixelFormat.RgbInteger, PixelType.Int)); + Add(Format.R8G8B8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Snorm, new FormatInfo(4, true, false, All.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte)); + Add(Format.R8G8B8A8Uint, new FormatInfo(4, false, false, All.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Sint, new FormatInfo(4, false, false, All.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16A16Float, new FormatInfo(4, false, false, All.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat)); + Add(Format.R16G16B16A16Unorm, new FormatInfo(4, true, false, All.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Snorm, new FormatInfo(4, true, false, All.Rgba16Snorm, PixelFormat.Rgba, PixelType.Short)); + Add(Format.R16G16B16A16Uint, new FormatInfo(4, false, false, All.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Sint, new FormatInfo(4, false, false, All.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32A32Float, new FormatInfo(4, false, false, All.Rgba32f, PixelFormat.Rgba, PixelType.Float)); + Add(Format.R32G32B32A32Uint, new FormatInfo(4, false, false, All.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32A32Sint, new FormatInfo(4, false, false, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte)); + Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort)); + Add(Format.D24X8Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt)); + Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float)); + Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248)); + Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev)); + Add(Format.R8G8B8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R4G4B4A4Unorm, new FormatInfo(4, true, false, All.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed)); + Add(Format.R5G5B5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Rgb, PixelType.UnsignedShort1555Reversed)); + Add(Format.R5G5B5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed)); + Add(Format.R5G6B5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed)); + Add(Format.R10G10B10A2Unorm, new FormatInfo(4, true, false, All.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R10G10B10A2Uint, new FormatInfo(4, false, false, All.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R11G11B10Float, new FormatInfo(3, false, false, All.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev)); + Add(Format.R9G9B9E5Float, new FormatInfo(3, false, false, All.Rgb9E5, PixelFormat.Rgb, PixelType.UnsignedInt5999Rev)); + Add(Format.Bc1RgbUnorm, new FormatInfo(2, true, false, All.CompressedRgbS3tcDxt1Ext)); + Add(Format.Bc1RgbaUnorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt1Ext)); + Add(Format.Bc2Unorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt3Ext)); + Add(Format.Bc3Unorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt5Ext)); + Add(Format.Bc1RgbSrgb, new FormatInfo(2, false, false, All.CompressedSrgbS3tcDxt1Ext)); + Add(Format.Bc1RgbaSrgb, new FormatInfo(1, true, false, All.CompressedSrgbAlphaS3tcDxt1Ext)); + Add(Format.Bc2Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaS3tcDxt3Ext)); + Add(Format.Bc3Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaS3tcDxt5Ext)); + Add(Format.Bc4Unorm, new FormatInfo(1, true, false, All.CompressedRedRgtc1)); + Add(Format.Bc4Snorm, new FormatInfo(1, true, false, All.CompressedSignedRedRgtc1)); + Add(Format.Bc5Unorm, new FormatInfo(1, true, false, All.CompressedRgRgtc2)); + Add(Format.Bc5Snorm, new FormatInfo(1, true, false, All.CompressedSignedRgRgtc2)); + Add(Format.Bc7Unorm, new FormatInfo(1, true, false, All.CompressedRgbaBptcUnorm)); + Add(Format.Bc7Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaBptcUnorm)); + Add(Format.Bc6HUfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcUnsignedFloat)); + Add(Format.Bc6HSfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcSignedFloat)); + Add(Format.R8Uscaled, new FormatInfo(1, false, true, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte)); + Add(Format.R8Sscaled, new FormatInfo(1, false, true, All.R8i, PixelFormat.RedInteger, PixelType.Byte)); + Add(Format.R16Uscaled, new FormatInfo(1, false, true, All.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort)); + Add(Format.R16Sscaled, new FormatInfo(1, false, true, All.R16i, PixelFormat.RedInteger, PixelType.Short)); + Add(Format.R32Uscaled, new FormatInfo(1, false, true, All.R32ui, PixelFormat.RedInteger, PixelType.UnsignedInt)); + Add(Format.R32Sscaled, new FormatInfo(1, false, true, All.R32i, PixelFormat.RedInteger, PixelType.Int)); + Add(Format.R8G8Uscaled, new FormatInfo(2, false, true, All.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte)); + Add(Format.R8G8Sscaled, new FormatInfo(2, false, true, All.Rg8i, PixelFormat.RgInteger, PixelType.Byte)); + Add(Format.R16G16Uscaled, new FormatInfo(2, false, true, All.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort)); + Add(Format.R16G16Sscaled, new FormatInfo(2, false, true, All.Rg16i, PixelFormat.RgInteger, PixelType.Short)); + Add(Format.R32G32Uscaled, new FormatInfo(2, false, true, All.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt)); + Add(Format.R32G32Sscaled, new FormatInfo(2, false, true, All.Rg32i, PixelFormat.RgInteger, PixelType.Int)); + Add(Format.R8G8B8Uscaled, new FormatInfo(3, false, true, All.Rgb8ui, PixelFormat.RgbInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8Sscaled, new FormatInfo(3, false, true, All.Rgb8i, PixelFormat.RgbInteger, PixelType.Byte)); + Add(Format.R16G16B16Uscaled, new FormatInfo(3, false, true, All.Rgb16ui, PixelFormat.RgbInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16Sscaled, new FormatInfo(3, false, true, All.Rgb16i, PixelFormat.RgbInteger, PixelType.Short)); + Add(Format.R32G32B32Uscaled, new FormatInfo(3, false, true, All.Rgb32ui, PixelFormat.RgbInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32Sscaled, new FormatInfo(3, false, true, All.Rgb32i, PixelFormat.RgbInteger, PixelType.Int)); + Add(Format.R8G8B8A8Uscaled, new FormatInfo(4, false, true, All.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Sscaled, new FormatInfo(4, false, true, All.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16A16Uscaled, new FormatInfo(4, false, true, All.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Sscaled, new FormatInfo(4, false, true, All.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32A32Uscaled, new FormatInfo(4, false, true, All.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32A32Sscaled, new FormatInfo(4, false, true, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.R10G10B10A2Snorm, new FormatInfo(4, true, false, All.Rgb10A2, PixelFormat.Rgba, (PixelType)All.Int2101010Rev)); + Add(Format.R10G10B10A2Sint, new FormatInfo(4, false, false, All.Rgb10A2, PixelFormat.RgbaInteger, (PixelType)All.Int2101010Rev)); + Add(Format.R10G10B10A2Uscaled, new FormatInfo(4, false, true, All.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R10G10B10A2Sscaled, new FormatInfo(4, false, true, All.Rgb10A2, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R8G8B8X8Unorm, new FormatInfo(4, true, false, All.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8X8Snorm, new FormatInfo(4, true, false, All.Rgb8Snorm, PixelFormat.Rgba, PixelType.Byte)); + Add(Format.R8G8B8X8Uint, new FormatInfo(4, false, false, All.Rgb8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8X8Sint, new FormatInfo(4, false, false, All.Rgb8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16X16Float, new FormatInfo(4, false, false, All.Rgb16f, PixelFormat.Rgba, PixelType.HalfFloat)); + Add(Format.R16G16B16X16Unorm, new FormatInfo(4, true, false, All.Rgb16, PixelFormat.Rgba, PixelType.UnsignedShort)); + Add(Format.R16G16B16X16Snorm, new FormatInfo(4, true, false, All.Rgb16Snorm, PixelFormat.Rgba, PixelType.Short)); + Add(Format.R16G16B16X16Uint, new FormatInfo(4, false, false, All.Rgb16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16X16Sint, new FormatInfo(4, false, false, All.Rgb16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32X32Float, new FormatInfo(4, false, false, All.Rgb32f, PixelFormat.Rgba, PixelType.Float)); + Add(Format.R32G32B32X32Uint, new FormatInfo(4, false, false, All.Rgb32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32X32Sint, new FormatInfo(4, false, false, All.Rgb32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.Astc4x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc4X4Khr)); + Add(Format.Astc5x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X4Khr)); + Add(Format.Astc5x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X5Khr)); + Add(Format.Astc6x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X5Khr)); + Add(Format.Astc6x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X6Khr)); + Add(Format.Astc8x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X5Khr)); + Add(Format.Astc8x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X6Khr)); + Add(Format.Astc8x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X8Khr)); + Add(Format.Astc10x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X5Khr)); + Add(Format.Astc10x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X6Khr)); + Add(Format.Astc10x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X8Khr)); + Add(Format.Astc10x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X10Khr)); + Add(Format.Astc12x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X10Khr)); + Add(Format.Astc12x12Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X12Khr)); + Add(Format.Astc4x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc4X4Khr)); + Add(Format.Astc5x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X4Khr)); + Add(Format.Astc5x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X5Khr)); + Add(Format.Astc6x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X5Khr)); + Add(Format.Astc6x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X6Khr)); + Add(Format.Astc8x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X5Khr)); + Add(Format.Astc8x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X6Khr)); + Add(Format.Astc8x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X8Khr)); + Add(Format.Astc10x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X5Khr)); + Add(Format.Astc10x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X6Khr)); + Add(Format.Astc10x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X8Khr)); + Add(Format.Astc10x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X10Khr)); + Add(Format.Astc12x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X10Khr)); + Add(Format.Astc12x12Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X12Khr)); + Add(Format.B5G6R5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Bgr, PixelType.UnsignedShort565)); + Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Bgra, PixelType.UnsignedShort5551)); + Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort5551)); + Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort1555Reversed)); + Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + } + + private static void Add(Format format, FormatInfo info) + { + _table[(int)format] = info; + } + + public static FormatInfo GetFormatInfo(Format format) + { + return _table[(int)format]; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs new file mode 100644 index 00000000..40913009 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -0,0 +1,116 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Framebuffer : IDisposable + { + public int Handle { get; private set; } + + private FramebufferAttachment _lastDsAttachment; + + public Framebuffer() + { + Handle = GL.GenFramebuffer(); + } + + public void Bind() + { + GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); + } + + public void AttachColor(int index, TextureView color) + { + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0 + index, + color?.Handle ?? 0, + 0); + } + + public void AttachColor(int index, TextureView color, int layer) + { + GL.FramebufferTextureLayer( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0 + index, + color?.Handle ?? 0, + 0, + layer); + } + + public void AttachDepthStencil(TextureView depthStencil) + { + // Detach the last depth/stencil buffer if there is any. + if (_lastDsAttachment != 0) + { + GL.FramebufferTexture(FramebufferTarget.Framebuffer, _lastDsAttachment, 0, 0); + } + + if (depthStencil != null) + { + FramebufferAttachment attachment; + + if (IsPackedDepthStencilFormat(depthStencil.Format)) + { + attachment = FramebufferAttachment.DepthStencilAttachment; + } + else if (IsDepthOnlyFormat(depthStencil.Format)) + { + attachment = FramebufferAttachment.DepthAttachment; + } + else + { + attachment = FramebufferAttachment.StencilAttachment; + } + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + attachment, + depthStencil.Handle, + 0); + + _lastDsAttachment = attachment; + } + else + { + _lastDsAttachment = 0; + } + } + + public void SetDrawBuffers(int colorsCount) + { + DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount]; + + for (int index = 0; index < colorsCount; index++) + { + drawBuffers[index] = DrawBuffersEnum.ColorAttachment0 + index; + } + + GL.DrawBuffers(colorsCount, drawBuffers); + } + + private static bool IsPackedDepthStencilFormat(Format format) + { + return format == Format.D24UnormS8Uint || + format == Format.D32FloatS8Uint; + } + + private static bool IsDepthOnlyFormat(Format format) + { + return format == Format.D16Unorm || + format == Format.D24X8Unorm || + format == Format.D32Float; + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteFramebuffer(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs b/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs new file mode 100644 index 00000000..e9f6b2fb --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs @@ -0,0 +1,718 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Blend; +using Ryujinx.Graphics.GAL.Color; +using Ryujinx.Graphics.GAL.DepthStencil; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.Shader; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class GraphicsPipeline : IGraphicsPipeline + { + private Program _program; + + private VertexArray _vertexArray; + private Framebuffer _framebuffer; + + private IntPtr _indexBaseOffset; + + private DrawElementsType _elementsType; + + private PrimitiveType _primitiveType; + + private int _stencilFrontMask; + private bool _depthMask; + private bool _depthTest; + private bool _hasDepthBuffer; + + private TextureView _unit0Texture; + + private ClipOrigin _clipOrigin; + + private uint[] _componentMasks; + + internal GraphicsPipeline() + { + _clipOrigin = ClipOrigin.LowerLeft; + } + + public void BindBlendState(int index, BlendDescriptor blend) + { + if (!blend.Enable) + { + GL.Disable(IndexedEnableCap.Blend, index); + + return; + } + + GL.BlendEquationSeparate( + index, + blend.ColorOp.Convert(), + blend.AlphaOp.Convert()); + + GL.BlendFuncSeparate( + index, + (BlendingFactorSrc) blend.ColorSrcFactor.Convert(), + (BlendingFactorDest)blend.ColorDstFactor.Convert(), + (BlendingFactorSrc) blend.AlphaSrcFactor.Convert(), + (BlendingFactorDest)blend.AlphaDstFactor.Convert()); + + GL.Enable(IndexedEnableCap.Blend, index); + } + + public void BindIndexBuffer(BufferRange buffer, IndexType type) + { + _elementsType = type.Convert(); + + _indexBaseOffset = (IntPtr)buffer.Offset; + + EnsureVertexArray(); + + _vertexArray.SetIndexBuffer((Buffer)buffer.Buffer); + } + + public void BindProgram(IProgram program) + { + _program = (Program)program; + + _program.Bind(); + } + + public void BindSampler(int index, ShaderStage stage, ISampler sampler) + { + int unit = _program.GetTextureUnit(stage, index); + + if (unit != -1 && sampler != null) + { + ((Sampler)sampler).Bind(unit); + } + } + + public void BindTexture(int index, ShaderStage stage, ITexture texture) + { + int unit = _program.GetTextureUnit(stage, index); + + if (unit != -1 && texture != null) + { + if (unit == 0) + { + _unit0Texture = ((TextureView)texture); + } + else + { + ((TextureView)texture).Bind(unit); + } + } + } + + public void BindStorageBuffers(int index, ShaderStage stage, BufferRange[] buffers) + { + BindBuffers(index, stage, buffers, isStorage: true); + } + + public void BindUniformBuffers(int index, ShaderStage stage, BufferRange[] buffers) + { + BindBuffers(index, stage, buffers, isStorage: false); + } + + private void BindBuffers(int index, ShaderStage stage, BufferRange[] buffers, bool isStorage) + { + for (int bufferIndex = 0; bufferIndex < buffers.Length; bufferIndex++, index++) + { + int bindingPoint = isStorage + ? _program.GetStorageBufferBindingPoint(stage, index) + : _program.GetUniformBufferBindingPoint(stage, index); + + if (bindingPoint == -1) + { + continue; + } + + BufferRange buffer = buffers[bufferIndex]; + + BufferRangeTarget target = isStorage + ? BufferRangeTarget.ShaderStorageBuffer + : BufferRangeTarget.UniformBuffer; + + if (buffer.Buffer == null) + { + GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); + + continue; + } + + int bufferHandle = ((Buffer)buffer.Buffer).Handle; + + IntPtr bufferOffset = (IntPtr)buffer.Offset; + + GL.BindBufferRange( + target, + bindingPoint, + bufferHandle, + bufferOffset, + buffer.Size); + } + } + + public void BindVertexAttribs(VertexAttribDescriptor[] vertexAttribs) + { + EnsureVertexArray(); + + _vertexArray.SetVertexAttributes(vertexAttribs); + } + + public void BindVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + { + EnsureVertexArray(); + + _vertexArray.SetVertexBuffers(vertexBuffers); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + { + GL.ColorMask( + index, + (componentMask & 1) != 0, + (componentMask & 2) != 0, + (componentMask & 4) != 0, + (componentMask & 8) != 0); + + float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorSI color) + { + GL.ColorMask( + index, + (componentMask & 1u) != 0, + (componentMask & 2u) != 0, + (componentMask & 4u) != 0, + (componentMask & 8u) != 0); + + int[] colors = new int[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorUI color) + { + GL.ColorMask( + index, + (componentMask & 1u) != 0, + (componentMask & 2u) != 0, + (componentMask & 4u) != 0, + (componentMask & 8u) != 0); + + uint[] colors = new uint[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetDepthStencil( + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask) + { + bool stencilMaskChanged = + stencilMask != 0 && + stencilMask != _stencilFrontMask; + + bool depthMaskChanged = depthMask && depthMask != _depthMask; + + if (stencilMaskChanged) + { + GL.StencilMaskSeparate(StencilFace.Front, stencilMask); + } + + if (depthMaskChanged) + { + GL.DepthMask(depthMask); + } + + if (depthMask && stencilMask != 0) + { + GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue); + } + else if (depthMask) + { + GL.ClearBuffer(ClearBuffer.Depth, 0, ref depthValue); + } + else if (stencilMask != 0) + { + GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencilValue); + } + + if (stencilMaskChanged) + { + GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask); + } + + if (depthMaskChanged) + { + GL.DepthMask(_depthMask); + } + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + if (!_program.IsLinked) + { + return; + } + + PrepareForDraw(); + + if (firstInstance == 0 && instanceCount == 1) + { + if (_primitiveType == PrimitiveType.Quads) + { + for (int offset = 0; offset < vertexCount; offset += 4) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex + offset, 4); + } + } + else if (_primitiveType == PrimitiveType.QuadStrip) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex, 4); + + for (int offset = 2; offset < vertexCount; offset += 2) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex + offset, 4); + } + } + else + { + GL.DrawArrays(_primitiveType, firstVertex, vertexCount); + } + + // GL.DrawArrays(_primitiveType, firstVertex, vertexCount); + } + else if (firstInstance == 0) + { + GL.DrawArraysInstanced(_primitiveType, firstVertex, vertexCount, instanceCount); + } + else + { + GL.DrawArraysInstancedBaseInstance( + _primitiveType, + firstVertex, + vertexCount, + instanceCount, + firstInstance); + } + } + + public void DrawIndexed( + int indexCount, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance) + { + if (!_program.IsLinked) + { + return; + } + + PrepareForDraw(); + + int firstIndexOffset = firstIndex; + + switch (_elementsType) + { + case DrawElementsType.UnsignedShort: firstIndexOffset *= 2; break; + case DrawElementsType.UnsignedInt: firstIndexOffset *= 4; break; + } + + IntPtr indexBaseOffset = _indexBaseOffset + firstIndexOffset; + + if (firstInstance == 0 && firstVertex == 0 && instanceCount == 1) + { + GL.DrawElements(_primitiveType, indexCount, _elementsType, indexBaseOffset); + } + else if (firstInstance == 0 && instanceCount == 1) + { + GL.DrawElementsBaseVertex( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + firstVertex); + } + else if (firstInstance == 0 && firstVertex == 0) + { + GL.DrawElementsInstanced( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount); + } + else if (firstInstance == 0) + { + GL.DrawElementsInstancedBaseVertex( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstVertex); + } + else if (firstVertex == 0) + { + GL.DrawElementsInstancedBaseInstance( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstInstance); + } + else + { + GL.DrawElementsInstancedBaseVertexBaseInstance( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstVertex, + firstInstance); + } + } + + public void DrawIndirect(BufferRange buffer, ulong offset, int drawCount, int stride) + { + + } + + public void DrawIndexedIndirect(BufferRange buffer, ulong offset, int drawCount, int stride) + { + + } + + public void SetBlendColor(ColorF color) + { + GL.BlendColor(color.Red, color.Green, color.Blue, color.Alpha); + } + + public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + { + if ((enables & PolygonModeMask.Point) != 0) + { + GL.Enable(EnableCap.PolygonOffsetPoint); + } + else + { + GL.Disable(EnableCap.PolygonOffsetPoint); + } + + if ((enables & PolygonModeMask.Line) != 0) + { + GL.Enable(EnableCap.PolygonOffsetLine); + } + else + { + GL.Disable(EnableCap.PolygonOffsetLine); + } + + if ((enables & PolygonModeMask.Fill) != 0) + { + GL.Enable(EnableCap.PolygonOffsetFill); + } + else + { + GL.Disable(EnableCap.PolygonOffsetFill); + } + + if (enables == 0) + { + return; + } + + GL.PolygonOffset(factor, units); + // GL.PolygonOffsetClamp(factor, units, clamp); + } + + public void SetDepthTest(DepthTestDescriptor depthTest) + { + GL.DepthFunc((DepthFunction)depthTest.Func.Convert()); + + _depthMask = depthTest.WriteEnable; + _depthTest = depthTest.TestEnable; + + UpdateDepthTest(); + } + + public void SetFaceCulling(bool enable, Face face) + { + if (!enable) + { + GL.Disable(EnableCap.CullFace); + + return; + } + + GL.CullFace(face.Convert()); + + GL.Enable(EnableCap.CullFace); + } + + public void SetFrontFace(FrontFace frontFace) + { + GL.FrontFace(frontFace.Convert()); + } + + public void SetPrimitiveRestart(bool enable, int index) + { + if (!enable) + { + GL.Disable(EnableCap.PrimitiveRestart); + + return; + } + + GL.PrimitiveRestartIndex(index); + + GL.Enable(EnableCap.PrimitiveRestart); + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + _primitiveType = topology.Convert(); + } + + public void SetRenderTargetColorMasks(uint[] componentMasks) + { + _componentMasks = (uint[])componentMasks.Clone(); + + for (int index = 0; index < componentMasks.Length; index++) + { + RestoreComponentMask(index); + } + } + + public void SetRenderTargets(ITexture color3D, ITexture depthStencil) + { + EnsureFramebuffer(); + + TextureView color = (TextureView)color3D; + + for (int index = 0; index < color.DepthOrLayers; index++) + { + _framebuffer.AttachColor(index, color, index); + } + + TextureView depthStencilView = (TextureView)depthStencil; + + _framebuffer.AttachDepthStencil(depthStencilView); + + _framebuffer.SetDrawBuffers(color.DepthOrLayers); + + _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; + + UpdateDepthTest(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + EnsureFramebuffer(); + + for (int index = 0; index < colors.Length; index++) + { + TextureView color = (TextureView)colors[index]; + + _framebuffer.AttachColor(index, color); + } + + TextureView depthStencilView = (TextureView)depthStencil; + + _framebuffer.AttachDepthStencil(depthStencilView); + + _framebuffer.SetDrawBuffers(colors.Length); + + _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; + + UpdateDepthTest(); + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + if (!stencilTest.TestEnable) + { + GL.Disable(EnableCap.StencilTest); + + return; + } + + GL.StencilOpSeparate( + StencilFace.Front, + stencilTest.FrontSFail.Convert(), + stencilTest.FrontDpFail.Convert(), + stencilTest.FrontDpPass.Convert()); + + GL.StencilFuncSeparate( + StencilFace.Front, + (StencilFunction)stencilTest.FrontFunc.Convert(), + stencilTest.FrontFuncRef, + stencilTest.FrontFuncMask); + + GL.StencilMaskSeparate(StencilFace.Front, stencilTest.FrontMask); + + GL.StencilOpSeparate( + StencilFace.Back, + stencilTest.BackSFail.Convert(), + stencilTest.BackDpFail.Convert(), + stencilTest.BackDpPass.Convert()); + + GL.StencilFuncSeparate( + StencilFace.Back, + (StencilFunction)stencilTest.BackFunc.Convert(), + stencilTest.BackFuncRef, + stencilTest.BackFuncMask); + + GL.StencilMaskSeparate(StencilFace.Back, stencilTest.BackMask); + + GL.Enable(EnableCap.StencilTest); + + _stencilFrontMask = stencilTest.FrontMask; + } + + public void SetViewports(int first, Viewport[] viewports) + { + bool flipY = false; + + float[] viewportArray = new float[viewports.Length * 4]; + + double[] depthRangeArray = new double[viewports.Length * 2]; + + for (int index = 0; index < viewports.Length; index++) + { + int viewportElemIndex = index * 4; + + Viewport viewport = viewports[index]; + + viewportArray[viewportElemIndex + 0] = viewport.Region.X; + viewportArray[viewportElemIndex + 1] = viewport.Region.Y; + + // OpenGL does not support per-viewport flipping, so + // instead we decide that based on the viewport 0 value. + // It will apply to all viewports. + if (index == 0) + { + flipY = viewport.Region.Height < 0; + } + + if (viewport.SwizzleY == ViewportSwizzle.NegativeY) + { + flipY = !flipY; + } + + viewportArray[viewportElemIndex + 2] = MathF.Abs(viewport.Region.Width); + viewportArray[viewportElemIndex + 3] = MathF.Abs(viewport.Region.Height); + + depthRangeArray[index * 2 + 0] = viewport.DepthNear; + depthRangeArray[index * 2 + 1] = viewport.DepthFar; + } + + GL.ViewportArray(first, viewports.Length, viewportArray); + + GL.DepthRangeArray(first, viewports.Length, depthRangeArray); + + SetOrigin(flipY ? ClipOrigin.UpperLeft : ClipOrigin.LowerLeft); + } + + private void SetOrigin(ClipOrigin origin) + { + if (_clipOrigin != origin) + { + _clipOrigin = origin; + + GL.ClipControl(origin, ClipDepthMode.NegativeOneToOne); + } + } + + private void EnsureVertexArray() + { + if (_vertexArray == null) + { + _vertexArray = new VertexArray(); + + _vertexArray.Bind(); + } + } + + private void EnsureFramebuffer() + { + if (_framebuffer == null) + { + _framebuffer = new Framebuffer(); + + _framebuffer.Bind(); + + GL.Enable(EnableCap.FramebufferSrgb); + } + } + + private void UpdateDepthTest() + { + // Enabling depth operations is only valid when we have + // a depth buffer, otherwise it's not allowed. + if (_hasDepthBuffer) + { + if (_depthTest) + { + GL.Enable(EnableCap.DepthTest); + } + else + { + GL.Disable(EnableCap.DepthTest); + } + + GL.DepthMask(_depthMask); + } + else + { + GL.Disable(EnableCap.DepthTest); + + GL.DepthMask(false); + } + } + + private void PrepareForDraw() + { + _vertexArray.Validate(); + + if (_unit0Texture != null) + { + _unit0Texture.Bind(0); + } + } + + private void RestoreComponentMask(int index) + { + GL.ColorMask( + index, + (_componentMasks[index] & 1u) != 0, + (_componentMasks[index] & 2u) != 0, + (_componentMasks[index] & 4u) != 0, + (_componentMasks[index] & 8u) != 0); + } + + public void RebindProgram() + { + _program?.Bind(); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs new file mode 100644 index 00000000..f958946e --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -0,0 +1,27 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class HwCapabilities + { + private static Lazy<bool> _astcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + + public static bool SupportsAstcCompression => _astcCompression.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; + } + } + + return false; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs new file mode 100644 index 00000000..1f95b449 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -0,0 +1,175 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class Program : IProgram + { + private const int StageShift = 5; + private const int SbStageShift = 4; + + public int Handle { get; private set; } + + public bool IsLinked { get; private set; } + + private int[] _ubBindingPoints; + private int[] _sbBindingPoints; + private int[] _textureUnits; + + public Program(IShader[] shaders) + { + _ubBindingPoints = new int[32 * 6]; + _sbBindingPoints = new int[16 * 6]; + _textureUnits = new int[32 * 6]; + + for (int index = 0; index < _ubBindingPoints.Length; index++) + { + _ubBindingPoints[index] = -1; + } + + for (int index = 0; index < _sbBindingPoints.Length; index++) + { + _sbBindingPoints[index] = -1; + } + + for (int index = 0; index < _textureUnits.Length; index++) + { + _textureUnits[index] = -1; + } + + Handle = GL.CreateProgram(); + + for (int index = 0; index < shaders.Length; index++) + { + int shaderHandle = ((Shader)shaders[index]).Handle; + + GL.AttachShader(Handle, shaderHandle); + } + + GL.LinkProgram(Handle); + + CheckProgramLink(); + + Bind(); + + int extraBlockindex = GL.GetUniformBlockIndex(Handle, "Extra"); + + if (extraBlockindex >= 0) + { + GL.UniformBlockBinding(Handle, extraBlockindex, 0); + } + + int ubBindingPoint = 1; + int sbBindingPoint = 0; + int textureUnit = 0; + + for (int index = 0; index < shaders.Length; index++) + { + Shader shader = (Shader)shaders[index]; + + foreach (BufferDescriptor descriptor in shader.Info.CBuffers) + { + int location = GL.GetUniformBlockIndex(Handle, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.UniformBlockBinding(Handle, location, ubBindingPoint); + + int bpIndex = (int)shader.Stage << StageShift | descriptor.Slot; + + _ubBindingPoints[bpIndex] = ubBindingPoint; + + ubBindingPoint++; + } + + foreach (BufferDescriptor descriptor in shader.Info.SBuffers) + { + int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint); + + int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot; + + _sbBindingPoints[bpIndex] = sbBindingPoint; + + sbBindingPoint++; + } + + int samplerIndex = 0; + + foreach (TextureDescriptor descriptor in shader.Info.Textures) + { + int location = GL.GetUniformLocation(Handle, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.Uniform1(location, textureUnit); + + int uIndex = (int)shader.Stage << StageShift | samplerIndex++; + + _textureUnits[uIndex] = textureUnit; + + textureUnit++; + } + } + } + + public void Bind() + { + GL.UseProgram(Handle); + } + + public int GetUniformBufferBindingPoint(ShaderStage stage, int index) + { + return _ubBindingPoints[(int)stage << StageShift | index]; + } + + public int GetStorageBufferBindingPoint(ShaderStage stage, int index) + { + return _sbBindingPoints[(int)stage << SbStageShift | index]; + } + + public int GetTextureUnit(ShaderStage stage, int index) + { + return _textureUnits[(int)stage << StageShift | index]; + } + + private void CheckProgramLink() + { + int status = 0; + + GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out status); + + if (status == 0) + { + // throw new System.Exception(GL.GetProgramInfoLog(Handle)); + } + else + { + IsLinked = true; + } + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteProgram(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs new file mode 100644 index 00000000..56ba7624 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -0,0 +1,84 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Sampler; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.OpenGL +{ + public class Renderer : IRenderer + { + public IComputePipeline ComputePipeline { get; } + public IGraphicsPipeline GraphicsPipeline { get; } + + private Counters _counters; + + private Window _window; + + public IWindow Window => _window; + + internal TextureCopy TextureCopy { get; } + + public Renderer() + { + ComputePipeline = new ComputePipeline(this); + GraphicsPipeline = new GraphicsPipeline(); + + _counters = new Counters(); + + _window = new Window(); + + TextureCopy = new TextureCopy(); + } + + public IShader CompileShader(ShaderProgram shader) + { + return new Shader(shader); + } + + public IBuffer CreateBuffer(int size) + { + return new Buffer(size); + } + + public IProgram CreateProgram(IShader[] shaders) + { + return new Program(shaders); + } + + public ISampler CreateSampler(SamplerCreateInfo info) + { + return new Sampler(info); + } + + public ITexture CreateTexture(TextureCreateInfo info) + { + return new TextureStorage(this, info).CreateDefaultView(); + } + + public void FlushPipelines() + { + GL.Finish(); + } + + public Capabilities GetCapabilities() + { + return new Capabilities(HwCapabilities.SupportsAstcCompression); + } + + public ulong GetCounter(CounterType type) + { + return _counters.GetCounter(type); + } + + public void InitializeCounters() + { + _counters.Initialize(); + } + + public void ResetCounter(CounterType type) + { + _counters.ResetCounter(type); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj new file mode 100644 index 00000000..484e8682 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <TargetFramework>netcoreapp3.0</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="OpenTK.NetStandard" Version="1.0.4" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" /> + <ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" /> + </ItemGroup> + +</Project> diff --git a/Ryujinx.Graphics.OpenGL/Sampler.cs b/Ryujinx.Graphics.OpenGL/Sampler.cs new file mode 100644 index 00000000..9b9f09a1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Sampler.cs @@ -0,0 +1,60 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Sampler; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class Sampler : ISampler + { + public int Handle { get; private set; } + + public Sampler(SamplerCreateInfo info) + { + Handle = GL.GenSampler(); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinFilter, (int)info.MinFilter.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMagFilter, (int)info.MagFilter.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapS, (int)info.AddressU.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapT, (int)info.AddressV.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapR, (int)info.AddressP.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareMode, (int)info.CompareMode.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareFunc, (int)info.CompareOp.Convert()); + + unsafe + { + float* borderColor = stackalloc float[4] + { + info.BorderColor.Red, + info.BorderColor.Green, + info.BorderColor.Blue, + info.BorderColor.Alpha + }; + + GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); + } + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureLodBias, info.MipLodBias); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxAnisotropyExt, info.MaxAnisotropy); + } + + public void Bind(int unit) + { + GL.BindSampler(unit, Handle); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteSampler(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Shader.cs b/Ryujinx.Graphics.OpenGL/Shader.cs new file mode 100644 index 00000000..f25845cf --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Shader.cs @@ -0,0 +1,49 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.OpenGL +{ + class Shader : IShader + { + public int Handle { get; private set; } + + private ShaderProgram _program; + + public ShaderProgramInfo Info => _program.Info; + + public ShaderStage Stage => _program.Stage; + + public Shader(ShaderProgram program) + { + _program = program; + + ShaderType type = ShaderType.VertexShader; + + switch (program.Stage) + { + case ShaderStage.Compute: type = ShaderType.ComputeShader; break; + case ShaderStage.Vertex: type = ShaderType.VertexShader; break; + case ShaderStage.TessellationControl: type = ShaderType.TessControlShader; break; + case ShaderStage.TessellationEvaluation: type = ShaderType.TessEvaluationShader; break; + case ShaderStage.Geometry: type = ShaderType.GeometryShader; break; + case ShaderStage.Fragment: type = ShaderType.FragmentShader; break; + } + + Handle = GL.CreateShader(type); + + GL.ShaderSource(Handle, program.Code); + GL.CompileShader(Handle); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteShader(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/TextureCopy.cs new file mode 100644 index 00000000..75ef07c1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureCopy.cs @@ -0,0 +1,128 @@ +using Ryujinx.Graphics.GAL; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureCopy + { + private int _srcFramebuffer; + private int _dstFramebuffer; + + public void Copy( + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter) + { + GL.Disable(EnableCap.FramebufferSrgb); + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); + + Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); + + ClearBufferMask mask = GetMask(src.Format); + + BlitFramebufferFilter filter = linearFilter + ? BlitFramebufferFilter.Linear + : BlitFramebufferFilter.Nearest; + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + GL.BlitFramebuffer( + srcRegion.X1, + srcRegion.Y1, + srcRegion.X2, + srcRegion.Y2, + dstRegion.X1, + dstRegion.Y1, + dstRegion.X2, + dstRegion.Y2, + mask, + filter); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + GL.Enable(EnableCap.FramebufferSrgb); + } + + private static void Detach(FramebufferTarget target, Format format) + { + Attach(target, format, 0); + } + + private static void Attach(FramebufferTarget target, Format format, int handle) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); + } + else if (IsDepthOnly(format)) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); + } + else if (format == Format.S8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); + } + else + { + GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); + } + } + + private static ClearBufferMask GetMask(Format format) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; + } + else if (IsDepthOnly(format)) + { + return ClearBufferMask.DepthBufferBit; + } + else if (format == Format.S8Uint) + { + return ClearBufferMask.StencilBufferBit; + } + else + { + return ClearBufferMask.ColorBufferBit; + } + } + + private static bool IsDepthOnly(Format format) + { + return format == Format.D16Unorm || + format == Format.D24X8Unorm || + format == Format.D32Float; + } + + private int GetSrcFramebufferLazy() + { + if (_srcFramebuffer == 0) + { + _srcFramebuffer = GL.GenFramebuffer(); + } + + return _srcFramebuffer; + } + + private int GetDstFramebufferLazy() + { + if (_dstFramebuffer == 0) + { + _dstFramebuffer = GL.GenFramebuffer(); + } + + return _dstFramebuffer; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs new file mode 100644 index 00000000..dae492d9 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs @@ -0,0 +1,85 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL.Texture; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class TextureCopyUnscaled + { + public static void Copy(TextureView src, TextureView dst, int dstLayer, int dstLevel) + { + int srcWidth = src.Width; + int srcHeight = src.Height; + int srcDepth = src.DepthOrLayers; + int srcLevels = src.Levels; + + srcWidth = Math.Max(1, srcWidth >> dstLevel); + srcHeight = Math.Max(1, srcHeight >> dstLevel); + + if (src.Target == Target.Texture3D) + { + srcDepth = Math.Max(1, srcDepth >> dstLevel); + } + + int dstWidth = dst.Width; + int dstHeight = dst.Height; + int dstDepth = dst.DepthOrLayers; + int dstLevels = dst.Levels; + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!src.IsCompressed && dst.IsCompressed) + { + dstWidth = BitUtils.DivRoundUp(dstWidth, dst.BlockWidth); + dstHeight = BitUtils.DivRoundUp(dstHeight, dst.BlockHeight); + } + else if (src.IsCompressed && !dst.IsCompressed) + { + dstWidth *= dst.BlockWidth; + dstHeight *= dst.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= src.Levels || dstLevel + level >= dst.Levels) + { + break; + } + + GL.CopyImageSubData( + src.Handle, + src.Target.ConvertToImageTarget(), + level, + 0, + 0, + 0, + dst.Handle, + dst.Target.ConvertToImageTarget(), + dstLevel + level, + 0, + 0, + dstLayer, + width, + height, + depth); + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (src.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/TextureStorage.cs new file mode 100644 index 00000000..d74b0a8e --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureStorage.cs @@ -0,0 +1,179 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.OpenGL.Formats; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureStorage + { + public int Handle { get; private set; } + + private Renderer _renderer; + + private TextureCreateInfo _info; + + public Target Target => _info.Target; + + private int _viewsCount; + + public TextureStorage(Renderer renderer, TextureCreateInfo info) + { + _renderer = renderer; + _info = info; + + Handle = GL.GenTexture(); + + CreateImmutableStorage(); + } + + private void CreateImmutableStorage() + { + TextureTarget target = _info.Target.Convert(); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + SizedInternalFormat internalFormat; + + if (format.IsCompressed) + { + internalFormat = (SizedInternalFormat)format.PixelFormat; + } + else + { + internalFormat = (SizedInternalFormat)format.PixelInternalFormat; + } + + switch (_info.Target) + { + case Target.Texture1D: + GL.TexStorage1D( + TextureTarget1d.Texture1D, + _info.Levels, + internalFormat, + _info.Width); + break; + + case Target.Texture1DArray: + GL.TexStorage2D( + TextureTarget2d.Texture1DArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.Texture2D: + GL.TexStorage2D( + TextureTarget2d.Texture2D, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.Texture2DArray: + GL.TexStorage3D( + TextureTarget3d.Texture2DArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + + case Target.Texture2DMultisample: + GL.TexStorage2DMultisample( + TextureTargetMultisample2d.Texture2DMultisample, + _info.Samples, + internalFormat, + _info.Width, + _info.Height, + true); + break; + + case Target.Texture2DMultisampleArray: + GL.TexStorage3DMultisample( + TextureTargetMultisample3d.Texture2DMultisampleArray, + _info.Samples, + internalFormat, + _info.Width, + _info.Height, + _info.Depth, + true); + break; + + case Target.Texture3D: + GL.TexStorage3D( + TextureTarget3d.Texture3D, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + + case Target.Cubemap: + GL.TexStorage2D( + TextureTarget2d.TextureCubeMap, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.CubemapArray: + GL.TexStorage3D( + (TextureTarget3d)All.TextureCubeMapArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + } + } + + public ITexture CreateDefaultView() + { + int layers = _info.GetLayers(); + + return CreateView(_info, 0, 0); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + IncrementViewsCount(); + + return new TextureView(_renderer, this, info, firstLayer, firstLevel); + } + + private void IncrementViewsCount() + { + _viewsCount++; + } + + public void DecrementViewsCount() + { + // If we don't have any views, then the storage is now useless, delete it. + if (--_viewsCount == 0) + { + Dispose(); + } + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/TextureView.cs new file mode 100644 index 00000000..8a2b50dc --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureView.cs @@ -0,0 +1,425 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.OpenGL.Formats; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureView : ITexture + { + public int Handle { get; private set; } + + private Renderer _renderer; + + private TextureStorage _parent; + + private TextureView _emulatedViewParent; + + private TextureCreateInfo _info; + + private int _firstLayer; + private int _firstLevel; + + private bool _acquired; + private bool _pendingDelete; + + public int Width => _info.Width; + public int Height => _info.Height; + public int DepthOrLayers => _info.GetDepthOrLayers(); + public int Levels => _info.Levels; + + public Target Target => _info.Target; + public Format Format => _info.Format; + + public int BlockWidth => _info.BlockWidth; + public int BlockHeight => _info.BlockHeight; + + public bool IsCompressed => _info.IsCompressed; + + public TextureView( + Renderer renderer, + TextureStorage parent, + TextureCreateInfo info, + int firstLayer, + int firstLevel) + { + _renderer = renderer; + _parent = parent; + _info = info; + + _firstLayer = firstLayer; + _firstLevel = firstLevel; + + Handle = GL.GenTexture(); + + CreateView(); + } + + private void CreateView() + { + TextureTarget target = Target.Convert(); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + PixelInternalFormat pixelInternalFormat; + + if (format.IsCompressed) + { + pixelInternalFormat = (PixelInternalFormat)format.PixelFormat; + } + else + { + pixelInternalFormat = format.PixelInternalFormat; + } + + GL.TextureView( + Handle, + target, + _parent.Handle, + pixelInternalFormat, + _firstLevel, + _info.Levels, + _firstLayer, + _info.GetLayers()); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + int[] swizzleRgba = new int[] + { + (int)_info.SwizzleR.Convert(), + (int)_info.SwizzleG.Convert(), + (int)_info.SwizzleB.Convert(), + (int)_info.SwizzleA.Convert() + }; + + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); + + int maxLevel = _info.Levels - 1; + + if (maxLevel < 0) + { + maxLevel = 0; + } + + GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel); + + // GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)_info.DepthStencilMode.Convert()); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + if (_info.IsCompressed == info.IsCompressed) + { + firstLayer += _firstLayer; + firstLevel += _firstLevel; + + return _parent.CreateView(info, firstLayer, firstLevel); + } + else + { + // TODO: Improve + TextureView emulatedView = (TextureView)_renderer.CreateTexture(info); + + emulatedView._emulatedViewParent = this; + + emulatedView._firstLayer = firstLayer; + emulatedView._firstLevel = firstLevel; + + return emulatedView; + } + } + + public int GetStorageDebugId() + { + return _parent.GetHashCode(); + } + + public void CopyTo(ITexture destination) + { + TextureView destinationView = (TextureView)destination; + + TextureCopyUnscaled.Copy(this, destinationView, 0, 0); + + int width = Math.Min(Width, destinationView.Width); + int height = Math.Min(Height, destinationView.Height); + + int depth = Math.Min(_info.GetDepthOrLayers(), destinationView._info.GetDepthOrLayers()); + + int levels = Math.Min(_info.Levels, destinationView._info.Levels); + + if (destinationView._emulatedViewParent != null) + { + TextureCopyUnscaled.Copy( + this, + destinationView._emulatedViewParent, + destinationView._firstLayer, + destinationView._firstLevel); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); + } + + public byte[] GetData(int face) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + int depth = _info.GetDepthOrLayers(); + + if (target == TextureTarget.TextureCubeMap) + { + target = TextureTarget.TextureCubeMapPositiveX + face; + } + + if (format.IsCompressed) + { + byte[] data = new byte[_info.Width * _info.Height * depth * 4]; + + GL.GetTexImage(target, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data); + + return data; + } + else + { + byte[] data = new byte[_info.GetMipSize(0)]; + + GL.GetTexImage(target, 0, format.PixelFormat, format.PixelType, data); + + return data; + } + } + + public void SetData(Span<byte> data) + { + unsafe + { + fixed (byte* ptr = data) + { + SetData((IntPtr)ptr, data.Length); + } + } + } + + private void SetData(IntPtr data, int size) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + int width = _info.Width; + int height = _info.Height; + int depth = _info.Depth; + + int offset = 0; + + for (int level = 0; level < _info.Levels; level++) + { + int mipSize = _info.GetMipSize(level); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)size) + { + return; + } + + switch (_info.Target) + { + case Target.Texture1D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture1DArray: + case Target.Texture2D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture2DArray: + case Target.Texture3D: + case Target.CubemapArray: + if (format.IsCompressed) + { + GL.CompressedTexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Cubemap: + int faceOffset = 0; + + for (int face = 0; face < 6; face++, faceOffset += mipSize / 6) + { + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize / 6, + data + faceOffset); + } + else + { + GL.TexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data + faceOffset); + } + } + break; + } + + data += mipSize; + offset += mipSize; + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + + public void Bind(int unit) + { + Bind(Target.Convert(), unit); + } + + private void Bind(TextureTarget target, int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + + GL.BindTexture(target, Handle); + } + + public void Acquire() + { + _acquired = true; + } + + public void Release() + { + _acquired = false; + + if (_pendingDelete) + { + _pendingDelete = false; + + Dispose(); + } + } + + public void Dispose() + { + if (_acquired) + { + _pendingDelete = true; + + return; + } + + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + _parent.DecrementViewsCount(); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/Ryujinx.Graphics.OpenGL/VertexArray.cs new file mode 100644 index 00000000..cf87c953 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -0,0 +1,135 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.OpenGL.Formats; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class VertexArray : IDisposable + { + public int Handle { get; } + + private bool _needsAttribsUpdate; + + private VertexBufferDescriptor[] _vertexBuffers; + private VertexAttribDescriptor[] _vertexAttribs; + + public VertexArray() + { + Handle = GL.GenVertexArray(); + } + + public void Bind() + { + GL.BindVertexArray(Handle); + } + + public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + { + int bindingIndex = 0; + + foreach (VertexBufferDescriptor vb in vertexBuffers) + { + if (vb.Buffer.Buffer != null) + { + int bufferHandle = ((Buffer)vb.Buffer.Buffer).Handle; + + GL.BindVertexBuffer(bindingIndex, bufferHandle, (IntPtr)vb.Buffer.Offset, vb.Stride); + + GL.VertexBindingDivisor(bindingIndex, vb.Divisor); + } + else + { + GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0); + } + + bindingIndex++; + } + + _vertexBuffers = vertexBuffers; + + _needsAttribsUpdate = true; + } + + public void SetVertexAttributes(VertexAttribDescriptor[] vertexAttribs) + { + int attribIndex = 0; + + foreach (VertexAttribDescriptor attrib in vertexAttribs) + { + FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format); + + GL.EnableVertexAttribArray(attribIndex); + + int offset = attrib.Offset; + int size = fmtInfo.Components; + + bool isFloat = fmtInfo.PixelType == PixelType.Float || + fmtInfo.PixelType == PixelType.HalfFloat; + + if (isFloat || fmtInfo.Normalized || fmtInfo.Scaled) + { + VertexAttribType type = (VertexAttribType)fmtInfo.PixelType; + + GL.VertexAttribFormat(attribIndex, size, type, fmtInfo.Normalized, offset); + } + else + { + VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType; + + GL.VertexAttribIFormat(attribIndex, size, type, offset); + } + + GL.VertexAttribBinding(attribIndex, attrib.BufferIndex); + + attribIndex++; + } + + for (; attribIndex < 16; attribIndex++) + { + GL.DisableVertexAttribArray(attribIndex); + } + + _vertexAttribs = vertexAttribs; + } + + public void SetIndexBuffer(Buffer indexBuffer) + { + GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer?.Handle ?? 0); + } + + public void Validate() + { + for (int attribIndex = 0; attribIndex < _vertexAttribs.Length; attribIndex++) + { + VertexAttribDescriptor attrib = _vertexAttribs[attribIndex]; + + if ((uint)attrib.BufferIndex >= _vertexBuffers.Length) + { + GL.DisableVertexAttribArray(attribIndex); + + continue; + } + + if (_vertexBuffers[attrib.BufferIndex].Buffer.Buffer == null) + { + GL.DisableVertexAttribArray(attribIndex); + + continue; + } + + if (_needsAttribsUpdate) + { + GL.EnableVertexAttribArray(attribIndex); + } + } + + _needsAttribsUpdate = false; + } + + public void Dispose() + { + GL.DeleteVertexArray(Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/VertexBuffer.cs b/Ryujinx.Graphics.OpenGL/VertexBuffer.cs new file mode 100644 index 00000000..19a58053 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/VertexBuffer.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.OpenGL +{ + struct VertexBuffer + { + public BufferRange Range { get; } + + public int Divisor { get; } + public int Stride { get; } + + public VertexBuffer(BufferRange range, int divisor, int stride) + { + Range = range; + Divisor = divisor; + Stride = stride; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs new file mode 100644 index 00000000..a05eb1c0 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -0,0 +1,248 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.OpenGL +{ + class Window : IWindow + { + private const int NativeWidth = 1280; + private const int NativeHeight = 720; + + private int _width = 1280; + private int _height = 720; + + private int _blitFramebufferHandle; + private int _copyFramebufferHandle; + + private int _screenTextureHandle; + + private TextureReleaseCallback _release; + + private struct PresentationTexture + { + public TextureView Texture { get; } + + public ImageCrop Crop { get; } + + public object Context { get; } + + public PresentationTexture(TextureView texture, ImageCrop crop, object context) + { + Texture = texture; + Crop = crop; + Context = context; + } + } + + private Queue<PresentationTexture> _textures; + + public Window() + { + _textures = new Queue<PresentationTexture>(); + } + + public void Present() + { + GL.Disable(EnableCap.FramebufferSrgb); + + CopyTextureFromQueue(); + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetCopyFramebufferHandleLazy()); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.BlitFramebuffer( + 0, + 0, + 1280, + 720, + 0, + 0, + 1280, + 720, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + GL.Enable(EnableCap.FramebufferSrgb); + } + + private void CopyTextureFromQueue() + { + if (!_textures.TryDequeue(out PresentationTexture presentationTexture)) + { + return; + } + + TextureView texture = presentationTexture.Texture; + ImageCrop crop = presentationTexture.Crop; + object context = presentationTexture.Context; + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetCopyFramebufferHandleLazy()); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetBlitFramebufferHandleLazy()); + + GL.FramebufferTexture( + FramebufferTarget.ReadFramebuffer, + FramebufferAttachment.ColorAttachment0, + texture.Handle, + 0); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + int srcX0, srcX1, srcY0, srcY1; + + if (crop.Left == 0 && crop.Right == 0) + { + srcX0 = 0; + srcX1 = texture.Width; + } + else + { + srcX0 = crop.Left; + srcX1 = crop.Right; + } + + if (crop.Top == 0 && crop.Bottom == 0) + { + srcY0 = 0; + srcY1 = texture.Height; + } + else + { + srcY0 = crop.Top; + srcY1 = crop.Bottom; + } + + float ratioX = MathF.Min(1f, (_height * (float)NativeWidth) / ((float)NativeHeight * _width)); + float ratioY = MathF.Min(1f, (_width * (float)NativeHeight) / ((float)NativeWidth * _height)); + + int dstWidth = (int)(_width * ratioX); + int dstHeight = (int)(_height * ratioY); + + int dstPaddingX = (_width - dstWidth) / 2; + int dstPaddingY = (_height - dstHeight) / 2; + + int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; + int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; + + int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; + int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; + + GL.BlitFramebuffer( + srcX0, + srcY0, + srcX1, + srcY1, + dstX0, + dstY0, + dstX1, + dstY1, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + texture.Release(); + + Release(context); + } + + public void QueueTexture(ITexture texture, ImageCrop crop, object context) + { + if (texture == null) + { + Release(context); + + return; + } + + TextureView textureView = (TextureView)texture; + + textureView.Acquire(); + + _textures.Enqueue(new PresentationTexture(textureView, crop, context)); + } + + public void RegisterTextureReleaseCallback(TextureReleaseCallback callback) + { + _release = callback; + } + + private void Release(object context) + { + if (_release != null) + { + _release(context); + } + } + + private int GetBlitFramebufferHandleLazy() + { + int handle = _blitFramebufferHandle; + + if (handle == 0) + { + handle = GL.GenFramebuffer(); + + _blitFramebufferHandle = handle; + } + + return handle; + } + + private int GetCopyFramebufferHandleLazy() + { + int handle = _copyFramebufferHandle; + + if (handle == 0) + { + int textureHandle = GL.GenTexture(); + + GL.BindTexture(TextureTarget.Texture2D, textureHandle); + + GL.TexImage2D( + TextureTarget.Texture2D, + 0, + PixelInternalFormat.Rgba8, + 1280, + 720, + 0, + PixelFormat.Rgba, + PixelType.UnsignedByte, + IntPtr.Zero); + + handle = GL.GenFramebuffer(); + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle); + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0, + textureHandle, + 0); + + _screenTextureHandle = textureHandle; + + _copyFramebufferHandle = handle; + } + + return handle; + } + } +} |
