Skip to content

File scene_framebuffer.hpp

File List > gui > scene_framebuffer.hpp

Go to the documentation of this file

#pragma once

#include <QOpenGLContext>
#include <QOpenGLExtraFunctions>
#include <QOpenGLFramebufferObject>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <memory>

#include "gui/gl_check.hpp"

// Off-screen color+depth target for the (non-point) scene layers. It is cleared
// and fully redrawn every non-incremental frame, then composited over the
// widget background. The point cloud uses a separate PointCloudFramebuffer,
// which is the buffer that accumulates points across incremental frames.
class SceneFramebuffer {
 public:
  void ensure_size(int width, int height) {
    if (width <= 0 || height <= 0) {
      return;
    }
    if (m_fbo && m_fbo->width() == width && m_fbo->height() == height) {
      return;
    }
    QOpenGLFramebufferObjectFormat format;
    format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
    format.setInternalTextureFormat(GL_RGBA8);
    m_fbo = std::make_unique<QOpenGLFramebufferObject>(width, height, format);
  }

  bool valid() const { return m_fbo && m_fbo->isValid(); }

  void bind() const {
    if (m_fbo) {
      m_fbo->bind();
      CHECK_GL_AFTER();
    }
  }

  // QOpenGLWidget does not render to FBO 0; blit depth to its defaultFramebufferObject().
  void blit_depth_to_widget_fbo(GLuint widget_fbo, int width, int height) const {
    if (!m_fbo || widget_fbo == 0) {
      return;
    }
    auto* gl = QOpenGLContext::currentContext()->extraFunctions();
    if (!gl) {
      return;
    }
    CHECK_GL(gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo->handle()));
    CHECK_GL(gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, widget_fbo));
    CHECK_GL(gl->glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_DEPTH_BUFFER_BIT,
                                   GL_NEAREST));
    CHECK_GL(gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, widget_fbo));
  }

  void composite_to_widget_fbo(QOpenGLExtraFunctions* gl, GLuint widget_fbo, int width, int height);

  // Fallback when the compositor shader is unavailable.
  void blit_to_widget_fbo(GLuint widget_fbo, int width, int height) const {
    if (!m_fbo || widget_fbo == 0) {
      return;
    }
    auto* gl = QOpenGLContext::currentContext()->extraFunctions();
    if (!gl) {
      return;
    }
    CHECK_GL(gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo->handle()));
    CHECK_GL(gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, widget_fbo));
    CHECK_GL(gl->glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
                                   GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST));
    CHECK_GL(gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, widget_fbo));
  }

 private:
  void ensure_compositor(QOpenGLFunctions* f);

  std::unique_ptr<QOpenGLFramebufferObject> m_fbo;
  std::unique_ptr<QOpenGLShaderProgram> m_compositor_shader;
  QOpenGLVertexArrayObject m_compositor_vao;
  int m_scene_color_loc = 0;
  int m_background_color_loc = 0;
  bool m_compositor_ready = false;
};