Fix for current framebuffer issues (#78)

[GPU] Fix some of the current framebuffer issues
This commit is contained in:
gdkchan 2018-04-13 15:12:58 -03:00 committed by GitHub
parent 262b5b8054
commit c8c86a3854
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 482 additions and 891 deletions

View file

@ -244,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
Context.Memory.WriteInt64(Position + 0x20, Offset); Context.Memory.WriteInt64(Position + 0x20, Offset);
Map.GpuAddress = Offset;
return 0; return 0;
} }

View file

@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
public int Align; public int Align;
public int Kind; public int Kind;
public long CpuAddress; public long CpuAddress;
public long GpuAddress;
} }
} }

View file

@ -2,6 +2,7 @@ using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Services.Nv; using Ryujinx.Core.OsHle.Services.Nv;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Gpu;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -63,13 +64,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
private ManualResetEvent WaitBufferFree; private ManualResetEvent WaitBufferFree;
private object RenderQueueLock; private bool Disposed;
private int RenderQueueCount;
private bool NvFlingerDisposed;
private bool KeepRunning;
public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent) public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
{ {
@ -92,10 +87,6 @@ namespace Ryujinx.Core.OsHle.Services.Android
BufferQueue = new BufferEntry[0x40]; BufferQueue = new BufferEntry[0x40];
WaitBufferFree = new ManualResetEvent(false); WaitBufferFree = new ManualResetEvent(false);
RenderQueueLock = new object();
KeepRunning = true;
} }
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code) public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
@ -285,35 +276,24 @@ namespace Ryujinx.Core.OsHle.Services.Android
return 0; return 0;
} }
private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot) private void SendFrameBuffer(ServiceCtx Context, int Slot)
{ {
int FbWidth = BufferQueue[Slot].Data.Width; int FbWidth = 1280;
int FbHeight = BufferQueue[Slot].Data.Height; int FbHeight = 720;
long FbSize = (uint)FbWidth * FbHeight * 4;
NvMap Map = GetNvMap(Context, Slot); NvMap Map = GetNvMap(Context, Slot);
NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0); NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
long Address = Map.CpuAddress; long CpuAddr = Map.CpuAddress;
long GpuAddr = Map.GpuAddress;
if (MapFb.HasBufferOffset(Slot)) if (MapFb.HasBufferOffset(Slot))
{ {
Address += MapFb.GetBufferOffset(Slot); CpuAddr += MapFb.GetBufferOffset(Slot);
}
if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize) //TODO: Enable once the frame buffers problems are fixed.
{ //GpuAddr += MapFb.GetBufferOffset(Slot);
Logging.Error($"Frame buffer address {Address:x16} is invalid!");
BufferQueue[Slot].State = BufferState.Free;
ReleaseEvent.Handle.Set();
WaitBufferFree.Set();
return;
} }
BufferQueue[Slot].State = BufferState.Acquired; BufferQueue[Slot].State = BufferState.Acquired;
@ -367,41 +347,28 @@ namespace Ryujinx.Core.OsHle.Services.Android
Rotate = -MathF.PI * 0.5f; Rotate = -MathF.PI * 0.5f;
} }
lock (RenderQueueLock) Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
{
if (NvFlingerDisposed)
{
return;
}
Interlocked.Increment(ref RenderQueueCount); //TODO: Support double buffering here aswell, it is broken for GPU
//frame buffers because it seems to be completely out of sync.
if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr))
{
//Frame buffer is rendered to by the GPU, we can just
//bind the frame buffer texture, it's not necessary to read anything.
Renderer.SetFrameBuffer(GpuAddr);
}
else
{
//Frame buffer is not set on the GPU registers, in this case
//assume that the app is manually writing to it.
Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight);
byte[] Data = TextureReader.Read(Context.Memory, Texture);
Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
} }
byte* Fb = (byte*)Context.Memory.Ram + Address; Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
Context.Ns.Gpu.Renderer.QueueAction(delegate()
{
Context.Ns.Gpu.Renderer.SetFrameBuffer(
Fb,
FbWidth,
FbHeight,
ScaleX,
ScaleY,
OffsX,
OffsY,
Rotate);
BufferQueue[Slot].State = BufferState.Free;
Interlocked.Decrement(ref RenderQueueCount);
ReleaseEvent.Handle.Set();
lock (WaitBufferFree)
{
WaitBufferFree.Set();
}
});
} }
private NvMap GetNvMap(ServiceCtx Context, int Slot) private NvMap GetNvMap(ServiceCtx Context, int Slot)
@ -420,6 +387,18 @@ namespace Ryujinx.Core.OsHle.Services.Android
return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle); return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
} }
private void ReleaseBuffer(int Slot)
{
BufferQueue[Slot].State = BufferState.Free;
ReleaseEvent.Handle.Set();
lock (WaitBufferFree)
{
WaitBufferFree.Set();
}
}
private int GetFreeSlotBlocking(int Width, int Height) private int GetFreeSlotBlocking(int Width, int Height)
{ {
int Slot; int Slot;
@ -435,7 +414,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
Logging.Debug("Waiting for a free BufferQueue slot..."); Logging.Debug("Waiting for a free BufferQueue slot...");
if (!KeepRunning) if (Disposed)
{ {
break; break;
} }
@ -445,7 +424,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
WaitBufferFree.WaitOne(); WaitBufferFree.WaitOne();
} }
while (KeepRunning); while (!Disposed);
Logging.Debug($"Found free BufferQueue slot {Slot}!"); Logging.Debug($"Found free BufferQueue slot {Slot}!");
@ -485,26 +464,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
protected virtual void Dispose(bool Disposing) protected virtual void Dispose(bool Disposing)
{ {
if (Disposing && !NvFlingerDisposed) if (Disposing && !Disposed)
{ {
lock (RenderQueueLock) Disposed = true;
{
NvFlingerDisposed = true;
}
//Ensure that all pending actions was sent before
//we can safely assume that the class was disposed.
while (RenderQueueCount > 0)
{
Thread.Yield();
}
Renderer.ResetFrameBuffer();
lock (WaitBufferFree) lock (WaitBufferFree)
{ {
KeepRunning = false;
WaitBufferFree.Set(); WaitBufferFree.Set();
} }

View file

@ -2,10 +2,10 @@ namespace Ryujinx.Graphics.Gal
{ {
public enum GalBlendEquation public enum GalBlendEquation
{ {
FuncAdd = 0x8006, FuncAdd = 1,
Min = 0x8007, FuncSubtract = 2,
Max = 0x8008, FuncReverseSubtract = 3,
FuncSubtract = 0x800a, Min = 4,
FuncReverseSubtract = 0x800b Max = 5
} }
} }

View file

@ -2,24 +2,24 @@ namespace Ryujinx.Graphics.Gal
{ {
public enum GalBlendFactor public enum GalBlendFactor
{ {
Zero = 0x4000, Zero = 0x1,
One = 0x4001, One = 0x2,
SrcColor = 0x4300, SrcColor = 0x3,
OneMinusSrcColor = 0x4301, OneMinusSrcColor = 0x4,
SrcAlpha = 0x4302, SrcAlpha = 0x5,
OneMinusSrcAlpha = 0x4303, OneMinusSrcAlpha = 0x6,
DstAlpha = 0x4304, DstAlpha = 0x7,
OneMinusDstAlpha = 0x4305, OneMinusDstAlpha = 0x8,
DstColor = 0x4306, DstColor = 0x9,
OneMinusDstColor = 0x4307, OneMinusDstColor = 0xa,
SrcAlphaSaturate = 0x4308, SrcAlphaSaturate = 0xb,
ConstantColor = 0xc001, Src1Color = 0x10,
OneMinusConstantColor = 0xc002, OneMinusSrc1Color = 0x11,
ConstantAlpha = 0xc003, Src1Alpha = 0x12,
OneMinusConstantAlpha = 0xc004, OneMinusSrc1Alpha = 0x13,
Src1Color = 0xc900, ConstantColor = 0x61,
OneMinusSrc1Color = 0xc901, OneMinusConstantColor = 0x62,
Src1Alpha = 0xc902, ConstantAlpha = 0x63,
OneMinusSrc1Alpha = 0xc903 OneMinusConstantAlpha = 0x64
} }
} }

View file

@ -6,21 +6,12 @@ namespace Ryujinx.Graphics.Gal
public unsafe interface IGalRenderer public unsafe interface IGalRenderer
{ {
void QueueAction(Action ActionMthd); void QueueAction(Action ActionMthd);
void RunActions(); void RunActions();
void InitializeFrameBuffer();
void ResetFrameBuffer();
void Render(); void Render();
void SetWindowSize(int Width, int Height); void SetWindowSize(int Width, int Height);
void SetFrameBuffer(
byte* Fb,
int Width,
int Height,
float ScaleX,
float ScaleY,
float OffsX,
float OffsY,
float Rotate);
//Blend //Blend
void SetBlendEnable(bool Enable); void SetBlendEnable(bool Enable);
@ -39,11 +30,17 @@ namespace Ryujinx.Graphics.Gal
GalBlendFactor FuncDstAlpha); GalBlendFactor FuncDstAlpha);
//Frame Buffer //Frame Buffer
void SetFb(int FbIndex, int Width, int Height); void CreateFrameBuffer(long Tag, int Width, int Height);
void BindFrameBuffer(int FbIndex); void BindFrameBuffer(long Tag);
void DrawFrameBuffer(int FbIndex); void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler);
void SetFrameBuffer(long Tag);
void SetFrameBuffer(byte[] Data, int Width, int Height);
void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
//Rasterizer //Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags); void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
@ -70,8 +67,8 @@ namespace Ryujinx.Graphics.Gal
void BindProgram(); void BindProgram();
//Texture //Texture
void SetTexture(int Index, GalTexture Tex); void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
void SetSampler(int Index, GalTextureSampler Sampler); void BindTexture(int Index);
} }
} }

