blob: 01320338c89a9e8e24a1366a8df464e7defad9b8 [file] [log] [blame]
//
// 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