blob: d41769f9be3777c018c84ff41f6ea169b29190d6 [file] [log] [blame]
//
// Copyright (c) 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.
//
// Debug.cpp: Defines debug state used for GL_KHR_debug
#include "libANGLE/Debug.h"
#include "common/debug.h"
#include <algorithm>
#include <tuple>
namespace gl
{
Debug::Control::Control() {}
Debug::Control::~Control() {}
Debug::Control::Control(const Control &other) = default;
Debug::Group::Group() {}
Debug::Group::~Group() {}
Debug::Group::Group(const Group &other) = default;
Debug::Debug(bool initialDebugState)
: mOutputEnabled(initialDebugState),
mCallbackFunction(nullptr),
mCallbackUserParam(nullptr),
mMessages(),
mMaxLoggedMessages(0),
mOutputSynchronous(false),
mGroups()
{
pushDefaultGroup();
}
Debug::~Debug() {}
void Debug::setMaxLoggedMessages(GLuint maxLoggedMessages)
{
mMaxLoggedMessages = maxLoggedMessages;
}
void Debug::setOutputEnabled(bool enabled)
{
mOutputEnabled = enabled;
}
bool Debug::isOutputEnabled() const
{
return mOutputEnabled;
}
void Debug::setOutputSynchronous(bool synchronous)
{
mOutputSynchronous = synchronous;
}
bool Debug::isOutputSynchronous() const
{
return mOutputSynchronous;
}
void Debug::setCallback(GLDEBUGPROCKHR callback, const void *userParam)
{
mCallbackFunction = callback;
mCallbackUserParam = userParam;
}
GLDEBUGPROCKHR Debug::getCallback() const
{
return mCallbackFunction;
}
const void *Debug::getUserParam() const
{
return mCallbackUserParam;
}
void Debug::insertMessage(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
const std::string &message) const
{
std::string messageCopy(message);
insertMessage(source, type, id, severity, std::move(messageCopy));
}
void Debug::insertMessage(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
std::string &&message) const
{
if (!isMessageEnabled(source, type, id, severity))
{
return;
}
if (mCallbackFunction != nullptr)
{
// TODO(geofflang) Check the synchronous flag and potentially flush messages from another
// thread.
mCallbackFunction(source, type, id, severity, static_cast<GLsizei>(message.length()),
message.c_str(), mCallbackUserParam);
}
else
{
if (mMessages.size() >= mMaxLoggedMessages)
{
// Drop messages over the limit
return;
}
Message m;
m.source = source;
m.type = type;
m.id = id;
m.severity = severity;
m.message = std::move(message);
mMessages.push_back(std::move(m));
}
}
size_t Debug::getMessages(GLuint count,
GLsizei bufSize,
GLenum *sources,
GLenum *types,
GLuint *ids,
GLenum *severities,
GLsizei *lengths,
GLchar *messageLog)
{
size_t messageCount = 0;
size_t messageStringIndex = 0;
while (messageCount <= count && !mMessages.empty())
{
const Message &m = mMessages.front();
if (messageLog != nullptr)
{
// Check that this message can fit in the message buffer
if (messageStringIndex + m.message.length() + 1 > static_cast<size_t>(bufSize))
{
break;
}
std::copy(m.message.begin(), m.message.end(), messageLog + messageStringIndex);
messageStringIndex += m.message.length();
messageLog[messageStringIndex] = '\0';
messageStringIndex += 1;
}
if (sources != nullptr)
{
sources[messageCount] = m.source;
}
if (types != nullptr)
{
types[messageCount] = m.type;
}
if (ids != nullptr)
{
ids[messageCount] = m.id;
}
if (severities != nullptr)
{
severities[messageCount] = m.severity;
}
if (lengths != nullptr)
{
lengths[messageCount] = static_cast<GLsizei>(m.message.length());
}
mMessages.pop_front();
messageCount++;
}
return messageCount;
}
size_t Debug::getNextMessageLength() const
{
return mMessages.empty() ? 0 : mMessages.front().message.length();
}
size_t Debug::getMessageCount() const
{
return mMessages.size();
}
void Debug::setMessageControl(GLenum source,
GLenum type,
GLenum severity,
std::vector<GLuint> &&ids,
bool enabled)
{
Control c;
c.source = source;
c.type = type;
c.severity = severity;
c.ids = std::move(ids);
c.enabled = enabled;
auto &controls = mGroups.back().controls;
controls.push_back(std::move(c));
}
void Debug::pushGroup(GLenum source, GLuint id, std::string &&message)
{
insertMessage(source, GL_DEBUG_TYPE_PUSH_GROUP, id, GL_DEBUG_SEVERITY_NOTIFICATION,
std::string(message));
Group g;
g.source = source;
g.id = id;
g.message = std::move(message);
mGroups.push_back(std::move(g));
}
void Debug::popGroup()
{
// Make sure the default group is not about to be popped
ASSERT(mGroups.size() > 1);
Group g = mGroups.back();
mGroups.pop_back();
insertMessage(g.source, GL_DEBUG_TYPE_POP_GROUP, g.id, GL_DEBUG_SEVERITY_NOTIFICATION,
g.message);
}
size_t Debug::getGroupStackDepth() const
{
return mGroups.size();
}
bool Debug::isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const
{
if (!mOutputEnabled)
{
return false;
}
for (auto groupIter = mGroups.rbegin(); groupIter != mGroups.rend(); groupIter++)
{
const auto &controls = groupIter->controls;
for (auto controlIter = controls.rbegin(); controlIter != controls.rend(); controlIter++)
{
const auto &control = *controlIter;
if (control.source != GL_DONT_CARE && control.source != source)
{
continue;
}
if (control.type != GL_DONT_CARE && control.type != type)
{
continue;
}
if (control.severity != GL_DONT_CARE && control.severity != severity)
{
continue;
}
if (!control.ids.empty() &&
std::find(control.ids.begin(), control.ids.end(), id) == control.ids.end())
{
continue;
}
return control.enabled;
}
}
return true;
}
void Debug::pushDefaultGroup()
{
Group g;
g.source = GL_NONE;
g.id = 0;
g.message = "";
Control c0;
c0.source = GL_DONT_CARE;
c0.type = GL_DONT_CARE;
c0.severity = GL_DONT_CARE;
c0.enabled = true;
g.controls.push_back(std::move(c0));
Control c1;
c1.source = GL_DONT_CARE;
c1.type = GL_DONT_CARE;
c1.severity = GL_DEBUG_SEVERITY_LOW;
c1.enabled = false;
g.controls.push_back(std::move(c1));
mGroups.push_back(std::move(g));
}
} // namespace gl
namespace egl
{
namespace
{
angle::PackedEnumBitSet<MessageType> GetDefaultMessageTypeBits()
{
angle::PackedEnumBitSet<MessageType> result;
result.set(MessageType::Critical);
result.set(MessageType::Error);
return result;
}
} // anonymous namespace
Debug::Debug() : mCallback(nullptr), mEnabledMessageTypes(GetDefaultMessageTypeBits()) {}
void Debug::setCallback(EGLDEBUGPROCKHR callback, const AttributeMap &attribs)
{
mCallback = callback;
const angle::PackedEnumBitSet<MessageType> defaultMessageTypes = GetDefaultMessageTypeBits();
if (mCallback != nullptr)
{
for (MessageType messageType : angle::AllEnums<MessageType>())
{
mEnabledMessageTypes[messageType] =
(attribs.getAsInt(egl::ToEGLenum(messageType), defaultMessageTypes[messageType]) ==
EGL_TRUE);
}
}
}
EGLDEBUGPROCKHR Debug::getCallback() const
{
return mCallback;
}
bool Debug::isMessageTypeEnabled(MessageType type) const
{
return mEnabledMessageTypes[type];
}
void Debug::insertMessage(EGLenum error,
const char *command,
MessageType messageType,
EGLLabelKHR threadLabel,
EGLLabelKHR objectLabel,
const std::string &message) const
{
// TODO(geofflang): Lock before checking the callback. http://anglebug.com/2464
if (mCallback && isMessageTypeEnabled(messageType))
{
mCallback(error, command, egl::ToEGLenum(messageType), threadLabel, objectLabel,
message.c_str());
}
}
} // namespace egl