using OpenTK; using OpenTK.Graphics.OpenGL; using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { class OGLFrameBuffer { private class FrameBuffer { public int Width { get; set; } public int Height { get; set; } public int Handle { get; private set; } public int RbHandle { get; private set; } public int TexHandle { get; private set; } public FrameBuffer(int Width, int Height) { this.Width = Width; this.Height = Height; Handle = GL.GenFramebuffer(); RbHandle = GL.GenRenderbuffer(); TexHandle = GL.GenTexture(); } } private struct ShaderProgram { public int Handle; public int VpHandle; public int FpHandle; } private Dictionary Fbs; private ShaderProgram Shader; private bool IsInitialized; private int RawFbTexWidth; private int RawFbTexHeight; private int RawFbTexHandle; private int CurrFbHandle; private int CurrTexHandle; private int VaoHandle; private int VboHandle; public OGLFrameBuffer() { Fbs = new Dictionary(); Shader = new ShaderProgram(); } public void Create(long Tag, int Width, int Height) { if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { if (Fb.Width != Width || Fb.Height != Height) { SetupTexture(Fb.TexHandle, Width, Height); Fb.Width = Width; Fb.Height = Height; } return; } Fb = new FrameBuffer(Width, Height); SetupTexture(Fb.TexHandle, Width, Height); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle); GL.RenderbufferStorage( RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, Width, Height); GL.FramebufferRenderbuffer( FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fb.RbHandle); GL.FramebufferTexture( FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fb.TexHandle, 0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0); Fbs.Add(Tag, Fb); } public void Bind(long Tag) { if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); CurrFbHandle = Fb.Handle; } } public void BindTexture(long Tag, int Index) { if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle); } } public void Set(long Tag) { if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) { CurrTexHandle = Fb.TexHandle; } } public void Set(byte[] Data, int Width, int Height) { if (RawFbTexHandle == 0) { RawFbTexHandle = GL.GenTexture(); } if (RawFbTexWidth != Width || RawFbTexHeight != Height) { SetupTexture(RawFbTexHandle, Width, Height); RawFbTexWidth = Width; RawFbTexHeight = Height; } GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle); (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data); CurrTexHandle = RawFbTexHandle; } public void SetTransform(Matrix2 Transform, Vector2 Offs) { EnsureInitialized(); int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); GL.UseProgram(Shader.Handle); int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset"); GL.Uniform2(OffsetUniformLocation, ref Offs); GL.UseProgram(CurrentProgram); } public void Render() { if (CurrTexHandle != 0) { EnsureInitialized(); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle); int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); GL.BindVertexArray(VaoHandle); GL.UseProgram(Shader.Handle); GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); //Restore the original state. GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle); GL.UseProgram(CurrentProgram); } } private void EnsureInitialized() { if (!IsInitialized) { IsInitialized = true; SetupShader(); SetupVertex(); } } private void SetupShader() { Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader); Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader); string VpSource = EmbeddedResource.GetString("GlFbVtxShader"); string FpSource = EmbeddedResource.GetString("GlFbFragShader"); GL.ShaderSource(Shader.VpHandle, VpSource); GL.ShaderSource(Shader.FpHandle, FpSource); GL.CompileShader(Shader.VpHandle); GL.CompileShader(Shader.FpHandle); Shader.Handle = GL.CreateProgram(); GL.AttachShader(Shader.Handle, Shader.VpHandle); GL.AttachShader(Shader.Handle, Shader.FpHandle); GL.LinkProgram(Shader.Handle); GL.UseProgram(Shader.Handle); Matrix2 Transform = Matrix2.Identity; int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex"); GL.Uniform1(TexUniformLocation, 0); int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size"); GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f)); int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform"); GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); } 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); } private void SetupTexture(int Handle, int Width, int Height) { GL.BindTexture(TextureTarget.Texture2D, Handle); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8); const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; const int Level = 0; const int Border = 0; GL.TexImage2D( TextureTarget.Texture2D, Level, InternalFmt, Width, Height, Border, Format, Type, IntPtr.Zero); } } }