| /*------------------------------------------------------------------------- |
| * 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 Sample shading tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fSampleShadingTests.hpp" |
| #include "es31fMultisampleShaderRenderCase.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuSurface.hpp" |
| #include "glsStateQueryUtil.hpp" |
| #include "gluCallLogWrapper.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "deStringUtil.hpp" |
| #include "deRandom.hpp" |
| |
| #include <map> |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| using namespace gls::StateQueryUtil; |
| |
| class SampleShadingStateCase : public TestCase |
| { |
| public: |
| SampleShadingStateCase (Context& ctx, const char* name, const char* desc, QueryType); |
| |
| void init (void); |
| IterateResult iterate (void); |
| |
| private: |
| const QueryType m_verifier; |
| }; |
| |
| SampleShadingStateCase::SampleShadingStateCase (Context& ctx, const char* name, const char* desc, QueryType type) |
| : TestCase (ctx, name, desc) |
| , m_verifier (type) |
| { |
| } |
| |
| void SampleShadingStateCase::init (void) |
| { |
| if (!contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) |
| throw tcu::NotSupportedError("Test requires GL_OES_sample_shading extension or a context version 3.2 or higher."); |
| } |
| |
| SampleShadingStateCase::IterateResult SampleShadingStateCase::iterate (void) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); |
| gl.enableLogging(true); |
| |
| // initial |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying initial value" << tcu::TestLog::EndMessage; |
| verifyStateBoolean(result, gl, GL_SAMPLE_SHADING, false, m_verifier); |
| } |
| |
| // true and false too |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying random values" << tcu::TestLog::EndMessage; |
| |
| gl.glEnable(GL_SAMPLE_SHADING); |
| verifyStateBoolean(result, gl, GL_SAMPLE_SHADING, true, m_verifier); |
| |
| gl.glDisable(GL_SAMPLE_SHADING); |
| verifyStateBoolean(result, gl, GL_SAMPLE_SHADING, false, m_verifier); |
| } |
| |
| result.setTestContextResult(m_testCtx); |
| return STOP; |
| } |
| |
| class MinSampleShadingValueCase : public TestCase |
| { |
| public: |
| MinSampleShadingValueCase (Context& ctx, const char* name, const char* desc, QueryType); |
| |
| void init (void); |
| IterateResult iterate (void); |
| |
| private: |
| const QueryType m_verifier; |
| }; |
| |
| MinSampleShadingValueCase::MinSampleShadingValueCase (Context& ctx, const char* name, const char* desc, QueryType type) |
| : TestCase (ctx, name, desc) |
| , m_verifier (type) |
| { |
| } |
| |
| void MinSampleShadingValueCase::init (void) |
| { |
| if (!contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) |
| throw tcu::NotSupportedError("Test requires GL_OES_sample_shading extension or a context version 3.2 or higher."); |
| } |
| |
| MinSampleShadingValueCase::IterateResult MinSampleShadingValueCase::iterate (void) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); |
| |
| gl.enableLogging(true); |
| |
| // initial |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying initial value" << tcu::TestLog::EndMessage; |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.0, m_verifier); |
| } |
| |
| // special values |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying special values" << tcu::TestLog::EndMessage; |
| |
| gl.glMinSampleShading(0.0f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.0, m_verifier); |
| |
| gl.glMinSampleShading(1.0f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 1.0, m_verifier); |
| |
| gl.glMinSampleShading(0.5f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.5, m_verifier); |
| } |
| |
| // random values |
| { |
| const int numRandomTests = 10; |
| de::Random rnd (0xde123); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying random values" << tcu::TestLog::EndMessage; |
| |
| for (int randNdx = 0; randNdx < numRandomTests; ++randNdx) |
| { |
| const float value = rnd.getFloat(); |
| |
| gl.glMinSampleShading(value); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, value, m_verifier); |
| } |
| } |
| |
| result.setTestContextResult(m_testCtx); |
| return STOP; |
| } |
| |
| class MinSampleShadingValueClampingCase : public TestCase |
| { |
| public: |
| MinSampleShadingValueClampingCase (Context& ctx, const char* name, const char* desc); |
| |
| void init (void); |
| IterateResult iterate (void); |
| }; |
| |
| MinSampleShadingValueClampingCase::MinSampleShadingValueClampingCase (Context& ctx, const char* name, const char* desc) |
| : TestCase(ctx, name, desc) |
| { |
| } |
| |
| void MinSampleShadingValueClampingCase::init (void) |
| { |
| if (!contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) |
| throw tcu::NotSupportedError("Test requires GL_OES_sample_shading extension or a context version 3.2 or higher."); |
| } |
| |
| MinSampleShadingValueClampingCase::IterateResult MinSampleShadingValueClampingCase::iterate (void) |
| { |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); |
| gl.enableLogging(true); |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| // special values |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Verifying clamped values. Value is clamped when specified." << tcu::TestLog::EndMessage; |
| |
| gl.glMinSampleShading(-0.5f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.0, QUERY_FLOAT); |
| |
| gl.glMinSampleShading(-1.0f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.0, QUERY_FLOAT); |
| |
| gl.glMinSampleShading(-1.5f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 0.0, QUERY_FLOAT); |
| |
| gl.glMinSampleShading(1.5f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 1.0, QUERY_FLOAT); |
| |
| gl.glMinSampleShading(2.0f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 1.0, QUERY_FLOAT); |
| |
| gl.glMinSampleShading(2.5f); |
| verifyStateFloat(result, gl, GL_MIN_SAMPLE_SHADING_VALUE, 1.0, QUERY_FLOAT); |
| } |
| |
| result.setTestContextResult(m_testCtx); |
| return STOP; |
| } |
| |
| class SampleShadingRenderingCase : public MultisampleShaderRenderUtil::MultisampleRenderCase |
| { |
| public: |
| enum TestType |
| { |
| TEST_DISCARD = 0, |
| TEST_COLOR, |
| |
| TEST_LAST |
| }; |
| SampleShadingRenderingCase (Context& ctx, const char* name, const char* desc, RenderTarget target, int numSamples, TestType type); |
| ~SampleShadingRenderingCase (void); |
| |
| void init (void); |
| private: |
| void setShadingValue (int sampleCount); |
| |
| void preDraw (void); |
| void postDraw (void); |
| std::string getIterationDescription (int iteration) const; |
| |
| bool verifyImage (const tcu::Surface& resultImage); |
| |
| std::string genFragmentSource (int numSamples) const; |
| |
| enum |
| { |
| RENDER_SIZE = 128 |
| }; |
| |
| const TestType m_type; |
| }; |
| |
| SampleShadingRenderingCase::SampleShadingRenderingCase (Context& ctx, const char* name, const char* desc, RenderTarget target, int numSamples, TestType type) |
| : MultisampleShaderRenderUtil::MultisampleRenderCase (ctx, name, desc, numSamples, target, RENDER_SIZE) |
| , m_type (type) |
| { |
| DE_ASSERT(type < TEST_LAST); |
| } |
| |
| SampleShadingRenderingCase::~SampleShadingRenderingCase (void) |
| { |
| deinit(); |
| } |
| |
| void SampleShadingRenderingCase::init (void) |
| { |
| // requirements |
| |
| if (!contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) && !m_context.getContextInfo().isExtensionSupported("GL_OES_sample_shading")) |
| throw tcu::NotSupportedError("Test requires GL_OES_sample_shading extension or a context version 3.2 or higher."); |
| if (m_renderTarget == TARGET_DEFAULT && m_context.getRenderTarget().getNumSamples() <= 1) |
| throw tcu::NotSupportedError("Multisampled default framebuffer required"); |
| |
| // test purpose and expectations |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Verifying that a varying is given at least N different values for different samples within a single pixel.\n" |
| << " Render high-frequency function, map result to black/white. Modify N with glMinSampleShading().\n" |
| << " => Resulting image should contain N+1 shades of gray.\n" |
| << tcu::TestLog::EndMessage; |
| |
| // setup resources |
| |
| MultisampleShaderRenderUtil::MultisampleRenderCase::init(); |
| |
| // set iterations |
| |
| m_numIterations = m_numTargetSamples + 1; |
| } |
| |
| void SampleShadingRenderingCase::setShadingValue (int sampleCount) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (sampleCount == 0) |
| { |
| gl.disable(GL_SAMPLE_SHADING); |
| gl.minSampleShading(1.0f); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set ratio"); |
| } |
| else |
| { |
| // Minimum number of samples is max(ceil(<mss> * <samples>),1). Decrease mss with epsilon to prevent |
| // ceiling to a too large sample count. |
| const float epsilon = 0.25f / (float)m_numTargetSamples; |
| const float ratio = ((float)sampleCount / (float)m_numTargetSamples) - epsilon; |
| |
| gl.enable(GL_SAMPLE_SHADING); |
| gl.minSampleShading(ratio); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set ratio"); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Setting MIN_SAMPLE_SHADING_VALUE = " << ratio << "\n" |
| << "Requested sample count: shadingValue * numSamples = " << ratio << " * " << m_numTargetSamples << " = " << (ratio * (float)m_numTargetSamples) << "\n" |
| << "Minimum sample count: ceil(shadingValue * numSamples) = ceil(" << (ratio * (float)m_numTargetSamples) << ") = " << sampleCount |
| << tcu::TestLog::EndMessage; |
| |
| // can't fail with reasonable values of numSamples |
| DE_ASSERT(deFloatCeil(ratio * (float)m_numTargetSamples) == float(sampleCount)); |
| } |
| } |
| |
| void SampleShadingRenderingCase::preDraw (void) |
| { |
| setShadingValue(m_iteration); |
| } |
| |
| void SampleShadingRenderingCase::postDraw (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| gl.disable(GL_SAMPLE_SHADING); |
| gl.minSampleShading(1.0f); |
| } |
| |
| std::string SampleShadingRenderingCase::getIterationDescription (int iteration) const |
| { |
| if (iteration == 0) |
| return "Disabled SAMPLE_SHADING"; |
| else |
| return "Samples per pixel: " + de::toString(iteration); |
| } |
| |
| bool SampleShadingRenderingCase::verifyImage (const tcu::Surface& resultImage) |
| { |
| const int numShadesRequired = (m_iteration == 0) ? (2) : (m_iteration + 1); |
| const int rareThreshold = 100; |
| int rareCount = 0; |
| std::map<deUint32, int> shadeFrequency; |
| |
| // we should now have n+1 different shades of white, n = num samples |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Image("ResultImage", "Result Image", resultImage.getAccess()) |
| << tcu::TestLog::Message |
| << "Verifying image has (at least) " << numShadesRequired << " different shades.\n" |
| << "Excluding pixels with no full coverage (pixels on the shared edge of the triangle pair)." |
| << tcu::TestLog::EndMessage; |
| |
| for (int y = 0; y < RENDER_SIZE; ++y) |
| for (int x = 0; x < RENDER_SIZE; ++x) |
| { |
| const tcu::RGBA color = resultImage.getPixel(x, y); |
| const deUint32 packed = ((deUint32)color.getRed()) + ((deUint32)color.getGreen() << 8) + ((deUint32)color.getGreen() << 16); |
| |
| // on the triangle edge, skip |
| if (x == y) |
| continue; |
| |
| if (shadeFrequency.find(packed) == shadeFrequency.end()) |
| shadeFrequency[packed] = 1; |
| else |
| shadeFrequency[packed] = shadeFrequency[packed] + 1; |
| } |
| |
| for (std::map<deUint32, int>::const_iterator it = shadeFrequency.begin(); it != shadeFrequency.end(); ++it) |
| if (it->second < rareThreshold) |
| rareCount++; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Found " << (int)shadeFrequency.size() << " different shades.\n" |
| << "\tRare (less than " << rareThreshold << " pixels): " << rareCount << "\n" |
| << "\tCommon: " << (int)shadeFrequency.size() - rareCount << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if ((int)shadeFrequency.size() < numShadesRequired) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage; |
| return false; |
| } |
| return true; |
| } |
| |
| std::string SampleShadingRenderingCase::genFragmentSource (int numSamples) const |
| { |
| DE_UNREF(numSamples); |
| const glu::GLSLVersion version = contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) |
| ? glu::GLSL_VERSION_320_ES |
| : glu::GLSL_VERSION_310_ES; |
| std::ostringstream buf; |
| |
| buf << glu::getGLSLVersionDeclaration(version) << "\n" |
| "in highp vec4 v_position;\n" |
| "layout(location = 0) out mediump vec4 fragColor;\n" |
| "void main (void)\n" |
| "{\n" |
| " highp float field = dot(v_position.xy, v_position.xy) + dot(21.0 * v_position.xx, sin(3.1 * v_position.xy));\n" |
| " fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" |
| "\n" |
| " if (fract(field) > 0.5)\n"; |
| |
| if (m_type == TEST_DISCARD) |
| buf << " discard;\n"; |
| else if (m_type == TEST_COLOR) |
| buf << " fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n"; |
| else |
| DE_ASSERT(false); |
| |
| buf << "}"; |
| |
| return buf.str(); |
| } |
| |
| } // anonymous |
| |
| SampleShadingTests::SampleShadingTests (Context& context) |
| : TestCaseGroup(context, "sample_shading", "Test sample shading") |
| { |
| } |
| |
| SampleShadingTests::~SampleShadingTests (void) |
| { |
| } |
| |
| void SampleShadingTests::init (void) |
| { |
| tcu::TestCaseGroup* const stateQueryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "State query tests."); |
| tcu::TestCaseGroup* const minSamplesGroup = new tcu::TestCaseGroup(m_testCtx, "min_sample_shading", "Min sample shading tests."); |
| |
| addChild(stateQueryGroup); |
| addChild(minSamplesGroup); |
| |
| // .state query |
| { |
| stateQueryGroup->addChild(new SampleShadingStateCase (m_context, "sample_shading_is_enabled", "test SAMPLE_SHADING", QUERY_ISENABLED)); |
| stateQueryGroup->addChild(new SampleShadingStateCase (m_context, "sample_shading_get_boolean", "test SAMPLE_SHADING", QUERY_BOOLEAN)); |
| stateQueryGroup->addChild(new SampleShadingStateCase (m_context, "sample_shading_get_integer", "test SAMPLE_SHADING", QUERY_INTEGER)); |
| stateQueryGroup->addChild(new SampleShadingStateCase (m_context, "sample_shading_get_float", "test SAMPLE_SHADING", QUERY_FLOAT)); |
| stateQueryGroup->addChild(new SampleShadingStateCase (m_context, "sample_shading_get_integer64", "test SAMPLE_SHADING", QUERY_INTEGER64)); |
| stateQueryGroup->addChild(new MinSampleShadingValueCase (m_context, "min_sample_shading_value_get_boolean", "test MIN_SAMPLE_SHADING_VALUE", QUERY_BOOLEAN)); |
| stateQueryGroup->addChild(new MinSampleShadingValueCase (m_context, "min_sample_shading_value_get_integer", "test MIN_SAMPLE_SHADING_VALUE", QUERY_INTEGER)); |
| stateQueryGroup->addChild(new MinSampleShadingValueCase (m_context, "min_sample_shading_value_get_float", "test MIN_SAMPLE_SHADING_VALUE", QUERY_FLOAT)); |
| stateQueryGroup->addChild(new MinSampleShadingValueCase (m_context, "min_sample_shading_value_get_integer64", "test MIN_SAMPLE_SHADING_VALUE", QUERY_INTEGER64)); |
| stateQueryGroup->addChild(new MinSampleShadingValueClampingCase (m_context, "min_sample_shading_value_clamping", "test MIN_SAMPLE_SHADING_VALUE clamping")); |
| } |
| |
| // .min_sample_count |
| { |
| static const struct Target |
| { |
| SampleShadingRenderingCase::RenderTarget target; |
| int numSamples; |
| const char* name; |
| } targets[] = |
| { |
| { SampleShadingRenderingCase::TARGET_DEFAULT, 0, "default_framebuffer" }, |
| { SampleShadingRenderingCase::TARGET_TEXTURE, 2, "multisample_texture_samples_2" }, |
| { SampleShadingRenderingCase::TARGET_TEXTURE, 4, "multisample_texture_samples_4" }, |
| { SampleShadingRenderingCase::TARGET_TEXTURE, 8, "multisample_texture_samples_8" }, |
| { SampleShadingRenderingCase::TARGET_TEXTURE, 16, "multisample_texture_samples_16" }, |
| { SampleShadingRenderingCase::TARGET_RENDERBUFFER, 2, "multisample_renderbuffer_samples_2" }, |
| { SampleShadingRenderingCase::TARGET_RENDERBUFFER, 4, "multisample_renderbuffer_samples_4" }, |
| { SampleShadingRenderingCase::TARGET_RENDERBUFFER, 8, "multisample_renderbuffer_samples_8" }, |
| { SampleShadingRenderingCase::TARGET_RENDERBUFFER, 16, "multisample_renderbuffer_samples_16" }, |
| }; |
| |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(targets); ++ndx) |
| { |
| minSamplesGroup->addChild(new SampleShadingRenderingCase(m_context, (std::string(targets[ndx].name) + "_color").c_str(), "Test multiple samples per pixel with color", targets[ndx].target, targets[ndx].numSamples, SampleShadingRenderingCase::TEST_COLOR)); |
| minSamplesGroup->addChild(new SampleShadingRenderingCase(m_context, (std::string(targets[ndx].name) + "_discard").c_str(), "Test multiple samples per pixel with", targets[ndx].target, targets[ndx].numSamples, SampleShadingRenderingCase::TEST_DISCARD)); |
| } |
| } |
| } |
| |
| } // Functional |
| } // gles31 |
| } // deqp |