Commit 47bb4933 by Geoff Lang Committed by Commit Bot

Implement CopyTexture functions for uint texture formats.

BUG=angleproject:1932 Change-Id: I6474237cbb82b59a0bd40c1b9b9e2455952d3755 Reviewed-on: https://chromium-review.googlesource.com/600510 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 553590a5
......@@ -431,9 +431,11 @@ gl::Error BlitGL::blitColorBufferWithShader(const gl::Framebuffer *source,
gl::Error BlitGL::copySubTexture(const gl::Context *context,
TextureGL *source,
size_t sourceLevel,
GLenum sourceComponentType,
TextureGL *dest,
GLenum destTarget,
size_t destLevel,
GLenum destComponentType,
const gl::Extents &sourceSize,
const gl::Rectangle &sourceArea,
const gl::Offset &destOffset,
......@@ -445,8 +447,9 @@ gl::Error BlitGL::copySubTexture(const gl::Context *context,
{
ANGLE_TRY(initializeResources());
BlitProgram *blitProgram = nullptr;
ANGLE_TRY(getBlitProgram(BlitProgramType::FLOAT_TO_FLOAT, &blitProgram));
BlitProgramType blitProgramType = getBlitProgramType(sourceComponentType, destComponentType);
BlitProgram *blitProgram = nullptr;
ANGLE_TRY(getBlitProgram(blitProgramType, &blitProgram));
// Setup the source texture
if (needsLumaWorkaround)
......@@ -614,6 +617,30 @@ void BlitGL::setScratchTextureParameter(GLenum param, GLenum value)
}
}
BlitGL::BlitProgramType BlitGL::getBlitProgramType(GLenum sourceComponentType,
GLenum destComponentType)
{
if (sourceComponentType == GL_UNSIGNED_INT)
{
ASSERT(destComponentType == GL_UNSIGNED_INT);
return BlitProgramType::UINT_TO_UINT;
}
else
{
// Source is a float type
ASSERT(sourceComponentType != GL_INT);
if (destComponentType == GL_UNSIGNED_INT)
{
return BlitProgramType::FLOAT_TO_UINT;
}
else
{
// Dest is a float type
return BlitProgramType::FLOAT_TO_FLOAT;
}
}
}
gl::Error BlitGL::getBlitProgram(BlitProgramType type, BlitProgram **program)
{
BlitProgram &result = mBlitPrograms[type];
......@@ -621,58 +648,175 @@ gl::Error BlitGL::getBlitProgram(BlitProgramType type, BlitProgram **program)
{
result.program = mFunctions->createProgram();
// Compile the fragment shader
const char *vsSource =
"#version 100\n"
"varying vec2 v_texcoord;\n"
"uniform vec2 u_scale;\n"
"uniform vec2 u_offset;\n"
"attribute vec2 a_texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4((a_texcoord * 2.0) - 1.0, 0.0, 1.0);\n"
" v_texcoord = a_texcoord * u_scale + u_offset;\n"
"}\n";
GLuint vs = mFunctions->createShader(GL_VERTEX_SHADER);
mFunctions->shaderSource(vs, 1, &vsSource, nullptr);
mFunctions->compileShader(vs);
ANGLE_TRY(CheckCompileStatus(mFunctions, vs));
mFunctions->attachShader(result.program, vs);
mFunctions->deleteShader(vs);
// 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 =
"#version 100\n"
"precision highp float;"
"uniform sampler2D u_source_texture;\n"
"uniform bool u_multiply_alpha;\n"
"uniform bool u_unmultiply_alpha;\n"
"varying vec2 v_texcoord;\n"
"\n"
"void main()\n"
"{\n"
" if (clamp(v_texcoord, vec2(0.0), vec2(1.0)) != v_texcoord)\n"
" {\n"
" discard;\n"
" }\n"
" vec4 color = texture2D(u_source_texture, v_texcoord);\n"
" if (u_multiply_alpha) {color.xyz = color.xyz * color.a;}"
" if (u_unmultiply_alpha && color.a != 0.0) {color.xyz = color.xyz / color.a;}"
" gl_FragColor = color;"
"}\n";
GLuint fs = mFunctions->createShader(GL_FRAGMENT_SHADER);
mFunctions->shaderSource(fs, 1, &fsSource, nullptr);
mFunctions->compileShader(fs);
ANGLE_TRY(CheckCompileStatus(mFunctions, fs));
mFunctions->attachShader(result.program, fs);
mFunctions->deleteShader(fs);
// Depending on what types need to be output by the shaders, different versions need to be
// used.
std::string version;
std::string vsInputVariableQualifier;
std::string vsOutputVariableQualifier;
std::string fsInputVariableQualifier;
std::string fsOutputVariableQualifier;
std::string sampleFunction;
if (type == BlitProgramType::FLOAT_TO_FLOAT)
{
version = "100";
vsInputVariableQualifier = "attribute";
vsOutputVariableQualifier = "varying";
fsInputVariableQualifier = "varying";
fsOutputVariableQualifier = "";
sampleFunction = "texture2D";
}
else
{
// Need to use a higher version to support non-float output types
if (mFunctions->standard == STANDARD_GL_DESKTOP)
{
version = "330";
}
else
{
ASSERT(mFunctions->standard == STANDARD_GL_ES);
version = "300 es";
}
vsInputVariableQualifier = "in";
vsOutputVariableQualifier = "out";
fsInputVariableQualifier = "in";
fsOutputVariableQualifier = "out";
sampleFunction = "texture";
}
{
// Compile the vertex shader
std::ostringstream vsSourceStream;
vsSourceStream << "#version " << version << "\n";
vsSourceStream << vsInputVariableQualifier << " vec2 a_texcoord;\n";
vsSourceStream << "uniform vec2 u_scale;\n";
vsSourceStream << "uniform vec2 u_offset;\n";
vsSourceStream << vsOutputVariableQualifier << " vec2 v_texcoord;\n";
vsSourceStream << "\n";
vsSourceStream << "void main()\n";
vsSourceStream << "{\n";
vsSourceStream << " gl_Position = vec4((a_texcoord * 2.0) - 1.0, 0.0, 1.0);\n";
vsSourceStream << " v_texcoord = a_texcoord * u_scale + u_offset;\n";
vsSourceStream << "}\n";
std::string vsSourceStr = vsSourceStream.str();
const char *vsSourceCStr = vsSourceStr.c_str();
GLuint vs = mFunctions->createShader(GL_VERTEX_SHADER);
mFunctions->shaderSource(vs, 1, &vsSourceCStr, nullptr);
mFunctions->compileShader(vs);
ANGLE_TRY(CheckCompileStatus(mFunctions, vs));
mFunctions->attachShader(result.program, vs);
mFunctions->deleteShader(vs);
}
{
// Sampling texture uniform changes depending on source texture type.
std::string samplerType;
std::string samplerResultType;
switch (type)
{
case BlitProgramType::FLOAT_TO_FLOAT:
case BlitProgramType::FLOAT_TO_UINT:
samplerType = "sampler2D";
samplerResultType = "vec4";
break;
case BlitProgramType::UINT_TO_UINT:
samplerType = "usampler2D";
samplerResultType = "uvec4";
break;
default:
UNREACHABLE();
break;
}
// Output variables depend on the output type
std::string outputType;
std::string outputVariableName;
std::string outputMultiplier;
switch (type)
{
case BlitProgramType::FLOAT_TO_FLOAT:
outputType = "";
outputVariableName = "gl_FragColor";
outputMultiplier = "1.0";
break;
case BlitProgramType::FLOAT_TO_UINT:
case BlitProgramType::UINT_TO_UINT:
outputType = "uvec4";
outputVariableName = "outputUint";
outputMultiplier = "255.0";
break;
default:
UNREACHABLE();
break;
}
// Compile the fragment shader
std::ostringstream fsSourceStream;
fsSourceStream << "#version " << version << "\n";
fsSourceStream << "precision highp float;\n";
fsSourceStream << "uniform " << samplerType << " u_source_texture;\n";
// Write the rest of the uniforms and varyings
fsSourceStream << "uniform bool u_multiply_alpha;\n";
fsSourceStream << "uniform bool u_unmultiply_alpha;\n";
fsSourceStream << fsInputVariableQualifier << " vec2 v_texcoord;\n";
if (!outputType.empty())
{
fsSourceStream << fsOutputVariableQualifier << " " << outputType << " "
<< outputVariableName << ";\n";
}
// Write the main body
fsSourceStream << "\n";
fsSourceStream << "void main()\n";
fsSourceStream << "{\n";
// discard 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.
fsSourceStream << " if (clamp(v_texcoord, vec2(0.0), vec2(1.0)) != v_texcoord)\n";
fsSourceStream << " {\n";
fsSourceStream << " discard;\n";
fsSourceStream << " }\n";
// Sampling code depends on the input data type
fsSourceStream << " " << samplerResultType << " color = " << sampleFunction
<< "(u_source_texture, v_texcoord);\n";
// Perform the premultiply or unmultiply alpha logic
fsSourceStream << " if (u_multiply_alpha)\n";
fsSourceStream << " {\n";
fsSourceStream << " color.xyz = color.xyz * color.a;\n";
fsSourceStream << " }\n";
fsSourceStream << " if (u_unmultiply_alpha && color.a != 0.0)\n";
fsSourceStream << " {\n";
fsSourceStream << " color.xyz = color.xyz / color.a;\n";
fsSourceStream << " }\n";
// Write the conversion to the destionation type
fsSourceStream << " color = color * " << outputMultiplier << ";\n";
// Write the output assignment code
fsSourceStream << " " << outputVariableName << " = " << outputType << "(color);\n";
fsSourceStream << "}\n";
std::string fsSourceStr = fsSourceStream.str();
const char *fsSourceCStr = fsSourceStr.c_str();
GLuint fs = mFunctions->createShader(GL_FRAGMENT_SHADER);
mFunctions->shaderSource(fs, 1, &fsSourceCStr, nullptr);
mFunctions->compileShader(fs);
ANGLE_TRY(CheckCompileStatus(mFunctions, fs));
mFunctions->attachShader(result.program, fs);
mFunctions->deleteShader(fs);
}
mFunctions->linkProgram(result.program);
ANGLE_TRY(CheckLinkStatus(mFunctions, result.program));
......
......@@ -67,9 +67,11 @@ class BlitGL : angle::NonCopyable
gl::Error copySubTexture(const gl::Context *context,
TextureGL *source,
size_t sourceLevel,
GLenum sourceComponentType,
TextureGL *dest,
GLenum destTarget,
size_t destLevel,
GLenum destComponentType,
const gl::Extents &sourceSize,
const gl::Rectangle &sourceArea,
const gl::Offset &destOffset,
......@@ -110,8 +112,11 @@ class BlitGL : angle::NonCopyable
enum class BlitProgramType
{
FLOAT_TO_FLOAT,
FLOAT_TO_UINT,
UINT_TO_UINT,
};
static BlitProgramType getBlitProgramType(GLenum sourceComponentType, GLenum destComponentType);
gl::Error getBlitProgram(BlitProgramType type, BlitProgram **program);
std::map<BlitProgramType, BlitProgram> mBlitPrograms;
......
......@@ -88,7 +88,8 @@ LevelInfoGL GetLevelInfo(GLenum originalInternalFormat, GLenum destinationIntern
{
GLenum originalFormat = gl::GetUnsizedFormat(originalInternalFormat);
GLenum destinationFormat = gl::GetUnsizedFormat(destinationInternalFormat);
return LevelInfoGL(originalFormat, GetDepthStencilWorkaround(originalFormat),
return LevelInfoGL(originalFormat, destinationInternalFormat,
GetDepthStencilWorkaround(originalFormat),
GetLUMAWorkaroundInfo(originalFormat, destinationFormat));
}
......@@ -113,14 +114,16 @@ LUMAWorkaroundGL::LUMAWorkaroundGL(bool enabled_, GLenum workaroundFormat_)
{
}
LevelInfoGL::LevelInfoGL() : LevelInfoGL(GL_NONE, false, LUMAWorkaroundGL())
LevelInfoGL::LevelInfoGL() : LevelInfoGL(GL_NONE, GL_NONE, false, LUMAWorkaroundGL())
{
}
LevelInfoGL::LevelInfoGL(GLenum sourceFormat_,
GLenum nativeInternalFormat_,
bool depthStencilWorkaround_,
const LUMAWorkaroundGL &lumaWorkaround_)
: sourceFormat(sourceFormat_),
nativeInternalFormat(nativeInternalFormat_),
depthStencilWorkaround(depthStencilWorkaround_),
lumaWorkaround(lumaWorkaround_)
{
......@@ -715,8 +718,8 @@ gl::Error TextureGL::copyTexture(const gl::Context *context,
gl::GetUnsizedFormat(internalFormat), type);
return copySubTextureHelper(context, target, level, gl::Offset(0, 0, 0), sourceLevel,
sourceArea, internalFormat, unpackFlipY, unpackPremultiplyAlpha,
unpackUnmultiplyAlpha, source);
sourceArea, gl::GetUnsizedFormat(internalFormat), type, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
}
gl::Error TextureGL::copySubTexture(const gl::Context *context,
......@@ -730,10 +733,10 @@ gl::Error TextureGL::copySubTexture(const gl::Context *context,
bool unpackUnmultiplyAlpha,
const gl::Texture *source)
{
GLenum destFormat = mState.getImageDesc(target, level).format.info->format;
const gl::InternalFormat &destFormatInfo = *mState.getImageDesc(target, level).format.info;
return copySubTextureHelper(context, target, level, destOffset, sourceLevel, sourceArea,
destFormat, unpackFlipY, unpackPremultiplyAlpha,
unpackUnmultiplyAlpha, source);
destFormatInfo.format, destFormatInfo.type, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
}
gl::Error TextureGL::copySubTextureHelper(const gl::Context *context,
......@@ -743,6 +746,7 @@ gl::Error TextureGL::copySubTextureHelper(const gl::Context *context,
size_t sourceLevel,
const gl::Rectangle &sourceArea,
GLenum destFormat,
GLenum destType,
bool unpackFlipY,
bool unpackPremultiplyAlpha,
bool unpackUnmultiplyAlpha,
......@@ -762,19 +766,31 @@ gl::Error TextureGL::copySubTextureHelper(const gl::Context *context,
(sourceFormat == destFormat && sourceFormat != GL_BGRA_EXT) ||
(sourceFormat == GL_RGBA && destFormat == GL_RGB);
GLenum sourceComponentType = sourceImageDesc.format.info->componentType;
GLenum destComponentType = gl::GetInternalFormatInfo(destFormat, destType).componentType;
if (source->getTarget() == GL_TEXTURE_2D && !unpackFlipY &&
unpackPremultiplyAlpha == unpackUnmultiplyAlpha && !needsLumaWorkaround &&
sourceFormatContainSupersetOfDestFormat)
sourceFormatContainSupersetOfDestFormat && sourceComponentType == destComponentType)
{
return mBlitter->copyTexSubImage(sourceGL, sourceLevel, this, target, level, sourceArea,
destOffset);
}
// We can't use copyTexSubImage, do a manual copy
return mBlitter->copySubTexture(context, sourceGL, sourceLevel, this, target, level,
sourceImageDesc.size, sourceArea, destOffset,
needsLumaWorkaround, sourceLevelInfo.sourceFormat, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
// Check if the destination is renderable and copy on the GPU
const LevelInfoGL &destLevelInfo = getLevelInfo(target, level);
if (nativegl::SupportsNativeRendering(mFunctions, target, destLevelInfo.nativeInternalFormat))
{
return mBlitter->copySubTexture(context, sourceGL, sourceLevel, sourceComponentType, this,
target, level, destComponentType, sourceImageDesc.size,
sourceArea, destOffset, needsLumaWorkaround,
sourceLevelInfo.sourceFormat, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
}
// Fall back to CPU-readback
UNIMPLEMENTED();
return gl::NoError();
}
gl::Error TextureGL::setStorage(const gl::Context *context,
......
......@@ -37,6 +37,9 @@ struct LevelInfoGL
// Format of the data used in this mip level.
GLenum sourceFormat;
// Internal format used for the native call to define this texture
GLenum nativeInternalFormat;
// If this mip level requires sampler-state re-writing so that only a red channel is exposed.
bool depthStencilWorkaround;
......@@ -45,6 +48,7 @@ struct LevelInfoGL
LevelInfoGL();
LevelInfoGL(GLenum sourceFormat,
GLenum nativeInternalFormat,
bool depthStencilWorkaround,
const LUMAWorkaroundGL &lumaWorkaround);
};
......@@ -134,6 +138,7 @@ class TextureGL : public TextureImpl
size_t sourceLevel,
const gl::Rectangle &sourceArea,
GLenum destFormat,
GLenum destType,
bool unpackFlipY,
bool unpackPremultiplyAlpha,
bool unpackUnmultiplyAlpha,
......
......@@ -1120,6 +1120,26 @@ bool SupportsOcclusionQueries(const FunctionsGL *functions)
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_occlusion_query_boolean");
}
bool SupportsNativeRendering(const FunctionsGL *functions, GLenum target, GLenum internalFormat)
{
// Some desktop drivers allow rendering to formats that are not required by the spec, this is
// exposed through the GL_FRAMEBUFFER_RENDERABLE query.
if (functions->isAtLeastGL(gl::Version(4, 3)) ||
functions->hasGLExtension("GL_ARB_internalformat_query2"))
{
GLint framebufferRenderable = GL_FALSE;
functions->getInternalformativ(target, internalFormat, GL_FRAMEBUFFER_RENDERABLE, 1,
&framebufferRenderable);
return framebufferRenderable != GL_FALSE;
}
else
{
const nativegl::InternalFormat &nativeInfo =
nativegl::GetInternalFormatInfo(internalFormat, functions->standard);
return nativegl_gl::MeetsRequirements(functions, nativeInfo.framebufferAttachment);
}
}
}
bool CanMapBufferForRead(const FunctionsGL *functions)
......
......@@ -60,6 +60,7 @@ namespace nativegl
{
bool SupportsFenceSync(const FunctionsGL *functions);
bool SupportsOcclusionQueries(const FunctionsGL *functions);
bool SupportsNativeRendering(const FunctionsGL *functions, GLenum target, GLenum internalFormat);
}
bool CanMapBufferForRead(const FunctionsGL *functions);
......
......@@ -1260,13 +1260,6 @@ TEST_P(CopyTextureTestES3, ES3UintFormats)
return;
}
if (IsOpenGL() || IsOpenGLES())
{
std::cout << "Test on OpenGL and OpenGLES because not all formats are implemented yet."
<< std::endl;
return;
}
using GLColor32U = std::tuple<GLuint, GLuint, GLuint, GLuint>;
auto testOutput = [this](GLuint texture, const GLColor32U &expectedColor) {
......@@ -1350,12 +1343,22 @@ TEST_P(CopyTextureTestES3, ES3UintFormats)
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGBA8UI,
GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 128));
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 32, 1));
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
GL_UNSIGNED_BYTE, false, true, false, GLColor32U(64, 32, 16, 1));
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RGB8UI,
GL_UNSIGNED_BYTE, false, false, true, GLColor32U(255, 128, 64, 1));
if (IsOpenGL() || IsOpenGLES())
{
std::cout << "Skipping GL_RGB8UI because it is not implemented yet." << std::endl;
}
else
{
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
GL_RGB8UI, GL_UNSIGNED_BYTE, false, false, false,
GLColor32U(128, 64, 32, 1));
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
GL_RGB8UI, GL_UNSIGNED_BYTE, false, true, false,
GLColor32U(64, 32, 16, 1));
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128),
GL_RGB8UI, GL_UNSIGNED_BYTE, false, false, true,
GLColor32U(255, 128, 64, 1));
}
testCopyCombination(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GLColor(128, 64, 32, 128), GL_RG8UI,
GL_UNSIGNED_BYTE, false, false, false, GLColor32U(128, 64, 0, 1));
......
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