View file

@ -1,250 +0,0 @@
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;
private object FbPtrLock;
public FrameBuffer(int Width, int Height)
{
if (Width < 0)
{
throw new ArgumentOutOfRangeException(nameof(Width));
}
if (Height < 0)
{
throw new ArgumentOutOfRangeException(nameof(Height));
}
FbPtrLock = new object();
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, Vector2 Offs)
{
if (Fb == null)
{
throw new ArgumentNullException(nameof(Fb));
}
if (Width < 0)
{
throw new ArgumentOutOfRangeException(nameof(Width));
}
if (Height < 0)
{
throw new ArgumentOutOfRangeException(nameof(Height));
}
lock (FbPtrLock)
{
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));
int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
GL.Uniform2(OffsetUniformLocation, Offs);
}
public void Reset()
{
lock (FbPtrLock)
{
FbPtr = null;
}
}
public void Render()
{
lock (FbPtrLock)
{
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;
}
}
}

View file

@ -127,17 +127,72 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation) public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
{ {
return (BlendEquationMode)BlendEquation; switch (BlendEquation)
{
case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd;
case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract;
case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
case GalBlendEquation.Min: return BlendEquationMode.Min;
case GalBlendEquation.Max: return BlendEquationMode.Max;
}
throw new ArgumentException(nameof(BlendEquation));
} }
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor) public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
{ {
return (BlendingFactorSrc)(BlendFactor - 0x4000); switch (BlendFactor)
{
case GalBlendFactor.Zero: return BlendingFactorSrc.Zero;
case GalBlendFactor.One: return BlendingFactorSrc.One;
case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor;
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorSrc.OneMinusSrcColor;
case GalBlendFactor.DstColor: return BlendingFactorSrc.DstColor;
case GalBlendFactor.OneMinusDstColor: return BlendingFactorSrc.OneMinusDstColor;
case GalBlendFactor.SrcAlpha: return BlendingFactorSrc.SrcAlpha;
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorSrc.OneMinusSrcAlpha;
case GalBlendFactor.DstAlpha: return BlendingFactorSrc.DstAlpha;
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorSrc.OneMinusDstAlpha;
case GalBlendFactor.ConstantColor: return BlendingFactorSrc.ConstantColor;
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor;
case GalBlendFactor.ConstantAlpha: return BlendingFactorSrc.ConstantAlpha;
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha;
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorSrc.SrcAlphaSaturate;
case GalBlendFactor.Src1Color: return BlendingFactorSrc.Src1Color;
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorSrc.OneMinusSrc1Color;
case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha;
}
throw new ArgumentException(nameof(BlendFactor));
} }
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor) public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
{ {
return (BlendingFactorDest)(BlendFactor - 0x4000); switch (BlendFactor)
{
case GalBlendFactor.Zero: return BlendingFactorDest.Zero;
case GalBlendFactor.One: return BlendingFactorDest.One;
case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor;
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorDest.OneMinusSrcColor;
case GalBlendFactor.DstColor: return BlendingFactorDest.DstColor;
case GalBlendFactor.OneMinusDstColor: return BlendingFactorDest.OneMinusDstColor;
case GalBlendFactor.SrcAlpha: return BlendingFactorDest.SrcAlpha;
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorDest.OneMinusSrcAlpha;
case GalBlendFactor.DstAlpha: return BlendingFactorDest.DstAlpha;
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorDest.OneMinusDstAlpha;
case GalBlendFactor.ConstantColor: return BlendingFactorDest.ConstantColor;
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor;
case GalBlendFactor.ConstantAlpha: return BlendingFactorDest.ConstantAlpha;
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha;
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorDest.SrcAlphaSaturate;
case GalBlendFactor.Src1Color: return BlendingFactorDest.Src1Color;
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorDest.OneMinusSrc1Color;
case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha;
}
throw new ArgumentException(nameof(BlendFactor));
} }
} }
} }

