Added support for more primitive types (aka topology), added alpha blending support, added texture wrap/filter support, started frame buffer support (not working properly), added index buffer support

This commit is contained in:
gdkchan 2018-04-04 02:02:56 -03:00
parent 46405f6767
commit 10a00cc796
22 changed files with 1044 additions and 227 deletions

View file

@ -680,8 +680,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
}
Map.CpuAddress = Addr;
Map.Align = Align;
Map.Kind = Kind;
Map.Align = Align;
Map.Kind = Kind;
return 0;
}

View file

@ -62,7 +62,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
private BufferEntry[] BufferQueue;
private ManualResetEvent WaitBufferFree;
private object RenderQueueLock;
private int RenderQueueCount;
@ -85,7 +85,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
{ ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
};
this.Renderer = Renderer;
this.ReleaseEvent = ReleaseEvent;
@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
BufferEntry Entry = BufferQueue[Slot];
int BufferCount = 1; //?
@ -243,7 +243,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
int Slot = ParcelReader.ReadInt32();
int BufferCount = ParcelReader.ReadInt32();
long BufferSize = ParcelReader.ReadInt64();
@ -293,7 +293,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
NvMapFb MapFb = (NvMapFb)ServiceNvDrv.NvMapsFb.GetData(Context.Process, 0);
long Address = Map.CpuAddress;
if (MapFb.HasBufferOffset(Slot))
{
Address += MapFb.GetBufferOffset(Slot);

View file

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

View file

@ -0,0 +1,25 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalBlendFactor
{
Zero = 0x4000,
One = 0x4001,
SrcColor = 0x4300,
OneMinusSrcColor = 0x4301,
SrcAlpha = 0x4302,
OneMinusSrcAlpha = 0x4303,
DstAlpha = 0x4304,
OneMinusDstAlpha = 0x4305,
DstColor = 0x4306,
OneMinusDstColor = 0x4307,
SrcAlphaSaturate = 0x4308,
ConstantColor = 0xc001,
OneMinusConstantColor = 0xc002,
ConstantAlpha = 0xc003,
OneMinusConstantAlpha = 0xc004,
Src1Color = 0xc900,
OneMinusSrc1Color = 0xc901,
Src1Alpha = 0xc902,
OneMinusSrc1Alpha = 0xc903
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalColorF
{
public float Red { get; private set; }
public float Green { get; private set; }
public float Blue { get; private set; }
public float Alpha { get; private set; }
public GalColorF(
float Red,
float Green,
float Blue,
float Alpha)
{
this.Red = Red;
this.Green = Green;
this.Blue = Blue;
this.Alpha = Alpha;
}
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalIndexFormat
{
Byte = 0,
Int16 = 1,
Int32 = 2
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFilter
{
Nearest = 1,
Linear = 2
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureMipFilter
{
None = 1,
Nearest = 2,
Linear = 3
}
}

View file

@ -0,0 +1,33 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalTextureSampler
{
public GalTextureWrap AddressU { get; private set; }
public GalTextureWrap AddressV { get; private set; }
public GalTextureWrap AddressP { get; private set; }
public GalTextureFilter MinFilter { get; private set; }
public GalTextureFilter MagFilter { get; private set; }
public GalTextureMipFilter MipFilter { get; private set; }
public GalColorF BorderColor { get; private set; }
public GalTextureSampler(
GalTextureWrap AddressU,
GalTextureWrap AddressV,
GalTextureWrap AddressP,
GalTextureFilter MinFilter,
GalTextureFilter MagFilter,
GalTextureMipFilter MipFilter,
GalColorF BorderColor)
{
this.AddressU = AddressU;
this.AddressV = AddressV;
this.AddressP = AddressP;
this.MinFilter = MinFilter;
this.MagFilter = MagFilter;
this.MipFilter = MipFilter;
this.BorderColor = BorderColor;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureWrap
{
Repeat = 0,
MirroredRepeat = 1,
ClampToEdge = 2,
ClampToBorder = 3,
Clamp = 4,
MirrorClampToEdge = 5,
MirrorClampToBorder = 6,
MirrorClamp = 7
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal
{
@ -21,23 +22,56 @@ namespace Ryujinx.Graphics.Gal
float OffsY,
float Rotate);
//Blend
void SetBlendEnable(bool Enable);
void SetBlend(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst);
void SetBlendSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha);
//Frame Buffer
void SetFb(int FbIndex, int Width, int Height);
void BindFrameBuffer(int FbIndex);
void DrawFrameBuffer(int FbIndex);
//Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
void RenderVertexArray(int VbIndex);
void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
void DrawArrays(int VbIndex, GalPrimitiveType PrimType);
void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
//Shader
void CreateShader(long Tag, GalShaderType Type, byte[] Data);
void SetShaderConstBuffer(long Tag, int Cbuf, byte[] Data);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
void SetUniform1(string UniformName, int Value);
void BindShader(long Tag);
void BindProgram();
//Texture
void UpdateTextures(Func<int, GalShaderType, GalTexture> RequestTextureCallback);
void SetTexture(int Index, GalTexture Tex);
void SetSampler(int Index, GalTextureSampler Sampler);
}
}

View file

@ -0,0 +1,49 @@
using OpenTK.Graphics.OpenGL;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLBlend
{
public void Enable()
{
GL.Enable(EnableCap.Blend);
}
public void Disable()
{
GL.Disable(EnableCap.Blend);
}
public void Set(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst)
{
GL.BlendEquation(
OGLEnumConverter.GetBlendEquation(Equation));
GL.BlendFunc(
OGLEnumConverter.GetBlendFactorSrc(FuncSrc),
OGLEnumConverter.GetBlendFactorDst(FuncDst));
}
public void SetSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha)
{
GL.BlendEquationSeparate(
OGLEnumConverter.GetBlendEquation(EquationRgb),
OGLEnumConverter.GetBlendEquation(EquationAlpha));
GL.BlendFuncSeparate(
OGLEnumConverter.GetBlendFactorSrc(FuncSrcRgb),
OGLEnumConverter.GetBlendFactorDst(FuncDstRgb),
OGLEnumConverter.GetBlendFactorSrc(FuncSrcAlpha),
OGLEnumConverter.GetBlendFactorDst(FuncDstAlpha));
}
}
}

View file

@ -5,6 +5,42 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLEnumConverter
{
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
{
switch (Format)
{
case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte;
case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
throw new ArgumentException(nameof(Format));
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
{
switch (Type)
{
case GalPrimitiveType.Points: return PrimitiveType.Points;
case GalPrimitiveType.Lines: return PrimitiveType.Lines;
case GalPrimitiveType.LineLoop: return PrimitiveType.LineLoop;
case GalPrimitiveType.LineStrip: return PrimitiveType.LineStrip;
case GalPrimitiveType.Triangles: return PrimitiveType.Triangles;
case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip;
case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan;
case GalPrimitiveType.Quads: return PrimitiveType.Quads;
case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip;
case GalPrimitiveType.Polygon: return PrimitiveType.Polygon;
case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency;
case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency;
case GalPrimitiveType.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency;
case GalPrimitiveType.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency;
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
throw new ArgumentException(nameof(Type));
}
public static ShaderType GetShaderType(GalShaderType Type)
{
switch (Type)
@ -30,5 +66,64 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new NotImplementedException(Format.ToString());
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
{
switch (Wrap)
{
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
//TODO: Those needs extensions (and are currently wrong).
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
}
throw new ArgumentException(nameof(Wrap));
}
public static TextureMinFilter GetTextureMinFilter(
GalTextureFilter MinFilter,
GalTextureMipFilter MipFilter)
{
//TODO: Mip (needs mipmap support first).
switch (MinFilter)
{
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
throw new ArgumentException(nameof(MinFilter));
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
{
switch (Filter)
{
case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
throw new ArgumentException(nameof(Filter));
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
{
return (BlendEquationMode)BlendEquation;
}
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
{
return (BlendingFactorSrc)(BlendFactor - 0x4000);
}
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
{
return (BlendingFactorDest)(BlendFactor - 0x4000);
}
}
}

View file

@ -0,0 +1,182 @@
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLFrameBuffer
{
private struct FrameBuffer
{
public int FbHandle;
public int RbHandle;
public int TexHandle;
}
private struct ShaderProgram
{
public int Handle;
public int VpHandle;
public int FpHandle;
}
private FrameBuffer[] Fbs;
private ShaderProgram Shader;
private bool IsInitialized;
private int VaoHandle;
private int VboHandle;
public OGLFrameBuffer()
{
Fbs = new FrameBuffer[16];
Shader = new ShaderProgram();
}
public void Set(int Index, int Width, int Height)
{
if (Fbs[Index].FbHandle != 0)
{
return;
}
Fbs[Index].FbHandle = GL.GenFramebuffer();
Fbs[Index].RbHandle = GL.GenRenderbuffer();
Fbs[Index].TexHandle = GL.GenTexture();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, 1280, 720);
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, Fbs[Index].RbHandle);
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 1280, 720, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, Fbs[Index].TexHandle, 0);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
}
public void Bind(int Index)
{
if (Fbs[Index].FbHandle == 0)
{
return;
}
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fbs[Index].FbHandle);
}
public void Draw(int Index)
{
if (Fbs[Index].FbHandle == 0)
{
return;
}
EnsureInitialized();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.BindTexture(TextureTarget.Texture2D, Fbs[Index].TexHandle);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindVertexArray(VaoHandle);
GL.UseProgram(Shader.Handle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
}
private void EnsureInitialized()
{
if (!IsInitialized)
{
IsInitialized = true;
SetupShader();
SetupVertex();
}
}
private void SetupShader()
{
Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader);
Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader);
string VpSource = EmbeddedResource.GetString("GlFbVtxShader");
string FpSource = EmbeddedResource.GetString("GlFbFragShader");
GL.ShaderSource(Shader.VpHandle, VpSource);
GL.ShaderSource(Shader.FpHandle, FpSource);
GL.CompileShader(Shader.VpHandle);
GL.CompileShader(Shader.FpHandle);
Shader.Handle = GL.CreateProgram();
GL.AttachShader(Shader.Handle, Shader.VpHandle);
GL.AttachShader(Shader.Handle, Shader.FpHandle);
GL.LinkProgram(Shader.Handle);
GL.UseProgram(Shader.Handle);
Matrix2 Transform = Matrix2.CreateScale(1, -1);
int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
GL.Uniform1(TexUniformLocation, 0);
int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
}
private void SetupVertex()
{
VaoHandle = GL.GenVertexArray();
VboHandle = GL.GenBuffer();
float[] Buffer = new float[]
{
-1, 1, 0, 0,
1, 1, 1, 0,
-1, -1, 0, 1,
1, -1, 1, 1
};
IntPtr Length = new IntPtr(Buffer.Length * 4);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(VaoHandle);
GL.EnableVertexAttribArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
GL.EnableVertexAttribArray(1);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
}
}
}

View file

@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //?
};
private struct VertexBuffer
private struct VbInfo
{
public int VaoHandle;
public int VboHandle;
@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int PrimCount;
}
private VertexBuffer[] VertexBuffers;
private struct IbInfo
{
public int IboHandle;
public int Count;
public DrawElementsType Type;
}
private VbInfo[] VertexBuffers;
private IbInfo IndexBuffer;
public OGLRasterizer()
{
VertexBuffers = new VertexBuffer[32];
VertexBuffers = new VbInfo[32];
IndexBuffer = new IbInfo();
}
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
@ -92,7 +104,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride;
VertexBuffer Vb = VertexBuffers[VbIndex];
VbInfo Vb = VertexBuffers[VbIndex];
IntPtr Length = new IntPtr(Buffer.Length);
@ -144,9 +156,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindVertexArray(0);
}
public void RenderVertexArray(int VbIndex)
public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
{
VertexBuffer Vb = VertexBuffers[VbIndex];
EnsureIbInitialized();
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
IndexBuffer.Count = Buffer.Length >> (int)Format;
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
}
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
{
VbInfo Vb = VertexBuffers[VbIndex];
if (Vb.PrimCount == 0)
{
@ -155,12 +182,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindVertexArray(Vb.VaoHandle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), 0, Vb.PrimCount);
}
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
{
VbInfo Vb = VertexBuffers[VbIndex];
if (Vb.PrimCount == 0)
{
return;
}
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
GL.BindVertexArray(Vb.VaoHandle);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
}
private void EnsureVbInitialized(int VbIndex)
{
VertexBuffer Vb = VertexBuffers[VbIndex];
VbInfo Vb = VertexBuffers[VbIndex];
if (Vb.VaoHandle == 0)
{
@ -174,5 +219,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
VertexBuffers[VbIndex] = Vb;
}
private void EnsureIbInitialized()
{
if (IndexBuffer.IboHandle == 0)
{
IndexBuffer.IboHandle = GL.GenBuffer();
}
}
}
}

