Initial work

This commit is contained in:
gdk 2019-10-13 03:02:07 -03:00 committed by Thog
parent f617fb542a
commit 1876b346fe
518 changed files with 15170 additions and 12486 deletions

View file

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

View file

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

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

@ -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}\".");
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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