2018-02-23 22:48:27 +01:00
|
|
|
using OpenTK;
|
|
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
using System;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
|
|
|
{
|
|
|
|
unsafe class FrameBuffer
|
|
|
|
{
|
|
|
|
public int WindowWidth { get; set; }
|
|
|
|
public int WindowHeight { get; set; }
|
|
|
|
|
|
|
|
private int VtxShaderHandle;
|
|
|
|
private int FragShaderHandle;
|
|
|
|
private int PrgShaderHandle;
|
|
|
|
|
|
|
|
private int TexHandle;
|
|
|
|
private int TexWidth;
|
|
|
|
private int TexHeight;
|
|
|
|
|
|
|
|
private int VaoHandle;
|
|
|
|
private int VboHandle;
|
|
|
|
|
|
|
|
private int[] Pixels;
|
|
|
|
|
|
|
|
private byte* FbPtr;
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
private object FbPtrLock;
|
|
|
|
|
2018-02-23 22:48:27 +01:00
|
|
|
public FrameBuffer(int Width, int Height)
|
|
|
|
{
|
|
|
|
if (Width < 0)
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Width));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Height < 0)
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Height));
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
FbPtrLock = new object();
|
|
|
|
|
2018-02-23 22:48:27 +01:00
|
|
|
TexWidth = Width;
|
|
|
|
TexHeight = Height;
|
|
|
|
|
|
|
|
WindowWidth = Width;
|
|
|
|
WindowHeight = Height;
|
|
|
|
|
|
|
|
SetupShaders();
|
|
|
|
SetupTexture();
|
|
|
|
SetupVertex();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupShaders()
|
|
|
|
{
|
|
|
|
VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader);
|
|
|
|
FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
|
|
|
|
|
|
|
|
string VtxShaderSource = EmbeddedResource.GetString("GlFbVtxShader");
|
|
|
|
string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader");
|
|
|
|
|
|
|
|
GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
|
|
|
|
GL.ShaderSource(FragShaderHandle, FragShaderSource);
|
|
|
|
GL.CompileShader(VtxShaderHandle);
|
|
|
|
GL.CompileShader(FragShaderHandle);
|
|
|
|
|
|
|
|
PrgShaderHandle = GL.CreateProgram();
|
|
|
|
|
|
|
|
GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
|
|
|
|
GL.AttachShader(PrgShaderHandle, FragShaderHandle);
|
|
|
|
GL.LinkProgram(PrgShaderHandle);
|
|
|
|
GL.UseProgram(PrgShaderHandle);
|
|
|
|
|
|
|
|
int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
|
|
|
|
|
|
|
|
GL.Uniform1(TexUniformLocation, 0);
|
|
|
|
|
|
|
|
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
|
|
|
|
|
|
|
|
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupTexture()
|
|
|
|
{
|
|
|
|
Pixels = new int[TexWidth * TexHeight];
|
|
|
|
|
|
|
|
if (TexHandle == 0)
|
|
|
|
{
|
|
|
|
TexHandle = GL.GenTexture();
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
|
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
|
|
GL.TexImage2D(TextureTarget.Texture2D,
|
|
|
|
0,
|
|
|
|
PixelInternalFormat.Rgba,
|
|
|
|
TexWidth,
|
|
|
|
TexHeight,
|
|
|
|
0,
|
|
|
|
PixelFormat.Rgba,
|
|
|
|
PixelType.UnsignedByte,
|
|
|
|
IntPtr.Zero);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupVertex()
|
|
|
|
{
|
|
|
|
VaoHandle = GL.GenVertexArray();
|
|
|
|
VboHandle = GL.GenBuffer();
|
|
|
|
|
|
|
|
float[] Buffer = new float[]
|
|
|
|
{
|
|
|
|
-1, 1, 0, 0,
|
|
|
|
1, 1, 1, 0,
|
|
|
|
-1, -1, 0, 1,
|
|
|
|
1, -1, 1, 1
|
|
|
|
};
|
|
|
|
|
|
|
|
IntPtr Length = new IntPtr(Buffer.Length * 4);
|
|
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
|
|
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
|
|
|
|
|
|
GL.BindVertexArray(VaoHandle);
|
|
|
|
|
|
|
|
GL.EnableVertexAttribArray(0);
|
|
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
|
|
|
|
|
|
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
|
|
|
|
|
|
|
|
GL.EnableVertexAttribArray(1);
|
|
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
|
|
|
|
|
|
|
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
|
|
|
|
|
|
|
|
GL.BindVertexArray(0);
|
|
|
|
}
|
|
|
|
|
2018-03-01 03:37:40 +01:00
|
|
|
public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
|
2018-02-23 22:48:27 +01:00
|
|
|
{
|
|
|
|
if (Fb == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentNullException(nameof(Fb));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Width < 0)
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Width));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Height < 0)
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Height));
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
lock (FbPtrLock)
|
|
|
|
{
|
|
|
|
FbPtr = Fb;
|
|
|
|
}
|
2018-02-23 22:48:27 +01:00
|
|
|
|
|
|
|
if (Width != TexWidth ||
|
|
|
|
Height != TexHeight)
|
|
|
|
{
|
|
|
|
TexWidth = Width;
|
|
|
|
TexHeight = Height;
|
|
|
|
|
|
|
|
SetupTexture();
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.UseProgram(PrgShaderHandle);
|
|
|
|
|
|
|
|
int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform");
|
|
|
|
|
|
|
|
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
|
|
|
|
|
|
|
|
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
|
|
|
|
|
|
|
|
GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight));
|
2018-03-01 03:37:40 +01:00
|
|
|
|
|
|
|
int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
|
|
|
|
|
|
|
|
GL.Uniform2(OffsetUniformLocation, Offs);
|
2018-02-23 22:48:27 +01:00
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
public void Reset()
|
2018-02-23 22:48:27 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
lock (FbPtrLock)
|
2018-02-23 22:48:27 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
FbPtr = null;
|
2018-02-23 22:48:27 +01:00
|
|
|
}
|
2018-03-12 05:04:52 +01:00
|
|
|
}
|
2018-02-23 22:48:27 +01:00
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
public void Render()
|
|
|
|
{
|
|
|
|
lock (FbPtrLock)
|
2018-02-23 22:48:27 +01:00
|
|
|
{
|
2018-03-12 05:04:52 +01:00
|
|
|
if (FbPtr == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int Y = 0; Y < TexHeight; Y++)
|
|
|
|
for (int X = 0; X < TexWidth; X++)
|
|
|
|
{
|
|
|
|
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
|
|
|
|
}
|
2018-02-23 22:48:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
|
|
|
GL.TexSubImage2D(TextureTarget.Texture2D,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
TexWidth,
|
|
|
|
TexHeight,
|
|
|
|
PixelFormat.Rgba,
|
|
|
|
PixelType.UnsignedByte,
|
|
|
|
Pixels);
|
|
|
|
|
|
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
|
|
|
|
|
|
GL.BindVertexArray(VaoHandle);
|
|
|
|
|
|
|
|
GL.UseProgram(PrgShaderHandle);
|
|
|
|
|
|
|
|
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
private int GetSwizzleOffset(int X, int Y)
|
|
|
|
{
|
|
|
|
int Pos;
|
|
|
|
|
|
|
|
Pos = (Y & 0x7f) >> 4;
|
|
|
|
Pos += (X >> 4) << 3;
|
|
|
|
Pos += (Y >> 7) * ((TexWidth >> 4) << 3);
|
|
|
|
Pos *= 1024;
|
|
|
|
Pos += ((Y & 0xf) >> 3) << 9;
|
|
|
|
Pos += ((X & 0xf) >> 3) << 8;
|
|
|
|
Pos += ((Y & 0x7) >> 1) << 6;
|
|
|
|
Pos += ((X & 0x7) >> 2) << 5;
|
|
|
|
Pos += ((Y & 0x1) >> 0) << 4;
|
|
|
|
Pos += ((X & 0x3) >> 0) << 2;
|
|
|
|
|
|
|
|
return Pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|