| /*------------------------------------------------------------------------- |
| * 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
|