// // Copyright 2015 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // FramebufferGL.cpp: Implements the class methods for FramebufferGL. #include "libANGLE/renderer/gl/FramebufferGL.h" #include "common/BitSetIterator.h" #include "common/debug.h" #include "libANGLE/ContextState.h" #include "libANGLE/State.h" #include "libANGLE/FramebufferAttachment.h" #include "libANGLE/angletypes.h" #include "libANGLE/formatutils.h" #include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/gl/BlitGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/RenderbufferGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/WorkaroundsGL.h" #include "libANGLE/renderer/gl/formatutilsgl.h" #include "libANGLE/renderer/gl/renderergl_utils.h" #include "platform/Platform.h" using namespace gl; using angle::CheckedNumeric; namespace rx { FramebufferGL::FramebufferGL(const FramebufferState &state, const FunctionsGL *functions, StateManagerGL *stateManager, const WorkaroundsGL &workarounds, BlitGL *blitter, bool isDefault) : FramebufferImpl(state), mFunctions(functions), mStateManager(stateManager), mWorkarounds(workarounds), mBlitter(blitter), mFramebufferID(0), mIsDefault(isDefault) { if (!mIsDefault) { mFunctions->genFramebuffers(1, &mFramebufferID); } } FramebufferGL::FramebufferGL(GLuint id, const FramebufferState &state, const FunctionsGL *functions, const WorkaroundsGL &workarounds, BlitGL *blitter, StateManagerGL *stateManager) : FramebufferImpl(state), mFunctions(functions), mStateManager(stateManager), mWorkarounds(workarounds), mBlitter(blitter), mFramebufferID(id), mIsDefault(true) { } FramebufferGL::~FramebufferGL() { mStateManager->deleteFramebuffer(mFramebufferID); mFramebufferID = 0; } static void BindFramebufferAttachment(const FunctionsGL *functions, GLenum attachmentPoint, const FramebufferAttachment *attachment) { if (attachment) { if (attachment->type() == GL_TEXTURE) { const Texture *texture = attachment->getTexture(); const TextureGL *textureGL = GetImplAs(texture); if (texture->getTarget() == GL_TEXTURE_2D) { functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textureGL->getTextureID(), attachment->mipLevel()); } else if (texture->getTarget() == GL_TEXTURE_CUBE_MAP) { functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, attachment->cubeMapFace(), textureGL->getTextureID(), attachment->mipLevel()); } else if (texture->getTarget() == GL_TEXTURE_2D_ARRAY || texture->getTarget() == GL_TEXTURE_3D) { functions->framebufferTextureLayer(GL_FRAMEBUFFER, attachmentPoint, textureGL->getTextureID(), attachment->mipLevel(), attachment->layer()); } else { UNREACHABLE(); } } else if (attachment->type() == GL_RENDERBUFFER) { const Renderbuffer *renderbuffer = attachment->getRenderbuffer(); const RenderbufferGL *renderbufferGL = GetImplAs(renderbuffer); functions->framebufferRenderbuffer(GL_FRAMEBUFFER, attachmentPoint, GL_RENDERBUFFER, renderbufferGL->getRenderbufferID()); } else { UNREACHABLE(); } } else { // Unbind this attachment functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, 0, 0); } } Error FramebufferGL::discard(size_t count, const GLenum *attachments) { UNIMPLEMENTED(); return Error(GL_INVALID_OPERATION); } Error FramebufferGL::invalidate(size_t count, const GLenum *attachments) { // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is available. if (mFunctions->invalidateFramebuffer) { mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->invalidateFramebuffer(GL_FRAMEBUFFER, static_cast(count), attachments); } return Error(GL_NO_ERROR); } Error FramebufferGL::invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) { // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is available. if (mFunctions->invalidateSubFramebuffer) { mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->invalidateSubFramebuffer(GL_FRAMEBUFFER, static_cast(count), attachments, area.x, area.y, area.width, area.height); } return Error(GL_NO_ERROR); } Error FramebufferGL::clear(ContextImpl *context, GLbitfield mask) { syncClearState(mask); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clear(mask); return Error(GL_NO_ERROR); } Error FramebufferGL::clearBufferfv(ContextImpl *context, GLenum buffer, GLint drawbuffer, const GLfloat *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferfv(buffer, drawbuffer, values); return Error(GL_NO_ERROR); } Error FramebufferGL::clearBufferuiv(ContextImpl *context, GLenum buffer, GLint drawbuffer, const GLuint *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferuiv(buffer, drawbuffer, values); return Error(GL_NO_ERROR); } Error FramebufferGL::clearBufferiv(ContextImpl *context, GLenum buffer, GLint drawbuffer, const GLint *values) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferiv(buffer, drawbuffer, values); return Error(GL_NO_ERROR); } Error FramebufferGL::clearBufferfi(ContextImpl *context, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { syncClearBufferState(buffer, drawbuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); mFunctions->clearBufferfi(buffer, drawbuffer, depth, stencil); return Error(GL_NO_ERROR); } GLenum FramebufferGL::getImplementationColorReadFormat() const { const auto *readAttachment = mState.getReadAttachment(); const Format &format = readAttachment->getFormat(); return format.info->getReadPixelsFormat(); } GLenum FramebufferGL::getImplementationColorReadType() const { const auto *readAttachment = mState.getReadAttachment(); const Format &format = readAttachment->getFormat(); return format.info->getReadPixelsType(); } Error FramebufferGL::readPixels(ContextImpl *context, const gl::Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const { // TODO: don't sync the pixel pack state here once the dirty bits contain the pixel pack buffer // binding const PixelPackState &packState = context->getGLState().getPackState(); mStateManager->setPixelPackState(packState); nativegl::ReadPixelsFormat readPixelsFormat = nativegl::GetReadPixelsFormat(mFunctions, mWorkarounds, format, type); GLenum readFormat = readPixelsFormat.format; GLenum readType = readPixelsFormat.type; mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID); if (mWorkarounds.packOverlappingRowsSeparatelyPackBuffer && packState.pixelBuffer.get() && packState.rowLength != 0 && packState.rowLength < area.width) { return readPixelsRowByRowWorkaround(area, readFormat, readType, packState, pixels); } if (mWorkarounds.packLastRowSeparatelyForPaddingInclusion) { gl::Extents size(area.width, area.height, 1); bool apply; ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(size, packState, readFormat, readType, false, pixels), apply); if (apply) { return readPixelsPaddingWorkaround(area, readFormat, readType, packState, pixels); } } mFunctions->readPixels(area.x, area.y, area.width, area.height, readFormat, readType, pixels); return gl::NoError(); } Error FramebufferGL::blit(ContextImpl *context, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea, GLbitfield mask, GLenum filter) { const Framebuffer *sourceFramebuffer = context->getGLState().getReadFramebuffer(); const Framebuffer *destFramebuffer = context->getGLState().getDrawFramebuffer(); const FramebufferAttachment *colorReadAttachment = sourceFramebuffer->getReadColorbuffer(); GLsizei readAttachmentSamples = colorReadAttachment->getSamples(); bool needManualColorBlit = false; // TODO(cwallez) when the filter is LINEAR and both source and destination are SRGB, we // could avoid doing a manual blit. // Prior to OpenGL 4.4 BlitFramebuffer (section 18.3.1 of GL 4.3 core profile) reads: // When values are taken from the read buffer, no linearization is performed, even // if the format of the buffer is SRGB. // Starting from OpenGL 4.4 (section 18.3.1) it reads: // When values are taken from the read buffer, if FRAMEBUFFER_SRGB is enabled and the // value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING for the framebuffer attachment // corresponding to the read buffer is SRGB, the red, green, and blue components are // converted from the non-linear sRGB color space according [...]. { bool sourceSRGB = colorReadAttachment != nullptr && colorReadAttachment->getColorEncoding() == GL_SRGB; needManualColorBlit = needManualColorBlit || (sourceSRGB && mFunctions->isAtMostGL(gl::Version(4, 3))); } // Prior to OpenGL 4.2 BlitFramebuffer (section 4.3.2 of GL 4.1 core profile) reads: // Blit operations bypass the fragment pipeline. The only fragment operations which // affect a blit are the pixel ownership test and scissor test. // Starting from OpenGL 4.2 (section 4.3.2) it reads: // When values are written to the draw buffers, blit operations bypass the fragment // pipeline. The only fragment operations which affect a blit are the pixel ownership // test, the scissor test and sRGB conversion. if (!needManualColorBlit) { bool destSRGB = false; for (size_t i = 0; i < destFramebuffer->getDrawbufferStateCount(); ++i) { const FramebufferAttachment *attachment = destFramebuffer->getDrawBuffer(i); if (attachment && attachment->getColorEncoding() == GL_SRGB) { destSRGB = true; break; } } needManualColorBlit = needManualColorBlit || (destSRGB && mFunctions->isAtMostGL(gl::Version(4, 1))); } // Enable FRAMEBUFFER_SRGB if needed mStateManager->setFramebufferSRGBEnabledForFramebuffer(true, this); GLenum blitMask = mask; if (needManualColorBlit && (mask & GL_COLOR_BUFFER_BIT) && readAttachmentSamples <= 1) { ANGLE_TRY(mBlitter->blitColorBufferWithShader(sourceFramebuffer, destFramebuffer, sourceArea, destArea, filter)); blitMask &= ~GL_COLOR_BUFFER_BIT; } if (blitMask == 0) { return gl::NoError(); } const FramebufferGL *sourceFramebufferGL = GetImplAs(sourceFramebuffer); mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID); mFunctions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(), destArea.x, destArea.y, destArea.x1(), destArea.y1(), blitMask, filter); return gl::NoError(); } bool FramebufferGL::checkStatus() const { mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); GLenum status = mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { ANGLEPlatformCurrent()->logWarning("GL framebuffer returned incomplete."); } return (status == GL_FRAMEBUFFER_COMPLETE); } void FramebufferGL::syncState(const Framebuffer::DirtyBits &dirtyBits) { // Don't need to sync state for the default FBO. if (mIsDefault) { return; } mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); for (auto dirtyBit : angle::IterateBitSet(dirtyBits)) { switch (dirtyBit) { case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: BindFramebufferAttachment(mFunctions, GL_DEPTH_ATTACHMENT, mState.getDepthAttachment()); break; case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: BindFramebufferAttachment(mFunctions, GL_STENCIL_ATTACHMENT, mState.getStencilAttachment()); break; case Framebuffer::DIRTY_BIT_DRAW_BUFFERS: { const auto &drawBuffers = mState.getDrawBufferStates(); mFunctions->drawBuffers(static_cast(drawBuffers.size()), drawBuffers.data()); break; } case Framebuffer::DIRTY_BIT_READ_BUFFER: mFunctions->readBuffer(mState.getReadBufferState()); break; default: { ASSERT(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 && dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); size_t index = static_cast(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); BindFramebufferAttachment(mFunctions, static_cast(GL_COLOR_ATTACHMENT0 + index), mState.getColorAttachment(index)); break; } } } } GLuint FramebufferGL::getFramebufferID() const { return mFramebufferID; } bool FramebufferGL::isDefault() const { return mIsDefault; } void FramebufferGL::syncClearState(GLbitfield mask) { if (mFunctions->standard == STANDARD_GL_DESKTOP) { if (mWorkarounds.doesSRGBClearsOnLinearFramebufferAttachments && (mask & GL_COLOR_BUFFER_BIT) != 0 && !mIsDefault) { bool hasSRGBAttachment = false; for (const auto &attachment : mState.getColorAttachments()) { if (attachment.isAttached() && attachment.getColorEncoding() == GL_SRGB) { hasSRGBAttachment = true; break; } } mStateManager->setFramebufferSRGBEnabled(hasSRGBAttachment); } else { mStateManager->setFramebufferSRGBEnabled(!mIsDefault); } } } void FramebufferGL::syncClearBufferState(GLenum buffer, GLint drawBuffer) { if (mFunctions->standard == STANDARD_GL_DESKTOP) { if (mWorkarounds.doesSRGBClearsOnLinearFramebufferAttachments && buffer == GL_COLOR && !mIsDefault) { // If doing a clear on a color buffer, set SRGB blend enabled only if the color buffer // is an SRGB format. const auto &drawbufferState = mState.getDrawBufferStates(); const auto &colorAttachments = mState.getColorAttachments(); const FramebufferAttachment *attachment = nullptr; if (drawbufferState[drawBuffer] >= GL_COLOR_ATTACHMENT0 && drawbufferState[drawBuffer] < GL_COLOR_ATTACHMENT0 + colorAttachments.size()) { size_t attachmentIdx = static_cast(drawbufferState[drawBuffer] - GL_COLOR_ATTACHMENT0); attachment = &colorAttachments[attachmentIdx]; } if (attachment != nullptr) { mStateManager->setFramebufferSRGBEnabled(attachment->getColorEncoding() == GL_SRGB); } } else { mStateManager->setFramebufferSRGBEnabled(!mIsDefault); } } } gl::Error FramebufferGL::readPixelsRowByRowWorkaround(const gl::Rectangle &area, GLenum format, GLenum type, const gl::PixelPackState &pack, GLvoid *pixels) const { intptr_t offset = reinterpret_cast(pixels); const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type)); GLuint rowBytes = 0; ANGLE_TRY_RESULT(glFormat.computeRowPitch(area.width, pack.alignment, pack.rowLength), rowBytes); GLuint skipBytes = 0; ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, 0, pack, false), skipBytes); gl::PixelPackState directPack; directPack.pixelBuffer = pack.pixelBuffer; directPack.alignment = 1; mStateManager->setPixelPackState(directPack); directPack.pixelBuffer.set(nullptr); offset += skipBytes; for (GLint row = 0; row < area.height; ++row) { mFunctions->readPixels(area.x, row + area.y, area.width, 1, format, type, reinterpret_cast(offset)); offset += row * rowBytes; } return gl::NoError(); } gl::Error FramebufferGL::readPixelsPaddingWorkaround(const gl::Rectangle &area, GLenum format, GLenum type, const gl::PixelPackState &pack, GLvoid *pixels) const { const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type)); GLuint rowBytes = 0; ANGLE_TRY_RESULT(glFormat.computeRowPitch(area.width, pack.alignment, pack.rowLength), rowBytes); GLuint skipBytes = 0; ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, 0, pack, false), skipBytes); // Get all by the last row if (area.height > 1) { mFunctions->readPixels(area.x, area.y, area.width, area.height - 1, format, type, pixels); } // Get the last row manually gl::PixelPackState directPack; directPack.pixelBuffer = pack.pixelBuffer; directPack.alignment = 1; mStateManager->setPixelPackState(directPack); directPack.pixelBuffer.set(nullptr); intptr_t lastRowOffset = reinterpret_cast(pixels) + skipBytes + (area.height - 1) * rowBytes; mFunctions->readPixels(area.x, area.y + area.height - 1, area.width, 1, format, type, reinterpret_cast(lastRowOffset)); return gl::NoError(); } } // namespace rx