View file

@ -1,6 +1,7 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Gal.Shader;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -13,23 +14,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
public int Handle { get; private set; }
public bool IsCompiled { get; private set; }
public GalShaderType Type { get; private set; }
public string Code { get; private set; }
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
public ShaderStage(
GalShaderType Type,
string Code,
IEnumerable<ShaderDeclInfo> TextureUsage,
IEnumerable<ShaderDeclInfo> UniformUsage)
{
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
this.Type = Type;
this.Code = Code;
this.TextureUsage = TextureUsage;
this.UniformUsage = UniformUsage;
}
public void Compile()
{
if (Handle == 0)
{
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
CompileAndCheck(Handle, Code);
}
}
public void Dispose()
{
Dispose(true);
@ -57,7 +72,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private ShaderProgram Current;
private Dictionary<long, ShaderStage> Stages;
private ConcurrentDictionary<long, ShaderStage> Stages;
private Dictionary<ShaderProgram, int> Programs;
@ -65,49 +80,62 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public OGLShader()
{
Stages = new Dictionary<long, ShaderStage>();
Stages = new ConcurrentDictionary<long, ShaderStage>();
Programs = new Dictionary<ShaderProgram, int>();
}
public void Create(long Tag, GalShaderType Type, byte[] Data)
{
if (!Stages.ContainsKey(Tag))
{
GlslProgram Program = GetGlslProgram(Data, Type);
ShaderStage Stage = new ShaderStage(
Type,
Program.Textures,
Program.Uniforms);
Stages.Add(Tag, Stage);
CompileAndCheck(Stage.Handle, Program.Code);
}
Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data));
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(GalShaderType ShaderType)
private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data)
{
switch (ShaderType)
{
case GalShaderType.Vertex: return GetTextureUsage(Current.Vertex);
case GalShaderType.TessControl: return GetTextureUsage(Current.TessControl);
case GalShaderType.TessEvaluation: return GetTextureUsage(Current.TessEvaluation);
case GalShaderType.Geometry: return GetTextureUsage(Current.Geometry);
case GalShaderType.Fragment: return GetTextureUsage(Current.Fragment);
}
GlslProgram Program = GetGlslProgram(Data, Type);
return null;
return new ShaderStage(
Type,
Program.Code,
Program.Textures,
Program.Uniforms);
}
private IEnumerable<ShaderDeclInfo> GetTextureUsage(ShaderStage Stage)
private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type)
{
return Stage?.TextureUsage ?? Enumerable.Empty<ShaderDeclInfo>();
int[] Code = new int[(Data.Length - 0x50) >> 2];
using (MemoryStream MS = new MemoryStream(Data))
{
MS.Seek(0x50, SeekOrigin.Begin);
BinaryReader Reader = new BinaryReader(MS);
for (int Index = 0; Index < Code.Length; Index++)
{
Code[Index] = Reader.ReadInt32();
}
}
GlslDecompiler Decompiler = new GlslDecompiler();
return Decompiler.Decompile(Code, Type);
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
{
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
{
return Stage.TextureUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
{
BindProgram();
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
{
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
@ -121,18 +149,32 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
public void SetUniform1(string UniformName, int Value)
{
BindProgram();
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
GL.Uniform1(Location, Value);
}
public void Bind(long Tag)
{
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
{
switch (Stage.Type)
{
case GalShaderType.Vertex: Current.Vertex = Stage; break;
case GalShaderType.TessControl: Current.TessControl = Stage; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
case GalShaderType.Geometry: Current.Geometry = Stage; break;
case GalShaderType.Fragment: Current.Fragment = Stage; break;
}
Bind(Stage);
}
}
private void Bind(ShaderStage Stage)
{
switch (Stage.Type)
{
case GalShaderType.Vertex: Current.Vertex = Stage; break;
case GalShaderType.TessControl: Current.TessControl = Stage; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
case GalShaderType.Geometry: Current.Geometry = Stage; break;
case GalShaderType.Fragment: Current.Fragment = Stage; break;
}
}
@ -170,32 +212,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{
if (Stage != null)
{
Stage.Compile();
GL.AttachShader(ProgramHandle, Stage.Handle);
}
}
private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type)
{
int[] Code = new int[(Data.Length - 0x50) >> 2];
using (MemoryStream MS = new MemoryStream(Data))
{
MS.Seek(0x50, SeekOrigin.Begin);
BinaryReader Reader = new BinaryReader(MS);
for (int Index = 0; Index < Code.Length; Index++)
{
Code[Index] = Reader.ReadInt32();
}
}
GlslDecompiler Decompiler = new GlslDecompiler();
return Decompiler.Decompile(Code, Type);
}
private static void CompileAndCheck(int Handle, string Code)
public static void CompileAndCheck(int Handle, string Code)
{
GL.ShaderSource(Handle, Code);
GL.CompileShader(Handle);

View file

@ -1,72 +1,34 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLTexture
{
private OGLShader Shader;
private int[] Textures;
private int CurrentTextureIndex;
public OGLTexture(OGLShader Shader)
public OGLTexture()
{
this.Shader = Shader;
Textures = new int[80];
}
public void UpdateTextures(Func<int, GalShaderType, GalTexture> RequestTextureCallback)
public void Set(int Index, GalTexture Tex)
{
CurrentTextureIndex = 0;
GL.ActiveTexture(TextureUnit.Texture0 + Index);
UpdateTextures(RequestTextureCallback, GalShaderType.Vertex);
UpdateTextures(RequestTextureCallback, GalShaderType.TessControl);
UpdateTextures(RequestTextureCallback, GalShaderType.TessEvaluation);
UpdateTextures(RequestTextureCallback, GalShaderType.Geometry);
UpdateTextures(RequestTextureCallback, GalShaderType.Fragment);
}
private void UpdateTextures(Func<int, GalShaderType, GalTexture> RequestTextureCallback, GalShaderType ShaderType)
{
foreach (ShaderDeclInfo DeclInfo in Shader.GetTextureUsage(ShaderType))
{
GalTexture Texture = RequestTextureCallback(DeclInfo.Index, ShaderType);
GL.ActiveTexture(TextureUnit.Texture0 + CurrentTextureIndex);
UploadTexture(Texture);
int Location = GL.GetUniformLocation(Shader.CurrentProgramHandle, DeclInfo.Name);
GL.Uniform1(Location, CurrentTextureIndex++);
}
}
private void UploadTexture(GalTexture Texture)
{
int Handle = EnsureTextureInitialized(CurrentTextureIndex);
int Handle = EnsureTextureInitialized(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
int W = Tex.Width;
int H = Tex.Height;
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
int W = Texture.Width;
int H = Texture.Height;
byte[] Data = Texture.Data;
byte[] Data = Tex.Data;
int Length = Data.Length;
if (IsCompressedTextureFormat(Texture.Format))
if (IsCompressedTextureFormat(Tex.Format))
{
PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format);
PixelInternalFormat Pif = OGLEnumConverter.GetCompressedTextureFormat(Tex.Format);
GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, Pif, W, H, 0, Length, Data);
}
@ -83,6 +45,35 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
public void Set(int Index, GalTextureSampler Sampler)
{
int Handle = EnsureTextureInitialized(Index);
GL.BindTexture(TextureTarget.Texture2D, Handle);
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
float[] Color = new float[]
{
Sampler.BorderColor.Red,
Sampler.BorderColor.Green,
Sampler.BorderColor.Blue,
Sampler.BorderColor.Alpha
};
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
}
private static bool IsCompressedTextureFormat(GalTextureFormat Format)
{
return Format == GalTextureFormat.BC1 ||
@ -90,13 +81,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Format == GalTextureFormat.BC3;
}
private int EnsureTextureInitialized(int TextureIndex)
private int EnsureTextureInitialized(int TexIndex)
{
int Handle = Textures[TextureIndex];
int Handle = Textures[TexIndex];
if (Handle == 0)
{
Handle = Textures[TextureIndex] = GL.GenTexture();
Handle = Textures[TexIndex] = GL.GenTexture();
}
return Handle;

View file

@ -1,11 +1,16 @@
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;
@ -18,11 +23,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public OpenGLRenderer()
{
Blend = new OGLBlend();
FrameBuffer = new OGLFrameBuffer();
Rasterizer = new OGLRasterizer();
Shader = new OGLShader();
Texture = new OGLTexture(Shader);
Texture = new OGLTexture();
ActionsQueue = new ConcurrentQueue<Action>();
}
@ -83,6 +92,61 @@ namespace Ryujinx.Graphics.Gal.OpenGL
FbRenderer.Set(Fb, Width, Height, Transform, Offs);
}
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 SetFb(int FbIndex, int Width, int Height)
{
ActionsQueue.Enqueue(() => FrameBuffer.Set(FbIndex, Width, Height));
}
public void BindFrameBuffer(int FbIndex)
{
ActionsQueue.Enqueue(() => FrameBuffer.Bind(FbIndex));
}
public void DrawFrameBuffer(int FbIndex)
{
ActionsQueue.Enqueue(() => FrameBuffer.Draw(FbIndex));
}
public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
{
ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
@ -100,14 +164,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Attribs ?? throw new ArgumentNullException(nameof(Attribs))));
}
public void RenderVertexArray(int VbIndex)
public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
{
if (Buffer == null)
{
throw new ArgumentNullException(nameof(Buffer));
}
ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
}
public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
{
if ((uint)VbIndex > 31)
{
throw new ArgumentOutOfRangeException(nameof(VbIndex));
}
ActionsQueue.Enqueue(() => Rasterizer.RenderVertexArray(VbIndex));
ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, PrimType));
}
public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
{
if ((uint)VbIndex > 31)
{
throw new ArgumentOutOfRangeException(nameof(VbIndex));
}
ActionsQueue.Enqueue(() => Rasterizer.DrawElements(VbIndex, First, PrimType));
}
public void CreateShader(long Tag, GalShaderType Type, byte[] Data)
@ -117,10 +201,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentNullException(nameof(Data));
}
ActionsQueue.Enqueue(() => Shader.Create(Tag, Type, Data));
Shader.Create(Tag, Type, Data);
}
public void SetShaderConstBuffer(long Tag, int Cbuf, byte[] Data)
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
{
if (Data == null)
{
@ -130,6 +214,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
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 IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
{
return Shader.GetTextureUsage(Tag);
}
public void BindShader(long Tag)
{
ActionsQueue.Enqueue(() => Shader.Bind(Tag));
@ -140,9 +239,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ActionsQueue.Enqueue(() => Shader.BindProgram());
}
public void UpdateTextures(Func<int, GalShaderType, GalTexture> RequestTextureCallback)
public void SetTexture(int Index, GalTexture Tex)
{
ActionsQueue.Enqueue(() => Texture.UpdateTextures(RequestTextureCallback));
ActionsQueue.Enqueue(() => Texture.Set(Index, Tex));
}
public void SetSampler(int Index, GalTextureSampler Sampler)
{
ActionsQueue.Enqueue(() => Texture.Set(Index, Sampler));
}
}
}

