| /*------------------------------------------------------------------------- |
| * 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 Internal format query tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fInternalFormatQueryTests.hpp" |
| #include "tcuTestLog.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluStrUtil.hpp" |
| #include "gluContextInfo.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| class FormatSamplesCase : public TestCase |
| { |
| public: |
| enum FormatType |
| { |
| FORMAT_COLOR, |
| FORMAT_INT, |
| FORMAT_DEPTH_STENCIL |
| }; |
| |
| FormatSamplesCase(Context &ctx, const char *name, const char *desc, glw::GLenum texTarget, |
| glw::GLenum internalFormat, FormatType type); |
| |
| private: |
| void init(void); |
| IterateResult iterate(void); |
| |
| const glw::GLenum m_target; |
| const glw::GLenum m_internalFormat; |
| const FormatType m_type; |
| }; |
| |
| FormatSamplesCase::FormatSamplesCase(Context &ctx, const char *name, const char *desc, glw::GLenum target, |
| glw::GLenum internalFormat, FormatType type) |
| : TestCase(ctx, name, desc) |
| , m_target(target) |
| , m_internalFormat(internalFormat) |
| , m_type(type) |
| { |
| DE_ASSERT(m_target == GL_TEXTURE_2D_MULTISAMPLE || m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY || |
| m_target == GL_RENDERBUFFER); |
| } |
| |
| void FormatSamplesCase::init(void) |
| { |
| const bool isTextureTarget = |
| (m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY); |
| auto ctxType = m_context.getRenderContext().getType(); |
| const bool isES32orGL45 = glu::contextSupports(ctxType, glu::ApiType::es(3, 2)) || |
| glu::contextSupports(ctxType, glu::ApiType::core(4, 5)); |
| |
| if (!isES32orGL45 && m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY && |
| !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array")) |
| TCU_THROW(NotSupportedError, "Test requires OES_texture_storage_multisample_2d_array extension or a context " |
| "version equal or higher than 3.2"); |
| |
| // stencil8 textures are not supported without GL_OES_texture_stencil8 extension |
| if (!isES32orGL45 && isTextureTarget && m_internalFormat == GL_STENCIL_INDEX8 && |
| !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_stencil8")) |
| TCU_THROW(NotSupportedError, |
| "Test requires GL_OES_texture_stencil8 extension or a context version equal or higher than 3.2"); |
| } |
| |
| FormatSamplesCase::IterateResult FormatSamplesCase::iterate(void) |
| { |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| bool isFloatFormat = false; |
| bool error = false; |
| glw::GLint maxSamples = 0; |
| glw::GLint numSampleCounts = 0; |
| auto ctxType = m_context.getRenderContext().getType(); |
| const bool isES32orGL45 = glu::contextSupports(ctxType, glu::ApiType::es(3, 2)) || |
| glu::contextSupports(ctxType, glu::ApiType::core(4, 5)); |
| |
| if (!isES32orGL45) |
| { |
| if (m_internalFormat == GL_RGBA16F || m_internalFormat == GL_R32F || m_internalFormat == GL_RG32F || |
| m_internalFormat == GL_RGBA32F || m_internalFormat == GL_R16F || m_internalFormat == GL_RG16F || |
| m_internalFormat == GL_R11F_G11F_B10F) |
| { |
| TCU_THROW(NotSupportedError, "The internal format is not supported in a context lower than 3.2"); |
| } |
| } |
| else if (m_internalFormat == GL_RGBA16F || m_internalFormat == GL_R32F || m_internalFormat == GL_RG32F || |
| m_internalFormat == GL_RGBA32F) |
| { |
| isFloatFormat = true; |
| } |
| |
| // Lowest limit |
| { |
| const glw::GLenum samplesEnum = (m_type == FORMAT_COLOR) ? (GL_MAX_COLOR_TEXTURE_SAMPLES) : |
| (m_type == FORMAT_INT) ? (GL_MAX_INTEGER_SAMPLES) : |
| (GL_MAX_DEPTH_TEXTURE_SAMPLES); |
| m_testCtx.getLog() << tcu::TestLog::Message << "Format must support sample count of " |
| << glu::getGettableStateStr(samplesEnum) << tcu::TestLog::EndMessage; |
| |
| gl.getIntegerv(samplesEnum, &maxSamples); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get MAX_*_SAMPLES"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << glu::getGettableStateStr(samplesEnum) << " = " << maxSamples |
| << tcu::TestLog::EndMessage; |
| |
| if (maxSamples < 1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: minimum value of " |
| << glu::getGettableStateStr(samplesEnum) << " is 1" << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Number of sample counts |
| { |
| gl.getInternalformativ(m_target, m_internalFormat, GL_NUM_SAMPLE_COUNTS, 1, &numSampleCounts); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_NUM_SAMPLE_COUNTS"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL_NUM_SAMPLE_COUNTS = " << numSampleCounts |
| << tcu::TestLog::EndMessage; |
| |
| if (!isFloatFormat) |
| { |
| if (numSampleCounts < 1) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "ERROR: Format MUST support some multisample configuration, got GL_NUM_SAMPLE_COUNTS = " |
| << numSampleCounts << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| } |
| |
| // Sample counts |
| { |
| tcu::MessageBuilder samplesMsg(&m_testCtx.getLog()); |
| std::vector<glw::GLint> samples(numSampleCounts > 0 ? numSampleCounts : 1); |
| |
| if (numSampleCounts > 0 || isFloatFormat) |
| { |
| gl.getInternalformativ(m_target, m_internalFormat, GL_SAMPLES, numSampleCounts, &samples[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_SAMPLES"); |
| } |
| else |
| TCU_FAIL("glGetInternalFormativ() reported 0 supported sample counts"); |
| |
| // make a pretty log |
| |
| samplesMsg << "GL_SAMPLES = ["; |
| for (size_t ndx = 0; ndx < samples.size(); ++ndx) |
| { |
| if (ndx) |
| samplesMsg << ", "; |
| samplesMsg << samples[ndx]; |
| } |
| samplesMsg << "]" << tcu::TestLog::EndMessage; |
| |
| // Samples are in order |
| for (size_t ndx = 1; ndx < samples.size(); ++ndx) |
| { |
| if (samples[ndx - 1] <= samples[ndx]) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Samples must be ordered descending." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| break; |
| } |
| } |
| |
| // samples are positive |
| for (size_t ndx = 1; ndx < samples.size(); ++ndx) |
| { |
| if (samples[ndx - 1] <= 0) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Only positive SAMPLES allowed." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| break; |
| } |
| } |
| |
| // maxSamples must be supported |
| if (!isFloatFormat) |
| { |
| if (samples[0] < maxSamples) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: MAX_*_SAMPLES must be supported." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| } |
| |
| // Result |
| if (!error) |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); |
| |
| return STOP; |
| } |
| |
| class NumSampleCountsBufferCase : public TestCase |
| { |
| public: |
| NumSampleCountsBufferCase(Context &ctx, const char *name, const char *desc); |
| |
| private: |
| IterateResult iterate(void); |
| }; |
| |
| NumSampleCountsBufferCase::NumSampleCountsBufferCase(Context &ctx, const char *name, const char *desc) |
| : TestCase(ctx, name, desc) |
| { |
| } |
| |
| NumSampleCountsBufferCase::IterateResult NumSampleCountsBufferCase::iterate(void) |
| { |
| const glw::GLint defaultValue = -123; // queries always return positive values |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| bool error = false; |
| |
| // Query to larger buffer |
| { |
| glw::GLint buffer[2] = {defaultValue, defaultValue}; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying GL_NUM_SAMPLE_COUNTS to larger-than-needed buffer." |
| << tcu::TestLog::EndMessage; |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 2, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_NUM_SAMPLE_COUNTS"); |
| |
| if (buffer[1] != defaultValue) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: trailing values were modified." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Query to empty buffer |
| { |
| glw::GLint buffer[1] = {defaultValue}; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying GL_NUM_SAMPLE_COUNTS to zero-sized buffer." |
| << tcu::TestLog::EndMessage; |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 0, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_NUM_SAMPLE_COUNTS"); |
| |
| if (buffer[0] != defaultValue) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Query wrote over buffer bounds." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Result |
| if (!error) |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected buffer modification"); |
| |
| return STOP; |
| } |
| |
| class SamplesBufferCase : public TestCase |
| { |
| public: |
| SamplesBufferCase(Context &ctx, const char *name, const char *desc); |
| |
| private: |
| IterateResult iterate(void); |
| }; |
| |
| SamplesBufferCase::SamplesBufferCase(Context &ctx, const char *name, const char *desc) : TestCase(ctx, name, desc) |
| { |
| } |
| |
| SamplesBufferCase::IterateResult SamplesBufferCase::iterate(void) |
| { |
| const glw::GLint defaultValue = -123; // queries always return positive values |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| bool error = false; |
| |
| glw::GLint numSampleCounts = 0; |
| |
| // Number of sample counts |
| { |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1, &numSampleCounts); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_NUM_SAMPLE_COUNTS"); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "GL_NUM_SAMPLE_COUNTS = " << numSampleCounts |
| << tcu::TestLog::EndMessage; |
| } |
| |
| if (numSampleCounts < 1) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "ERROR: Format MUST support some multisample configuration, got GL_NUM_SAMPLE_COUNTS = " |
| << numSampleCounts << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| else |
| { |
| // Query to larger buffer |
| { |
| std::vector<glw::GLint> buffer(numSampleCounts + 1, defaultValue); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying GL_SAMPLES to larger-than-needed buffer." |
| << tcu::TestLog::EndMessage; |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_SAMPLES, (glw::GLsizei)buffer.size(), |
| &buffer[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_SAMPLES"); |
| |
| if (buffer.back() != defaultValue) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: trailing value was modified." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Query to smaller buffer |
| if (numSampleCounts > 2) |
| { |
| glw::GLint buffer[3] = {defaultValue, defaultValue, defaultValue}; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying GL_SAMPLES to buffer with bufSize=2." |
| << tcu::TestLog::EndMessage; |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_SAMPLES, 2, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_SAMPLES"); |
| |
| if (buffer[2] != defaultValue) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Query wrote over buffer bounds." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| |
| // Query to empty buffer |
| { |
| glw::GLint buffer[1] = {defaultValue}; |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Querying GL_SAMPLES to zero-sized buffer." |
| << tcu::TestLog::EndMessage; |
| gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_RGBA8, GL_SAMPLES, 0, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_SAMPLES"); |
| |
| if (buffer[0] != defaultValue) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "ERROR: Query wrote over buffer bounds." |
| << tcu::TestLog::EndMessage; |
| error = true; |
| } |
| } |
| } |
| |
| // Result |
| if (!error) |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected buffer modification"); |
| |
| return STOP; |
| } |
| |
| } // namespace |
| |
| InternalFormatQueryTests::InternalFormatQueryTests(Context &context) |
| : TestCaseGroup(context, "internal_format", "Internal format queries") |
| { |
| } |
| |
| InternalFormatQueryTests::~InternalFormatQueryTests(void) |
| { |
| } |
| |
| void InternalFormatQueryTests::init(void) |
| { |
| static const struct InternalFormat |
| { |
| const char *name; |
| glw::GLenum format; |
| FormatSamplesCase::FormatType type; |
| } internalFormats[] = { |
| // color renderable |
| {"r8", GL_R8, FormatSamplesCase::FORMAT_COLOR}, |
| {"rg8", GL_RG8, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgb8", GL_RGB8, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgb565", GL_RGB565, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgba4", GL_RGBA4, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgb5_a1", GL_RGB5_A1, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgba8", GL_RGBA8, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgb10_a2", GL_RGB10_A2, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgb10_a2ui", GL_RGB10_A2UI, FormatSamplesCase::FORMAT_INT}, |
| {"srgb8_alpha8", GL_SRGB8_ALPHA8, FormatSamplesCase::FORMAT_COLOR}, |
| {"r8i", GL_R8I, FormatSamplesCase::FORMAT_INT}, |
| {"r8ui", GL_R8UI, FormatSamplesCase::FORMAT_INT}, |
| {"r16i", GL_R16I, FormatSamplesCase::FORMAT_INT}, |
| {"r16ui", GL_R16UI, FormatSamplesCase::FORMAT_INT}, |
| {"r32i", GL_R32I, FormatSamplesCase::FORMAT_INT}, |
| {"r32ui", GL_R32UI, FormatSamplesCase::FORMAT_INT}, |
| {"rg8i", GL_RG8I, FormatSamplesCase::FORMAT_INT}, |
| {"rg8ui", GL_RG8UI, FormatSamplesCase::FORMAT_INT}, |
| {"rg16i", GL_RG16I, FormatSamplesCase::FORMAT_INT}, |
| {"rg16ui", GL_RG16UI, FormatSamplesCase::FORMAT_INT}, |
| {"rg32i", GL_RG32I, FormatSamplesCase::FORMAT_INT}, |
| {"rg32ui", GL_RG32UI, FormatSamplesCase::FORMAT_INT}, |
| {"rgba8i", GL_RGBA8I, FormatSamplesCase::FORMAT_INT}, |
| {"rgba8ui", GL_RGBA8UI, FormatSamplesCase::FORMAT_INT}, |
| {"rgba16i", GL_RGBA16I, FormatSamplesCase::FORMAT_INT}, |
| {"rgba16ui", GL_RGBA16UI, FormatSamplesCase::FORMAT_INT}, |
| {"rgba32i", GL_RGBA32I, FormatSamplesCase::FORMAT_INT}, |
| {"rgba32ui", GL_RGBA32UI, FormatSamplesCase::FORMAT_INT}, |
| |
| // float formats |
| {"r16f", GL_R16F, FormatSamplesCase::FORMAT_COLOR}, |
| {"rg16f", GL_RG16F, FormatSamplesCase::FORMAT_COLOR}, |
| {"rgba16f", GL_RGBA16F, FormatSamplesCase::FORMAT_COLOR}, |
| {"r32f", GL_R32F, FormatSamplesCase::FORMAT_INT}, |
| {"rg32f", GL_RG32F, FormatSamplesCase::FORMAT_INT}, |
| {"rgba32f", GL_RGBA32F, FormatSamplesCase::FORMAT_INT}, |
| {"r11f_g11f_b10f", GL_R11F_G11F_B10F, FormatSamplesCase::FORMAT_COLOR}, |
| |
| // depth renderable |
| {"depth_component16", GL_DEPTH_COMPONENT16, FormatSamplesCase::FORMAT_DEPTH_STENCIL}, |
| {"depth_component24", GL_DEPTH_COMPONENT24, FormatSamplesCase::FORMAT_DEPTH_STENCIL}, |
| {"depth_component32f", GL_DEPTH_COMPONENT32F, FormatSamplesCase::FORMAT_DEPTH_STENCIL}, |
| {"depth24_stencil8", GL_DEPTH24_STENCIL8, FormatSamplesCase::FORMAT_DEPTH_STENCIL}, |
| {"depth32f_stencil8", GL_DEPTH32F_STENCIL8, FormatSamplesCase::FORMAT_DEPTH_STENCIL}, |
| |
| // stencil renderable |
| {"stencil_index8", GL_STENCIL_INDEX8, FormatSamplesCase::FORMAT_DEPTH_STENCIL} |
| // DEPTH24_STENCIL8, duplicate |
| // DEPTH32F_STENCIL8 duplicate |
| }; |
| |
| static const struct |
| { |
| const char *name; |
| uint32_t target; |
| } textureTargets[] = { |
| {"renderbuffer", GL_RENDERBUFFER}, |
| {"texture_2d_multisample", GL_TEXTURE_2D_MULTISAMPLE}, |
| {"texture_2d_multisample_array", GL_TEXTURE_2D_MULTISAMPLE_ARRAY}, |
| }; |
| |
| for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(textureTargets); ++groupNdx) |
| { |
| tcu::TestCaseGroup *const group = |
| new tcu::TestCaseGroup(m_testCtx, textureTargets[groupNdx].name, |
| glu::getInternalFormatTargetName(textureTargets[groupNdx].target)); |
| const glw::GLenum texTarget = textureTargets[groupNdx].target; |
| |
| addChild(group); |
| |
| for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(internalFormats); ++caseNdx) |
| { |
| const std::string name = std::string(internalFormats[caseNdx].name) + "_samples"; |
| const std::string desc = std::string("Verify GL_SAMPLES of ") + internalFormats[caseNdx].name; |
| |
| group->addChild(new FormatSamplesCase(m_context, name.c_str(), desc.c_str(), texTarget, |
| internalFormats[caseNdx].format, internalFormats[caseNdx].type)); |
| } |
| } |
| |
| // Check buffer sizes are honored |
| { |
| tcu::TestCaseGroup *const group = |
| new tcu::TestCaseGroup(m_testCtx, "partial_query", "Query data to too short a buffer"); |
| |
| addChild(group); |
| |
| group->addChild(new NumSampleCountsBufferCase(m_context, "num_sample_counts", |
| "Query GL_NUM_SAMPLE_COUNTS to too short a buffer")); |
| group->addChild(new SamplesBufferCase(m_context, "samples", "Query GL_SAMPLES to too short a buffer")); |
| } |
| } |
| |
| } // namespace Functional |
| } // namespace gles31 |
| } // namespace deqp |