From 7cf98849523cfced6bb237a26ac4848191ccb591 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Thu, 10 Sep 2026 12:23:34 +0800 Subject: [PATCH] Draw the Preview's Toolpath Segments From an Index Buffer The preview's frame cost is dominated by one call: a single instanced draw of every visible toolpath segment. On a tall multi-filament print the wipe tower supplies most of those segments, which is why the preview of a large tower is slow and why shrinking the layer range speeds it up again. That draw is not fill bound. Shrinking the model to about a fortieth of its screen area moved the frame from 419 ms to 401 ms, so the cost is per segment, not per pixel, and it is paid in the vertex shader: five texelFetch calls plus several cross/normalize per invocation. Each segment is a box of eight corners, but it was submitted with glDrawArraysInstanced over a 24 entry array, so every corner was transformed once per triangle that touches it and the shader ran 24 times per segment. The same 24 entries are now an element buffer over the eight distinct corners, which lets the post-transform cache reuse them and drops the shader to 8 runs per segment. The triangles, their winding and the vertex_id each corner receives are unchanged. Measured over 100 frames on the 636-layer, 351k-vertex three-filament fixture, the segment draw goes from 381 ms to 322 ms per frame. That is a software rasterizer, where triangle setup dominates and understates the win; the drop in shader invocations is the transferable part. Verified by loading the same project in this build and in a build of the parent commit and comparing the canvas across three states - the default view, a rotated camera, and a reduced layer range: pixel identical in all three. The rotated case matters because the shader picks its corner offsets from the camera direction. The only pixels that differ anywhere on screen are in the G-code text panel, which prints a per-process object id that varies between any two runs. --- src/libvgcode/src/SegmentTemplate.cpp | 24 ++++++++++++++++++++---- src/libvgcode/src/SegmentTemplate.hpp | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libvgcode/src/SegmentTemplate.cpp b/src/libvgcode/src/SegmentTemplate.cpp index 4e692d5b10..9d9872aa3b 100644 --- a/src/libvgcode/src/SegmentTemplate.cpp +++ b/src/libvgcode/src/SegmentTemplate.cpp @@ -15,7 +15,12 @@ namespace libvgcode { //| 2--0-------5--7 | //| \ | | / | //| 3-------4 | -static constexpr const std::array VERTEX_DATA = { +// The eight corners the vertex shader knows how to place. Each is sent once and +// referenced by INDEX_DATA below, so the post-transform cache can reuse it across +// the triangles that share it: the shader runs 8 times per segment instead of 24. +static constexpr const std::array VERTEX_DATA = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +static constexpr const std::array INDEX_DATA = { 0, 1, 2, // front spike 0, 2, 3, // front spike 0, 3, 4, // right/bottom body @@ -31,7 +36,7 @@ void SegmentTemplate::init() if (m_vao_id != 0) return; - m_size_in_bytes_gpu += VERTEX_DATA.size() * sizeof(uint8_t); + m_size_in_bytes_gpu += (VERTEX_DATA.size() + INDEX_DATA.size()) * sizeof(uint8_t); int curr_vertex_array; glsafe(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &curr_vertex_array)); @@ -51,12 +56,22 @@ void SegmentTemplate::init() glsafe(glVertexAttribIPointer(0, 1, GL_UNSIGNED_BYTE, 0, (const void*)0)); #endif // ENABLE_OPENGL_ES + // The element buffer binding is part of the vao state, so it is left bound here + // and restored together with the vao. + glsafe(glGenBuffers(1, &m_ibo_id)); + glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id)); + glsafe(glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDEX_DATA.size() * sizeof(uint8_t), INDEX_DATA.data(), GL_STATIC_DRAW)); + glsafe(glBindBuffer(GL_ARRAY_BUFFER, curr_array_buffer)); glsafe(glBindVertexArray(curr_vertex_array)); } void SegmentTemplate::shutdown() { + if (m_ibo_id != 0) { + glsafe(glDeleteBuffers(1, &m_ibo_id)); + m_ibo_id = 0; + } if (m_vbo_id != 0) { glsafe(glDeleteBuffers(1, &m_vbo_id)); m_vbo_id = 0; @@ -71,14 +86,15 @@ void SegmentTemplate::shutdown() void SegmentTemplate::render(size_t count) { - if (m_vao_id == 0 || m_vbo_id == 0 || count == 0) + if (m_vao_id == 0 || m_vbo_id == 0 || m_ibo_id == 0 || count == 0) return; int curr_vertex_array; glsafe(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &curr_vertex_array)); glsafe(glBindVertexArray(m_vao_id)); - glsafe(glDrawArraysInstanced(GL_TRIANGLES, 0, static_cast(VERTEX_DATA.size()), static_cast(count))); + glsafe(glDrawElementsInstanced(GL_TRIANGLES, static_cast(INDEX_DATA.size()), GL_UNSIGNED_BYTE, + nullptr, static_cast(count))); glsafe(glBindVertexArray(curr_vertex_array)); } diff --git a/src/libvgcode/src/SegmentTemplate.hpp b/src/libvgcode/src/SegmentTemplate.hpp index f104eef5d2..117ed2b678 100644 --- a/src/libvgcode/src/SegmentTemplate.hpp +++ b/src/libvgcode/src/SegmentTemplate.hpp @@ -40,6 +40,7 @@ private: // unsigned int m_vao_id{ 0 }; unsigned int m_vbo_id{ 0 }; + unsigned int m_ibo_id{ 0 }; // // Size of the data sent to gpu, in bytes. //