Call OpenGL functions directly, remove the pfifo thread, some refactoring
This commit is contained in:
parent
69697957e6
commit
88a38e9754
27 changed files with 262 additions and 403 deletions
|
@ -1,6 +1,5 @@
|
|||
using ChocolArm64.Translation;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace ChocolArm64.Instruction
|
||||
{
|
||||
|
|
22
Ryujinx.Graphics/Gal/IGalBlend.cs
Normal file
22
Ryujinx.Graphics/Gal/IGalBlend.cs
Normal 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);
|
||||
}
|
||||
}
|
27
Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
Normal file
27
Ryujinx.Graphics/Gal/IGalFrameBuffer.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public interface IGalFrameBuffer
|
||||
{
|
||||
void Create(long Tag, int Width, int Height);
|
||||
|
||||
void Bind(long Tag);
|
||||
|
||||
void BindTexture(long Tag, int Index);
|
||||
|
||||
void Set(long Tag);
|
||||
|
||||
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 Tag, Action<byte[]> Callback);
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics/Gal/IGalRasterizer.cs
Normal file
23
Ryujinx.Graphics/Gal/IGalRasterizer.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public interface IGalRasterizer
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,90 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public unsafe interface IGalRenderer
|
||||
public interface IGalRenderer
|
||||
{
|
||||
void QueueAction(Action ActionMthd);
|
||||
|
||||
void RunActions();
|
||||
|
||||
void Render();
|
||||
IGalBlend Blend { get; }
|
||||
|
||||
void SetWindowSize(int Width, int Height);
|
||||
IGalFrameBuffer FrameBuffer { get; }
|
||||
|
||||
//Blend
|
||||
void SetBlendEnable(bool Enable);
|
||||
IGalRasterizer Rasterizer { get; }
|
||||
|
||||
void SetBlend(
|
||||
GalBlendEquation Equation,
|
||||
GalBlendFactor FuncSrc,
|
||||
GalBlendFactor FuncDst);
|
||||
IGalShader Shader { get; }
|
||||
|
||||
void SetBlendSeparate(
|
||||
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);
|
||||
IGalTexture Texture { get; }
|
||||
}
|
||||
}
|
21
Ryujinx.Graphics/Gal/IGalShader.cs
Normal file
21
Ryujinx.Graphics/Gal/IGalShader.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public interface IGalShader
|
||||
{
|
||||
void Create(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 Bind(long Tag);
|
||||
|
||||
void BindProgram();
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics/Gal/IGalTexture.cs
Normal file
13
Ryujinx.Graphics/Gal/IGalTexture.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public interface IGalTexture
|
||||
{
|
||||
void Create(long Tag, byte[] Data, GalTexture Texture);
|
||||
|
||||
bool TryGetCachedTexture(long Tag, long DataSize, out GalTexture Texture);
|
||||
|
||||
void Bind(long Tag, int Index);
|
||||
|
||||
void SetSampler(GalTextureSampler Sampler);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLBlend
|
||||
public class OGLBlend : IGalBlend
|
||||
{
|
||||
public void Enable()
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLFrameBuffer
|
||||
public class OGLFrameBuffer : IGalFrameBuffer
|
||||
{
|
||||
private struct Rect
|
||||
{
|
||||
|
@ -16,9 +16,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
|
||||
public Rect(int X, int Y, int Width, int Height)
|
||||
{
|
||||
this.X = X;
|
||||
this.Y = Y;
|
||||
this.Width = Width;
|
||||
this.X = X;
|
||||
this.Y = Y;
|
||||
this.Width = Width;
|
||||
this.Height = Height;
|
||||
}
|
||||
}
|
||||
|
@ -185,10 +185,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
CurrTexHandle = RawFbTexHandle;
|
||||
}
|
||||
|
||||
public void SetTransform(Matrix2 Transform, Vector2 Offs)
|
||||
public void SetTransform(float SX, float SY, float Rotate, float TX, float TY)
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
Matrix2 Transform;
|
||||
|
||||
Transform = Matrix2.CreateScale(SX, SY);
|
||||
Transform *= Matrix2.CreateRotation(Rotate);
|
||||
|
||||
Vector2 Offs = new Vector2(TX, TY);
|
||||
|
||||
int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||
|
||||
GL.UseProgram(Shader.Handle);
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLRasterizer
|
||||
public class OGLRasterizer : IGalRasterizer
|
||||
{
|
||||
private static Dictionary<GalVertexAttribSize, int> AttribElements =
|
||||
new Dictionary<GalVertexAttribSize, int>()
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Linq;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLShader
|
||||
public class OGLShader : IGalShader
|
||||
{
|
||||
private class ShaderStage : IDisposable
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ using System;
|
|||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
class OGLTexture
|
||||
public class OGLTexture : IGalTexture
|
||||
{
|
||||
private class TCE
|
||||
{
|
||||
|
@ -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 WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
using OpenTK;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
public class OpenGLRenderer : IGalRenderer
|
||||
{
|
||||
private OGLBlend Blend;
|
||||
public IGalBlend Blend { get; private set; }
|
||||
|
||||
private OGLFrameBuffer FrameBuffer;
|
||||
public IGalFrameBuffer FrameBuffer { get; private set; }
|
||||
|
||||
private OGLRasterizer Rasterizer;
|
||||
public IGalRasterizer Rasterizer { get; private set; }
|
||||
|
||||
private OGLShader Shader;
|
||||
public IGalShader Shader { get; private set; }
|
||||
|
||||
private OGLTexture Texture;
|
||||
public IGalTexture Texture { get; private set; }
|
||||
|
||||
private ConcurrentQueue<Action> ActionsQueue;
|
||||
|
||||
|
@ -48,237 +46,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
|||
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));
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.HLE/Gpu/GpuMacroException.cs
Normal file
11
Ryujinx.HLE/Gpu/GpuMacroException.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
{
|
||||
class GpuMacroException : Exception
|
||||
{
|
||||
public GpuMacroException() : base() { }
|
||||
|
||||
public GpuMacroException(string ExMsg) : base(ExMsg) { }
|
||||
}
|
||||
}
|
7
Ryujinx.HLE/Gpu/GpuMacroExceptionMsgs.cs
Normal file
7
Ryujinx.HLE/Gpu/GpuMacroExceptionMsgs.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.HLE.Gpu
|
||||
{
|
||||
static class GpuMacroExceptionMsgs
|
||||
{
|
||||
public const string CallCountExceeded = "Method call count exceeded the limit allowed per run!";
|
||||
}
|
||||
}
|
|
@ -5,6 +5,10 @@ namespace Ryujinx.HLE.Gpu
|
|||
{
|
||||
class MacroInterpreter
|
||||
{
|
||||
private const int MaxCallCountPerRun = 500;
|
||||
|
||||
private int CallCount;
|
||||
|
||||
private enum AssignmentOperation
|
||||
{
|
||||
IgnoreAndFetch = 0,
|
||||
|
@ -96,6 +100,8 @@ namespace Ryujinx.HLE.Gpu
|
|||
MethIncr = 0;
|
||||
|
||||
Carry = false;
|
||||
|
||||
CallCount = 0;
|
||||
}
|
||||
|
||||
private bool Step(NvGpuVmm Vmm, int[] Mme)
|
||||
|
@ -407,6 +413,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)
|
||||
{
|
||||
throw new GpuMacroException(GpuMacroExceptionMsgs.CallCountExceeded);
|
||||
}
|
||||
|
||||
NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value);
|
||||
|
||||
Engine.CallMethod(Vmm, PBEntry);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
SrcWidth = 1280;
|
||||
SrcHeight = 720;
|
||||
|
||||
Gpu.Renderer.GetFrameBufferData(Tag, (byte[] Buffer) =>
|
||||
Gpu.Renderer.FrameBuffer.GetBufferData(Tag, (byte[] Buffer) =>
|
||||
{
|
||||
CopyTexture(
|
||||
Vmm,
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
long[] Tags = UploadShaders(Vmm);
|
||||
|
||||
Gpu.Renderer.BindProgram();
|
||||
Gpu.Renderer.Shader.BindProgram();
|
||||
|
||||
SetAlphaBlending();
|
||||
|
||||
|
@ -113,8 +113,8 @@ 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)
|
||||
|
@ -142,8 +142,8 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
Tags[(int)ShaderType] = Tag;
|
||||
|
||||
Gpu.Renderer.CreateShader(Vmm, Tag, ShaderType);
|
||||
Gpu.Renderer.BindShader(Tag);
|
||||
Gpu.Renderer.Shader.Create(Vmm, Tag, ShaderType);
|
||||
Gpu.Renderer.Shader.Bind(Tag);
|
||||
}
|
||||
|
||||
int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
|
||||
|
@ -155,7 +155,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
float SignX = MathF.Sign(SX);
|
||||
float SignY = MathF.Sign(SY);
|
||||
|
||||
Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY);
|
||||
Gpu.Renderer.Shader.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY);
|
||||
|
||||
return Tags;
|
||||
}
|
||||
|
@ -180,7 +180,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 +210,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,7 +220,7 @@ namespace Ryujinx.HLE.Gpu
|
|||
}
|
||||
else
|
||||
{
|
||||
Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb);
|
||||
Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,13 +236,13 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
for (int Index = 0; Index < Tags.Length; Index++)
|
||||
{
|
||||
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
|
||||
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Tags[Index]))
|
||||
{
|
||||
long Position = ConstBuffers[Index][TextureCbIndex].Position;
|
||||
|
||||
UploadTexture(Vmm, Position, TexIndex, DeclInfo.Index);
|
||||
|
||||
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
|
||||
Gpu.Renderer.Shader.SetUniform1(DeclInfo.Name, TexIndex);
|
||||
|
||||
TexIndex++;
|
||||
}
|
||||
|
@ -280,7 +287,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 +295,29 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
long Size = (uint)TextureHelper.GetTextureSize(NewTexture);
|
||||
|
||||
if (Gpu.Renderer.TryGetCachedTexture(Tag, Size, out GalTexture Texture))
|
||||
bool HasCachedTexture = false;
|
||||
|
||||
if (Gpu.Renderer.Texture.TryGetCachedTexture(Tag, Size, out GalTexture Texture))
|
||||
{
|
||||
if (NewTexture.Equals(Texture) && !Vmm.IsRegionModified(Tag, Size, NvGpuBufferType.Texture))
|
||||
{
|
||||
Gpu.Renderer.BindTexture(Tag, TexIndex);
|
||||
Gpu.Renderer.Texture.Bind(Tag, TexIndex);
|
||||
|
||||
return;
|
||||
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(Tag, Data, NewTexture);
|
||||
}
|
||||
|
||||
Gpu.Renderer.BindTexture(Tag, TexIndex);
|
||||
Gpu.Renderer.Texture.Bind(Tag, TexIndex);
|
||||
}
|
||||
|
||||
Gpu.Renderer.Texture.SetSampler(Sampler);
|
||||
}
|
||||
|
||||
private void UploadUniforms(NvGpuVmm Vmm)
|
||||
|
@ -331,7 +345,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 +355,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 +443,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,5 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.Gpu
|
||||
{
|
||||
|
@ -11,6 +12,10 @@ namespace Ryujinx.HLE.Gpu
|
|||
//a guess here and use 256kb as the size. Increase if needed.
|
||||
private const int MmeWords = 256 * 256;
|
||||
|
||||
//This is used to prevent an unbounded growth of the FIFO queue.
|
||||
//The game shouldn't send more commands than we are capable to process.
|
||||
private const int FifoCapacity = 10000;
|
||||
|
||||
private NvGpu Gpu;
|
||||
|
||||
private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry)> BufferQueue;
|
||||
|
@ -48,6 +53,8 @@ namespace Ryujinx.HLE.Gpu
|
|||
|
||||
private int[] Mme;
|
||||
|
||||
private ManualResetEvent FifoWait;
|
||||
|
||||
public NvGpuFifo(NvGpu Gpu)
|
||||
{
|
||||
this.Gpu = Gpu;
|
||||
|
@ -59,10 +66,19 @@ namespace Ryujinx.HLE.Gpu
|
|||
Macros = new CachedMacro[MacrosCount];
|
||||
|
||||
Mme = new int[MmeWords];
|
||||
|
||||
FifoWait = new ManualResetEvent(true);
|
||||
}
|
||||
|
||||
public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer)
|
||||
{
|
||||
if (BufferQueue.Count + Buffer.Length > FifoCapacity)
|
||||
{
|
||||
FifoWait.Reset();
|
||||
}
|
||||
|
||||
FifoWait.WaitOne();
|
||||
|
||||
foreach (NvGpuPBEntry PBEntry in Buffer)
|
||||
{
|
||||
BufferQueue.Enqueue((Vmm, PBEntry));
|
||||
|
@ -72,6 +88,8 @@ namespace Ryujinx.HLE.Gpu
|
|||
public void DispatchCalls()
|
||||
{
|
||||
while (Step());
|
||||
|
||||
FifoWait.Set();
|
||||
}
|
||||
|
||||
public bool Step()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -339,7 +339,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,7 +347,7 @@ 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
|
||||
{
|
||||
|
@ -357,7 +357,7 @@ namespace Ryujinx.HLE.OsHle.Services.Android
|
|||
|
||||
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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Ryujinx
|
|||
{
|
||||
VSync = VSyncMode.On;
|
||||
|
||||
Renderer.SetWindowSize(Width, Height);
|
||||
Renderer.FrameBuffer.SetWindowSize(Width, Height);
|
||||
}
|
||||
|
||||
protected override void OnUpdateFrame(FrameEventArgs e)
|
||||
|
@ -183,24 +183,29 @@ namespace Ryujinx
|
|||
|
||||
protected override void OnRenderFrame(FrameEventArgs e)
|
||||
{
|
||||
Ns.Statistics.StartSystemFrame();
|
||||
|
||||
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
|
||||
$"{Ns.Statistics.GameFrameRate:0})";
|
||||
|
||||
Renderer.RunActions();
|
||||
Renderer.Render();
|
||||
|
||||
Renderer.FrameBuffer.Render();
|
||||
|
||||
Ns.Statistics.EndSystemFrame();
|
||||
|
||||
double HostFps = Ns.Statistics.SystemFrameRate;
|
||||
double GameFps = Ns.Statistics.GameFrameRate;
|
||||
|
||||
Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}";
|
||||
|
||||
SwapBuffers();
|
||||
|
||||
Ns.Statistics.EndSystemFrame();
|
||||
Ns.Statistics.StartSystemFrame();
|
||||
|
||||
Ns.ProcessFrame();
|
||||
|
||||
Ns.Os.SignalVsync();
|
||||
}
|
||||
|
||||
protected override void OnResize(EventArgs e)
|
||||
{
|
||||
Renderer.SetWindowSize(Width, Height);
|
||||
Renderer.FrameBuffer.SetWindowSize(Width, Height);
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyboardKeyEventArgs e)
|
||||
|
|
Loading…
Add table
Reference in a new issue