aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2018-02-23 18:48:27 -0300
committergdkchan <gab.dark.100@gmail.com>2018-02-23 18:48:27 -0300
commit2ed733b1d5addad027f48acfdd407e64b71427fc (patch)
tree1254df3ab2bace8de561b5a3549f668fabcf4aa9 /Ryujinx.Graphics
parenteafc58c9f2e2e0c19d22f0da2a93ab5372aeef29 (diff)
Somewhat better NvFlinger (I guess) (fixes #30)
Diffstat (limited to 'Ryujinx.Graphics')
-rw-r--r--Ryujinx.Graphics/Gal/EmbeddedResource.cs20
-rw-r--r--Ryujinx.Graphics/Gal/IGalRenderer.cs7
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl13
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl26
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs228
-rw-r--r--Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs40
-rw-r--r--Ryujinx.Graphics/Ryujinx.Graphics.csproj17
7 files changed, 345 insertions, 6 deletions
diff --git a/Ryujinx.Graphics/Gal/EmbeddedResource.cs b/Ryujinx.Graphics/Gal/EmbeddedResource.cs
new file mode 100644
index 00000000..45b77da7
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/EmbeddedResource.cs
@@ -0,0 +1,20 @@
+using System.IO;
+using System.Reflection;
+
+namespace Ryujinx.Graphics.Gal
+{
+ static class EmbeddedResource
+ {
+ public static string GetString(string Name)
+ {
+ Assembly Asm = typeof(EmbeddedResource).Assembly;
+
+ using (Stream ResStream = Asm.GetManifestResourceStream(Name))
+ {
+ StreamReader Reader = new StreamReader(ResStream);
+
+ return Reader.ReadToEnd();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index 1870aca5..5854c54a 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -2,14 +2,15 @@ using System;
namespace Ryujinx.Graphics.Gal
{
- public interface IGalRenderer
+ public unsafe interface IGalRenderer
{
- long FrameBufferPtr { get; set; }
-
void QueueAction(Action ActionMthd);
void RunActions();
+ void InitializeFrameBuffer();
void Render();
+ void SetWindowSize(int Width, int Height);
+ void SetFrameBuffer(byte* Fb, int Width, int Height, float SX, float SY, float R);
void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
void BindTexture(int Index);
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
new file mode 100644
index 00000000..74e33bd7
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl
@@ -0,0 +1,13 @@
+#version 330 core
+
+precision highp float;
+
+uniform sampler2D tex;
+
+in vec2 tex_coord;
+
+out vec4 out_frag_color;
+
+void main(void) {
+ out_frag_color = texture(tex, tex_coord);
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
new file mode 100644
index 00000000..933fa6aa
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl
@@ -0,0 +1,26 @@
+#version 330 core
+
+precision highp float;
+
+uniform vec2 window_size;
+uniform mat2 transform;
+
+layout(location = 0) in vec2 in_position;
+layout(location = 1) in vec2 in_tex_coord;
+
+out vec2 tex_coord;
+
+// Have a fixed aspect ratio, fit the image within the available space.
+vec2 get_scale_ratio(void) {
+ vec2 native_size = vec2(1280, 720);
+ vec2 ratio = vec2(
+ (window_size.y * native_size.x) / (native_size.y * window_size.x),
+ (window_size.x * native_size.y) / (native_size.x * window_size.y)
+ );
+ return min(ratio, 1);
+}
+
+void main(void) {
+ tex_coord = in_tex_coord;
+ gl_Position = vec4((transform * in_position) * get_scale_ratio(), 0, 1);
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
new file mode 100644
index 00000000..c66c0cb7
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
@@ -0,0 +1,228 @@
+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;
+
+ public FrameBuffer(int Width, int Height)
+ {
+ if (Width < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Width));
+ }
+
+ if (Height < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Height));
+ }
+
+ 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);
+ }
+
+ public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform)
+ {
+ if (Fb == null)
+ {
+ throw new ArgumentNullException(nameof(Fb));
+ }
+
+ if (Width < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Width));
+ }
+
+ if (Height < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(Height));
+ }
+
+ FbPtr = Fb;
+
+ 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));
+ }
+
+ public void Render()
+ {
+ 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)));
+ }
+
+ 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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
index 7429569b..6bf17d09 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -1,7 +1,9 @@
+using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
+
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OpenGLRenderer : IGalRenderer
@@ -25,6 +27,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private Queue<Action> ActionsQueue;
+ private FrameBuffer FbRenderer;
+
public long FrameBufferPtr { get; set; }
public OpenGLRenderer()
@@ -36,6 +40,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue = new Queue<Action>();
}
+ public void InitializeFrameBuffer()
+ {
+ FbRenderer = new FrameBuffer(1280, 720);
+ }
+
public void QueueAction(Action ActionMthd)
{
ActionsQueue.Enqueue(ActionMthd);
@@ -43,14 +52,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void RunActions()
{
- while (ActionsQueue.Count > 0)
+ int Count = ActionsQueue.Count;
+
+ while (Count-- > 0)
{
ActionsQueue.Dequeue()();
}
- }
+ }
public void Render()
{
+ FbRenderer.Render();
+
for (int Index = 0; Index < VertexBuffers.Count; Index++)
{
VertexBuffer Vb = VertexBuffers[Index];
@@ -62,7 +75,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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 Rotate)
+ {
+ Matrix2 Transform;
+
+ Transform = Matrix2.CreateScale(ScaleX, ScaleY);
+ Transform *= Matrix2.CreateRotation(Rotate);
+
+ FbRenderer.Set(Fb, Width, Height, Transform);
}
public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj
index 657beb82..01bf41f8 100644
--- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj
+++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj
@@ -4,6 +4,14 @@
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+
<ItemGroup>
<PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
</ItemGroup>
@@ -12,4 +20,13 @@
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Gal\OpenGL\FbVtxShader.glsl">
+ <LogicalName>GlFbVtxShader</LogicalName>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Gal\OpenGL\FbFragShader.glsl">
+ <LogicalName>GlFbFragShader</LogicalName>
+ </EmbeddedResource>
+ </ItemGroup>
+
</Project>