From fcafa568cd10355d9e263f999611f4de0cbe8365 Mon Sep 17 00:00:00 2001 From: Kai Blaschke Date: Mon, 12 Feb 2024 15:11:22 +0100 Subject: [PATCH] Improve performance with vertex buffer updates In wave/shape drawing code, we now preallocate the vertex buffer once for the maximum number of sides and only update the existing buffer instead of reallocating it over and over again. Trades a few KB of additional RAM for a good amount of drawing performance. In the motion vector grid code, we only reallocate the buffer if we draw more vertices than in the previous draw call. Also changed the drawing hint to GL_STREAM_DRAW, which is slightly better suited for this kind of usage (update once, draw once, repeat). These changes should hopefully improve performance, especially when large numbers of custom shapes are drawn. --- src/libprojectM/MilkdropPreset/CustomShape.cpp | 17 ++++++++++++++--- .../MilkdropPreset/CustomWaveform.cpp | 6 +++++- .../MilkdropPreset/MotionVectors.cpp | 10 +++++++++- .../MilkdropPreset/MotionVectors.hpp | 2 ++ src/libprojectM/MilkdropPreset/Waveform.cpp | 6 +++++- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/CustomShape.cpp b/src/libprojectM/MilkdropPreset/CustomShape.cpp index a4b22bb6c..0b5649064 100644 --- a/src/libprojectM/MilkdropPreset/CustomShape.cpp +++ b/src/libprojectM/MilkdropPreset/CustomShape.cpp @@ -13,6 +13,9 @@ CustomShape::CustomShape(PresetState& presetState) : m_presetState(presetState) , m_perFrameContext(presetState.globalMemory, &presetState.globalRegisters) { + std::vector vertexData; + vertexData.resize(102); + glGenVertexArrays(1, &m_vaoIdTextured); glGenBuffers(1, &m_vboIdTextured); @@ -30,6 +33,8 @@ CustomShape::CustomShape(PresetState& presetState) glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast(offsetof(TexturedPoint, r))); // Color glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast(offsetof(TexturedPoint, u))); // Texture coordinate + glBufferData(GL_ARRAY_BUFFER, sizeof(TexturedPoint) * vertexData.size(), vertexData.data(), GL_STREAM_DRAW); + glBindVertexArray(m_vaoIdUntextured); glBindBuffer(GL_ARRAY_BUFFER, m_vboIdUntextured); @@ -39,6 +44,8 @@ CustomShape::CustomShape(PresetState& presetState) glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast(offsetof(TexturedPoint, x))); // Position glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast(offsetof(TexturedPoint, r))); // Color + glBufferData(GL_ARRAY_BUFFER, sizeof(TexturedPoint) * vertexData.size(), vertexData.data(), GL_STREAM_DRAW); + RenderItem::Init(); m_perFrameContext.RegisterBuiltinVariables(); @@ -58,6 +65,10 @@ void CustomShape::InitVertexAttrib() glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); // points glDisableVertexAttribArray(1); + + std::vector vertexData; + vertexData.resize(100); + glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * vertexData.size(), vertexData.data(), GL_STREAM_DRAW); } void CustomShape::Initialize(PresetFileParser& parsedFile, int index) @@ -217,7 +228,7 @@ void CustomShape::Draw() glBindBuffer(GL_ARRAY_BUFFER, m_vboIdTextured); - glBufferData(GL_ARRAY_BUFFER, sizeof(TexturedPoint) * (sides + 2), vertexData.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(TexturedPoint) * (sides + 2), vertexData.data()); glBindVertexArray(m_vaoIdTextured); glDrawArrays(GL_TRIANGLE_FAN, 0, sides + 2); @@ -231,7 +242,7 @@ void CustomShape::Draw() // Untextured (creates a color gradient: center=r/g/b/a to border=r2/b2/g2/a2) glBindBuffer(GL_ARRAY_BUFFER, m_vboIdUntextured); - glBufferData(GL_ARRAY_BUFFER, sizeof(TexturedPoint) * (sides + 2), vertexData.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(TexturedPoint) * (sides + 2), vertexData.data()); m_presetState.untexturedShader.Bind(); m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection); @@ -304,7 +315,7 @@ void CustomShape::Draw() break; } - glBufferData(GL_ARRAY_BUFFER, static_cast(sizeof(Point) * sides), points.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast(sizeof(Point) * sides), points.data()); glDrawArrays(GL_LINE_LOOP, 0, sides); } } diff --git a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp index ee2e1721f..e63e8710a 100644 --- a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp +++ b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp @@ -30,6 +30,10 @@ void CustomWaveform::InitVertexAttrib() glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ColoredPoint), nullptr); // points glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ColoredPoint), reinterpret_cast(sizeof(float) * 2)); // colors + + std::vector vertexData; + vertexData.resize(std::max(libprojectM::Audio::SpectrumSamples, libprojectM::Audio::WaveformSamples) * 2 + 2); + glBufferData(GL_ARRAY_BUFFER, sizeof(ColoredPoint) * vertexData.size(), vertexData.data(), GL_STREAM_DRAW); } void CustomWaveform::Initialize(PresetFileParser& parsedFile, int index) @@ -224,7 +228,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) break; } - glBufferData(GL_ARRAY_BUFFER, sizeof(ColoredPoint) * smoothedVertexCount, pointsSmoothed.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ColoredPoint) * smoothedVertexCount, pointsSmoothed.data()); glDrawArrays(drawType, 0, smoothedVertexCount); } diff --git a/src/libprojectM/MilkdropPreset/MotionVectors.cpp b/src/libprojectM/MilkdropPreset/MotionVectors.cpp index 191c9db5e..cf165cbd9 100644 --- a/src/libprojectM/MilkdropPreset/MotionVectors.cpp +++ b/src/libprojectM/MilkdropPreset/MotionVectors.cpp @@ -137,7 +137,15 @@ void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext, std::shar } // Draw a row of lines. - glBufferData(GL_ARRAY_BUFFER, sizeof(MotionVectorVertex) * vertex, lineVertices.data(), GL_DYNAMIC_DRAW); + if (m_lastVertexCount >= vertex) + { + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(MotionVectorVertex) * vertex, lineVertices.data()); + } + else + { + glBufferData(GL_ARRAY_BUFFER, sizeof(MotionVectorVertex) * vertex, lineVertices.data(), GL_STREAM_DRAW); + m_lastVertexCount = vertex; + } glDrawArrays(GL_LINES, 0, static_cast(vertex)); } } diff --git a/src/libprojectM/MilkdropPreset/MotionVectors.hpp b/src/libprojectM/MilkdropPreset/MotionVectors.hpp index 973063c3f..6ceb940df 100644 --- a/src/libprojectM/MilkdropPreset/MotionVectors.hpp +++ b/src/libprojectM/MilkdropPreset/MotionVectors.hpp @@ -47,6 +47,8 @@ class MotionVectors : public Renderer::RenderItem Renderer::Shader m_motionVectorShader; //!< The motion vector shader, calculates the trace positions in the GPU. std::shared_ptr m_sampler{std::make_shared(GL_CLAMP_TO_EDGE, GL_LINEAR)}; //!< The texture sampler. + + int m_lastVertexCount{}; //!< Number of vertices drawn in the previous draw call. }; } // namespace MilkdropPreset diff --git a/src/libprojectM/MilkdropPreset/Waveform.cpp b/src/libprojectM/MilkdropPreset/Waveform.cpp index 477ff6d5a..80e09e406 100644 --- a/src/libprojectM/MilkdropPreset/Waveform.cpp +++ b/src/libprojectM/MilkdropPreset/Waveform.cpp @@ -30,6 +30,10 @@ void Waveform::InitVertexAttrib() glDisableVertexAttribArray(1); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + std::vector vertexData; + vertexData.resize(std::max(libprojectM::Audio::SpectrumSamples, libprojectM::Audio::WaveformSamples) * 2 + 2); + glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * vertexData.size(), vertexData.data(), GL_STREAM_DRAW); } void Waveform::Draw(const PerFrameContext& presetPerFrameContext) @@ -118,7 +122,7 @@ void Waveform::Draw(const PerFrameContext& presetPerFrameContext) break; } - glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * smoothedWave.size(), smoothedWave.data(), GL_DYNAMIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Point) * smoothedWave.size(), smoothedWave.data()); glDrawArrays(drawType, 0, static_cast(smoothedWave.size())); } }