View file

@ -1,16 +1,30 @@
using OpenTK; using OpenTK;
using OpenTK.Graphics.OpenGL; using OpenTK.Graphics.OpenGL;
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLFrameBuffer class OGLFrameBuffer
{ {
private struct FrameBuffer private class FrameBuffer
{ {
public int FbHandle; public int Width { get; set; }
public int RbHandle; public int Height { get; set; }
public int TexHandle;
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 private struct ShaderProgram
@ -20,83 +34,175 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int FpHandle; public int FpHandle;
} }
private FrameBuffer[] Fbs; private Dictionary<long, FrameBuffer> Fbs;
private ShaderProgram Shader; private ShaderProgram Shader;
private bool IsInitialized; private bool IsInitialized;
private int RawFbTexWidth;
private int RawFbTexHeight;
private int RawFbTexHandle;
private int CurrFbHandle;
private int CurrTexHandle;
private int VaoHandle; private int VaoHandle;
private int VboHandle; private int VboHandle;
public OGLFrameBuffer() public OGLFrameBuffer()
{ {
Fbs = new FrameBuffer[16]; Fbs = new Dictionary<long, FrameBuffer>();
Shader = new ShaderProgram(); Shader = new ShaderProgram();
} }
public void Set(int Index, int Width, int Height) public void Create(long Tag, int Width, int Height)
{ {
if (Fbs[Index].FbHandle != 0) 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; return;
} }
Fbs[Index].FbHandle = GL.GenFramebuffer(); Fb = new FrameBuffer(Width, Height);
Fbs[Index].RbHandle = GL.GenRenderbuffer();
Fbs[Index].TexHandle = GL.GenTexture();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); SetupTexture(Fb.TexHandle, Width, Height);
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle); GL.RenderbufferStorage(
RenderbufferTarget.Renderbuffer,
RenderbufferStorage.Depth24Stencil8,
Width,
Height);
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); GL.FramebufferRenderbuffer(
FramebufferTarget.Framebuffer,
FramebufferAttachment.DepthStencilAttachment,
RenderbufferTarget.Renderbuffer,
Fb.RbHandle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.FramebufferTexture(
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0,
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); Fb.TexHandle,
0);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Fbs.Add(Tag, Fb);
} }
public void Bind(int Index) public void Bind(long Tag)
{ {
if (Fbs[Index].FbHandle == 0) if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{ {
return; GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
}
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle); CurrFbHandle = Fb.Handle;
}
} }
public void Draw(int Index) public void BindTexture(long Tag, int Index)
{ {
if (Fbs[Index].FbHandle == 0) if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
{ {
return; 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();
} }
EnsureInitialized(); if (RawFbTexWidth != Width ||
RawFbTexHeight != Height)
{
SetupTexture(RawFbTexHandle, Width, Height);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); RawFbTexWidth = Width;
RawFbTexHeight = Height;
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle); }
GL.ActiveTexture(TextureUnit.Texture0); GL.ActiveTexture(TextureUnit.Texture0);
GL.BindVertexArray(VaoHandle); 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); GL.UseProgram(Shader.Handle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); 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() private void EnsureInitialized()
@ -130,7 +236,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.LinkProgram(Shader.Handle); GL.LinkProgram(Shader.Handle);
GL.UseProgram(Shader.Handle); GL.UseProgram(Shader.Handle);
Matrix2 Transform = Matrix2.CreateScale(1, -1); Matrix2 Transform = Matrix2.Identity;
int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex"); int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
@ -178,5 +284,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8); 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);
}
} }
} }

View file

@ -15,10 +15,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
int Handle = EnsureTextureInitialized(Index); Bind(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle);
const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0; const int Border = 0;
if (IsCompressedTextureFormat(Texture.Format)) if (IsCompressedTextureFormat(Texture.Format))
@ -27,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.CompressedTexImage2D( GL.CompressedTexImage2D(
TextureTarget.Texture2D, TextureTarget.Texture2D,
0, Level,
InternalFmt, InternalFmt,
Texture.Width, Texture.Width,
Texture.Height, Texture.Height,
@ -39,27 +38,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba; const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
(PixelFormat, PixelType) Format = OGLEnumConverter.GetTextureFormat(Texture.Format); (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
GL.TexImage2D( GL.TexImage2D(
TextureTarget.Texture2D, TextureTarget.Texture2D,
0, Level,
InternalFmt, InternalFmt,
Texture.Width, Texture.Width,
Texture.Height, Texture.Height,
Border, Border,
Format.Item1, Format,
Format.Item2, Type,
Texture.Data); Texture.Data);
} }
} }
public void Set(int Index, GalTextureSampler Sampler) public void Bind(int Index)
{ {
int Handle = EnsureTextureInitialized(Index); int Handle = EnsureTextureInitialized(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle); GL.BindTexture(TextureTarget.Texture2D, Handle);
}
public static void Set(GalTextureSampler Sampler)
{
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU); int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV); int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);

View file

@ -19,8 +19,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private ConcurrentQueue<Action> ActionsQueue; private ConcurrentQueue<Action> ActionsQueue;
private FrameBuffer FbRenderer;
public OpenGLRenderer() public OpenGLRenderer()
{ {
Blend = new OGLBlend(); Blend = new OGLBlend();
@ -36,16 +34,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue = new ConcurrentQueue<Action>(); ActionsQueue = new ConcurrentQueue<Action>();
} }
public void InitializeFrameBuffer()
{
FbRenderer = new FrameBuffer(1280, 720);
}
public void ResetFrameBuffer()
{
FbRenderer.Reset();
}
public void QueueAction(Action ActionMthd) public void QueueAction(Action ActionMthd)
{ {
ActionsQueue.Enqueue(ActionMthd); ActionsQueue.Enqueue(ActionMthd);
@ -63,33 +51,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void Render() public void Render()
{ {
FbRenderer.Render(); FrameBuffer.Render();
} }
public void SetWindowSize(int Width, int Height) public void SetWindowSize(int Width, int Height)
{ {
FbRenderer.WindowWidth = Width; //TODO
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 SetBlendEnable(bool Enable) public void SetBlendEnable(bool Enable)
@ -132,19 +99,46 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}); });
} }
public void SetFb(int FbIndex, int Width, int Height) public void CreateFrameBuffer(long Tag, int Width, int Height)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height)); ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
} }
public void BindFrameBuffer(int FbIndex) public void BindFrameBuffer(long Tag)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex)); ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
} }
public void DrawFrameBuffer(int FbIndex) public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
{ {
ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex)); ActionsQueue.Enqueue(() =>
{
FrameBuffer.BindTexture(Tag, Index);
OGLTexture.Set(Sampler);
});
}
public void SetFrameBuffer(long Tag)
{
ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag));
}
public void SetFrameBuffer(byte[] Data, int Width, int Height)
{
ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height));
}
public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY)
{
Matrix2 Transform;
Transform = Matrix2.CreateScale(SX, SY);
Transform *= Matrix2.CreateRotation(Rotate);
Vector2 Offs = new Vector2(TX, TY);
ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs));
} }
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags) public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
@ -239,14 +233,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue.Enqueue(() => Shader.BindProgram()); ActionsQueue.Enqueue(() => Shader.BindProgram());
} }
public void SetTexture(int Index, GalTexture Tex) public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler)
{ {
ActionsQueue.Enqueue(() => Texture.Set(Index, Tex)); ActionsQueue.Enqueue(() =>
{
this.Texture.Set(Index, Texture);
OGLTexture.Set(Sampler);
});
} }
public void SetSampler(int Index, GalTextureSampler Sampler) public void BindTexture(int Index)
{ {
ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler)); ActionsQueue.Enqueue(() => Texture.Bind(Index));
} }
} }
} }