View file

@ -152,7 +152,7 @@ namespace Ryujinx.Graphics.Gal.Shader
}
else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
{
Name = "out " + GetDecl(DeclInfo) + ";";
Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";";
}
else
{

View file

@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Gpu
private ConstBuffer[] Cbs;
private bool HasDataToRender;
public NvGpuEngine3d(NsGpu Gpu)
{
this.Gpu = Gpu;
@ -61,34 +63,60 @@ namespace Ryujinx.Graphics.Gpu
}
}
private const long GoodFbAddress = 0x1615b2a00;
private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry)
{
int TexCbuf = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
SetFrameBuffer(0);
int TexHandle = ReadCb(Memory, TexCbuf, 0x20);
UploadShaders(Memory);
long[] Tags = UploadShaders(Memory);
Gpu.Renderer.BindProgram();
UploadTextures(Memory);
SetAlphaBlending();
UploadTextures(Memory, Tags);
UploadUniforms(Memory);
UploadVertexArrays(Memory);
HasDataToRender = true;
}
private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
{
if (HasDataToRender)
{
HasDataToRender = false;
Gpu.Renderer.DrawFrameBuffer(0);
}
int Arg0 = PBEntry.Arguments[0];
int Rt = (Arg0 >> 6) & 0xf;
int FbIndex = (Arg0 >> 6) & 0xf;
int Layer = (Arg0 >> 10) & 0x3ff;
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
Gpu.Renderer.ClearBuffers(Rt, Flags);
SetFrameBuffer(0);
Gpu.Renderer.ClearBuffers(Layer, Flags);
}
private void UploadShaders(AMemory Memory)
private void SetFrameBuffer(int FbIndex)
{
int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10);
int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
Gpu.Renderer.SetFb(FbIndex, Width, Height);
Gpu.Renderer.BindFrameBuffer(FbIndex);
}
private long[] UploadShaders(AMemory Memory)
{
long[] Tags = new long[5];
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
for (int Index = 0; Index < 6; Index++)
@ -115,9 +143,108 @@ namespace Ryujinx.Graphics.Gpu
GalShaderType ShaderType = GetTypeFromProgram(Index);
Tags[(int)ShaderType] = Tag;
Gpu.Renderer.CreateShader(Tag, ShaderType, Code);
Gpu.Renderer.BindShader(Tag);
}
return Tags;
}
private static GalShaderType GetTypeFromProgram(int Program)
{
switch (Program)
{
case 0:
case 1: return GalShaderType.Vertex;
case 2: return GalShaderType.TessControl;
case 3: return GalShaderType.TessEvaluation;
case 4: return GalShaderType.Geometry;
case 5: return GalShaderType.Fragment;
}
throw new ArgumentOutOfRangeException(nameof(Program));
}
private void SetAlphaBlending()
{
bool BlendEnableMaster = (ReadRegister(NvGpuEngine3dReg.BlendEnableMaster) & 1) != 0;
Gpu.Renderer.SetBlendEnable(BlendEnableMaster);
bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.BlendSeparateAlpha) & 1) != 0;
GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationRgb);
GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcRgb);
GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstRgb);
if (BlendSeparateAlpha)
{
GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.BlendEquationAlpha);
GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncSrcAlpha);
GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.BlendFuncDstAlpha);
Gpu.Renderer.SetBlendSeparate(
EquationRgb,
EquationAlpha,
FuncSrcRgb,
FuncDstRgb,
FuncSrcAlpha,
FuncDstAlpha);
}
else
{
Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb);
}
}
private void UploadTextures(AMemory Memory, long[] Tags)
{
long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
long BasePosition = Cbs[TextureCbIndex].Position;
long Size = (uint)Cbs[TextureCbIndex].Size;
int TexIndex = 0;
for (int Index = 0; Index < Tags.Length; Index++)
{
foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
{
long Position = BasePosition + Index * Size;
UploadTexture(Memory, Position, TexIndex, DeclInfo.Index);
Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
TexIndex++;
}
}
}
private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex)
{
long Position = BasePosition + HndIndex * 4;
int TextureHandle = Memory.ReadInt32(Position);
int TicIndex = (TextureHandle >> 0) & 0xfffff;
int TscIndex = (TextureHandle >> 20) & 0xfff;
TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition);
TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition);
TicPosition += TicIndex * 0x20;
TscPosition += TscIndex * 0x20;
Gpu.Renderer.SetTexture(TexIndex, TextureFactory.MakeTexture(Gpu, Memory, TicPosition));
Gpu.Renderer.SetSampler(TexIndex, TextureFactory.MakeSampler(Gpu, Memory, TscPosition));
}
private void UploadUniforms(AMemory Memory)
@ -147,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu
byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size);
Gpu.Renderer.SetShaderConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
}
}
}
@ -155,6 +282,32 @@ namespace Ryujinx.Graphics.Gpu
private void UploadVertexArrays(AMemory Memory)
{
long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
int IndexSize = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize;
IndexSize = 1 << IndexSize;
if (IndexSize > 4)
{
throw new InvalidOperationException();
}
if (IndexSize != 0)
{
IndexPosition = Gpu.GetCpuAddr(IndexPosition);
int BufferSize = IndexCount * IndexSize;
byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize);
Gpu.Renderer.SetIndexArray(Data, IndexFormat);
}
List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
for (int Attr = 0; Attr < 16; Attr++)
@ -187,83 +340,36 @@ namespace Ryujinx.Graphics.Gpu
continue;
}
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
long EndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
long Size = (EndPos - Position) + 1;
long Size = (VertexEndPos - VertexPosition) + 1;
int Stride = Control & 0xfff;
Position = Gpu.GetCpuAddr(Position);
VertexPosition = Gpu.GetCpuAddr(VertexPosition);
byte[] Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size);
GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray);
Gpu.Renderer.RenderVertexArray(Index);
int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
if (IndexCount != 0)
{
Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType);
}
else
{
Gpu.Renderer.DrawArrays(Index, PrimType);
}
}
}
private void UploadTextures(AMemory Memory)
{
int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
long BasePosition = Cbs[TextureCbIndex].Position;
long Size = (uint)Cbs[TextureCbIndex].Size;
Gpu.Renderer.UpdateTextures((int Index, GalShaderType ShaderType) =>
{
long Position = BasePosition + (int)ShaderType * Size;
return TextureRequestHandler(Memory, Position, Index);
});
}
private GalTexture TextureRequestHandler(AMemory Memory, long BasePosition, int Index)
{
long Position = BasePosition + Index * 4;
int TextureHandle = Memory.ReadInt32(Position);
int TicIndex = (TextureHandle >> 0) & 0xfffff;
int TscIndex = (TextureHandle >> 20) & 0xfff;
TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset, out long TicPosition);
TicPosition += TicIndex * 0x20;
return TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
}
private int[] ReadWords(AMemory Memory, long Position, int Count)
{
int[] Words = new int[Count];
for (int Index = 0; Index < Count; Index++, Position += 4)
{
Words[Index] = Memory.ReadInt32(Position);
}
return Words;
}
private static GalShaderType GetTypeFromProgram(int Program)
{
switch (Program)
{
case 0:
case 1: return GalShaderType.Vertex;
case 2: return GalShaderType.TessControl;
case 3: return GalShaderType.TessEvaluation;
case 4: return GalShaderType.Geometry;
case 5: return GalShaderType.Fragment;
}
throw new ArgumentOutOfRangeException(nameof(Program));
}
private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry)
{
if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position))

