Implement vertex instancing (#381)
This commit is contained in:
parent
da7e702751
commit
a42ab2e40c
7 changed files with 82 additions and 20 deletions
|
@ -7,6 +7,8 @@
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
public int Stride;
|
public int Stride;
|
||||||
public long VboKey;
|
public long VboKey;
|
||||||
|
public bool Instanced;
|
||||||
|
public int Divisor;
|
||||||
public GalVertexAttrib[] Attribs;
|
public GalVertexAttrib[] Attribs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +24,8 @@
|
||||||
public float FlipX;
|
public float FlipX;
|
||||||
public float FlipY;
|
public float FlipY;
|
||||||
|
|
||||||
|
public int Instance;
|
||||||
|
|
||||||
public GalFrontFace FrontFace;
|
public GalFrontFace FrontFace;
|
||||||
|
|
||||||
public bool CullFaceEnabled;
|
public bool CullFaceEnabled;
|
||||||
|
|
|
@ -126,9 +126,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
BindVertexLayout(New);
|
BindVertexLayout(New);
|
||||||
|
|
||||||
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY)
|
if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance)
|
||||||
{
|
{
|
||||||
Shader.SetFlip(New.FlipX, New.FlipY);
|
Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
|
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
|
||||||
|
@ -290,8 +290,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private void BindConstBuffers(GalPipelineState New)
|
private void BindConstBuffers(GalPipelineState New)
|
||||||
{
|
{
|
||||||
//Index 0 is reserved
|
int FreeBinding = OGLShader.ReservedCbufCount;
|
||||||
int FreeBinding = 1;
|
|
||||||
|
|
||||||
void BindIfNotNull(OGLShaderStage Stage)
|
void BindIfNotNull(OGLShaderStage Stage)
|
||||||
{
|
{
|
||||||
|
@ -385,6 +384,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
|
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Binding.Instanced && Binding.Divisor != 0)
|
||||||
|
{
|
||||||
|
GL.VertexAttribDivisor(Attrib.Index, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.VertexAttribDivisor(Attrib.Index, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
class OGLShader : IGalShader
|
class OGLShader : IGalShader
|
||||||
{
|
{
|
||||||
|
public const int ReservedCbufCount = 1;
|
||||||
|
|
||||||
|
private const int ExtraDataSize = 4;
|
||||||
|
|
||||||
public OGLShaderProgram Current;
|
public OGLShaderProgram Current;
|
||||||
|
|
||||||
private ConcurrentDictionary<long, OGLShaderStage> Stages;
|
private ConcurrentDictionary<long, OGLShaderStage> Stages;
|
||||||
|
@ -96,7 +100,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
return Enumerable.Empty<ShaderDeclInfo>();
|
return Enumerable.Empty<ShaderDeclInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void SetFlip(float X, float Y)
|
public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
|
||||||
{
|
{
|
||||||
BindProgram();
|
BindProgram();
|
||||||
|
|
||||||
|
@ -104,14 +108,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
||||||
|
|
||||||
float* Data = stackalloc float[4];
|
float* Data = stackalloc float[ExtraDataSize];
|
||||||
Data[0] = X;
|
Data[0] = FlipX;
|
||||||
Data[1] = Y;
|
Data[1] = FlipY;
|
||||||
|
Data[2] = BitConverter.Int32BitsToSingle(Instance);
|
||||||
|
|
||||||
//Invalidate buffer
|
//Invalidate buffer
|
||||||
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||||
|
|
||||||
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, 4 * sizeof(float), (IntPtr)Data);
|
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Bind(long Key)
|
public void Bind(long Key)
|
||||||
|
@ -197,7 +202,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
|
||||||
|
|
||||||
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||||
|
|
||||||
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
|
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
|
||||||
}
|
}
|
||||||
|
@ -219,8 +224,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
|
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
|
||||||
|
|
||||||
//First index is reserved
|
int FreeBinding = ReservedCbufCount;
|
||||||
int FreeBinding = 1;
|
|
||||||
|
|
||||||
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
|
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
public const string ExtraUniformBlockName = "Extra";
|
public const string ExtraUniformBlockName = "Extra";
|
||||||
public const string FlipUniformName = "flip";
|
public const string FlipUniformName = "flip";
|
||||||
|
public const string InstanceUniformName = "instance";
|
||||||
|
|
||||||
public const string ProgramName = "program";
|
public const string ProgramName = "program";
|
||||||
public const string ProgramAName = ProgramName + "_a";
|
public const string ProgramAName = ProgramName + "_a";
|
||||||
|
|
|
@ -241,10 +241,15 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
if (Decl.ShaderType == GalShaderType.Vertex)
|
if (Decl.ShaderType == GalShaderType.Vertex)
|
||||||
{
|
{
|
||||||
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + "{");
|
//Memory layout here is [flip_x, flip_y, instance, unused]
|
||||||
|
//It's using 4 bytes, not 8
|
||||||
|
|
||||||
|
SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + " {");
|
||||||
|
|
||||||
SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";");
|
SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";");
|
||||||
|
|
||||||
|
SB.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";");
|
||||||
|
|
||||||
SB.AppendLine("};");
|
SB.AppendLine("};");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,7 +821,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
switch (Abuf.Offs)
|
switch (Abuf.Offs)
|
||||||
{
|
{
|
||||||
case GlslDecl.VertexIdAttr: return "gl_VertexID";
|
case GlslDecl.VertexIdAttr: return "gl_VertexID";
|
||||||
case GlslDecl.InstanceIdAttr: return "gl_InstanceID";
|
case GlslDecl.InstanceIdAttr: return GlslDecl.InstanceUniformName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Decl.ShaderType == GalShaderType.TessEvaluation)
|
else if (Decl.ShaderType == GalShaderType.TessEvaluation)
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
|
|
||||||
private List<long>[] UploadedKeys;
|
private List<long>[] UploadedKeys;
|
||||||
|
|
||||||
|
private int CurrentInstance = 0;
|
||||||
|
|
||||||
public NvGpuEngine3d(NvGpu Gpu)
|
public NvGpuEngine3d(NvGpu Gpu)
|
||||||
{
|
{
|
||||||
this.Gpu = Gpu;
|
this.Gpu = Gpu;
|
||||||
|
@ -654,10 +656,25 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
|
||||||
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
|
long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2);
|
||||||
|
|
||||||
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
|
int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4);
|
||||||
|
|
||||||
|
bool Instanced = (ReadRegister(NvGpuEngine3dReg.VertexArrayNInstance + Index) & 1) != 0;
|
||||||
|
|
||||||
int Stride = Control & 0xfff;
|
int Stride = Control & 0xfff;
|
||||||
|
|
||||||
|
if (Instanced && VertexDivisor != 0)
|
||||||
|
{
|
||||||
|
VertexPosition += Stride * (CurrentInstance / VertexDivisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VertexPosition > VertexEndPos)
|
||||||
|
{
|
||||||
|
//Instance is invalid, ignore the draw call
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long VboKey = Vmm.GetPhysicalAddress(VertexPosition);
|
||||||
|
|
||||||
long VbSize = (VertexEndPos - VertexPosition) + 1;
|
long VbSize = (VertexEndPos - VertexPosition) + 1;
|
||||||
|
|
||||||
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
|
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
|
||||||
|
@ -669,10 +686,12 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
|
Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
State.VertexBindings[Index].Enabled = true;
|
State.VertexBindings[Index].Enabled = true;
|
||||||
State.VertexBindings[Index].Stride = Stride;
|
State.VertexBindings[Index].Stride = Stride;
|
||||||
State.VertexBindings[Index].VboKey = VboKey;
|
State.VertexBindings[Index].VboKey = VboKey;
|
||||||
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
|
State.VertexBindings[Index].Instanced = Instanced;
|
||||||
|
State.VertexBindings[Index].Divisor = VertexDivisor;
|
||||||
|
State.VertexBindings[Index].Attribs = Attribs[Index].ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,6 +702,25 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
|
|
||||||
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
|
GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
|
||||||
|
|
||||||
|
bool InstanceNext = ((PrimCtrl >> 26) & 1) != 0;
|
||||||
|
bool InstanceCont = ((PrimCtrl >> 27) & 1) != 0;
|
||||||
|
|
||||||
|
if (InstanceNext && InstanceCont)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InstanceNext)
|
||||||
|
{
|
||||||
|
CurrentInstance++;
|
||||||
|
}
|
||||||
|
else if (!InstanceCont)
|
||||||
|
{
|
||||||
|
CurrentInstance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
State.Instance = CurrentInstance;
|
||||||
|
|
||||||
Gpu.Renderer.Pipeline.Bind(State);
|
Gpu.Renderer.Pipeline.Bind(State);
|
||||||
|
|
||||||
if (IndexCount != 0)
|
if (IndexCount != 0)
|
||||||
|
|
|
@ -53,6 +53,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
StencilFrontFuncMask = 0x4e6,
|
StencilFrontFuncMask = 0x4e6,
|
||||||
StencilFrontMask = 0x4e7,
|
StencilFrontMask = 0x4e7,
|
||||||
VertexArrayElemBase = 0x50d,
|
VertexArrayElemBase = 0x50d,
|
||||||
|
VertexArrayInstBase = 0x50e,
|
||||||
ZetaEnable = 0x54e,
|
ZetaEnable = 0x54e,
|
||||||
TexHeaderPoolOffset = 0x55d,
|
TexHeaderPoolOffset = 0x55d,
|
||||||
TexSamplerPoolOffset = 0x557,
|
TexSamplerPoolOffset = 0x557,
|
||||||
|
@ -70,6 +71,7 @@ namespace Ryujinx.HLE.Gpu.Engines
|
||||||
IndexArrayFormat = 0x5f6,
|
IndexArrayFormat = 0x5f6,
|
||||||
IndexBatchFirst = 0x5f7,
|
IndexBatchFirst = 0x5f7,
|
||||||
IndexBatchCount = 0x5f8,
|
IndexBatchCount = 0x5f8,
|
||||||
|
VertexArrayNInstance = 0x620,
|
||||||
CullFaceEnable = 0x646,
|
CullFaceEnable = 0x646,
|
||||||
FrontFace = 0x647,
|
FrontFace = 0x647,
|
||||||
CullFace = 0x648,
|
CullFace = 0x648,
|
||||||
|
|
Loading…
Reference in a new issue