View file

@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gal.Shader
public GlslProgram Decompile(int[] Code, GalShaderType ShaderType) public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
{ {
ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0, ShaderType); ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
ShaderIrNode[] Nodes = Block.GetNodes(); ShaderIrNode[] Nodes = Block.GetNodes();

View file

@ -74,11 +74,9 @@ namespace Ryujinx.Graphics.Gal.Shader
for (int Ch = 0; Ch < 4; Ch++) for (int Ch = 0; Ch < 4; Ch++)
{ {
ShaderIrOperGpr Dst = (Ch >> 1) != 0 //Assign it to a temp because the destination registers
? GetOperGpr28(OpCode) //may be used as texture coord input aswell.
: GetOperGpr0 (OpCode); ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
Dst.Index += Ch & 1;
ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
@ -86,6 +84,19 @@ namespace Ryujinx.Graphics.Gal.Shader
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
} }
for (int Ch = 0; Ch < 4; Ch++)
{
ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
ShaderIrOperGpr Dst = (Ch >> 1) != 0
? GetOperGpr28(OpCode)
: GetOperGpr0 (OpCode);
Dst.Index += Ch & 1;
Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
}
} }
} }
} }

View file

@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
static class ShaderDecoder static class ShaderDecoder
{ {
public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset, GalShaderType ShaderType) public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
{ {
ShaderIrBlock Block = new ShaderIrBlock(); ShaderIrBlock Block = new ShaderIrBlock();
@ -37,8 +37,6 @@ namespace Ryujinx.Graphics.Gal.Shader
} }
} }
Block.RunOptimizationPasses(ShaderType);
return Block; return Block;
} }

View file