View file

@ -2,23 +2,43 @@ namespace Ryujinx.Graphics.Gpu
{
enum NvGpuEngine3dReg
{
VertexAttribNFormat = 0x458,
TexHeaderPoolOffset = 0x55d,
ShaderAddress = 0x582,
QueryAddress = 0x6c0,
QuerySequence = 0x6c2,
QueryControl = 0x6c3,
VertexArrayNControl = 0x700,
VertexArrayNAddress = 0x701,
VertexArrayNDivisor = 0x703,
VertexArrayNEndAddr = 0x7c0,
ShaderNControl = 0x800,
ShaderNOffset = 0x801,
ShaderNMaxGprs = 0x803,
ShaderNType = 0x804,
ConstBufferNSize = 0x8e0,
ConstBufferNAddress = 0x8e1,
ConstBufferNOffset = 0x8e3,
TextureCbIndex = 0x982
FrameBufferNAddress = 0x200,
FrameBufferNWidth = 0x202,
FrameBufferNHeight = 0x203,
FrameBufferNFormat = 0x204,
VertexAttribNFormat = 0x458,
BlendSeparateAlpha = 0x4cf,
BlendEquationRgb = 0x4d0,
BlendFuncSrcRgb = 0x4d1,
BlendFuncDstRgb = 0x4d2,
BlendEquationAlpha = 0x4d3,
BlendFuncSrcAlpha = 0x4d4,
BlendFuncDstAlpha = 0x4d6,
BlendEnableMaster = 0x4d7,
VertexArrayElemBase = 0x50d,
TexHeaderPoolOffset = 0x55d,
TexSamplerPoolOffset = 0x557,
ShaderAddress = 0x582,
VertexBeginGl = 0x586,
IndexArrayAddress = 0x5f2,
IndexArrayEndAddr = 0x5f4,
IndexArrayFormat = 0x5f6,
IndexBatchFirst = 0x5f7,
IndexBatchCount = 0x5f8,
QueryAddress = 0x6c0,
QuerySequence = 0x6c2,
QueryControl = 0x6c3,
VertexArrayNControl = 0x700,
VertexArrayNAddress = 0x701,
VertexArrayNDivisor = 0x703,
VertexArrayNEndAddr = 0x7c0,
ShaderNControl = 0x800,
ShaderNOffset = 0x801,
ShaderNMaxGprs = 0x803,
ShaderNType = 0x804,
ConstBufferNSize = 0x8e0,
ConstBufferNAddress = 0x8e1,
ConstBufferNOffset = 0x8e3,
TextureCbIndex = 0x982
}
}

View file

@ -1,5 +1,6 @@
using ChocolArm64.Memory;
using Ryujinx.Graphics.Gal;
using System;
namespace Ryujinx.Graphics.Gpu
{
@ -39,6 +40,34 @@ namespace Ryujinx.Graphics.Gpu
return new GalTexture(Data, Width, Height, Format);
}
public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition)
{
int[] Tsc = ReadWords(Memory, TscPosition, 8);
GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3);
GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3);
GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
GalColorF BorderColor = new GalColorF(
BitConverter.Int32BitsToSingle(Tsc[4]),
BitConverter.Int32BitsToSingle(Tsc[5]),
BitConverter.Int32BitsToSingle(Tsc[6]),
BitConverter.Int32BitsToSingle(Tsc[7]));
return new GalTextureSampler(
AddressU,
AddressV,
AddressP,
MinFilter,
MagFilter,
MipFilter,
BorderColor);
}
private static int[] ReadWords(AMemory Memory, long Position, int Count)
{
int[] Words = new int[Count];