2018-02-23 22:48:27 +01:00
|
|
|
using OpenTK;
|
2018-02-05 00:08:20 +01:00
|
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
using System;
|
2018-03-01 03:37:40 +01:00
|
|
|
using System.Collections.Concurrent;
|
2018-02-05 00:08:20 +01:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
2018-02-20 21:09:23 +01:00
|
|
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
|
|
|
public class OpenGLRenderer : IGalRenderer
|
|
|
|
{
|
|
|
|
private struct VertexBuffer
|
|
|
|
{
|
|
|
|
public int VaoHandle;
|
|
|
|
public int VboHandle;
|
|
|
|
|
|
|
|
public int PrimCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private struct Texture
|
|
|
|
{
|
|
|
|
public int Handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<VertexBuffer> VertexBuffers;
|
|
|
|
|
|
|
|
private Texture[] Textures;
|
|
|
|
|
2018-03-01 03:37:40 +01:00
|
|
|
private ConcurrentQueue<Action> ActionsQueue;
|
2018-02-05 00:08:20 +01:00
|
|
|
|
2018-02-23 22:48:27 +01:00
|
|
|
private FrameBuffer FbRenderer;
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
public OpenGLRenderer()
|
|
|
|
{
|
|
|
|
VertexBuffers = new List<VertexBuffer>();
|
|
|
|
|
|
|
|
Textures = new Texture[8];
|
|
|
|
|
2018-03-01 03:37:40 +01:00
|
|
|
ActionsQueue = new ConcurrentQueue<Action>();
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
2018-02-23 22:48:27 +01:00
|
|
|
public void InitializeFrameBuffer()
|
|
|
|
{
|
|
|
|
FbRenderer = new FrameBuffer(1280, 720);
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:04:52 +01:00
|
|
|
public void ResetFrameBuffer()
|
|
|
|
{
|
|
|
|
FbRenderer.Reset();
|
|
|
|
}
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
public void QueueAction(Action ActionMthd)
|
|
|
|
{
|
|
|
|
ActionsQueue.Enqueue(ActionMthd);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void RunActions()
|
|
|
|
{
|
2018-02-23 22:48:27 +01:00
|
|
|
int Count = ActionsQueue.Count;
|
|
|
|
|
2018-03-01 03:37:40 +01:00
|
|
|
while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
|
2018-02-05 00:08:20 +01:00
|
|
|
{
|
2018-03-01 03:37:40 +01:00
|
|
|
RenderAction();
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
2018-02-24 01:59:38 +01:00
|
|
|
}
|
2018-02-05 00:08:20 +01:00
|
|
|
|
|
|
|
public void Render()
|
|
|
|
{
|
2018-02-23 22:48:27 +01:00
|
|
|
FbRenderer.Render();
|
|
|
|
|
2018-02-05 00:08:20 +01:00
|
|
|
for (int Index = 0; Index < VertexBuffers.Count; Index++)
|
|
|
|
{
|
|
|
|
VertexBuffer Vb = VertexBuffers[Index];
|
|
|
|
|
|
|
|
if (Vb.VaoHandle != 0 &&
|
|
|
|
Vb.PrimCount != 0)
|
|
|
|
{
|
|
|
|
GL.BindVertexArray(Vb.VaoHandle);
|
|
|
|
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
|
|
|
|
}
|
|
|
|
}
|
2018-02-23 22:48:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetWindowSize(int Width, int Height)
|
|
|
|
{
|
|
|
|
FbRenderer.WindowWidth = Width;
|
|
|
|
FbRenderer.WindowHeight = Height;
|
|
|
|
}
|
|
|
|
|
|
|
|
public unsafe void SetFrameBuffer(
|
|
|
|
byte* Fb,
|
|
|
|
int Width,
|
|
|
|
int Height,
|
|
|
|
float ScaleX,
|
|
|
|
float ScaleY,
|
2018-03-01 03:37:40 +01:00
|
|
|
float OffsX,
|
|
|
|
float OffsY,
|
2018-02-23 22:48:27 +01:00
|
|
|
float Rotate)
|
|
|
|
{
|
|
|
|
Matrix2 Transform;
|
|
|
|
|
|
|
|
Transform = Matrix2.CreateScale(ScaleX, ScaleY);
|
|
|
|
Transform *= Matrix2.CreateRotation(Rotate);
|
|
|
|
|
2018-03-01 03:37:40 +01:00
|
|
|
Vector2 Offs = new Vector2(OffsX, OffsY);
|
|
|
|
|
|
|
|
FbRenderer.Set(Fb, Width, Height, Transform, Offs);
|
2018-02-05 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
|
|
|
|
{
|
|
|
|
if (Index < 0)
|
|
|
|
{
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(Index));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Buffer.Length == 0 || Stride == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnsureVbInitialized(Index);
|
|
|
|
|
|
|
|
VertexBuffer Vb = VertexBuffers[Index];
|
|
|
|
|
|
|
|
Vb.PrimCount = Buffer.Length / Stride;
|
|
|
|
|
|
|
|
VertexBuffers[Index] = Vb;
|
|
|
|
|
|
|
|
IntPtr Length = new IntPtr(Buffer.Length);
|
|
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
|
|
|
|
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
|
|
|
|
|
|
GL.BindVertexArray(Vb.VaoHandle);
|
|
|
|
|
|
|
|
for (int Attr = 0; Attr < 16; Attr++)
|
|
|
|
{
|
|
|
|
GL.DisableVertexAttribArray(Attr);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (GalVertexAttrib Attrib in Attribs)
|
|
|
|
{
|
|
|
|
if (Attrib.Index >= 3) break;
|
|
|
|
|
|
|
|
GL.EnableVertexAttribArray(Attrib.Index);
|
|
|
|
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
|
|
|
|
|
|
|
|
int Size = 0;
|
|
|
|
|
|
|
|
switch (Attrib.Size)
|
|
|
|
{
|
|
|
|
case GalVertexAttribSize._8:
|
|
|
|
case GalVertexAttribSize._16:
|
|
|
|
case GalVertexAttribSize._32:
|
|
|
|
Size = 1;
|
|
|
|
break;
|
|
|
|
case GalVertexAttribSize._8_8:
|
|
|
|
case GalVertexAttribSize._16_16:
|
|
|
|
case GalVertexAttribSize._32_32:
|
|
|
|
Size = 2;
|
|
|
|
break;
|
|
|
|
case GalVertexAttribSize._8_8_8:
|
|
|
|
case GalVertexAttribSize._11_11_10:
|
|
|
|
case GalVertexAttribSize._16_16_16:
|
|
|
|
case GalVertexAttribSize._32_32_32:
|
|
|
|
Size = 3;
|
|
|
|
break;
|
|
|
|
case GalVertexAttribSize._8_8_8_8:
|
|
|
|
case GalVertexAttribSize._10_10_10_2:
|
|
|
|
case GalVertexAttribSize._16_16_16_16:
|
|
|
|
case GalVertexAttribSize._32_32_32_32:
|
|
|
|
Size = 4;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Signed =
|
|
|
|
Attrib.Type == GalVertexAttribType.Snorm ||
|
|
|
|
Attrib.Type == GalVertexAttribType.Sint ||
|
|
|
|
Attrib.Type == GalVertexAttribType.Sscaled;
|
|
|
|
|
|
|
|
bool Normalize =
|
|
|
|
Attrib.Type == GalVertexAttribType.Snorm ||
|
|
|
|
Attrib.Type == GalVertexAttribType.Unorm;
|
|
|
|
|
|
|
|
VertexAttribPointerType Type = 0;
|
|
|
|
|
|
|
|
switch (Attrib.Type)
|
|
|
|
{
|
|
|
|
case GalVertexAttribType.Snorm:
|
|
|
|
case GalVertexAttribType.Unorm:
|
|
|
|
case GalVertexAttribType.Sint:
|
|
|
|
case GalVertexAttribType.Uint:
|
|
|
|
case GalVertexAttribType.Uscaled:
|
|
|
|
case GalVertexAttribType.Sscaled:
|
|
|
|
{
|
|
|
|
switch (Attrib.Size)
|
|
|
|
{
|
|
|
|
case GalVertexAttribSize._8:
|
|
|
|
case GalVertexAttribSize._8_8:
|
|
|
|
case GalVertexAttribSize._8_8_8:
|
|
|
|
case GalVertexAttribSize._8_8_8_8:
|
|
|
|
{
|
|
|
|
Type = Signed
|
|
|
|
? VertexAttribPointerType.Byte
|
|
|
|
: VertexAttribPointerType.UnsignedByte;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GalVertexAttribSize._16:
|
|
|
|
case GalVertexAttribSize._16_16:
|
|
|
|
case GalVertexAttribSize._16_16_16:
|
|
|
|
case GalVertexAttribSize._16_16_16_16:
|
|
|
|
{
|
|
|
|
Type = Signed
|
|
|
|
? VertexAttribPointerType.Short
|
|
|
|
: VertexAttribPointerType.UnsignedShort;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GalVertexAttribSize._10_10_10_2:
|
|
|
|
case GalVertexAttribSize._11_11_10:
|
|
|
|
case GalVertexAttribSize._32:
|
|
|
|
case GalVertexAttribSize._32_32:
|
|
|
|
case GalVertexAttribSize._32_32_32:
|
|
|
|
case GalVertexAttribSize._32_32_32_32:
|
|
|
|
{
|
|
|
|
Type = Signed
|
|
|
|
? VertexAttribPointerType.Int
|
|
|
|
: VertexAttribPointerType.UnsignedInt;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case GalVertexAttribType.Float:
|
|
|
|
{
|
|
|
|
Type = VertexAttribPointerType.Float;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.VertexAttribPointer(
|
|
|
|
Attrib.Index,
|
|
|
|
Size,
|
|
|
|
Type,
|
|
|
|
Normalize,
|
|
|
|
Stride,
|
|
|
|
Attrib.Offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.BindVertexArray(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height)
|
|
|
|
{
|
|
|
|
EnsureTexInitialized(Index);
|
|
|
|
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
|
|
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
|
|
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
|
|
|
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,
|
|
|
|
Width,
|
|
|
|
Height,
|
|
|
|
0,
|
|
|
|
PixelFormat.Rgba,
|
|
|
|
PixelType.UnsignedByte,
|
|
|
|
Buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void BindTexture(int Index)
|
|
|
|
{
|
|
|
|
GL.ActiveTexture(TextureUnit.Texture0 + Index);
|
|
|
|
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void EnsureVbInitialized(int VbIndex)
|
|
|
|
{
|
|
|
|
while (VbIndex >= VertexBuffers.Count)
|
|
|
|
{
|
|
|
|
VertexBuffers.Add(new VertexBuffer());
|
|
|
|
}
|
|
|
|
|
|
|
|
VertexBuffer Vb = VertexBuffers[VbIndex];
|
|
|
|
|
|
|
|
if (Vb.VaoHandle == 0)
|
|
|
|
{
|
|
|
|
Vb.VaoHandle = GL.GenVertexArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Vb.VboHandle == 0)
|
|
|
|
{
|
|
|
|
Vb.VboHandle = GL.GenBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
VertexBuffers[VbIndex] = Vb;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void EnsureTexInitialized(int TexIndex)
|
|
|
|
{
|
|
|
|
Texture Tex = Textures[TexIndex];
|
|
|
|
|
|
|
|
if (Tex.Handle == 0)
|
|
|
|
{
|
|
|
|
Tex.Handle = GL.GenTexture();
|
|
|
|
}
|
|
|
|
|
|
|
|
Textures[TexIndex] = Tex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|