@ -16,11 +16,6 @@ namespace Ryujinx.Graphics.Gal.Shader
Nodes.Add(Node); Nodes.Add(Node);
} }
public void RunOptimizationPasses(GalShaderType ShaderType)
{
ShaderOptExprProp.Optimize(Nodes, ShaderType);
}
public ShaderIrNode[] GetNodes() public ShaderIrNode[] GetNodes()
{ {
return Nodes.ToArray(); return Nodes.ToArray();

View file

@ -1,366 +0,0 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.Shader
{
static class ShaderOptExprProp
{
private struct UseSite
{
public ShaderIrNode Parent { get; private set; }
public ShaderIrCond Cond { get; private set; }
public int UseIndex { get; private set; }
public int OperIndex { get; private set; }
public UseSite(
ShaderIrNode Parent,
ShaderIrCond Cond,
int UseIndex,
int OperIndex)
{
this.Parent = Parent;
this.Cond = Cond;
this.UseIndex = UseIndex;
this.OperIndex = OperIndex;
}
}
private class RegUse
{
public ShaderIrAsg Asg { get; private set; }
public int AsgIndex { get; private set; }
public int LastSiteIndex { get; private set; }
public ShaderIrCond Cond { get; private set; }
private List<UseSite> Sites;
public RegUse()
{
Sites = new List<UseSite>();
}
public void AddUseSite(UseSite Site)
{
if (LastSiteIndex < Site.UseIndex)
{
LastSiteIndex = Site.UseIndex;
}
Sites.Add(Site);
}
public bool TryPropagate()
{
//This happens when a untiliazied register is used,
//this usually indicates a decoding error, but may also
//be caused by bogus programs (?). In any case, we just
//keep the unitialized access and avoid trying to propagate
//the expression (since we can't propagate what doesn't yet exist).
if (Asg == null)
{
return false;
}
if (Cond != null)
{
//If the assignment is conditional, we can only propagate
//to the use sites that shares the same condition of the assignment.
foreach (UseSite Site in Sites)
{
if (!IsSameCondition(Cond, Site.Cond))
{
return false;
}
}
}
if (Sites.Count > 0)
{
foreach (UseSite Site in Sites)
{
if (Site.Parent is ShaderIrCond Cond)
{
switch (Site.OperIndex)
{
case 0: Cond.Pred = Asg.Src; break;
case 1: Cond.Child = Asg.Src; break;
default: throw new InvalidOperationException();
}
}
else if (Site.Parent is ShaderIrOp Op)
{
switch (Site.OperIndex)
{
case 0: Op.OperandA = Asg.Src; break;
case 1: Op.OperandB = Asg.Src; break;
case 2: Op.OperandC = Asg.Src; break;
default: throw new InvalidOperationException();
}
}
else if (Site.Parent is ShaderIrAsg SiteAsg)
{
SiteAsg.Src = Asg.Src;
}
else
{
throw new InvalidOperationException();
}
}
}
return true;
}
public void SetNewAsg(ShaderIrAsg Asg, int AsgIndex, ShaderIrCond Cond)
{
this.Asg = Asg;
this.AsgIndex = AsgIndex;
this.Cond = Cond;
LastSiteIndex = 0;
Sites.Clear();
}
}
public static void Optimize(List<ShaderIrNode> Nodes, GalShaderType ShaderType)
{
Dictionary<int, RegUse> Uses = new Dictionary<int, RegUse>();
RegUse GetUse(int Key)
{
RegUse Use;
if (!Uses.TryGetValue(Key, out Use))
{
Use = new RegUse();
Uses.Add(Key, Use);
}
return Use;
}
int GetGprKey(int GprIndex)
{
return GprIndex;
}
int GetPredKey(int PredIndex)
{
return PredIndex | 0x10000000;
}
RegUse GetGprUse(int GprIndex)
{
return GetUse(GetGprKey(GprIndex));
}
RegUse GetPredUse(int PredIndex)
{
return GetUse(GetPredKey(PredIndex));
}
void RemoveUse(RegUse Use)
{
if (!Nodes.Remove((ShaderIrNode)Use.Cond ?? Use.Asg))
{
throw new InvalidOperationException();
}
}
void FindRegUses(
List<(int, UseSite)> UseList,
ShaderIrNode Parent,
ShaderIrNode Node,
ShaderIrCond CondNode,
int UseIndex,
int OperIndex = 0)
{
if (Node is ShaderIrAsg Asg)
{
FindRegUses(UseList, Asg, Asg.Src, CondNode, UseIndex);
}
else if (Node is ShaderIrCond Cond)
{
FindRegUses(UseList, Cond, Cond.Pred, CondNode, UseIndex, 0);
FindRegUses(UseList, Cond, Cond.Child, CondNode, UseIndex, 1);
}
else if (Node is ShaderIrOp Op)
{
FindRegUses(UseList, Op, Op.OperandA, CondNode, UseIndex, 0);
FindRegUses(UseList, Op, Op.OperandB, CondNode, UseIndex, 1);
FindRegUses(UseList, Op, Op.OperandC, CondNode, UseIndex, 2);
}
else if (Node is ShaderIrOperGpr Gpr && !Gpr.IsConst)
{
UseList.Add((GetGprKey(Gpr.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
}
else if (Node is ShaderIrOperPred Pred)
{
UseList.Add((GetPredKey(Pred.Index), new UseSite(Parent, CondNode, UseIndex, OperIndex)));
}
}
void TryAddRegUseSite(ShaderIrNode Node, ShaderIrCond CondNode, int UseIndex)
{
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
FindRegUses(UseList, null, Node, CondNode, UseIndex);
foreach ((int Key, UseSite Site) in UseList)
{
GetUse(Key).AddUseSite(Site);
}
}
bool TryPropagate(RegUse Use)
{
//We can only propagate if the registers that the expression depends
//on weren't assigned after the original expression assignment
//to a register took place. We traverse the expression tree to find
//all registers being used, if any of those registers was assigned
//after the assignment to be propagated, then we can't propagate.
if (Use?.Asg == null)
{
return false;
}
List<(int, UseSite)> UseList = new List<(int, UseSite)>();
if (Use.Cond != null)
{
FindRegUses(UseList, null, Use.Cond, null, 0);
}
else
{
FindRegUses(UseList, Use.Asg, Use.Asg.Src, null, 0);
}
foreach ((int Key, UseSite Site) in UseList)
{
//TODO: Build an assignment list inside RegUse,
//and check if there is an assignment inside the
//range of Use.AsgIndex and Use.LastSiteIndex,
//and if that's the case, then we should return false.
//The current method is too conservative.
if (GetUse(Key).AsgIndex >= Use.AsgIndex)
{
return false;
}
}
return Use.TryPropagate();
}
for (int Index = 0, IterCount = 0; Index < Nodes.Count; Index++, IterCount++)
{
ShaderIrNode Node = Nodes[Index];
ShaderIrCond CondNode = null;
if (Node is ShaderIrCond)
{
CondNode = (ShaderIrCond)Node;
}
TryAddRegUseSite(Node, CondNode, IterCount);;
while (Node is ShaderIrCond Cond)
{
Node = Cond.Child;
}
if (!(Node is ShaderIrAsg Asg))
{
continue;
}
RegUse Use = null;
if (Asg.Dst is ShaderIrOperGpr Gpr && !Gpr.IsConst)
{
Use = GetGprUse(Gpr.Index);
}
else if (Asg.Dst is ShaderIrOperPred Pred)
{
Use = GetPredUse(Pred.Index);
}
bool CanRemoveAsg = CondNode == null;
CanRemoveAsg |= IsSameCondition(CondNode, Use?.Cond);
if (CanRemoveAsg && TryPropagate(Use))
{
RemoveUse(Use);
//Note: Only decrement if the removal was successful.
//RemoveUse throws when this is not the case so we should be good.
Index--;
}
//All nodes inside conditional nodes can't be propagated,
//as we don't even know if they will be executed to begin with.
Use?.SetNewAsg(Asg, IterCount, CondNode);
}
foreach (RegUse Use in Uses.Values)
{
//Gprs 0-3 are the color output on fragment shaders,
//so we can't remove the last assignments to those registers.
if (ShaderType == GalShaderType.Fragment)
{
if (Use.Asg?.Dst is ShaderIrOperGpr Gpr && Gpr.Index < 4)
{
continue;
}
}
if (TryPropagate(Use))
{
RemoveUse(Use);
}
}
}
private static bool IsSameCondition(ShaderIrCond CondA, ShaderIrCond CondB)
{
if (CondA == null || CondB == null)
{
return CondA == CondB;
}
if (CondA.Not != CondB.Not)
{
return false;
}
if (CondA.Pred is ShaderIrOperPred PredA)
{
if (!(CondB.Pred is ShaderIrOperPred PredB))
{
return false;
}
if (PredA.Index != PredB.Index)
{
return false;
}
}
else if (CondA.Pred != CondB.Pred)
{
return false;
}
return true;
}
}
}

View file

@ -9,9 +9,9 @@ namespace Ryujinx.Graphics.Gpu
internal NsGpuMemoryMgr MemoryMgr { get; private set; } internal NsGpuMemoryMgr MemoryMgr { get; private set; }
public NvGpuFifo Fifo; public NvGpuFifo Fifo { get; private set; }
internal NvGpuEngine3d Engine3d; public NvGpuEngine3d Engine3d { get; private set; }
private Thread FifoProcessing; private Thread FifoProcessing;
@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Gpu
KeepRunning = true; KeepRunning = true;
FifoProcessing = new Thread(ProcessFifo); FifoProcessing = new Thread(ProcessFifo);
FifoProcessing.Start(); FifoProcessing.Start();
} }

View file

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
class NvGpuEngine3d : INvGpuEngine public class NvGpuEngine3d : INvGpuEngine
{ {
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
@ -20,9 +20,9 @@ namespace Ryujinx.Graphics.Gpu
public int Size; public int Size;
} }
private ConstBuffer[] Cbs; private ConstBuffer[] ConstBuffers;
private bool HasDataToRender; private HashSet<long> FrameBuffers;
public NvGpuEngine3d(NsGpu Gpu) public NvGpuEngine3d(NsGpu Gpu)
{ {
@ -48,7 +48,9 @@ namespace Ryujinx.Graphics.Gpu
AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x8e4, 16, 1, CbData);
AddMethod(0x904, 1, 1, CbBind); AddMethod(0x904, 1, 1, CbBind);
Cbs = new ConstBuffer[18]; ConstBuffers = new ConstBuffer[18];
FrameBuffers = new HashSet<long>();
} }
public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry) public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
@ -76,19 +78,10 @@ namespace Ryujinx.Graphics.Gpu
UploadTextures(Memory, Tags); UploadTextures(Memory, Tags);
UploadUniforms(Memory); UploadUniforms(Memory);
UploadVertexArrays(Memory); UploadVertexArrays(Memory);
HasDataToRender = true;
} }
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry) private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
{ {
if (HasDataToRender)
{
HasDataToRender = false;
Gpu.Renderer.DrawFrameBuffer(0);
}
int Arg0 = PBEntry.Arguments[0]; int Arg0 = PBEntry.Arguments[0];
int FbIndex = (Arg0 >> 6) & 0xf; int FbIndex = (Arg0 >> 6) & 0xf;
@ -99,16 +92,23 @@ namespace Ryujinx.Graphics.Gpu
SetFrameBuffer(0); SetFrameBuffer(0);
Gpu.Renderer.ClearBuffers(Layer, Flags); //TODO: Enable this once the frame buffer problems are fixed.
//Gpu.Renderer.ClearBuffers(Layer, Flags);
} }
private void SetFrameBuffer(int FbIndex) private void SetFrameBuffer(int FbIndex)
{ {
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
Gpu.Renderer.SetFb(FbIndex, Width, Height); FrameBuffers.Add(Address);
Gpu.Renderer.BindFrameBuffer(FbIndex);
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
//Note: Using the Width/Height results seems to give incorrect results.
//Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
Gpu.Renderer.CreateFrameBuffer(Address, 1280, 720);
Gpu.Renderer.BindFrameBuffer(Address);
} }
private long[] UploadShaders(AMemory Memory) private long[] UploadShaders(AMemory Memory)
@ -167,23 +167,24 @@ namespace Ryujinx.Graphics.Gpu
private void SetAlphaBlending() private void SetAlphaBlending()
{ {
bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0; //TODO: Support independent blend properly.
bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendEnable) & 1) != 0;
Gpu.Renderer.SetBlendEnable(BlendEnableMaster); Gpu.Renderer.SetBlendEnable(Enable);
bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0; bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0;
GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb); GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb);
GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb); GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb);
GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb); GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb);
if (BlendSeparateAlpha) if (BlendSeparateAlpha)
{ {
GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha); GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha);
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha); GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha); GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
Gpu.Renderer.SetBlendSeparate( Gpu.Renderer.SetBlendSeparate(
EquationRgb, EquationRgb,
@ -205,11 +206,13 @@ namespace Ryujinx.Graphics.Gpu
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
long BasePosition = Cbs[TextureCbIndex].Position; long BasePosition = ConstBuffers[TextureCbIndex].Position;
long Size = (uint)Cbs[TextureCbIndex].Size; long Size = (uint)ConstBuffers[TextureCbIndex].Size;
int TexIndex = 0; //Note: On the emulator renderer, Texture Unit 0 is
//reserved for drawing the frame buffer.
int TexIndex = 1;
for (int Index = 0; Index < Tags.Length; Index++) for (int Index = 0; Index < Tags.Length; Index++)
{ {
@ -241,8 +244,25 @@ namespace Ryujinx.Graphics.Gpu
TicPosition += TicIndex * 0x20; TicPosition += TicIndex * 0x20;
TscPosition += TscIndex * 0x20; TscPosition += TscIndex * 0x20;
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition)); GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition);
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff;
if (FrameBuffers.Contains(TextureAddress))
{
//This texture is a frame buffer texture,
//we shouldn't read anything from memory and bind
//the frame buffer texture instead, since we're not
//really writing anything to memory.
Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler);
}
else
{
GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
Gpu.Renderer.BindTexture(TexIndex);
}
} }
private void UploadUniforms(AMemory Memory) private void UploadUniforms(AMemory Memory)
@ -262,9 +282,9 @@ namespace Ryujinx.Graphics.Gpu
continue; continue;
} }
for (int Cbuf = 0; Cbuf < Cbs.Length; Cbuf++) for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
{ {
ConstBuffer Cb = Cbs[Cbuf]; ConstBuffer Cb = ConstBuffers[Cbuf];
if (Cb.Enabled) if (Cb.Enabled)
{ {
@ -379,7 +399,7 @@ namespace Ryujinx.Graphics.Gpu
if (Mode == 0) if (Mode == 0)
{ {
//Write. //Write mode.
Memory.WriteInt32(Position, Seq); Memory.WriteInt32(Position, Seq);
} }
} }
@ -414,16 +434,16 @@ namespace Ryujinx.Graphics.Gpu
if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position)) if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
{ {
Cbs[Index].Position = Position; ConstBuffers[Index].Position = Position;
Cbs[Index].Enabled = Enabled; ConstBuffers[Index].Enabled = Enabled;
Cbs[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize); ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
} }
} }
private int ReadCb(AMemory Memory, int Cbuf, int Offset) private int ReadCb(AMemory Memory, int Cbuf, int Offset)
{ {
long Position = Cbs[Cbuf].Position; long Position = ConstBuffers[Cbuf].Position;
int Value = Memory.ReadInt32(Position + Offset); int Value = Memory.ReadInt32(Position + Offset);
@ -465,5 +485,10 @@ namespace Ryujinx.Graphics.Gpu
{ {
Registers[(int)Reg] = Value; Registers[(int)Reg] = Value;
} }
public bool IsFrameBufferPosition(long Position)
{
return FrameBuffers.Contains(Position);
}
} }
} }

