1 //
2 // Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // Debug.cpp: Defines debug state used for GL_KHR_debug
8 
9 #include "libANGLE/Debug.h"
10 
11 #include "common/debug.h"
12 
13 #include <algorithm>
14 #include <tuple>
15 
16 namespace gl
17 {
18 
Control()19 Debug::Control::Control()
20 {
21 }
22 
~Control()23 Debug::Control::~Control()
24 {
25 }
26 
27 Debug::Control::Control(const Control &other) = default;
28 
Group()29 Debug::Group::Group()
30 {
31 }
32 
~Group()33 Debug::Group::~Group()
34 {
35 }
36 
37 Debug::Group::Group(const Group &other) = default;
38 
Debug()39 Debug::Debug()
40     : mOutputEnabled(false),
41       mCallbackFunction(nullptr),
42       mCallbackUserParam(nullptr),
43       mMessages(),
44       mMaxLoggedMessages(0),
45       mOutputSynchronous(false),
46       mGroups()
47 {
48     pushDefaultGroup();
49 }
50 
~Debug()51 Debug::~Debug()
52 {
53 }
54 
setMaxLoggedMessages(GLuint maxLoggedMessages)55 void Debug::setMaxLoggedMessages(GLuint maxLoggedMessages)
56 {
57     mMaxLoggedMessages = maxLoggedMessages;
58 }
59 
setOutputEnabled(bool enabled)60 void Debug::setOutputEnabled(bool enabled)
61 {
62     mOutputEnabled = enabled;
63 }
64 
isOutputEnabled() const65 bool Debug::isOutputEnabled() const
66 {
67     return mOutputEnabled;
68 }
69 
setOutputSynchronous(bool synchronous)70 void Debug::setOutputSynchronous(bool synchronous)
71 {
72     mOutputSynchronous = synchronous;
73 }
74 
isOutputSynchronous() const75 bool Debug::isOutputSynchronous() const
76 {
77     return mOutputSynchronous;
78 }
79 
setCallback(GLDEBUGPROCKHR callback,const void * userParam)80 void Debug::setCallback(GLDEBUGPROCKHR callback, const void *userParam)
81 {
82     mCallbackFunction  = callback;
83     mCallbackUserParam = userParam;
84 }
85 
getCallback() const86 GLDEBUGPROCKHR Debug::getCallback() const
87 {
88     return mCallbackFunction;
89 }
90 
getUserParam() const91 const void *Debug::getUserParam() const
92 {
93     return mCallbackUserParam;
94 }
95 
insertMessage(GLenum source,GLenum type,GLuint id,GLenum severity,const std::string & message)96 void Debug::insertMessage(GLenum source,
97                           GLenum type,
98                           GLuint id,
99                           GLenum severity,
100                           const std::string &message)
101 {
102     std::string messageCopy(message);
103     insertMessage(source, type, id, severity, std::move(messageCopy));
104 }
105 
insertMessage(GLenum source,GLenum type,GLuint id,GLenum severity,std::string && message)106 void Debug::insertMessage(GLenum source,
107                           GLenum type,
108                           GLuint id,
109                           GLenum severity,
110                           std::string &&message)
111 {
112     if (!isMessageEnabled(source, type, id, severity))
113     {
114         return;
115     }
116 
117     if (mCallbackFunction != nullptr)
118     {
119         // TODO(geofflang) Check the synchronous flag and potentially flush messages from another
120         // thread.
121         mCallbackFunction(source, type, id, severity, static_cast<GLsizei>(message.length()),
122                           message.c_str(), mCallbackUserParam);
123     }
124     else
125     {
126         if (mMessages.size() >= mMaxLoggedMessages)
127         {
128             // Drop messages over the limit
129             return;
130         }
131 
132         Message m;
133         m.source   = source;
134         m.type     = type;
135         m.id       = id;
136         m.severity = severity;
137         m.message  = std::move(message);
138 
139         mMessages.push_back(std::move(m));
140     }
141 }
142 
getMessages(GLuint count,GLsizei bufSize,GLenum * sources,GLenum * types,GLuint * ids,GLenum * severities,GLsizei * lengths,GLchar * messageLog)143 size_t Debug::getMessages(GLuint count,
144                           GLsizei bufSize,
145                           GLenum *sources,
146                           GLenum *types,
147                           GLuint *ids,
148                           GLenum *severities,
149                           GLsizei *lengths,
150                           GLchar *messageLog)
151 {
152     size_t messageCount       = 0;
153     size_t messageStringIndex = 0;
154     while (messageCount <= count && !mMessages.empty())
155     {
156         const Message &m = mMessages.front();
157 
158         if (messageLog != nullptr)
159         {
160             // Check that this message can fit in the message buffer
161             if (messageStringIndex + m.message.length() + 1 > static_cast<size_t>(bufSize))
162             {
163                 break;
164             }
165 
166             std::copy(m.message.begin(), m.message.end(), messageLog + messageStringIndex);
167             messageStringIndex += m.message.length();
168 
169             messageLog[messageStringIndex] = '\0';
170             messageStringIndex += 1;
171         }
172 
173         if (sources != nullptr)
174         {
175             sources[messageCount] = m.source;
176         }
177 
178         if (types != nullptr)
179         {
180             types[messageCount] = m.type;
181         }
182 
183         if (ids != nullptr)
184         {
185             ids[messageCount] = m.id;
186         }
187 
188         if (severities != nullptr)
189         {
190             severities[messageCount] = m.severity;
191         }
192 
193         if (lengths != nullptr)
194         {
195             lengths[messageCount] = static_cast<GLsizei>(m.message.length());
196         }
197 
198         mMessages.pop_front();
199 
200         messageCount++;
201     }
202 
203     return messageCount;
204 }
205 
getNextMessageLength() const206 size_t Debug::getNextMessageLength() const
207 {
208     return mMessages.empty() ? 0 : mMessages.front().message.length();
209 }
210 
getMessageCount() const211 size_t Debug::getMessageCount() const
212 {
213     return mMessages.size();
214 }
215 
setMessageControl(GLenum source,GLenum type,GLenum severity,std::vector<GLuint> && ids,bool enabled)216 void Debug::setMessageControl(GLenum source,
217                               GLenum type,
218                               GLenum severity,
219                               std::vector<GLuint> &&ids,
220                               bool enabled)
221 {
222     Control c;
223     c.source   = source;
224     c.type     = type;
225     c.severity = severity;
226     c.ids      = std::move(ids);
227     c.enabled  = enabled;
228 
229     auto &controls = mGroups.back().controls;
230     controls.push_back(std::move(c));
231 }
232 
pushGroup(GLenum source,GLuint id,std::string && message)233 void Debug::pushGroup(GLenum source, GLuint id, std::string &&message)
234 {
235     insertMessage(source, GL_DEBUG_TYPE_PUSH_GROUP, id, GL_DEBUG_SEVERITY_NOTIFICATION,
236                   std::string(message));
237 
238     Group g;
239     g.source  = source;
240     g.id      = id;
241     g.message = std::move(message);
242     mGroups.push_back(std::move(g));
243 }
244 
popGroup()245 void Debug::popGroup()
246 {
247     // Make sure the default group is not about to be popped
248     ASSERT(mGroups.size() > 1);
249 
250     Group g = mGroups.back();
251     mGroups.pop_back();
252 
253     insertMessage(g.source, GL_DEBUG_TYPE_POP_GROUP, g.id, GL_DEBUG_SEVERITY_NOTIFICATION,
254                   g.message);
255 }
256 
getGroupStackDepth() const257 size_t Debug::getGroupStackDepth() const
258 {
259     return mGroups.size();
260 }
261 
isMessageEnabled(GLenum source,GLenum type,GLuint id,GLenum severity) const262 bool Debug::isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const
263 {
264     if (!mOutputEnabled)
265     {
266         return false;
267     }
268 
269     for (auto groupIter = mGroups.rbegin(); groupIter != mGroups.rend(); groupIter++)
270     {
271         const auto &controls = groupIter->controls;
272         for (auto controlIter = controls.rbegin(); controlIter != controls.rend(); controlIter++)
273         {
274             const auto &control = *controlIter;
275 
276             if (control.source != GL_DONT_CARE && control.source != source)
277             {
278                 continue;
279             }
280 
281             if (control.type != GL_DONT_CARE && control.type != type)
282             {
283                 continue;
284             }
285 
286             if (control.severity != GL_DONT_CARE && control.severity != severity)
287             {
288                 continue;
289             }
290 
291             if (!control.ids.empty() &&
292                 std::find(control.ids.begin(), control.ids.end(), id) == control.ids.end())
293             {
294                 continue;
295             }
296 
297             return control.enabled;
298         }
299     }
300 
301     return true;
302 }
303 
pushDefaultGroup()304 void Debug::pushDefaultGroup()
305 {
306     Group g;
307     g.source  = GL_NONE;
308     g.id      = 0;
309     g.message = "";
310 
311     Control c0;
312     c0.source   = GL_DONT_CARE;
313     c0.type     = GL_DONT_CARE;
314     c0.severity = GL_DONT_CARE;
315     c0.enabled = true;
316     g.controls.push_back(std::move(c0));
317 
318     Control c1;
319     c1.source   = GL_DONT_CARE;
320     c1.type     = GL_DONT_CARE;
321     c1.severity = GL_DEBUG_SEVERITY_LOW;
322     c1.enabled = false;
323     g.controls.push_back(std::move(c1));
324 
325     mGroups.push_back(std::move(g));
326 }
327 }  // namespace gl
328