/*------------------------------------------------------------------------- * drawElements Quality Program EGL 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 Config query tests. *//*--------------------------------------------------------------------*/ #include "teglQueryConfigTests.hpp" #include "teglSimpleConfigCase.hpp" #include "tcuTestLog.hpp" #include "tcuTestContext.hpp" #include "tcuCommandLine.hpp" #include "egluCallLogWrapper.hpp" #include "egluStrUtil.hpp" #include "egluUtil.hpp" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" #include "deRandom.hpp" #include #include namespace deqp { namespace egl { using eglu::ConfigInfo; using tcu::TestLog; using namespace eglw; static void logConfigAttribute(TestLog &log, EGLenum attrib, EGLint value) { log << TestLog::Message << " " << eglu::getConfigAttribName(attrib) << ": " << eglu::getConfigAttribValueStr(attrib, value) << TestLog::EndMessage; } static bool isAttributePresent(const eglu::Version &version, EGLenum attribute) { switch (attribute) { case EGL_CONFORMANT: if (version < eglu::Version(1, 3)) return false; break; case EGL_LUMINANCE_SIZE: case EGL_ALPHA_MASK_SIZE: case EGL_COLOR_BUFFER_TYPE: case EGL_MATCH_NATIVE_PIXMAP: if (version < eglu::Version(1, 2)) return false; break; case EGL_BIND_TO_TEXTURE_RGB: case EGL_BIND_TO_TEXTURE_RGBA: case EGL_MAX_SWAP_INTERVAL: case EGL_MIN_SWAP_INTERVAL: case EGL_RENDERABLE_TYPE: if (version < eglu::Version(1, 1)) return false; break; default: break; } return true; } static bool hasRequiredExtension(const eglw::Library &egl, EGLDisplay display, EGLenum attribute) { switch (attribute) { case EGL_RECORDABLE_ANDROID: return eglu::hasExtension(egl, display, "EGL_ANDROID_recordable"); default: break; } return true; } class GetConfigsBoundsCase : public TestCase, protected eglu::CallLogWrapper { public: GetConfigsBoundsCase(EglTestContext &eglTestCtx, const char *name, const char *description) : TestCase(eglTestCtx, name, description) , CallLogWrapper(eglTestCtx.getLibrary(), eglTestCtx.getTestContext().getLog()) , m_display(EGL_NO_DISPLAY) { } void init(void) { DE_ASSERT(m_display == EGL_NO_DISPLAY); m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } void deinit(void) { m_eglTestCtx.getLibrary().terminate(m_display); m_display = EGL_NO_DISPLAY; } void checkGetConfigsBounds(de::Random &rnd, const int numConfigAll, const int numConfigRequested) { tcu::TestLog &log = m_testCtx.getLog(); std::vector buffer(numConfigAll + 10); std::vector magicBuffer((buffer.size() * sizeof(EGLConfig)) / sizeof(uint32_t) + 1); const EGLConfig *magicConfigs = reinterpret_cast(&magicBuffer[0]); int numConfigReturned; // Fill buffers with magic for (size_t ndx = 0; ndx < magicBuffer.size(); ndx++) magicBuffer[ndx] = rnd.getUint32(); for (size_t ndx = 0; ndx < buffer.size(); ndx++) buffer[ndx] = magicConfigs[ndx]; eglGetConfigs(m_display, &buffer[0], numConfigRequested, &numConfigReturned); eglu::checkError(eglGetError(), DE_NULL, __FILE__, __LINE__); log << TestLog::Message << numConfigReturned << " configs returned" << TestLog::EndMessage; // Compare results with stored magic { int numOverwritten = 0; for (size_t ndx = 0; ndx < buffer.size(); ndx++) { if (buffer[ndx] == magicConfigs[ndx]) { numOverwritten = (int)ndx; break; } } log << TestLog::Message << numOverwritten << " values actually written" << TestLog::EndMessage; if (numConfigReturned > deMax32(numConfigRequested, 0)) { log << TestLog::Message << "Fail, more configs returned than requested." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Too many configs returned"); } if (numOverwritten > deMax32(numConfigReturned, 0)) { log << TestLog::Message << "Fail, buffer overflow detected." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Buffer overflow"); } else if (numOverwritten != numConfigReturned) { log << TestLog::Message << "Fail, reported number of returned configs differs from number of values written." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Incorrect size"); } } } IterateResult iterate(void) { tcu::TestLog &log = m_testCtx.getLog(); EGLint numConfigAll; enableLogging(true); eglGetConfigs(m_display, 0, 0, &numConfigAll); log << TestLog::Message << numConfigAll << " configs available" << TestLog::EndMessage; log << TestLog::Message << TestLog::EndMessage; if (numConfigAll > 0) { de::Random rnd(123); for (int i = 0; i < 5; i++) { checkGetConfigsBounds(rnd, numConfigAll, rnd.getInt(0, numConfigAll)); log << TestLog::Message << TestLog::EndMessage; } checkGetConfigsBounds(rnd, numConfigAll, -1); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "No configs"); } enableLogging(false); return STOP; } protected: EGLDisplay m_display; }; class GetConfigAttribCase : public TestCase, protected eglu::CallLogWrapper { public: GetConfigAttribCase(EglTestContext &eglTestCtx, const char *name, const char *description); void init(void); void deinit(void); IterateResult iterate(void); EGLint getValue(EGLConfig config, EGLenum attrib, bool logValue = true); virtual void executeTest(EGLConfig config) = 0; protected: EGLDisplay m_display; private: std::vector m_configs; std::vector::const_iterator m_configsIter; }; GetConfigAttribCase::GetConfigAttribCase(EglTestContext &eglTestCtx, const char *name, const char *description) : TestCase(eglTestCtx, name, description) , CallLogWrapper(eglTestCtx.getLibrary(), eglTestCtx.getTestContext().getLog()) , m_display(EGL_NO_DISPLAY) { } void GetConfigAttribCase::init(void) { DE_ASSERT(m_display == EGL_NO_DISPLAY); m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); m_configs = eglu::getConfigs(m_eglTestCtx.getLibrary(), m_display); m_configsIter = m_configs.begin(); m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } void GetConfigAttribCase::deinit(void) { m_eglTestCtx.getLibrary().terminate(m_display); m_display = EGL_NO_DISPLAY; } tcu::TestNode::IterateResult GetConfigAttribCase::iterate(void) { tcu::TestLog &log = m_testCtx.getLog(); if (m_configsIter == m_configs.end()) { log << TestLog::Message << "No configs available." << TestLog::EndMessage; return STOP; } { const EGLConfig config = *m_configsIter; EGLint id; eglGetConfigAttrib(m_display, config, EGL_CONFIG_ID, &id); eglu::checkError(eglGetError(), DE_NULL, __FILE__, __LINE__); log << TestLog::Message << "Config ID " << id << TestLog::EndMessage; executeTest(config); } log << TestLog::Message << TestLog::EndMessage; m_configsIter++; if (m_configsIter == m_configs.end()) return STOP; else return CONTINUE; } EGLint GetConfigAttribCase::getValue(EGLConfig config, EGLenum attrib, bool logValue) { TestLog &log = m_testCtx.getLog(); EGLint value; eglGetConfigAttrib(m_display, config, attrib, &value); eglu::checkError(eglGetError(), DE_NULL, __FILE__, __LINE__); if (logValue) logConfigAttribute(log, attrib, value); return value; } class GetConfigAttribSimpleCase : public GetConfigAttribCase { public: GetConfigAttribSimpleCase(EglTestContext &eglTestCtx, const char *name, const char *description, EGLenum attribute) : GetConfigAttribCase(eglTestCtx, name, description) , m_attrib(attribute) { } void checkColorBufferType(EGLint value) { const bool isRGBBuffer = value == EGL_RGB_BUFFER; const bool isLuminanceBuffer = value == EGL_LUMINANCE_BUFFER; const bool isYuvBuffer = value == EGL_YUV_BUFFER_EXT; const bool hasYuvSupport = eglu::hasExtension(m_eglTestCtx.getLibrary(), m_display, "EGL_EXT_yuv_surface"); if (!(isRGBBuffer || isLuminanceBuffer || (isYuvBuffer && hasYuvSupport))) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, invalid EGL_COLOR_BUFFER_TYPE value" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkCaveat(EGLint value) { if (!(value == EGL_NONE || value == EGL_SLOW_CONFIG || value == EGL_NON_CONFORMANT_CONFIG)) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, invalid EGL_CONFIG_CAVEAT value" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkTransparentType(EGLint value) { if (!(value == EGL_NONE || value == EGL_TRANSPARENT_RGB)) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, invalid EGL_TRANSPARENT_TYPE value" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkBoolean(EGLenum attrib, EGLint value) { if (!(value == EGL_FALSE || value == EGL_TRUE)) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, " << eglu::getConfigAttribStr(attrib) << " should be a boolean value." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkInteger(EGLenum attrib, EGLint value) { if (attrib == EGL_NATIVE_VISUAL_ID || attrib == EGL_NATIVE_VISUAL_TYPE) // Implementation-defined return; if (attrib == EGL_CONFIG_ID && value < 1) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, config IDs should be positive integer values beginning from 1." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkSurfaceTypeMask(EGLint value) { const EGLint wantedBits = EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT; if ((value & wantedBits) == 0) { TestLog &log = m_testCtx.getLog(); log << TestLog::Message << "Fail, config does not actually support creation of any surface type?" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid value"); } } void checkAttribute(EGLenum attrib, EGLint value) { switch (attrib) { case EGL_COLOR_BUFFER_TYPE: checkColorBufferType(value); break; case EGL_CONFIG_CAVEAT: checkCaveat(value); break; case EGL_TRANSPARENT_TYPE: checkTransparentType(value); break; case EGL_CONFORMANT: case EGL_RENDERABLE_TYPE: // Just print what we know break; case EGL_SURFACE_TYPE: checkSurfaceTypeMask(value); break; case EGL_BIND_TO_TEXTURE_RGB: case EGL_BIND_TO_TEXTURE_RGBA: case EGL_NATIVE_RENDERABLE: case EGL_RECORDABLE_ANDROID: checkBoolean(attrib, value); break; default: checkInteger(attrib, value); } } void executeTest(EGLConfig config) { TestLog &log = m_testCtx.getLog(); eglu::Version version = eglu::getVersion(m_eglTestCtx.getLibrary(), m_display); if (!isAttributePresent(version, m_attrib)) { log << TestLog::Message << eglu::getConfigAttribStr(m_attrib) << " not supported by this EGL version"; } else if (!hasRequiredExtension(m_eglTestCtx.getLibrary(), m_display, m_attrib)) { std::string message = std::string(eglu::getConfigAttribName(m_attrib)) + " not supported due to missing extension"; TCU_THROW(NotSupportedError, message); } else { EGLint value; enableLogging(true); eglGetConfigAttrib(m_display, config, m_attrib, &value); eglu::checkError(eglGetError(), DE_NULL, __FILE__, __LINE__); logConfigAttribute(log, m_attrib, value); checkAttribute(m_attrib, value); enableLogging(false); } } private: EGLenum m_attrib; }; class GetConfigAttribBufferSizeCase : public GetConfigAttribCase { public: GetConfigAttribBufferSizeCase(EglTestContext &eglTestCtx, const char *name, const char *description) : GetConfigAttribCase(eglTestCtx, name, description) { } void executeTest(EGLConfig config) { TestLog &log = m_testCtx.getLog(); const EGLint colorBufferType = getValue(config, EGL_COLOR_BUFFER_TYPE); const EGLint bufferSize = getValue(config, EGL_BUFFER_SIZE); const EGLint redSize = getValue(config, EGL_RED_SIZE); const EGLint greenSize = getValue(config, EGL_GREEN_SIZE); const EGLint blueSize = getValue(config, EGL_BLUE_SIZE); const EGLint luminanceSize = getValue(config, EGL_LUMINANCE_SIZE); const EGLint alphaSize = getValue(config, EGL_ALPHA_SIZE); if (alphaSize < 0) { log << TestLog::Message << "Fail, alpha size must be zero or positive." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid alpha size"); } if (colorBufferType == EGL_RGB_BUFFER) { if (luminanceSize != 0) { log << TestLog::Message << "Fail, luminance size must be zero for an RGB buffer." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid luminance size"); } if (redSize <= 0 || greenSize <= 0 || blueSize <= 0) { log << TestLog::Message << "Fail, RGB component sizes must be positive for an RGB buffer." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid color component size"); } if (bufferSize != (redSize + greenSize + blueSize + alphaSize)) { log << TestLog::Message << "Fail, buffer size must be equal to the sum of RGB component sizes and alpha size." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid buffer size"); } } else if (colorBufferType == EGL_LUMINANCE_BUFFER) { if (luminanceSize <= 0) { log << TestLog::Message << "Fail, luminance size must be positive for a luminance buffer." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid luminance size"); } if (redSize != 0 || greenSize != 0 || blueSize != 0) { log << TestLog::Message << "Fail, RGB component sizes must be zero for a luminance buffer." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid color component size"); } if (bufferSize != (luminanceSize + alphaSize)) { log << TestLog::Message << "Fail, buffer size must be equal to the sum of luminance size and alpha size." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid buffer size"); } } } }; class GetConfigAttribTransparentValueCase : public GetConfigAttribCase { public: GetConfigAttribTransparentValueCase(EglTestContext &eglTestCtx, const char *name, const char *description) : GetConfigAttribCase(eglTestCtx, name, description) { } void executeTest(EGLConfig config) { TestLog &log = m_testCtx.getLog(); const EGLint transparentType = getValue(config, EGL_TRANSPARENT_TYPE); const EGLint redValue = getValue(config, EGL_TRANSPARENT_RED_VALUE); const EGLint greenValue = getValue(config, EGL_TRANSPARENT_GREEN_VALUE); const EGLint blueValue = getValue(config, EGL_TRANSPARENT_BLUE_VALUE); const EGLint redSize = getValue(config, EGL_RED_SIZE); const EGLint greenSize = getValue(config, EGL_GREEN_SIZE); const EGLint blueSize = getValue(config, EGL_BLUE_SIZE); if (transparentType == EGL_TRANSPARENT_RGB) { if ((redValue < 0 || redValue >= (1 << redSize)) || (greenValue < 0 || greenValue >= (1 << greenSize)) || (blueValue < 0 || blueValue >= (1 << blueSize))) { log << TestLog::Message << "Fail, transparent color values must lie between 0 and the maximum component value." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid transparent color value"); } } } }; QueryConfigTests::QueryConfigTests(EglTestContext &eglTestCtx) : TestCaseGroup(eglTestCtx, "query_config", "Surface config query tests") { } QueryConfigTests::~QueryConfigTests(void) { } void QueryConfigTests::init(void) { // eglGetGonfigs { tcu::TestCaseGroup *getConfigsGroup = new tcu::TestCaseGroup(m_testCtx, "get_configs", "eglGetConfigs tests"); addChild(getConfigsGroup); getConfigsGroup->addChild( new GetConfigsBoundsCase(m_eglTestCtx, "get_configs_bounds", "eglGetConfigs bounds checking test")); } // eglGetConfigAttrib { static const struct { EGLenum attribute; const char *testName; } attributes[] = { {EGL_BUFFER_SIZE, "buffer_size"}, {EGL_RED_SIZE, "red_size"}, {EGL_GREEN_SIZE, "green_size"}, {EGL_BLUE_SIZE, "blue_size"}, {EGL_LUMINANCE_SIZE, "luminance_size"}, {EGL_ALPHA_SIZE, "alpha_size"}, {EGL_ALPHA_MASK_SIZE, "alpha_mask_size"}, {EGL_BIND_TO_TEXTURE_RGB, "bind_to_texture_rgb"}, {EGL_BIND_TO_TEXTURE_RGBA, "bind_to_texture_rgba"}, {EGL_COLOR_BUFFER_TYPE, "color_buffer_type"}, {EGL_CONFIG_CAVEAT, "config_caveat"}, {EGL_CONFIG_ID, "config_id"}, {EGL_CONFORMANT, "conformant"}, {EGL_DEPTH_SIZE, "depth_size"}, {EGL_LEVEL, "level"}, {EGL_MAX_SWAP_INTERVAL, "max_swap_interval"}, {EGL_MIN_SWAP_INTERVAL, "min_swap_interval"}, {EGL_NATIVE_RENDERABLE, "native_renderable"}, {EGL_NATIVE_VISUAL_TYPE, "native_visual_type"}, {EGL_RENDERABLE_TYPE, "renderable_type"}, {EGL_SAMPLE_BUFFERS, "sample_buffers"}, {EGL_SAMPLES, "samples"}, {EGL_STENCIL_SIZE, "stencil_size"}, {EGL_SURFACE_TYPE, "surface_type"}, {EGL_TRANSPARENT_TYPE, "transparent_type"}, {EGL_TRANSPARENT_RED_VALUE, "transparent_red_value"}, {EGL_TRANSPARENT_GREEN_VALUE, "transparent_green_value"}, {EGL_TRANSPARENT_BLUE_VALUE, "transparent_blue_value"}, {EGL_RECORDABLE_ANDROID, "recordable_android"}, }; tcu::TestCaseGroup *simpleGroup = new tcu::TestCaseGroup(m_testCtx, "get_config_attrib", "eglGetConfigAttrib() tests"); addChild(simpleGroup); for (int ndx = 0; ndx < (int)DE_LENGTH_OF_ARRAY(attributes); ndx++) { simpleGroup->addChild(new GetConfigAttribSimpleCase( m_eglTestCtx, attributes[ndx].testName, "Simple attribute query case", attributes[ndx].attribute)); } } // Attribute constraints { tcu::TestCaseGroup *constraintsGroup = new tcu::TestCaseGroup(m_testCtx, "constraints", "Attribute constraint tests"); addChild(constraintsGroup); constraintsGroup->addChild( new GetConfigAttribBufferSizeCase(m_eglTestCtx, "color_buffer_size", "Color buffer component sizes")); constraintsGroup->addChild( new GetConfigAttribTransparentValueCase(m_eglTestCtx, "transparent_value", "Transparent color value")); } } } // namespace egl } // namespace deqp