-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathOpenGlOffscreenSurface.h
192 lines (152 loc) · 7.28 KB
/
OpenGlOffscreenSurface.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// #ifndef OPENGLOFFSCREENSURFACE_H
// #define OPENGLOFFSCREENSURFACE_H
// #pragma once
#include <QObject>
#include <QScreen>
#include <QOffscreenSurface>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QOpenGLPaintDevice>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_0>
#include <QOpenGLVersionFunctions>
#include <QOpenGLFramebufferObject>
#include <QSurfaceFormat>
#include <QWidget>
#include <QOpenGLShaderProgram>
#include <atomic>
#include <mutex>
class OpenGlOffscreenSurface
: public QOffscreenSurface
{
Q_OBJECT
public:
/// @brief Constructor. Creates a render window.
/// @param targetScreen Target screen.
/// @param size Initial size of a surface buffer.
/// this is because before the FBO and off-screen surface haven't been created.
/// By default this uses the QWindow::requestedFormat() for OpenGL context and off-screen
/// surface.
explicit OpenGlOffscreenSurface(
QScreen* targetScreen = nullptr,
const QSize& size = QSize (1, 1));
/// @brief Destructor.
virtual ~OpenGlOffscreenSurface();
/// @brief Check if the window is initialized and can be used for rendering.
/// @return Returns true if context, surface and FBO have been set up to start rendering.
bool isValid() const;
/// @brief Return the context used in this window.
/// @return The context used in this window or nullptr if it hasn't been created yet.
QOpenGLContext* context() const;
/// @brief Return the OpenGL function object that can be used the issue OpenGL commands.
/// @return The functions for the context or nullptr if it the context hasn't been created yet.
QOpenGLFunctions* functions() const;
/// @brief Return the OpenGL off-screen frame buffer object identifier.
/// @return The OpenGL off-screen frame buffer object identifier or 0 if no FBO has been created
/// yet.
/// @note This changes on every resize!
GLuint framebufferObjectHandle() const;
/// @brief Return the OpenGL off-screen frame buffer object.
/// @return The OpenGL off-screen frame buffer object or nullptr if no FBO has been created yet.
/// @note This changes on every resize!
const QOpenGLFramebufferObject* getFramebufferObject() const;
/// @brief Return the QPaintDevice for paint into it.
QPaintDevice* getPaintDevice() const;
/// @brief Return the OpenGL off-screen frame buffer object identifier.
/// @return The OpenGL off-screen frame buffer object identifier or 0 if no FBO has been created
/// yet.
void bindFramebufferObject();
/// @brief Return the current contents of the FBO.
/// @return FBO content as 32bit QImage. You might need to swap RGBA to BGRA or vice-versa.
QImage grabFramebuffer();
/// @brief Makes the OpenGL context current for rendering.
/// @note Make sure to bindFramebufferObject() if you want to render to this widgets FBO.
void makeCurrent();
/// @brief Release the OpenGL context.
void doneCurrent();
/// @brief Copy content of framebuffer to back buffer and swap buffers if the surface is
/// double-buffered.
/// If the surface is not double-buffered, the frame buffer content is blitted to the front
/// buffer.
/// If the window is not exposed, only the OpenGL pipeline is glFlush()ed so the framebuffer can
/// be read back.
void swapBuffers();
/// @brief Use bufferSize() instead size() for get a size of a surface buffer. We can't override size() as it is not virtual.
QSize bufferSize() const;
/// @brief Resize surface buffer to newSize.
void resize(const QSize& newSize);
/// @brief Resize surface buffer to size with width w and height h.
/// @param w Width.
/// @param h Height.
void resize(int w, int h);
public slots:
/// @brief Lazy update routine like QWidget::update().
void update();
/// @brief Immediately render the widget contents to framebuffer.
void render();
signals:
/// @brief Emitted when swapBuffers() was called and bufferswapping is done.
void frameSwapped();
/// @brief Emitted after a resizeEvent().
void resized();
protected:
virtual void exposeEvent(QExposeEvent* e);
virtual void resizeEvent(QResizeEvent* e);
virtual bool event(QEvent* e) override;
// virtual int metric(QPaintDevice::PaintDeviceMetric metric) const override;
/// @brief Called exactly once when the window is first exposed OR render() is called when the
/// widget is invisible.
/// @note After this the off-screen surface and FBO are available.
virtual void initializeGL() = 0;
/// @brief Called whenever the window size changes.
/// @param width New window width.
/// @param height New window height.
virtual void resizeGL(
int width,
int height) = 0;
/// @brief Called whenever the window needs to repaint itself. Override to draw OpenGL content.
/// When this function is called, the context is already current and the correct framebuffer is
/// bound.
virtual void paintGL() = 0;
// /// @brief Called whenever the window needs to repaint itself. Override to draw QPainter
// content.
// /// @brief This is called AFTER paintGL()! Only needed when painting using a QPainter.
// virtual void paintEvent(QPainter & painter) = 0;
private:
Q_DISABLE_COPY(OpenGlOffscreenSurface)
/// @brief Initialize the window.
void initializeInternal();
/// @brief Internal method that does the actual swap work, NOT using a mutex.
void swapBuffersInternal();
/// @brief Internal method that checks state and makes the context current, NOT using a mutex.
void makeCurrentInternal();
/// @brief Internal method to grab content of a specific framebuffer.
QImage grabFramebufferInternal(QOpenGLFramebufferObject* fbo);
/// @brief (Re-)allocate FBO and paint device if needed due to size changes etc.
void recreateFBOAndPaintDevice();
/// @brief False before the window was first exposed OR render() was called.
std::atomic_bool m_initialized;
/// @brief False before the overridden initializeGL() was first called.
bool m_initializedGL = false;
/// @brief True when currently a window update is pending.
std::atomic_bool m_updatePending;
/// @brief Mutex making sure not grabbing while drawing etc.
std::mutex m_mutex;
/// @brief OpenGL render context.
QOpenGLContext* m_context = nullptr;
/// @brief The OpenGL 2.1 / ES 2.0 function object that can be used the issue OpenGL commands.
QOpenGLFunctions* m_functions = nullptr;
/// @brief The OpenGL 3.0 function object that can be used the issue OpenGL commands.
QOpenGLFunctions_3_0* m_functions_3_0 = nullptr;
/// @brief OpenGL paint device for painting with a QPainter.
QOpenGLPaintDevice* m_paintDevice = nullptr;
/// @brief Background FBO for off-screen rendering when the window is not exposed.
QOpenGLFramebufferObject* m_fbo = nullptr;
/// @brief Background FBO resolving a multi sampling frame buffer in m_fbo to a frame buffer
/// that can be grabbed to a QImage.
QOpenGLFramebufferObject* m_resolvedFbo = nullptr;
/// @brief Shader used for blitting m_fbo to screen if glBlitFrameBuffer is not available.
QOpenGLShaderProgram* m_blitShader;
QSize m_size;
};
// #endif // OPENGLOFFSCREENSURFACE_H