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