/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 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 Stencil texturing tests. *//*--------------------------------------------------------------------*/ #include "es31fStencilTexturingTests.hpp" #include "gluStrUtil.hpp" #include "gluObjectWrapper.hpp" #include "gluRenderContext.hpp" #include "gluShaderProgram.hpp" #include "gluDrawUtil.hpp" #include "gluPixelTransfer.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "glsTextureTestUtil.hpp" #include "tcuVector.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuTestLog.hpp" #include "tcuTexLookupVerifier.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include "deStringUtil.hpp" namespace deqp { namespace gles31 { namespace Functional { using std::string; using std::vector; using tcu::IVec4; using tcu::TestLog; using tcu::TextureFormat; using tcu::TextureLevel; using tcu::Vec2; using tcu::Vec4; namespace { static void genTestRects(vector &rects, int width, int height) { int curWidth = width; int curHeight = height; int ndx = 0; for (;;) { rects.push_back(IVec4(width - curWidth, height - curHeight, curWidth, curHeight)); DE_ASSERT(curWidth >= 1 && curHeight >= 1); if (curWidth == 1 && curHeight == 1) break; else if (curHeight > 1 && ((ndx % 2) == 0 || curWidth == 1)) curHeight -= 1; else curWidth -= 1; ndx += 1; } } static void rectsToTriangles(const vector &rects, int width, int height, vector &positions, vector &indices) { const float w = float(width); const float h = float(height); positions.resize(rects.size() * 4); indices.resize(rects.size() * 6); for (int rectNdx = 0; rectNdx < (int)rects.size(); rectNdx++) { const int rx = rects[rectNdx].x(); const int ry = rects[rectNdx].y(); const int rw = rects[rectNdx].z(); const int rh = rects[rectNdx].w(); const float x0 = float(rx * 2) / w - 1.0f; const float x1 = float((rx + rw) * 2) / w - 1.0f; const float y0 = float(ry * 2) / h - 1.0f; const float y1 = float((ry + rh) * 2) / h - 1.0f; positions[rectNdx * 4 + 0] = Vec2(x0, y0); positions[rectNdx * 4 + 1] = Vec2(x1, y0); positions[rectNdx * 4 + 2] = Vec2(x0, y1); positions[rectNdx * 4 + 3] = Vec2(x1, y1); indices[rectNdx * 6 + 0] = (uint16_t)(rectNdx * 4 + 0); indices[rectNdx * 6 + 1] = (uint16_t)(rectNdx * 4 + 1); indices[rectNdx * 6 + 2] = (uint16_t)(rectNdx * 4 + 2); indices[rectNdx * 6 + 3] = (uint16_t)(rectNdx * 4 + 2); indices[rectNdx * 6 + 4] = (uint16_t)(rectNdx * 4 + 1); indices[rectNdx * 6 + 5] = (uint16_t)(rectNdx * 4 + 3); } } static void drawTestPattern(const glu::RenderContext &renderCtx, int width, int height) { const glu::ShaderProgram program(renderCtx, glu::ProgramSources() << glu::VertexSource("#version 300 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n") << glu::FragmentSource("#version 300 es\n" "void main (void) {}\n")); const glw::Functions &gl = renderCtx.getFunctions(); vector rects; vector positions; vector indices; if (!program.isOk()) throw tcu::TestError("Compile failed"); gl.useProgram(program.getProgram()); gl.viewport(0, 0, width, height); gl.clear(GL_STENCIL_BUFFER_BIT); gl.enable(GL_STENCIL_TEST); gl.stencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP); gl.stencilFunc(GL_ALWAYS, 0, ~0u); GLU_EXPECT_NO_ERROR(gl.getError(), "State setup failed"); genTestRects(rects, width, height); rectsToTriangles(rects, width, height, positions, indices); { const glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 2, (int)positions.size(), 0, positions[0].getPtr()); glu::draw(renderCtx, program.getProgram(), 1, &posBinding, glu::pr::Triangles((int)indices.size(), &indices[0])); } gl.disable(GL_STENCIL_TEST); } static void renderTestPatternReference(const tcu::PixelBufferAccess &dst) { const int stencilBits = tcu::getTextureFormatBitDepth(tcu::getEffectiveDepthStencilAccess(dst, tcu::Sampler::MODE_STENCIL).getFormat()) .x(); const uint32_t stencilMask = (1u << stencilBits) - 1u; vector rects; DE_ASSERT(dst.getFormat().order == TextureFormat::S || dst.getFormat().order == TextureFormat::DS); // clear depth and stencil if (dst.getFormat().order == TextureFormat::DS) tcu::clearDepth(dst, 0.0f); tcu::clearStencil(dst, 0u); genTestRects(rects, dst.getWidth(), dst.getHeight()); for (vector::const_iterator rectIter = rects.begin(); rectIter != rects.end(); ++rectIter) { const int x0 = rectIter->x(); const int y0 = rectIter->y(); const int x1 = x0 + rectIter->z(); const int y1 = y0 + rectIter->w(); for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { const int oldVal = dst.getPixStencil(x, y); const int newVal = (oldVal + 1) & stencilMask; dst.setPixStencil(newVal, x, y); } } } } static void blitStencilToColor2D(const glu::RenderContext &renderCtx, uint32_t srcTex, int width, int height) { const glu::ShaderProgram program( renderCtx, glu::ProgramSources() << glu::VertexSource("#version 300 es\n" "in highp vec4 a_position;\n" "in highp vec2 a_texCoord;\n" "out highp vec2 v_texCoord;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_texCoord = a_texCoord;\n" "}\n") << glu::FragmentSource("#version 300 es\n" "uniform highp usampler2D u_sampler;\n" "in highp vec2 v_texCoord;\n" "layout(location = 0) out highp uint o_stencil;\n" "void main (void)\n" "{\n" " o_stencil = texture(u_sampler, v_texCoord).x;\n" "}\n")); const float positions[] = {-1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f}; const float texCoord[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 2, 4, 0, &positions[0]), glu::va::Float("a_texCoord", 2, 4, 0, &texCoord[0])}; const uint8_t indices[] = {0, 1, 2, 2, 1, 3}; const glw::Functions &gl = renderCtx.getFunctions(); if (!program.isOk()) throw tcu::TestError("Compile failed"); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_2D, srcTex); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); GLU_EXPECT_NO_ERROR(gl.getError(), "Texture state setup failed"); gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_sampler"), 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed"); gl.viewport(0, 0, width, height); glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); } static void blitStencilToColor2DArray(const glu::RenderContext &renderCtx, uint32_t srcTex, int width, int height, int level) { const glu::ShaderProgram program( renderCtx, glu::ProgramSources() << glu::VertexSource("#version 300 es\n" "in highp vec4 a_position;\n" "in highp vec3 a_texCoord;\n" "out highp vec3 v_texCoord;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_texCoord = a_texCoord;\n" "}\n") << glu::FragmentSource("#version 300 es\n" "uniform highp usampler2DArray u_sampler;\n" "in highp vec3 v_texCoord;\n" "layout(location = 0) out highp uint o_stencil;\n" "void main (void)\n" "{\n" " o_stencil = texture(u_sampler, v_texCoord).x;\n" "}\n")); const float positions[] = {-1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f}; const float texCoord[] = {0.0f, 0.0f, float(level), 1.0f, 0.0f, float(level), 0.0f, 1.0f, float(level), 1.0f, 1.0f, float(level)}; const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 2, 4, 0, &positions[0]), glu::va::Float("a_texCoord", 3, 4, 0, &texCoord[0])}; const uint8_t indices[] = {0, 1, 2, 2, 1, 3}; const glw::Functions &gl = renderCtx.getFunctions(); if (!program.isOk()) throw tcu::TestError("Compile failed"); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_2D_ARRAY, srcTex); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); GLU_EXPECT_NO_ERROR(gl.getError(), "Texture state setup failed"); gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_sampler"), 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed"); gl.viewport(0, 0, width, height); glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); } static void blitStencilToColorCube(const glu::RenderContext &renderCtx, uint32_t srcTex, const float *texCoord, int width, int height) { const glu::ShaderProgram program( renderCtx, glu::ProgramSources() << glu::VertexSource("#version 300 es\n" "in highp vec4 a_position;\n" "in highp vec3 a_texCoord;\n" "out highp vec3 v_texCoord;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_texCoord = a_texCoord;\n" "}\n") << glu::FragmentSource( "#version 300 es\n" "uniform highp usamplerCube u_sampler;\n" "in highp vec3 v_texCoord;\n" "layout(location = 0) out highp vec4 o_color;\n" "void main (void)\n" "{\n" " o_color.x = float(texture(u_sampler, v_texCoord).x) / 255.0;\n" " o_color.yzw = vec3(0.0, 0.0, 1.0);\n" "}\n")); const float positions[] = {-1.0f, -1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f, +1.0f}; const glu::VertexArrayBinding vertexArrays[] = {glu::va::Float("a_position", 2, 4, 0, &positions[0]), glu::va::Float("a_texCoord", 3, 4, 0, texCoord)}; const uint8_t indices[] = {0, 1, 2, 2, 1, 3}; const glw::Functions &gl = renderCtx.getFunctions(); if (!program.isOk()) throw tcu::TestError("Compile failed"); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_CUBE_MAP, srcTex); gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); GLU_EXPECT_NO_ERROR(gl.getError(), "Texture state setup failed"); gl.useProgram(program.getProgram()); gl.uniform1i(gl.getUniformLocation(program.getProgram(), "u_sampler"), 0); GLU_EXPECT_NO_ERROR(gl.getError(), "Program setup failed"); gl.viewport(0, 0, width, height); glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); } static inline tcu::ConstPixelBufferAccess stencilToRedAccess(const tcu::ConstPixelBufferAccess &access) { DE_ASSERT(access.getFormat() == TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8)); return tcu::ConstPixelBufferAccess(TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT8), access.getSize(), access.getPitch(), access.getDataPtr()); } static bool compareStencilToRed(tcu::TestLog &log, const tcu::ConstPixelBufferAccess &stencilRef, const tcu::ConstPixelBufferAccess &result) { const int maxPrints = 10; int numFailed = 0; DE_ASSERT(stencilRef.getFormat().order == TextureFormat::S); DE_ASSERT(stencilRef.getWidth() == result.getWidth() && stencilRef.getHeight() == result.getHeight()); for (int y = 0; y < stencilRef.getHeight(); y++) { for (int x = 0; x < stencilRef.getWidth(); x++) { const int ref = stencilRef.getPixStencil(x, y); const int res = result.getPixelInt(x, y).x(); if (ref != res) { if (numFailed < maxPrints) log << TestLog::Message << "ERROR: Expected " << ref << ", got " << res << " at (" << x << ", " << y << ")" << TestLog::EndMessage; else if (numFailed == maxPrints) log << TestLog::Message << "..." << TestLog::EndMessage; numFailed += 1; } } } log << TestLog::Message << "Found " << numFailed << " faulty pixels, comparison " << (numFailed == 0 ? "passed." : "FAILED!") << TestLog::EndMessage; log << TestLog::ImageSet("ComparisonResult", "Image comparison result") << TestLog::Image("Result", "Result stencil buffer", result); if (numFailed > 0) log << TestLog::Image("Reference", "Reference stencil buffer", stencilToRedAccess(stencilRef)); log << TestLog::EndImageSet; return numFailed == 0; } static bool compareRedChannel(tcu::TestLog &log, const tcu::ConstPixelBufferAccess &result, int reference) { const int maxPrints = 10; int numFailed = 0; for (int y = 0; y < result.getHeight(); y++) { for (int x = 0; x < result.getWidth(); x++) { const int res = result.getPixelInt(x, y).x(); if (reference != res) { if (numFailed < maxPrints) log << TestLog::Message << "ERROR: Expected " << reference << ", got " << res << " at (" << x << ", " << y << ")" << TestLog::EndMessage; else if (numFailed == maxPrints) log << TestLog::Message << "..." << TestLog::EndMessage; numFailed += 1; } } } log << TestLog::Message << "Found " << numFailed << " faulty pixels, comparison " << (numFailed == 0 ? "passed." : "FAILED!") << TestLog::EndMessage; log << TestLog::ImageSet("ComparisonResult", "Image comparison result") << TestLog::Image("Result", "Result stencil buffer", result); log << TestLog::EndImageSet; return numFailed == 0; } static void stencilToUnorm8(const tcu::TextureCube &src, tcu::TextureCube &dst) { for (int levelNdx = 0; levelNdx < src.getNumLevels(); levelNdx++) { for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) { const tcu::CubeFace face = tcu::CubeFace(faceNdx); if (!src.isLevelEmpty(face, levelNdx)) { dst.allocLevel(face, levelNdx); const tcu::ConstPixelBufferAccess srcLevel = src.getLevelFace(levelNdx, face); const tcu::PixelBufferAccess dstLevel = dst.getLevelFace(levelNdx, face); for (int y = 0; y < src.getSize(); y++) for (int x = 0; x < src.getSize(); x++) dstLevel.setPixel(Vec4(float(srcLevel.getPixStencil(x, y)) / 255.f, 0.f, 0.f, 1.f), x, y); } } } } static void checkDepthStencilFormatSupport(Context &context, uint32_t format) { if (format == GL_STENCIL_INDEX8) { const char *reqExt = "GL_OES_texture_stencil8"; glu::ContextType contextType = context.getRenderContext().getType(); if (!glu::contextSupports(contextType, glu::ApiType::es(3, 2)) && !glu::contextSupports(contextType, glu::ApiType::core(4, 5)) && !context.getContextInfo().isExtensionSupported(reqExt)) throw tcu::NotSupportedError(glu::getTextureFormatStr(format).toString() + " requires " + reqExt); } else { DE_ASSERT(format == GL_DEPTH32F_STENCIL8 || format == GL_DEPTH24_STENCIL8); } } static void checkFramebufferStatus(const glw::Functions &gl) { const uint32_t status = gl.checkFramebufferStatus(GL_FRAMEBUFFER); if (status == GL_FRAMEBUFFER_UNSUPPORTED) throw tcu::NotSupportedError("Unsupported framebuffer configuration"); else if (status != GL_FRAMEBUFFER_COMPLETE) throw tcu::TestError("Incomplete framebuffer: " + glu::getFramebufferStatusStr(status).toString()); } class UploadTex2DCase : public TestCase { public: UploadTex2DCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 129; const int height = 113; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel uploadLevel(glu::mapGLInternalFormat(m_format), width, height); TextureLevel readLevel(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), width, height); TextureLevel stencilOnlyLevel(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), width, height); checkDepthStencilFormatSupport(m_context, m_format); renderTestPatternReference(uploadLevel); renderTestPatternReference(stencilOnlyLevel); gl.bindTexture(GL_TEXTURE_2D, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_2D, 1, m_format, width, height); glu::texSubImage2D(renderCtx, GL_TEXTURE_2D, 0, 0, 0, uploadLevel); GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading texture data failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); checkFramebufferStatus(gl); blitStencilToColor2D(renderCtx, *depthStencilTex, width, height); glu::readPixels(renderCtx, 0, 0, readLevel); { const bool compareOk = compareStencilToRed(m_testCtx.getLog(), stencilOnlyLevel, readLevel); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class UploadTex2DArrayCase : public TestCase { public: UploadTex2DArrayCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 41; const int height = 13; const int levels = 7; const int ptrnLevel = 3; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel uploadLevel(glu::mapGLInternalFormat(m_format), width, height, levels); checkDepthStencilFormatSupport(m_context, m_format); for (int levelNdx = 0; levelNdx < levels; levelNdx++) { const tcu::PixelBufferAccess levelAccess = tcu::getSubregion(uploadLevel.getAccess(), 0, 0, levelNdx, width, height, 1); if (levelNdx == ptrnLevel) renderTestPatternReference(levelAccess); else tcu::clearStencil(levelAccess, levelNdx); } gl.bindTexture(GL_TEXTURE_2D_ARRAY, *depthStencilTex); gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, m_format, width, height, levels); glu::texSubImage3D(renderCtx, GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, uploadLevel); GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading texture data failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); checkFramebufferStatus(gl); { TextureLevel readLevel(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), width, height); bool allLevelsOk = true; for (int levelNdx = 0; levelNdx < levels; levelNdx++) { tcu::ScopedLogSection section(m_testCtx.getLog(), "Level" + de::toString(levelNdx), "Level " + de::toString(levelNdx)); bool levelOk; blitStencilToColor2DArray(renderCtx, *depthStencilTex, width, height, levelNdx); glu::readPixels(renderCtx, 0, 0, readLevel); if (levelNdx == ptrnLevel) { TextureLevel reference(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), width, height); renderTestPatternReference(reference); levelOk = compareStencilToRed(m_testCtx.getLog(), reference, readLevel); } else levelOk = compareRedChannel(m_testCtx.getLog(), readLevel, levelNdx); if (!levelOk) { allLevelsOk = false; break; } } m_testCtx.setTestResult(allLevelsOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, allLevelsOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class UploadTexCubeCase : public TestCase { public: UploadTexCubeCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int size = 64; const int renderWidth = 128; const int renderHeight = 128; vector texCoord; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); tcu::TextureCube texData(glu::mapGLInternalFormat(m_format), size); tcu::TextureLevel result(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), renderWidth, renderHeight); checkDepthStencilFormatSupport(m_context, m_format); for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) { const tcu::CubeFace face = tcu::CubeFace(faceNdx); const int stencilVal = 42 * faceNdx; texData.allocLevel(face, 0); tcu::clearStencil(texData.getLevelFace(0, face), stencilVal); } glu::TextureTestUtil::computeQuadTexCoordCube(texCoord, tcu::CUBEFACE_NEGATIVE_X, Vec2(-1.5f, -1.3f), Vec2(1.3f, 1.4f)); gl.bindTexture(GL_TEXTURE_CUBE_MAP, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_CUBE_MAP, 1, m_format, size, size); for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) glu::texSubImage2D(renderCtx, glu::getGLCubeFace(tcu::CubeFace(faceNdx)), 0, 0, 0, texData.getLevelFace(0, tcu::CubeFace(faceNdx))); GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading texture data failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, renderWidth, renderHeight); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); checkFramebufferStatus(gl); blitStencilToColorCube(renderCtx, *depthStencilTex, &texCoord[0], renderWidth, renderHeight); glu::readPixels(renderCtx, 0, 0, result); { using namespace glu::TextureTestUtil; tcu::TextureCube redTex(TextureFormat(TextureFormat::R, TextureFormat::UNORM_INT8), size); const ReferenceParams sampleParams(TEXTURETYPE_CUBE, tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST)); tcu::LookupPrecision lookupPrec; tcu::LodPrecision lodPrec; bool compareOk; lookupPrec.colorMask = tcu::BVec4(true, true, true, true); lookupPrec.colorThreshold = tcu::computeFixedPointThreshold(IVec4(8, 8, 8, 8)); lookupPrec.coordBits = tcu::IVec3(22, 22, 22); lookupPrec.uvwBits = tcu::IVec3(5, 5, 0); lodPrec.lodBits = 7; lodPrec.derivateBits = 16; stencilToUnorm8(texData, redTex); compareOk = verifyTextureResult(m_testCtx, result, redTex, &texCoord[0], sampleParams, lookupPrec, lodPrec, tcu::PixelFormat(8, 8, 8, 8)); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class RenderTex2DCase : public TestCase { public: RenderTex2DCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 117; const int height = 193; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel result(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), width, height); TextureLevel reference(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), width, height); checkDepthStencilFormatSupport(m_context, m_format); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height); gl.bindTexture(GL_TEXTURE_2D, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_2D, 1, m_format, width, height); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, *depthStencilTex, 0); checkFramebufferStatus(gl); drawTestPattern(renderCtx, width, height); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); checkFramebufferStatus(gl); blitStencilToColor2D(renderCtx, *depthStencilTex, width, height); glu::readPixels(renderCtx, 0, 0, result.getAccess()); renderTestPatternReference(reference); { const bool compareOk = compareStencilToRed(m_testCtx.getLog(), reference, result); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class ClearTex2DCase : public TestCase { public: ClearTex2DCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 125; const int height = 117; const int cellSize = 8; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel result(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), width, height); TextureLevel reference(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), width, height); checkDepthStencilFormatSupport(m_context, m_format); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height); gl.bindTexture(GL_TEXTURE_2D, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_2D, 1, m_format, width, height); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, *depthStencilTex, 0); checkFramebufferStatus(gl); gl.enable(GL_SCISSOR_TEST); for (int y = 0; y < height; y += cellSize) { for (int x = 0; x < width; x += cellSize) { const int clearW = de::min(cellSize, width - x); const int clearH = de::min(cellSize, height - y); const int stencil = int((deInt32Hash(x) ^ deInt32Hash(y)) & 0xff); gl.clearStencil(stencil); gl.scissor(x, y, clearW, clearH); gl.clear(GL_STENCIL_BUFFER_BIT); tcu::clearStencil(tcu::getSubregion(reference.getAccess(), x, y, clearW, clearH), stencil); } } gl.disable(GL_SCISSOR_TEST); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); checkFramebufferStatus(gl); blitStencilToColor2D(renderCtx, *depthStencilTex, width, height); glu::readPixels(renderCtx, 0, 0, result.getAccess()); { const bool compareOk = compareStencilToRed(m_testCtx.getLog(), reference, result); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class CompareModeCase : public TestCase { public: CompareModeCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 64; const int height = 64; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel uploadLevel(glu::mapGLInternalFormat(m_format), width, height); TextureLevel readLevel(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), width, height); TextureLevel stencilOnlyLevel(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), width, height); checkDepthStencilFormatSupport(m_context, m_format); m_testCtx.getLog() << TestLog::Message << "NOTE: Texture compare mode has no effect when reading stencil values." << TestLog::EndMessage; renderTestPatternReference(uploadLevel); renderTestPatternReference(stencilOnlyLevel); gl.bindTexture(GL_TEXTURE_2D, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_2D, 1, m_format, width, height); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); glu::texSubImage2D(renderCtx, GL_TEXTURE_2D, 0, 0, 0, uploadLevel); GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading texture data failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, width, height); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); checkFramebufferStatus(gl); blitStencilToColor2D(renderCtx, *depthStencilTex, width, height); glu::readPixels(renderCtx, 0, 0, readLevel); { const bool compareOk = compareStencilToRed(m_testCtx.getLog(), stencilOnlyLevel, readLevel); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; class BaseLevelCase : public TestCase { public: BaseLevelCase(Context &context, const char *name, uint32_t format) : TestCase(context, name, glu::getTextureFormatName(format)) , m_format(format) { } IterateResult iterate(void) { const glu::RenderContext &renderCtx = m_context.getRenderContext(); const glw::Functions &gl = renderCtx.getFunctions(); const int width = 128; const int height = 128; const int levelNdx = 2; const int levelWidth = width >> levelNdx; const int levelHeight = height >> levelNdx; glu::Framebuffer fbo(renderCtx); glu::Renderbuffer colorBuf(renderCtx); glu::Texture depthStencilTex(renderCtx); TextureLevel uploadLevel(glu::mapGLInternalFormat(m_format), levelWidth, levelHeight); TextureLevel readLevel(TextureFormat(TextureFormat::RGBA, TextureFormat::UNSIGNED_INT32), levelWidth, levelHeight); TextureLevel stencilOnlyLevel(TextureFormat(TextureFormat::S, TextureFormat::UNSIGNED_INT8), levelWidth, levelHeight); checkDepthStencilFormatSupport(m_context, m_format); m_testCtx.getLog() << TestLog::Message << "GL_TEXTURE_BASE_LEVEL = " << levelNdx << TestLog::EndMessage; renderTestPatternReference(uploadLevel); renderTestPatternReference(stencilOnlyLevel); gl.bindTexture(GL_TEXTURE_2D, *depthStencilTex); gl.texStorage2D(GL_TEXTURE_2D, deLog2Floor32(de::max(width, height)) + 1, m_format, width, height); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, levelNdx); glu::texSubImage2D(renderCtx, GL_TEXTURE_2D, levelNdx, 0, 0, uploadLevel); GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading texture data failed"); gl.bindRenderbuffer(GL_RENDERBUFFER, *colorBuf); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, levelWidth, levelHeight); gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuf); checkFramebufferStatus(gl); blitStencilToColor2D(renderCtx, *depthStencilTex, levelWidth, levelHeight); glu::readPixels(renderCtx, 0, 0, readLevel); { const bool compareOk = compareStencilToRed(m_testCtx.getLog(), stencilOnlyLevel, readLevel); m_testCtx.setTestResult(compareOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, compareOk ? "Pass" : "Image comparison failed"); } return STOP; } private: const uint32_t m_format; }; } // namespace StencilTexturingTests::StencilTexturingTests(Context &context) : TestCaseGroup(context, "stencil_texturing", "Stencil texturing tests") { } StencilTexturingTests::~StencilTexturingTests(void) { } void StencilTexturingTests::init(void) { // .format { tcu::TestCaseGroup *const formatGroup = new tcu::TestCaseGroup(m_testCtx, "format", "Formats"); addChild(formatGroup); formatGroup->addChild(new UploadTex2DCase(m_context, "depth32f_stencil8_2d", GL_DEPTH32F_STENCIL8)); formatGroup->addChild(new UploadTex2DArrayCase(m_context, "depth32f_stencil8_2d_array", GL_DEPTH32F_STENCIL8)); formatGroup->addChild(new UploadTexCubeCase(m_context, "depth32f_stencil8_cube", GL_DEPTH32F_STENCIL8)); formatGroup->addChild(new UploadTex2DCase(m_context, "depth24_stencil8_2d", GL_DEPTH24_STENCIL8)); formatGroup->addChild(new UploadTex2DArrayCase(m_context, "depth24_stencil8_2d_array", GL_DEPTH24_STENCIL8)); formatGroup->addChild(new UploadTexCubeCase(m_context, "depth24_stencil8_cube", GL_DEPTH24_STENCIL8)); // OES_texture_stencil8 formatGroup->addChild(new UploadTex2DCase(m_context, "stencil_index8_2d", GL_STENCIL_INDEX8)); formatGroup->addChild(new UploadTex2DArrayCase(m_context, "stencil_index8_2d_array", GL_STENCIL_INDEX8)); formatGroup->addChild(new UploadTexCubeCase(m_context, "stencil_index8_cube", GL_STENCIL_INDEX8)); } // .render { tcu::TestCaseGroup *const readRenderGroup = new tcu::TestCaseGroup(m_testCtx, "render", "Read rendered stencil values"); addChild(readRenderGroup); readRenderGroup->addChild(new ClearTex2DCase(m_context, "depth32f_stencil8_clear", GL_DEPTH32F_STENCIL8)); readRenderGroup->addChild(new RenderTex2DCase(m_context, "depth32f_stencil8_draw", GL_DEPTH32F_STENCIL8)); readRenderGroup->addChild(new ClearTex2DCase(m_context, "depth24_stencil8_clear", GL_DEPTH24_STENCIL8)); readRenderGroup->addChild(new RenderTex2DCase(m_context, "depth24_stencil8_draw", GL_DEPTH24_STENCIL8)); } // .misc { tcu::TestCaseGroup *const miscGroup = new tcu::TestCaseGroup(m_testCtx, "misc", "Misc cases"); addChild(miscGroup); miscGroup->addChild(new CompareModeCase(m_context, "compare_mode_effect", GL_DEPTH24_STENCIL8)); miscGroup->addChild(new BaseLevelCase(m_context, "base_level", GL_DEPTH24_STENCIL8)); } } } // namespace Functional } // namespace gles31 } // namespace deqp