// // Copyright 2015 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // DebugTest.cpp : Tests of the GL_KHR_debug extension #include "test_utils/ANGLETest.h" namespace angle { class DebugTest : public ANGLETest { protected: DebugTest() : mDebugExtensionAvailable(false) { setWindowWidth(128); setWindowHeight(128); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); setConfigDepthBits(24); setDebugEnabled(true); } void SetUp() override { ANGLETest::SetUp(); mDebugExtensionAvailable = extensionEnabled("GL_KHR_debug"); if (mDebugExtensionAvailable) { glEnable(GL_DEBUG_OUTPUT); } } bool mDebugExtensionAvailable; }; struct Message { GLenum source; GLenum type; GLuint id; GLenum severity; std::string message; const void *userParam; }; static void GL_APIENTRY Callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { Message m{source, type, id, severity, std::string(message, length), userParam}; std::vector *messages = static_cast *>(const_cast(userParam)); messages->push_back(m); } // Test that all ANGLE back-ends have GL_KHR_debug enabled TEST_P(DebugTest, Enabled) { ASSERT_TRUE(mDebugExtensionAvailable); } // Test that when debug output is disabled, no message are outputted TEST_P(DebugTest, DisabledOutput) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } glDisable(GL_DEBUG_OUTPUT); glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 1, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "discarded"); GLint numMessages = 0; glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); ASSERT_EQ(0, numMessages); std::vector messages; glDebugMessageCallbackKHR(Callback, &messages); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); ASSERT_EQ(0u, messages.size()); } // Test a basic flow of inserting a message and reading it back TEST_P(DebugTest, InsertMessage) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } const GLenum source = GL_DEBUG_SOURCE_APPLICATION; const GLenum type = GL_DEBUG_TYPE_OTHER; const GLuint id = 1; const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; const std::string message = "Message"; glDebugMessageInsertKHR(source, type, id, severity, -1, message.c_str()); GLint numMessages = 0; glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); ASSERT_EQ(1, numMessages); GLint messageLength = 0; glGetIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &messageLength); EXPECT_EQ(static_cast(message.length()), messageLength); GLenum sourceBuf = 0; GLenum typeBuf = 0; GLenum idBuf = 0; GLenum severityBuf = 0; GLsizei lengthBuf = 0; std::vector messageBuf(messageLength + 1); GLuint ret = glGetDebugMessageLogKHR(1, static_cast(messageBuf.size()), &sourceBuf, &typeBuf, &idBuf, &severityBuf, &lengthBuf, messageBuf.data()); EXPECT_EQ(1u, ret); EXPECT_EQ(source, sourceBuf); EXPECT_EQ(type, typeBuf); EXPECT_EQ(id, idBuf); EXPECT_EQ(severity, severityBuf); EXPECT_EQ(lengthBuf, messageLength); EXPECT_STREQ(message.c_str(), messageBuf.data()); glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); EXPECT_EQ(0, numMessages); ASSERT_GL_NO_ERROR(); } // Test inserting multiple messages TEST_P(DebugTest, InsertMessageMultiple) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } const GLenum source = GL_DEBUG_SOURCE_APPLICATION; const GLenum type = GL_DEBUG_TYPE_OTHER; const GLuint startID = 1; const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; const char messageRepeatChar = 'm'; const size_t messageCount = 32; for (size_t i = 0; i < messageCount; i++) { std::string message(i + 1, messageRepeatChar); glDebugMessageInsertKHR(source, type, startID + static_cast(i), severity, -1, message.c_str()); } GLint numMessages = 0; glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); ASSERT_EQ(static_cast(messageCount), numMessages); for (size_t i = 0; i < messageCount; i++) { glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); EXPECT_EQ(static_cast(messageCount - i), numMessages); std::string expectedMessage(i + 1, messageRepeatChar); GLint messageLength = 0; glGetIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &messageLength); EXPECT_EQ(static_cast(expectedMessage.length()), messageLength); GLenum sourceBuf = 0; GLenum typeBuf = 0; GLenum idBuf = 0; GLenum severityBuf = 0; GLsizei lengthBuf = 0; std::vector messageBuf(messageLength + 1); GLuint ret = glGetDebugMessageLogKHR(1, static_cast(messageBuf.size()), &sourceBuf, &typeBuf, &idBuf, &severityBuf, &lengthBuf, messageBuf.data()); EXPECT_EQ(1u, ret); EXPECT_EQ(source, sourceBuf); EXPECT_EQ(type, typeBuf); EXPECT_EQ(startID + i, idBuf); EXPECT_EQ(severity, severityBuf); EXPECT_EQ(lengthBuf, messageLength); EXPECT_STREQ(expectedMessage.c_str(), messageBuf.data()); } glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); EXPECT_EQ(0, numMessages); ASSERT_GL_NO_ERROR(); } // Test using a debug callback TEST_P(DebugTest, DebugCallback) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } std::vector messages; glDebugMessageCallbackKHR(Callback, &messages); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); const GLenum source = GL_DEBUG_SOURCE_APPLICATION; const GLenum type = GL_DEBUG_TYPE_OTHER; const GLuint id = 1; const GLenum severity = GL_DEBUG_SEVERITY_NOTIFICATION; const std::string message = "Message"; glDebugMessageInsertKHR(source, type, id, severity, -1, message.c_str()); GLint numMessages = 0; glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages); EXPECT_EQ(0, numMessages); ASSERT_EQ(1u, messages.size()); const Message &m = messages.front(); EXPECT_EQ(source, m.source); EXPECT_EQ(type, m.type); EXPECT_EQ(id, m.id); EXPECT_EQ(severity, m.severity); EXPECT_EQ(message, m.message); ASSERT_GL_NO_ERROR(); } // Test the glGetPointervKHR entry point TEST_P(DebugTest, GetPointer) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } std::vector messages; glDebugMessageCallbackKHR(Callback, &messages); void *callback = nullptr; glGetPointervKHR(GL_DEBUG_CALLBACK_FUNCTION, &callback); EXPECT_EQ(reinterpret_cast(Callback), callback); void *userData = nullptr; glGetPointervKHR(GL_DEBUG_CALLBACK_USER_PARAM, &userData); EXPECT_EQ(static_cast(&messages), userData); } // Test usage of message control. Example taken from GL_KHR_debug spec. TEST_P(DebugTest, MessageControl1) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } std::vector messages; glDebugMessageCallbackKHR(Callback, &messages); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // Setup of the default active debug group: Filter everything in glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); // Generate a debug marker debug output message glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 1"); // Push debug group 1 glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Message 2"); // Setup of the debug group 1: Filter everything out glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE); // This message won't appear in the debug output log of glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 3"); // Pop debug group 1, restore the volume control of the default debug group. glPopDebugGroupKHR(); // Generate a debug marker debug output message glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 100, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Message 5"); // Expected debug output from the GL implementation // Message 1 // Message 2 // Message 2 // Message 5 EXPECT_EQ(4u, messages.size()); EXPECT_STREQ(messages[0].message.c_str(), "Message 1"); EXPECT_STREQ(messages[1].message.c_str(), "Message 2"); EXPECT_STREQ(messages[2].message.c_str(), "Message 2"); EXPECT_STREQ(messages[3].message.c_str(), "Message 5"); ASSERT_GL_NO_ERROR(); } // Test usage of message control. Example taken from GL_KHR_debug spec. TEST_P(DebugTest, MessageControl2) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } std::vector messages; glDebugMessageCallbackKHR(Callback, &messages); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); // Setup the control of de debug output for the default debug group glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE); glDebugMessageControlKHR(GL_DEBUG_SOURCE_THIRD_PARTY, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE); std::vector ids0 = {1234, 2345, 3456, 4567}; glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, static_cast(ids0.size()), ids0.data(), GL_FALSE); glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, GL_DONT_CARE, static_cast(ids0.size()), ids0.data(), GL_FALSE); // Push debug group 1 // Inherit of the default debug group debug output volume control // Filtered out by glDebugMessageControl glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Message 1"); // In this section of the code, we are interested in performances. glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, GL_DONT_CARE, 0, NULL, GL_TRUE); // But we already identify that some messages are not really useful for us. std::vector ids1 = {5678, 6789}; glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE, static_cast(ids1.size()), ids1.data(), GL_FALSE); glDebugMessageInsertKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 1357, GL_DEBUG_SEVERITY_MEDIUM, -1, "Message 2"); glDebugMessageInsertKHR(GL_DEBUG_SOURCE_THIRD_PARTY, // We still filter out these messages. GL_DEBUG_TYPE_OTHER, 3579, GL_DEBUG_SEVERITY_MEDIUM, -1, "Message 3"); glPopDebugGroupKHR(); // Expected debug output from the GL implementation // Message 2 EXPECT_EQ(1u, messages.size()); EXPECT_STREQ(messages[0].message.c_str(), "Message 2"); ASSERT_GL_NO_ERROR(); } // Test basic usage of setting and getting labels TEST_P(DebugTest, ObjectLabels) { if (!mDebugExtensionAvailable) { std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl; return; } GLuint renderbuffer = 0; glGenRenderbuffers(1, &renderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); const std::string &label = "renderbuffer"; glObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, -1, label.c_str()); std::vector labelBuf(label.length() + 1); GLsizei labelLengthBuf = 0; glGetObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, static_cast(labelBuf.size()), &labelLengthBuf, labelBuf.data()); EXPECT_EQ(static_cast(label.length()), labelLengthBuf); EXPECT_STREQ(label.c_str(), labelBuf.data()); ASSERT_GL_NO_ERROR(); glDeleteRenderbuffers(1, &renderbuffer); glObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, -1, label.c_str()); EXPECT_GL_ERROR(GL_INVALID_VALUE); glGetObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, static_cast(labelBuf.size()), &labelLengthBuf, labelBuf.data()); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Test basic usage of setting and getting labels TEST_P(DebugTest, ObjectPtrLabels) { if (!mDebugExtensionAvailable || getClientMajorVersion() < 3) { std::cout << "Test skipped because GL_KHR_debug or ES3 is not available." << std::endl; return; } GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); const std::string &label = "sync"; glObjectPtrLabelKHR(sync, -1, label.c_str()); std::vector labelBuf(label.length() + 1); GLsizei labelLengthBuf = 0; glGetObjectPtrLabelKHR(sync, static_cast(labelBuf.size()), &labelLengthBuf, labelBuf.data()); EXPECT_EQ(static_cast(label.length()), labelLengthBuf); EXPECT_STREQ(label.c_str(), labelBuf.data()); ASSERT_GL_NO_ERROR(); glDeleteSync(sync); glObjectPtrLabelKHR(sync, -1, label.c_str()); EXPECT_GL_ERROR(GL_INVALID_VALUE); glGetObjectPtrLabelKHR(sync, static_cast(labelBuf.size()), &labelLengthBuf, labelBuf.data()); EXPECT_GL_ERROR(GL_INVALID_VALUE); } // Use this to select which configurations (e.g. which renderer, which GLES major version) these // tests should be run against. ANGLE_INSTANTIATE_TEST(DebugTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); } // namespace angle