View file

@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu
FrameBufferNHeight = 0x203, FrameBufferNHeight = 0x203,
FrameBufferNFormat = 0x204, FrameBufferNFormat = 0x204,
VertexAttribNFormat = 0x458, VertexAttribNFormat = 0x458,
IBlendEnable = 0x4b9,
BlendSeparateAlpha = 0x4cf, BlendSeparateAlpha = 0x4cf,
BlendEquationRgb = 0x4d0, BlendEquationRgb = 0x4d0,
BlendFuncSrcRgb = 0x4d1, BlendFuncSrcRgb = 0x4d1,
@ -31,6 +32,13 @@ namespace Ryujinx.Graphics.Gpu
VertexArrayNControl = 0x700, VertexArrayNControl = 0x700,
VertexArrayNAddress = 0x701, VertexArrayNAddress = 0x701,
VertexArrayNDivisor = 0x703, VertexArrayNDivisor = 0x703,
IBlendNSeparateAlpha = 0x780,
IBlendNEquationRgb = 0x781,
IBlendNFuncSrcRgb = 0x782,
IBlendNFuncDstRgb = 0x783,
IBlendNEquationAlpha = 0x784,
IBlendNFuncSrcAlpha = 0x785,
IBlendNFuncDstAlpha = 0x786,
VertexArrayNEndAddr = 0x7c0, VertexArrayNEndAddr = 0x7c0,
ShaderNControl = 0x800, ShaderNControl = 0x800,
ShaderNOffset = 0x801, ShaderNOffset = 0x801,

