Small OpenGL Renderer refactoring (#177)

* Call OpenGL functions directly, remove the pfifo thread, some refactoring

* Fix PerformanceStatistics calculating the wrong host fps, remove wait event on PFIFO as this wasn't exactly was causing the freezes (may replace with an exception later)

* Organized the Gpu folder a bit more, renamed a few things, address PR feedback

* Make PerformanceStatistics thread safe

* Remove unused constant

* Use unlimited update rate for better pref
This commit is contained in:
gdkchan 2018-06-23 21:39:25 -03:00 committed by GitHub
parent 69697957e6
commit e7559f128f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 518 additions and 633 deletions

View file

@ -1,6 +1,5 @@
using ChocolArm64.Translation; using ChocolArm64.Translation;
using System; using System;
using System.Numerics;
namespace ChocolArm64.Instruction namespace ChocolArm64.Instruction
{ {

View file

@ -1,7 +0,0 @@
namespace Ryujinx.Graphics.Gal
{
public static class GalConsts
{
public const string FlipUniformName = "flip";
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gal
{
public interface IGalBlend
{
void Enable();
void Disable();
void Set(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst);
void SetSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha);
}
}

View file

@ -0,0 +1,27 @@
using System;
namespace Ryujinx.Graphics.Gal
{
public interface IGalFrameBuffer
{
void Create(long Key, int Width, int Height);
void Bind(long Key);
void BindTexture(long Key, int Index);
void Set(long Key);
void Set(byte[] Data, int Width, int Height);
void SetTransform(float SX, float SY, float Rotate, float TX, float TY);
void SetWindowSize(int Width, int Height);
void SetViewport(int X, int Y, int Width, int Height);
void Render();
void GetBufferData(long Key, Action<byte[]> Callback);
}
}

View file

@ -0,0 +1,23 @@
namespace Ryujinx.Graphics.Gal
{
public interface IGalRasterizer
{
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
bool IsVboCached(long Key, long DataSize);
bool IsIboCached(long Key, long DataSize);
void CreateVbo(long Key, byte[] Buffer);
void CreateIbo(long Key, byte[] Buffer);
void SetVertexArray(int VbIndex, int Stride, long VboKey, GalVertexAttrib[] Attribs);
void SetIndexArray(long Key, int Size, GalIndexFormat Format);
void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
void DrawElements(long IboKey, int First, GalPrimitiveType PrimType);
}
}

View file

@ -1,90 +1,21 @@
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal namespace Ryujinx.Graphics.Gal
{ {
public unsafe interface IGalRenderer public interface IGalRenderer
{ {
void QueueAction(Action ActionMthd); void QueueAction(Action ActionMthd);
void RunActions(); void RunActions();
void Render(); IGalBlend Blend { get; }
void SetWindowSize(int Width, int Height); IGalFrameBuffer FrameBuffer { get; }
//Blend IGalRasterizer Rasterizer { get; }
void SetBlendEnable(bool Enable);
void SetBlend( IGalShader Shader { get; }
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst);
void SetBlendSeparate( IGalTexture Texture { get; }
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha);
//Frame Buffer
void CreateFrameBuffer(long Tag, int Width, int Height);
void BindFrameBuffer(long Tag);
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);
void SetViewport(int X, int Y, int Width, int Height);
void GetFrameBufferData(long Tag, Action<byte[]> Callback);
//Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
bool IsVboCached(long Tag, long DataSize);
bool IsIboCached(long Tag, long DataSize);
void CreateVbo(long Tag, byte[] Buffer);
void CreateIbo(long Tag, byte[] Buffer);
void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs);
void SetIndexArray(long Tag, int Size, GalIndexFormat Format);
void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType);
void DrawElements(long IboTag, int First, GalPrimitiveType PrimType);
//Shader
void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
void SetUniform1(string UniformName, int Value);
void SetUniform2F(string UniformName, float X, float Y);
void BindShader(long Tag);
void BindProgram();
//Texture
void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler);
bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture);
void BindTexture(long Tag, int Index);
} }
} }

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal
{
public interface IGalShader
{
void Create(IGalMemory Memory, long Key, GalShaderType Type);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
void SetConstBuffer(long Key, int Cbuf, byte[] Data);
void EnsureTextureBinding(string UniformName, int Value);
void SetFlip(float X, float Y);
void Bind(long Key);
void BindProgram();
}
}

View file

@ -0,0 +1,13 @@
namespace Ryujinx.Graphics.Gal
{
public interface IGalTexture
{
void Create(long Key, byte[] Data, GalTexture Texture);
bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture);
void Bind(long Key, int Index);
void SetSampler(GalTextureSampler Sampler);
}
}

View file

@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLBlend public class OGLBlend : IGalBlend
{ {
public void Enable() public void Enable()
{ {

View file

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLFrameBuffer public class OGLFrameBuffer : IGalFrameBuffer
{ {
private struct Rect private struct Rect
{ {
@ -16,9 +16,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public Rect(int X, int Y, int Width, int Height) public Rect(int X, int Y, int Width, int Height)
{ {
this.X = X; this.X = X;
this.Y = Y; this.Y = Y;
this.Width = Width; this.Width = Width;
this.Height = Height; this.Height = Height;
} }
} }
@ -76,14 +76,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Shader = new ShaderProgram(); Shader = new ShaderProgram();
} }
public void Create(long Tag, int Width, int Height) public void Create(long Key, int Width, int Height)
{ {
//TODO: We should either use the original frame buffer size, //TODO: We should either use the original frame buffer size,
//or just remove the Width/Height arguments. //or just remove the Width/Height arguments.
Width = Window.Width; Width = Window.Width;
Height = Window.Height; Height = Window.Height;
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{ {
if (Fb.Width != Width || if (Fb.Width != Width ||
Fb.Height != Height) Fb.Height != Height)
@ -127,12 +127,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Viewport(0, 0, Width, Height); GL.Viewport(0, 0, Width, Height);
Fbs.Add(Tag, Fb); Fbs.Add(Key, Fb);
} }
public void Bind(long Tag) public void Bind(long Key)
{ {
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{ {
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
@ -140,9 +140,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public void BindTexture(long Tag, int Index) public void BindTexture(long Key, int Index)
{ {
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
@ -150,9 +150,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public void Set(long Tag) public void Set(long Key)
{ {
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{ {
CurrTexHandle = Fb.TexHandle; CurrTexHandle = Fb.TexHandle;
} }
@ -185,10 +185,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL
CurrTexHandle = RawFbTexHandle; CurrTexHandle = RawFbTexHandle;
} }
public void SetTransform(Matrix2 Transform, Vector2 Offs) public void SetTransform(float SX, float SY, float Rotate, float TX, float TY)
{ {
EnsureInitialized(); EnsureInitialized();
Matrix2 Transform;
Transform = Matrix2.CreateScale(SX, SY);
Transform *= Matrix2.CreateRotation(Rotate);
Vector2 Offs = new Vector2(TX, TY);
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram); int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
GL.UseProgram(Shader.Handle); GL.UseProgram(Shader.Handle);
@ -270,9 +277,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public void GetBufferData(long Tag, Action<byte[]> Callback) public void GetBufferData(long Key, Action<byte[]> Callback)
{ {
if (Fbs.TryGetValue(Tag, out FrameBuffer Fb)) if (Fbs.TryGetValue(Key, out FrameBuffer Fb))
{ {
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, Fb.Handle);

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLRasterizer public class OGLRasterizer : IGalRasterizer
{ {
private static Dictionary<GalVertexAttribSize, int> AttribElements = private static Dictionary<GalVertexAttribSize, int> AttribElements =
new Dictionary<GalVertexAttribSize, int>() new Dictionary<GalVertexAttribSize, int>()
@ -74,8 +74,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
ClearBufferMask Mask = 0; ClearBufferMask Mask = 0;
//OpenGL doesn't support clearing just a single color channel, //TODO: Use glColorMask to clear just the specified channels.
//so we can't just clear all channels...
if (Flags.HasFlag(GalClearBufferFlags.ColorRed) && if (Flags.HasFlag(GalClearBufferFlags.ColorRed) &&
Flags.HasFlag(GalClearBufferFlags.ColorGreen) && Flags.HasFlag(GalClearBufferFlags.ColorGreen) &&
Flags.HasFlag(GalClearBufferFlags.ColorBlue) && Flags.HasFlag(GalClearBufferFlags.ColorBlue) &&
@ -97,45 +96,43 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Clear(Mask); GL.Clear(Mask);
} }
public bool IsVboCached(long Tag, long DataSize) public bool IsVboCached(long Key, long DataSize)
{ {
return VboCache.TryGetSize(Tag, out long Size) && Size == DataSize; return VboCache.TryGetSize(Key, out long Size) && Size == DataSize;
} }
public bool IsIboCached(long Tag, long DataSize) public bool IsIboCached(long Key, long DataSize)
{ {
return IboCache.TryGetSize(Tag, out long Size) && Size == DataSize; return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
} }
public void CreateVbo(long Tag, byte[] Buffer) public void CreateVbo(long Key, byte[] Buffer)
{ {
int Handle = GL.GenBuffer(); int Handle = GL.GenBuffer();
VboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length); VboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length);
IntPtr Length = new IntPtr(Buffer.Length); IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle); GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
} }
public void CreateIbo(long Tag, byte[] Buffer) public void CreateIbo(long Key, byte[] Buffer)
{ {
int Handle = GL.GenBuffer(); int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Tag, Handle, (uint)Buffer.Length); IboCache.AddOrUpdate(Key, Handle, (uint)Buffer.Length);
IntPtr Length = new IntPtr(Buffer.Length); IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle); GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
} }
public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs) public void SetVertexArray(int VbIndex, int Stride, long VboKey, GalVertexAttrib[] Attribs)
{ {
if (!VboCache.TryGetValue(VboTag, out int VboHandle)) if (!VboCache.TryGetValue(VboKey, out int VboHandle))
{ {
return; return;
} }
@ -178,11 +175,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset); GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset);
} }
GL.BindVertexArray(0);
} }
public void SetIndexArray(long Tag, int Size, GalIndexFormat Format) public void SetIndexArray(long Key, int Size, GalIndexFormat Format)
{ {
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format); IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
@ -201,9 +196,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount); GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount);
} }
public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType) public void DrawElements(long IboKey, int First, GalPrimitiveType PrimType)
{ {
if (!IboCache.TryGetValue(IboTag, out int IboHandle)) if (!IboCache.TryGetValue(IboKey, out int IboHandle))
{ {
return; return;
} }

View file

@ -0,0 +1,50 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLRenderer : IGalRenderer
{
public IGalBlend Blend { get; private set; }
public IGalFrameBuffer FrameBuffer { get; private set; }
public IGalRasterizer Rasterizer { get; private set; }
public IGalShader Shader { get; private set; }
public IGalTexture Texture { get; private set; }
private ConcurrentQueue<Action> ActionsQueue;
public OGLRenderer()
{
Blend = new OGLBlend();
FrameBuffer = new OGLFrameBuffer();
Rasterizer = new OGLRasterizer();
Shader = new OGLShader();
Texture = new OGLTexture();
ActionsQueue = new ConcurrentQueue<Action>();
}
public void QueueAction(Action ActionMthd)
{
ActionsQueue.Enqueue(ActionMthd);
}
public void RunActions()
{
int Count = ActionsQueue.Count;
while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
{
RenderAction();
}
}
}
}

View file

@ -7,7 +7,7 @@ using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLShader public class OGLShader : IGalShader
{ {
private class ShaderStage : IDisposable private class ShaderStage : IDisposable
{ {
@ -84,9 +84,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Programs = new Dictionary<ShaderProgram, int>(); Programs = new Dictionary<ShaderProgram, int>();
} }
public void Create(IGalMemory Memory, long Tag, GalShaderType Type) public void Create(IGalMemory Memory, long Key, GalShaderType Type)
{ {
Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Memory, Tag, Type)); Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, Type));
} }
private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type) private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type)
@ -107,9 +107,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Decompiler.Decompile(Memory, Position + 0x50, Type); return Decompiler.Decompile(Memory, Position + 0x50, Type);
} }
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag) public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{ {
if (Stages.TryGetValue(Tag, out ShaderStage Stage)) if (Stages.TryGetValue(Key, out ShaderStage Stage))
{ {
return Stage.TextureUsage; return Stage.TextureUsage;
} }
@ -117,11 +117,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Enumerable.Empty<ShaderDeclInfo>(); return Enumerable.Empty<ShaderDeclInfo>();
} }
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data) public void SetConstBuffer(long Key, int Cbuf, byte[] Data)
{ {
BindProgram(); BindProgram();
if (Stages.TryGetValue(Tag, out ShaderStage Stage)) if (Stages.TryGetValue(Key, out ShaderStage Stage))
{ {
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
{ {
@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public void SetUniform1(string UniformName, int Value) public void EnsureTextureBinding(string UniformName, int Value)
{ {
BindProgram(); BindProgram();
@ -153,18 +153,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Uniform1(Location, Value); GL.Uniform1(Location, Value);
} }
public void SetUniform2F(string UniformName, float X, float Y) public void SetFlip(float X, float Y)
{ {
BindProgram(); BindProgram();
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName); int Location = GL.GetUniformLocation(CurrentProgramHandle, GlslDecl.FlipUniformName);
GL.Uniform2(Location, X, Y); GL.Uniform2(Location, X, Y);
} }
public void Bind(long Tag) public void Bind(long Key)
{ {
if (Stages.TryGetValue(Tag, out ShaderStage Stage)) if (Stages.TryGetValue(Key, out ShaderStage Stage))
{ {
Bind(Stage); Bind(Stage);
} }

View file

@ -4,7 +4,7 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
class OGLTexture public class OGLTexture : IGalTexture
{ {
private class TCE private class TCE
{ {
@ -31,11 +31,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.DeleteTexture(CachedTexture.Handle); GL.DeleteTexture(CachedTexture.Handle);
} }
public void Create(long Tag, byte[] Data, GalTexture Texture) public void Create(long Key, byte[] Data, GalTexture Texture)
{ {
int Handle = GL.GenTexture(); int Handle = GL.GenTexture();
TextureCache.AddOrUpdate(Tag, new TCE(Handle, Texture), (uint)Data.Length); TextureCache.AddOrUpdate(Key, new TCE(Handle, Texture), (uint)Data.Length);
GL.BindTexture(TextureTarget.Texture2D, Handle); GL.BindTexture(TextureTarget.Texture2D, Handle);
@ -146,11 +146,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentException(nameof(Format)); throw new ArgumentException(nameof(Format));
} }
public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture) public bool TryGetCachedTexture(long Key, long DataSize, out GalTexture Texture)
{ {
if (TextureCache.TryGetSize(Tag, out long Size) && Size == DataSize) if (TextureCache.TryGetSize(Key, out long Size) && Size == DataSize)
{ {
if (TextureCache.TryGetValue(Tag, out TCE CachedTexture)) if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
{ {
Texture = CachedTexture.Texture; Texture = CachedTexture.Texture;
@ -163,9 +163,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return false; return false;
} }
public void Bind(long Tag, int Index) public void Bind(long Key, int Index)
{ {
if (TextureCache.TryGetValue(Tag, out TCE CachedTexture)) if (TextureCache.TryGetValue(Key, out TCE CachedTexture))
{ {
GL.ActiveTexture(TextureUnit.Texture0 + Index); GL.ActiveTexture(TextureUnit.Texture0 + Index);
@ -173,7 +173,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
} }
} }
public static void Set(GalTextureSampler Sampler) public void SetSampler(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

@ -1,284 +0,0 @@
using OpenTK;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OpenGLRenderer : IGalRenderer
{
private OGLBlend Blend;
private OGLFrameBuffer FrameBuffer;
private OGLRasterizer Rasterizer;
private OGLShader Shader;
private OGLTexture Texture;
private ConcurrentQueue<Action> ActionsQueue;
public OpenGLRenderer()
{
Blend = new OGLBlend();
FrameBuffer = new OGLFrameBuffer();
Rasterizer = new OGLRasterizer();
Shader = new OGLShader();
Texture = new OGLTexture();
ActionsQueue = new ConcurrentQueue<Action>();
}
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()
{
FrameBuffer.Render();
}
public void SetWindowSize(int Width, int Height)
{
FrameBuffer.SetWindowSize(Width, Height);
}
public void SetBlendEnable(bool Enable)
{
if (Enable)
{
ActionsQueue.Enqueue(() => Blend.Enable());
}
else
{
ActionsQueue.Enqueue(() => Blend.Disable());
}
}
public void SetBlend(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst)
{
ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst));
}
public void SetBlendSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha)
{
ActionsQueue.Enqueue(() =>
{
Blend.SetSeparate(
EquationRgb,
EquationAlpha,
FuncSrcRgb,
FuncDstRgb,
FuncSrcAlpha,
FuncDstAlpha);
});
}
public void CreateFrameBuffer(long Tag, int Width, int Height)
{
ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
}
public void BindFrameBuffer(long Tag)
{
ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
}
public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
{
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 SetViewport(int X, int Y, int Width, int Height)
{
ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height));
}
public void GetFrameBufferData(long Tag, Action<byte[]> Callback)
{
ActionsQueue.Enqueue(() => FrameBuffer.GetBufferData(Tag, Callback));
}
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
{
ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
}
public bool IsVboCached(long Tag, long DataSize)
{
return Rasterizer.IsVboCached(Tag, DataSize);
}
public bool IsIboCached(long Tag, long DataSize)
{
return Rasterizer.IsIboCached(Tag, DataSize);
}
public void CreateVbo(long Tag, byte[] Buffer)
{
ActionsQueue.Enqueue(() => Rasterizer.CreateVbo(Tag, Buffer));
}
public void CreateIbo(long Tag, byte[] Buffer)
{
ActionsQueue.Enqueue(() => Rasterizer.CreateIbo(Tag, Buffer));
}
public void SetVertexArray(int VbIndex, int Stride, long VboTag, GalVertexAttrib[] Attribs)
{
if ((uint)VbIndex > 31)
{
throw new ArgumentOutOfRangeException(nameof(VbIndex));
}
if (Attribs == null)
{
throw new ArgumentNullException(nameof(Attribs));
}
ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride, VboTag, Attribs));
}
public void SetIndexArray(long Tag, int Size, GalIndexFormat Format)
{
ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Tag, Size, Format));
}
public void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType)
{
ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(First, PrimCount, PrimType));
}
public void DrawElements(long IboTag, int First, GalPrimitiveType PrimType)
{
ActionsQueue.Enqueue(() => Rasterizer.DrawElements(IboTag, First, PrimType));
}
public void CreateShader(IGalMemory Memory, long Tag, GalShaderType Type)
{
if (Memory == null)
{
throw new ArgumentNullException(nameof(Memory));
}
Shader.Create(Memory, Tag, Type);
}
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
{
if (Data == null)
{
throw new ArgumentNullException(nameof(Data));
}
ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Tag, Cbuf, Data));
}
public void SetUniform1(string UniformName, int Value)
{
if (UniformName == null)
{
throw new ArgumentNullException(nameof(UniformName));
}
ActionsQueue.Enqueue(() => Shader.SetUniform1(UniformName, Value));
}
public void SetUniform2F(string UniformName, float X, float Y)
{
if (UniformName == null)
{
throw new ArgumentNullException(nameof(UniformName));
}
ActionsQueue.Enqueue(() => Shader.SetUniform2F(UniformName, X, Y));
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
{
return Shader.GetTextureUsage(Tag);
}
public void BindShader(long Tag)
{
ActionsQueue.Enqueue(() => Shader.Bind(Tag));
}
public void BindProgram()
{
ActionsQueue.Enqueue(() => Shader.BindProgram());
}
public void SetTextureAndSampler(long Tag, byte[] Data, GalTexture Texture, GalTextureSampler Sampler)
{
ActionsQueue.Enqueue(() =>
{
this.Texture.Create(Tag, Data, Texture);
OGLTexture.Set(Sampler);
});
}
public bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture)
{
return this.Texture.TryGetCachedTexture(Tag, DataSize, out Texture);
}
public void BindTexture(long Tag, int Index)
{
ActionsQueue.Enqueue(() => Texture.Bind(Tag, Index));
}
}
}

View file

@ -26,6 +26,8 @@ namespace Ryujinx.Graphics.Gal.Shader
public const string FragmentOutputName = "FragColor"; public const string FragmentOutputName = "FragColor";
public const string FlipUniformName = "flip";
private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
private string StagePrefix; private string StagePrefix;

View file

@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.Gal.Shader
{ {
if (Decl.ShaderType == GalShaderType.Vertex) if (Decl.ShaderType == GalShaderType.Vertex)
{ {
SB.AppendLine("uniform vec2 " + GalConsts.FlipUniformName + ";"); SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";");
} }
foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))

View file

@ -1,4 +1,6 @@
namespace Ryujinx.HLE.Gpu using Ryujinx.HLE.Gpu.Memory;
namespace Ryujinx.HLE.Gpu.Engines
{ {
interface INvGpuEngine interface INvGpuEngine
{ {

View file

@ -1,10 +1,16 @@
using Ryujinx.HLE.Gpu.Exceptions;
using Ryujinx.HLE.Gpu.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
class MacroInterpreter class MacroInterpreter
{ {
private const int MaxCallCountPerRun = 500;
private int CallCount;
private enum AssignmentOperation private enum AssignmentOperation
{ {
IgnoreAndFetch = 0, IgnoreAndFetch = 0,
@ -96,6 +102,8 @@ namespace Ryujinx.HLE.Gpu
MethIncr = 0; MethIncr = 0;
Carry = false; Carry = false;
CallCount = 0;
} }
private bool Step(NvGpuVmm Vmm, int[] Mme) private bool Step(NvGpuVmm Vmm, int[] Mme)
@ -407,6 +415,15 @@ namespace Ryujinx.HLE.Gpu
private void Send(NvGpuVmm Vmm, int Value) private void Send(NvGpuVmm Vmm, int Value)
{ {
//This is an artificial limit that prevents excessive calls
//to VertexEndGl since that triggers rendering, and in the
//case that something is bugged and causes an absurd amount of
//draw calls, this prevents the system from freezing (and throws instead).
if (MethAddr == 0x585 && ++CallCount > MaxCallCountPerRun)
{
GpuExceptionHelper.ThrowCallCoundExceeded();
}
NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value);
Engine.CallMethod(Vmm, PBEntry); Engine.CallMethod(Vmm, PBEntry);

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
enum NvGpuEngine enum NvGpuEngine
{ {

View file

@ -1,7 +1,9 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Gpu.Texture;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
class NvGpuEngine2d : INvGpuEngine class NvGpuEngine2d : INvGpuEngine
{ {
@ -75,19 +77,19 @@ namespace Ryujinx.HLE.Gpu
int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
long Tag = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress)); long Key = Vmm.GetPhysicalAddress(MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress));
long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress);
long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress);
bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag); bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Key);
if (IsFbTexture && DstLinear) if (IsFbTexture && DstLinear)
{ {
DstSwizzle = TextureSwizzle.BlockLinear; DstSwizzle = TextureSwizzle.BlockLinear;
} }
Texture DstTexture = new Texture( TextureInfo DstTexture = new TextureInfo(
DstAddress, DstAddress,
DstWidth, DstWidth,
DstHeight, DstHeight,
@ -103,7 +105,7 @@ namespace Ryujinx.HLE.Gpu
SrcWidth = 1280; SrcWidth = 1280;
SrcHeight = 720; SrcHeight = 720;
Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) => Gpu.Renderer.FrameBuffer.GetBufferData(Key, (byte[] Buffer) =>
{ {
CopyTexture( CopyTexture(
Vmm, Vmm,
@ -129,11 +131,11 @@ namespace Ryujinx.HLE.Gpu
} }
private void CopyTexture( private void CopyTexture(
NvGpuVmm Vmm, NvGpuVmm Vmm,
Texture Texture, TextureInfo Texture,
byte[] Buffer, byte[] Buffer,
int Width, int Width,
int Height) int Height)
{ {
TextureWriter.Write(Vmm, Texture, Buffer, Width, Height); TextureWriter.Write(Vmm, Texture, Buffer, Width, Height);
} }

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
enum NvGpuEngine2dReg enum NvGpuEngine2dReg
{ {

View file

@ -1,8 +1,10 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Gpu.Texture;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
class NvGpuEngine3d : INvGpuEngine class NvGpuEngine3d : INvGpuEngine
{ {
@ -73,13 +75,13 @@ namespace Ryujinx.HLE.Gpu
{ {
SetFrameBuffer(Vmm, 0); SetFrameBuffer(Vmm, 0);
long[] Tags = UploadShaders(Vmm); long[] Keys = UploadShaders(Vmm);
Gpu.Renderer.BindProgram(); Gpu.Renderer.Shader.BindProgram();
SetAlphaBlending(); SetAlphaBlending();
UploadTextures(Vmm, Tags); UploadTextures(Vmm, Keys);
UploadUniforms(Vmm); UploadUniforms(Vmm);
UploadVertexArrays(Vmm); UploadVertexArrays(Vmm);
} }
@ -113,13 +115,13 @@ namespace Ryujinx.HLE.Gpu
//Note: Using the Width/Height results seems to give incorrect results. //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. //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
Gpu.Renderer.CreateFrameBuffer(PA, 1280, 720); Gpu.Renderer.FrameBuffer.Create(PA, 1280, 720);
Gpu.Renderer.BindFrameBuffer(PA); Gpu.Renderer.FrameBuffer.Bind(PA);
} }
private long[] UploadShaders(NvGpuVmm Vmm) private long[] UploadShaders(NvGpuVmm Vmm)
{ {
long[] Tags = new long[5]; long[] Keys = new long[5];
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
@ -136,14 +138,14 @@ namespace Ryujinx.HLE.Gpu
continue; continue;
} }
long Tag = BasePosition + (uint)Offset; long Key = BasePosition + (uint)Offset;
GalShaderType ShaderType = GetTypeFromProgram(Index); GalShaderType ShaderType = GetTypeFromProgram(Index);
Tags[(int)ShaderType] = Tag; Keys[(int)ShaderType] = Key;
Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType); Gpu.Renderer.Shader.Create(Vmm, Key, ShaderType);
Gpu.Renderer.BindShader(Tag); Gpu.Renderer.Shader.Bind(Key);
} }
int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX); int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
@ -155,9 +157,9 @@ namespace Ryujinx.HLE.Gpu
float SignX = MathF.Sign(SX); float SignX = MathF.Sign(SX);
float SignY = MathF.Sign(SY); float SignY = MathF.Sign(SY);
Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY); Gpu.Renderer.Shader.SetFlip(SignX, SignY);
return Tags; return Keys;
} }
private static GalShaderType GetTypeFromProgram(int Program) private static GalShaderType GetTypeFromProgram(int Program)
@ -180,7 +182,14 @@ namespace Ryujinx.HLE.Gpu
//TODO: Support independent blend properly. //TODO: Support independent blend properly.
bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0;
Gpu.Renderer.SetBlendEnable(Enable); if (Enable)
{
Gpu.Renderer.Blend.Enable();
}
else
{
Gpu.Renderer.Blend.Disable();
}
if (!Enable) if (!Enable)
{ {
@ -203,7 +212,7 @@ namespace Ryujinx.HLE.Gpu
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
Gpu.Renderer.SetBlendSeparate( Gpu.Renderer.Blend.SetSeparate(
EquationRgb, EquationRgb,
EquationAlpha, EquationAlpha,
FuncSrcRgb, FuncSrcRgb,
@ -213,11 +222,11 @@ namespace Ryujinx.HLE.Gpu
} }
else else
{ {
Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb); Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb);
} }
} }
private void UploadTextures(NvGpuVmm Vmm, long[] Tags) private void UploadTextures(NvGpuVmm Vmm, long[] Keys)
{ {
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
@ -227,15 +236,15 @@ namespace Ryujinx.HLE.Gpu
//reserved for drawing the frame buffer. //reserved for drawing the frame buffer.
int TexIndex = 1; int TexIndex = 1;
for (int Index = 0; Index < Tags.Length; Index++) for (int Index = 0; Index < Keys.Length; Index++)
{ {
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index])) foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Keys[Index]))
{ {
long Position = ConstBuffers[Index][TextureCbIndex].Position; long Position = ConstBuffers[Index][TextureCbIndex].Position;
UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index); UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index);
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex); Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex);
TexIndex++; TexIndex++;
} }
@ -270,7 +279,7 @@ namespace Ryujinx.HLE.Gpu
long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
long Tag = TextureAddress; long Key = TextureAddress;
TextureAddress = Vmm.GetPhysicalAddress(TextureAddress); TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
@ -280,7 +289,7 @@ namespace Ryujinx.HLE.Gpu
//we shouldn't read anything from memory and bind //we shouldn't read anything from memory and bind
//the frame buffer texture instead, since we're not //the frame buffer texture instead, since we're not
//really writing anything to memory. //really writing anything to memory.
Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler); Gpu.Renderer.FrameBuffer.BindTexture(TextureAddress, TexIndex);
} }
else else
{ {
@ -288,22 +297,29 @@ namespace Ryujinx.HLE.Gpu
long Size = (uint)TextureHelper.GetTextureSize(NewTexture); long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture)) bool HasCachedTexture = false;
{
if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture))
{
Gpu.Renderer.BindTexture(Tag, TexIndex);
return; if (Gpu.Renderer.Texture.TryGetCachedTexture(Key, Size, out GalTexture Texture))
{
if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Key, Size, NvGpuBufferType.Texture))
{
Gpu.Renderer.Texture.Bind(Key, TexIndex);
HasCachedTexture = true;
} }
} }
byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition); if (!HasCachedTexture)
{
byte[] Data = TextureFactory.GetTextureData(Vmm, TicPosition);
Gpu.Renderer.SetTextureAndSampler(Tag, Data, NewTexture, Sampler); Gpu.Renderer.Texture.Create(Key, Data, NewTexture);
}
Gpu.Renderer.BindTexture(Tag, TexIndex); Gpu.Renderer.Texture.Bind(Key, TexIndex);
} }
Gpu.Renderer.Texture.SetSampler(Sampler);
} }
private void UploadUniforms(NvGpuVmm Vmm) private void UploadUniforms(NvGpuVmm Vmm)
@ -331,7 +347,7 @@ namespace Ryujinx.HLE.Gpu
{ {
byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size); byte[] Data = Vmm.ReadBytes(Cb.Position, (uint)Cb.Size);
Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data); Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
} }
} }
} }
@ -341,33 +357,33 @@ namespace Ryujinx.HLE.Gpu
{ {
long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize; GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt;
IndexSize = 1 << IndexSize; int IndexEntrySize = 1 << IndexEntryFmt;
if (IndexSize > 4) if (IndexEntrySize > 4)
{ {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
if (IndexCount != 0) if (IndexCount != 0)
{ {
int IbSize = IndexCount * IndexSize; int IbSize = IndexCount * IndexEntrySize;
bool IboCached = Gpu.Renderer.IsIboCached(IndexPosition, (uint)IbSize); bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IndexPosition, (uint)IbSize);
if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index)) if (!IboCached || Vmm.IsRegionModified(IndexPosition, (uint)IbSize, NvGpuBufferType.Index))
{ {
byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize); byte[] Data = Vmm.ReadBytes(IndexPosition, (uint)IbSize);
Gpu.Renderer.CreateIbo(IndexPosition, Data); Gpu.Renderer.Rasterizer.CreateIbo(IndexPosition, Data);
} }
Gpu.Renderer.SetIndexArray(IndexPosition, IbSize, IndexFormat); Gpu.Renderer.Rasterizer.SetIndexArray(IndexPosition, IbSize, IndexFormat);
} }
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32]; List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
@ -429,27 +445,27 @@ namespace Ryujinx.HLE.Gpu
VbSize = VertexCount * Stride; VbSize = VertexCount * Stride;
} }
bool VboCached = Gpu.Renderer.IsVboCached(VertexPosition, VbSize); bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VertexPosition, VbSize);
if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex)) if (!VboCached || Vmm.IsRegionModified(VertexPosition, VbSize, NvGpuBufferType.Vertex))
{ {
byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize); byte[] Data = Vmm.ReadBytes(VertexPosition, VbSize);
Gpu.Renderer.CreateVbo(VertexPosition, Data); Gpu.Renderer.Rasterizer.CreateVbo(VertexPosition, Data);
} }
Gpu.Renderer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray()); Gpu.Renderer.Rasterizer.SetVertexArray(Index, Stride, VertexPosition, Attribs[Index].ToArray());
} }
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
if (IndexCount != 0) if (IndexCount != 0)
{ {
Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType); Gpu.Renderer.Rasterizer.DrawElements(IndexPosition, IndexFirst, PrimType);
} }
else else
{ {
Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType); Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType);
} }
} }

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
enum NvGpuEngine3dReg enum NvGpuEngine3dReg
{ {

View file

@ -1,6 +1,8 @@
using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Gpu.Texture;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
class NvGpuEngineDma : INvGpuEngine class NvGpuEngineDma : INvGpuEngine
{ {

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
enum NvGpuEngineDmaReg enum NvGpuEngineDmaReg
{ {

View file

@ -1,6 +1,7 @@
using Ryujinx.HLE.Gpu.Memory;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
class NvGpuFifo class NvGpuFifo
{ {

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Engines
{ {
enum NvGpuFifoMeth enum NvGpuFifoMeth
{ {

View file

@ -1,4 +1,6 @@
namespace Ryujinx.HLE.Gpu using Ryujinx.HLE.Gpu.Memory;
namespace Ryujinx.HLE.Gpu.Engines
{ {
delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry);
} }

View file

@ -0,0 +1,11 @@
using System;
namespace Ryujinx.HLE.Gpu.Exceptions
{
class GpuException : Exception
{
public GpuException() : base() { }
public GpuException(string ExMsg) : base(ExMsg) { }
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.HLE.Gpu.Exceptions
{
static class GpuExceptionHelper
{
private const string CallCountExceeded = "Method call count exceeded the limit allowed per run!";
public static void ThrowCallCoundExceeded()
{
throw new GpuException(CallCountExceeded);
}
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Memory
{ {
enum NvGpuBufferType enum NvGpuBufferType
{ {

View file

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Memory
{ {
struct NvGpuPBEntry struct NvGpuPBEntry
{ {

View file

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Memory
{ {
static class NvGpuPushBuffer static class NvGpuPushBuffer
{ {

View file

@ -2,7 +2,7 @@ using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Memory
{ {
class NvGpuVmm : IAMemory, IGalMemory class NvGpuVmm : IAMemory, IGalMemory
{ {

View file

@ -2,7 +2,7 @@ using ChocolArm64.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Memory
{ {
class NvGpuVmmCache class NvGpuVmmCache
{ {

View file

@ -1,5 +1,5 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System.Threading; using Ryujinx.HLE.Gpu.Engines;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu
{ {
@ -13,10 +13,6 @@ namespace Ryujinx.HLE.Gpu
public NvGpuEngine3d Engine3d { get; private set; } public NvGpuEngine3d Engine3d { get; private set; }
public NvGpuEngineDma EngineDma { get; private set; } public NvGpuEngineDma EngineDma { get; private set; }
private Thread FifoProcessing;
private bool KeepRunning;
public NvGpu(IGalRenderer Renderer) public NvGpu(IGalRenderer Renderer)
{ {
this.Renderer = Renderer; this.Renderer = Renderer;
@ -26,22 +22,6 @@ namespace Ryujinx.HLE.Gpu
Engine2d = new NvGpuEngine2d(this); Engine2d = new NvGpuEngine2d(this);
Engine3d = new NvGpuEngine3d(this); Engine3d = new NvGpuEngine3d(this);
EngineDma = new NvGpuEngineDma(this); EngineDma = new NvGpuEngineDma(this);
KeepRunning = true;
FifoProcessing = new Thread(ProcessFifo);
FifoProcessing.Start();
}
private void ProcessFifo()
{
while (KeepRunning)
{
Fifo.DispatchCalls();
Thread.Yield();
}
} }
} }
} }

View file

@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
class BlockLinearSwizzle : ISwizzle class BlockLinearSwizzle : ISwizzle
{ {

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
interface ISwizzle interface ISwizzle
{ {

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
class LinearSwizzle : ISwizzle class LinearSwizzle : ISwizzle
{ {

View file

@ -1,7 +1,8 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.Gpu.Memory;
using System; using System;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
static class TextureFactory static class TextureFactory
{ {
@ -61,7 +62,7 @@ namespace Ryujinx.HLE.Gpu
int Width = (Tic[4] & 0xffff) + 1; int Width = (Tic[4] & 0xffff) + 1;
int Height = (Tic[5] & 0xffff) + 1; int Height = (Tic[5] & 0xffff) + 1;
Texture Texture = new Texture( TextureInfo Texture = new TextureInfo(
TextureAddress, TextureAddress,
Width, Width,
Height, Height,

View file

@ -1,12 +1,13 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.Gpu.Memory;
using System; using System;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
static class TextureHelper static class TextureHelper
{ {
public static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp) public static ISwizzle GetSwizzle(TextureInfo Texture, int Width, int Bpp)
{ {
switch (Texture.Swizzle) switch (Texture.Swizzle)
{ {

View file

@ -1,8 +1,8 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
struct Texture struct TextureInfo
{ {
public long Position { get; private set; } public long Position { get; private set; }
@ -16,7 +16,7 @@ namespace Ryujinx.HLE.Gpu
public GalTextureFormat Format { get; private set; } public GalTextureFormat Format { get; private set; }
public Texture( public TextureInfo(
long Position, long Position,
int Width, int Width,
int Height) int Height)
@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Gpu
Format = GalTextureFormat.A8B8G8R8; Format = GalTextureFormat.A8B8G8R8;
} }
public Texture( public TextureInfo(
long Position, long Position,
int Width, int Width,
int Height, int Height,

View file

@ -2,11 +2,11 @@ using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
static class TextureReader static class TextureReader
{ {
public static byte[] Read(IAMemory Memory, Texture Texture) public static byte[] Read(IAMemory Memory, TextureInfo Texture)
{ {
switch (Texture.Format) switch (Texture.Format)
{ {
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Gpu
throw new NotImplementedException(Texture.Format.ToString()); throw new NotImplementedException(Texture.Format.ToString());
} }
private unsafe static byte[] Read1Bpp(IAMemory Memory, Texture Texture) private unsafe static byte[] Read1Bpp(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read5551(IAMemory Memory, Texture Texture) private unsafe static byte[] Read5551(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read565(IAMemory Memory, Texture Texture) private unsafe static byte[] Read565(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read2Bpp(IAMemory Memory, Texture Texture) private unsafe static byte[] Read2Bpp(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -172,7 +172,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read4Bpp(IAMemory Memory, Texture Texture) private unsafe static byte[] Read4Bpp(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -205,7 +205,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read8Bpp(IAMemory Memory, Texture Texture) private unsafe static byte[] Read8Bpp(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -238,7 +238,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read16Bpp(IAMemory Memory, Texture Texture) private unsafe static byte[] Read16Bpp(IAMemory Memory, TextureInfo Texture)
{ {
int Width = Texture.Width; int Width = Texture.Width;
int Height = Texture.Height; int Height = Texture.Height;
@ -273,7 +273,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, Texture Texture) private unsafe static byte[] Read8Bpt4x4(IAMemory Memory, TextureInfo Texture)
{ {
int Width = (Texture.Width + 3) / 4; int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4; int Height = (Texture.Height + 3) / 4;
@ -306,7 +306,7 @@ namespace Ryujinx.HLE.Gpu
return Output; return Output;
} }
private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, Texture Texture) private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, TextureInfo Texture)
{ {
int Width = (Texture.Width + 3) / 4; int Width = (Texture.Width + 3) / 4;
int Height = (Texture.Height + 3) / 4; int Height = (Texture.Height + 3) / 4;

View file

@ -1,4 +1,4 @@
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
enum TextureSwizzle enum TextureSwizzle
{ {

View file

@ -2,16 +2,16 @@ using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using System; using System;
namespace Ryujinx.HLE.Gpu namespace Ryujinx.HLE.Gpu.Texture
{ {
static class TextureWriter static class TextureWriter
{ {
public static void Write( public static void Write(
IAMemory Memory, IAMemory Memory,
Texture Texture, TextureInfo Texture,
byte[] Data, byte[] Data,
int Width, int Width,
int Height) int Height)
{ {
switch (Texture.Format) switch (Texture.Format)
{ {
@ -22,11 +22,11 @@ namespace Ryujinx.HLE.Gpu
} }
private unsafe static void Write4Bpp( private unsafe static void Write4Bpp(
IAMemory Memory, IAMemory Memory,
Texture Texture, TextureInfo Texture,
byte[] Data, byte[] Data,
int Width, int Width,
int Height) int Height)
{ {
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4); ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);

View file

@ -1,4 +1,3 @@
using Ryujinx.HLE.OsHle.Utilities;
using System; using System;
using System.IO; using System.IO;

View file

@ -1,4 +1,3 @@
using Ryujinx.HLE.OsHle.Utilities;
using System; using System;
using System.IO; using System.IO;

View file

@ -1,6 +1,4 @@
using Ryujinx.HLE.OsHle.Utilities; using Ryujinx.HLE.OsHle.Utilities;
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;

View file

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;

View file

@ -1,5 +1,5 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Logging; using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Services.Nv.NvMap; using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
using System; using System;

View file

@ -1,5 +1,5 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Logging; using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS; using Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS;
using System; using System;

View file

@ -1,5 +1,5 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Gpu.Memory;
using Ryujinx.HLE.Logging; using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Utilities; using Ryujinx.HLE.OsHle.Utilities;
using System.Collections.Concurrent; using System.Collections.Concurrent;

View file

@ -1,5 +1,5 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.Gpu; using Ryujinx.HLE.Gpu.Texture;
using Ryujinx.HLE.Logging; using Ryujinx.HLE.Logging;
using Ryujinx.HLE.OsHle.Handles; using Ryujinx.HLE.OsHle.Handles;
using Ryujinx.HLE.OsHle.Services.Nv.NvMap; using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using static Ryujinx.HLE.OsHle.Services.Android.Parcel; using static Ryujinx.HLE.OsHle.Services.Android.Parcel;
namespace Ryujinx.HLE.OsHle.Services.Android namespace Ryujinx.HLE.OsHle.Services.Android
@ -339,7 +340,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
Rotate = -MathF.PI * 0.5f; Rotate = -MathF.PI * 0.5f;
} }
Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY); Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY));
//TODO: Support double buffering here aswell, it is broken for GPU //TODO: Support double buffering here aswell, it is broken for GPU
//frame buffers because it seems to be completely out of sync. //frame buffers because it seems to be completely out of sync.
@ -347,17 +348,17 @@ namespace Ryujinx.HLE.OsHle.Services.Android
{ {
//Frame buffer is rendered to by the GPU, we can just //Frame buffer is rendered to by the GPU, we can just
//bind the frame buffer texture, it's not necessary to read anything. //bind the frame buffer texture, it's not necessary to read anything.
Renderer.SetFrameBuffer(FbAddr); Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr));
} }
else else
{ {
//Frame buffer is not set on the GPU registers, in this case //Frame buffer is not set on the GPU registers, in this case
//assume that the app is manually writing to it. //assume that the app is manually writing to it.
Texture Texture = new Texture(FbAddr, FbWidth, FbHeight); TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight);
byte[] Data = TextureReader.Read(Context.Memory, Texture); byte[] Data = TextureReader.Read(Context.Memory, Texture);
Renderer.SetFrameBuffer(Data, FbWidth, FbHeight); Renderer.QueueAction(() => Renderer.FrameBuffer.Set(Data, FbWidth, FbHeight));
} }
Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));

View file

@ -1,84 +1,119 @@
using System.Diagnostics; using System.Diagnostics;
using System.Timers; using System.Timers;
namespace Ryujinx.HLE namespace Ryujinx.HLE
{ {
public class PerformanceStatistics public class PerformanceStatistics
{ {
Stopwatch ExecutionTime = new Stopwatch(); private const double FrameRateWeight = 0.5;
Timer ResetTimer = new Timer(1000);
long CurrentGameFrameEnded; private const int FrameTypeSystem = 0;
long CurrentSystemFrameEnded; private const int FrameTypeGame = 1;
long CurrentSystemFrameStart;
long LastGameFrameEnded;
long LastSystemFrameEnded;
double AccumulatedGameFrameTime; private double[] AverageFrameRate;
double AccumulatedSystemFrameTime; private double[] AccumulatedFrameTime;
double CurrentGameFrameTime; private double[] PreviousFrameTime;
double CurrentSystemFrameTime;
double PreviousGameFrameTime; private long[] FramesRendered;
double PreviousSystemFrameTime;
public double GameFrameRate { get; private set; } private object[] FrameLock;
public double SystemFrameRate { get; private set; }
public long SystemFramesRendered; private double TicksToSeconds;
public long GameFramesRendered;
public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds; private Stopwatch ExecutionTime;
public long ElapsedMicroseconds => (long)
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000); private Timer ResetTimer;
public long ElapsedNanoseconds => (long)
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000);
public PerformanceStatistics() public PerformanceStatistics()
{ {
AverageFrameRate = new double[2];
AccumulatedFrameTime = new double[2];
PreviousFrameTime = new double[2];
FramesRendered = new long[2];
FrameLock = new object[] { new object(), new object() };
ExecutionTime = new Stopwatch();
ExecutionTime.Start(); ExecutionTime.Start();
ResetTimer = new Timer(1000);
ResetTimer.Elapsed += ResetTimerElapsed; ResetTimer.Elapsed += ResetTimerElapsed;
ResetTimer.AutoReset = true; ResetTimer.AutoReset = true;
ResetTimer.Start(); ResetTimer.Start();
TicksToSeconds = 1.0 / Stopwatch.Frequency;
} }
private void ResetTimerElapsed(object sender, ElapsedEventArgs e) private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
{ {
ResetStatistics(); CalculateAverageFrameRate(FrameTypeSystem);
CalculateAverageFrameRate(FrameTypeGame);
} }
public void StartSystemFrame() private void CalculateAverageFrameRate(int FrameType)
{ {
PreviousSystemFrameTime = CurrentSystemFrameTime; double FrameRate = 0;
LastSystemFrameEnded = CurrentSystemFrameEnded;
CurrentSystemFrameStart = ElapsedMicroseconds; if (AccumulatedFrameTime[FrameType] > 0)
{
FrameRate = FramesRendered[FrameType] / AccumulatedFrameTime[FrameType];
}
lock (FrameLock[FrameType])
{
AverageFrameRate[FrameType] = LinearInterpolate(AverageFrameRate[FrameType], FrameRate);
FramesRendered[FrameType] = 0;
AccumulatedFrameTime[FrameType] = 0;
}
} }
public void EndSystemFrame() private double LinearInterpolate(double Old, double New)
{ {
CurrentSystemFrameEnded = ElapsedMicroseconds; return Old * (1.0 - FrameRateWeight) + New * FrameRateWeight;
CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart; }
AccumulatedSystemFrameTime += CurrentSystemFrameTime;
SystemFramesRendered++; public void RecordSystemFrameTime()
{
RecordFrameTime(FrameTypeSystem);
} }
public void RecordGameFrameTime() public void RecordGameFrameTime()
{ {
CurrentGameFrameEnded = ElapsedMicroseconds; RecordFrameTime(FrameTypeGame);
CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded;
PreviousGameFrameTime = CurrentGameFrameTime;
LastGameFrameEnded = CurrentGameFrameEnded;
AccumulatedGameFrameTime += CurrentGameFrameTime;
GameFramesRendered++;
} }
public void ResetStatistics() private void RecordFrameTime(int FrameType)
{ {
GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000); double CurrentFrameTime = ExecutionTime.ElapsedTicks * TicksToSeconds;
GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate;
SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000);
SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate;
GameFramesRendered = 0; double ElapsedFrameTime = CurrentFrameTime - PreviousFrameTime[FrameType];
SystemFramesRendered = 0;
AccumulatedGameFrameTime = 0; PreviousFrameTime[FrameType] = CurrentFrameTime;
AccumulatedSystemFrameTime = 0;
lock (FrameLock[FrameType])
{
AccumulatedFrameTime[FrameType] += ElapsedFrameTime;
FramesRendered[FrameType]++;
}
}
public double GetSystemFrameRate()
{
return AverageFrameRate[FrameTypeSystem];
}
public double GetGameFrameRate()
{
return AverageFrameRate[FrameTypeGame];
} }
} }
} }

View file

@ -71,6 +71,11 @@ namespace Ryujinx.HLE
Os.LoadProgram(FileName); Os.LoadProgram(FileName);
} }
public void ProcessFrame()
{
Gpu.Fifo.DispatchCalls();
}
internal virtual void OnFinish(EventArgs e) internal virtual void OnFinish(EventArgs e)
{ {
Finish?.Invoke(this, e); Finish?.Invoke(this, e);

View file

@ -42,7 +42,7 @@ namespace Ryujinx
{ {
VSync = VSyncMode.On; VSync = VSyncMode.On;
Renderer.SetWindowSize(Width, Height); Renderer.FrameBuffer.SetWindowSize(Width, Height);
} }
protected override void OnUpdateFrame(FrameEventArgs e) protected override void OnUpdateFrame(FrameEventArgs e)
@ -179,28 +179,31 @@ namespace Ryujinx
CurrentButton, CurrentButton,
LeftJoystick, LeftJoystick,
RightJoystick); RightJoystick);
Ns.ProcessFrame();
Renderer.RunActions();
} }
protected override void OnRenderFrame(FrameEventArgs e) protected override void OnRenderFrame(FrameEventArgs e)
{ {
Ns.Statistics.StartSystemFrame(); Renderer.FrameBuffer.Render();
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + Ns.Statistics.RecordSystemFrameTime();
$"{Ns.Statistics.GameFrameRate:0})";
Renderer.RunActions(); double HostFps = Ns.Statistics.GetSystemFrameRate();
Renderer.Render(); double GameFps = Ns.Statistics.GetGameFrameRate();
Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}";
SwapBuffers(); SwapBuffers();
Ns.Statistics.EndSystemFrame();
Ns.Os.SignalVsync(); Ns.Os.SignalVsync();
} }
protected override void OnResize(EventArgs e) protected override void OnResize(EventArgs e)
{ {
Renderer.SetWindowSize(Width, Height); Renderer.FrameBuffer.SetWindowSize(Width, Height);
} }
protected override void OnKeyDown(KeyboardKeyEventArgs e) protected override void OnKeyDown(KeyboardKeyEventArgs e)

View file

@ -14,7 +14,7 @@ namespace Ryujinx
{ {
Console.Title = "Ryujinx Console"; Console.Title = "Ryujinx Console";
IGalRenderer Renderer = new OpenGLRenderer(); IGalRenderer Renderer = new OGLRenderer();
IAalOutput AudioOut = new OpenALAudioOut(); IAalOutput AudioOut = new OpenALAudioOut();
@ -67,7 +67,7 @@ namespace Ryujinx
Screen.Exit(); Screen.Exit();
}; };
Screen.Run(60.0); Screen.Run(0.0, 60.0);
} }
Environment.Exit(0); Environment.Exit(0);