File indexing completed on 2024-11-10 04:56:53
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2006-2007 Rivo Laks <rivolaks@hot.ee> 0006 SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org> 0007 SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 #include "glframebuffer.h" 0012 #include "core/rendertarget.h" 0013 #include "core/renderviewport.h" 0014 #include "glplatform.h" 0015 #include "gltexture.h" 0016 #include "glutils.h" 0017 #include "utils/common.h" 0018 0019 namespace KWin 0020 { 0021 0022 void GLFramebuffer::initStatic() 0023 { 0024 if (GLPlatform::instance()->isGLES()) { 0025 s_supported = true; 0026 s_supportsPackedDepthStencil = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_OES_packed_depth_stencil")); 0027 s_supportsDepth24 = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_OES_depth24")); 0028 s_blitSupported = hasGLVersion(3, 0); 0029 } else { 0030 s_supported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_object")); 0031 s_supportsPackedDepthStencil = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_packed_depth_stencil")); 0032 s_blitSupported = hasGLVersion(3, 0) || hasGLExtension(QByteArrayLiteral("GL_ARB_framebuffer_object")) || hasGLExtension(QByteArrayLiteral("GL_EXT_framebuffer_blit")); 0033 } 0034 } 0035 0036 void GLFramebuffer::cleanup() 0037 { 0038 Q_ASSERT(s_fbos.isEmpty()); 0039 s_supported = false; 0040 s_blitSupported = false; 0041 } 0042 0043 bool GLFramebuffer::blitSupported() 0044 { 0045 return s_blitSupported; 0046 } 0047 0048 GLFramebuffer *GLFramebuffer::currentFramebuffer() 0049 { 0050 return s_fbos.isEmpty() ? nullptr : s_fbos.top(); 0051 } 0052 0053 void GLFramebuffer::pushFramebuffer(GLFramebuffer *fbo) 0054 { 0055 fbo->bind(); 0056 s_fbos.push(fbo); 0057 } 0058 0059 GLFramebuffer *GLFramebuffer::popFramebuffer() 0060 { 0061 GLFramebuffer *ret = s_fbos.pop(); 0062 if (!s_fbos.isEmpty()) { 0063 s_fbos.top()->bind(); 0064 } else { 0065 glBindFramebuffer(GL_FRAMEBUFFER, 0); 0066 } 0067 0068 return ret; 0069 } 0070 0071 GLFramebuffer::GLFramebuffer() 0072 : m_colorAttachment(nullptr) 0073 { 0074 } 0075 0076 static QString formatFramebufferStatus(GLenum status) 0077 { 0078 switch (status) { 0079 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0080 // An attachment is the wrong type / is invalid / has 0 width or height 0081 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); 0082 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0083 // There are no images attached to the framebuffer 0084 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); 0085 case GL_FRAMEBUFFER_UNSUPPORTED: 0086 // A format or the combination of formats of the attachments is unsupported 0087 return QStringLiteral("GL_FRAMEBUFFER_UNSUPPORTED"); 0088 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 0089 // Not all attached images have the same width and height 0090 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"); 0091 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 0092 // The color attachments don't have the same format 0093 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"); 0094 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT: 0095 // The attachments don't have the same number of samples 0096 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"); 0097 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: 0098 // The draw buffer is missing 0099 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); 0100 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: 0101 // The read buffer is missing 0102 return QStringLiteral("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); 0103 default: 0104 return QStringLiteral("Unknown (0x") + QString::number(status, 16) + QStringLiteral(")"); 0105 } 0106 } 0107 0108 GLFramebuffer::GLFramebuffer(GLTexture *colorAttachment, Attachment attachment) 0109 : m_size(colorAttachment->size()) 0110 , m_colorAttachment(colorAttachment) 0111 { 0112 if (!s_supported) { 0113 qCCritical(KWIN_OPENGL) << "Framebuffer objects aren't supported!"; 0114 return; 0115 } 0116 0117 GLuint prevFbo = 0; 0118 if (const GLFramebuffer *current = currentFramebuffer()) { 0119 prevFbo = current->handle(); 0120 } 0121 0122 glGenFramebuffers(1, &m_handle); 0123 glBindFramebuffer(GL_FRAMEBUFFER, m_handle); 0124 0125 initColorAttachment(colorAttachment); 0126 if (attachment == Attachment::CombinedDepthStencil) { 0127 initDepthStencilAttachment(); 0128 } 0129 0130 const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 0131 glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); 0132 0133 if (status != GL_FRAMEBUFFER_COMPLETE) { 0134 // We have an incomplete framebuffer, consider it invalid 0135 qCCritical(KWIN_OPENGL) << "Invalid framebuffer status: " << formatFramebufferStatus(status); 0136 glDeleteFramebuffers(1, &m_handle); 0137 return; 0138 } 0139 0140 m_valid = true; 0141 } 0142 0143 GLFramebuffer::GLFramebuffer(GLuint handle, const QSize &size) 0144 : m_handle(handle) 0145 , m_size(size) 0146 , m_valid(true) 0147 , m_foreign(true) 0148 , m_colorAttachment(nullptr) 0149 { 0150 } 0151 0152 GLFramebuffer::~GLFramebuffer() 0153 { 0154 if (!m_foreign && m_valid) { 0155 glDeleteFramebuffers(1, &m_handle); 0156 } 0157 if (m_depthBuffer) { 0158 glDeleteRenderbuffers(1, &m_depthBuffer); 0159 } 0160 if (m_stencilBuffer && m_stencilBuffer != m_depthBuffer) { 0161 glDeleteRenderbuffers(1, &m_stencilBuffer); 0162 } 0163 } 0164 0165 bool GLFramebuffer::bind() 0166 { 0167 if (!valid()) { 0168 qCCritical(KWIN_OPENGL) << "Can't enable invalid framebuffer object!"; 0169 return false; 0170 } 0171 0172 glBindFramebuffer(GL_FRAMEBUFFER, handle()); 0173 glViewport(0, 0, m_size.width(), m_size.height()); 0174 0175 return true; 0176 } 0177 0178 void GLFramebuffer::initColorAttachment(GLTexture *colorAttachment) 0179 { 0180 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0181 colorAttachment->target(), colorAttachment->texture(), 0); 0182 } 0183 0184 void GLFramebuffer::initDepthStencilAttachment() 0185 { 0186 GLuint buffer = 0; 0187 0188 // Try to attach a depth/stencil combined attachment. 0189 if (s_supportsPackedDepthStencil) { 0190 glGenRenderbuffers(1, &buffer); 0191 glBindRenderbuffer(GL_RENDERBUFFER, buffer); 0192 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_size.width(), m_size.height()); 0193 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer); 0194 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer); 0195 0196 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 0197 glDeleteRenderbuffers(1, &buffer); 0198 } else { 0199 m_depthBuffer = buffer; 0200 m_stencilBuffer = buffer; 0201 return; 0202 } 0203 } 0204 0205 // Try to attach a depth attachment separately. 0206 GLenum depthFormat; 0207 if (GLPlatform::instance()->isGLES()) { 0208 if (s_supportsDepth24) { 0209 depthFormat = GL_DEPTH_COMPONENT24; 0210 } else { 0211 depthFormat = GL_DEPTH_COMPONENT16; 0212 } 0213 } else { 0214 depthFormat = GL_DEPTH_COMPONENT; 0215 } 0216 0217 glGenRenderbuffers(1, &buffer); 0218 glBindRenderbuffer(GL_RENDERBUFFER, buffer); 0219 glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, m_size.width(), m_size.height()); 0220 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer); 0221 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 0222 glDeleteRenderbuffers(1, &buffer); 0223 } else { 0224 m_depthBuffer = buffer; 0225 } 0226 0227 // Try to attach a stencil attachment separately. 0228 GLenum stencilFormat; 0229 if (GLPlatform::instance()->isGLES()) { 0230 stencilFormat = GL_STENCIL_INDEX8; 0231 } else { 0232 stencilFormat = GL_STENCIL_INDEX; 0233 } 0234 0235 glGenRenderbuffers(1, &buffer); 0236 glBindRenderbuffer(GL_RENDERBUFFER, buffer); 0237 glRenderbufferStorage(GL_RENDERBUFFER, stencilFormat, m_size.width(), m_size.height()); 0238 glFramebufferRenderbuffer(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer); 0239 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 0240 glDeleteRenderbuffers(1, &buffer); 0241 } else { 0242 m_stencilBuffer = buffer; 0243 } 0244 } 0245 0246 void GLFramebuffer::blitFromFramebuffer(const QRect &source, const QRect &destination, GLenum filter, bool flipX, bool flipY) 0247 { 0248 if (!valid()) { 0249 return; 0250 } 0251 0252 const GLFramebuffer *top = currentFramebuffer(); 0253 GLFramebuffer::pushFramebuffer(this); 0254 0255 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, handle()); 0256 glBindFramebuffer(GL_READ_FRAMEBUFFER, top->handle()); 0257 0258 const QRect s = source.isNull() ? QRect(QPoint(0, 0), top->size()) : source; 0259 const QRect d = destination.isNull() ? QRect(QPoint(0, 0), size()) : destination; 0260 0261 GLuint srcX0 = s.x(); 0262 GLuint srcY0 = top->size().height() - (s.y() + s.height()); 0263 GLuint srcX1 = s.x() + s.width(); 0264 GLuint srcY1 = top->size().height() - s.y(); 0265 if (flipX) { 0266 std::swap(srcX0, srcX1); 0267 } 0268 if (flipY) { 0269 std::swap(srcY0, srcY1); 0270 } 0271 0272 const GLuint dstX0 = d.x(); 0273 const GLuint dstY0 = m_size.height() - (d.y() + d.height()); 0274 const GLuint dstX1 = d.x() + d.width(); 0275 const GLuint dstY1 = m_size.height() - d.y(); 0276 0277 glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, GL_COLOR_BUFFER_BIT, filter); 0278 0279 GLFramebuffer::popFramebuffer(); 0280 } 0281 0282 bool GLFramebuffer::blitFromRenderTarget(const RenderTarget &sourceRenderTarget, const RenderViewport &sourceViewport, const QRect &source, const QRect &destination) 0283 { 0284 OutputTransform transform = sourceRenderTarget.texture() ? sourceRenderTarget.texture()->contentTransform() : OutputTransform(); 0285 0286 // TODO: Also blit if rotated 180 degrees, it's equivalent to flipping both x and y axis 0287 const bool normal = transform == OutputTransform::Normal; 0288 const bool mirrorX = transform == OutputTransform::FlipX; 0289 const bool mirrorY = transform == OutputTransform::FlipY; 0290 if ((normal || mirrorX || mirrorY) && blitSupported()) { 0291 // either no transformation or flipping only 0292 blitFromFramebuffer(sourceViewport.mapToRenderTarget(source), destination, GL_LINEAR, mirrorX, mirrorY); 0293 return true; 0294 } else { 0295 const auto texture = sourceRenderTarget.texture(); 0296 if (!texture) { 0297 // rotations aren't possible without a texture 0298 return false; 0299 } 0300 0301 GLFramebuffer::pushFramebuffer(this); 0302 0303 QMatrix4x4 mat; 0304 mat.ortho(QRectF(QPointF(), size())); 0305 // GLTexture::render renders with origin (0, 0), move it to the correct place 0306 mat.translate(destination.x(), destination.y()); 0307 0308 ShaderBinder binder(ShaderTrait::MapTexture); 0309 binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mat); 0310 0311 texture->render(sourceViewport.mapToRenderTargetTexture(source), infiniteRegion(), destination.size(), 1); 0312 0313 GLFramebuffer::popFramebuffer(); 0314 return true; 0315 } 0316 } 0317 0318 GLTexture *GLFramebuffer::colorAttachment() const 0319 { 0320 return m_colorAttachment; 0321 } 0322 0323 }