View file

@ -2,7 +2,7 @@ using Ryujinx.Graphics.Gal;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
struct Texture public struct Texture
{ {
public long Position { get; private set; } public long Position { get; private set; }
@ -16,6 +16,24 @@ namespace Ryujinx.Graphics.Gpu
public GalTextureFormat Format { get; private set; } public GalTextureFormat Format { get; private set; }
public Texture(
long Position,
int Width,
int Height)
{
this.Position = Position;
this.Width = Width;
this.Height = Height;
Pitch = 0;
BlockHeight = 16;
Swizzle = TextureSwizzle.BlockLinear;
Format = GalTextureFormat.A8B8G8R8;
}
public Texture( public Texture(
long Position, long Position,
int Width, int Width,

View file

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
static class TextureReader public static class TextureReader
{ {
public static byte[] Read(AMemory Memory, Texture Texture) public static byte[] Read(AMemory Memory, Texture Texture)
{ {

View file

@ -1,6 +1,6 @@
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
enum TextureSwizzle public enum TextureSwizzle
{ {
_1dBuffer = 0, _1dBuffer = 0,
PitchColorKey = 1, PitchColorKey = 1,

View file

@ -38,8 +38,6 @@ namespace Ryujinx
protected override void OnLoad(EventArgs e) protected override void OnLoad(EventArgs e)
{ {
VSync = VSyncMode.On; VSync = VSyncMode.On;
Renderer.InitializeFrameBuffer();
} }
protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnUpdateFrame(FrameEventArgs e)