Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
74
Ryujinx.Graphics.OpenGL/Buffer.cs
Normal file
74
Ryujinx.Graphics.OpenGL/Buffer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
95
Ryujinx.Graphics.OpenGL/ComputePipeline.cs
Normal file
95
Ryujinx.Graphics.OpenGL/ComputePipeline.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs
Normal file
26
Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
39
Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs
Normal file
39
Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs
Normal file
25
Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs
Normal file
20
Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
28
Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs
Normal file
28
Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}\".");
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs
Normal file
23
Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs
Normal file
22
Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs
Normal file
21
Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs
Normal file
20
Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs
Normal file
24
Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}\".");
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs
Normal file
27
Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}\".");
|
||||
}
|
||||
}
|
||||
}
|
33
Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs
Normal file
33
Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs
Normal 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}\".");
|
||||
}
|
||||
}
|
||||
}
|
77
Ryujinx.Graphics.OpenGL/Counters.cs
Normal file
77
Ryujinx.Graphics.OpenGL/Counters.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
43
Ryujinx.Graphics.OpenGL/Debugger.cs
Normal file
43
Ryujinx.Graphics.OpenGL/Debugger.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
45
Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs
Normal file
45
Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
183
Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs
Normal file
183
Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
116
Ryujinx.Graphics.OpenGL/Framebuffer.cs
Normal file
116
Ryujinx.Graphics.OpenGL/Framebuffer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
718
Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs
Normal file
718
Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
27
Ryujinx.Graphics.OpenGL/HwCapabilities.cs
Normal file
27
Ryujinx.Graphics.OpenGL/HwCapabilities.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
175
Ryujinx.Graphics.OpenGL/Program.cs
Normal file
175
Ryujinx.Graphics.OpenGL/Program.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
Ryujinx.Graphics.OpenGL/Renderer.cs
Normal file
84
Ryujinx.Graphics.OpenGL/Renderer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
Normal file
17
Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj
Normal 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>
|
60
Ryujinx.Graphics.OpenGL/Sampler.cs
Normal file
60
Ryujinx.Graphics.OpenGL/Sampler.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
Ryujinx.Graphics.OpenGL/Shader.cs
Normal file
49
Ryujinx.Graphics.OpenGL/Shader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
128
Ryujinx.Graphics.OpenGL/TextureCopy.cs
Normal file
128
Ryujinx.Graphics.OpenGL/TextureCopy.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
85
Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs
Normal file
85
Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
179
Ryujinx.Graphics.OpenGL/TextureStorage.cs
Normal file
179
Ryujinx.Graphics.OpenGL/TextureStorage.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
425
Ryujinx.Graphics.OpenGL/TextureView.cs
Normal file
425
Ryujinx.Graphics.OpenGL/TextureView.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
Ryujinx.Graphics.OpenGL/VertexArray.cs
Normal file
135
Ryujinx.Graphics.OpenGL/VertexArray.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Graphics.OpenGL/VertexBuffer.cs
Normal file
19
Ryujinx.Graphics.OpenGL/VertexBuffer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
248
Ryujinx.Graphics.OpenGL/Window.cs
Normal file
248
Ryujinx.Graphics.OpenGL/Window.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue