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:
parent
69697957e6
commit
e7559f128f
58 changed files with 518 additions and 633 deletions
|
@ -1,4 +1,6 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
interface INvGpuEngine
|
||||
{
|
|
@ -1,10 +1,16 @@
|
|||
using Ryujinx.HLE.Gpu.Exceptions;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
class MacroInterpreter
|
||||
{
|
||||
private const int MaxCallCountPerRun = 500;
|
||||
|
||||
private int CallCount;
|
||||
|
||||
private enum AssignmentOperation
|
||||
{
|
||||
IgnoreAndFetch = 0,
|
||||
|
@ -96,6 +102,8 @@ namespace Ryujinx.HLE.Gpu
|
|||
MethIncr = 0;
|
||||
|
||||
Carry = false;
|
||||
|
||||
CallCount = 0;
|
||||
}
|
||||
|
||||
private bool Step(NvGpuVmm Vmm, int[] Mme)
|
||||
|
@ -407,6 +415,15 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
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);
|
||||
|
||||
Engine.CallMethod(Vmm, PBEntry);
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
enum NvGpuEngine
|
||||
{
|
|
@ -1,7 +1,9 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Gpu.Texture;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
class NvGpuEngine2d : INvGpuEngine
|
||||
{
|
||||
|
@ -75,19 +77,19 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
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 DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress);
|
||||
|
||||
bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Tag);
|
||||
bool IsFbTexture = Gpu.Engine3d.IsFrameBufferPosition(Key);
|
||||
|
||||
if (IsFbTexture && DstLinear)
|
||||
{
|
||||
DstSwizzle = TextureSwizzle.BlockLinear;
|
||||
}
|
||||
|
||||
Texture DstTexture = new Texture(
|
||||
TextureInfo DstTexture = new TextureInfo(
|
||||
DstAddress,
|
||||
DstWidth,
|
||||
DstHeight,
|
||||
|
@ -103,7 +105,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
SrcWidth = 1280;
|
||||
SrcHeight = 720;
|
||||
|
||||
Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) =>
|
||||
Gpu.Renderer.FrameBuffer.GetBufferData(Key, (byte[] Buffer) =>
|
||||
{
|
||||
CopyTexture(
|
||||
Vmm,
|
||||
|
@ -129,11 +131,11 @@ namespace Ryujinx.HLE.Gpu
|
|||
}
|
||||
|
||||
private void CopyTexture(
|
||||
NvGpuVmm Vmm,
|
||||
Texture Texture,
|
||||
byte[] Buffer,
|
||||
int Width,
|
||||
int Height)
|
||||
NvGpuVmm Vmm,
|
||||
TextureInfo Texture,
|
||||
byte[] Buffer,
|
||||
int Width,
|
||||
int Height)
|
||||
{
|
||||
TextureWriter.Write(Vmm, Texture, Buffer, Width, Height);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
enum NvGpuEngine2dReg
|
||||
{
|
|
@ -1,8 +1,10 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Gpu.Texture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
class NvGpuEngine3d : INvGpuEngine
|
||||
{
|
||||
|
@ -73,13 +75,13 @@ namespace Ryujinx.HLE.Gpu
|
|||
{
|
||||
SetFrameBuffer(Vmm, 0);
|
||||
|
||||
long[] Tags = UploadShaders(Vmm);
|
||||
long[] Keys = UploadShaders(Vmm);
|
||||
|
||||
Gpu.Renderer.BindProgram();
|
||||
Gpu.Renderer.Shader.BindProgram();
|
||||
|
||||
SetAlphaBlending();
|
||||
|
||||
UploadTextures(Vmm, Tags);
|
||||
UploadTextures(Vmm, Keys);
|
||||
UploadUniforms(Vmm);
|
||||
UploadVertexArrays(Vmm);
|
||||
}
|
||||
|
@ -113,13 +115,13 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
//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(PA, 1280, 720);
|
||||
Gpu.Renderer.BindFrameBuffer(PA);
|
||||
Gpu.Renderer.FrameBuffer.Create(PA, 1280, 720);
|
||||
Gpu.Renderer.FrameBuffer.Bind(PA);
|
||||
}
|
||||
|
||||
private long[] UploadShaders(NvGpuVmm Vmm)
|
||||
{
|
||||
long[] Tags = new long[5];
|
||||
long[] Keys = new long[5];
|
||||
|
||||
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
|
||||
|
||||
|
@ -136,14 +138,14 @@ namespace Ryujinx.HLE.Gpu
|
|||
continue;
|
||||
}
|
||||
|
||||
long Tag = BasePosition + (uint)Offset;
|
||||
long Key = BasePosition + (uint)Offset;
|
||||
|
||||
GalShaderType ShaderType = GetTypeFromProgram(Index);
|
||||
|
||||
Tags[(int)ShaderType] = Tag;
|
||||
Keys[(int)ShaderType] = Key;
|
||||
|
||||
Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType);
|
||||
Gpu.Renderer.BindShader(Tag);
|
||||
Gpu.Renderer.Shader.Create(Vmm, Key, ShaderType);
|
||||
Gpu.Renderer.Shader.Bind(Key);
|
||||
}
|
||||
|
||||
int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
|
||||
|
@ -155,9 +157,9 @@ namespace Ryujinx.HLE.Gpu
|
|||
float SignX = MathF.Sign(SX);
|
||||
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)
|
||||
|
@ -180,7 +182,14 @@ namespace Ryujinx.HLE.Gpu
|
|||
//TODO: Support independent blend properly.
|
||||
bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0;
|
||||
|
||||
Gpu.Renderer.SetBlendEnable(Enable);
|
||||
if (Enable)
|
||||
{
|
||||
Gpu.Renderer.Blend.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.Blend.Disable();
|
||||
}
|
||||
|
||||
if (!Enable)
|
||||
{
|
||||
|
@ -203,7 +212,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
|
||||
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
|
||||
|
||||
Gpu.Renderer.SetBlendSeparate(
|
||||
Gpu.Renderer.Blend.SetSeparate(
|
||||
EquationRgb,
|
||||
EquationAlpha,
|
||||
FuncSrcRgb,
|
||||
|
@ -213,11 +222,11 @@ namespace Ryujinx.HLE.Gpu
|
|||
}
|
||||
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);
|
||||
|
||||
|
@ -227,15 +236,15 @@ namespace Ryujinx.HLE.Gpu
|
|||
//reserved for drawing the frame buffer.
|
||||
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;
|
||||
|
||||
UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index);
|
||||
|
||||
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
|
||||
Gpu.Renderer.Shader.EnsureTextureBinding(DeclInfo.Name, TexIndex);
|
||||
|
||||
TexIndex++;
|
||||
}
|
||||
|
@ -270,7 +279,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
long TextureAddress = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff;
|
||||
|
||||
long Tag = TextureAddress;
|
||||
long Key = TextureAddress;
|
||||
|
||||
TextureAddress = Vmm.GetPhysicalAddress(TextureAddress);
|
||||
|
||||
|
@ -280,7 +289,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
//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);
|
||||
Gpu.Renderer.FrameBuffer.BindTexture(TextureAddress, TexIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -288,22 +297,29 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
|
||||
|
||||
if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture))
|
||||
{
|
||||
if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture))
|
||||
{
|
||||
Gpu.Renderer.BindTexture(Tag, TexIndex);
|
||||
bool HasCachedTexture = false;
|
||||
|
||||
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)
|
||||
|
@ -331,7 +347,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
{
|
||||
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);
|
||||
|
||||
int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
|
||||
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
|
||||
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
|
||||
int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
|
||||
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
|
||||
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();
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
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];
|
||||
|
@ -429,27 +445,27 @@ namespace Ryujinx.HLE.Gpu
|
|||
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))
|
||||
{
|
||||
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);
|
||||
|
||||
if (IndexCount != 0)
|
||||
{
|
||||
Gpu.Renderer.DrawElements(IndexPosition, IndexFirst, PrimType);
|
||||
Gpu.Renderer.Rasterizer.DrawElements(IndexPosition, IndexFirst, PrimType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.DrawArrays(VertexFirst, VertexCount, PrimType);
|
||||
Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
enum NvGpuEngine3dReg
|
||||
{
|
|
@ -1,6 +1,8 @@
|
|||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Gpu.Texture;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
class NvGpuEngineDma : INvGpuEngine
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
enum NvGpuEngineDmaReg
|
||||
{
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
class NvGpuFifo
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Engines
|
||||
{
|
||||
enum NvGpuFifoMeth
|
||||
{
|
|
@ -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);
|
||||
}
|
11
Ryujinx.HLE/Gpu/Exceptions/GpuException.cs
Normal file
11
Ryujinx.HLE/Gpu/Exceptions/GpuException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu.Exceptions
|
||||
{
|
||||
class GpuException : Exception
|
||||
{
|
||||
public GpuException() : base() { }
|
||||
|
||||
public GpuException(string ExMsg) : base(ExMsg) { }
|
||||
}
|
||||
}
|
12
Ryujinx.HLE/Gpu/Exceptions/GpuExceptionHelper.cs
Normal file
12
Ryujinx.HLE/Gpu/Exceptions/GpuExceptionHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Memory
|
||||
{
|
||||
enum NvGpuBufferType
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Memory
|
||||
{
|
||||
struct NvGpuPBEntry
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Memory
|
||||
{
|
||||
static class NvGpuPushBuffer
|
||||
{
|
|
@ -2,7 +2,7 @@ using ChocolArm64.Memory;
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Memory
|
||||
{
|
||||
class NvGpuVmm : IAMemory, IGalMemory
|
||||
{
|
|
@ -2,7 +2,7 @@ using ChocolArm64.Memory;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Memory
|
||||
{
|
||||
class NvGpuVmmCache
|
||||
{
|
|
@ -1,5 +1,5 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using System.Threading;
|
||||
using Ryujinx.HLE.Gpu.Engines;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
{
|
||||
|
@ -13,10 +13,6 @@ namespace Ryujinx.HLE.Gpu
|
|||
public NvGpuEngine3d Engine3d { get; private set; }
|
||||
public NvGpuEngineDma EngineDma { get; private set; }
|
||||
|
||||
private Thread FifoProcessing;
|
||||
|
||||
private bool KeepRunning;
|
||||
|
||||
public NvGpu(IGalRenderer Renderer)
|
||||
{
|
||||
this.Renderer = Renderer;
|
||||
|
@ -26,22 +22,6 @@ namespace Ryujinx.HLE.Gpu
|
|||
Engine2d = new NvGpuEngine2d(this);
|
||||
Engine3d = new NvGpuEngine3d(this);
|
||||
EngineDma = new NvGpuEngineDma(this);
|
||||
|
||||
KeepRunning = true;
|
||||
|
||||
FifoProcessing = new Thread(ProcessFifo);
|
||||
|
||||
FifoProcessing.Start();
|
||||
}
|
||||
|
||||
private void ProcessFifo()
|
||||
{
|
||||
while (KeepRunning)
|
||||
{
|
||||
Fifo.DispatchCalls();
|
||||
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
class BlockLinearSwizzle : ISwizzle
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
interface ISwizzle
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
class LinearSwizzle : ISwizzle
|
||||
{
|
|
@ -1,7 +1,8 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
static class TextureFactory
|
||||
{
|
||||
|
@ -61,7 +62,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
int Width = (Tic[4] & 0xffff) + 1;
|
||||
int Height = (Tic[5] & 0xffff) + 1;
|
||||
|
||||
Texture Texture = new Texture(
|
||||
TextureInfo Texture = new TextureInfo(
|
||||
TextureAddress,
|
||||
Width,
|
||||
Height,
|
|
@ -1,12 +1,13 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
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)
|
||||
{
|
|
@ -1,8 +1,8 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
struct Texture
|
||||
struct TextureInfo
|
||||
{
|
||||
public long Position { get; private set; }
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
public GalTextureFormat Format { get; private set; }
|
||||
|
||||
public Texture(
|
||||
public TextureInfo(
|
||||
long Position,
|
||||
int Width,
|
||||
int Height)
|
||||
|
@ -34,7 +34,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
Format = GalTextureFormat.A8B8G8R8;
|
||||
}
|
||||
|
||||
public Texture(
|
||||
public TextureInfo(
|
||||
long Position,
|
||||
int Width,
|
||||
int Height,
|
|
@ -2,11 +2,11 @@ using ChocolArm64.Memory;
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
static class TextureReader
|
||||
{
|
||||
public static byte[] Read(IAMemory Memory, Texture Texture)
|
||||
public static byte[] Read(IAMemory Memory, TextureInfo Texture)
|
||||
{
|
||||
switch (Texture.Format)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -172,7 +172,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -205,7 +205,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -238,7 +238,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = Texture.Height;
|
||||
|
@ -273,7 +273,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = (Texture.Height + 3) / 4;
|
||||
|
@ -306,7 +306,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
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 Height = (Texture.Height + 3) / 4;
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
enum TextureSwizzle
|
||||
{
|
|
@ -2,16 +2,16 @@ using ChocolArm64.Memory;
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
namespace Ryujinx.HLE.Gpu.Texture
|
||||
{
|
||||
static class TextureWriter
|
||||
{
|
||||
public static void Write(
|
||||
IAMemory Memory,
|
||||
Texture Texture,
|
||||
byte[] Data,
|
||||
int Width,
|
||||
int Height)
|
||||
IAMemory Memory,
|
||||
TextureInfo Texture,
|
||||
byte[] Data,
|
||||
int Width,
|
||||
int Height)
|
||||
{
|
||||
switch (Texture.Format)
|
||||
{
|
||||
|
@ -22,11 +22,11 @@ namespace Ryujinx.HLE.Gpu
|
|||
}
|
||||
|
||||
private unsafe static void Write4Bpp(
|
||||
IAMemory Memory,
|
||||
Texture Texture,
|
||||
byte[] Data,
|
||||
int Width,
|
||||
int Height)
|
||||
IAMemory Memory,
|
||||
TextureInfo Texture,
|
||||
byte[] Data,
|
||||
int Width,
|
||||
int Height)
|
||||
{
|
||||
ISwizzle Swizzle = TextureHelper.GetSwizzle(Texture, Width, 4);
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
using Ryujinx.HLE.OsHle.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
|
@ -20,7 +19,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
public KernelAccessControl KernelAccessControl;
|
||||
|
||||
public const long ACI0Magic = 'A' << 0 | 'C' << 8 | 'I' << 16 | '0' << 24;
|
||||
|
||||
|
||||
public ACI0(Stream ACI0Stream, int Offset)
|
||||
{
|
||||
ACI0Stream.Seek(Offset, SeekOrigin.Begin);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using Ryujinx.HLE.OsHle.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
|
@ -24,7 +23,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
public FSAccessControl FSAccessControl;
|
||||
public ServiceAccessControl ServiceAccessControl;
|
||||
public KernelAccessControl KernelAccessControl;
|
||||
|
||||
|
||||
public const long ACIDMagic = 'A' << 0 | 'C' << 8 | 'I' << 16 | 'D' << 24;
|
||||
|
||||
public ACID(Stream ACIDStream, int Offset)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using Ryujinx.HLE.OsHle.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
@ -29,7 +27,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
|
||||
public ACI0 ACI0;
|
||||
public ACID ACID;
|
||||
|
||||
|
||||
public const long NpdmMagic = 'M' << 0 | 'E' << 8 | 'T' << 16 | 'A' << 24;
|
||||
|
||||
public Npdm(Stream NPDMStream)
|
||||
|
@ -61,7 +59,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
// ProcessCategory (0: regular title, 1: kernel built-in). Should be 0 here.
|
||||
ProcessCategory = EndianSwap.Swap32(Reader.ReadInt32());
|
||||
|
||||
// Main entrypoint stack size
|
||||
// Main entrypoint stack size
|
||||
// (Should(?) be page-aligned. In non-nspwn scenarios, values of 0 can also rarely break in Horizon.
|
||||
// This might be something auto-adapting or a security feature of some sort ?)
|
||||
MainEntrypointStackSize = Reader.ReadInt32();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
|
@ -25,7 +24,7 @@ namespace Ryujinx.HLE.Loaders.Npdm
|
|||
|
||||
int Length = ((ControlByte & 0x07)) + 1;
|
||||
bool RegisterAllowed = ((ControlByte & 0x80) != 0);
|
||||
|
||||
|
||||
Services.Add((Encoding.ASCII.GetString(Reader.ReadBytes(Length), 0, Length), RegisterAllowed));
|
||||
|
||||
ByteReaded += Length + 1;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.HLE.Gpu;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
|
||||
using System;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.HLE.Gpu;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Services.Nv.NvGpuAS;
|
||||
using System;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using ChocolArm64.Memory;
|
||||
using Ryujinx.HLE.Gpu;
|
||||
using Ryujinx.HLE.Gpu.Memory;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Utilities;
|
||||
using System.Collections.Concurrent;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Ryujinx.Graphics.Gal;
|
||||
using Ryujinx.HLE.Gpu;
|
||||
using Ryujinx.HLE.Gpu.Texture;
|
||||
using Ryujinx.HLE.Logging;
|
||||
using Ryujinx.HLE.OsHle.Handles;
|
||||
using Ryujinx.HLE.OsHle.Services.Nv.NvMap;
|
||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.HLE.OsHle.Services.Android.Parcel;
|
||||
|
||||
namespace Ryujinx.HLE.OsHle.Services.Android
|
||||
|
@ -339,7 +340,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
|
|||
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
|
||||
//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
|
||||
//bind the frame buffer texture, it's not necessary to read anything.
|
||||
Renderer.SetFrameBuffer(FbAddr);
|
||||
Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr));
|
||||
}
|
||||
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(FbAddr, FbWidth, FbHeight);
|
||||
TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight);
|
||||
|
||||
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));
|
||||
|
|
|
@ -1,84 +1,119 @@
|
|||
using System.Diagnostics;
|
||||
using System.Timers;
|
||||
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
{
|
||||
public class PerformanceStatistics
|
||||
{
|
||||
Stopwatch ExecutionTime = new Stopwatch();
|
||||
Timer ResetTimer = new Timer(1000);
|
||||
private const double FrameRateWeight = 0.5;
|
||||
|
||||
long CurrentGameFrameEnded;
|
||||
long CurrentSystemFrameEnded;
|
||||
long CurrentSystemFrameStart;
|
||||
long LastGameFrameEnded;
|
||||
long LastSystemFrameEnded;
|
||||
private const int FrameTypeSystem = 0;
|
||||
private const int FrameTypeGame = 1;
|
||||
|
||||
double AccumulatedGameFrameTime;
|
||||
double AccumulatedSystemFrameTime;
|
||||
double CurrentGameFrameTime;
|
||||
double CurrentSystemFrameTime;
|
||||
double PreviousGameFrameTime;
|
||||
double PreviousSystemFrameTime;
|
||||
public double GameFrameRate { get; private set; }
|
||||
public double SystemFrameRate { get; private set; }
|
||||
public long SystemFramesRendered;
|
||||
public long GameFramesRendered;
|
||||
public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds;
|
||||
public long ElapsedMicroseconds => (long)
|
||||
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000);
|
||||
public long ElapsedNanoseconds => (long)
|
||||
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000);
|
||||
private double[] AverageFrameRate;
|
||||
private double[] AccumulatedFrameTime;
|
||||
private double[] PreviousFrameTime;
|
||||
|
||||
private long[] FramesRendered;
|
||||
|
||||
private object[] FrameLock;
|
||||
|
||||
private double TicksToSeconds;
|
||||
|
||||
private Stopwatch ExecutionTime;
|
||||
|
||||
private Timer ResetTimer;
|
||||
|
||||
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();
|
||||
|
||||
ResetTimer = new Timer(1000);
|
||||
|
||||
ResetTimer.Elapsed += ResetTimerElapsed;
|
||||
|
||||
ResetTimer.AutoReset = true;
|
||||
|
||||
ResetTimer.Start();
|
||||
|
||||
TicksToSeconds = 1.0 / Stopwatch.Frequency;
|
||||
}
|
||||
|
||||
private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
ResetStatistics();
|
||||
CalculateAverageFrameRate(FrameTypeSystem);
|
||||
CalculateAverageFrameRate(FrameTypeGame);
|
||||
}
|
||||
|
||||
public void StartSystemFrame()
|
||||
private void CalculateAverageFrameRate(int FrameType)
|
||||
{
|
||||
PreviousSystemFrameTime = CurrentSystemFrameTime;
|
||||
LastSystemFrameEnded = CurrentSystemFrameEnded;
|
||||
CurrentSystemFrameStart = ElapsedMicroseconds;
|
||||
double FrameRate = 0;
|
||||
|
||||
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;
|
||||
CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart;
|
||||
AccumulatedSystemFrameTime += CurrentSystemFrameTime;
|
||||
SystemFramesRendered++;
|
||||
return Old * (1.0 - FrameRateWeight) + New * FrameRateWeight;
|
||||
}
|
||||
|
||||
public void RecordSystemFrameTime()
|
||||
{
|
||||
RecordFrameTime(FrameTypeSystem);
|
||||
}
|
||||
|
||||
public void RecordGameFrameTime()
|
||||
{
|
||||
CurrentGameFrameEnded = ElapsedMicroseconds;
|
||||
CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded;
|
||||
PreviousGameFrameTime = CurrentGameFrameTime;
|
||||
LastGameFrameEnded = CurrentGameFrameEnded;
|
||||
AccumulatedGameFrameTime += CurrentGameFrameTime;
|
||||
GameFramesRendered++;
|
||||
RecordFrameTime(FrameTypeGame);
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
private void RecordFrameTime(int FrameType)
|
||||
{
|
||||
GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000);
|
||||
GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate;
|
||||
SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000);
|
||||
SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate;
|
||||
double CurrentFrameTime = ExecutionTime.ElapsedTicks * TicksToSeconds;
|
||||
|
||||
GameFramesRendered = 0;
|
||||
SystemFramesRendered = 0;
|
||||
AccumulatedGameFrameTime = 0;
|
||||
AccumulatedSystemFrameTime = 0;
|
||||
double ElapsedFrameTime = CurrentFrameTime - PreviousFrameTime[FrameType];
|
||||
|
||||
PreviousFrameTime[FrameType] = CurrentFrameTime;
|
||||
|
||||
lock (FrameLock[FrameType])
|
||||
{
|
||||
AccumulatedFrameTime[FrameType] += ElapsedFrameTime;
|
||||
|
||||
FramesRendered[FrameType]++;
|
||||
}
|
||||
}
|
||||
|
||||
public double GetSystemFrameRate()
|
||||
{
|
||||
return AverageFrameRate[FrameTypeSystem];
|
||||
}
|
||||
|
||||
public double GetGameFrameRate()
|
||||
{
|
||||
return AverageFrameRate[FrameTypeGame];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,11 @@ namespace Ryujinx.HLE
|
|||
Os.LoadProgram(FileName);
|
||||
}
|
||||
|
||||
public void ProcessFrame()
|
||||
{
|
||||
Gpu.Fifo.DispatchCalls();
|
||||
}
|
||||
|
||||
internal virtual void OnFinish(EventArgs e)
|
||||
{
|
||||
Finish?.Invoke(this, e);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue