Implement GL_KHR_debug.
BUG=angleproject:520
Change-Id: I78d14cc8c94f5cef58604220f0ca847473b25bf8
Reviewed-on: https://chromium-review.googlesource.com/317820
Tryjob-Request: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Buffer.cpp b/src/libANGLE/Buffer.cpp
index 59b2d5e..32c84e0 100644
--- a/src/libANGLE/Buffer.cpp
+++ b/src/libANGLE/Buffer.cpp
@@ -17,6 +17,7 @@
Buffer::Buffer(rx::BufferImpl *impl, GLuint id)
: RefCountObject(id),
+ mLabel(),
mBuffer(impl),
mUsage(GL_STATIC_DRAW),
mSize(0),
@@ -34,6 +35,16 @@
SafeDelete(mBuffer);
}
+void Buffer::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &Buffer::getLabel() const
+{
+ return mLabel;
+}
+
Error Buffer::bufferData(const void *data, GLsizeiptr size, GLenum usage)
{
gl::Error error = mBuffer->setData(data, size, usage);
diff --git a/src/libANGLE/Buffer.h b/src/libANGLE/Buffer.h
index be8d7f3..6c951ef 100644
--- a/src/libANGLE/Buffer.h
+++ b/src/libANGLE/Buffer.h
@@ -12,6 +12,7 @@
#define LIBANGLE_BUFFER_H_
#include "common/angleutils.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/IndexRangeCache.h"
#include "libANGLE/RefCountObject.h"
@@ -24,13 +25,15 @@
namespace gl
{
-class Buffer : public RefCountObject
+class Buffer final : public RefCountObject, public LabeledObject
{
public:
Buffer(rx::BufferImpl *impl, GLuint id);
-
virtual ~Buffer();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
Error bufferData(const void *data, GLsizeiptr size, GLenum usage);
Error bufferSubData(const void *data, GLsizeiptr size, GLintptr offset);
Error copyBufferSubData(Buffer* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size);
@@ -61,6 +64,8 @@
private:
rx::BufferImpl *mBuffer;
+ std::string mLabel;
+
GLenum mUsage;
GLint64 mSize;
GLbitfield mAccessFlags;
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 5766b19..a7c7a0b 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -148,6 +148,10 @@
packSubimage(false),
vertexArrayObject(false),
debug(false),
+ maxDebugMessageLength(0),
+ maxDebugLoggedMessages(0),
+ maxDebugGroupStackDepth(0),
+ maxLabelLength(0),
colorBufferFloat(false)
{
}
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 9596df8..537f964 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -262,6 +262,10 @@
// GL_KHR_debug
bool debug;
+ GLuint maxDebugMessageLength;
+ GLuint maxDebugLoggedMessages;
+ GLuint maxDebugGroupStackDepth;
+ GLuint maxLabelLength;
// ES3 Extension support
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 1a13433..d652695 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -62,7 +62,8 @@
const Context *shareContext,
rx::Renderer *renderer,
bool notifyResets,
- bool robustAccess)
+ bool robustAccess,
+ bool debug)
: ValidationContext(clientVersion,
mState,
mCaps,
@@ -77,7 +78,7 @@
ASSERT(robustAccess == false); // Unimplemented
initCaps(clientVersion);
- mState.initialize(mCaps, clientVersion);
+ mState.initialize(mCaps, mExtensions, clientVersion, debug);
mClientVersion = clientVersion;
@@ -503,7 +504,7 @@
}
}
-Buffer *Context::getBuffer(GLuint handle)
+Buffer *Context::getBuffer(GLuint handle) const
{
return mResourceManager->getBuffer(handle);
}
@@ -523,7 +524,7 @@
return mResourceManager->getTexture(handle);
}
-Renderbuffer *Context::getRenderbuffer(GLuint handle)
+Renderbuffer *Context::getRenderbuffer(GLuint handle) const
{
return mResourceManager->getRenderbuffer(handle);
}
@@ -550,6 +551,41 @@
return (iter != mTransformFeedbackMap.end()) ? iter->second : nullptr;
}
+LabeledObject *Context::getLabeledObject(GLenum identifier, GLuint name) const
+{
+ switch (identifier)
+ {
+ case GL_BUFFER:
+ return getBuffer(name);
+ case GL_SHADER:
+ return getShader(name);
+ case GL_PROGRAM:
+ return getProgram(name);
+ case GL_VERTEX_ARRAY:
+ return getVertexArray(name);
+ case GL_QUERY:
+ return getQuery(name);
+ case GL_TRANSFORM_FEEDBACK:
+ return getTransformFeedback(name);
+ case GL_SAMPLER:
+ return getSampler(name);
+ case GL_TEXTURE:
+ return getTexture(name);
+ case GL_RENDERBUFFER:
+ return getRenderbuffer(name);
+ case GL_FRAMEBUFFER:
+ return getFramebuffer(name);
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+}
+
+LabeledObject *Context::getLabeledObjectFromPtr(const void *ptr) const
+{
+ return getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr)));
+}
+
bool Context::isSampler(GLuint samplerName) const
{
return mResourceManager->isSampler(samplerName);
@@ -776,6 +812,12 @@
}
}
+Query *Context::getQuery(GLuint handle) const
+{
+ auto iter = mQueryMap.find(handle);
+ return (iter != mQueryMap.end()) ? iter->second : nullptr;
+}
+
Texture *Context::getTargetTexture(GLenum target) const
{
ASSERT(ValidTextureTarget(this, target));
@@ -905,6 +947,21 @@
case GL_NUM_EXTENSIONS:
*params = static_cast<GLint>(mExtensionStrings.size());
break;
+
+ // GL_KHR_debug
+ case GL_MAX_DEBUG_MESSAGE_LENGTH:
+ *params = mExtensions.maxDebugMessageLength;
+ break;
+ case GL_MAX_DEBUG_LOGGED_MESSAGES:
+ *params = mExtensions.maxDebugLoggedMessages;
+ break;
+ case GL_MAX_DEBUG_GROUP_STACK_DEPTH:
+ *params = mExtensions.maxDebugGroupStackDepth;
+ break;
+ case GL_MAX_LABEL_LENGTH:
+ *params = mExtensions.maxLabelLength;
+ break;
+
default:
mState.getIntegerv(getData(), pname, params);
break;
@@ -938,6 +995,11 @@
}
}
+void Context::getPointerv(GLenum pname, void **params) const
+{
+ mState.getPointerv(pname, params);
+}
+
bool Context::getIndexedIntegerv(GLenum target, GLuint index, GLint *data)
{
// Queries about context capabilities and maximums are answered by Context.
@@ -1149,6 +1211,29 @@
return true;
}
+ if (mExtensions.debug)
+ {
+ switch (pname)
+ {
+ case GL_DEBUG_LOGGED_MESSAGES:
+ case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH:
+ case GL_DEBUG_GROUP_STACK_DEPTH:
+ case GL_MAX_DEBUG_MESSAGE_LENGTH:
+ case GL_MAX_DEBUG_LOGGED_MESSAGES:
+ case GL_MAX_DEBUG_GROUP_STACK_DEPTH:
+ case GL_MAX_LABEL_LENGTH:
+ *type = GL_INT;
+ *numParams = 1;
+ return true;
+
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ case GL_DEBUG_OUTPUT:
+ *type = GL_BOOL;
+ *numParams = 1;
+ return true;
+ }
+ }
+
// Check for ES3.0+ parameter names which are also exposed as ES2 extensions
switch (pname)
{
@@ -1393,6 +1478,13 @@
if (error.isError())
{
mErrors.insert(error.getCode());
+
+ if (!error.getMessage().empty())
+ {
+ auto &debug = mState.getDebug();
+ debug.insertMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, error.getID(),
+ GL_DEBUG_SEVERITY_HIGH, error.getMessage());
+ }
}
}
@@ -1740,6 +1832,13 @@
//mExtensions.sRGB = false;
}
+ // Explicitly enable GL_KHR_debug
+ mExtensions.debug = true;
+ mExtensions.maxDebugMessageLength = 1024;
+ mExtensions.maxDebugLoggedMessages = 1024;
+ mExtensions.maxDebugGroupStackDepth = 1024;
+ mExtensions.maxLabelLength = 1024;
+
// Apply implementation limits
mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
mCaps.maxVertexUniformBlocks = std::min<GLuint>(mCaps.maxVertexUniformBlocks, IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS);
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index 10fdd0c..f144990 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -58,7 +58,13 @@
class Context final : public ValidationContext
{
public:
- Context(const egl::Config *config, int clientVersion, const Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess);
+ Context(const egl::Config *config,
+ int clientVersion,
+ const Context *shareContext,
+ rx::Renderer *renderer,
+ bool notifyResets,
+ bool robustAccess,
+ bool debug);
virtual ~Context();
@@ -133,18 +139,21 @@
GLint getSamplerParameteri(GLuint sampler, GLenum pname);
GLfloat getSamplerParameterf(GLuint sampler, GLenum pname);
- Buffer *getBuffer(GLuint handle);
+ Buffer *getBuffer(GLuint handle) const;
FenceNV *getFenceNV(GLuint handle);
FenceSync *getFenceSync(GLsync handle) const;
Shader *getShader(GLuint handle) const;
Program *getProgram(GLuint handle) const;
Texture *getTexture(GLuint handle) const;
Framebuffer *getFramebuffer(GLuint handle) const;
- Renderbuffer *getRenderbuffer(GLuint handle);
+ Renderbuffer *getRenderbuffer(GLuint handle) const;
VertexArray *getVertexArray(GLuint handle) const;
Sampler *getSampler(GLuint handle) const;
Query *getQuery(GLuint handle, bool create, GLenum type);
+ Query *getQuery(GLuint handle) const;
TransformFeedback *getTransformFeedback(GLuint handle) const;
+ LabeledObject *getLabeledObject(GLenum identifier, GLuint name) const;
+ LabeledObject *getLabeledObjectFromPtr(const void *ptr) const;
Texture *getTargetTexture(GLenum target) const;
Texture *getSamplerTexture(unsigned int sampler, GLenum type) const;
@@ -160,6 +169,7 @@
void getFloatv(GLenum pname, GLfloat *params);
void getIntegerv(GLenum pname, GLint *params);
void getInteger64v(GLenum pname, GLint64 *params);
+ void getPointerv(GLenum pname, void **params) const;
bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data);
bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data);
diff --git a/src/libANGLE/Debug.cpp b/src/libANGLE/Debug.cpp
new file mode 100644
index 0000000..30321f4
--- /dev/null
+++ b/src/libANGLE/Debug.cpp
@@ -0,0 +1,303 @@
+//
+// 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::Debug()
+ : mOutputEnabled(false),
+ mCallbackFunction(nullptr),
+ mCallbackUserParam(nullptr),
+ mMessages(),
+ mMaxLoggedMessages(0),
+ mOutputSynchronous(false),
+ mGroups()
+{
+ pushDefaultGroup();
+}
+
+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)
+{
+ 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)
+{
+ 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
diff --git a/src/libANGLE/Debug.h b/src/libANGLE/Debug.h
new file mode 100644
index 0000000..cb5ca23
--- /dev/null
+++ b/src/libANGLE/Debug.h
@@ -0,0 +1,119 @@
+//
+// 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.h: Defines debug state used for GL_KHR_debug
+
+#ifndef LIBANGLE_DEBUG_H_
+#define LIBANGLE_DEBUG_H_
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+
+#include <deque>
+#include <string>
+#include <vector>
+
+namespace gl
+{
+
+class LabeledObject
+{
+ public:
+ virtual void setLabel(const std::string &label) = 0;
+ virtual const std::string &getLabel() const = 0;
+};
+
+class Debug : angle::NonCopyable
+{
+ public:
+ Debug();
+
+ void setMaxLoggedMessages(GLuint maxLoggedMessages);
+
+ void setOutputEnabled(bool enabled);
+ bool isOutputEnabled() const;
+
+ void setOutputSynchronous(bool synchronous);
+ bool isOutputSynchronous() const;
+
+ void setCallback(GLDEBUGPROCKHR callback, const void *userParam);
+ GLDEBUGPROCKHR getCallback() const;
+ const void *getUserParam() const;
+
+ void insertMessage(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ const std::string &message);
+ void insertMessage(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ std::string &&message);
+
+ void setMessageControl(GLenum source,
+ GLenum type,
+ GLenum severity,
+ std::vector<GLuint> &&ids,
+ bool enabled);
+ size_t getMessages(GLuint count,
+ GLsizei bufSize,
+ GLenum *sources,
+ GLenum *types,
+ GLuint *ids,
+ GLenum *severities,
+ GLsizei *lengths,
+ GLchar *messageLog);
+ size_t getNextMessageLength() const;
+ size_t getMessageCount() const;
+
+ void pushGroup(GLenum source, GLuint id, std::string &&message);
+ void popGroup();
+ size_t getGroupStackDepth() const;
+
+ private:
+ bool isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const;
+
+ void pushDefaultGroup();
+
+ struct Message
+ {
+ GLenum source;
+ GLenum type;
+ GLuint id;
+ GLenum severity;
+ std::string message;
+ };
+
+ struct Control
+ {
+ GLenum source;
+ GLenum type;
+ GLenum severity;
+ std::vector<GLuint> ids;
+ bool enabled;
+ };
+
+ struct Group
+ {
+ GLenum source;
+ GLuint id;
+ std::string message;
+
+ std::vector<Control> controls;
+ };
+
+ bool mOutputEnabled;
+ GLDEBUGPROCKHR mCallbackFunction;
+ const void *mCallbackUserParam;
+ std::deque<Message> mMessages;
+ GLuint mMaxLoggedMessages;
+ bool mOutputSynchronous;
+ std::vector<Group> mGroups;
+};
+} // namespace gl
+
+#endif // LIBANGLE_DEBUG_H_
diff --git a/src/libANGLE/Error.cpp b/src/libANGLE/Error.cpp
index 0024145..ea52ea1 100644
--- a/src/libANGLE/Error.cpp
+++ b/src/libANGLE/Error.cpp
@@ -17,8 +17,17 @@
{
Error::Error(GLenum errorCode, const char *msg, ...)
- : mCode(errorCode),
- mMessage(nullptr)
+ : mCode(errorCode), mID(errorCode), mMessage(nullptr)
+{
+ va_list vararg;
+ va_start(vararg, msg);
+ createMessageString();
+ *mMessage = FormatString(msg, vararg);
+ va_end(vararg);
+}
+
+Error::Error(GLenum errorCode, GLuint id, const char *msg, ...)
+ : mCode(errorCode), mID(id), mMessage(nullptr)
{
va_list vararg;
va_start(vararg, msg);
diff --git a/src/libANGLE/Error.h b/src/libANGLE/Error.h
index 81c9330..bdb6e0e 100644
--- a/src/libANGLE/Error.h
+++ b/src/libANGLE/Error.h
@@ -22,6 +22,7 @@
public:
explicit inline Error(GLenum errorCode);
Error(GLenum errorCode, const char *msg, ...);
+ Error(GLenum errorCode, GLuint id, const char *msg, ...);
inline Error(const Error &other);
inline Error(Error &&other);
@@ -31,6 +32,7 @@
inline Error &operator=(Error &&other);
inline GLenum getCode() const;
+ inline GLuint getID() const;
inline bool isError() const;
const std::string &getMessage() const;
@@ -43,6 +45,7 @@
void createMessageString() const;
GLenum mCode;
+ GLuint mID;
mutable std::string *mMessage;
};
diff --git a/src/libANGLE/Error.inl b/src/libANGLE/Error.inl
index 32e8f05..d16d11d 100644
--- a/src/libANGLE/Error.inl
+++ b/src/libANGLE/Error.inl
@@ -15,12 +15,14 @@
Error::Error(GLenum errorCode)
: mCode(errorCode),
+ mID(errorCode),
mMessage(nullptr)
{
}
Error::Error(const Error &other)
: mCode(other.mCode),
+ mID(other.mID),
mMessage(nullptr)
{
if (other.mMessage != nullptr)
@@ -32,6 +34,7 @@
Error::Error(Error &&other)
: mCode(other.mCode),
+ mID(other.mID),
mMessage(other.mMessage)
{
other.mMessage = nullptr;
@@ -45,6 +48,7 @@
Error &Error::operator=(const Error &other)
{
mCode = other.mCode;
+ mID = other.mID;
if (other.mMessage != nullptr)
{
@@ -62,6 +66,7 @@
Error &Error::operator=(Error &&other)
{
mCode = other.mCode;
+ mID = other.mID;
mMessage = other.mMessage;
other.mMessage = nullptr;
@@ -74,6 +79,11 @@
return mCode;
}
+GLuint Error::getID() const
+{
+ return mID;
+}
+
bool Error::isError() const
{
return (mCode != GL_NO_ERROR);
diff --git a/src/libANGLE/Fence.cpp b/src/libANGLE/Fence.cpp
index aa76791..ff32f4b 100644
--- a/src/libANGLE/Fence.cpp
+++ b/src/libANGLE/Fence.cpp
@@ -76,10 +76,7 @@
}
FenceSync::FenceSync(rx::FenceSyncImpl *impl, GLuint id)
- : RefCountObject(id),
- mFence(impl),
- mCondition(GL_NONE),
- mFlags(0)
+ : RefCountObject(id), mFence(impl), mLabel(), mCondition(GL_NONE), mFlags(0)
{
}
@@ -88,6 +85,16 @@
SafeDelete(mFence);
}
+void FenceSync::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &FenceSync::getLabel() const
+{
+ return mLabel;
+}
+
Error FenceSync::set(GLenum condition, GLbitfield flags)
{
Error error = mFence->set(condition, flags);
diff --git a/src/libANGLE/Fence.h b/src/libANGLE/Fence.h
index 74dad05..b2daed6 100644
--- a/src/libANGLE/Fence.h
+++ b/src/libANGLE/Fence.h
@@ -10,6 +10,7 @@
#ifndef LIBANGLE_FENCE_H_
#define LIBANGLE_FENCE_H_
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.h"
@@ -47,12 +48,15 @@
GLenum mCondition;
};
-class FenceSync final : public RefCountObject
+class FenceSync final : public RefCountObject, public LabeledObject
{
public:
- explicit FenceSync(rx::FenceSyncImpl *impl, GLuint id);
+ FenceSync(rx::FenceSyncImpl *impl, GLuint id);
virtual ~FenceSync();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
Error set(GLenum condition, GLbitfield flags);
Error clientWait(GLbitfield flags, GLuint64 timeout, GLenum *outResult);
Error serverWait(GLbitfield flags, GLuint64 timeout);
@@ -64,6 +68,8 @@
private:
rx::FenceSyncImpl *mFence;
+ std::string mLabel;
+
GLenum mCondition;
GLbitfield mFlags;
};
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index c388d1e..1c2646d 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -40,7 +40,8 @@
}
Framebuffer::Data::Data()
- : mColorAttachments(1),
+ : mLabel(),
+ mColorAttachments(1),
mDrawBufferStates(1, GL_NONE),
mReadBufferState(GL_COLOR_ATTACHMENT0_EXT)
{
@@ -48,7 +49,8 @@
}
Framebuffer::Data::Data(const Caps &caps)
- : mColorAttachments(caps.maxColorAttachments),
+ : mLabel(),
+ mColorAttachments(caps.maxColorAttachments),
mDrawBufferStates(caps.maxDrawBuffers, GL_NONE),
mReadBufferState(GL_COLOR_ATTACHMENT0_EXT)
{
@@ -59,6 +61,11 @@
{
}
+const std::string &Framebuffer::Data::getLabel()
+{
+ return mLabel;
+}
+
const FramebufferAttachment *Framebuffer::Data::getReadAttachment() const
{
ASSERT(mReadBufferState == GL_BACK || (mReadBufferState >= GL_COLOR_ATTACHMENT0 && mReadBufferState <= GL_COLOR_ATTACHMENT15));
@@ -179,6 +186,16 @@
SafeDelete(mImpl);
}
+void Framebuffer::setLabel(const std::string &label)
+{
+ mData.mLabel = label;
+}
+
+const std::string &Framebuffer::getLabel() const
+{
+ return mData.mLabel;
+}
+
void Framebuffer::detachTexture(GLuint textureId)
{
detachResourceById(GL_TEXTURE, textureId);
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index fb9060d..57e8de5 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -14,6 +14,7 @@
#include "common/angleutils.h"
#include "libANGLE/Constants.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/RefCountObject.h"
@@ -44,7 +45,7 @@
struct ImageIndex;
struct Rectangle;
-class Framebuffer
+class Framebuffer final : public LabeledObject
{
public:
@@ -55,6 +56,8 @@
explicit Data(const Caps &caps);
~Data();
+ const std::string &getLabel();
+
const FramebufferAttachment *getReadAttachment() const;
const FramebufferAttachment *getFirstColorAttachment() const;
const FramebufferAttachment *getDepthOrStencilAttachment() const;
@@ -71,6 +74,8 @@
private:
friend class Framebuffer;
+ std::string mLabel;
+
std::vector<FramebufferAttachment> mColorAttachments;
FramebufferAttachment mDepthAttachment;
FramebufferAttachment mStencilAttachment;
@@ -83,6 +88,9 @@
Framebuffer(rx::SurfaceImpl *surface);
virtual ~Framebuffer();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
const rx::FramebufferImpl *getImplementation() const { return mImpl; }
rx::FramebufferImpl *getImplementation() { return mImpl; }
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 4a99c88..e21c8f4 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -223,7 +223,8 @@
}
Program::Data::Data()
- : mAttachedFragmentShader(nullptr),
+ : mLabel(),
+ mAttachedFragmentShader(nullptr),
mAttachedVertexShader(nullptr),
mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
mBinaryRetrieveableHint(false)
@@ -243,6 +244,11 @@
}
}
+const std::string &Program::Data::getLabel()
+{
+ return mLabel;
+}
+
const LinkedUniform *Program::Data::getUniformByName(const std::string &name) const
{
for (const LinkedUniform &linkedUniform : mUniforms)
@@ -328,6 +334,16 @@
SafeDelete(mProgram);
}
+void Program::setLabel(const std::string &label)
+{
+ mData.mLabel = label;
+}
+
+const std::string &Program::getLabel() const
+{
+ return mData.mLabel;
+}
+
bool Program::attachShader(Shader *shader)
{
if (shader->getType() == GL_VERTEX_SHADER)
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index f538ce4..f885ad1 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -24,6 +24,7 @@
#include "libANGLE/angletypes.h"
#include "libANGLE/Constants.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.h"
@@ -143,7 +144,7 @@
unsigned int index;
};
-class Program : angle::NonCopyable
+class Program final : angle::NonCopyable, public LabeledObject
{
public:
class Data final : angle::NonCopyable
@@ -152,6 +153,8 @@
Data();
~Data();
+ const std::string &getLabel();
+
const Shader *getAttachedVertexShader() const { return mAttachedVertexShader; }
const Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; }
const std::vector<std::string> &getTransformFeedbackVaryingNames() const
@@ -191,6 +194,8 @@
private:
friend class Program;
+ std::string mLabel;
+
Shader *mAttachedFragmentShader;
Shader *mAttachedVertexShader;
@@ -224,6 +229,9 @@
GLuint id() const { return mHandle; }
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
rx::ProgramImpl *getImplementation() { return mProgram; }
const rx::ProgramImpl *getImplementation() const { return mProgram; }
diff --git a/src/libANGLE/Query.cpp b/src/libANGLE/Query.cpp
index e88ea2b..09bf53d 100644
--- a/src/libANGLE/Query.cpp
+++ b/src/libANGLE/Query.cpp
@@ -11,9 +11,7 @@
namespace gl
{
-Query::Query(rx::QueryImpl *impl, GLuint id)
- : RefCountObject(id),
- mQuery(impl)
+Query::Query(rx::QueryImpl *impl, GLuint id) : RefCountObject(id), mQuery(impl), mLabel()
{
}
@@ -22,6 +20,16 @@
SafeDelete(mQuery);
}
+void Query::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &Query::getLabel() const
+{
+ return mLabel;
+}
+
Error Query::begin()
{
return mQuery->begin();
diff --git a/src/libANGLE/Query.h b/src/libANGLE/Query.h
index cf54199..61c41ec 100644
--- a/src/libANGLE/Query.h
+++ b/src/libANGLE/Query.h
@@ -9,6 +9,7 @@
#ifndef LIBANGLE_QUERY_H_
#define LIBANGLE_QUERY_H_
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.h"
@@ -24,12 +25,15 @@
namespace gl
{
-class Query : public RefCountObject
+class Query final : public RefCountObject, public LabeledObject
{
public:
Query(rx::QueryImpl *impl, GLuint id);
virtual ~Query();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
Error begin();
Error end();
@@ -43,6 +47,8 @@
private:
rx::QueryImpl *mQuery;
+
+ std::string mLabel;
};
}
diff --git a/src/libANGLE/Renderbuffer.cpp b/src/libANGLE/Renderbuffer.cpp
index 3c1a2cc..161fbea 100644
--- a/src/libANGLE/Renderbuffer.cpp
+++ b/src/libANGLE/Renderbuffer.cpp
@@ -22,6 +22,7 @@
Renderbuffer::Renderbuffer(rx::RenderbufferImpl *impl, GLuint id)
: egl::ImageSibling(id),
mRenderbuffer(impl),
+ mLabel(),
mWidth(0),
mHeight(0),
mInternalFormat(GL_RGBA4),
@@ -34,6 +35,16 @@
SafeDelete(mRenderbuffer);
}
+void Renderbuffer::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &Renderbuffer::getLabel() const
+{
+ return mLabel;
+}
+
Error Renderbuffer::setStorage(GLenum internalformat, size_t width, size_t height)
{
orphanImages();
diff --git a/src/libANGLE/Renderbuffer.h b/src/libANGLE/Renderbuffer.h
index 1e0d650..04af03e 100644
--- a/src/libANGLE/Renderbuffer.h
+++ b/src/libANGLE/Renderbuffer.h
@@ -13,6 +13,7 @@
#include "angle_gl.h"
#include "common/angleutils.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Image.h"
@@ -25,12 +26,17 @@
// FramebufferAttachment and Framebuffer for how they are applied to an FBO via an
// attachment point.
-class Renderbuffer : public egl::ImageSibling, public gl::FramebufferAttachmentObject
+class Renderbuffer final : public egl::ImageSibling,
+ public gl::FramebufferAttachmentObject,
+ public LabeledObject
{
public:
Renderbuffer(rx::RenderbufferImpl *impl, GLuint id);
virtual ~Renderbuffer();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
Error setStorage(GLenum internalformat, size_t width, size_t height);
Error setStorageMultisample(size_t samples, GLenum internalformat, size_t width, size_t height);
Error setStorageEGLImageTarget(egl::Image *imageTarget);
@@ -63,6 +69,8 @@
rx::RenderbufferImpl *mRenderbuffer;
+ std::string mLabel;
+
GLsizei mWidth;
GLsizei mHeight;
GLenum mInternalFormat;
diff --git a/src/libANGLE/Sampler.cpp b/src/libANGLE/Sampler.cpp
index eff8702..d8d606a 100644
--- a/src/libANGLE/Sampler.cpp
+++ b/src/libANGLE/Sampler.cpp
@@ -16,7 +16,7 @@
{
Sampler::Sampler(rx::ImplFactory *factory, GLuint id)
- : RefCountObject(id), mImpl(factory->createSampler()), mSamplerState()
+ : RefCountObject(id), mImpl(factory->createSampler()), mLabel(), mSamplerState()
{
}
@@ -25,6 +25,16 @@
SafeDelete(mImpl);
}
+void Sampler::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &Sampler::getLabel() const
+{
+ return mLabel;
+}
+
void Sampler::setMinFilter(GLenum minFilter)
{
mSamplerState.minFilter = minFilter;
diff --git a/src/libANGLE/Sampler.h b/src/libANGLE/Sampler.h
index 2e78c58..a40b165 100644
--- a/src/libANGLE/Sampler.h
+++ b/src/libANGLE/Sampler.h
@@ -11,6 +11,7 @@
#define LIBANGLE_SAMPLER_H_
#include "libANGLE/angletypes.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/RefCountObject.h"
namespace rx
@@ -22,12 +23,15 @@
namespace gl
{
-class Sampler final : public RefCountObject
+class Sampler final : public RefCountObject, public LabeledObject
{
public:
Sampler(rx::ImplFactory *factory, GLuint id);
~Sampler() override;
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
void setMinFilter(GLenum minFilter);
GLenum getMinFilter() const;
@@ -66,6 +70,8 @@
private:
rx::SamplerImpl *mImpl;
+ std::string mLabel;
+
SamplerState mSamplerState;
};
diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp
index 6cfa4b9..bbe9077 100644
--- a/src/libANGLE/Shader.cpp
+++ b/src/libANGLE/Shader.cpp
@@ -72,7 +72,7 @@
return gl::VariableSortOrder(x.type) < gl::VariableSortOrder(y.type);
}
-Shader::Data::Data(GLenum shaderType) : mShaderType(shaderType), mShaderVersion(100)
+Shader::Data::Data(GLenum shaderType) : mLabel(), mShaderType(shaderType), mShaderVersion(100)
{
}
@@ -103,6 +103,16 @@
SafeDelete(mImplementation);
}
+void Shader::setLabel(const std::string &label)
+{
+ mData.mLabel = label;
+}
+
+const std::string &Shader::getLabel() const
+{
+ return mData.mLabel;
+}
+
GLuint Shader::getHandle() const
{
return mHandle;
diff --git a/src/libANGLE/Shader.h b/src/libANGLE/Shader.h
index 290e1cd..997977c 100644
--- a/src/libANGLE/Shader.h
+++ b/src/libANGLE/Shader.h
@@ -21,6 +21,7 @@
#include "common/angleutils.h"
#include "libANGLE/angletypes.h"
+#include "libANGLE/Debug.h"
namespace rx
{
@@ -36,7 +37,7 @@
class ResourceManager;
struct Data;
-class Shader : angle::NonCopyable
+class Shader final : angle::NonCopyable, public LabeledObject
{
public:
class Data final : angle::NonCopyable
@@ -45,6 +46,8 @@
Data(GLenum shaderType);
~Data();
+ const std::string &getLabel() const { return mLabel; }
+
const std::string &getSource() const { return mSource; }
const std::string &getTranslatedSource() const { return mTranslatedSource; }
@@ -66,6 +69,8 @@
private:
friend class Shader;
+ std::string mLabel;
+
GLenum mShaderType;
int mShaderVersion;
std::string mTranslatedSource;
@@ -86,6 +91,9 @@
virtual ~Shader();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
GLenum getType() const { return mType; }
GLuint getHandle() const;
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index e5feef4..aaf4b68 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -10,6 +10,7 @@
#include "libANGLE/Context.h"
#include "libANGLE/Caps.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/Query.h"
@@ -78,7 +79,10 @@
reset();
}
-void State::initialize(const Caps &caps, GLuint clientVersion)
+void State::initialize(const Caps &caps,
+ const Extensions &extensions,
+ GLuint clientVersion,
+ bool debug)
{
mMaxDrawBuffers = caps.maxDrawBuffers;
mMaxCombinedTextureImageUnits = caps.maxCombinedTextureImageUnits;
@@ -185,6 +189,9 @@
mDrawFramebuffer = NULL;
mPrimitiveRestart = false;
+
+ mDebug.setOutputEnabled(debug);
+ mDebug.setMaxLoggedMessages(extensions.maxDebugLoggedMessages);
}
void State::reset()
@@ -581,6 +588,12 @@
case GL_DITHER: setDither(enabled); break;
case GL_PRIMITIVE_RESTART_FIXED_INDEX: setPrimitiveRestart(enabled); break;
case GL_RASTERIZER_DISCARD: setRasterizerDiscard(enabled); break;
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ mDebug.setOutputSynchronous(enabled);
+ break;
+ case GL_DEBUG_OUTPUT:
+ mDebug.setOutputEnabled(enabled);
+ break;
default: UNREACHABLE();
}
}
@@ -600,6 +613,10 @@
case GL_DITHER: return isDitherEnabled();
case GL_PRIMITIVE_RESTART_FIXED_INDEX: return isPrimitiveRestartEnabled();
case GL_RASTERIZER_DISCARD: return isRasterizerDiscardEnabled();
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ return mDebug.isOutputSynchronous();
+ case GL_DEBUG_OUTPUT:
+ return mDebug.isOutputEnabled();
default: UNREACHABLE(); return false;
}
}
@@ -1271,6 +1288,16 @@
return mUnpack;
}
+const Debug &State::getDebug() const
+{
+ return mDebug;
+}
+
+Debug &State::getDebug()
+{
+ return mDebug;
+}
+
void State::getBooleanv(GLenum pname, GLboolean *params)
{
switch (pname)
@@ -1300,6 +1327,12 @@
case GL_RASTERIZER_DISCARD:
*params = isRasterizerDiscardEnabled() ? GL_TRUE : GL_FALSE;
break;
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ *params = mDebug.isOutputSynchronous() ? GL_TRUE : GL_FALSE;
+ break;
+ case GL_DEBUG_OUTPUT:
+ *params = mDebug.isOutputEnabled() ? GL_TRUE : GL_FALSE;
+ break;
default:
UNREACHABLE();
break;
@@ -1557,12 +1590,37 @@
case GL_PIXEL_UNPACK_BUFFER_BINDING:
*params = mUnpack.pixelBuffer.id();
break;
+ case GL_DEBUG_LOGGED_MESSAGES:
+ *params = static_cast<GLint>(mDebug.getMessageCount());
+ break;
+ case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH:
+ *params = static_cast<GLint>(mDebug.getNextMessageLength());
+ break;
+ case GL_DEBUG_GROUP_STACK_DEPTH:
+ *params = static_cast<GLint>(mDebug.getGroupStackDepth());
+ break;
default:
UNREACHABLE();
break;
}
}
+void State::getPointerv(GLenum pname, void **params) const
+{
+ switch (pname)
+ {
+ case GL_DEBUG_CALLBACK_FUNCTION:
+ *params = reinterpret_cast<void *>(mDebug.getCallback());
+ break;
+ case GL_DEBUG_CALLBACK_USER_PARAM:
+ *params = const_cast<void *>(mDebug.getUserParam());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
bool State::getIndexedIntegerv(GLenum target, GLuint index, GLint *data)
{
switch (target)
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 41f391f..f6bec0f 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -10,8 +10,10 @@
#define LIBANGLE_STATE_H_
#include <bitset>
+#include <memory>
#include "common/angleutils.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Program.h"
#include "libANGLE/RefCountObject.h"
#include "libANGLE/Renderbuffer.h"
@@ -37,7 +39,10 @@
State();
~State();
- void initialize(const Caps& caps, GLuint clientVersion);
+ void initialize(const Caps &caps,
+ const Extensions &extensions,
+ GLuint clientVersion,
+ bool debug);
void reset();
// State chunk getters
@@ -258,10 +263,15 @@
const PixelUnpackState &getUnpackState() const;
PixelUnpackState &getUnpackState();
+ // Debug state
+ const Debug &getDebug() const;
+ Debug &getDebug();
+
// State query functions
void getBooleanv(GLenum pname, GLboolean *params);
void getFloatv(GLenum pname, GLfloat *params);
void getIntegerv(const gl::Data &data, GLenum pname, GLint *params);
+ void getPointerv(GLenum pname, void **params) const;
bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data);
bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data);
@@ -412,6 +422,8 @@
bool mPrimitiveRestart;
+ Debug mDebug;
+
DirtyBits mDirtyBits;
DirtyBits mUnpackStateBitMask;
DirtyBits mPackStateBitMask;
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index b22011d..8d7a5f8 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -50,6 +50,7 @@
Texture::Texture(rx::TextureImpl *impl, GLuint id, GLenum target)
: egl::ImageSibling(id),
mTexture(impl),
+ mLabel(),
mTextureState(),
mTarget(target),
mImageDescs(IMPLEMENTATION_MAX_TEXTURE_LEVELS * (target == GL_TEXTURE_CUBE_MAP ? 6 : 1)),
@@ -68,6 +69,16 @@
SafeDelete(mTexture);
}
+void Texture::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &Texture::getLabel() const
+{
+ return mLabel;
+}
+
GLenum Texture::getTarget() const
{
return mTarget;
diff --git a/src/libANGLE/Texture.h b/src/libANGLE/Texture.h
index 721e775..7ca8a45 100644
--- a/src/libANGLE/Texture.h
+++ b/src/libANGLE/Texture.h
@@ -15,6 +15,7 @@
#include "angle_gl.h"
#include "common/debug.h"
#include "libANGLE/Caps.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/Constants.h"
#include "libANGLE/Error.h"
#include "libANGLE/FramebufferAttachment.h"
@@ -33,14 +34,19 @@
class Framebuffer;
struct Data;
-bool IsMipmapFiltered(const gl::SamplerState &samplerState);
+bool IsMipmapFiltered(const SamplerState &samplerState);
-class Texture final : public egl::ImageSibling, public gl::FramebufferAttachmentObject
+class Texture final : public egl::ImageSibling,
+ public FramebufferAttachmentObject,
+ public LabeledObject
{
public:
Texture(rx::TextureImpl *impl, GLuint id, GLenum target);
~Texture() override;
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
GLenum getTarget() const;
void setSwizzleRed(GLenum swizzleRed);
@@ -184,6 +190,8 @@
rx::TextureImpl *mTexture;
+ std::string mLabel;
+
TextureState mTextureState;
GLenum mTarget;
diff --git a/src/libANGLE/TransformFeedback.cpp b/src/libANGLE/TransformFeedback.cpp
index ff4cd3d..b796197 100644
--- a/src/libANGLE/TransformFeedback.cpp
+++ b/src/libANGLE/TransformFeedback.cpp
@@ -13,9 +13,10 @@
namespace gl
{
-TransformFeedback::TransformFeedback(rx::TransformFeedbackImpl* impl, GLuint id, const Caps &caps)
+TransformFeedback::TransformFeedback(rx::TransformFeedbackImpl *impl, GLuint id, const Caps &caps)
: RefCountObject(id),
mImplementation(impl),
+ mLabel(),
mActive(false),
mPrimitiveMode(GL_NONE),
mPaused(false),
@@ -36,6 +37,16 @@
SafeDelete(mImplementation);
}
+void TransformFeedback::setLabel(const std::string &label)
+{
+ mLabel = label;
+}
+
+const std::string &TransformFeedback::getLabel() const
+{
+ return mLabel;
+}
+
void TransformFeedback::begin(GLenum primitiveMode)
{
mActive = true;
diff --git a/src/libANGLE/TransformFeedback.h b/src/libANGLE/TransformFeedback.h
index a3aecb8..098e4ea 100644
--- a/src/libANGLE/TransformFeedback.h
+++ b/src/libANGLE/TransformFeedback.h
@@ -10,6 +10,7 @@
#include "libANGLE/RefCountObject.h"
#include "common/angleutils.h"
+#include "libANGLE/Debug.h"
#include "angle_gl.h"
@@ -23,12 +24,15 @@
class Buffer;
struct Caps;
-class TransformFeedback : public RefCountObject
+class TransformFeedback final : public RefCountObject, public LabeledObject
{
public:
TransformFeedback(rx::TransformFeedbackImpl* impl, GLuint id, const Caps &caps);
virtual ~TransformFeedback();
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
void begin(GLenum primitiveMode);
void end();
void pause();
@@ -53,6 +57,8 @@
private:
rx::TransformFeedbackImpl* mImplementation;
+ std::string mLabel;
+
bool mActive;
GLenum mPrimitiveMode;
bool mPaused;
diff --git a/src/libANGLE/VertexArray.cpp b/src/libANGLE/VertexArray.cpp
index afa4332..8d51e9b 100644
--- a/src/libANGLE/VertexArray.cpp
+++ b/src/libANGLE/VertexArray.cpp
@@ -15,8 +15,7 @@
{
VertexArray::Data::Data(size_t maxAttribs)
- : mVertexAttributes(maxAttribs),
- mMaxEnabledAttribute(0)
+ : mLabel(), mVertexAttributes(maxAttribs), mMaxEnabledAttribute(0)
{
}
@@ -46,6 +45,16 @@
return mId;
}
+void VertexArray::setLabel(const std::string &label)
+{
+ mData.mLabel = label;
+}
+
+const std::string &VertexArray::getLabel() const
+{
+ return mData.mLabel;
+}
+
void VertexArray::detachBuffer(GLuint bufferName)
{
for (size_t attribute = 0; attribute < getMaxAttribs(); attribute++)
diff --git a/src/libANGLE/VertexArray.h b/src/libANGLE/VertexArray.h
index 9ac7f7b..27b020b 100644
--- a/src/libANGLE/VertexArray.h
+++ b/src/libANGLE/VertexArray.h
@@ -15,6 +15,7 @@
#include "libANGLE/RefCountObject.h"
#include "libANGLE/Constants.h"
+#include "libANGLE/Debug.h"
#include "libANGLE/State.h"
#include "libANGLE/VertexAttribute.h"
@@ -30,7 +31,7 @@
{
class Buffer;
-class VertexArray
+class VertexArray final : public LabeledObject
{
public:
VertexArray(rx::ImplFactory *factory, GLuint id, size_t maxAttribs);
@@ -38,6 +39,9 @@
GLuint id() const;
+ void setLabel(const std::string &label) override;
+ const std::string &getLabel() const override;
+
const VertexAttribute &getVertexAttribute(size_t attributeIndex) const;
void detachBuffer(GLuint bufferName);
@@ -63,6 +67,8 @@
explicit Data(size_t maxAttribs);
~Data();
+ const std::string &getLabel() const { return mLabel; }
+
const BindingPointer<Buffer> &getElementArrayBuffer() const { return mElementArrayBuffer; }
size_t getMaxAttribs() const { return mVertexAttributes.size(); }
size_t getMaxEnabledAttribute() const { return mMaxEnabledAttribute; }
@@ -74,6 +80,7 @@
private:
friend class VertexArray;
+ std::string mLabel;
std::vector<VertexAttribute> mVertexAttributes;
BindingPointer<Buffer> mElementArrayBuffer;
size_t mMaxEnabledAttribute;
diff --git a/src/libANGLE/renderer/d3d/DisplayD3D.cpp b/src/libANGLE/renderer/d3d/DisplayD3D.cpp
index ceea6dd..3aced4f 100644
--- a/src/libANGLE/renderer/d3d/DisplayD3D.cpp
+++ b/src/libANGLE/renderer/d3d/DisplayD3D.cpp
@@ -235,8 +235,10 @@
EGLint clientVersion = attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1);
bool notifyResets = (attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION_EXT) == EGL_LOSE_CONTEXT_ON_RESET_EXT);
bool robustAccess = (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE);
+ bool debug = (attribs.get(EGL_CONTEXT_OPENGL_DEBUG, EGL_FALSE) == EGL_TRUE);
- *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets, robustAccess);
+ *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets,
+ robustAccess, debug);
return egl::Error(EGL_SUCCESS);
}
diff --git a/src/libANGLE/renderer/gl/DisplayGL.cpp b/src/libANGLE/renderer/gl/DisplayGL.cpp
index 8c809d6..16cbc3c 100644
--- a/src/libANGLE/renderer/gl/DisplayGL.cpp
+++ b/src/libANGLE/renderer/gl/DisplayGL.cpp
@@ -62,8 +62,10 @@
EGLint clientVersion = attribs.get(EGL_CONTEXT_CLIENT_VERSION, 1);
bool notifyResets = (attribs.get(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_NO_RESET_NOTIFICATION_EXT) == EGL_LOSE_CONTEXT_ON_RESET_EXT);
bool robustAccess = (attribs.get(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE) == EGL_TRUE);
+ bool debug = (attribs.get(EGL_CONTEXT_OPENGL_DEBUG, EGL_FALSE) == EGL_TRUE);
- *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets, robustAccess);
+ *outContext = new gl::Context(config, clientVersion, shareContext, mRenderer, notifyResets,
+ robustAccess, debug);
return egl::Error(EGL_SUCCESS);
}
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index 14f83ba..5cbb6d8 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -210,6 +210,9 @@
contextFlags = value;
break;
+ case EGL_CONTEXT_OPENGL_DEBUG:
+ break;
+
case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
// Only valid for OpenGL (non-ES) contexts
return Error(EGL_BAD_ATTRIBUTE);
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index e9cd516..1784bba 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -109,9 +109,15 @@
case GL_BLEND:
case GL_DITHER:
return true;
+
case GL_PRIMITIVE_RESTART_FIXED_INDEX:
case GL_RASTERIZER_DISCARD:
return (context->getClientVersion() >= 3);
+
+ case GL_DEBUG_OUTPUT_SYNCHRONOUS:
+ case GL_DEBUG_OUTPUT:
+ return context->getExtensions().debug;
+
default:
return false;
}
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 75d9096..b929078 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -1069,6 +1069,61 @@
return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
}
+static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication)
+{
+ switch (source)
+ {
+ case GL_DEBUG_SOURCE_API:
+ case GL_DEBUG_SOURCE_SHADER_COMPILER:
+ case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+ case GL_DEBUG_SOURCE_OTHER:
+ // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted
+ return !mustBeThirdPartyOrApplication;
+
+ case GL_DEBUG_SOURCE_THIRD_PARTY:
+ case GL_DEBUG_SOURCE_APPLICATION:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidDebugType(GLenum type)
+{
+ switch (type)
+ {
+ case GL_DEBUG_TYPE_ERROR:
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ case GL_DEBUG_TYPE_PERFORMANCE:
+ case GL_DEBUG_TYPE_PORTABILITY:
+ case GL_DEBUG_TYPE_OTHER:
+ case GL_DEBUG_TYPE_MARKER:
+ case GL_DEBUG_TYPE_PUSH_GROUP:
+ case GL_DEBUG_TYPE_POP_GROUP:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool ValidDebugSeverity(GLenum severity)
+{
+ switch (severity)
+ {
+ case GL_DEBUG_SEVERITY_HIGH:
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ case GL_DEBUG_SEVERITY_LOW:
+ case GL_DEBUG_SEVERITY_NOTIFICATION:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
bool ValidateDebugMessageControlKHR(Context *context,
GLenum source,
GLenum type,
@@ -1083,7 +1138,43 @@
return false;
}
- UNIMPLEMENTED();
+ if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ return false;
+ }
+
+ if (!ValidDebugType(type) && type != GL_DONT_CARE)
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+ return false;
+ }
+
+ if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+ return false;
+ }
+
+ if (count > 0)
+ {
+ if (source == GL_DONT_CARE || type == GL_DONT_CARE)
+ {
+ context->recordError(Error(
+ GL_INVALID_OPERATION,
+ "If count is greater than zero, source and severity cannot be GL_DONT_CARE."));
+ return false;
+ }
+
+ if (severity != GL_DONT_CARE)
+ {
+ context->recordError(
+ Error(GL_INVALID_OPERATION,
+ "If count is greater than zero, severity must be GL_DONT_CARE."));
+ return false;
+ }
+ }
+
return true;
}
@@ -1101,7 +1192,39 @@
return false;
}
- UNIMPLEMENTED();
+ if (!context->getState().getDebug().isOutputEnabled())
+ {
+ // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do
+ // not generate an error.
+ return false;
+ }
+
+ if (!ValidDebugSeverity(severity))
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+ return false;
+ }
+
+ if (!ValidDebugType(type))
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+ return false;
+ }
+
+ if (!ValidDebugSource(source, true))
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(buf) : length;
+ if (messageLength > context->getExtensions().maxDebugMessageLength)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+ return false;
+ }
+
return true;
}
@@ -1115,7 +1238,6 @@
return false;
}
- UNIMPLEMENTED();
return true;
}
@@ -1135,7 +1257,13 @@
return false;
}
- UNIMPLEMENTED();
+ if (bufSize < 0 && messageLog != nullptr)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "bufSize must be positive if messageLog is not null."));
+ return false;
+ }
+
return true;
}
@@ -1151,7 +1279,29 @@
return false;
}
- UNIMPLEMENTED();
+ if (!ValidDebugSource(source, true))
+ {
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+ return false;
+ }
+
+ size_t messageLength = (length < 0) ? strlen(message) : length;
+ if (messageLength > context->getExtensions().maxDebugMessageLength)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+ return false;
+ }
+
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
+ {
+ context->recordError(
+ Error(GL_STACK_OVERFLOW,
+ "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups."));
+ return false;
+ }
+
return true;
}
@@ -1163,7 +1313,106 @@
return false;
}
- UNIMPLEMENTED();
+ size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+ if (currentStackSize <= 1)
+ {
+ context->recordError(Error(GL_STACK_UNDERFLOW, "Cannot pop the default debug group."));
+ return false;
+ }
+
+ return true;
+}
+
+static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name)
+{
+ switch (identifier)
+ {
+ case GL_BUFFER:
+ if (context->getBuffer(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid buffer."));
+ return false;
+ }
+ return true;
+
+ case GL_SHADER:
+ if (context->getShader(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid shader."));
+ return false;
+ }
+ return true;
+
+ case GL_PROGRAM:
+ if (context->getProgram(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid program."));
+ return false;
+ }
+ return true;
+
+ case GL_VERTEX_ARRAY:
+ if (context->getVertexArray(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid vertex array."));
+ return false;
+ }
+ return true;
+
+ case GL_QUERY:
+ if (context->getQuery(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid query."));
+ return false;
+ }
+ return true;
+
+ case GL_TRANSFORM_FEEDBACK:
+ if (context->getTransformFeedback(name) == nullptr)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "name is not a valid transform feedback."));
+ return false;
+ }
+ return true;
+
+ case GL_SAMPLER:
+ if (context->getSampler(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sampler."));
+ return false;
+ }
+ return true;
+
+ case GL_TEXTURE:
+ if (context->getTexture(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid texture."));
+ return false;
+ }
+ return true;
+
+ case GL_RENDERBUFFER:
+ if (context->getRenderbuffer(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid renderbuffer."));
+ return false;
+ }
+ return true;
+
+ case GL_FRAMEBUFFER:
+ if (context->getFramebuffer(name) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid framebuffer."));
+ return false;
+ }
+ return true;
+
+ default:
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid identifier."));
+ return false;
+ }
+
return true;
}
@@ -1179,7 +1428,19 @@
return false;
}
- UNIMPLEMENTED();
+ if (!ValidateObjectIdentifierAndName(context, identifier, name))
+ {
+ return false;
+ }
+
+ size_t labelLength = (length < 0) ? strlen(label) : length;
+ if (labelLength > context->getExtensions().maxLabelLength)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
+ return false;
+ }
+
return true;
}
@@ -1196,7 +1457,29 @@
return false;
}
- UNIMPLEMENTED();
+ if (bufSize < 0)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+ return false;
+ }
+
+ if (!ValidateObjectIdentifierAndName(context, identifier, name))
+ {
+ return false;
+ }
+
+ // Can no-op if bufSize is zero.
+ return bufSize > 0;
+}
+
+static bool ValidateObjectPtrName(Context *context, const void *ptr)
+{
+ if (context->getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sync."));
+ return false;
+ }
+
return true;
}
@@ -1211,7 +1494,19 @@
return false;
}
- UNIMPLEMENTED();
+ if (!ValidateObjectPtrName(context, ptr))
+ {
+ return false;
+ }
+
+ size_t labelLength = (length < 0) ? strlen(label) : length;
+ if (labelLength > context->getExtensions().maxLabelLength)
+ {
+ context->recordError(
+ Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
+ return false;
+ }
+
return true;
}
@@ -1227,8 +1522,19 @@
return false;
}
- UNIMPLEMENTED();
- return true;
+ if (bufSize < 0)
+ {
+ context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+ return false;
+ }
+
+ if (!ValidateObjectPtrName(context, ptr))
+ {
+ return false;
+ }
+
+ // Can no-op if bufSize is zero.
+ return bufSize > 0;
}
bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
@@ -1239,7 +1545,18 @@
return false;
}
- UNIMPLEMENTED();
+ // TODO: represent this in Context::getQueryParameterInfo.
+ switch (pname)
+ {
+ case GL_DEBUG_CALLBACK_FUNCTION:
+ case GL_DEBUG_CALLBACK_USER_PARAM:
+ break;
+
+ default:
+ context->recordError(Error(GL_INVALID_ENUM, "Invalid pname."));
+ return false;
+ }
+
return true;
}
}
diff --git a/src/libANGLE/validationES_unittest.cpp b/src/libANGLE/validationES_unittest.cpp
index 9071ae9..e81b846 100644
--- a/src/libANGLE/validationES_unittest.cpp
+++ b/src/libANGLE/validationES_unittest.cpp
@@ -96,7 +96,7 @@
caps.maxElementIndex = 100;
caps.maxDrawBuffers = 1;
caps.maxColorAttachments = 1;
- state.initialize(caps, 3);
+ state.initialize(caps, extensions, 3, false);
MockTextureImpl *textureImpl = new MockTextureImpl();
EXPECT_CALL(*textureImpl, setStorage(_, _, _, _)).WillOnce(Return(Error(GL_NO_ERROR)));
diff --git a/src/libGLESv2.gypi b/src/libGLESv2.gypi
index 6a9975a..aebcab5 100644
--- a/src/libGLESv2.gypi
+++ b/src/libGLESv2.gypi
@@ -68,6 +68,8 @@
'libANGLE/Context.h',
'libANGLE/Data.cpp',
'libANGLE/Data.h',
+ 'libANGLE/Debug.cpp',
+ 'libANGLE/Debug.h',
'libANGLE/Device.cpp',
'libANGLE/Device.h',
'libANGLE/Display.cpp',
diff --git a/src/libGLESv2/entry_points_gles_2_0_ext.cpp b/src/libGLESv2/entry_points_gles_2_0_ext.cpp
index 20a7683..50f3dec 100644
--- a/src/libGLESv2/entry_points_gles_2_0_ext.cpp
+++ b/src/libGLESv2/entry_points_gles_2_0_ext.cpp
@@ -1298,7 +1298,9 @@
return;
}
- UNIMPLEMENTED();
+ std::vector<GLuint> idVector(ids, ids + count);
+ context->getState().getDebug().setMessageControl(
+ source, type, severity, std::move(idVector), (enabled != GL_FALSE));
}
}
@@ -1322,7 +1324,8 @@
return;
}
- UNIMPLEMENTED();
+ std::string msg(buf, (length > 0) ? static_cast<size_t>(length) : strlen(buf));
+ context->getState().getDebug().insertMessage(source, type, id, severity, std::move(msg));
}
}
@@ -1339,7 +1342,7 @@
return;
}
- UNIMPLEMENTED();
+ context->getState().getDebug().setCallback(callback, userParam);
}
}
@@ -1367,7 +1370,8 @@
return 0;
}
- UNIMPLEMENTED();
+ return static_cast<GLuint>(context->getState().getDebug().getMessages(
+ count, bufSize, sources, types, ids, severities, lengths, messageLog));
}
return 0;
@@ -1388,7 +1392,8 @@
return;
}
- UNIMPLEMENTED();
+ std::string msg(message, (length > 0) ? static_cast<size_t>(length) : strlen(message));
+ context->getState().getDebug().pushGroup(source, id, std::move(msg));
}
}
@@ -1404,7 +1409,7 @@
return;
}
- UNIMPLEMENTED();
+ context->getState().getDebug().popGroup();
}
}
@@ -1423,7 +1428,11 @@
return;
}
- UNIMPLEMENTED();
+ LabeledObject *object = context->getLabeledObject(identifier, name);
+ ASSERT(object != nullptr);
+
+ std::string lbl(label, (length > 0) ? static_cast<size_t>(length) : strlen(label));
+ object->setLabel(lbl);
}
}
@@ -1443,7 +1452,14 @@
return;
}
- UNIMPLEMENTED();
+ LabeledObject *object = context->getLabeledObject(identifier, name);
+ ASSERT(object != nullptr);
+
+ const std::string &objectLabel = object->getLabel();
+ size_t writeLength = std::min(static_cast<size_t>(bufSize) - 1, objectLabel.length());
+ std::copy(objectLabel.begin(), objectLabel.begin() + writeLength, label);
+ label[writeLength] = '\0';
+ *length = static_cast<GLsizei>(writeLength);
}
}
@@ -1460,7 +1476,11 @@
return;
}
- UNIMPLEMENTED();
+ LabeledObject *object = context->getLabeledObjectFromPtr(ptr);
+ ASSERT(object != nullptr);
+
+ std::string lbl(label, (length > 0) ? static_cast<size_t>(length) : strlen(label));
+ object->setLabel(lbl);
}
}
@@ -1482,7 +1502,14 @@
return;
}
- UNIMPLEMENTED();
+ LabeledObject *object = context->getLabeledObjectFromPtr(ptr);
+ ASSERT(object != nullptr);
+
+ const std::string &objectLabel = object->getLabel();
+ size_t writeLength = std::min(static_cast<size_t>(bufSize) - 1, objectLabel.length());
+ std::copy(objectLabel.begin(), objectLabel.begin() + writeLength, label);
+ label[writeLength] = '\0';
+ *length = static_cast<GLsizei>(writeLength);
}
}
@@ -1498,7 +1525,7 @@
return;
}
- UNIMPLEMENTED();
+ context->getPointerv(pname, params);
}
}
}
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index e67cda3..b245be9 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -23,6 +23,7 @@
'<(angle_path)/src/tests/gl_tests/CopyTexImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/DebugMarkerTest.cpp',
+ '<(angle_path)/src/tests/gl_tests/DebugTest.cpp',
'<(angle_path)/src/tests/gl_tests/DepthStencilFormatsTest.cpp',
'<(angle_path)/src/tests/gl_tests/DiscardFramebufferEXTTest.cpp',
'<(angle_path)/src/tests/gl_tests/DrawBuffersTest.cpp',
diff --git a/src/tests/gl_tests/DebugTest.cpp b/src/tests/gl_tests/DebugTest.cpp
new file mode 100644
index 0000000..9a0965f
--- /dev/null
+++ b/src/tests/gl_tests/DebugTest.cpp
@@ -0,0 +1,453 @@
+//
+// 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<Message> *messages =
+ static_cast<std::vector<Message> *>(const_cast<void *>(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<Message> 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<GLint>(message.length()), messageLength);
+
+ GLenum sourceBuf = 0;
+ GLenum typeBuf = 0;
+ GLenum idBuf = 0;
+ GLenum severityBuf = 0;
+ GLsizei lengthBuf = 0;
+ std::vector<char> messageBuf(messageLength + 1);
+ GLuint ret =
+ glGetDebugMessageLogKHR(1, static_cast<GLsizei>(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<GLuint>(i), severity, -1,
+ message.c_str());
+ }
+
+ GLint numMessages = 0;
+ glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages);
+ ASSERT_EQ(static_cast<GLint>(messageCount), numMessages);
+
+ for (size_t i = 0; i < messageCount; i++)
+ {
+ glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages);
+ EXPECT_EQ(static_cast<GLint>(messageCount - i), numMessages);
+
+ std::string expectedMessage(i + 1, messageRepeatChar);
+
+ GLint messageLength = 0;
+ glGetIntegerv(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH, &messageLength);
+ EXPECT_EQ(static_cast<GLint>(expectedMessage.length()), messageLength);
+
+ GLenum sourceBuf = 0;
+ GLenum typeBuf = 0;
+ GLenum idBuf = 0;
+ GLenum severityBuf = 0;
+ GLsizei lengthBuf = 0;
+ std::vector<char> messageBuf(messageLength + 1);
+ GLuint ret =
+ glGetDebugMessageLogKHR(1, static_cast<GLsizei>(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<Message> 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<Message> messages;
+
+ glDebugMessageCallbackKHR(Callback, &messages);
+
+ void *callback = nullptr;
+ glGetPointervKHR(GL_DEBUG_CALLBACK_FUNCTION, &callback);
+ EXPECT_EQ(reinterpret_cast<void *>(Callback), callback);
+
+ void *userData = nullptr;
+ glGetPointervKHR(GL_DEBUG_CALLBACK_USER_PARAM, &userData);
+ EXPECT_EQ(static_cast<void *>(&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<Message> 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<Message> 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<GLuint> ids0 = {1234, 2345, 3456, 4567};
+ glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE,
+ static_cast<GLuint>(ids0.size()), ids0.data(), GL_FALSE);
+ glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, GL_DONT_CARE,
+ static_cast<GLuint>(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<GLuint> ids1 = {5678, 6789};
+ glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, GL_DONT_CARE,
+ static_cast<GLuint>(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<char> labelBuf(label.length() + 1);
+ GLsizei labelLengthBuf = 0;
+ glGetObjectLabelKHR(GL_RENDERBUFFER, renderbuffer, static_cast<GLsizei>(labelBuf.size()),
+ &labelLengthBuf, labelBuf.data());
+
+ EXPECT_EQ(static_cast<GLsizei>(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<GLsizei>(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 || getClientVersion() < 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<char> labelBuf(label.length() + 1);
+ GLsizei labelLengthBuf = 0;
+ glGetObjectPtrLabelKHR(sync, static_cast<GLsizei>(labelBuf.size()), &labelLengthBuf,
+ labelBuf.data());
+
+ EXPECT_EQ(static_cast<GLsizei>(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<GLsizei>(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(),
+ ES2_OPENGLES(),
+ ES3_OPENGLES());
+
+} // namespace angle
diff --git a/src/tests/test_utils/ANGLETest.cpp b/src/tests/test_utils/ANGLETest.cpp
index ffc1ec1..7ead125 100644
--- a/src/tests/test_utils/ANGLETest.cpp
+++ b/src/tests/test_utils/ANGLETest.cpp
@@ -223,6 +223,11 @@
mEGLWindow->setMultisample(enabled);
}
+void ANGLETest::setDebugEnabled(bool enabled)
+{
+ mEGLWindow->setDebugEnabled(enabled);
+}
+
int ANGLETest::getClientVersion() const
{
return mEGLWindow->getClientMajorVersion();
diff --git a/src/tests/test_utils/ANGLETest.h b/src/tests/test_utils/ANGLETest.h
index 7ebf248..64f5e9d 100644
--- a/src/tests/test_utils/ANGLETest.h
+++ b/src/tests/test_utils/ANGLETest.h
@@ -96,6 +96,7 @@
void setConfigDepthBits(int bits);
void setConfigStencilBits(int bits);
void setMultisampleEnabled(bool enabled);
+ void setDebugEnabled(bool enabled);
int getClientVersion() const;