From e0f66c1fbfa7874df9970c2d269bd61e05758309 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 18 Aug 2018 14:42:26 -0500 Subject: GLRasterizer: Implemented instanced vertex arrays. Before each draw call, for every enabled vertex array configured as instanced, we take the current instance id and divide it by its configured divisor, then we multiply that by the corresponding stride and increment the start address by the resulting amount. This way we can simulate the vertex array being incremented once per instance without actually using OpenGL's instancing functions. --- src/video_core/engines/maxwell_3d.h | 15 ++++++++++++++- src/video_core/renderer_opengl/gl_rasterizer.cpp | 19 ++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 771eb5abc..3c869d3a1 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -679,7 +679,19 @@ public: INSERT_PADDING_WORDS(0x7); - INSERT_PADDING_WORDS(0x46); + INSERT_PADDING_WORDS(0x20); + + struct { + u32 is_instanced[NumVertexArrays]; + + /// Returns whether the vertex array specified by index is supposed to be + /// accessed per instance or not. + bool IsInstancingEnabled(u32 index) const { + return is_instanced[index]; + } + } instanced_arrays; + + INSERT_PADDING_WORDS(0x6); Cull cull; @@ -928,6 +940,7 @@ ASSERT_REG_POSITION(point_coord_replace, 0x581); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); ASSERT_REG_POSITION(index_array, 0x5F2); +ASSERT_REG_POSITION(instanced_arrays, 0x620); ASSERT_REG_POSITION(cull, 0x646); ASSERT_REG_POSITION(clear_buffers, 0x674); ASSERT_REG_POSITION(query, 0x6C0); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 93eadde7a..fe1f55e85 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -98,7 +98,8 @@ RasterizerOpenGL::~RasterizerOpenGL() {} std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VAO); - const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + const auto& regs = gpu.regs; state.draw.vertex_array = hw_vao.handle; state.draw.vertex_buffer = stream_buffer.GetHandle(); @@ -110,9 +111,13 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, if (!vertex_array.IsEnabled()) continue; - const Tegra::GPUVAddr start = vertex_array.StartAddress(); + Tegra::GPUVAddr start = vertex_array.StartAddress(); const Tegra::GPUVAddr end = regs.vertex_array_limit[index].LimitAddress(); + if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { + start += vertex_array.stride * (gpu.state.current_instance / vertex_array.divisor); + } + ASSERT(end > start); u64 size = end - start + 1; @@ -124,7 +129,15 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, vertex_array.stride); - ASSERT_MSG(vertex_array.divisor == 0, "Instanced vertex arrays are not supported"); + if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { + // Tell OpenGL that this is an instanced vertex buffer to prevent accessing different + // indexes on each vertex. We do the instance indexing manually by incrementing the + // start address of the vertex buffer. + glVertexBindingDivisor(index, 1); + } else { + // Disable the vertex buffer instancing. + glVertexBindingDivisor(index, 0); + } } // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. -- cgit v1.2.3