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