| // |
| // 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/bitset_utils.h" |
| #include "common/debug.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/FramebufferAttachment.h" |
| #include "libANGLE/State.h" |
| #include "libANGLE/angletypes.h" |
| #include "libANGLE/formatutils.h" |
| #include "libANGLE/queryconversions.h" |
| #include "libANGLE/renderer/ContextImpl.h" |
| #include "libANGLE/renderer/gl/BlitGL.h" |
| #include "libANGLE/renderer/gl/ClearMultiviewGL.h" |
| #include "libANGLE/renderer/gl/ContextGL.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 |
| { |
| |
| namespace |
| { |
| |
| 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<TextureGL>(texture); |
| |
| if (texture->getType() == TextureType::_2D || |
| texture->getType() == TextureType::_2DMultisample || |
| texture->getType() == TextureType::Rectangle) |
| { |
| functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, |
| ToGLenum(texture->getType()), |
| textureGL->getTextureID(), attachment->mipLevel()); |
| } |
| else if (texture->getType() == TextureType::CubeMap) |
| { |
| functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, |
| ToGLenum(attachment->cubeMapFace()), |
| textureGL->getTextureID(), attachment->mipLevel()); |
| } |
| else if (texture->getType() == TextureType::_2DArray || |
| texture->getType() == TextureType::_3D) |
| { |
| if (attachment->getMultiviewLayout() == GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE) |
| { |
| ASSERT(functions->framebufferTexture); |
| functions->framebufferTexture(GL_FRAMEBUFFER, attachmentPoint, |
| textureGL->getTextureID(), |
| attachment->mipLevel()); |
| } |
| else |
| { |
| 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<RenderbufferGL>(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); |
| } |
| } |
| |
| bool AreAllLayersActive(const FramebufferAttachment &attachment) |
| { |
| int baseViewIndex = attachment.getBaseViewIndex(); |
| if (baseViewIndex != 0) |
| { |
| return false; |
| } |
| const ImageIndex &imageIndex = attachment.getTextureImageIndex(); |
| int numLayers = static_cast<int>( |
| attachment.getTexture()->getDepth(imageIndex.getTarget(), imageIndex.getLevelIndex())); |
| return (attachment.getNumViews() == numLayers); |
| } |
| |
| bool RequiresMultiviewClear(const FramebufferState &state, bool scissorTestEnabled) |
| { |
| // Get one attachment and check whether all layers are attached. |
| const FramebufferAttachment *attachment = nullptr; |
| bool allTextureArraysAreFullyAttached = true; |
| for (const FramebufferAttachment &colorAttachment : state.getColorAttachments()) |
| { |
| if (colorAttachment.isAttached()) |
| { |
| if (colorAttachment.getMultiviewLayout() == GL_NONE) |
| { |
| return false; |
| } |
| attachment = &colorAttachment; |
| allTextureArraysAreFullyAttached = |
| allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment); |
| } |
| } |
| |
| const FramebufferAttachment *depthAttachment = state.getDepthAttachment(); |
| if (depthAttachment) |
| { |
| if (depthAttachment->getMultiviewLayout() == GL_NONE) |
| { |
| return false; |
| } |
| attachment = depthAttachment; |
| allTextureArraysAreFullyAttached = |
| allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment); |
| } |
| const FramebufferAttachment *stencilAttachment = state.getStencilAttachment(); |
| if (stencilAttachment) |
| { |
| if (stencilAttachment->getMultiviewLayout() == GL_NONE) |
| { |
| return false; |
| } |
| attachment = stencilAttachment; |
| allTextureArraysAreFullyAttached = |
| allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment); |
| } |
| |
| if (attachment == nullptr) |
| { |
| return false; |
| } |
| switch (attachment->getMultiviewLayout()) |
| { |
| case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE: |
| // If all layers of each texture array are active, then there is no need to issue a |
| // special multiview clear. |
| return !allTextureArraysAreFullyAttached; |
| case GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE: |
| return (scissorTestEnabled == true); |
| default: |
| UNREACHABLE(); |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| FramebufferGL::FramebufferGL(const gl::FramebufferState &data, GLuint id, bool isDefault) |
| : FramebufferImpl(data), |
| mFramebufferID(id), |
| mIsDefault(isDefault), |
| mAppliedEnabledDrawBuffers(1) |
| { |
| } |
| |
| FramebufferGL::~FramebufferGL() |
| { |
| ASSERT(mFramebufferID == 0); |
| } |
| |
| void FramebufferGL::destroy(const gl::Context *context) |
| { |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| stateManager->deleteFramebuffer(mFramebufferID); |
| mFramebufferID = 0; |
| } |
| |
| Error FramebufferGL::discard(const gl::Context *context, size_t count, const GLenum *attachments) |
| { |
| // glInvalidateFramebuffer accepts the same enums as glDiscardFramebufferEXT |
| return invalidate(context, count, attachments); |
| } |
| |
| Error FramebufferGL::invalidate(const gl::Context *context, size_t count, const GLenum *attachments) |
| { |
| const GLenum *finalAttachmentsPtr = attachments; |
| |
| std::vector<GLenum> modifiedAttachments; |
| if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments)) |
| { |
| finalAttachmentsPtr = modifiedAttachments.data(); |
| } |
| |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| // Since this function is just a hint, only call a native function if it exists. |
| if (functions->invalidateFramebuffer) |
| { |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| functions->invalidateFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count), |
| finalAttachmentsPtr); |
| } |
| else if (functions->discardFramebufferEXT) |
| { |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| functions->discardFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLsizei>(count), |
| finalAttachmentsPtr); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::invalidateSub(const gl::Context *context, |
| size_t count, |
| const GLenum *attachments, |
| const gl::Rectangle &area) |
| { |
| |
| const GLenum *finalAttachmentsPtr = attachments; |
| |
| std::vector<GLenum> modifiedAttachments; |
| if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments)) |
| { |
| finalAttachmentsPtr = modifiedAttachments.data(); |
| } |
| |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is |
| // available. |
| if (functions->invalidateSubFramebuffer) |
| { |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| functions->invalidateSubFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count), |
| finalAttachmentsPtr, area.x, area.y, area.width, |
| area.height); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::clear(const gl::Context *context, GLbitfield mask) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| syncClearState(context, mask); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled())) |
| { |
| functions->clear(mask); |
| } |
| else |
| { |
| ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context); |
| multiviewClearer->clearMultiviewFBO(mState, context->getGLState().getScissor(), |
| ClearMultiviewGL::ClearCommandType::Clear, mask, |
| GL_NONE, 0, nullptr, 0.0f, 0); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::clearBufferfv(const gl::Context *context, |
| GLenum buffer, |
| GLint drawbuffer, |
| const GLfloat *values) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| syncClearBufferState(context, buffer, drawbuffer); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled())) |
| { |
| functions->clearBufferfv(buffer, drawbuffer, values); |
| } |
| else |
| { |
| ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context); |
| multiviewClearer->clearMultiviewFBO(mState, context->getGLState().getScissor(), |
| ClearMultiviewGL::ClearCommandType::ClearBufferfv, |
| static_cast<GLbitfield>(0u), buffer, drawbuffer, |
| reinterpret_cast<const uint8_t *>(values), 0.0f, 0); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::clearBufferuiv(const gl::Context *context, |
| GLenum buffer, |
| GLint drawbuffer, |
| const GLuint *values) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| syncClearBufferState(context, buffer, drawbuffer); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled())) |
| { |
| functions->clearBufferuiv(buffer, drawbuffer, values); |
| } |
| else |
| { |
| ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context); |
| multiviewClearer->clearMultiviewFBO(mState, context->getGLState().getScissor(), |
| ClearMultiviewGL::ClearCommandType::ClearBufferuiv, |
| static_cast<GLbitfield>(0u), buffer, drawbuffer, |
| reinterpret_cast<const uint8_t *>(values), 0.0f, 0); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::clearBufferiv(const gl::Context *context, |
| GLenum buffer, |
| GLint drawbuffer, |
| const GLint *values) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| syncClearBufferState(context, buffer, drawbuffer); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled())) |
| { |
| functions->clearBufferiv(buffer, drawbuffer, values); |
| } |
| else |
| { |
| ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context); |
| multiviewClearer->clearMultiviewFBO(mState, context->getGLState().getScissor(), |
| ClearMultiviewGL::ClearCommandType::ClearBufferiv, |
| static_cast<GLbitfield>(0u), buffer, drawbuffer, |
| reinterpret_cast<const uint8_t *>(values), 0.0f, 0); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| Error FramebufferGL::clearBufferfi(const gl::Context *context, |
| GLenum buffer, |
| GLint drawbuffer, |
| GLfloat depth, |
| GLint stencil) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| syncClearBufferState(context, buffer, drawbuffer); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| if (!RequiresMultiviewClear(mState, context->getGLState().isScissorTestEnabled())) |
| { |
| functions->clearBufferfi(buffer, drawbuffer, depth, stencil); |
| } |
| else |
| { |
| ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context); |
| multiviewClearer->clearMultiviewFBO(mState, context->getGLState().getScissor(), |
| ClearMultiviewGL::ClearCommandType::ClearBufferfi, |
| static_cast<GLbitfield>(0u), buffer, drawbuffer, |
| nullptr, depth, stencil); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| GLenum FramebufferGL::getImplementationColorReadFormat(const gl::Context *context) const |
| { |
| const auto *readAttachment = mState.getReadAttachment(); |
| const Format &format = readAttachment->getFormat(); |
| return format.info->getReadPixelsFormat(); |
| } |
| |
| GLenum FramebufferGL::getImplementationColorReadType(const gl::Context *context) const |
| { |
| const auto *readAttachment = mState.getReadAttachment(); |
| const Format &format = readAttachment->getFormat(); |
| return format.info->getReadPixelsType(context->getClientVersion()); |
| } |
| |
| Error FramebufferGL::readPixels(const gl::Context *context, |
| const gl::Rectangle &origArea, |
| GLenum format, |
| GLenum type, |
| void *ptrOrOffset) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| const WorkaroundsGL &workarounds = GetWorkaroundsGL(context); |
| |
| // Clip read area to framebuffer. |
| const gl::Extents fbSize = getState().getReadAttachment()->getSize(); |
| const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height); |
| gl::Rectangle area; |
| if (!ClipRectangle(origArea, fbRect, &area)) |
| { |
| // nothing to read |
| return gl::NoError(); |
| } |
| |
| PixelPackState packState = context->getGLState().getPackState(); |
| const gl::Buffer *packBuffer = |
| context->getGLState().getTargetBuffer(gl::BufferBinding::PixelPack); |
| |
| nativegl::ReadPixelsFormat readPixelsFormat = |
| nativegl::GetReadPixelsFormat(functions, workarounds, format, type); |
| GLenum readFormat = readPixelsFormat.format; |
| GLenum readType = readPixelsFormat.type; |
| |
| stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID); |
| |
| bool useOverlappingRowsWorkaround = workarounds.packOverlappingRowsSeparatelyPackBuffer && |
| packBuffer && packState.rowLength != 0 && |
| packState.rowLength < area.width; |
| |
| GLubyte *pixels = reinterpret_cast<GLubyte *>(ptrOrOffset); |
| int leftClip = area.x - origArea.x; |
| int topClip = area.y - origArea.y; |
| if (leftClip || topClip) |
| { |
| // Adjust destination to match portion clipped off left and/or top. |
| const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(readFormat, readType); |
| |
| GLuint rowBytes = 0; |
| ANGLE_TRY_RESULT(glFormat.computeRowPitch(readType, origArea.width, packState.alignment, |
| packState.rowLength), |
| rowBytes); |
| pixels += leftClip * glFormat.pixelBytes + topClip * rowBytes; |
| } |
| |
| if (packState.rowLength == 0 && area.width != origArea.width) |
| { |
| // No rowLength was specified so it will derive from read width, but clipping changed the |
| // read width. Use the original width so we fill the user's buffer as they intended. |
| packState.rowLength = origArea.width; |
| } |
| |
| // We want to use rowLength, but that might not be supported. |
| bool cannotSetDesiredRowLength = |
| packState.rowLength && !GetImplAs<ContextGL>(context)->getNativeExtensions().packSubimage; |
| |
| gl::Error retVal = gl::NoError(); |
| if (cannotSetDesiredRowLength || useOverlappingRowsWorkaround) |
| { |
| retVal = readPixelsRowByRow(context, area, readFormat, readType, packState, pixels); |
| } |
| else |
| { |
| gl::ErrorOrResult<bool> useLastRowPaddingWorkaround = false; |
| if (workarounds.packLastRowSeparatelyForPaddingInclusion) |
| { |
| useLastRowPaddingWorkaround = ShouldApplyLastRowPaddingWorkaround( |
| gl::Extents(area.width, area.height, 1), packState, packBuffer, readFormat, |
| readType, false, pixels); |
| } |
| |
| if (useLastRowPaddingWorkaround.isError()) |
| { |
| retVal = useLastRowPaddingWorkaround.getError(); |
| } |
| else |
| { |
| retVal = readPixelsAllAtOnce(context, area, readFormat, readType, packState, pixels, |
| useLastRowPaddingWorkaround.getResult()); |
| } |
| } |
| |
| return retVal; |
| } |
| |
| Error FramebufferGL::blit(const gl::Context *context, |
| const gl::Rectangle &sourceArea, |
| const gl::Rectangle &destArea, |
| GLbitfield mask, |
| GLenum filter) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| const Framebuffer *sourceFramebuffer = context->getGLState().getReadFramebuffer(); |
| const Framebuffer *destFramebuffer = context->getGLState().getDrawFramebuffer(); |
| |
| const FramebufferAttachment *colorReadAttachment = sourceFramebuffer->getReadColorbuffer(); |
| |
| GLsizei readAttachmentSamples = 0; |
| if (colorReadAttachment != nullptr) |
| { |
| 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 && functions->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 && functions->isAtMostGL(gl::Version(4, 1))); |
| } |
| |
| // Enable FRAMEBUFFER_SRGB if needed |
| stateManager->setFramebufferSRGBEnabledForFramebuffer(context, true, this); |
| |
| GLenum blitMask = mask; |
| if (needManualColorBlit && (mask & GL_COLOR_BUFFER_BIT) && readAttachmentSamples <= 1) |
| { |
| BlitGL *blitter = GetBlitGL(context); |
| ANGLE_TRY(blitter->blitColorBufferWithShader(sourceFramebuffer, destFramebuffer, sourceArea, |
| destArea, filter)); |
| blitMask &= ~GL_COLOR_BUFFER_BIT; |
| } |
| |
| if (blitMask == 0) |
| { |
| return gl::NoError(); |
| } |
| |
| const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(sourceFramebuffer); |
| stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); |
| stateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID); |
| |
| functions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(), |
| destArea.x, destArea.y, destArea.x1(), destArea.y1(), blitMask, |
| filter); |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error FramebufferGL::getSamplePosition(const gl::Context *context, |
| size_t index, |
| GLfloat *xy) const |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| functions->getMultisamplefv(GL_SAMPLE_POSITION, static_cast<GLuint>(index), xy); |
| return gl::NoError(); |
| } |
| |
| bool FramebufferGL::checkStatus(const gl::Context *context) const |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| GLenum status = functions->checkFramebufferStatus(GL_FRAMEBUFFER); |
| if (status != GL_FRAMEBUFFER_COMPLETE) |
| { |
| WARN() << "GL framebuffer returned incomplete."; |
| } |
| return (status == GL_FRAMEBUFFER_COMPLETE); |
| } |
| |
| gl::Error FramebufferGL::syncState(const gl::Context *context, |
| const Framebuffer::DirtyBits &dirtyBits) |
| { |
| // Don't need to sync state for the default FBO. |
| if (mIsDefault) |
| { |
| return gl::NoError(); |
| } |
| |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID); |
| |
| // A pointer to one of the attachments for which the texture or the render buffer is not zero. |
| const FramebufferAttachment *attachment = nullptr; |
| |
| for (auto dirtyBit : dirtyBits) |
| { |
| switch (dirtyBit) |
| { |
| case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT: |
| { |
| const FramebufferAttachment *newAttachment = mState.getDepthAttachment(); |
| BindFramebufferAttachment(functions, GL_DEPTH_ATTACHMENT, newAttachment); |
| if (newAttachment) |
| { |
| attachment = newAttachment; |
| } |
| break; |
| } |
| case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT: |
| { |
| const FramebufferAttachment *newAttachment = mState.getStencilAttachment(); |
| BindFramebufferAttachment(functions, GL_STENCIL_ATTACHMENT, newAttachment); |
| if (newAttachment) |
| { |
| attachment = newAttachment; |
| } |
| break; |
| } |
| case Framebuffer::DIRTY_BIT_DRAW_BUFFERS: |
| { |
| const auto &drawBuffers = mState.getDrawBufferStates(); |
| functions->drawBuffers(static_cast<GLsizei>(drawBuffers.size()), |
| drawBuffers.data()); |
| mAppliedEnabledDrawBuffers = mState.getEnabledDrawBuffers(); |
| break; |
| } |
| case Framebuffer::DIRTY_BIT_READ_BUFFER: |
| functions->readBuffer(mState.getReadBufferState()); |
| break; |
| case Framebuffer::DIRTY_BIT_DEFAULT_WIDTH: |
| functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, |
| mState.getDefaultWidth()); |
| break; |
| case Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT: |
| functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, |
| mState.getDefaultHeight()); |
| break; |
| case Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES: |
| functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES, |
| mState.getDefaultSamples()); |
| break; |
| case Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS: |
| functions->framebufferParameteri( |
| GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS, |
| gl::ConvertToGLBoolean(mState.getDefaultFixedSampleLocations())); |
| break; |
| default: |
| { |
| ASSERT(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 && |
| dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX); |
| size_t index = |
| static_cast<size_t>(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); |
| const FramebufferAttachment *newAttachment = mState.getColorAttachment(index); |
| BindFramebufferAttachment( |
| functions, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + index), newAttachment); |
| if (newAttachment) |
| { |
| attachment = newAttachment; |
| } |
| break; |
| } |
| } |
| } |
| |
| if (attachment) |
| { |
| const bool isSideBySide = |
| (attachment->getMultiviewLayout() == GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE); |
| stateManager->setSideBySide(isSideBySide); |
| stateManager->setViewportOffsets(attachment->getMultiviewViewportOffsets()); |
| stateManager->updateMultiviewBaseViewLayerIndexUniform(context->getGLState().getProgram(), |
| getState()); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| GLuint FramebufferGL::getFramebufferID() const |
| { |
| return mFramebufferID; |
| } |
| |
| bool FramebufferGL::isDefault() const |
| { |
| return mIsDefault; |
| } |
| |
| void FramebufferGL::maskOutInactiveOutputDrawBuffers(const gl::Context *context, |
| GLenum binding, |
| DrawBufferMask maxSet) |
| { |
| |
| auto targetAppliedDrawBuffers = mState.getEnabledDrawBuffers() & maxSet; |
| if (mAppliedEnabledDrawBuffers != targetAppliedDrawBuffers) |
| { |
| mAppliedEnabledDrawBuffers = targetAppliedDrawBuffers; |
| |
| const auto &stateDrawBuffers = mState.getDrawBufferStates(); |
| GLsizei drawBufferCount = static_cast<GLsizei>(stateDrawBuffers.size()); |
| ASSERT(drawBufferCount <= IMPLEMENTATION_MAX_DRAW_BUFFERS); |
| |
| GLenum drawBuffers[IMPLEMENTATION_MAX_DRAW_BUFFERS]; |
| for (GLenum i = 0; static_cast<int>(i) < drawBufferCount; ++i) |
| { |
| drawBuffers[i] = targetAppliedDrawBuffers[i] ? stateDrawBuffers[i] : GL_NONE; |
| } |
| |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| stateManager->bindFramebuffer(binding, mFramebufferID); |
| functions->drawBuffers(drawBufferCount, drawBuffers); |
| } |
| } |
| |
| void FramebufferGL::syncClearState(const gl::Context *context, GLbitfield mask) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| |
| if (functions->standard == STANDARD_GL_DESKTOP) |
| { |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| const WorkaroundsGL &workarounds = GetWorkaroundsGL(context); |
| |
| if (workarounds.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; |
| } |
| } |
| |
| stateManager->setFramebufferSRGBEnabled(context, hasSRGBAttachment); |
| } |
| else |
| { |
| stateManager->setFramebufferSRGBEnabled(context, !mIsDefault); |
| } |
| } |
| } |
| |
| void FramebufferGL::syncClearBufferState(const gl::Context *context, |
| GLenum buffer, |
| GLint drawBuffer) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| |
| if (functions->standard == STANDARD_GL_DESKTOP) |
| { |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| const WorkaroundsGL &workarounds = GetWorkaroundsGL(context); |
| |
| if (workarounds.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<size_t>(drawbufferState[drawBuffer] - GL_COLOR_ATTACHMENT0); |
| attachment = &colorAttachments[attachmentIdx]; |
| } |
| |
| if (attachment != nullptr) |
| { |
| stateManager->setFramebufferSRGBEnabled(context, |
| attachment->getColorEncoding() == GL_SRGB); |
| } |
| } |
| else |
| { |
| stateManager->setFramebufferSRGBEnabled(context, !mIsDefault); |
| } |
| } |
| } |
| |
| bool FramebufferGL::modifyInvalidateAttachmentsForEmulatedDefaultFBO( |
| size_t count, |
| const GLenum *attachments, |
| std::vector<GLenum> *modifiedAttachments) const |
| { |
| bool needsModification = mIsDefault && mFramebufferID != 0; |
| if (!needsModification) |
| { |
| return false; |
| } |
| |
| modifiedAttachments->resize(count); |
| for (size_t i = 0; i < count; i++) |
| { |
| switch (attachments[i]) |
| { |
| case GL_COLOR: |
| (*modifiedAttachments)[i] = GL_COLOR_ATTACHMENT0; |
| break; |
| |
| case GL_DEPTH: |
| (*modifiedAttachments)[i] = GL_DEPTH_ATTACHMENT; |
| break; |
| |
| case GL_STENCIL: |
| (*modifiedAttachments)[i] = GL_STENCIL_ATTACHMENT; |
| break; |
| |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| gl::Error FramebufferGL::readPixelsRowByRow(const gl::Context *context, |
| const gl::Rectangle &area, |
| GLenum format, |
| GLenum type, |
| const gl::PixelPackState &pack, |
| GLubyte *pixels) const |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type); |
| |
| GLuint rowBytes = 0; |
| ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, pack.alignment, pack.rowLength), |
| rowBytes); |
| GLuint skipBytes = 0; |
| ANGLE_TRY_RESULT(glFormat.computeSkipBytes(type, rowBytes, 0, pack, false), skipBytes); |
| |
| gl::PixelPackState directPack; |
| directPack.alignment = 1; |
| stateManager->setPixelPackState(directPack); |
| |
| pixels += skipBytes; |
| for (GLint y = area.y; y < area.y + area.height; ++y) |
| { |
| functions->readPixels(area.x, y, area.width, 1, format, type, pixels); |
| pixels += rowBytes; |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error FramebufferGL::readPixelsAllAtOnce(const gl::Context *context, |
| const gl::Rectangle &area, |
| GLenum format, |
| GLenum type, |
| const gl::PixelPackState &pack, |
| GLubyte *pixels, |
| bool readLastRowSeparately) const |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| GLint height = area.height - readLastRowSeparately; |
| if (height > 0) |
| { |
| stateManager->setPixelPackState(pack); |
| functions->readPixels(area.x, area.y, area.width, height, format, type, pixels); |
| } |
| |
| if (readLastRowSeparately) |
| { |
| const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type); |
| |
| GLuint rowBytes = 0; |
| ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, pack.alignment, pack.rowLength), |
| rowBytes); |
| GLuint skipBytes = 0; |
| ANGLE_TRY_RESULT(glFormat.computeSkipBytes(type, rowBytes, 0, pack, false), skipBytes); |
| |
| gl::PixelPackState directPack; |
| directPack.alignment = 1; |
| stateManager->setPixelPackState(directPack); |
| |
| pixels += skipBytes + (area.height - 1) * rowBytes; |
| functions->readPixels(area.x, area.y + area.height - 1, area.width, 1, format, type, |
| pixels); |
| } |
| |
| return gl::NoError(); |
| } |
| } // namespace rx |