using OpenTK; using OpenTK.Graphics.OpenGL; using System; using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { public class OpenGLRenderer : IGalRenderer { private struct VertexBuffer { public int VaoHandle; public int VboHandle; public int PrimCount; } private struct Texture { public int Handle; } private List VertexBuffers; private Texture[] Textures; private ConcurrentQueue ActionsQueue; private FrameBuffer FbRenderer; public OpenGLRenderer() { VertexBuffers = new List(); Textures = new Texture[8]; ActionsQueue = new ConcurrentQueue(); } public void InitializeFrameBuffer() { FbRenderer = new FrameBuffer(1280, 720); } public void ResetFrameBuffer() { FbRenderer.Reset(); } public void QueueAction(Action ActionMthd) { ActionsQueue.Enqueue(ActionMthd); } public void RunActions() { int Count = ActionsQueue.Count; while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction)) { RenderAction(); } } public void Render() { FbRenderer.Render(); 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); } } } 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, float OffsX, float OffsY, float Rotate) { Matrix2 Transform; Transform = Matrix2.CreateScale(ScaleX, ScaleY); Transform *= Matrix2.CreateRotation(Rotate); Vector2 Offs = new Vector2(OffsX, OffsY); FbRenderer.Set(Fb, Width, Height, Transform, Offs); } 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; } } }