blob: bf83b39a49e48d76b70bc662eb3a69e2bd9092a0 [file] [log] [blame]
/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 3.0 Module
* -------------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Framebuffer Object Tests.
*//*--------------------------------------------------------------------*/
#include "es3fFboRenderTest.hpp"
#include "sglrContextUtil.hpp"
#include "sglrGLContext.hpp"
#include "sglrReferenceContext.hpp"
#include "es3fFboTestUtil.hpp"
#include "tcuSurface.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuVectorUtil.hpp"
#include "tcuRenderTarget.hpp"
#include "gluPixelTransfer.hpp"
#include "gluTextureUtil.hpp"
#include "gluStrUtil.hpp"
#include "deRandom.h"
#include "deString.h"
#include "glwDefs.hpp"
#include "glwEnums.hpp"
#include <sstream>
using std::vector;
using std::string;
using tcu::TestLog;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::IVec2;
using tcu::IVec3;
using tcu::IVec4;
using tcu::RGBA;
using tcu::Surface;
namespace deqp
{
namespace gles3
{
namespace Functional
{
using glw::GLenum;
using namespace FboTestUtil;
class FboConfig
{
public:
FboConfig (deUint32 buffers_, deUint32 colorType_, deUint32 colorFormat_, deUint32 depthStencilType_, deUint32 depthStencilFormat_, int width_ = 0, int height_ = 0, int samples_ = 0)
: buffers (buffers_)
, colorType (colorType_)
, colorFormat (colorFormat_)
, depthStencilType (depthStencilType_)
, depthStencilFormat (depthStencilFormat_)
, width (width_)
, height (height_)
, samples (samples_)
{
}
FboConfig (void)
: buffers (0)
, colorType (GL_NONE)
, colorFormat (GL_NONE)
, depthStencilType (GL_NONE)
, depthStencilFormat (GL_NONE)
, width (0)
, height (0)
, samples (0)
{
}
std::string getName (void) const;
deUint32 buffers; //!< Buffer bit mask (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|...)
GLenum colorType; //!< GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP, GL_RENDERBUFFER
GLenum colorFormat; //!< Internal format for color buffer texture or renderbuffer
GLenum depthStencilType;
GLenum depthStencilFormat;
int width;
int height;
int samples;
};
static const char* getTypeName (GLenum type)
{
switch (type)
{
case GL_TEXTURE_2D: return "tex2d";
case GL_RENDERBUFFER: return "rbo";
default:
TCU_FAIL("Unknown type");
}
}
std::string FboConfig::getName (void) const
{
std::ostringstream name;
DE_ASSERT(buffers & GL_COLOR_BUFFER_BIT);
name << getTypeName(colorType) << "_" << getFormatName(colorFormat);
if (buffers & GL_DEPTH_BUFFER_BIT)
name << "_depth";
if (buffers & GL_STENCIL_BUFFER_BIT)
name << "_stencil";
if (buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT))
name << "_" << getTypeName(depthStencilType) << "_" << getFormatName(depthStencilFormat);
return name.str();
}
class Framebuffer
{
public:
Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo = 0, deUint32 colorBuffer = 0, deUint32 depthStencilBuffer = 0);
~Framebuffer (void);
const FboConfig& getConfig (void) const { return m_config; }
deUint32 getFramebuffer (void) const { return m_framebuffer; }
deUint32 getColorBuffer (void) const { return m_colorBuffer; }
deUint32 getDepthStencilBuffer (void) const { return m_depthStencilBuffer; }
void checkCompleteness (void);
private:
deUint32 createTex2D (deUint32 name, GLenum format, int width, int height);
deUint32 createRbo (deUint32 name, GLenum format, int width, int height);
void destroyBuffer (deUint32 name, GLenum type);
FboConfig m_config;
sglr::Context& m_context;
deUint32 m_framebuffer;
deUint32 m_colorBuffer;
deUint32 m_depthStencilBuffer;
};
static std::vector<std::string> getEnablingExtensions (deUint32 format)
{
std::vector<std::string> out;
switch (format)
{
case GL_RGB16F:
out.push_back("GL_EXT_color_buffer_half_float");
break;
case GL_RGBA16F:
case GL_RG16F:
case GL_R16F:
out.push_back("GL_EXT_color_buffer_half_float");
case GL_RGBA32F:
case GL_RGB32F:
case GL_R11F_G11F_B10F:
case GL_RG32F:
case GL_R32F:
out.push_back("GL_EXT_color_buffer_float");
default:
break;
}
return out;
}
static bool isExtensionSupported (sglr::Context& context, const char* name)
{
std::istringstream extensions(context.getString(GL_EXTENSIONS));
std::string extension;
while (std::getline(extensions, extension, ' '))
{
if (extension == name)
return true;
}
return false;
}
static bool isAnyExtensionSupported (sglr::Context& context, const std::vector<std::string>& requiredExts)
{
if (requiredExts.empty())
return true;
for (std::vector<std::string>::const_iterator iter = requiredExts.begin(); iter != requiredExts.end(); iter++)
{
const std::string& extension = *iter;
if (isExtensionSupported(context, extension.c_str()))
return true;
}
return false;
}
template<typename T>
static std::string join (const std::vector<T>& list, const std::string& sep)
{
std::ostringstream out;
for (typename std::vector<T>::const_iterator iter = list.begin(); iter != list.end(); iter++)
{
if (iter != list.begin())
out << sep;
out << *iter;
}
return out.str();
}
static void checkColorFormatSupport (sglr::Context& context, deUint32 sizedFormat)
{
const std::vector<std::string> requiredExts = getEnablingExtensions(sizedFormat);
if (!isAnyExtensionSupported(context, requiredExts))
{
std::string errMsg = "Format not supported, requires "
+ ((requiredExts.size() == 1) ? requiredExts[0] : " one of the following: " + join(requiredExts, ", "));
throw tcu::NotSupportedError(errMsg);
}
}
Framebuffer::Framebuffer (sglr::Context& context, const FboConfig& config, int width, int height, deUint32 fbo, deUint32 colorBufferName, deUint32 depthStencilBufferName)
: m_config (config)
, m_context (context)
, m_framebuffer (fbo)
, m_colorBuffer (0)
, m_depthStencilBuffer (0)
{
// Verify that color format is supported
checkColorFormatSupport(context, config.colorFormat);
if (m_framebuffer == 0)
context.genFramebuffers(1, &m_framebuffer);
context.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
if (m_config.buffers & (GL_COLOR_BUFFER_BIT))
{
switch (m_config.colorType)
{
case GL_TEXTURE_2D:
m_colorBuffer = createTex2D(colorBufferName, m_config.colorFormat, width, height);
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorBuffer, 0);
break;
case GL_RENDERBUFFER:
m_colorBuffer = createRbo(colorBufferName, m_config.colorFormat, width, height);
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorBuffer);
break;
default:
TCU_FAIL("Unsupported type");
}
}
if (m_config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT))
{
switch (m_config.depthStencilType)
{
case GL_TEXTURE_2D: m_depthStencilBuffer = createTex2D(depthStencilBufferName, m_config.depthStencilFormat, width, height); break;
case GL_RENDERBUFFER: m_depthStencilBuffer = createRbo(depthStencilBufferName, m_config.depthStencilFormat, width, height); break;
default:
TCU_FAIL("Unsupported type");
}
}
for (int ndx = 0; ndx < 2; ndx++)
{
deUint32 bit = ndx ? GL_STENCIL_BUFFER_BIT : GL_DEPTH_BUFFER_BIT;
deUint32 point = ndx ? GL_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
if ((m_config.buffers & bit) == 0)
continue; /* Not used. */
switch (m_config.depthStencilType)
{
case GL_TEXTURE_2D: context.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, m_depthStencilBuffer, 0); break;
case GL_RENDERBUFFER: context.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, m_depthStencilBuffer); break;
default:
DE_ASSERT(false);
}
}
GLenum err = m_context.getError();
if (err != GL_NO_ERROR)
throw glu::Error(err, glu::getErrorStr(err).toString().c_str(), "", __FILE__, __LINE__);
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
}
Framebuffer::~Framebuffer (void)
{
m_context.deleteFramebuffers(1, &m_framebuffer);
destroyBuffer(m_colorBuffer, m_config.colorType);
destroyBuffer(m_depthStencilBuffer, m_config.depthStencilType);
}
void Framebuffer::checkCompleteness (void)
{
m_context.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
GLenum status = m_context.checkFramebufferStatus(GL_FRAMEBUFFER);
m_context.bindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw FboIncompleteException(status, __FILE__, __LINE__);
}
deUint32 Framebuffer::createTex2D (deUint32 name, GLenum format, int width, int height)
{
if (name == 0)
m_context.genTextures(1, &name);
m_context.bindTexture(GL_TEXTURE_2D, name);
m_context.texImage2D(GL_TEXTURE_2D, 0, format, width, height);
if (!deIsPowerOfTwo32(width) || !deIsPowerOfTwo32(height))
{
// Set wrap mode to clamp for NPOT FBOs
m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
m_context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return name;
}
deUint32 Framebuffer::createRbo (deUint32 name, GLenum format, int width, int height)
{
if (name == 0)
m_context.genRenderbuffers(1, &name);
m_context.bindRenderbuffer(GL_RENDERBUFFER, name);
m_context.renderbufferStorage(GL_RENDERBUFFER, format, width, height);
return name;
}
void Framebuffer::destroyBuffer (deUint32 name, GLenum type)
{
if (type == GL_TEXTURE_2D || type == GL_TEXTURE_CUBE_MAP)
m_context.deleteTextures(1, &name);
else if (type == GL_RENDERBUFFER)
m_context.deleteRenderbuffers(1, &name);
else
DE_ASSERT(type == GL_NONE);
}
static void createMetaballsTex2D (sglr::Context& context, deUint32 name, GLenum format, GLenum dataType, int width, int height)
{
tcu::TextureFormat texFormat = glu::mapGLTransferFormat(format, dataType);
tcu::TextureLevel level (texFormat, width, height);
tcu::fillWithMetaballs(level.getAccess(), 5, name ^ width ^ height);
context.bindTexture(GL_TEXTURE_2D, name);
context.texImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, dataType, level.getAccess().getDataPtr());
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
static void createQuadsTex2D (sglr::Context& context, deUint32 name, GLenum format, GLenum dataType, int width, int height)
{
tcu::TextureFormat texFormat = glu::mapGLTransferFormat(format, dataType);
tcu::TextureLevel level (texFormat, width, height);
tcu::fillWithRGBAQuads(level.getAccess());
context.bindTexture(GL_TEXTURE_2D, name);
context.texImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, dataType, level.getAccess().getDataPtr());
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
class FboRenderCase : public TestCase
{
public:
FboRenderCase (Context& context, const char* name, const char* description, const FboConfig& config);
virtual ~FboRenderCase (void) {}
virtual IterateResult iterate (void);
virtual void render (sglr::Context& fboContext, Surface& dst) = DE_NULL;
bool compare (const tcu::Surface& reference, const tcu::Surface& result);
protected:
const FboConfig m_config;
};
FboRenderCase::FboRenderCase (Context& context, const char* name, const char* description, const FboConfig& config)
: TestCase (context, name, description)
, m_config (config)
{
}
TestCase::IterateResult FboRenderCase::iterate (void)
{
tcu::Vec4 clearColor = tcu::Vec4(0.125f, 0.25f, 0.5f, 1.0f);
glu::RenderContext& renderCtx = m_context.getRenderContext();
const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget();
tcu::TestLog& log = m_testCtx.getLog();
const char* failReason = DE_NULL;
// Position & size for context
deRandom rnd;
deRandom_init(&rnd, deStringHash(getName()));
int width = deMin32(renderTarget.getWidth(), 128);
int height = deMin32(renderTarget.getHeight(), 128);
int xMax = renderTarget.getWidth()-width+1;
int yMax = renderTarget.getHeight()-height+1;
int x = deRandom_getUint32(&rnd) % xMax;
int y = deRandom_getUint32(&rnd) % yMax;
tcu::Surface gles3Frame (width, height);
tcu::Surface refFrame (width, height);
GLenum gles3Error;
GLenum refError;
// Render using GLES3
try
{
sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS, tcu::IVec4(x, y, width, height));
context.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
render(context, gles3Frame); // Call actual render func
gles3Error = context.getError();
}
catch (const FboIncompleteException& e)
{
if (e.getReason() == GL_FRAMEBUFFER_UNSUPPORTED)
{
// Mark test case as unsupported
log << e;
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
return STOP;
}
else
throw; // Propagate error
}
// Render reference image
{
sglr::ReferenceContextBuffers buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), renderTarget.getDepthBits(), renderTarget.getStencilBits(), width, height);
sglr::ReferenceContext context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
context.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
render(context, refFrame);
refError = context.getError();
}
// Compare error codes
bool errorCodesOk = (gles3Error == refError);
if (!errorCodesOk)
{
log << tcu::TestLog::Message << "Error code mismatch: got " << glu::getErrorStr(gles3Error) << ", expected " << glu::getErrorStr(refError) << tcu::TestLog::EndMessage;
failReason = "Got unexpected error";
}
// Compare images
bool imagesOk = compare(refFrame, gles3Frame);
if (!imagesOk && !failReason)
failReason = "Image comparison failed";
// Store test result
bool isOk = errorCodesOk && imagesOk;
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
isOk ? "Pass" : failReason);
return STOP;
}
bool FboRenderCase::compare (const tcu::Surface& reference, const tcu::Surface& result)
{
const tcu::RGBA threshold (tcu::max(getFormatThreshold(m_config.colorFormat), tcu::RGBA(12, 12, 12, 12)));
return tcu::bilinearCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", reference.getAccess(), result.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
}
namespace FboCases
{
class StencilClearsTest : public FboRenderCase
{
public:
StencilClearsTest (Context& context, const FboConfig& config);
virtual ~StencilClearsTest (void) {};
void render (sglr::Context& context, Surface& dst);
};
StencilClearsTest::StencilClearsTest (Context& context, const FboConfig& config)
: FboRenderCase (context, config.getName().c_str(), "Stencil clears", config)
{
}
void StencilClearsTest::render (sglr::Context& context, Surface& dst)
{
tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat);
glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat);
glu::DataType fboOutputType = getFragmentOutputType(colorFormat);
tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat);
Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin;
Vec4 fboOutBias = fboRangeInfo.valueMin;
Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType);
Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4);
deUint32 texToFboShaderID = context.createProgram(&texToFboShader);
deUint32 texFromFboShaderID = context.createProgram(&texFromFboShader);
deUint32 metaballsTex = 1;
deUint32 quadsTex = 2;
int width = 128;
int height = 128;
texToFboShader.setOutScaleBias(fboOutScale, fboOutBias);
texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
createQuadsTex2D(context, quadsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height);
createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, width, height);
Framebuffer fbo(context, m_config, width, height);
fbo.checkCompleteness();
// Bind framebuffer and clear
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, width, height);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Do stencil clears
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 16, 32, 120);
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
context.scissor(16, 32, 100, 64);
context.clearStencil(2);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Draw 2 textures with stecil tests
context.enable(GL_STENCIL_TEST);
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.stencilFunc(GL_EQUAL, 1, 0xffu);
texToFboShader.setUniforms(context, texToFboShaderID);
sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.stencilFunc(GL_EQUAL, 2, 0xffu);
texToFboShader.setUniforms(context, texToFboShaderID);
sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
if (fbo.getConfig().colorType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
texFromFboShader.setUniforms(context, texFromFboShaderID);
sglr::drawQuad(context, texFromFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
readPixels(context, dst, 0, 0, width, height, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
}
class SharedColorbufferTest : public FboRenderCase
{
public:
SharedColorbufferTest (Context& context, const FboConfig& config);
virtual ~SharedColorbufferTest (void) {};
void render (sglr::Context& context, Surface& dst);
};
SharedColorbufferTest::SharedColorbufferTest (Context& context, const FboConfig& config)
: FboRenderCase (context, config.getName().c_str(), "Shared colorbuffer", config)
{
}
void SharedColorbufferTest::render (sglr::Context& context, Surface& dst)
{
Texture2DShader texShader (DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4);
FlatColorShader flatShader (glu::TYPE_FLOAT_VEC4);
deUint32 texShaderID = context.createProgram(&texShader);
deUint32 flatShaderID = context.createProgram(&flatShader);
int width = 128;
int height = 128;
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0;
context.disable(GL_DITHER);
// Textures
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(context, metaballsTex, GL_RGBA, GL_UNSIGNED_BYTE, 64, 64);
context.viewport(0, 0, width, height);
// Fbo A
Framebuffer fboA(context, m_config, width, height);
fboA.checkCompleteness();
// Fbo B - don't create colorbuffer
FboConfig cfg = m_config;
cfg.buffers &= ~GL_COLOR_BUFFER_BIT;
cfg.colorType = GL_NONE;
cfg.colorFormat = GL_NONE;
Framebuffer fboB(context, cfg, width, height);
// Attach color buffer from fbo A
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
switch (m_config.colorType)
{
case GL_TEXTURE_2D:
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboA.getColorBuffer(), 0);
break;
case GL_RENDERBUFFER:
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fboA.getColorBuffer());
break;
default:
DE_ASSERT(DE_FALSE);
}
// Clear depth and stencil in fbo B
context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
// Render quads to fbo 1, with depth 0.0
context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
if (stencil)
{
// Stencil to 1 in fbo A
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
}
texShader.setUniforms(context, texShaderID);
context.enable(GL_DEPTH_TEST);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_DEPTH_TEST);
// Blend metaballs to fbo 2
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.enable(GL_BLEND);
context.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Render small quad that is only visible if depth buffer is not shared with fbo A - or there is no depth bits
context.bindTexture(GL_TEXTURE_2D, quadsTex);
context.enable(GL_DEPTH_TEST);
sglr::drawQuad(context, texShaderID, Vec3(0.5f, 0.5f, 0.5f), Vec3(1.0f, 1.0f, 0.5f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
flatShader.setColor(context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
// Clear subset of stencil buffer to 1
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 10, 12, 25);
context.clearStencil(1);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
// Render quad with stencil mask == 1
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 1, 0xffu);
sglr::drawQuad(context, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
// Get results
if (fboA.getConfig().colorType == GL_TEXTURE_2D)
{
texShader.setUniforms(context, texShaderID);
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.bindTexture(GL_TEXTURE_2D, fboA.getColorBuffer());
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
readPixels(context, dst, 0, 0, width, height, glu::mapGLInternalFormat(fboA.getConfig().colorFormat), Vec4(1.0f), Vec4(0.0f));
}
class SharedColorbufferClearsTest : public FboRenderCase
{
public:
SharedColorbufferClearsTest (Context& context, const FboConfig& config);
virtual ~SharedColorbufferClearsTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
SharedColorbufferClearsTest::SharedColorbufferClearsTest (Context& context, const FboConfig& config)
: FboRenderCase (context, config.getName().c_str(), "Shared colorbuffer clears", config)
{
}
void SharedColorbufferClearsTest::render (sglr::Context& context, Surface& dst)
{
tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat);
glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat);
int width = 128;
int height = 128;
deUint32 colorbuffer = 1;
// Check for format support.
checkColorFormatSupport(context, m_config.colorFormat);
// Single colorbuffer
if (m_config.colorType == GL_TEXTURE_2D)
{
context.bindTexture(GL_TEXTURE_2D, colorbuffer);
context.texImage2D(GL_TEXTURE_2D, 0, m_config.colorFormat, width, height);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
DE_ASSERT(m_config.colorType == GL_RENDERBUFFER);
context.bindRenderbuffer(GL_RENDERBUFFER, colorbuffer);
context.renderbufferStorage(GL_RENDERBUFFER, m_config.colorFormat, width, height);
}
// Multiple framebuffers sharing the colorbuffer
for (int fbo = 1; fbo <= 3; fbo++)
{
context.bindFramebuffer(GL_FRAMEBUFFER, fbo);
if (m_config.colorType == GL_TEXTURE_2D)
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorbuffer, 0);
else
context.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer);
}
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
// Check completeness
{
GLenum status = context.checkFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
throw FboIncompleteException(status, __FILE__, __LINE__);
}
// Render to them
context.viewport(0, 0, width, height);
context.clearColor(0.0f, 0.0f, 1.0f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT);
context.enable(GL_SCISSOR_TEST);
context.bindFramebuffer(GL_FRAMEBUFFER, 2);
context.clearColor(0.6f, 0.0f, 0.0f, 1.0f);
context.scissor(10, 10, 64, 64);
context.clear(GL_COLOR_BUFFER_BIT);
context.clearColor(0.0f, 0.6f, 0.0f, 1.0f);
context.scissor(60, 60, 40, 20);
context.clear(GL_COLOR_BUFFER_BIT);
context.bindFramebuffer(GL_FRAMEBUFFER, 3);
context.clearColor(0.0f, 0.0f, 0.6f, 1.0f);
context.scissor(20, 20, 100, 10);
context.clear(GL_COLOR_BUFFER_BIT);
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
context.clearColor(0.6f, 0.0f, 0.6f, 1.0f);
context.scissor(20, 20, 5, 100);
context.clear(GL_COLOR_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
if (m_config.colorType == GL_TEXTURE_2D)
{
Texture2DShader shader(DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4);
deUint32 shaderID = context.createProgram(&shader);
shader.setUniforms(context, shaderID);
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
sglr::drawQuad(context, shaderID, Vec3(-0.9f, -0.9f, 0.0f), Vec3(0.9f, 0.9f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
readPixels(context, dst, 0, 0, width, height, colorFormat, Vec4(1.0f), Vec4(0.0f));
}
class SharedDepthStencilTest : public FboRenderCase
{
public:
SharedDepthStencilTest (Context& context, const FboConfig& config);
virtual ~SharedDepthStencilTest (void) {};
static bool isConfigSupported (const FboConfig& config);
void render (sglr::Context& context, Surface& dst);
};
SharedDepthStencilTest::SharedDepthStencilTest (Context& context, const FboConfig& config)
: FboRenderCase (context, config.getName().c_str(), "Shared depth/stencilbuffer", config)
{
}
bool SharedDepthStencilTest::isConfigSupported (const FboConfig& config)
{
return (config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) != 0;
}
void SharedDepthStencilTest::render (sglr::Context& context, Surface& dst)
{
Texture2DShader texShader (DataTypes() << glu::TYPE_SAMPLER_2D, glu::TYPE_FLOAT_VEC4);
FlatColorShader flatShader (glu::TYPE_FLOAT_VEC4);
deUint32 texShaderID = context.createProgram(&texShader);
deUint32 flatShaderID = context.createProgram(&flatShader);
int width = 128;
int height = 128;
// bool depth = (m_config.buffers & GL_DEPTH_BUFFER_BIT) != 0;
bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0;
// Textures
deUint32 metaballsTex = 5;
deUint32 quadsTex = 6;
createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
context.viewport(0, 0, width, height);
// Fbo A
Framebuffer fboA(context, m_config, width, height);
fboA.checkCompleteness();
// Fbo B
FboConfig cfg = m_config;
cfg.buffers &= ~(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
cfg.depthStencilType = GL_NONE;
cfg.depthStencilFormat = GL_NONE;
Framebuffer fboB(context, cfg, width, height);
// Bind depth/stencil buffers from fbo A to fbo B
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
for (int ndx = 0; ndx < 2; ndx++)
{
deUint32 bit = ndx ? GL_STENCIL_BUFFER_BIT : GL_DEPTH_BUFFER_BIT;
deUint32 point = ndx ? GL_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
if ((m_config.buffers & bit) == 0)
continue;
switch (m_config.depthStencilType)
{
case GL_TEXTURE_2D: context.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, fboA.getDepthStencilBuffer(), 0); break;
case GL_RENDERBUFFER: context.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, fboA.getDepthStencilBuffer()); break;
default:
TCU_FAIL("Not implemented");
}
}
// Setup uniforms
texShader.setUniforms(context, texShaderID);
// Clear color to red and stencil to 1 in fbo B.
context.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
context.clearStencil(1);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.enable(GL_DEPTH_TEST);
// Render quad to fbo A
context.bindFramebuffer(GL_FRAMEBUFFER, fboA.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
if (stencil)
{
// Clear subset of stencil buffer to 0 in fbo A
context.enable(GL_SCISSOR_TEST);
context.scissor(10, 10, 12, 25);
context.clearStencil(0);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
}
// Render metaballs to fbo B
context.bindFramebuffer(GL_FRAMEBUFFER, fboB.getFramebuffer());
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, -1.0f), Vec3(1.0f, 1.0f, 1.0f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
// Render quad with stencil mask == 0
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 0, 0xffu);
context.useProgram(flatShaderID);
flatShader.setColor(context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f));
sglr::drawQuad(context, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
if (m_config.colorType == GL_TEXTURE_2D)
{
// Render both to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fboA.getColorBuffer());
sglr::drawQuad(context, texShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
context.bindTexture(GL_TEXTURE_2D, fboB.getColorBuffer());
sglr::drawQuad(context, texShaderID, Vec3(0.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
{
// Read results from fbo B
readPixels(context, dst, 0, 0, width, height, glu::mapGLInternalFormat(m_config.colorFormat), Vec4(1.0f), Vec4(0.0f));
}
}
#if 0
class TexSubImageAfterRenderTest : public FboRenderCase
{
public:
TexSubImageAfterRenderTest (Context& context, const FboConfig& config);
virtual ~TexSubImageAfterRenderTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
TexSubImageAfterRenderTest::TexSubImageAfterRenderTest (Context& context, const FboConfig& config)
: FboRenderCase(context, (string("after_render_") + config.getName()).c_str(), "TexSubImage after rendering to texture", config)
{
}
void TexSubImageAfterRenderTest::render (sglr::Context& context, Surface& dst)
{
using sglr::TexturedQuadOp;
bool isRGBA = true;
Surface fourQuads(Surface::PIXELFORMAT_RGB, 64, 64);
tcu::SurfaceUtil::fillWithFourQuads(fourQuads);
Surface metaballs(isRGBA ? Surface::PIXELFORMAT_RGBA : Surface::PIXELFORMAT_RGB, 64, 64);
tcu::SurfaceUtil::fillWithMetaballs(metaballs, 5, 3);
deUint32 fourQuadsTex = 1;
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGB, fourQuads);
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
deUint32 fboTex = 2;
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texImage2D(GL_TEXTURE_2D, 0, isRGBA ? GL_RGBA : GL_RGB, 128, 128);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
// Render to fbo
context.viewport(0, 0, 128, 128);
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0));
// Update texture using TexSubImage2D
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, metaballs);
// Draw to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
class TexSubImageBetweenRenderTest : public FboRenderCase
{
public:
TexSubImageBetweenRenderTest (Context& context, const FboConfig& config);
virtual ~TexSubImageBetweenRenderTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
TexSubImageBetweenRenderTest::TexSubImageBetweenRenderTest (Context& context, const FboConfig& config)
: FboRenderCase(context, (string("between_render_") + config.getName()).c_str(), "TexSubImage between rendering calls", config)
{
}
void TexSubImageBetweenRenderTest::render (sglr::Context& context, Surface& dst)
{
using sglr::TexturedQuadOp;
using sglr::BlendTextureOp;
bool isRGBA = true;
Surface fourQuads(Surface::PIXELFORMAT_RGB, 64, 64);
tcu::SurfaceUtil::fillWithFourQuads(fourQuads);
Surface metaballs(isRGBA ? Surface::PIXELFORMAT_RGBA : Surface::PIXELFORMAT_RGB, 64, 64);
tcu::SurfaceUtil::fillWithMetaballs(metaballs, 5, 3);
Surface metaballs2(Surface::PIXELFORMAT_RGBA, 64, 64);
tcu::SurfaceUtil::fillWithMetaballs(metaballs2, 5, 4);
deUint32 metaballsTex = 3;
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, metaballs2);
deUint32 fourQuadsTex = 1;
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.texImage2D(GL_TEXTURE_2D, 0, GL_RGB, fourQuads);
context.bindFramebuffer(GL_FRAMEBUFFER, 1);
deUint32 fboTex = 2;
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texImage2D(GL_TEXTURE_2D, 0, isRGBA ? GL_RGBA : GL_RGB, 128, 128);
context.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
context.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
// Render to fbo
context.viewport(0, 0, 128, 128);
context.bindTexture(GL_TEXTURE_2D, fourQuadsTex);
context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0));
// Update texture using TexSubImage2D
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.texSubImage2D(GL_TEXTURE_2D, 0, 32, 32, metaballs);
// Render again to fbo
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
context.draw(BlendTextureOp(0));
// Draw to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fboTex);
context.draw(TexturedQuadOp(-1.0f, -1.0f, 1.0f, 1.0f, 0));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
#endif
class ResizeTest : public FboRenderCase
{
public:
ResizeTest (Context& context, const FboConfig& config);
virtual ~ResizeTest (void) {}
void render (sglr::Context& context, Surface& dst);
};
ResizeTest::ResizeTest (Context& context, const FboConfig& config)
: FboRenderCase (context, config.getName().c_str(), "Resize framebuffer", config)
{
}
void ResizeTest::render (sglr::Context& context, Surface& dst)
{
tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat);
glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat);
glu::DataType fboOutputType = getFragmentOutputType(colorFormat);
tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat);
Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin;
Vec4 fboOutBias = fboRangeInfo.valueMin;
Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType);
Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4);
FlatColorShader flatShader (fboOutputType);
deUint32 texToFboShaderID = context.createProgram(&texToFboShader);
deUint32 texFromFboShaderID = context.createProgram(&texFromFboShader);
deUint32 flatShaderID = context.createProgram(&flatShader);
deUint32 quadsTex = 1;
deUint32 metaballsTex = 2;
bool depth = (m_config.buffers & GL_DEPTH_BUFFER_BIT) != 0;
bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0;
int initialWidth = 128;
int initialHeight = 128;
int newWidth = 64;
int newHeight = 32;
texToFboShader.setOutScaleBias(fboOutScale, fboOutBias);
texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
createQuadsTex2D(context, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(context, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 32, 32);
Framebuffer fbo(context, m_config, initialWidth, initialHeight);
fbo.checkCompleteness();
// Setup shaders
texToFboShader.setUniforms (context, texToFboShaderID);
texFromFboShader.setUniforms(context, texFromFboShaderID);
flatShader.setColor (context, flatShaderID, Vec4(0.0f, 1.0f, 0.0f, 1.0f) * fboOutScale + fboOutBias);
// Render quads
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
context.viewport(0, 0, initialWidth, initialHeight);
clearColorBuffer(context, colorFormat, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
if (fbo.getConfig().colorType == GL_TEXTURE_2D)
{
// Render fbo to screen
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer());
sglr::drawQuad(context, texFromFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Restore binding
context.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
}
// Resize buffers
switch (fbo.getConfig().colorType)
{
case GL_TEXTURE_2D:
context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer());
context.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().colorFormat, newWidth, newHeight);
break;
case GL_RENDERBUFFER:
context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getColorBuffer());
context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().colorFormat, newWidth, newHeight);
break;
default:
DE_ASSERT(DE_FALSE);
}
if (depth || stencil)
{
switch (fbo.getConfig().depthStencilType)
{
case GL_TEXTURE_2D:
context.bindTexture(GL_TEXTURE_2D, fbo.getDepthStencilBuffer());
context.texImage2D(GL_TEXTURE_2D, 0, fbo.getConfig().depthStencilFormat, newWidth, newHeight);
break;
case GL_RENDERBUFFER:
context.bindRenderbuffer(GL_RENDERBUFFER, fbo.getDepthStencilBuffer());
context.renderbufferStorage(GL_RENDERBUFFER, fbo.getConfig().depthStencilFormat, newWidth, newHeight);
break;
default:
DE_ASSERT(false);
}
}
// Render to resized fbo
context.viewport(0, 0, newWidth, newHeight);
clearColorBuffer(context, colorFormat, tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
context.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
context.enable(GL_DEPTH_TEST);
context.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(context, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(context, texToFboShaderID, Vec3(0.0f, 0.0f, -1.0f), Vec3(+1.0f, +1.0f, 1.0f));
context.disable(GL_DEPTH_TEST);
if (stencil)
{
context.enable(GL_SCISSOR_TEST);
context.clearStencil(1);
context.scissor(10, 10, 5, 15);
context.clear(GL_STENCIL_BUFFER_BIT);
context.disable(GL_SCISSOR_TEST);
context.enable(GL_STENCIL_TEST);
context.stencilFunc(GL_EQUAL, 1, 0xffu);
sglr::drawQuad(context, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(+1.0f, +1.0f, 0.0f));
context.disable(GL_STENCIL_TEST);
}
if (m_config.colorType == GL_TEXTURE_2D)
{
context.bindFramebuffer(GL_FRAMEBUFFER, 0);
context.viewport(0, 0, context.getWidth(), context.getHeight());
context.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer());
sglr::drawQuad(context, texFromFboShaderID, Vec3(-0.5f, -0.5f, 0.0f), Vec3(0.5f, 0.5f, 0.0f));
context.readPixels(dst, 0, 0, context.getWidth(), context.getHeight());
}
else
readPixels(context, dst, 0, 0, newWidth, newHeight, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
}
class RecreateBuffersTest : public FboRenderCase
{
public:
RecreateBuffersTest (Context& context, const FboConfig& config, deUint32 buffers, bool rebind);
virtual ~RecreateBuffersTest (void) {}
void render (sglr::Context& context, Surface& dst);
private:
deUint32 m_buffers;
bool m_rebind;
};
RecreateBuffersTest::RecreateBuffersTest (Context& context, const FboConfig& config, deUint32 buffers, bool rebind)
: FboRenderCase (context, (string(config.getName()) + (rebind ? "" : "_no_rebind")).c_str(), "Recreate buffers", config)
, m_buffers (buffers)
, m_rebind (rebind)
{
}
void RecreateBuffersTest::render (sglr::Context& ctx, Surface& dst)
{
tcu::TextureFormat colorFormat = glu::mapGLInternalFormat(m_config.colorFormat);
glu::DataType fboSamplerType = glu::getSampler2DType(colorFormat);
glu::DataType fboOutputType = getFragmentOutputType(colorFormat);
tcu::TextureFormatInfo fboRangeInfo = tcu::getTextureFormatInfo(colorFormat);
Vec4 fboOutScale = fboRangeInfo.valueMax - fboRangeInfo.valueMin;
Vec4 fboOutBias = fboRangeInfo.valueMin;
Texture2DShader texToFboShader (DataTypes() << glu::TYPE_SAMPLER_2D, fboOutputType);
Texture2DShader texFromFboShader (DataTypes() << fboSamplerType, glu::TYPE_FLOAT_VEC4);
FlatColorShader flatShader (fboOutputType);
deUint32 texToFboShaderID = ctx.createProgram(&texToFboShader);
deUint32 texFromFboShaderID = ctx.createProgram(&texFromFboShader);
deUint32 flatShaderID = ctx.createProgram(&flatShader);
int width = 128;
int height = 128;
deUint32 metaballsTex = 1;
deUint32 quadsTex = 2;
bool stencil = (m_config.buffers & GL_STENCIL_BUFFER_BIT) != 0;
createQuadsTex2D(ctx, quadsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
createMetaballsTex2D(ctx, metaballsTex, GL_RGB, GL_UNSIGNED_BYTE, 64, 64);
Framebuffer fbo(ctx, m_config, width, height);
fbo.checkCompleteness();
// Setup shaders
texToFboShader.setOutScaleBias(fboOutScale, fboOutBias);
texFromFboShader.setTexScaleBias(0, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
texToFboShader.setUniforms (ctx, texToFboShaderID);
texFromFboShader.setUniforms(ctx, texFromFboShaderID);
flatShader.setColor (ctx, flatShaderID, Vec4(0.0f, 0.0f, 1.0f, 1.0f) * fboOutScale + fboOutBias);
// Draw scene
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.viewport(0, 0, width, height);
clearColorBuffer(ctx, colorFormat, tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
ctx.clear(GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
ctx.enable(GL_DEPTH_TEST);
ctx.bindTexture(GL_TEXTURE_2D, quadsTex);
sglr::drawQuad(ctx, texToFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
ctx.disable(GL_DEPTH_TEST);
if (stencil)
{
ctx.enable(GL_SCISSOR_TEST);
ctx.scissor(width/4, height/4, width/2, height/2);
ctx.clearStencil(1);
ctx.clear(GL_STENCIL_BUFFER_BIT);
ctx.disable(GL_SCISSOR_TEST);
}
// Recreate buffers
if (!m_rebind)
ctx.bindFramebuffer(GL_FRAMEBUFFER, 0);
DE_ASSERT((m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) == 0 ||
(m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)) == (m_config.buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)));
// Recreate.
for (int ndx = 0; ndx < 2; ndx++)
{
deUint32 bit = ndx == 0 ? GL_COLOR_BUFFER_BIT
: (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
deUint32 type = ndx == 0 ? fbo.getConfig().colorType
: fbo.getConfig().depthStencilType;
deUint32 format = ndx == 0 ? fbo.getConfig().colorFormat
: fbo.getConfig().depthStencilFormat;
deUint32 buf = ndx == 0 ? fbo.getColorBuffer()
: fbo.getDepthStencilBuffer();
if ((m_buffers & bit) == 0)
continue;
switch (type)
{
case GL_TEXTURE_2D:
ctx.deleteTextures(1, &buf);
ctx.bindTexture(GL_TEXTURE_2D, buf);
ctx.texImage2D(GL_TEXTURE_2D, 0, format, width, height);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
ctx.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case GL_RENDERBUFFER:
ctx.deleteRenderbuffers(1, &buf);
ctx.bindRenderbuffer(GL_RENDERBUFFER, buf);
ctx.renderbufferStorage(GL_RENDERBUFFER, format, width, height);
break;
default:
DE_ASSERT(false);
}
}
// Rebind.
if (m_rebind)
{
for (int ndx = 0; ndx < 3; ndx++)
{
deUint32 bit = ndx == 0 ? GL_COLOR_BUFFER_BIT :
ndx == 1 ? GL_DEPTH_BUFFER_BIT :
ndx == 2 ? GL_STENCIL_BUFFER_BIT : 0;
deUint32 point = ndx == 0 ? GL_COLOR_ATTACHMENT0 :
ndx == 1 ? GL_DEPTH_ATTACHMENT :
ndx == 2 ? GL_STENCIL_ATTACHMENT : 0;
deUint32 type = ndx == 0 ? fbo.getConfig().colorType
: fbo.getConfig().depthStencilType;
deUint32 buf = ndx == 0 ? fbo.getColorBuffer()
: fbo.getDepthStencilBuffer();
if ((m_buffers & bit) == 0)
continue;
switch (type)
{
case GL_TEXTURE_2D:
ctx.framebufferTexture2D(GL_FRAMEBUFFER, point, GL_TEXTURE_2D, buf, 0);
break;
case GL_RENDERBUFFER:
ctx.framebufferRenderbuffer(GL_FRAMEBUFFER, point, GL_RENDERBUFFER, buf);
break;
default:
DE_ASSERT(false);
}
}
}
if (!m_rebind)
ctx.bindFramebuffer(GL_FRAMEBUFFER, fbo.getFramebuffer());
ctx.clearStencil(0);
ctx.clear(m_buffers & (GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT)); // \note Clear only buffers that were re-created
if (m_buffers & GL_COLOR_BUFFER_BIT)
{
// Clearing of integer buffers is undefined so do clearing by rendering flat color.
sglr::drawQuad(ctx, flatShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
}
ctx.enable(GL_DEPTH_TEST);
if (stencil)
{
// \note Stencil test enabled only if we have stencil buffer
ctx.enable(GL_STENCIL_TEST);
ctx.stencilFunc(GL_EQUAL, 0, 0xffu);
}
ctx.bindTexture(GL_TEXTURE_2D, metaballsTex);
sglr::drawQuad(ctx, texToFboShaderID, Vec3(-1.0f, -1.0f, 1.0f), Vec3(1.0f, 1.0f, -1.0f));
if (stencil)
ctx.disable(GL_STENCIL_TEST);
ctx.disable(GL_DEPTH_TEST);
if (fbo.getConfig().colorType == GL_TEXTURE_2D)
{
// Unbind fbo
ctx.bindFramebuffer(GL_FRAMEBUFFER, 0);
// Draw to screen
ctx.bindTexture(GL_TEXTURE_2D, fbo.getColorBuffer());
ctx.viewport(0, 0, ctx.getWidth(), ctx.getHeight());
sglr::drawQuad(ctx, texFromFboShaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
// Read from screen
ctx.readPixels(dst, 0, 0, ctx.getWidth(), ctx.getHeight());
}
else
{
// Read from fbo
readPixels(ctx, dst, 0, 0, width, height, colorFormat, fboRangeInfo.lookupScale, fboRangeInfo.lookupBias);
}
}
} // FboCases
FboRenderTestGroup::FboRenderTestGroup (Context& context)
: TestCaseGroup(context, "render", "Rendering Tests")
{
}
FboRenderTestGroup::~FboRenderTestGroup (void)
{
}
void FboRenderTestGroup::init (void)
{
static const deUint32 objectTypes[] =
{
GL_TEXTURE_2D,
GL_RENDERBUFFER
};
enum FormatType
{
FORMATTYPE_FLOAT = 0,
FORMATTYPE_FIXED,
FORMATTYPE_INT,
FORMATTYPE_UINT,
FORMATTYPE_LAST
};
// Required by specification.
static const struct
{
deUint32 format;
FormatType type;
} colorFormats[] =
{
{ GL_RGBA32F, FORMATTYPE_FLOAT },
{ GL_RGBA32I, FORMATTYPE_INT },
{ GL_RGBA32UI, FORMATTYPE_UINT },
{ GL_RGBA16F, FORMATTYPE_FLOAT },
{ GL_RGBA16I, FORMATTYPE_INT },
{ GL_RGBA16UI, FORMATTYPE_UINT },
{ GL_RGB16F, FORMATTYPE_FLOAT },
{ GL_RGBA8, FORMATTYPE_FIXED },
{ GL_RGBA8I, FORMATTYPE_INT },
{ GL_RGBA8UI, FORMATTYPE_UINT },
{ GL_SRGB8_ALPHA8, FORMATTYPE_FIXED },
{ GL_RGB10_A2, FORMATTYPE_FIXED },
{ GL_RGB10_A2UI, FORMATTYPE_UINT },
{ GL_RGBA4, FORMATTYPE_FIXED },
{ GL_RGB5_A1, FORMATTYPE_FIXED },
{ GL_RGB8, FORMATTYPE_FIXED },
{ GL_RGB565, FORMATTYPE_FIXED },
{ GL_R11F_G11F_B10F, FORMATTYPE_FLOAT },
{ GL_RG32F, FORMATTYPE_FLOAT },
{ GL_RG32I, FORMATTYPE_INT },
{ GL_RG32UI, FORMATTYPE_UINT },
{ GL_RG16F, FORMATTYPE_FLOAT },
{ GL_RG16I, FORMATTYPE_INT },
{ GL_RG16UI, FORMATTYPE_UINT },
{ GL_RG8, FORMATTYPE_FLOAT },
{ GL_RG8I, FORMATTYPE_INT },
{ GL_RG8UI, FORMATTYPE_UINT },
{ GL_R32F, FORMATTYPE_FLOAT },
{ GL_R32I, FORMATTYPE_INT },
{ GL_R32UI, FORMATTYPE_UINT },
{ GL_R16F, FORMATTYPE_FLOAT },
{ GL_R16I, FORMATTYPE_INT },
{ GL_R16UI, FORMATTYPE_UINT },
{ GL_R8, FORMATTYPE_FLOAT },
{ GL_R8I, FORMATTYPE_INT },
{ GL_R8UI, FORMATTYPE_UINT }
};
static const struct
{
deUint32 format;
bool depth;
bool stencil;
} depthStencilFormats[] =
{
{ GL_DEPTH_COMPONENT32F, true, false },
{ GL_DEPTH_COMPONENT24, true, false },
{ GL_DEPTH_COMPONENT16, true, false },
{ GL_DEPTH32F_STENCIL8, true, true },
{ GL_DEPTH24_STENCIL8, true, true },
{ GL_STENCIL_INDEX8, false, true }
};
using namespace FboCases;
// .stencil_clear
tcu::TestCaseGroup* stencilClearGroup = new tcu::TestCaseGroup(m_testCtx, "stencil_clear", "Stencil buffer clears");
addChild(stencilClearGroup);
for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++)
{
deUint32 colorType = GL_TEXTURE_2D;
deUint32 stencilType = GL_RENDERBUFFER;
deUint32 colorFmt = GL_RGBA8;
if (!depthStencilFormats[fmtNdx].stencil)
continue;
FboConfig config(GL_COLOR_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, colorType, colorFmt, stencilType, depthStencilFormats[fmtNdx].format);
stencilClearGroup->addChild(new StencilClearsTest(m_context, config));
}
// .shared_colorbuffer_clear
tcu::TestCaseGroup* sharedColorbufferClearGroup = new tcu::TestCaseGroup(m_testCtx, "shared_colorbuffer_clear", "Shader colorbuffer clears");
addChild(sharedColorbufferClearGroup);
for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++)
{
// Clearing of integer buffers is undefined.
if (colorFormats[colorFmtNdx].type == FORMATTYPE_INT || colorFormats[colorFmtNdx].type == FORMATTYPE_UINT)
continue;
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
FboConfig config(GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, GL_NONE, GL_NONE);
sharedColorbufferClearGroup->addChild(new SharedColorbufferClearsTest(m_context, config));
}
}
// .shared_colorbuffer
tcu::TestCaseGroup* sharedColorbufferGroup = new tcu::TestCaseGroup(m_testCtx, "shared_colorbuffer", "Shared colorbuffer tests");
addChild(sharedColorbufferGroup);
for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++)
{
deUint32 depthStencilType = GL_RENDERBUFFER;
deUint32 depthStencilFormat = GL_DEPTH24_STENCIL8;
// Blending with integer buffers and fp32 targets is not supported.
if (colorFormats[colorFmtNdx].type == FORMATTYPE_INT ||
colorFormats[colorFmtNdx].type == FORMATTYPE_UINT ||
colorFormats[colorFmtNdx].format == GL_RGBA32F ||
colorFormats[colorFmtNdx].format == GL_RGB32F ||
colorFormats[colorFmtNdx].format == GL_RG32F ||
colorFormats[colorFmtNdx].format == GL_R32F)
continue;
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
FboConfig colorOnlyConfig (GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, GL_NONE, GL_NONE);
FboConfig colorDepthConfig (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, depthStencilType, depthStencilFormat);
FboConfig colorDepthStencilConfig (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, objectTypes[typeNdx], colorFormats[colorFmtNdx].format, depthStencilType, depthStencilFormat);
sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorOnlyConfig));
sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorDepthConfig));
sharedColorbufferGroup->addChild(new SharedColorbufferTest(m_context, colorDepthStencilConfig));
}
}
// .shared_depth_stencil
tcu::TestCaseGroup* sharedDepthStencilGroup = new tcu::TestCaseGroup(m_testCtx, "shared_depth_stencil", "Shared depth and stencil buffers");
addChild(sharedDepthStencilGroup);
for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++)
{
deUint32 colorType = GL_TEXTURE_2D;
deUint32 colorFmt = GL_RGBA8;
bool depth = depthStencilFormats[fmtNdx].depth;
bool stencil = depthStencilFormats[fmtNdx].stencil;
if (!depth)
continue; // Not verified.
// Depth and stencil: both rbo and textures
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), colorType, colorFmt, objectTypes[typeNdx], depthStencilFormats[fmtNdx].format);
sharedDepthStencilGroup->addChild(new SharedDepthStencilTest(m_context, config));
}
}
// .resize
tcu::TestCaseGroup* resizeGroup = new tcu::TestCaseGroup(m_testCtx, "resize", "FBO resize tests");
addChild(resizeGroup);
for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++)
{
deUint32 colorFormat = colorFormats[colorFmtNdx].format;
// Color-only.
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
FboConfig config(GL_COLOR_BUFFER_BIT, objectTypes[typeNdx], colorFormat, GL_NONE, GL_NONE);
resizeGroup->addChild(new ResizeTest(m_context, config));
}
// For selected color formats tests depth & stencil variants.
if (colorFormat == GL_RGBA8 || colorFormat == GL_RGBA16F)
{
for (int depthStencilFmtNdx = 0; depthStencilFmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); depthStencilFmtNdx++)
{
deUint32 colorType = GL_TEXTURE_2D;
bool depth = depthStencilFormats[depthStencilFmtNdx].depth;
bool stencil = depthStencilFormats[depthStencilFmtNdx].stencil;
// Depth and stencil: both rbo and textures
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
if (!depth && objectTypes[typeNdx] != GL_RENDERBUFFER)
continue; // Not supported.
FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0),
colorType, colorFormat,
objectTypes[typeNdx], depthStencilFormats[depthStencilFmtNdx].format);
resizeGroup->addChild(new ResizeTest(m_context, config));
}
}
}
}
// .recreate_color
tcu::TestCaseGroup* recreateColorGroup = new tcu::TestCaseGroup(m_testCtx, "recreate_color", "Recreate colorbuffer tests");
addChild(recreateColorGroup);
for (int colorFmtNdx = 0; colorFmtNdx < DE_LENGTH_OF_ARRAY(colorFormats); colorFmtNdx++)
{
deUint32 colorFormat = colorFormats[colorFmtNdx].format;
deUint32 depthStencilFormat = GL_DEPTH24_STENCIL8;
deUint32 depthStencilType = GL_RENDERBUFFER;
// Color-only.
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
FboConfig config(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT, objectTypes[typeNdx], colorFormat, depthStencilType, depthStencilFormat);
recreateColorGroup->addChild(new RecreateBuffersTest(m_context, config, GL_COLOR_BUFFER_BIT, true /* rebind */));
}
}
// .recreate_depth_stencil
tcu::TestCaseGroup* recreateDepthStencilGroup = new tcu::TestCaseGroup(m_testCtx, "recreate_depth_stencil", "Recreate depth and stencil buffers");
addChild(recreateDepthStencilGroup);
for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(depthStencilFormats); fmtNdx++)
{
deUint32 colorType = GL_TEXTURE_2D;
deUint32 colorFmt = GL_RGBA8;
bool depth = depthStencilFormats[fmtNdx].depth;
bool stencil = depthStencilFormats[fmtNdx].stencil;
// Depth and stencil: both rbo and textures
for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(objectTypes); typeNdx++)
{
if (!depth && objectTypes[typeNdx] != GL_RENDERBUFFER)
continue;
FboConfig config(GL_COLOR_BUFFER_BIT|(depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), colorType, colorFmt, objectTypes[typeNdx], depthStencilFormats[fmtNdx].format);
recreateDepthStencilGroup->addChild(new RecreateBuffersTest(m_context, config, (depth ? GL_DEPTH_BUFFER_BIT : 0)|(stencil ? GL_STENCIL_BUFFER_BIT : 0), true /* rebind */));
}
}
}
} // Functional
} // gles3
} // deqp