KWin
Loading...
Searching...
No Matches
glframebuffer.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee>
6 SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
7 SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11#include "glframebuffer.h"
12#include "core/rendertarget.h"
13#include "core/renderviewport.h"
14#include "glplatform.h"
15#include "gltexture.h"
16#include "glutils.h"
17#include "utils/common.h"
18
19namespace KWin
20{
21
23{
24 if (GLPlatform::instance()->isGLES()) {
25 s_supported = true;
26 s_supportsPackedDepthStencil = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_OES_packed_depth_stencil"));
27 s_supportsDepth24 = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_OES_depth24"));
28 s_blitSupported = hasGLVersion(3, 0);
29 } else {
30 s_supported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object"));
31 s_supportsPackedDepthStencil = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_packed_depth_stencil"));
32 s_blitSupported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_blit"));
33 }
34}
35
36void GLFramebuffer::cleanup()
37{
38 Q_ASSERT(s_fbos.isEmpty());
39 s_supported = false;
40 s_blitSupported = false;
41}
42
44{
45 return s_blitSupported;
46}
47
49{
50 return s_fbos.isEmpty() ? nullptr : s_fbos.top();
51}
52
54{
55 fbo->bind();
56 s_fbos.push(fbo);
57}
58
60{
61 GLFramebuffer *ret = s_fbos.pop();
62 if (!s_fbos.isEmpty()) {
63 s_fbos.top()->bind();
64 } else {
65 glBindFramebuffer(GL_FRAMEBUFFER, 0);
66 }
67
68 return ret;
69}
70
72 : m_colorAttachment(nullptr)
73{
74}
75
76static QString formatFramebufferStatus(GLenum status)
77{
78 switch (status) {
79 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
80 // An attachment is the wrong type / is invalid / has 0 width or height
81 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
82 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
83 // There are no images attached to the framebuffer
84 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
85 case GL_FRAMEBUFFER_UNSUPPORTED:
86 // A format or the combination of formats of the attachments is unsupported
87 return QStringLiteral("GL_FRAMEBUFFER_UNSUPPORTED");
88 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
89 // Not all attached images have the same width and height
90 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT");
91 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
92 // The color attachments don't have the same format
93 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT");
94 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
95 // The attachments don't have the same number of samples
96 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE");
97 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
98 // The draw buffer is missing
99 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
100 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
101 // The read buffer is missing
102 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
103 default:
104 return QStringLiteral("Unknown (0x") + QString::number(status, 16) + QStringLiteral(")");
105 }
106}
107
109 : m_size(colorAttachment->size())
110 , m_colorAttachment(colorAttachment)
111{
112 if (!s_supported) {
113 qCCritical(KWIN_OPENGL) << "Framebuffer objects aren't supported!";
114 return;
115 }
116
117 GLuint prevFbo = 0;
118 if (const GLFramebuffer *current = currentFramebuffer()) {
119 prevFbo = current->handle();
120 }
121
122 glGenFramebuffers(1, &m_handle);
123 glBindFramebuffer(GL_FRAMEBUFFER, m_handle);
124
126 if (attachment == Attachment::CombinedDepthStencil) {
128 }
129
130 const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
131 glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
132
133 if (status != GL_FRAMEBUFFER_COMPLETE) {
134 // We have an incomplete framebuffer, consider it invalid
135 qCCritical(KWIN_OPENGL) << "Invalid framebuffer status: " << formatFramebufferStatus(status);
136 glDeleteFramebuffers(1, &m_handle);
137 return;
138 }
139
140 m_valid = true;
141}
142
143GLFramebuffer::GLFramebuffer(GLuint handle, const QSize &size)
144 : m_handle(handle)
145 , m_size(size)
146 , m_valid(true)
147 , m_foreign(true)
148 , m_colorAttachment(nullptr)
149{
150}
151
153{
154 if (!m_foreign && m_valid) {
155 glDeleteFramebuffers(1, &m_handle);
156 }
157 if (m_depthBuffer) {
158 glDeleteRenderbuffers(1, &m_depthBuffer);
159 }
160 if (m_stencilBuffer && m_stencilBuffer != m_depthBuffer) {
161 glDeleteRenderbuffers(1, &m_stencilBuffer);
162 }
163}
164
165bool GLFramebuffer::bind()
166{
167 if (!valid()) {
168 qCCritical(KWIN_OPENGL) << "Can't enable invalid framebuffer object!";
169 return false;
170 }
171
172 glBindFramebuffer(GL_FRAMEBUFFER, handle());
173 glViewport(0, 0, m_size.width(), m_size.height());
174
175 return true;
176}
177
179{
180 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
182}
183
185{
186 GLuint buffer = 0;
187
188 // Try to attach a depth/stencil combined attachment.
189 if (s_supportsPackedDepthStencil) {
190 glGenRenderbuffers(1, &buffer);
191 glBindRenderbuffer(GL_RENDERBUFFER, buffer);
192 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_size.width(), m_size.height());
193 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer);
194 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
195
196 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
197 glDeleteRenderbuffers(1, &buffer);
198 } else {
199 m_depthBuffer = buffer;
200 m_stencilBuffer = buffer;
201 return;
202 }
203 }
204
205 // Try to attach a depth attachment separately.
206 GLenum depthFormat;
207 if (GLPlatform::instance()->isGLES()) {
208 if (s_supportsDepth24) {
209 depthFormat = GL_DEPTH_COMPONENT24;
210 } else {
211 depthFormat = GL_DEPTH_COMPONENT16;
212 }
213 } else {
214 depthFormat = GL_DEPTH_COMPONENT;
215 }
216
217 glGenRenderbuffers(1, &buffer);
218 glBindRenderbuffer(GL_RENDERBUFFER, buffer);
219 glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, m_size.width(), m_size.height());
220 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer);
221 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
222 glDeleteRenderbuffers(1, &buffer);
223 } else {
224 m_depthBuffer = buffer;
225 }
226
227 // Try to attach a stencil attachment separately.
228 GLenum stencilFormat;
229 if (GLPlatform::instance()->isGLES()) {
230 stencilFormat = GL_STENCIL_INDEX8;
231 } else {
232 stencilFormat = GL_STENCIL_INDEX;
233 }
234
235 glGenRenderbuffers(1, &buffer);
236 glBindRenderbuffer(GL_RENDERBUFFER, buffer);
237 glRenderbufferStorage(GL_RENDERBUFFER, stencilFormat, m_size.width(), m_size.height());
238 glFramebufferRenderbuffer(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
239 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
240 glDeleteRenderbuffers(1, &buffer);
241 } else {
242 m_stencilBuffer = buffer;
243 }
244}
245
246void GLFramebuffer::blitFromFramebuffer(const QRect &source, const QRect &destination, GLenum filter, bool flipX, bool flipY)
247{
248 if (!valid()) {
249 return;
250 }
251
252 const GLFramebuffer *top = currentFramebuffer();
254
255 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, handle());
256 glBindFramebuffer(GL_READ_FRAMEBUFFER, top->handle());
257
258 const QRect s = source.isNull() ? QRect(QPoint(0, 0), top->size()) : source;
259 const QRect d = destination.isNull() ? QRect(QPoint(0, 0), size()) : destination;
260
261 GLuint srcX0 = s.x();
262 GLuint srcY0 = top->size().height() - (s.y() + s.height());
263 GLuint srcX1 = s.x() + s.width();
264 GLuint srcY1 = top->size().height() - s.y();
265 if (flipX) {
266 std::swap(srcX0, srcX1);
267 }
268 if (flipY) {
269 std::swap(srcY0, srcY1);
270 }
271
272 const GLuint dstX0 = d.x();
273 const GLuint dstY0 = m_size.height() - (d.y() + d.height());
274 const GLuint dstX1 = d.x() + d.width();
275 const GLuint dstY1 = m_size.height() - d.y();
276
277 glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, filter);
278
280}
281
282bool GLFramebuffer::blitFromRenderTarget(const RenderTarget &sourceRenderTarget, const RenderViewport &sourceViewport, const QRect &source, const QRect &destination)
283{
284 OutputTransform transform = sourceRenderTarget.texture() ? sourceRenderTarget.texture()->contentTransform() : OutputTransform();
285
286 // TODO: Also blit if rotated 180 degrees, it's equivalent to flipping both x and y axis
287 const bool normal = transform == OutputTransform::Normal;
288 const bool mirrorX = transform == OutputTransform::FlipX;
289 const bool mirrorY = transform == OutputTransform::FlipY;
290 if ((normal || mirrorX || mirrorY) && blitSupported()) {
291 // either no transformation or flipping only
292 blitFromFramebuffer(sourceViewport.mapToRenderTarget(source), destination, GL_LINEAR, mirrorX, mirrorY);
293 return true;
294 } else {
295 const auto texture = sourceRenderTarget.texture();
296 if (!texture) {
297 // rotations aren't possible without a texture
298 return false;
299 }
300
302
303 QMatrix4x4 mat;
304 mat.ortho(QRectF(QPointF(), size()));
305 // GLTexture::render renders with origin (0, 0), move it to the correct place
306 mat.translate(destination.x(), destination.y());
307
310
311 texture->render(sourceViewport.mapToRenderTargetTexture(source), infiniteRegion(), destination.size(), 1);
312
314 return true;
315 }
316}
317
319{
320 return m_colorAttachment;
321}
322
323}
OpenGL framebuffer object.
QSize size() const
static GLFramebuffer * currentFramebuffer()
void blitFromFramebuffer(const QRect &source=QRect(), const QRect &destination=QRect(), GLenum filter=GL_LINEAR, bool flipX=false, bool flipY=false)
static GLFramebuffer * popFramebuffer()
bool blitFromRenderTarget(const RenderTarget &sourceRenderTarget, const RenderViewport &sourceViewport, const QRect &source, const QRect &destination)
static void pushFramebuffer(GLFramebuffer *fbo)
static void initStatic()
static bool blitSupported()
GLTexture * colorAttachment() const
GLuint handle() const
void initColorAttachment(GLTexture *colorAttachment)
static GLPlatform * instance()
Definition glplatform.h:394
bool setUniform(const char *name, float value)
Definition glshader.cpp:301
OutputTransform contentTransform() const
GLuint texture() const
GLenum target() const
GLTexture * texture() const
QRectF mapToRenderTargetTexture(const QRectF &logicalGeometry) const
QRectF mapToRenderTarget(const QRectF &logicalGeometry) const
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
bool hasGLVersion(int major, int minor, int release)
Definition glutils.cpp:133
bool hasGLExtension(const QByteArray &extension)
Definition glutils.cpp:138