/*------------------------------------------------------------------------- * 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 Debug output (KHR_debug) tests *//*--------------------------------------------------------------------*/ #include "es31fDebugTests.hpp" #include "es31fNegativeTestShared.hpp" #include "es31fNegativeBufferApiTests.hpp" #include "es31fNegativeTextureApiTests.hpp" #include "es31fNegativeShaderApiTests.hpp" #include "es31fNegativeFragmentApiTests.hpp" #include "es31fNegativeVertexArrayApiTests.hpp" #include "es31fNegativeStateApiTests.hpp" #include "es31fNegativeAtomicCounterTests.hpp" #include "es31fNegativeShaderImageLoadStoreTests.hpp" #include "es31fNegativeShaderFunctionTests.hpp" #include "es31fNegativeShaderDirectiveTests.hpp" #include "es31fNegativeSSBOBlockTests.hpp" #include "es31fNegativePreciseTests.hpp" #include "es31fNegativeAdvancedBlendEquationTests.hpp" #include "es31fNegativeShaderStorageTests.hpp" #include "es31fNegativeTessellationTests.hpp" #include "es31fNegativeComputeTests.hpp" #include "es31fNegativeSampleVariablesTests.hpp" #include "es31fNegativeShaderFramebufferFetchTests.hpp" #include "deUniquePtr.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deSTLUtil.hpp" #include "deMutex.hpp" #include "deThread.h" #include "gluRenderContext.hpp" #include "gluContextInfo.hpp" #include "gluCallLogWrapper.hpp" #include "gluStrUtil.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tes31Context.hpp" #include "tcuTestContext.hpp" #include "tcuCommandLine.hpp" #include "tcuResultCollector.hpp" #include "glsStateQueryUtil.hpp" namespace deqp { namespace gles31 { namespace Functional { namespace { using namespace glw; using std::string; using std::vector; using std::set; using std::map; using de::MovePtr; using tcu::ResultCollector; using tcu::TestLog; using glu::CallLogWrapper; using NegativeTestShared::NegativeTestContext; static const GLenum s_debugTypes[] = { GL_DEBUG_TYPE_ERROR, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, GL_DEBUG_TYPE_PORTABILITY, GL_DEBUG_TYPE_PERFORMANCE, GL_DEBUG_TYPE_OTHER, GL_DEBUG_TYPE_MARKER, GL_DEBUG_TYPE_PUSH_GROUP, GL_DEBUG_TYPE_POP_GROUP, }; static const GLenum s_debugSeverities[] = { GL_DEBUG_SEVERITY_HIGH, GL_DEBUG_SEVERITY_MEDIUM, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION, }; static bool isKHRDebugSupported (Context& ctx) { const bool supportsES32 = glu::contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2)); return supportsES32 || ctx.getContextInfo().isExtensionSupported("GL_KHR_debug"); } class BaseCase; class DebugMessageTestContext : public NegativeTestContext { public: DebugMessageTestContext (BaseCase& host, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, tcu::TestLog& log, tcu::ResultCollector& results, bool enableLog); ~DebugMessageTestContext (void); void expectMessage (GLenum source, GLenum type); private: BaseCase& m_debugHost; }; class TestFunctionWrapper { public: typedef void (*CoreTestFunc)(NegativeTestContext& ctx); typedef void (*DebugTestFunc)(DebugMessageTestContext& ctx); TestFunctionWrapper (void); explicit TestFunctionWrapper (CoreTestFunc func); explicit TestFunctionWrapper (DebugTestFunc func); void call (DebugMessageTestContext& ctx) const; private: enum FuncType { TYPE_NULL = 0, TYPE_CORE, TYPE_DEBUG, }; FuncType m_type; union { CoreTestFunc coreFn; DebugTestFunc debugFn; } m_func; }; TestFunctionWrapper::TestFunctionWrapper (void) : m_type(TYPE_NULL) { } TestFunctionWrapper::TestFunctionWrapper (CoreTestFunc func) : m_type(TYPE_CORE) { m_func.coreFn = func; } TestFunctionWrapper::TestFunctionWrapper (DebugTestFunc func) : m_type(TYPE_DEBUG) { m_func.debugFn = func; } void TestFunctionWrapper::call (DebugMessageTestContext& ctx) const { if (m_type == TYPE_CORE) m_func.coreFn(static_cast<NegativeTestContext&>(ctx)); else if (m_type == TYPE_DEBUG) m_func.debugFn(ctx); else DE_ASSERT(false); } void emitMessages (DebugMessageTestContext& ctx, GLenum source) { for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_debugTypes); typeNdx++) { for (int severityNdx = 0; severityNdx < DE_LENGTH_OF_ARRAY(s_debugSeverities); severityNdx++) { const GLenum type = s_debugTypes[typeNdx]; const GLenum severity = s_debugSeverities[severityNdx]; const string msg = string("Application generated message with type ") + glu::getDebugMessageTypeName(type) + " and severity " + glu::getDebugMessageSeverityName(severity); // Use severity as ID, guaranteed unique ctx.glDebugMessageInsert(source, type, severity, severity, -1, msg.c_str()); ctx.expectMessage(source, type); } } } void application_messages (DebugMessageTestContext& ctx) { ctx.beginSection("Messages with source of GL_DEBUG_SOURCE_APPLICATION"); emitMessages(ctx, GL_DEBUG_SOURCE_APPLICATION); ctx.endSection(); } void thirdparty_messages (DebugMessageTestContext& ctx) { ctx.beginSection("Messages with source of GL_DEBUG_SOURCE_THIRD_PARTY"); emitMessages(ctx, GL_DEBUG_SOURCE_THIRD_PARTY); ctx.endSection(); } void push_pop_messages (DebugMessageTestContext& ctx) { ctx.beginSection("Push/Pop Debug Group"); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Application group 1"); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 2, -1, "Application group 1-1"); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 3, -1, "Application group 1-1-1"); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 4, -1, "Application group 1-2"); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_THIRD_PARTY, 4, -1, "3rd Party group 1-3"); ctx.expectMessage(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_POP_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP); ctx.glPushDebugGroup(GL_DEBUG_SOURCE_THIRD_PARTY, 4, -1, "3rd Party group 2"); ctx.expectMessage(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_PUSH_GROUP); ctx.glPopDebugGroup(); ctx.expectMessage(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_POP_GROUP); ctx.endSection(); } struct FunctionContainer { TestFunctionWrapper function; const char* name; const char* desc; }; vector<FunctionContainer> getUserMessageFuncs (void) { FunctionContainer funcs[] = { { TestFunctionWrapper(application_messages), "application_messages", "Externally generated messages from the application" }, { TestFunctionWrapper(thirdparty_messages), "third_party_messages", "Externally generated messages from a third party" }, { TestFunctionWrapper(push_pop_messages), "push_pop_stack", "Messages from pushing/popping debug groups" }, }; return std::vector<FunctionContainer>(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } // Data required to uniquely identify a debug message struct MessageID { GLenum source; GLenum type; GLuint id; MessageID (void) : source(GL_NONE), type(GL_NONE), id(0) {} MessageID (GLenum source_, GLenum type_, GLuint id_) : source(source_), type(type_), id(id_) {} bool operator== (const MessageID& rhs) const { return source == rhs.source && type == rhs.type && id == rhs.id;} bool operator!= (const MessageID& rhs) const { return source != rhs.source || type != rhs.type || id != rhs.id;} bool operator< (const MessageID& rhs) const { return source < rhs.source || (source == rhs.source && (type < rhs.type || (type == rhs.type && id < rhs.id))); } }; std::ostream& operator<< (std::ostream& str, const MessageID &id) { return str << glu::getDebugMessageSourceStr(id.source) << ", " << glu::getDebugMessageTypeStr(id.type) << ", " << id.id; } // All info from a single debug message struct MessageData { MessageID id; GLenum severity; string message; MessageData (void) : id(MessageID()), severity(GL_NONE) {} MessageData (const MessageID& id_, GLenum severity_, const string& message_) : id(id_) , severity(severity_) , message(message_) {} }; extern "C" typedef void GLW_APIENTRY DebugCallbackFunc(GLenum, GLenum, GLuint, GLenum, GLsizei, const char*, const void*); // Base class class BaseCase : public NegativeTestShared::ErrorCase { public: BaseCase (Context& ctx, const char* name, const char* desc); virtual ~BaseCase (void) {} virtual IterateResult iterate (void) = 0; virtual void expectMessage (GLenum source, GLenum type); virtual void expectError (GLenum error0, GLenum error1); protected: struct VerificationResult { const qpTestResult result; const string resultMessage; const string logMessage; VerificationResult (qpTestResult result_, const string& resultMessage_, const string& logMessage_) : result(result_), resultMessage(resultMessage_), logMessage(logMessage_) {} }; static DebugCallbackFunc callbackHandle; virtual void callback (GLenum source, GLenum type, GLuint id, GLenum severity, const std::string& message); VerificationResult verifyMessageCount (const MessageID& id, GLenum severity, int refCount, int resCount, bool messageEnabled) const; // Verify a single message instance against expected attributes void verifyMessage (const MessageData& message, GLenum source, GLenum type, GLuint id, GLenum severity); void verifyMessage (const MessageData& message, GLenum source, GLenum type); bool verifyMessageExists (const MessageData& message, GLenum source, GLenum type); void verifyMessageGroup (const MessageData& message, GLenum source, GLenum type); void verifyMessageString (const MessageData& message); bool isDebugContext (void) const; tcu::ResultCollector m_results; }; void BaseCase::callbackHandle (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char* message, const void* userParam) { static_cast<BaseCase*>(const_cast<void*>(userParam))->callback(source, type, id, severity, string(message, &message[length])); } BaseCase::BaseCase (Context& ctx, const char* name, const char* desc) : ErrorCase(ctx, name, desc) { } void BaseCase::expectMessage (GLenum source, GLenum type) { DE_UNREF(source); DE_UNREF(type); } void BaseCase::expectError (GLenum error0, GLenum error1) { if (error0 != GL_NO_ERROR || error1 != GL_NO_ERROR) expectMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR); else expectMessage(GL_DONT_CARE, GL_DONT_CARE); } void BaseCase::callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message) { DE_UNREF(source); DE_UNREF(type); DE_UNREF(id); DE_UNREF(severity); DE_UNREF(message); } BaseCase::VerificationResult BaseCase::verifyMessageCount (const MessageID& id, GLenum severity, int refCount, int resCount, bool messageEnabled) const { std::stringstream log; // This message should not be filtered out if (messageEnabled) { if (resCount != refCount) { /* * Technically nothing requires the implementation to be consistent in terms * of the messages it produces in most situations, allowing the set of messages * produced to vary between executions. This function splits messages * into deterministic and non-deterministic to facilitate handling of such messages. * * Non-deterministic messages that are present in differing quantities in filtered and * unfiltered runs will not fail the test case unless in direct violation of a filter: * the implementation may produce an arbitrary number of such messages when they are * not filtered out and none when they are filtered. * * A list of error source/type combinations with their assumed behaviour and * the rationale for expecting such behaviour follows * * For API/shader messages we assume that the following types are deterministic: * DEBUG_TYPE_ERROR Errors specified by spec and should always be produced * * For API messages the following types are assumed to be non-deterministic * and treated as quality warnings since the underlying reported issue does not change between calls: * DEBUG_TYPE_DEPRECATED_BEHAVIOR Reasonable to only report first instance * DEBUG_TYPE_UNDEFINED_BEHAVIOR Reasonable to only report first instance * DEBUG_TYPE_PORTABILITY Reasonable to only report first instance * * For API messages the following types are assumed to be non-deterministic * and do not affect test results. * DEBUG_TYPE_PERFORMANCE May be tied to arbitrary factors, reasonable to report only first instance * DEBUG_TYPE_OTHER Definition allows arbitrary contents * * For 3rd party and application messages the following types are deterministic: * DEBUG_TYPE_MARKER Only generated by test * DEBUG_TYPE_PUSH_GROUP Only generated by test * DEBUG_TYPE_POP_GROUP Only generated by test * All others Only generated by test * * All messages with category of window system or other are treated as non-deterministic * and do not effect test results since they can be assumed to be outside control of * both the implementation and test case * */ const bool isDeterministic = id.source == GL_DEBUG_SOURCE_APPLICATION || id.source == GL_DEBUG_SOURCE_THIRD_PARTY || ((id.source == GL_DEBUG_SOURCE_API || id.source == GL_DEBUG_SOURCE_SHADER_COMPILER) && id.type == GL_DEBUG_TYPE_ERROR); const bool canIgnore = id.source == GL_DEBUG_SOURCE_WINDOW_SYSTEM || id.source == GL_DEBUG_SOURCE_OTHER; if (isDeterministic) { if (resCount > refCount) { log << "Extra instances of message were found: (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_FAIL, "Extra instances of a deterministic message were present", log.str()); } else { log << "Instances of message were missing: (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_FAIL, "Message missing", log.str()); } } else if(!canIgnore) { if (resCount > refCount) { log << "Extra instances of message were found but the message is non-deterministic(warning): (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_QUALITY_WARNING, "Extra instances of a message were present", log.str()); } else { log << "Instances of message were missing but the message is non-deterministic(warning): (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_QUALITY_WARNING, "Message missing", log.str()); } } else { if (resCount > refCount) { log << "Extra instances of message were found but the message is non-deterministic(ignored): (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_PASS, "", log.str()); } else { log << "Instances of message were missing but the message is non-deterministic(ignored): (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << " (got " << resCount << ", expected " << refCount << ")"; return VerificationResult(QP_TEST_RESULT_PASS, "", log.str()); } } } else // Passed as appropriate { log << "Message was found when expected: ("<< id << ") with " << glu::getDebugMessageSeverityStr(severity); return VerificationResult(QP_TEST_RESULT_PASS, "", log.str()); } } // Message should be filtered out else { // Filtered out if (resCount == 0) { log << "Message was excluded correctly: (" << id << ") with " << glu::getDebugMessageSeverityStr(severity); return VerificationResult(QP_TEST_RESULT_PASS, "", log.str()); } // Only present in filtered run (ERROR) else if (resCount > 0 && refCount == 0) { log << "A message was not excluded as it should have been: (" << id << ") with " << glu::getDebugMessageSeverityStr(severity) << ". This message was not present in the reference run"; return VerificationResult(QP_TEST_RESULT_FAIL, "A message was not filtered out", log.str()); } // Present in both runs (ERROR) else { log << "A message was not excluded as it should have been: (" << id << ") with " << glu::getDebugMessageSeverityStr(severity); return VerificationResult(QP_TEST_RESULT_FAIL, "A message was not filtered out", log.str()); } } } // Return true if message needs further verification bool BaseCase::verifyMessageExists (const MessageData& message, GLenum source, GLenum type) { TestLog& log = m_testCtx.getLog(); if (source == GL_DONT_CARE || type == GL_DONT_CARE) return false; else if (message.id.source == GL_NONE || message.id.type == GL_NONE) { if (isDebugContext()) { m_results.addResult(QP_TEST_RESULT_FAIL, "Message was not reported as expected"); log << TestLog::Message << "A message was expected but none was reported" << TestLog::EndMessage; } else { m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Verification accuracy is lacking without a debug context"); log << TestLog::Message << "A message was expected but none was reported. Running without a debug context" << TestLog::EndMessage; } return false; } else return true; } void BaseCase::verifyMessageGroup (const MessageData& message, GLenum source, GLenum type) { TestLog& log = m_testCtx.getLog(); if (message.id.source != source) { m_results.addResult(QP_TEST_RESULT_FAIL, "Incorrect message source"); log << TestLog::Message << "Message source was " << glu::getDebugMessageSourceStr(message.id.source) << " when it should have been " << glu::getDebugMessageSourceStr(source) << TestLog::EndMessage; } if (message.id.type != type) { m_results.addResult(QP_TEST_RESULT_FAIL, "Incorrect message type"); log << TestLog::Message << "Message type was " << glu::getDebugMessageTypeStr(message.id.type) << " when it should have been " << glu::getDebugMessageTypeStr(type) << TestLog::EndMessage; } } void BaseCase::verifyMessageString (const MessageData& message) { TestLog& log = m_testCtx.getLog(); log << TestLog::Message << "Driver says: \"" << message.message << "\"" << TestLog::EndMessage; if (message.message.empty()) { m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Empty message"); log << TestLog::Message << "Message message was empty" << TestLog::EndMessage; } } void BaseCase::verifyMessage (const MessageData& message, GLenum source, GLenum type) { if (verifyMessageExists(message, source, type)) { verifyMessageString(message); verifyMessageGroup(message, source, type); } } void BaseCase::verifyMessage (const MessageData& message, GLenum source, GLenum type, GLuint id, GLenum severity) { TestLog& log = m_testCtx.getLog(); if (verifyMessageExists(message, source, type)) { verifyMessageString(message); verifyMessageGroup(message, source, type); if (message.id.id != id) { m_results.addResult(QP_TEST_RESULT_FAIL, "Incorrect message id"); log << TestLog::Message << "Message id was " << message.id.id << " when it should have been " << id << TestLog::EndMessage; } if (message.severity != severity) { m_results.addResult(QP_TEST_RESULT_FAIL, "Incorrect message severity"); log << TestLog::Message << "Message severity was " << glu::getDebugMessageSeverityStr(message.severity) << " when it should have been " << glu::getDebugMessageSeverityStr(severity) << TestLog::EndMessage; } } } bool BaseCase::isDebugContext (void) const { return (m_context.getRenderContext().getType().getFlags() & glu::CONTEXT_DEBUG) != 0; } // Generate errors, verify that each error results in a callback call class CallbackErrorCase : public BaseCase { public: CallbackErrorCase (Context& ctx, const char* name, const char* desc, TestFunctionWrapper errorFunc); virtual ~CallbackErrorCase (void) {} virtual IterateResult iterate (void); virtual void expectMessage (GLenum source, GLenum type); private: virtual void callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message); const TestFunctionWrapper m_errorFunc; MessageData m_lastMessage; }; CallbackErrorCase::CallbackErrorCase (Context& ctx, const char* name, const char* desc, TestFunctionWrapper errorFunc) : BaseCase (ctx, name, desc) , m_errorFunc (errorFunc) { } CallbackErrorCase::IterateResult CallbackErrorCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_testCtx.getLog(); DebugMessageTestContext context = DebugMessageTestContext(*this, m_context.getRenderContext(), m_context.getContextInfo(), log, m_results, true); gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, false); // disable all gl.debugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, DE_NULL, true); // enable API errors gl.debugMessageControl(GL_DEBUG_SOURCE_APPLICATION, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); // enable application messages gl.debugMessageControl(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); // enable third party messages gl.debugMessageCallback(callbackHandle, this); m_errorFunc.call(context); gl.debugMessageCallback(DE_NULL, DE_NULL); gl.disable(GL_DEBUG_OUTPUT); m_results.setTestContextResult(m_testCtx); return STOP; } void CallbackErrorCase::expectMessage (GLenum source, GLenum type) { verifyMessage(m_lastMessage, source, type); m_lastMessage = MessageData(); // Reset error so that code afterwards (such as glu::ShaderProgram) doesn't break because of // lingering error state. m_context.getRenderContext().getFunctions().getError(); } void CallbackErrorCase::callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message) { m_lastMessage = MessageData(MessageID(source, type, id), severity, message); } // Generate errors, verify that each error results in a log entry class LogErrorCase : public BaseCase { public: LogErrorCase (Context& context, const char* name, const char* desc, TestFunctionWrapper errorFunc); virtual ~LogErrorCase (void) {} virtual IterateResult iterate (void); virtual void expectMessage (GLenum source, GLenum type); private: const TestFunctionWrapper m_errorFunc; MessageData m_lastMessage; }; LogErrorCase::LogErrorCase (Context& ctx, const char* name, const char* desc, TestFunctionWrapper errorFunc) : BaseCase (ctx, name, desc) , m_errorFunc (errorFunc) { } LogErrorCase::IterateResult LogErrorCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_testCtx.getLog(); DebugMessageTestContext context = DebugMessageTestContext(*this, m_context.getRenderContext(), m_context.getContextInfo(), log, m_results, true); GLint numMsg = 0; gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, false); // disable all gl.debugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, DE_NULL, true); // enable API errors gl.debugMessageCallback(DE_NULL, DE_NULL); // enable logging gl.getIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMsg); gl.getDebugMessageLog(numMsg, 0, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL); // clear log m_errorFunc.call(context); gl.disable(GL_DEBUG_OUTPUT); m_results.setTestContextResult(m_testCtx); return STOP; } void LogErrorCase::expectMessage (GLenum source, GLenum type) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); int numMsg = 0; TestLog& log = m_testCtx.getLog(); MessageData lastMsg; if (source == GL_DONT_CARE || type == GL_DONT_CARE) return; gl.getIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMsg); if (numMsg == 0) { if (isDebugContext()) { m_results.addResult(QP_TEST_RESULT_FAIL, "Error was not reported as expected"); log << TestLog::Message << "A message was expected but none was reported (empty message log)" << TestLog::EndMessage; } else { m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Verification accuracy is lacking without a debug context"); log << TestLog::Message << "A message was expected but none was reported (empty message log). Running without a debug context" << TestLog::EndMessage; } return; } // There may be messages other than the error we are looking for in the log. // Strictly nothing prevents the implementation from producing more than the // required error from an API call with a defined error. however we assume that // since calls that produce an error should not change GL state the implementation // should have nothing else to report. if (numMsg > 1) gl.getDebugMessageLog(numMsg-1, 0, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL); // Clear all but last { int msgLen = 0; gl.getIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &msgLen); TCU_CHECK_MSG(msgLen >= 0, "Negative message length"); TCU_CHECK_MSG(msgLen < 100000, "Excessively long message"); lastMsg.message.resize(msgLen); gl.getDebugMessageLog(1, msgLen, &lastMsg.id.source, &lastMsg.id.type, &lastMsg.id.id, &lastMsg.severity, &msgLen, &lastMsg.message[0]); } log << TestLog::Message << "Driver says: \"" << lastMsg.message << "\"" << TestLog::EndMessage; verifyMessage(lastMsg, source, type); // Reset error so that code afterwards (such as glu::ShaderProgram) doesn't break because of // lingering error state. m_context.getRenderContext().getFunctions().getError(); } // Generate errors, verify that calling glGetError afterwards produces desired result class GetErrorCase : public BaseCase { public: GetErrorCase (Context& ctx, const char* name, const char* desc, TestFunctionWrapper errorFunc); virtual ~GetErrorCase (void) {} virtual IterateResult iterate (void); virtual void expectMessage (GLenum source, GLenum type); virtual void expectError (glw::GLenum error0, glw::GLenum error1); private: const TestFunctionWrapper m_errorFunc; }; GetErrorCase::GetErrorCase (Context& ctx, const char* name, const char* desc, TestFunctionWrapper errorFunc) : BaseCase (ctx, name, desc) , m_errorFunc (errorFunc) { } GetErrorCase::IterateResult GetErrorCase::iterate (void) { tcu::TestLog& log = m_testCtx.getLog(); DebugMessageTestContext context = DebugMessageTestContext(*this, m_context.getRenderContext(), m_context.getContextInfo(), log, m_results, true); m_errorFunc.call(context); m_results.setTestContextResult(m_testCtx); return STOP; } void GetErrorCase::expectMessage (GLenum source, GLenum type) { DE_UNREF(source); DE_UNREF(type); DE_FATAL("GetErrorCase cannot handle anything other than error codes"); } void GetErrorCase::expectError (glw::GLenum error0, glw::GLenum error1) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); TestLog& log = m_testCtx.getLog(); const GLenum result = gl.getError(); if (result != error0 && result != error1) { m_results.addResult(QP_TEST_RESULT_FAIL, "Incorrect error was reported"); if (error0 == error1) log << TestLog::Message << glu::getErrorStr(error0) << " was expected but got " << glu::getErrorStr(result) << TestLog::EndMessage; else log << TestLog::Message << glu::getErrorStr(error0) << " or " << glu::getErrorStr(error1) << " was expected but got " << glu::getErrorStr(result) << TestLog::EndMessage; return; } } // Generate errors, log the types, disable some, regenerate errors, verify correct errors (not)reported class FilterCase : public BaseCase { public: FilterCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs); virtual ~FilterCase (void) {} virtual IterateResult iterate (void); virtual void expectMessage (GLenum source, GLenum type); protected: struct MessageFilter { MessageFilter() : source(GL_DONT_CARE), type(GL_DONT_CARE), severity(GL_DONT_CARE), enabled(true) {} // Default to enable all MessageFilter(GLenum source_, GLenum type_, GLenum severity_, const vector<GLuint>& ids_, bool enabled_) : source(source_), type(type_), severity(severity_), ids(ids_), enabled(enabled_) {} GLenum source; GLenum type; GLenum severity; vector<GLuint> ids; bool enabled; }; virtual void callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message); vector<MessageData> genMessages (bool uselog, const string& desc); vector<MessageFilter> genFilters (const vector<MessageData>& messages, const vector<MessageFilter>& initial, deUint32 seed, int iterations) const; void applyFilters (const vector<MessageFilter>& filters) const; bool isEnabled (const vector<MessageFilter>& filters, const MessageData& message) const; void verify (const vector<MessageData>& refMessages, const vector<MessageData>& filteredMessages, const vector<MessageFilter>& filters); const vector<TestFunctionWrapper> m_errorFuncs; vector<MessageData>* m_currentErrors; }; FilterCase::FilterCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs) : BaseCase (ctx, name, desc) , m_errorFuncs (errorFuncs) , m_currentErrors (DE_NULL) { } FilterCase::IterateResult FilterCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageCallback(callbackHandle, this); try { gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); { const vector<MessageData> refMessages = genMessages(true, "Reference run"); const MessageFilter baseFilter (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, vector<GLuint>(), true); const deUint32 baseSeed = deStringHash(getName()) ^ m_testCtx.getCommandLine().getBaseSeed(); const vector<MessageFilter> filters = genFilters(refMessages, vector<MessageFilter>(1, baseFilter), baseSeed, 4); vector<MessageData> filteredMessages; applyFilters(filters); // Generate errors filteredMessages = genMessages(false, "Filtered run"); // Verify verify(refMessages, filteredMessages, filters); if (!isDebugContext() && refMessages.empty()) m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Verification accuracy is lacking without a debug context"); } } catch (...) { gl.disable(GL_DEBUG_OUTPUT); gl.debugMessageCallback(DE_NULL, DE_NULL); throw; } gl.disable(GL_DEBUG_OUTPUT); gl.debugMessageCallback(DE_NULL, DE_NULL); m_results.setTestContextResult(m_testCtx); return STOP; } void FilterCase::expectMessage (GLenum source, GLenum type) { DE_UNREF(source); DE_UNREF(type); } void FilterCase::callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message) { if (m_currentErrors) m_currentErrors->push_back(MessageData(MessageID(source, type, id), severity, message)); } vector<MessageData> FilterCase::genMessages (bool uselog, const string& desc) { tcu::TestLog& log = m_testCtx.getLog(); DebugMessageTestContext context = DebugMessageTestContext(*this, m_context.getRenderContext(), m_context.getContextInfo(), log, m_results, uselog); tcu::ScopedLogSection section (log, "message gen", desc); vector<MessageData> messages; m_currentErrors = &messages; for (int ndx = 0; ndx < int(m_errorFuncs.size()); ndx++) m_errorFuncs[ndx].call(context); m_currentErrors = DE_NULL; return messages; } vector<FilterCase::MessageFilter> FilterCase::genFilters (const vector<MessageData>& messages, const vector<MessageFilter>& initial, deUint32 seed, int iterations) const { de::Random rng (seed ^ deInt32Hash(deStringHash(getName()))); set<MessageID> tempMessageIds; set<GLenum> tempSources; set<GLenum> tempTypes; set<GLenum> tempSeverities; if (messages.empty()) return initial; for (int ndx = 0; ndx < int(messages.size()); ndx++) { const MessageData& msg = messages[ndx]; tempMessageIds.insert(msg.id); tempSources.insert(msg.id.source); tempTypes.insert(msg.id.type); tempSeverities.insert(msg.severity); } { // Fetchable by index const vector<MessageID> messageIds (tempMessageIds.begin(), tempMessageIds.end()); const vector<GLenum> sources (tempSources.begin(), tempSources.end()); const vector<GLenum> types (tempTypes.begin(), tempTypes.end()); const vector<GLenum> severities (tempSeverities.begin(), tempSeverities.end()); vector<MessageFilter> filters = initial; for (int iteration = 0; iteration < iterations; iteration++) { switch(rng.getInt(0, 8)) // Distribute so that per-message randomization (the default branch) is prevalent { case 0: { const GLenum source = sources[rng.getInt(0, int(sources.size()-1))]; const bool enabled = rng.getBool(); filters.push_back(MessageFilter(source, GL_DONT_CARE, GL_DONT_CARE, vector<GLuint>(), enabled)); break; } case 1: { const GLenum type = types[rng.getUint32()%types.size()]; const bool enabled = rng.getBool(); filters.push_back(MessageFilter(GL_DONT_CARE, type, GL_DONT_CARE, vector<GLuint>(), enabled)); break; } case 2: { const GLenum severity = severities[rng.getUint32()%severities.size()]; const bool enabled = rng.getBool(); filters.push_back(MessageFilter(GL_DONT_CARE, GL_DONT_CARE, severity, vector<GLuint>(), enabled)); break; } default: { const int start = rng.getInt(0, int(messageIds.size())); for (int itr = 0; itr < 4; itr++) { const MessageID& id = messageIds[(start+itr)%messageIds.size()]; const bool enabled = rng.getBool(); filters.push_back(MessageFilter(id.source, id.type, GL_DONT_CARE, vector<GLuint>(1, id.id), enabled)); } } } } return filters; } } void FilterCase::applyFilters (const vector<MessageFilter>& filters) const { TestLog& log = m_testCtx.getLog(); const tcu::ScopedLogSection section (log, "", "Setting message filters"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); for (size_t filterNdx = 0; filterNdx < filters.size(); filterNdx++) { const MessageFilter& filter = filters[filterNdx]; if (filter.ids.empty()) log << TestLog::Message << "Setting messages with" << " source " << glu::getDebugMessageSourceStr(filter.source) << ", type " << glu::getDebugMessageTypeStr(filter.type) << " and severity " << glu::getDebugMessageSeverityStr(filter.severity) << (filter.enabled ? " to enabled" : " to disabled") << TestLog::EndMessage; else { for (size_t ndx = 0; ndx < filter.ids.size(); ndx++) log << TestLog::Message << "Setting message (" << MessageID(filter.source, filter.type, filter.ids[ndx]) << ") to " << (filter.enabled ? "enabled" : "disabled") << TestLog::EndMessage; } gl.debugMessageControl(filter.source, filter.type, filter.severity, GLsizei(filter.ids.size()), filter.ids.empty() ? DE_NULL : &filter.ids[0], filter.enabled); } } bool FilterCase::isEnabled (const vector<MessageFilter>& filters, const MessageData& message) const { bool retval = true; for (size_t filterNdx = 0; filterNdx < filters.size(); filterNdx++) { const MessageFilter& filter = filters[filterNdx]; if (filter.ids.empty()) { if (filter.source != GL_DONT_CARE && filter.source != message.id.source) continue; if (filter.type != GL_DONT_CARE && filter.type != message.id.type) continue; if (filter.severity != GL_DONT_CARE && filter.severity != message.severity) continue; } else { DE_ASSERT(filter.source != GL_DONT_CARE); DE_ASSERT(filter.type != GL_DONT_CARE); DE_ASSERT(filter.severity == GL_DONT_CARE); if (filter.source != message.id.source || filter.type != message.id.type) continue; if (!de::contains(filter.ids.begin(), filter.ids.end(), message.id.id)) continue; } retval = filter.enabled; } return retval; } struct MessageMeta { int refCount; int resCount; GLenum severity; MessageMeta (void) : refCount(0), resCount(0), severity(GL_NONE) {} }; void FilterCase::verify (const vector<MessageData>& refMessages, const vector<MessageData>& resMessages, const vector<MessageFilter>& filters) { TestLog& log = m_testCtx.getLog(); map<MessageID, MessageMeta> counts; log << TestLog::Section("verification", "Verifying"); // Gather message counts & severities, report severity mismatches if found for (size_t refNdx = 0; refNdx < refMessages.size(); refNdx++) { const MessageData& msg = refMessages[refNdx]; MessageMeta& meta = counts[msg.id]; if (meta.severity != GL_NONE && meta.severity != msg.severity) { log << TestLog::Message << "A message has variable severity between instances: (" << msg.id << ") with severity " << glu::getDebugMessageSeverityStr(meta.severity) << " and " << glu::getDebugMessageSeverityStr(msg.severity) << TestLog::EndMessage; m_results.addResult(QP_TEST_RESULT_FAIL, "Message severity changed between instances of the same message"); } meta.refCount++; meta.severity = msg.severity; } for (size_t resNdx = 0; resNdx < resMessages.size(); resNdx++) { const MessageData& msg = resMessages[resNdx]; MessageMeta& meta = counts[msg.id]; if (meta.severity != GL_NONE && meta.severity != msg.severity) { log << TestLog::Message << "A message has variable severity between instances: (" << msg.id << ") with severity " << glu::getDebugMessageSeverityStr(meta.severity) << " and " << glu::getDebugMessageSeverityStr(msg.severity) << TestLog::EndMessage; m_results.addResult(QP_TEST_RESULT_FAIL, "Message severity changed between instances of the same message"); } meta.resCount++; meta.severity = msg.severity; } for (map<MessageID, MessageMeta>::const_iterator itr = counts.begin(); itr != counts.end(); itr++) { const MessageID& id = itr->first; const GLenum severity = itr->second.severity; const int refCount = itr->second.refCount; const int resCount = itr->second.resCount; const bool enabled = isEnabled(filters, MessageData(id, severity, "")); VerificationResult result = verifyMessageCount(id, severity, refCount, resCount, enabled); log << TestLog::Message << result.logMessage << TestLog::EndMessage; if (result.result != QP_TEST_RESULT_PASS) m_results.addResult(result.result, result.resultMessage); } log << TestLog::EndSection; } // Filter case that uses debug groups class GroupFilterCase : public FilterCase { public: GroupFilterCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs); virtual ~GroupFilterCase (void) {} virtual IterateResult iterate (void); }; GroupFilterCase::GroupFilterCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs) : FilterCase(ctx, name, desc, errorFuncs) { } template<typename T> vector<T> join(const vector<T>& a, const vector<T>&b) { vector<T> retval; retval.reserve(a.size()+b.size()); retval.insert(retval.end(), a.begin(), a.end()); retval.insert(retval.end(), b.begin(), b.end()); return retval; } GroupFilterCase::IterateResult GroupFilterCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_testCtx.getLog(); gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageCallback(callbackHandle, this); try { gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); { // Generate reference (all errors) const vector<MessageData> refMessages = genMessages(true, "Reference run"); const deUint32 baseSeed = deStringHash(getName()) ^ m_testCtx.getCommandLine().getBaseSeed(); const MessageFilter baseFilter (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, vector<GLuint>(), true); const vector<MessageFilter> filter0 = genFilters(refMessages, vector<MessageFilter>(1, baseFilter), baseSeed, 4); vector<MessageData> resMessages0; applyFilters(filter0); resMessages0 = genMessages(false, "Filtered run, default debug group"); // Initial verification verify(refMessages, resMessages0, filter0); { // Generate reference (filters inherited from parent) const vector<MessageFilter> filter1base = genFilters(refMessages, vector<MessageFilter>(), baseSeed ^ 0xDEADBEEF, 4); const vector<MessageFilter> filter1full = join(filter0, filter1base); tcu::ScopedLogSection section1 (log, "", "Pushing Debug Group"); vector<MessageData> resMessages1; gl.pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Test Group"); applyFilters(filter1base); // First nested verification resMessages1 = genMessages(false, "Filtered run, pushed one debug group"); verify(refMessages, resMessages1, filter1full); { // Generate reference (filters iherited again) const vector<MessageFilter> filter2base = genFilters(refMessages, vector<MessageFilter>(), baseSeed ^ 0x43211234, 4); const vector<MessageFilter> filter2full = join(filter1full, filter2base); tcu::ScopedLogSection section2 (log, "", "Pushing Debug Group"); vector<MessageData> resMessages2; gl.pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Nested Test Group"); applyFilters(filter2base); // Second nested verification resMessages2 = genMessages(false, "Filtered run, pushed two debug groups"); verify(refMessages, resMessages2, filter2full); gl.popDebugGroup(); } // First restore verification resMessages1 = genMessages(false, "Filtered run, popped second debug group"); verify(refMessages, resMessages1, filter1full); gl.popDebugGroup(); } // restore verification resMessages0 = genMessages(false, "Filtered run, popped first debug group"); verify(refMessages, resMessages0, filter0); if (!isDebugContext() && refMessages.empty()) m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Verification accuracy is lacking without a debug context"); } } catch (...) { gl.disable(GL_DEBUG_OUTPUT); gl.debugMessageCallback(DE_NULL, DE_NULL); throw; } gl.disable(GL_DEBUG_OUTPUT); gl.debugMessageCallback(DE_NULL, DE_NULL); m_results.setTestContextResult(m_testCtx); return STOP; } // Basic grouping functionality class GroupCase : public BaseCase { public: GroupCase (Context& ctx, const char* name, const char* desc); virtual ~GroupCase () {} virtual IterateResult iterate (void); private: virtual void callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message); MessageData m_lastMessage; }; GroupCase::GroupCase (Context& ctx, const char* name, const char* desc) : BaseCase(ctx, name, desc) { } GroupCase::IterateResult GroupCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_testCtx.getLog(); glu::CallLogWrapper wrapper (gl, log); gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, false); // disable all gl.debugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, DE_NULL, true); // enable API errors gl.debugMessageControl(GL_DEBUG_SOURCE_APPLICATION, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); // enable application messages gl.debugMessageControl(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, true); // enable third party messages gl.debugMessageCallback(callbackHandle, this); wrapper.enableLogging(true); wrapper.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1234, -1, "Pushed debug stack"); verifyMessage(m_lastMessage, GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PUSH_GROUP, 1234, GL_DEBUG_SEVERITY_NOTIFICATION); wrapper.glPopDebugGroup(); verifyMessage(m_lastMessage, GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_POP_GROUP, 1234, GL_DEBUG_SEVERITY_NOTIFICATION); wrapper.glPushDebugGroup(GL_DEBUG_SOURCE_THIRD_PARTY, 4231, -1, "Pushed debug stack"); verifyMessage(m_lastMessage, GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_PUSH_GROUP, 4231, GL_DEBUG_SEVERITY_NOTIFICATION); wrapper.glPopDebugGroup(); verifyMessage(m_lastMessage, GL_DEBUG_SOURCE_THIRD_PARTY, GL_DEBUG_TYPE_POP_GROUP, 4231, GL_DEBUG_SEVERITY_NOTIFICATION); gl.debugMessageCallback(DE_NULL, DE_NULL); gl.disable(GL_DEBUG_OUTPUT); m_results.setTestContextResult(m_testCtx); return STOP; } void GroupCase::callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message) { m_lastMessage = MessageData(MessageID(source, type, id), severity, message); } // Asynchronous debug output class AsyncCase : public BaseCase { public: AsyncCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs, bool useCallbacks); virtual ~AsyncCase (void) {} virtual IterateResult iterate (void); virtual void expectMessage (glw::GLenum source, glw::GLenum type); private: struct MessageCount { int received; int expected; MessageCount(void) : received(0), expected(0) {} }; typedef map<MessageID, MessageCount> MessageCounter; enum VerifyState { VERIFY_PASS = 0, VERIFY_MINIMUM, VERIFY_FAIL, VERIFY_LAST }; virtual void callback (glw::GLenum source, glw::GLenum type, glw::GLuint id, glw::GLenum severity, const std::string& message); VerifyState verify (bool uselog); void fetchLogMessages (void); const vector<TestFunctionWrapper> m_errorFuncs; const bool m_useCallbacks; MessageCounter m_counts; de::Mutex m_mutex; }; AsyncCase::AsyncCase (Context& ctx, const char* name, const char* desc, const vector<TestFunctionWrapper>& errorFuncs, bool useCallbacks) : BaseCase (ctx, name, desc) , m_errorFuncs (errorFuncs) , m_useCallbacks (useCallbacks) { } AsyncCase::IterateResult AsyncCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::TestLog& log = m_testCtx.getLog(); DebugMessageTestContext context = DebugMessageTestContext(*this, m_context.getRenderContext(), m_context.getContextInfo(), log, m_results, true); const int maxWait = 10000; // ms const int warnWait = 100; // Clear log from earlier messages { GLint numMessages = 0; gl.getIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); gl.getDebugMessageLog(numMessages, 0, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL); } gl.enable(GL_DEBUG_OUTPUT); gl.enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); gl.debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, DE_NULL, false); // Some messages could be dependent on the value of DEBUG_OUTPUT_SYNCHRONOUS so only use API errors which should be generated in all cases gl.debugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, DE_NULL, true); if (m_useCallbacks) // will use log otherwise gl.debugMessageCallback(callbackHandle, this); else gl.debugMessageCallback(DE_NULL, DE_NULL); // Reference run (synchoronous) { tcu::ScopedLogSection section(log, "reference run", "Reference run (synchronous)"); for (int ndx = 0; ndx < int(m_errorFuncs.size()); ndx++) m_errorFuncs[ndx].call(context); } if (m_counts.empty()) { if (!isDebugContext()) m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Need debug context to guarantee implementation behaviour (see command line options)"); log << TestLog::Message << "Reference run produced no messages, nothing to verify" << TestLog::EndMessage; gl.debugMessageCallback(DE_NULL, DE_NULL); gl.disable(GL_DEBUG_OUTPUT); m_results.setTestContextResult(m_testCtx); return STOP; } for (MessageCounter::iterator itr = m_counts.begin(); itr != m_counts.end(); itr++) { itr->second.expected = itr->second.received; itr->second.received = 0; } gl.disable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // Result run (async) for (int ndx = 0; ndx < int(m_errorFuncs.size()); ndx++) m_errorFuncs[ndx].call(context); // Repatedly try verification, new results may be added to m_receivedMessages at any time { tcu::ScopedLogSection section (log, "result run", "Result run (asynchronous)"); VerifyState lastTimelyState = VERIFY_FAIL; for (int waited = 0;;) { const VerifyState pass = verify(false); const int wait = de::max(50, waited>>2); // Pass (possibly due to time limit) if (pass == VERIFY_PASS || (pass == VERIFY_MINIMUM && waited >= maxWait)) { verify(true); // log // State changed late if (waited >= warnWait && lastTimelyState != pass) m_results.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Async messages were returned to application somewhat slowly"); log << TestLog::Message << "Passed after ~" << waited << "ms of waiting" << TestLog::EndMessage; break; } // fail else if (waited >= maxWait) { verify(true); // log log << TestLog::Message << "Waited for ~" << waited << "ms without getting all expected messages" << TestLog::EndMessage; m_results.addResult(QP_TEST_RESULT_FAIL, "Async messages were not returned to application within a reasonable timeframe"); break; } if (waited < warnWait) lastTimelyState = pass; deSleep(wait); waited += wait; if (!m_useCallbacks) fetchLogMessages(); } } gl.debugMessageCallback(DE_NULL, DE_NULL); gl.disable(GL_DEBUG_OUTPUT); m_results.setTestContextResult(m_testCtx); return STOP; } void AsyncCase::expectMessage (GLenum source, GLenum type) { // Good time to clean up the queue as this should be called after most messages are generated if (!m_useCallbacks) fetchLogMessages(); DE_UNREF(source); DE_UNREF(type); } void AsyncCase::callback (GLenum source, GLenum type, GLuint id, GLenum severity, const string& message) { DE_ASSERT(m_useCallbacks); DE_UNREF(severity); DE_UNREF(message); de::ScopedLock lock(m_mutex); m_counts[MessageID(source, type, id)].received++; } // Note that we can never guarantee getting all messages back when using logs/fetching as the GL may create more than its log size limit during an arbitrary period of time void AsyncCase::fetchLogMessages (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); GLint numMsg = 0; gl.getIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMsg); for(int msgNdx = 0; msgNdx < numMsg; msgNdx++) { int msgLen = 0; MessageData msg; gl.getIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &msgLen); TCU_CHECK_MSG(msgLen >= 0, "Negative message length"); TCU_CHECK_MSG(msgLen < 100000, "Excessively long message"); msg.message.resize(msgLen); gl.getDebugMessageLog(1, msgLen, &msg.id.source, &msg.id.type, &msg.id.id, &msg.severity, &msgLen, &msg.message[0]); { const de::ScopedLock lock(m_mutex); // Don't block during API call m_counts[MessageID(msg.id)].received++; } } } AsyncCase::VerifyState AsyncCase::verify (bool uselog) { using std::map; VerifyState retval = VERIFY_PASS; TestLog& log = m_testCtx.getLog(); const de::ScopedLock lock(m_mutex); for (map<MessageID, MessageCount>::const_iterator itr = m_counts.begin(); itr != m_counts.end(); itr++) { const MessageID& id = itr->first; const int refCount = itr->second.expected; const int resCount = itr->second.received; const bool enabled = true; VerificationResult result = verifyMessageCount(id, GL_DONT_CARE, refCount, resCount, enabled); if (uselog) log << TestLog::Message << result.logMessage << TestLog::EndMessage; if (result.result == QP_TEST_RESULT_FAIL) retval = VERIFY_FAIL; else if (result.result != QP_TEST_RESULT_PASS && retval == VERIFY_PASS) retval = VERIFY_MINIMUM; } return retval; } // Tests debug labels class LabelCase : public TestCase { public: LabelCase (Context& ctx, const char* name, const char* desc, GLenum identifier); virtual ~LabelCase (void) {} virtual IterateResult iterate (void); private: GLenum m_identifier; }; LabelCase::LabelCase (Context& ctx, const char* name, const char* desc, GLenum identifier) : TestCase (ctx, name, desc) , m_identifier (identifier) { } LabelCase::IterateResult LabelCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const char* const msg = "This is a debug label"; GLuint object = 0; int outlen = -1; char buffer[64]; switch(m_identifier) { case GL_BUFFER: gl.genBuffers(1, &object); gl.bindBuffer(GL_ARRAY_BUFFER, object); gl.bindBuffer(GL_ARRAY_BUFFER, 0); break; case GL_SHADER: object = gl.createShader(GL_FRAGMENT_SHADER); break; case GL_PROGRAM: object = gl.createProgram(); break; case GL_QUERY: gl.genQueries(1, &object); gl.beginQuery(GL_ANY_SAMPLES_PASSED, object); // Create gl.endQuery(GL_ANY_SAMPLES_PASSED); // Cleanup break; case GL_PROGRAM_PIPELINE: gl.genProgramPipelines(1, &object); gl.bindProgramPipeline(object); // Create gl.bindProgramPipeline(0); // Cleanup break; case GL_TRANSFORM_FEEDBACK: gl.genTransformFeedbacks(1, &object); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, object); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); break; case GL_SAMPLER: gl.genSamplers(1, &object); gl.bindSampler(0, object); gl.bindSampler(0, 0); break; case GL_TEXTURE: gl.genTextures(1, &object); gl.bindTexture(GL_TEXTURE_2D, object); gl.bindTexture(GL_TEXTURE_2D, 0); break; case GL_RENDERBUFFER: gl.genRenderbuffers(1, &object); gl.bindRenderbuffer(GL_RENDERBUFFER, object); gl.bindRenderbuffer(GL_RENDERBUFFER, 0); break; case GL_FRAMEBUFFER: gl.genFramebuffers(1, &object); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, object); gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); break; default: DE_FATAL("Invalid identifier"); } gl.objectLabel(m_identifier, object, -1, msg); deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(m_identifier, object, sizeof(buffer), &outlen, buffer); if (outlen == 0) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to query debug label from object"); else if (deStringEqual(msg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned wrong string: expected \"" << msg << "\" but got \"" << buffer << "\"" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query returned wrong label"); } switch(m_identifier) { case GL_BUFFER: gl.deleteBuffers(1, &object); break; case GL_SHADER: gl.deleteShader(object); break; case GL_PROGRAM: gl.deleteProgram(object); break; case GL_QUERY: gl.deleteQueries(1, &object); break; case GL_PROGRAM_PIPELINE: gl.deleteProgramPipelines(1, &object); break; case GL_TRANSFORM_FEEDBACK: gl.deleteTransformFeedbacks(1, &object); break; case GL_SAMPLER: gl.deleteSamplers(1, &object); break; case GL_TEXTURE: gl.deleteTextures(1, &object); break; case GL_RENDERBUFFER: gl.deleteRenderbuffers(1, &object); break; case GL_FRAMEBUFFER: gl.deleteFramebuffers(1, &object); break; default: DE_FATAL("Invalid identifier"); } return STOP; } DebugMessageTestContext::DebugMessageTestContext (BaseCase& host, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, tcu::TestLog& log, tcu::ResultCollector& results, bool enableLog) : NegativeTestContext (host, renderCtx, ctxInfo, log, results, enableLog) , m_debugHost (host) { } DebugMessageTestContext::~DebugMessageTestContext (void) { } void DebugMessageTestContext::expectMessage (GLenum source, GLenum type) { m_debugHost.expectMessage(source, type); } class SyncLabelCase : public TestCase { public: SyncLabelCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; SyncLabelCase::SyncLabelCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } SyncLabelCase::IterateResult SyncLabelCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const char* const msg = "This is a debug label"; int outlen = -1; char buffer[64]; glw::GLsync sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "fenceSync"); gl.objectPtrLabel(sync, -1, msg); deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, sizeof(buffer), &outlen, buffer); if (outlen == 0) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Failed to query debug label from object"); else if (deStringEqual(msg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned wrong string: expected \"" << msg << "\" but got \"" << buffer << "\"" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Query returned wrong label"); } gl.deleteSync(sync); return STOP; } class InitialLabelCase : public TestCase { public: InitialLabelCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; InitialLabelCase::InitialLabelCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } InitialLabelCase::IterateResult InitialLabelCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); int outlen = -1; GLuint shader; glw::GLsync sync; char buffer[64]; sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Shader", "Shader object"); m_testCtx.getLog() << TestLog::Message << "Querying initial value" << TestLog::EndMessage; buffer[0] = 'X'; outlen = -1; gl.getObjectLabel(GL_SHADER, shader, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 0) result.fail("'length' was not zero, got " + de::toString(outlen)); else if (buffer[0] != '\0') result.fail("label was not null terminated"); else m_testCtx.getLog() << TestLog::Message << "Got 0-sized null-terminated string." << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Sync", "Sync object"); m_testCtx.getLog() << TestLog::Message << "Querying initial value" << TestLog::EndMessage; buffer[0] = 'X'; outlen = -1; gl.getObjectPtrLabel(sync, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 0) result.fail("'length' was not zero, got " + de::toString(outlen)); else if (buffer[0] != '\0') result.fail("label was not null terminated"); else m_testCtx.getLog() << TestLog::Message << "Got 0-sized null-terminated string." << TestLog::EndMessage; } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class ClearLabelCase : public TestCase { public: ClearLabelCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; ClearLabelCase::ClearLabelCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } ClearLabelCase::IterateResult ClearLabelCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); static const struct { const char* description; int length; } s_clearMethods[] = { { " with NULL label and 0 length", 0 }, { " with NULL label and 1 length", 1 }, { " with NULL label and negative length", -1 }, }; const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); const char* const msg = "This is a debug label"; int outlen = -1; GLuint shader; glw::GLsync sync; char buffer[64]; sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Shader", "Shader object"); for (int methodNdx = 0; methodNdx < DE_LENGTH_OF_ARRAY(s_clearMethods); ++methodNdx) { m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, -2, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Clearing label " << s_clearMethods[methodNdx].description << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, s_clearMethods[methodNdx].length, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label" << TestLog::EndMessage; buffer[0] = 'X'; outlen = -1; gl.getObjectLabel(GL_SHADER, shader, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 0) result.fail("'length' was not zero, got " + de::toString(outlen)); else if (buffer[0] != '\0') result.fail("label was not null terminated"); else m_testCtx.getLog() << TestLog::Message << "Got 0-sized null-terminated string." << TestLog::EndMessage; } } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Sync", "Sync object"); for (int methodNdx = 0; methodNdx < DE_LENGTH_OF_ARRAY(s_clearMethods); ++methodNdx) { m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectPtrLabel(sync, -2, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Clearing label " << s_clearMethods[methodNdx].description << TestLog::EndMessage; gl.objectPtrLabel(sync, s_clearMethods[methodNdx].length, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label" << TestLog::EndMessage; buffer[0] = 'X'; outlen = -1; gl.getObjectPtrLabel(sync, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 0) result.fail("'length' was not zero, got " + de::toString(outlen)); else if (buffer[0] != '\0') result.fail("label was not null terminated"); else m_testCtx.getLog() << TestLog::Message << "Got 0-sized null-terminated string." << TestLog::EndMessage; } } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class SpecifyWithLengthCase : public TestCase { public: SpecifyWithLengthCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; SpecifyWithLengthCase::SpecifyWithLengthCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } SpecifyWithLengthCase::IterateResult SpecifyWithLengthCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); const char* const msg = "This is a debug label"; const char* const clipMsg = "This is a de"; int outlen = -1; GLuint shader; glw::GLsync sync; char buffer[64]; sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Shader", "Shader object"); m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\" with length 12" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, 12, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 12) result.fail("'length' was not 12, got " + de::toString(outlen)); else if (deStringEqual(clipMsg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } else { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned wrong string: expected \"" << clipMsg << "\" but got \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Sync", "Sync object"); m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\" with length 12" << TestLog::EndMessage; gl.objectPtrLabel(sync, 12, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 12) result.fail("'length' was not 12, got " + de::toString(outlen)); else if (deStringEqual(clipMsg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } else { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned wrong string: expected \"" << clipMsg << "\" but got \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "ZeroSized", "ZeroSized"); m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\" with length 0" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, 0, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, sizeof(buffer), &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 0) result.fail("'length' was not zero, got " + de::toString(outlen)); else if (buffer[0] != '\0') result.fail("label was not null terminated"); else m_testCtx.getLog() << TestLog::Message << "Got 0-sized null-terminated string." << TestLog::EndMessage; } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class BufferLimitedLabelCase : public TestCase { public: BufferLimitedLabelCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; BufferLimitedLabelCase::BufferLimitedLabelCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } BufferLimitedLabelCase::IterateResult BufferLimitedLabelCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); const char* const msg = "This is a debug label"; int outlen = -1; GLuint shader; glw::GLsync sync; char buffer[64]; sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection superSection(m_testCtx.getLog(), "Shader", "Shader object"); m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, -1, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryAll", "Query All"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 22" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, 22, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 21) result.fail("'length' was not 21, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringEqual(msg, buffer)) { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryAllNoSize", "Query all without size"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 22" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, 22, DE_NULL, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); buffer[63] = '\0'; // make sure buffer is null terminated before strlen if (strlen(buffer) != 21) result.fail("Buffer length was not 21"); else if (buffer[21] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[22] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringEqual(msg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryLess", "Query substring"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 2" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, 2, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 1) result.fail("'length' was not 1, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringBeginsWith(msg, buffer)) { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryNone", "Query one character"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 1" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectLabel(GL_SHADER, shader, 1, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 0) result.fail("'length' was not 0, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else m_testCtx.getLog() << TestLog::Message << "Query returned zero-sized null-terminated string" << TestLog::EndMessage; } } { const tcu::ScopedLogSection superSection(m_testCtx.getLog(), "Sync", "Sync object"); m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectPtrLabel(sync, -1, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryAll", "Query All"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 22" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, 22, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 21) result.fail("'length' was not 21, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringEqual(msg, buffer)) { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryAllNoSize", "Query all without size"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 22" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, 22, DE_NULL, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); buffer[63] = '\0'; // make sure buffer is null terminated before strlen if (strlen(buffer) != 21) result.fail("Buffer length was not 21"); else if (buffer[21] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[22] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringEqual(msg, buffer)) { m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryLess", "Query substring"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 2" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, 2, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 1) result.fail("'length' was not 1, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else if (!deStringBeginsWith(msg, buffer)) { buffer[63] = '\0'; // make sure buffer is null terminated before printing m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; result.fail("Query returned wrong label"); } else m_testCtx.getLog() << TestLog::Message << "Query returned string: \"" << buffer << "\"" << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "QueryNone", "Query one character"); m_testCtx.getLog() << TestLog::Message << "Querying whole label, buffer size = 1" << TestLog::EndMessage; deMemset(buffer, 'X', sizeof(buffer)); gl.getObjectPtrLabel(sync, 1, &outlen, buffer); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 0) result.fail("'length' was not 0, got " + de::toString(outlen)); else if (buffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); else if (buffer[outlen+1] != 'X') result.fail("Query wrote over buffer bound"); else m_testCtx.getLog() << TestLog::Message << "Query returned zero-sized null-terminated string" << TestLog::EndMessage; } } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class LabelMaxSizeCase : public TestCase { public: LabelMaxSizeCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; LabelMaxSizeCase::LabelMaxSizeCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } LabelMaxSizeCase::IterateResult LabelMaxSizeCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); int maxLabelLen = -1; int outlen = -1; GLuint shader; glw::GLsync sync; gl.getIntegerv(GL_MAX_LABEL_LENGTH, &maxLabelLen); GLS_COLLECT_GL_ERROR(result, gl.getError(), "GL_MAX_LABEL_LENGTH"); m_testCtx.getLog() << TestLog::Message << "GL_MAX_LABEL_LENGTH = " << maxLabelLen << TestLog::EndMessage; if (maxLabelLen < 256) throw tcu::TestError("maxLabelLen was less than required (256)"); if (maxLabelLen > 8192) { m_testCtx.getLog() << TestLog::Message << "GL_MAX_LABEL_LENGTH is very large. Application having larger labels is unlikely, skipping test." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Shader", "Shader object"); std::vector<char> buffer (maxLabelLen, 'X'); std::vector<char> readBuffer (maxLabelLen, 'X'); buffer[maxLabelLen-1] = '\0'; m_testCtx.getLog() << TestLog::Message << "Setting max length label, with implicit size. (length = -1)" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, -1, &buffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label back" << TestLog::EndMessage; outlen = -1; gl.getObjectLabel(GL_SHADER, shader, maxLabelLen, &outlen, &readBuffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != maxLabelLen-1) result.fail("'length' was not " + de::toString(maxLabelLen-1) + ", got " + de::toString(outlen)); else if (readBuffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); m_testCtx.getLog() << TestLog::Message << "Setting max length label, with explicit size. (length = " << (maxLabelLen-1) << ")" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, maxLabelLen-1, &buffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label back" << TestLog::EndMessage; outlen = -1; readBuffer[maxLabelLen-1] = 'X'; gl.getObjectLabel(GL_SHADER, shader, maxLabelLen, &outlen, &readBuffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != maxLabelLen-1) result.fail("'length' was not " + de::toString(maxLabelLen-1) + ", got " + de::toString(outlen)); else if (readBuffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Sync", "Sync object"); std::vector<char> buffer (maxLabelLen, 'X'); std::vector<char> readBuffer (maxLabelLen, 'X'); buffer[maxLabelLen-1] = '\0'; m_testCtx.getLog() << TestLog::Message << "Setting max length label, with implicit size. (length = -1)" << TestLog::EndMessage; gl.objectPtrLabel(sync, -1, &buffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label back" << TestLog::EndMessage; outlen = -1; gl.getObjectPtrLabel(sync, maxLabelLen, &outlen, &readBuffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != maxLabelLen-1) result.fail("'length' was not " + de::toString(maxLabelLen-1) + ", got " + de::toString(outlen)); else if (readBuffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); m_testCtx.getLog() << TestLog::Message << "Setting max length label, with explicit size. (length = " << (maxLabelLen-1) << ")" << TestLog::EndMessage; gl.objectPtrLabel(sync, maxLabelLen-1, &buffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label back" << TestLog::EndMessage; outlen = -1; readBuffer[maxLabelLen-1] = 'X'; gl.getObjectPtrLabel(sync, maxLabelLen, &outlen, &readBuffer[0]); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != maxLabelLen-1) result.fail("'length' was not " + de::toString(maxLabelLen-1) + ", got " + de::toString(outlen)); else if (readBuffer[outlen] != '\0') result.fail("Buffer was not null-terminated"); } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class LabelLengthCase : public TestCase { public: LabelLengthCase (Context& ctx, const char* name, const char* desc); virtual IterateResult iterate (void); }; LabelLengthCase::LabelLengthCase (Context& ctx, const char* name, const char* desc) : TestCase(ctx, name, desc) { } LabelLengthCase::IterateResult LabelLengthCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); const char* const msg = "This is a debug label"; int outlen = -1; GLuint shader; glw::GLsync sync; sync = gl.fenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLS_COLLECT_GL_ERROR(result, gl.getError(), "fenceSync"); shader = gl.createShader(GL_FRAGMENT_SHADER); GLS_COLLECT_GL_ERROR(result, gl.getError(), "createShader"); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Shader", "Shader object"); m_testCtx.getLog() << TestLog::Message << "Querying label length" << TestLog::EndMessage; outlen = -1; gl.getObjectLabel(GL_SHADER, shader, 0, &outlen, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 0) result.fail("'length' was not 0, got " + de::toString(outlen)); else m_testCtx.getLog() << TestLog::Message << "Query returned length: " << outlen << TestLog::EndMessage; m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectLabel(GL_SHADER, shader, -1, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label length" << TestLog::EndMessage; outlen = -1; gl.getObjectLabel(GL_SHADER, shader, 0, &outlen, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectLabel"); if (outlen != 21) result.fail("'length' was not 21, got " + de::toString(outlen)); else m_testCtx.getLog() << TestLog::Message << "Query returned length: " << outlen << TestLog::EndMessage; } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Sync", "Sync object"); m_testCtx.getLog() << TestLog::Message << "Querying label length" << TestLog::EndMessage; outlen = -1; gl.getObjectPtrLabel(sync, 0, &outlen, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 0) result.fail("'length' was not 0, got " + de::toString(outlen)); else m_testCtx.getLog() << TestLog::Message << "Query returned length: " << outlen << TestLog::EndMessage; m_testCtx.getLog() << TestLog::Message << "Setting label to string: \"" << msg << "\"" << TestLog::EndMessage; gl.objectPtrLabel(sync, -1, msg); GLS_COLLECT_GL_ERROR(result, gl.getError(), "objectPtrLabel"); m_testCtx.getLog() << TestLog::Message << "Querying label length" << TestLog::EndMessage; outlen = -1; gl.getObjectPtrLabel(sync, 0, &outlen, DE_NULL); GLS_COLLECT_GL_ERROR(result, gl.getError(), "getObjectPtrLabel"); if (outlen != 21) result.fail("'length' was not 21, got " + de::toString(outlen)); else m_testCtx.getLog() << TestLog::Message << "Query returned length: " << outlen << TestLog::EndMessage; } gl.deleteShader(shader); gl.deleteSync(sync); result.setTestContextResult(m_testCtx); return STOP; } class LimitQueryCase : public TestCase { public: LimitQueryCase (Context& context, const char* name, const char* description, glw::GLenum target, int limit, gls::StateQueryUtil::QueryType type); IterateResult iterate (void); private: const gls::StateQueryUtil::QueryType m_type; const int m_limit; const glw::GLenum m_target; }; LimitQueryCase::LimitQueryCase (Context& context, const char* name, const char* description, glw::GLenum target, int limit, gls::StateQueryUtil::QueryType type) : TestCase (context, name, description) , m_type (type) , m_limit (limit) , m_target (target) { } LimitQueryCase::IterateResult LimitQueryCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); gls::StateQueryUtil::verifyStateIntegerMin(result, gl, m_target, m_limit, m_type); result.setTestContextResult(m_testCtx); return STOP; } class IsEnabledCase : public TestCase { public: enum InitialValue { INITIAL_CTX_IS_DEBUG = 0, INITIAL_FALSE, }; IsEnabledCase (Context& context, const char* name, const char* description, glw::GLenum target, InitialValue initial, gls::StateQueryUtil::QueryType type); IterateResult iterate (void); private: const gls::StateQueryUtil::QueryType m_type; const glw::GLenum m_target; const InitialValue m_initial; }; IsEnabledCase::IsEnabledCase (Context& context, const char* name, const char* description, glw::GLenum target, InitialValue initial, gls::StateQueryUtil::QueryType type) : TestCase (context, name, description) , m_type (type) , m_target (target) , m_initial (initial) { } IsEnabledCase::IterateResult IsEnabledCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); bool initial; gl.enableLogging(true); if (m_initial == INITIAL_FALSE) initial = false; else { DE_ASSERT(m_initial == INITIAL_CTX_IS_DEBUG); initial = (m_context.getRenderContext().getType().getFlags() & glu::CONTEXT_DEBUG) != 0; } // check inital value gls::StateQueryUtil::verifyStateBoolean(result, gl, m_target, initial, m_type); // check toggle gl.glEnable(m_target); GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glEnable"); gls::StateQueryUtil::verifyStateBoolean(result, gl, m_target, true, m_type); gl.glDisable(m_target); GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glDisable"); gls::StateQueryUtil::verifyStateBoolean(result, gl, m_target, false, m_type); result.setTestContextResult(m_testCtx); return STOP; } class PositiveIntegerCase : public TestCase { public: PositiveIntegerCase (Context& context, const char* name, const char* description, glw::GLenum target, gls::StateQueryUtil::QueryType type); IterateResult iterate (void); private: const gls::StateQueryUtil::QueryType m_type; const glw::GLenum m_target; }; PositiveIntegerCase::PositiveIntegerCase (Context& context, const char* name, const char* description, glw::GLenum target, gls::StateQueryUtil::QueryType type) : TestCase (context, name, description) , m_type (type) , m_target (target) { } PositiveIntegerCase::IterateResult PositiveIntegerCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); gls::StateQueryUtil::verifyStateIntegerMin(result, gl, m_target, 0, m_type); result.setTestContextResult(m_testCtx); return STOP; } class GroupStackDepthQueryCase : public TestCase { public: GroupStackDepthQueryCase (Context& context, const char* name, const char* description, gls::StateQueryUtil::QueryType type); IterateResult iterate (void); private: const gls::StateQueryUtil::QueryType m_type; }; GroupStackDepthQueryCase::GroupStackDepthQueryCase (Context& context, const char* name, const char* description, gls::StateQueryUtil::QueryType type) : TestCase (context, name, description) , m_type (type) { } GroupStackDepthQueryCase::IterateResult GroupStackDepthQueryCase::iterate (void) { TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Initial", "Initial"); gls::StateQueryUtil::verifyStateInteger(result, gl, GL_DEBUG_GROUP_STACK_DEPTH, 1, m_type); } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Scoped", "Scoped"); gl.glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Application group 1"); gls::StateQueryUtil::verifyStateInteger(result, gl, GL_DEBUG_GROUP_STACK_DEPTH, 2, m_type); gl.glPopDebugGroup(); } result.setTestContextResult(m_testCtx); return STOP; } extern "C" void GLW_APIENTRY dummyCallback(GLenum, GLenum, GLuint, GLenum, GLsizei, const char*, const void*) { // dummy } class DebugCallbackFunctionCase : public TestCase { public: DebugCallbackFunctionCase (Context& context, const char* name, const char* description); IterateResult iterate (void); }; DebugCallbackFunctionCase::DebugCallbackFunctionCase (Context& context, const char* name, const char* description) : TestCase (context, name, description) { } DebugCallbackFunctionCase::IterateResult DebugCallbackFunctionCase::iterate (void) { using namespace gls::StateQueryUtil; TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Initial", "Initial"); verifyStatePointer(result, gl, GL_DEBUG_CALLBACK_FUNCTION, 0, QUERY_POINTER); } { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Set", "Set"); gl.glDebugMessageCallback(dummyCallback, DE_NULL); verifyStatePointer(result, gl, GL_DEBUG_CALLBACK_FUNCTION, (const void*)dummyCallback, QUERY_POINTER); } result.setTestContextResult(m_testCtx); return STOP; } class DebugCallbackUserParamCase : public TestCase { public: DebugCallbackUserParamCase (Context& context, const char* name, const char* description); IterateResult iterate (void); }; DebugCallbackUserParamCase::DebugCallbackUserParamCase (Context& context, const char* name, const char* description) : TestCase (context, name, description) { } DebugCallbackUserParamCase::IterateResult DebugCallbackUserParamCase::iterate (void) { using namespace gls::StateQueryUtil; TCU_CHECK_AND_THROW(NotSupportedError, isKHRDebugSupported(m_context), "GL_KHR_debug is not supported"); glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: "); gl.enableLogging(true); { const tcu::ScopedLogSection section(m_testCtx.getLog(), "Initial", "Initial"); verifyStatePointer(result, gl, GL_DEBUG_CALLBACK_USER_PARAM, 0, QUERY_POINTER); } { const tcu::ScopedLogSection section (m_testCtx.getLog(), "Set", "Set"); const void* param = (void*)(int*)0x123; gl.glDebugMessageCallback(dummyCallback, param); verifyStatePointer(result, gl, GL_DEBUG_CALLBACK_USER_PARAM, param, QUERY_POINTER); } result.setTestContextResult(m_testCtx); return STOP; } } // anonymous DebugTests::DebugTests (Context& context) : TestCaseGroup(context, "debug", "Debug tests") { } enum CaseType { CASETYPE_CALLBACK = 0, CASETYPE_LOG, CASETYPE_GETERROR, CASETYPE_LAST }; tcu::TestNode* createCase (CaseType type, Context& ctx, const char* name, const char* desc, TestFunctionWrapper function) { switch(type) { case CASETYPE_CALLBACK: return new CallbackErrorCase(ctx, name, desc, function); case CASETYPE_LOG: return new LogErrorCase(ctx, name, desc, function); case CASETYPE_GETERROR: return new GetErrorCase(ctx, name, desc, function); default: DE_FATAL("Invalid type"); } return DE_NULL; } tcu::TestCaseGroup* createChildCases (CaseType type, Context& ctx, const char* name, const char* desc, const vector<FunctionContainer>& funcs) { tcu::TestCaseGroup* host = new tcu::TestCaseGroup(ctx.getTestContext(), name, desc); for (size_t ndx = 0; ndx < funcs.size(); ndx++) host->addChild(createCase(type, ctx, funcs[ndx].name, funcs[ndx].desc, funcs[ndx].function)); return host; } vector<FunctionContainer> wrapCoreFunctions (const vector<NegativeTestShared::FunctionContainer>& fns) { vector<FunctionContainer> retVal; retVal.resize(fns.size()); for (int ndx = 0; ndx < (int)fns.size(); ++ndx) { retVal[ndx].function = TestFunctionWrapper(fns[ndx].function); retVal[ndx].name = fns[ndx].name; retVal[ndx].desc = fns[ndx].desc; } return retVal; } void DebugTests::init (void) { const vector<FunctionContainer> bufferFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeBufferApiTestFunctions()); const vector<FunctionContainer> textureFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeTextureApiTestFunctions()); const vector<FunctionContainer> shaderFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderApiTestFunctions()); const vector<FunctionContainer> fragmentFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeFragmentApiTestFunctions()); const vector<FunctionContainer> vaFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeVertexArrayApiTestFunctions()); const vector<FunctionContainer> stateFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeStateApiTestFunctions()); const vector<FunctionContainer> tessellationFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeTessellationTestFunctions()); const vector<FunctionContainer> atomicCounterFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeAtomicCounterTestFunctions()); const vector<FunctionContainer> imageLoadFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderImageLoadTestFunctions()); const vector<FunctionContainer> imageStoreFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderImageStoreTestFunctions()); const vector<FunctionContainer> imageAtomicFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderImageAtomicTestFunctions()); const vector<FunctionContainer> imageAtomicExchangeFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderImageAtomicExchangeTestFunctions()); const vector<FunctionContainer> shaderFunctionFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderFunctionTestFunctions()); const vector<FunctionContainer> shaderDirectiveFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderDirectiveTestFunctions()); const vector<FunctionContainer> ssboBlockFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeSSBOBlockTestFunctions()); const vector<FunctionContainer> preciseFuncs = wrapCoreFunctions(NegativeTestShared::getNegativePreciseTestFunctions()); const vector<FunctionContainer> advancedBlendFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeAdvancedBlendEquationTestFunctions()); const vector<FunctionContainer> shaderStorageFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderStorageTestFunctions()); const vector<FunctionContainer> sampleVariablesFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeSampleVariablesTestFunctions()); const vector<FunctionContainer> computeFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeComputeTestFunctions()); const vector<FunctionContainer> framebufferFetchFuncs = wrapCoreFunctions(NegativeTestShared::getNegativeShaderFramebufferFetchTestFunctions()); const vector<FunctionContainer> externalFuncs = getUserMessageFuncs(); { using namespace gls::StateQueryUtil; tcu::TestCaseGroup* const queries = new tcu::TestCaseGroup(m_testCtx, "state_query", "State query"); static const struct { const char* name; const char* targetName; glw::GLenum target; int limit; } limits[] = { { "max_debug_message_length", "MAX_DEBUG_MESSAGE_LENGTH", GL_MAX_DEBUG_MESSAGE_LENGTH, 1 }, { "max_debug_logged_messages", "MAX_DEBUG_LOGGED_MESSAGES", GL_MAX_DEBUG_LOGGED_MESSAGES, 1 }, { "max_debug_group_stack_depth", "MAX_DEBUG_GROUP_STACK_DEPTH", GL_MAX_DEBUG_GROUP_STACK_DEPTH, 64 }, { "max_label_length", "MAX_LABEL_LENGTH", GL_MAX_LABEL_LENGTH, 256 }, }; addChild(queries); #define FOR_ALL_TYPES(X) \ do \ { \ { \ const char* const postfix = "_getboolean"; \ const QueryType queryType = QUERY_BOOLEAN; \ X; \ } \ { \ const char* const postfix = "_getinteger"; \ const QueryType queryType = QUERY_INTEGER; \ X; \ } \ { \ const char* const postfix = "_getinteger64"; \ const QueryType queryType = QUERY_INTEGER64; \ X; \ } \ { \ const char* const postfix = "_getfloat"; \ const QueryType queryType = QUERY_FLOAT; \ X; \ } \ } \ while (deGetFalse()) #define FOR_ALL_ENABLE_TYPES(X) \ do \ { \ { \ const char* const postfix = "_isenabled"; \ const QueryType queryType = QUERY_ISENABLED; \ X; \ } \ FOR_ALL_TYPES(X); \ } \ while (deGetFalse()) for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(limits); ++ndx) { FOR_ALL_TYPES(queries->addChild(new LimitQueryCase(m_context, (std::string(limits[ndx].name) + postfix).c_str(), (std::string("Test ") + limits[ndx].targetName).c_str(), limits[ndx].target, limits[ndx].limit, queryType))); } FOR_ALL_ENABLE_TYPES(queries->addChild(new IsEnabledCase (m_context, (std::string("debug_output") + postfix).c_str(), "Test DEBUG_OUTPUT", GL_DEBUG_OUTPUT, IsEnabledCase::INITIAL_CTX_IS_DEBUG, queryType))); FOR_ALL_ENABLE_TYPES(queries->addChild(new IsEnabledCase (m_context, (std::string("debug_output_synchronous") + postfix).c_str(), "Test DEBUG_OUTPUT_SYNCHRONOUS", GL_DEBUG_OUTPUT_SYNCHRONOUS, IsEnabledCase::INITIAL_FALSE, queryType))); FOR_ALL_TYPES(queries->addChild(new PositiveIntegerCase (m_context, (std::string("debug_logged_messages") + postfix).c_str(), "Test DEBUG_LOGGED_MESSAGES", GL_DEBUG_LOGGED_MESSAGES, queryType))); FOR_ALL_TYPES(queries->addChild(new PositiveIntegerCase (m_context, (std::string("debug_next_logged_message_length") + postfix).c_str(), "Test DEBUG_NEXT_LOGGED_MESSAGE_LENGTH", GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, queryType))); FOR_ALL_TYPES(queries->addChild(new GroupStackDepthQueryCase(m_context, (std::string("debug_group_stack_depth") + postfix).c_str(), "Test DEBUG_GROUP_STACK_DEPTH", queryType))); queries->addChild(new DebugCallbackFunctionCase (m_context, "debug_callback_function_getpointer", "Test DEBUG_CALLBACK_FUNCTION")); queries->addChild(new DebugCallbackUserParamCase(m_context, "debug_callback_user_param_getpointer", "Test DEBUG_CALLBACK_USER_PARAM")); #undef FOR_ALL_TYPES #undef FOR_ALL_ENABLE_TYPES } { tcu::TestCaseGroup* const negative = new tcu::TestCaseGroup(m_testCtx, "negative_coverage", "API error coverage with various reporting methods"); addChild(negative); { tcu::TestCaseGroup* const host = new tcu::TestCaseGroup(m_testCtx, "callbacks", "Reporting of standard API errors via callback"); negative->addChild(host); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "buffer", "Negative Buffer API Cases", bufferFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "texture", "Negative Texture API Cases", textureFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader", "Negative Shader API Cases", shaderFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "fragment", "Negative Fragment API Cases", fragmentFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "vertex_array", "Negative Vertex Array API Cases", vaFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "state", "Negative GL State API Cases", stateFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "atomic_counter", "Negative Atomic Counter API Cases", atomicCounterFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_image_load", "Negative Shader Image Load API Cases", imageLoadFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_image_store", "Negative Shader Image Store API Cases", imageStoreFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_image_atomic", "Negative Shader Image Atomic API Cases", imageAtomicFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_image_exchange", "Negative Shader Image Atomic Exchange API Cases", imageAtomicExchangeFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_function", "Negative Shader Function Cases", shaderFunctionFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_directive", "Negative Shader Directive Cases", shaderDirectiveFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "ssbo_block", "Negative SSBO Block Cases", ssboBlockFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "precise", "Negative Precise Cases", preciseFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "advanced_blend", "Negative Advanced Blend Equation Cases", advancedBlendFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "shader_storage", "Negative Shader Storage Cases", shaderStorageFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "tessellation", "Negative Tessellation Cases", tessellationFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "oes_sample_variables", "Negative Sample Variables Cases", sampleVariablesFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "compute", "Negative Compute Cases", computeFuncs)); host->addChild(createChildCases(CASETYPE_CALLBACK, m_context, "framebuffer_fetch", "Negative Framebuffer Fetch Cases", framebufferFetchFuncs)); } { tcu::TestCaseGroup* const host = new tcu::TestCaseGroup(m_testCtx, "log", "Reporting of standard API errors via log"); negative->addChild(host); host->addChild(createChildCases(CASETYPE_LOG, m_context, "buffer", "Negative Buffer API Cases", bufferFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "texture", "Negative Texture API Cases", textureFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader", "Negative Shader API Cases", shaderFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "fragment", "Negative Fragment API Cases", fragmentFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "vertex_array", "Negative Vertex Array API Cases", vaFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "state", "Negative GL State API Cases", stateFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "atomic_counter", "Negative Atomic Counter API Cases", atomicCounterFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_image_load", "Negative Shader Image Load API Cases", imageLoadFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_image_store", "Negative Shader Image Store API Cases", imageStoreFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_image_atomic", "Negative Shader Image Atomic API Cases", imageAtomicFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_image_exchange", "Negative Shader Image Atomic Exchange API Cases", imageAtomicExchangeFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_function", "Negative Shader Function Cases", shaderFunctionFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_directive", "Negative Shader Directive Cases", shaderDirectiveFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "ssbo_block", "Negative SSBO Block Cases", ssboBlockFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "precise", "Negative Precise Cases", preciseFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "advanced_blend", "Negative Advanced Blend Equation Cases", advancedBlendFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "shader_storage", "Negative Shader Storage Cases", shaderStorageFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "tessellation", "Negative Tessellation Cases", tessellationFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "oes_sample_variables", "Negative Sample Variables Cases", sampleVariablesFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "compute", "Negative Compute Cases", computeFuncs)); host->addChild(createChildCases(CASETYPE_LOG, m_context, "framebuffer_fetch", "Negative Framebuffer Fetch Cases", framebufferFetchFuncs)); } { tcu::TestCaseGroup* const host = new tcu::TestCaseGroup(m_testCtx, "get_error", "Reporting of standard API errors via glGetError"); negative->addChild(host); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "buffer", "Negative Buffer API Cases", bufferFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "texture", "Negative Texture API Cases", textureFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader", "Negative Shader API Cases", shaderFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "fragment", "Negative Fragment API Cases", fragmentFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "vertex_array", "Negative Vertex Array API Cases", vaFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "state", "Negative GL State API Cases", stateFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "atomic_counter", "Negative Atomic Counter API Cases", atomicCounterFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_image_load", "Negative Shader Image Load API Cases", imageLoadFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_image_store", "Negative Shader Image Store API Cases", imageStoreFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_image_atomic", "Negative Shader Image Atomic API Cases", imageAtomicFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_image_exchange", "Negative Shader Image Atomic Exchange API Cases", imageAtomicExchangeFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_function", "Negative Shader Function Cases", shaderFunctionFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_directive", "Negative Shader Directive Cases", shaderDirectiveFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "ssbo_block", "Negative SSBO Block Cases", ssboBlockFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "precise", "Negative Precise Cases", preciseFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "advanced_blend", "Negative Advanced Blend Equation Cases", advancedBlendFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "shader_storage", "Negative Shader Storage Cases", shaderStorageFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "tessellation", "Negative Tessellation Cases", tessellationFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "oes_sample_variables", "Negative Sample Variables Cases", sampleVariablesFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "compute", "Negative Compute Cases", computeFuncs)); host->addChild(createChildCases(CASETYPE_GETERROR, m_context, "framebuffer_fetch", "Negative Framebuffer Fetch Cases", framebufferFetchFuncs)); } } { tcu::TestCaseGroup* const host = createChildCases(CASETYPE_CALLBACK, m_context, "externally_generated", "Externally Generated Messages", externalFuncs); host->addChild(new GroupCase(m_context, "push_pop_consistency", "Push/pop message generation with full message output checking")); addChild(host); } { vector<FunctionContainer> containers; vector<TestFunctionWrapper> allFuncs; de::Random rng (0x53941903 ^ m_context.getTestContext().getCommandLine().getBaseSeed()); containers.insert(containers.end(), bufferFuncs.begin(), bufferFuncs.end()); containers.insert(containers.end(), textureFuncs.begin(), textureFuncs.end()); containers.insert(containers.end(), externalFuncs.begin(), externalFuncs.end()); for (size_t ndx = 0; ndx < containers.size(); ndx++) allFuncs.push_back(containers[ndx].function); rng.shuffle(allFuncs.begin(), allFuncs.end()); { tcu::TestCaseGroup* const filtering = new tcu::TestCaseGroup(m_testCtx, "error_filters", "Filtering of reported errors"); const int errorFuncsPerCase = 4; const int maxFilteringCaseCount = 32; const int caseCount = (int(allFuncs.size()) + errorFuncsPerCase-1) / errorFuncsPerCase; addChild(filtering); for (int caseNdx = 0; caseNdx < de::min(caseCount, maxFilteringCaseCount); caseNdx++) { const int start = caseNdx*errorFuncsPerCase; const int end = de::min((caseNdx+1)*errorFuncsPerCase, int(allFuncs.size())); const string name = "case_" + de::toString(caseNdx); vector<TestFunctionWrapper> funcs (allFuncs.begin()+start, allFuncs.begin()+end); // These produce lots of different message types, thus always include at least one when testing filtering funcs.insert(funcs.end(), externalFuncs[caseNdx%externalFuncs.size()].function); filtering->addChild(new FilterCase(m_context, name.c_str(), "DebugMessageControl usage", funcs)); } } { tcu::TestCaseGroup* const groups = new tcu::TestCaseGroup(m_testCtx, "error_groups", "Filtering of reported errors with use of Error Groups"); const int errorFuncsPerCase = 4; const int maxFilteringCaseCount = 16; const int caseCount = (int(allFuncs.size()) + errorFuncsPerCase-1) / errorFuncsPerCase; addChild(groups); for (int caseNdx = 0; caseNdx < caseCount && caseNdx < maxFilteringCaseCount; caseNdx++) { const int start = caseNdx*errorFuncsPerCase; const int end = de::min((caseNdx+1)*errorFuncsPerCase, int(allFuncs.size())); const string name = ("case_" + de::toString(caseNdx)).c_str(); vector<TestFunctionWrapper> funcs (&allFuncs[0]+start, &allFuncs[0]+end); // These produce lots of different message types, thus always include at least one when testing filtering funcs.insert(funcs.end(), externalFuncs[caseNdx%externalFuncs.size()].function); groups->addChild(new GroupFilterCase(m_context, name.c_str(), "Debug Group usage", funcs)); } } { tcu::TestCaseGroup* const async = new tcu::TestCaseGroup(m_testCtx, "async", "Asynchronous message generation"); const int errorFuncsPerCase = 2; const int maxAsyncCaseCount = 16; const int caseCount = (int(allFuncs.size()) + errorFuncsPerCase-1) / errorFuncsPerCase; addChild(async); for (int caseNdx = 0; caseNdx < caseCount && caseNdx < maxAsyncCaseCount; caseNdx++) { const int start = caseNdx*errorFuncsPerCase; const int end = de::min((caseNdx+1)*errorFuncsPerCase, int(allFuncs.size())); const string name = ("case_" + de::toString(caseNdx)).c_str(); vector<TestFunctionWrapper> funcs (&allFuncs[0]+start, &allFuncs[0]+end); if (caseNdx&0x1) async->addChild(new AsyncCase(m_context, (name+"_callback").c_str(), "Async message generation", funcs, true)); else async->addChild(new AsyncCase(m_context, (name+"_log").c_str(), "Async message generation", funcs, false)); } } } { tcu::TestCaseGroup* const labels = new tcu::TestCaseGroup(m_testCtx, "object_labels", "Labeling objects"); const struct { GLenum identifier; const char* name; const char* desc; } cases[] = { { GL_BUFFER, "buffer", "Debug label on a buffer object" }, { GL_SHADER, "shader", "Debug label on a shader object" }, { GL_PROGRAM, "program", "Debug label on a program object" }, { GL_QUERY, "query", "Debug label on a query object" }, { GL_PROGRAM_PIPELINE, "program_pipeline", "Debug label on a program pipeline object" }, { GL_TRANSFORM_FEEDBACK, "transform_feedback", "Debug label on a transform feedback object" }, { GL_SAMPLER, "sampler", "Debug label on a sampler object" }, { GL_TEXTURE, "texture", "Debug label on a texture object" }, { GL_RENDERBUFFER, "renderbuffer", "Debug label on a renderbuffer object" }, { GL_FRAMEBUFFER, "framebuffer", "Debug label on a framebuffer object" }, }; addChild(labels); labels->addChild(new InitialLabelCase (m_context, "initial", "Debug label initial value")); labels->addChild(new ClearLabelCase (m_context, "clearing", "Debug label clearing")); labels->addChild(new SpecifyWithLengthCase (m_context, "specify_with_length", "Debug label specified with length")); labels->addChild(new BufferLimitedLabelCase (m_context, "buffer_limited_query", "Debug label query to too short buffer")); labels->addChild(new LabelMaxSizeCase (m_context, "max_label_length", "Max sized debug label")); labels->addChild(new LabelLengthCase (m_context, "query_length_only", "Query debug label length")); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ndx++) labels->addChild(new LabelCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].identifier)); labels->addChild(new SyncLabelCase(m_context, "sync", "Debug label on a sync object")); } } } // Functional } // gles31 } // deqp