From 7328150c8ecb3ac86d924e48da51a07adf8c256e Mon Sep 17 00:00:00 2001 From: Sean Curtis Date: Tue, 14 Jul 2026 11:29:58 -0700 Subject: [PATCH] Upgrade RenderEngineGl's image readback Using glReadPixels is much faster than glTextTextureImage (the latter incurs a number of pieces of overhead that are unnecessary when all we want to do is read back from an uncompressed frame buffer). --- .../render_gl/internal_render_engine_gl.cc | 27 ++++++++++++------- .../render_gl/internal_render_engine_gl.h | 4 +-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/geometry/render_gl/internal_render_engine_gl.cc b/geometry/render_gl/internal_render_engine_gl.cc index f193536f288c..39a7b6276699 100644 --- a/geometry/render_gl/internal_render_engine_gl.cc +++ b/geometry/render_gl/internal_render_engine_gl.cc @@ -1106,8 +1106,11 @@ void RenderEngineGl::DoRenderColorImage(const ColorRenderCamera& camera, // the front buffer; reversing the order means the image we've just rendered // wouldn't be visible. SetWindowVisibility(camera.core(), camera.show_window(), render_target); - glGetTextureImage(render_target.value_texture, 0, GL_RGBA, GL_UNSIGNED_BYTE, - color_image_out->size(), color_image_out->at(0, 0)); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glReadPixels(0, 0, color_image_out->width(), color_image_out->height(), + GL_RGBA, GL_UNSIGNED_BYTE, color_image_out->at(0, 0)); } void RenderEngineGl::DoRenderDepthImage(const DepthRenderCamera& camera, @@ -1139,9 +1142,11 @@ void RenderEngineGl::DoRenderDepthImage(const DepthRenderCamera& camera, shader_program.Unuse(); } - glGetTextureImage(render_target.value_texture, 0, GL_RED, GL_FLOAT, - depth_image_out->size() * sizeof(GLfloat), - depth_image_out->at(0, 0)); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glReadPixels(0, 0, depth_image_out->width(), depth_image_out->height(), + GL_RED, GL_FLOAT, depth_image_out->at(0, 0)); } void RenderEngineGl::DoRenderLabelImage(const ColorRenderCamera& camera, @@ -1182,7 +1187,7 @@ void RenderEngineGl::DoRenderLabelImage(const ColorRenderCamera& camera, // buffer texture consisting of a single-channel, 16-bit, signed int (to match // the underlying RenderLabel value). Doing so would allow us to render labels // directly and eliminate this additional pass. - GetLabelImage(label_image_out, render_target); + GetLabelImage(label_image_out); } std::string RenderEngineGl::DoGetParameterYaml() const { @@ -2268,11 +2273,13 @@ RenderTarget RenderEngineGl::CreateRenderTarget(const RenderCameraCore& camera, return target; } -void RenderEngineGl::GetLabelImage(ImageLabel16I* label_image_out, - const RenderTarget& target) const { +void RenderEngineGl::GetLabelImage(ImageLabel16I* label_image_out) const { ImageRgba8U image(label_image_out->width(), label_image_out->height()); - glGetTextureImage(target.value_texture, 0, GL_RGBA, GL_UNSIGNED_BYTE, - image.size() * sizeof(GLubyte), image.at(0, 0)); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glReadPixels(0, 0, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE, + image.at(0, 0)); for (int y = 0; y < image.height(); ++y) { for (int x = 0; x < image.width(); ++x) { *label_image_out->at(x, y) = RenderEngine::MakeLabelFromRgb( diff --git a/geometry/render_gl/internal_render_engine_gl.h b/geometry/render_gl/internal_render_engine_gl.h index f0fa938f2c8b..c567086bfbac 100644 --- a/geometry/render_gl/internal_render_engine_gl.h +++ b/geometry/render_gl/internal_render_engine_gl.h @@ -279,8 +279,8 @@ class RenderEngineGl final : public render::RenderEngine, private ShapeReifier { // Obtains the label image rendered from a specific object pose. This is // slower than it has to be because it does per-pixel processing on the CPU. // TODO(SeanCurtis-TRI): Figure out how to do all of this directly on the GPU. - void GetLabelImage(drake::systems::sensors::ImageLabel16I* label_image_out, - const RenderTarget& target) const; + void GetLabelImage( + drake::systems::sensors::ImageLabel16I* label_image_out) const; // Acquires the render target for the given camera. "Acquiring" the render // target guarantees that the target will be ready for receiving OpenGL