Commit 26a717b0 by Corentin Wallez Committed by Commit Bot

GL: Emulate SRGB blits where needed.

Desktop OpenGL before 4.4 doesn't handle SRGB blits the same way OpenGL ES does. Emulate them by drawing a quad. BUG=angleproject:1492 BUG=chromium:634525 Change-Id: I9f2992d9b373941b10f19f8a51564f0f756cc4df Reviewed-on: https://chromium-review.googlesource.com/389853Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
parent b80a5e9c
...@@ -44,6 +44,15 @@ struct Vector4 ...@@ -44,6 +44,15 @@ struct Vector4
float w; float w;
}; };
struct Vector2
{
Vector2() {}
Vector2(float x, float y) : x(x), y(y) {}
float x;
float y;
};
inline bool isPow2(int x) inline bool isPow2(int x)
{ {
return (x & (x - 1)) == 0 && (x != 0); return (x & (x - 1)) == 0 && (x != 0);
......
...@@ -58,6 +58,9 @@ BlitGL::BlitGL(const FunctionsGL *functions, ...@@ -58,6 +58,9 @@ BlitGL::BlitGL(const FunctionsGL *functions,
mWorkarounds(workarounds), mWorkarounds(workarounds),
mStateManager(stateManager), mStateManager(stateManager),
mBlitProgram(0), mBlitProgram(0),
mSourceTextureLocation(-1),
mScaleLocation(-1),
mOffsetLocation(-1),
mScratchFBO(0), mScratchFBO(0),
mVAO(0) mVAO(0)
{ {
...@@ -142,7 +145,6 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, ...@@ -142,7 +145,6 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture,
const gl::InternalFormat &internalFormatInfo = const gl::InternalFormat &internalFormatInfo =
gl::GetInternalFormatInfo(copyTexImageFormat.internalFormat); gl::GetInternalFormatInfo(copyTexImageFormat.internalFormat);
mStateManager->activeTexture(0);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]); mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]);
mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat, sourceArea.x, mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat, sourceArea.x,
sourceArea.y, sourceArea.width, sourceArea.height, 0); sourceArea.y, sourceArea.width, sourceArea.height, 0);
...@@ -167,7 +169,6 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, ...@@ -167,7 +169,6 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture,
mScratchTextures[1], 0); mScratchTextures[1], 0);
// Render to the destination texture, sampling from the scratch texture // Render to the destination texture, sampling from the scratch texture
mStateManager->useProgram(mBlitProgram);
mStateManager->setViewport(gl::Rectangle(0, 0, sourceArea.width, sourceArea.height)); mStateManager->setViewport(gl::Rectangle(0, 0, sourceArea.width, sourceArea.height));
mStateManager->setScissorTestEnabled(false); mStateManager->setScissorTestEnabled(false);
mStateManager->setDepthRange(0.0f, 1.0f); mStateManager->setDepthRange(0.0f, 1.0f);
...@@ -182,9 +183,19 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, ...@@ -182,9 +183,19 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture,
mStateManager->setRasterizerDiscardEnabled(false); mStateManager->setRasterizerDiscardEnabled(false);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]); mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]);
setScratchTextureParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
setScratchTextureParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
mStateManager->activeTexture(0);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]);
mStateManager->useProgram(mBlitProgram);
mFunctions->uniform1i(mSourceTextureLocation, 0);
mFunctions->uniform2f(mScaleLocation, 1.0, 1.0);
mFunctions->uniform2f(mOffsetLocation, 0.0, 0.0);
mStateManager->bindVertexArray(mVAO, 0); mStateManager->bindVertexArray(mVAO, 0);
mFunctions->drawArrays(GL_TRIANGLES, 0, 6); mFunctions->drawArrays(GL_TRIANGLES, 0, 3);
// Copy the swizzled texture to the destination texture // Copy the swizzled texture to the destination texture
mStateManager->bindTexture(textureType, texture); mStateManager->bindTexture(textureType, texture);
...@@ -192,10 +203,170 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture, ...@@ -192,10 +203,170 @@ gl::Error BlitGL::copySubImageToLUMAWorkaroundTexture(GLuint texture,
0, sourceArea.width, sourceArea.height); 0, sourceArea.width, sourceArea.height);
// Finally orphan the scratch textures so they can be GCed by the driver. // Finally orphan the scratch textures so they can be GCed by the driver.
orphanScratchTextures();
return gl::NoError();
}
gl::Error BlitGL::blitColorBufferWithShader(const gl::Framebuffer *source,
const gl::Framebuffer *dest,
const gl::Rectangle &sourceAreaIn,
const gl::Rectangle &destAreaIn,
GLenum filter)
{
ANGLE_TRY(initializeResources());
// Normalize the destination area to have positive width and height because we will use
// glViewport to set it, which doesn't allow negative width or height.
gl::Rectangle sourceArea = sourceAreaIn;
gl::Rectangle destArea = destAreaIn;
if (destArea.width < 0)
{
destArea.x += destArea.width;
destArea.width = -destArea.width;
sourceArea.x += sourceArea.width;
sourceArea.width = -sourceArea.width;
}
if (destArea.height < 0)
{
destArea.y += destArea.height;
destArea.height = -destArea.height;
sourceArea.y += sourceArea.height;
sourceArea.height = -sourceArea.height;
}
const gl::FramebufferAttachment *readAttachment = source->getReadColorbuffer();
ASSERT(readAttachment->getSamples() <= 1);
// Compute the part of the source that will be sampled.
gl::Rectangle inBoundsSource;
{
gl::Extents sourceSize = readAttachment->getSize();
gl::Rectangle sourceBounds(0, 0, sourceSize.width, sourceSize.height);
gl::ClipRectangle(sourceArea, sourceBounds, &inBoundsSource);
// Note that inBoundsSource will have lost the orientation information.
ASSERT(inBoundsSource.width >= 0 && inBoundsSource.height >= 0);
// Early out when the sampled part is empty as the blit will be a noop,
// and it prevents a division by zero in later computations.
if (inBoundsSource.width == 0 || inBoundsSource.height == 0)
{
return gl::NoError();
}
}
// The blit will be emulated by getting the source of the blit in a texture and sampling it
// with CLAMP_TO_EDGE. The quad used to draw can trivially compute texture coordinates going
// from (0, 0) to (1, 1). These texture coordinates will need to be transformed to make two
// regions match:
// - The region of the texture representing the source framebuffer region that will be sampled
// - The region of the drawn quad that corresponds to non-clamped blit, this is the same as the
// region of the source rectangle that is inside the source attachment.
//
// These two regions, T (texture) and D (dest) are defined by their offset in texcoord space
// in (0, 1)^2 and their size in texcoord space in (-1, 1)^2. The size can be negative to
// represent the orientation of the blit.
//
// Then if P is the quad texcoord, Q the texcoord inside T, and R the texture texcoord:
// - Q = (P - D.offset) / D.size
// - Q = (R - T.offset) / T.size
// Hence R = (P - D.offset) / D.size * T.size - T.offset
// = P * (T.size / D.size) + (T.offset - D.offset * T.size / D.size)
GLuint textureId;
gl::Vector2 TOffset;
gl::Vector2 TSize;
// TODO(cwallez) once texture dirty bits are landed, reuse attached texture instead of using
// CopyTexImage2D
{
textureId = mScratchTextures[0];
TOffset = gl::Vector2(0.0, 0.0);
TSize = gl::Vector2(1.0, 1.0);
if (sourceArea.width < 0)
{
TOffset.x = 1.0;
TSize.x = -1.0;
}
if (sourceArea.height < 0)
{
TOffset.y = 1.0;
TSize.y = -1.0;
}
GLenum format = readAttachment->getFormat().info->internalFormat;
const FramebufferGL *sourceGL = GetImplAs<FramebufferGL>(source);
mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceGL->getFramebufferID());
mStateManager->bindTexture(GL_TEXTURE_2D, textureId);
mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, format, inBoundsSource.x, inBoundsSource.y,
inBoundsSource.width, inBoundsSource.height, 0);
}
// Compute normalized sampled draw quad region
// It is the same as the region of the source rectangle that is in bounds.
gl::Vector2 DOffset;
gl::Vector2 DSize;
{
ASSERT(sourceArea.width != 0 && sourceArea.height != 0);
gl::Rectangle orientedInBounds = inBoundsSource;
if (sourceArea.width < 0)
{
orientedInBounds.x += orientedInBounds.width;
orientedInBounds.width = -orientedInBounds.width;
}
if (sourceArea.height < 0)
{
orientedInBounds.y += orientedInBounds.height;
orientedInBounds.height = -orientedInBounds.height;
}
DOffset =
gl::Vector2(static_cast<float>(orientedInBounds.x - sourceArea.x) / sourceArea.width,
static_cast<float>(orientedInBounds.y - sourceArea.y) / sourceArea.height);
DSize = gl::Vector2(static_cast<float>(orientedInBounds.width) / sourceArea.width,
static_cast<float>(orientedInBounds.height) / sourceArea.height);
}
ASSERT(DSize.x != 0.0 && DSize.y != 0.0);
gl::Vector2 texCoordScale = gl::Vector2(TSize.x / DSize.x, TSize.y / DSize.y);
gl::Vector2 texCoordOffset = gl::Vector2(TOffset.x - DOffset.x * texCoordScale.x,
TOffset.y - DOffset.y * texCoordScale.y);
// Reset all the state except scissor and viewport
mStateManager->setDepthRange(0.0f, 1.0f);
mStateManager->setBlendEnabled(false);
mStateManager->setColorMask(true, true, true, true);
mStateManager->setSampleAlphaToCoverageEnabled(false);
mStateManager->setSampleCoverageEnabled(false);
mStateManager->setDepthTestEnabled(false);
mStateManager->setStencilTestEnabled(false);
mStateManager->setCullFaceEnabled(false);
mStateManager->setPolygonOffsetFillEnabled(false);
mStateManager->setRasterizerDiscardEnabled(false);
// Use the viewport to draw exactly to the destination rectangle
mStateManager->setViewport(destArea);
// Set uniforms
setScratchTextureParameter(GL_TEXTURE_MIN_FILTER, filter);
setScratchTextureParameter(GL_TEXTURE_MAG_FILTER, filter);
setScratchTextureParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
setScratchTextureParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
mStateManager->activeTexture(0);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]); mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[0]);
mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[1]); mStateManager->useProgram(mBlitProgram);
mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); mFunctions->uniform1i(mSourceTextureLocation, 0);
mFunctions->uniform2f(mScaleLocation, texCoordScale.x, texCoordScale.y);
mFunctions->uniform2f(mOffsetLocation, texCoordOffset.x, texCoordOffset.y);
const FramebufferGL *destGL = GetImplAs<FramebufferGL>(dest);
mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, destGL->getFramebufferID());
mStateManager->bindVertexArray(mVAO, 0);
mFunctions->drawArrays(GL_TRIANGLES, 0, 3);
return gl::NoError(); return gl::NoError();
} }
...@@ -207,25 +378,25 @@ gl::Error BlitGL::initializeResources() ...@@ -207,25 +378,25 @@ gl::Error BlitGL::initializeResources()
mBlitProgram = mFunctions->createProgram(); mBlitProgram = mFunctions->createProgram();
// Compile the fragment shader // Compile the fragment shader
// It uses a single, large triangle, to avoid arithmetic precision issues where fragments
// with the same Y coordinate don't get exactly the same interpolated texcoord Y.
const char *vsSource = const char *vsSource =
"#version 150\n" "#version 150\n"
"out vec2 v_texcoord;\n" "out vec2 v_texcoord;\n"
"uniform vec2 u_scale;\n"
"uniform vec2 u_offset;\n"
"\n" "\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" const vec2 quad_positions[6] = vec2[6]\n" " const vec2 quad_positions[3] = vec2[3]\n"
" (\n" " (\n"
" vec2(0.0f, 0.0f),\n" " vec2(-0.5f, 0.0f),\n"
" vec2(0.0f, 1.0f),\n" " vec2( 1.5f, 0.0f),\n"
" vec2(1.0f, 0.0f),\n" " vec2( 0.5f, 2.0f)\n"
"\n"
" vec2(0.0f, 1.0f),\n"
" vec2(1.0f, 0.0f),\n"
" vec2(1.0f, 1.0f)\n"
" );\n" " );\n"
"\n" "\n"
" gl_Position = vec4((quad_positions[gl_VertexID] * 2.0) - 1.0, 0.0, 1.0);\n" " gl_Position = vec4((quad_positions[gl_VertexID] * 2.0) - 1.0, 0.0, 1.0);\n"
" v_texcoord = quad_positions[gl_VertexID];\n" " v_texcoord = quad_positions[gl_VertexID] * u_scale + u_offset;\n"
"}\n"; "}\n";
GLuint vs = mFunctions->createShader(GL_VERTEX_SHADER); GLuint vs = mFunctions->createShader(GL_VERTEX_SHADER);
...@@ -237,6 +408,8 @@ gl::Error BlitGL::initializeResources() ...@@ -237,6 +408,8 @@ gl::Error BlitGL::initializeResources()
mFunctions->deleteShader(vs); mFunctions->deleteShader(vs);
// Compile the vertex shader // Compile the vertex shader
// It discards if the texcoord is outside (0, 1)^2 so the blitframebuffer workaround
// doesn't write when the point sampled is outside of the source framebuffer.
const char *fsSource = const char *fsSource =
"#version 150\n" "#version 150\n"
"uniform sampler2D u_source_texture;\n" "uniform sampler2D u_source_texture;\n"
...@@ -245,6 +418,10 @@ gl::Error BlitGL::initializeResources() ...@@ -245,6 +418,10 @@ gl::Error BlitGL::initializeResources()
"\n" "\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" if (clamp(v_texcoord, vec2(0.0), vec2(1.0)) != v_texcoord)\n"
" {\n"
" discard;\n"
" }\n"
" output_color = texture(u_source_texture, v_texcoord);\n" " output_color = texture(u_source_texture, v_texcoord);\n"
"}\n"; "}\n";
...@@ -259,9 +436,10 @@ gl::Error BlitGL::initializeResources() ...@@ -259,9 +436,10 @@ gl::Error BlitGL::initializeResources()
mFunctions->linkProgram(mBlitProgram); mFunctions->linkProgram(mBlitProgram);
ANGLE_TRY(CheckLinkStatus(mFunctions, mBlitProgram)); ANGLE_TRY(CheckLinkStatus(mFunctions, mBlitProgram));
GLuint textureUniform = mFunctions->getUniformLocation(mBlitProgram, "u_source_texture"); mSourceTextureLocation = mFunctions->getUniformLocation(mBlitProgram, "u_source_texture");
mScaleLocation = mFunctions->getUniformLocation(mBlitProgram, "u_scale");
mOffsetLocation = mFunctions->getUniformLocation(mBlitProgram, "u_offset");
mStateManager->useProgram(mBlitProgram); mStateManager->useProgram(mBlitProgram);
mFunctions->uniform1i(textureUniform, 0);
} }
for (size_t i = 0; i < ArraySize(mScratchTextures); i++) for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
...@@ -269,11 +447,6 @@ gl::Error BlitGL::initializeResources() ...@@ -269,11 +447,6 @@ gl::Error BlitGL::initializeResources()
if (mScratchTextures[i] == 0) if (mScratchTextures[i] == 0)
{ {
mFunctions->genTextures(1, &mScratchTextures[i]); mFunctions->genTextures(1, &mScratchTextures[i]);
mStateManager->bindTexture(GL_TEXTURE_2D, mScratchTextures[i]);
// Use nearest, non-mipmapped sampling with the scratch texture
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
} }
} }
...@@ -289,4 +462,25 @@ gl::Error BlitGL::initializeResources() ...@@ -289,4 +462,25 @@ gl::Error BlitGL::initializeResources()
return gl::NoError(); return gl::NoError();
} }
void BlitGL::orphanScratchTextures()
{
for (auto texture : mScratchTextures)
{
mStateManager->bindTexture(GL_TEXTURE_2D, texture);
mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
}
} }
void BlitGL::setScratchTextureParameter(GLenum param, GLenum value)
{
for (auto texture : mScratchTextures)
{
mStateManager->bindTexture(GL_TEXTURE_2D, texture);
mFunctions->texParameteri(GL_TEXTURE_2D, param, value);
mFunctions->texParameteri(GL_TEXTURE_2D, param, value);
}
}
} // namespace rx
...@@ -52,14 +52,26 @@ class BlitGL : public angle::NonCopyable ...@@ -52,14 +52,26 @@ class BlitGL : public angle::NonCopyable
const gl::Rectangle &sourceArea, const gl::Rectangle &sourceArea,
const gl::Framebuffer *source); const gl::Framebuffer *source);
gl::Error blitColorBufferWithShader(const gl::Framebuffer *source,
const gl::Framebuffer *dest,
const gl::Rectangle &sourceArea,
const gl::Rectangle &destArea,
GLenum filter);
gl::Error initializeResources(); gl::Error initializeResources();
private: private:
void orphanScratchTextures();
void setScratchTextureParameter(GLenum param, GLenum value);
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
GLuint mBlitProgram; GLuint mBlitProgram;
GLint mSourceTextureLocation;
GLint mScaleLocation;
GLint mOffsetLocation;
GLuint mScratchTextures[2]; GLuint mScratchTextures[2];
GLuint mScratchFBO; GLuint mScratchFBO;
......
...@@ -62,7 +62,8 @@ ProgramImpl *ContextGL::createProgram(const gl::ProgramState &data) ...@@ -62,7 +62,8 @@ ProgramImpl *ContextGL::createProgram(const gl::ProgramState &data)
FramebufferImpl *ContextGL::createFramebuffer(const gl::FramebufferState &data) FramebufferImpl *ContextGL::createFramebuffer(const gl::FramebufferState &data)
{ {
return new FramebufferGL(data, getFunctions(), getStateManager(), getWorkaroundsGL(), false); return new FramebufferGL(data, getFunctions(), getStateManager(), getWorkaroundsGL(),
mRenderer->getBlitter(), false);
} }
TextureImpl *ContextGL::createTexture(const gl::TextureState &state) TextureImpl *ContextGL::createTexture(const gl::TextureState &state)
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/gl/BlitGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/RenderbufferGL.h" #include "libANGLE/renderer/gl/RenderbufferGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h"
...@@ -35,11 +36,13 @@ FramebufferGL::FramebufferGL(const FramebufferState &state, ...@@ -35,11 +36,13 @@ FramebufferGL::FramebufferGL(const FramebufferState &state,
const FunctionsGL *functions, const FunctionsGL *functions,
StateManagerGL *stateManager, StateManagerGL *stateManager,
const WorkaroundsGL &workarounds, const WorkaroundsGL &workarounds,
BlitGL *blitter,
bool isDefault) bool isDefault)
: FramebufferImpl(state), : FramebufferImpl(state),
mFunctions(functions), mFunctions(functions),
mStateManager(stateManager), mStateManager(stateManager),
mWorkarounds(workarounds), mWorkarounds(workarounds),
mBlitter(blitter),
mFramebufferID(0), mFramebufferID(0),
mIsDefault(isDefault) mIsDefault(isDefault)
{ {
...@@ -53,11 +56,13 @@ FramebufferGL::FramebufferGL(GLuint id, ...@@ -53,11 +56,13 @@ FramebufferGL::FramebufferGL(GLuint id,
const FramebufferState &state, const FramebufferState &state,
const FunctionsGL *functions, const FunctionsGL *functions,
const WorkaroundsGL &workarounds, const WorkaroundsGL &workarounds,
BlitGL *blitter,
StateManagerGL *stateManager) StateManagerGL *stateManager)
: FramebufferImpl(state), : FramebufferImpl(state),
mFunctions(functions), mFunctions(functions),
mStateManager(stateManager), mStateManager(stateManager),
mWorkarounds(workarounds), mWorkarounds(workarounds),
mBlitter(blitter),
mFramebufferID(id), mFramebufferID(id),
mIsDefault(true) mIsDefault(true)
{ {
...@@ -276,15 +281,81 @@ Error FramebufferGL::blit(ContextImpl *context, ...@@ -276,15 +281,81 @@ Error FramebufferGL::blit(ContextImpl *context,
GLenum filter) GLenum filter)
{ {
const Framebuffer *sourceFramebuffer = context->getGLState().getReadFramebuffer(); const Framebuffer *sourceFramebuffer = context->getGLState().getReadFramebuffer();
const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(sourceFramebuffer); const Framebuffer *destFramebuffer = context->getGLState().getDrawFramebuffer();
bool needManualColorBlit = false;
// The manual SRGB blit is only needed to perform correct linear interpolation. We don't
// need to make sure there is SRGB conversion for NEAREST as the values will be copied.
if (filter != GL_NEAREST)
{
// 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 [...].
{
const FramebufferAttachment *readAttachment = sourceFramebuffer->getReadColorbuffer();
bool sourceSRGB =
readAttachment != nullptr && readAttachment->getColorEncoding() == GL_SRGB;
needManualColorBlit =
needManualColorBlit || (sourceSRGB && !mFunctions->isAtLeastGL(gl::Version(4, 4)));
}
// 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->isAtLeastGL(gl::Version(4, 2)));
}
}
// Enable FRAMEBUFFER_SRGB if needed
syncDrawState();
GLenum blitMask = mask;
if (needManualColorBlit && (mask & GL_COLOR_BUFFER_BIT))
{
ANGLE_TRY(mBlitter->blitColorBufferWithShader(sourceFramebuffer, destFramebuffer,
sourceArea, destArea, filter));
blitMask &= ~GL_COLOR_BUFFER_BIT;
}
if (blitMask == 0)
{
return gl::NoError();
}
const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(sourceFramebuffer);
mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID); mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID);
mFunctions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(), mFunctions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(),
destArea.x, destArea.y, destArea.x1(), destArea.y1(), mask, filter); destArea.x, destArea.y, destArea.x1(), destArea.y1(), blitMask,
filter);
return Error(GL_NO_ERROR); return gl::NoError();
} }
bool FramebufferGL::checkStatus() const bool FramebufferGL::checkStatus() const
...@@ -370,17 +441,17 @@ void FramebufferGL::syncClearState(GLbitfield mask) ...@@ -370,17 +441,17 @@ void FramebufferGL::syncClearState(GLbitfield mask)
if (mWorkarounds.doesSRGBClearsOnLinearFramebufferAttachments && if (mWorkarounds.doesSRGBClearsOnLinearFramebufferAttachments &&
(mask & GL_COLOR_BUFFER_BIT) != 0 && !mIsDefault) (mask & GL_COLOR_BUFFER_BIT) != 0 && !mIsDefault)
{ {
bool hasSRBAttachment = false; bool hasSRGBAttachment = false;
for (const auto &attachment : mState.getColorAttachments()) for (const auto &attachment : mState.getColorAttachments())
{ {
if (attachment.isAttached() && attachment.getColorEncoding() == GL_SRGB) if (attachment.isAttached() && attachment.getColorEncoding() == GL_SRGB)
{ {
hasSRBAttachment = true; hasSRGBAttachment = true;
break; break;
} }
} }
mStateManager->setFramebufferSRGBEnabled(hasSRBAttachment); mStateManager->setFramebufferSRGBEnabled(hasSRGBAttachment);
} }
else else
{ {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
namespace rx namespace rx
{ {
class BlitGL;
class FunctionsGL; class FunctionsGL;
class StateManagerGL; class StateManagerGL;
struct WorkaroundsGL; struct WorkaroundsGL;
...@@ -25,6 +26,7 @@ class FramebufferGL : public FramebufferImpl ...@@ -25,6 +26,7 @@ class FramebufferGL : public FramebufferImpl
const FunctionsGL *functions, const FunctionsGL *functions,
StateManagerGL *stateManager, StateManagerGL *stateManager,
const WorkaroundsGL &workarounds, const WorkaroundsGL &workarounds,
BlitGL *blitter,
bool isDefault); bool isDefault);
// Constructor called when we need to create a FramebufferGL from an // Constructor called when we need to create a FramebufferGL from an
// existing framebuffer name, for example for the default framebuffer // existing framebuffer name, for example for the default framebuffer
...@@ -33,6 +35,7 @@ class FramebufferGL : public FramebufferImpl ...@@ -33,6 +35,7 @@ class FramebufferGL : public FramebufferImpl
const gl::FramebufferState &data, const gl::FramebufferState &data,
const FunctionsGL *functions, const FunctionsGL *functions,
const WorkaroundsGL &workarounds, const WorkaroundsGL &workarounds,
BlitGL *blitter,
StateManagerGL *stateManager); StateManagerGL *stateManager);
~FramebufferGL() override; ~FramebufferGL() override;
...@@ -100,6 +103,7 @@ class FramebufferGL : public FramebufferImpl ...@@ -100,6 +103,7 @@ class FramebufferGL : public FramebufferImpl
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
BlitGL *mBlitter;
GLuint mFramebufferID; GLuint mFramebufferID;
bool mIsDefault; bool mIsDefault;
......
...@@ -26,6 +26,6 @@ SurfaceGL::~SurfaceGL() ...@@ -26,6 +26,6 @@ SurfaceGL::~SurfaceGL()
FramebufferImpl *SurfaceGL::createDefaultFramebuffer(const gl::FramebufferState &data) FramebufferImpl *SurfaceGL::createDefaultFramebuffer(const gl::FramebufferState &data)
{ {
return new FramebufferGL(data, mRenderer->getFunctions(), mRenderer->getStateManager(), return new FramebufferGL(data, mRenderer->getFunctions(), mRenderer->getStateManager(),
mRenderer->getWorkarounds(), true); mRenderer->getWorkarounds(), mRenderer->getBlitter(), true);
} }
} }
...@@ -53,7 +53,7 @@ class PbufferSurfaceCGL : public SurfaceGL ...@@ -53,7 +53,7 @@ class PbufferSurfaceCGL : public SurfaceGL
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
const WorkaroundsGL &mWorkarounds; RendererGL *mRenderer;
GLuint mFramebuffer; GLuint mFramebuffer;
GLuint mColorRenderbuffer; GLuint mColorRenderbuffer;
......
...@@ -28,7 +28,7 @@ PbufferSurfaceCGL::PbufferSurfaceCGL(const egl::SurfaceState &state, ...@@ -28,7 +28,7 @@ PbufferSurfaceCGL::PbufferSurfaceCGL(const egl::SurfaceState &state,
mHeight(height), mHeight(height),
mFunctions(functions), mFunctions(functions),
mStateManager(renderer->getStateManager()), mStateManager(renderer->getStateManager()),
mWorkarounds(renderer->getWorkarounds()), mRenderer(renderer),
mFramebuffer(0), mFramebuffer(0),
mColorRenderbuffer(0), mColorRenderbuffer(0),
mDSRenderbuffer(0) mDSRenderbuffer(0)
...@@ -136,7 +136,8 @@ EGLint PbufferSurfaceCGL::getSwapBehavior() const ...@@ -136,7 +136,8 @@ EGLint PbufferSurfaceCGL::getSwapBehavior() const
FramebufferImpl *PbufferSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state) FramebufferImpl *PbufferSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state)
{ {
// TODO(cwallez) assert it happens only once? // TODO(cwallez) assert it happens only once?
return new FramebufferGL(mFramebuffer, state, mFunctions, mWorkarounds, mStateManager); return new FramebufferGL(mFramebuffer, state, mFunctions, mRenderer->getWorkarounds(),
mRenderer->getBlitter(), mStateManager);
} }
} }
...@@ -88,6 +88,7 @@ class WindowSurfaceCGL : public SurfaceGL ...@@ -88,6 +88,7 @@ class WindowSurfaceCGL : public SurfaceGL
CGLContextObj mContext; CGLContextObj mContext;
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
RendererGL *mRenderer;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
GLuint mFramebuffer; GLuint mFramebuffer;
......
...@@ -154,10 +154,11 @@ ...@@ -154,10 +154,11 @@
mContext(context), mContext(context),
mFunctions(functions), mFunctions(functions),
mStateManager(renderer->getStateManager()), mStateManager(renderer->getStateManager()),
mRenderer(renderer),
mWorkarounds(renderer->getWorkarounds()), mWorkarounds(renderer->getWorkarounds()),
mFramebuffer(0), mFramebuffer(0),
mDSRenderbuffer(0) mDSRenderbuffer(0)
{ {
pthread_mutex_init(&mSwapState.mutex, nullptr); pthread_mutex_init(&mSwapState.mutex, nullptr);
} }
...@@ -324,7 +325,8 @@ EGLint WindowSurfaceCGL::getSwapBehavior() const ...@@ -324,7 +325,8 @@ EGLint WindowSurfaceCGL::getSwapBehavior() const
FramebufferImpl *WindowSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state) FramebufferImpl *WindowSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state)
{ {
// TODO(cwallez) assert it happens only once? // TODO(cwallez) assert it happens only once?
return new FramebufferGL(mFramebuffer, state, mFunctions, mWorkarounds, mStateManager); return new FramebufferGL(mFramebuffer, state, mFunctions, mWorkarounds, mRenderer->getBlitter(),
mStateManager);
} }
} }
...@@ -308,9 +308,9 @@ uint32_t DisplayOzone::Buffer::getDRMFB() ...@@ -308,9 +308,9 @@ uint32_t DisplayOzone::Buffer::getDRMFB()
FramebufferGL *DisplayOzone::Buffer::framebufferGL(const gl::FramebufferState &state) FramebufferGL *DisplayOzone::Buffer::framebufferGL(const gl::FramebufferState &state)
{ {
return new FramebufferGL(mGLFB, state, mDisplay->mFunctionsGL, return new FramebufferGL(
mDisplay->getRenderer()->getWorkarounds(), mGLFB, state, mDisplay->mFunctionsGL, mDisplay->getRenderer()->getWorkarounds(),
mDisplay->getRenderer()->getStateManager()); mDisplay->getRenderer()->getBlitter(), mDisplay->getRenderer()->getStateManager());
} }
void DisplayOzone::Buffer::present() void DisplayOzone::Buffer::present()
......
...@@ -36,6 +36,7 @@ DXGISwapChainWindowSurfaceWGL::DXGISwapChainWindowSurfaceWGL(const egl::SurfaceS ...@@ -36,6 +36,7 @@ DXGISwapChainWindowSurfaceWGL::DXGISwapChainWindowSurfaceWGL(const egl::SurfaceS
mWindow(window), mWindow(window),
mStateManager(renderer->getStateManager()), mStateManager(renderer->getStateManager()),
mWorkarounds(renderer->getWorkarounds()), mWorkarounds(renderer->getWorkarounds()),
mRenderer(renderer),
mFunctionsGL(functionsGL), mFunctionsGL(functionsGL),
mFunctionsWGL(functionsWGL), mFunctionsWGL(functionsWGL),
mDevice(device), mDevice(device),
...@@ -292,7 +293,8 @@ EGLint DXGISwapChainWindowSurfaceWGL::getSwapBehavior() const ...@@ -292,7 +293,8 @@ EGLint DXGISwapChainWindowSurfaceWGL::getSwapBehavior() const
FramebufferImpl *DXGISwapChainWindowSurfaceWGL::createDefaultFramebuffer( FramebufferImpl *DXGISwapChainWindowSurfaceWGL::createDefaultFramebuffer(
const gl::FramebufferState &data) const gl::FramebufferState &data)
{ {
return new FramebufferGL(mFramebufferID, data, mFunctionsGL, mWorkarounds, mStateManager); return new FramebufferGL(mFramebufferID, data, mFunctionsGL, mWorkarounds,
mRenderer->getBlitter(), mStateManager);
} }
egl::Error DXGISwapChainWindowSurfaceWGL::setObjectsLocked(bool locked) egl::Error DXGISwapChainWindowSurfaceWGL::setObjectsLocked(bool locked)
......
...@@ -66,6 +66,7 @@ class DXGISwapChainWindowSurfaceWGL : public SurfaceGL ...@@ -66,6 +66,7 @@ class DXGISwapChainWindowSurfaceWGL : public SurfaceGL
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
RendererGL *mRenderer;
const FunctionsGL *mFunctionsGL; const FunctionsGL *mFunctionsGL;
const FunctionsWGL *mFunctionsWGL; const FunctionsWGL *mFunctionsWGL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment