1 /**************************************************************************
2 *
3 * Copyright 2011 Jose Fonseca
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26
27 #include <string.h>
28
29 #include <algorithm>
30 #include <iostream>
31
32 #include "image.hpp"
33 #include "state_writer.hpp"
34 #include "glproc.hpp"
35 #include "glws.hpp"
36 #include "glsize.hpp"
37 #include "glstate.hpp"
38 #include "glstate_internal.hpp"
39
40
41 namespace glstate {
42
43
PixelPackState(const Context & context)44 PixelPackState::PixelPackState(const Context &context)
45 {
46 desktop = !context.ES;
47 texture_3d = context.texture_3d;
48 pixel_buffer_object = context.pixel_buffer_object;
49 color_buffer_float = context.ARB_color_buffer_float;
50
51 // Start with default state
52 pack_alignment = 4;
53 pack_image_height = 0;
54 pack_lsb_first = GL_FALSE;
55 pack_row_length = 0;
56 pack_skip_images = 0;
57 pack_skip_pixels = 0;
58 pack_skip_rows = 0;
59 pack_swap_bytes = GL_FALSE;
60 pixel_pack_buffer_binding = 0;
61 clamp_read_color = GL_FIXED_ONLY;
62
63 // Get current state
64 glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment);
65 if (desktop) {
66 glGetIntegerv(GL_PACK_LSB_FIRST, &pack_lsb_first);
67 glGetIntegerv(GL_PACK_ROW_LENGTH, &pack_row_length);
68 glGetIntegerv(GL_PACK_SKIP_PIXELS, &pack_skip_pixels);
69 glGetIntegerv(GL_PACK_SKIP_ROWS, &pack_skip_rows);
70 glGetIntegerv(GL_PACK_SWAP_BYTES, &pack_swap_bytes);
71 if (texture_3d) {
72 glGetIntegerv(GL_PACK_IMAGE_HEIGHT, &pack_image_height);
73 glGetIntegerv(GL_PACK_SKIP_IMAGES, &pack_skip_images);
74 }
75 }
76 if (pixel_buffer_object) {
77 glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &pixel_pack_buffer_binding);
78 }
79 if (color_buffer_float) {
80 glGetIntegerv(GL_CLAMP_READ_COLOR, &clamp_read_color);
81 }
82
83 // Reset state for compact images
84 glPixelStorei(GL_PACK_ALIGNMENT, 1);
85 if (desktop) {
86 glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE);
87 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
88 glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
89 glPixelStorei(GL_PACK_SKIP_ROWS, 0);
90 glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE);
91 if (texture_3d) {
92 glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
93 glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
94 }
95 }
96 if (pixel_buffer_object) {
97 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
98 }
99 if (color_buffer_float) {
100 glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
101 }
102 }
103
~PixelPackState()104 PixelPackState::~PixelPackState() {
105 glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);
106 if (desktop) {
107 glPixelStorei(GL_PACK_LSB_FIRST, pack_lsb_first);
108 glPixelStorei(GL_PACK_ROW_LENGTH, pack_row_length);
109 glPixelStorei(GL_PACK_SKIP_PIXELS, pack_skip_pixels);
110 glPixelStorei(GL_PACK_SKIP_ROWS, pack_skip_rows);
111 glPixelStorei(GL_PACK_SWAP_BYTES, pack_swap_bytes);
112 if (texture_3d) {
113 glPixelStorei(GL_PACK_IMAGE_HEIGHT, pack_image_height);
114 glPixelStorei(GL_PACK_SKIP_IMAGES, pack_skip_images);
115 }
116 }
117 if (pixel_buffer_object) {
118 glBindBuffer(GL_PIXEL_PACK_BUFFER, pixel_pack_buffer_binding);
119 }
120 if (color_buffer_float) {
121 glClampColor(GL_CLAMP_READ_COLOR, clamp_read_color);
122 }
123 }
124
TempId(GLenum _target)125 TempId::TempId(GLenum _target) :
126 target(_target),
127 id(0)
128 {
129 switch (target){
130 case GL_ARRAY_BUFFER:
131 glGenBuffers(1, &id);
132 break;
133 case GL_FRAMEBUFFER:
134 glGenFramebuffers(1, &id);
135 break;
136 case GL_PROGRAM:
137 id = glCreateProgram();
138 break;
139 case GL_RENDERBUFFER:
140 glGenRenderbuffers(1, &id);
141 break;
142 case GL_TEXTURE:
143 glGenTextures(1, &id);
144 break;
145 case GL_VERTEX_ARRAY:
146 glGenVertexArrays(1, &id);
147 break;
148 default:
149 assert(false);
150 id = 0;
151 return;
152 }
153 }
154
~TempId()155 TempId::~TempId()
156 {
157 switch (target){
158 case GL_ARRAY_BUFFER:
159 glDeleteBuffers(1, &id);
160 break;
161 case GL_FRAMEBUFFER:
162 glDeleteFramebuffers(1, &id);
163 break;
164 case GL_PROGRAM:
165 glDeleteProgram(id);
166 break;
167 case GL_RENDERBUFFER:
168 glGenRenderbuffers(1, &id);
169 break;
170 case GL_TEXTURE:
171 glDeleteTextures(1, &id);
172 break;
173 case GL_VERTEX_ARRAY:
174 glGenVertexArrays(1, &id);
175 break;
176 default:
177 assert(false);
178 id = 0;
179 return;
180 }
181 }
182
TextureBinding(GLenum _target,GLuint _id)183 TextureBinding::TextureBinding(GLenum _target, GLuint _id) :
184 target(_target),
185 id(_id),
186 prev_id(0)
187 {
188 GLenum binding = getTextureBinding(target);
189 glGetIntegerv(binding, (GLint *) &prev_id);
190 if (prev_id != id) {
191 glBindTexture(target, id);
192 }
193 }
194
~TextureBinding()195 TextureBinding::~TextureBinding() {
196 if (prev_id != id) {
197 glBindTexture(target, prev_id);
198 }
199 }
200
201 static GLenum
getBufferBinding(GLenum target)202 getBufferBinding(GLenum target) {
203 switch (target) {
204 case GL_ARRAY_BUFFER:
205 return GL_ARRAY_BUFFER_BINDING;
206 case GL_ATOMIC_COUNTER_BUFFER:
207 return GL_ATOMIC_COUNTER_BUFFER_BINDING;
208 case GL_COPY_READ_BUFFER:
209 return GL_COPY_READ_BUFFER_BINDING;
210 case GL_COPY_WRITE_BUFFER:
211 return GL_COPY_WRITE_BUFFER_BINDING;
212 case GL_DRAW_BUFFER:
213 return GL_DRAW_BUFFER;
214 case GL_DRAW_INDIRECT_BUFFER:
215 return GL_DRAW_INDIRECT_BUFFER_BINDING;
216 case GL_FRAMEBUFFER:
217 return GL_FRAMEBUFFER_BINDING;
218 case GL_DISPATCH_INDIRECT_BUFFER:
219 return GL_DISPATCH_INDIRECT_BUFFER_BINDING;
220 case GL_ELEMENT_ARRAY_BUFFER:
221 return GL_ELEMENT_ARRAY_BUFFER_BINDING;
222 case GL_PIXEL_PACK_BUFFER:
223 return GL_PIXEL_PACK_BUFFER_BINDING;
224 case GL_PIXEL_UNPACK_BUFFER:
225 return GL_PIXEL_UNPACK_BUFFER_BINDING;
226 case GL_QUERY_BUFFER:
227 return GL_QUERY_BUFFER_BINDING;
228 case GL_READ_BUFFER:
229 return GL_READ_BUFFER;
230 case GL_RENDERBUFFER:
231 return GL_RENDERBUFFER_BINDING;
232 case GL_SHADER_STORAGE_BUFFER:
233 return GL_SHADER_STORAGE_BUFFER_BINDING;
234 case GL_TEXTURE_BUFFER:
235 return GL_TEXTURE_BUFFER;
236 case GL_TRANSFORM_FEEDBACK_BUFFER:
237 return GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
238 case GL_UNIFORM_BUFFER:
239 return GL_UNIFORM_BUFFER_BINDING;
240 default:
241 assert(false);
242 return GL_NONE;
243 }
244 }
245
246
BufferBinding(GLenum _target,GLuint _buffer)247 BufferBinding::BufferBinding(GLenum _target, GLuint _buffer) :
248 target(_target),
249 buffer(_buffer),
250 prevBuffer(0)
251 {
252 GLenum binding = getBufferBinding(target);
253 glGetIntegerv(binding, (GLint *) &prevBuffer);
254
255 if (prevBuffer != buffer) {
256 switch(target)
257 {
258 case GL_DRAW_BUFFER:
259 glDrawBuffer(buffer);
260 break;
261 case GL_READ_BUFFER:
262 glReadBuffer(buffer);
263 break;
264 case GL_FRAMEBUFFER:
265 glBindFramebuffer(target, buffer);
266 break;
267 case GL_RENDERBUFFER:
268 glBindRenderbuffer(target, buffer);
269 break;
270 default:
271 glBindBuffer(target, buffer);
272 break;
273 }
274 }
275 }
276
~BufferBinding()277 BufferBinding::~BufferBinding() {
278 if (prevBuffer != buffer) {
279 switch(target)
280 {
281 case GL_DRAW_BUFFER:
282 glDrawBuffer(prevBuffer);
283 break;
284 case GL_READ_BUFFER:
285 glReadBuffer(prevBuffer);
286 break;
287 case GL_FRAMEBUFFER:
288 glBindFramebuffer(target, prevBuffer);
289 break;
290 case GL_RENDERBUFFER:
291 glBindRenderbuffer(target, prevBuffer);
292 break;
293 default:
294 glBindBuffer(target, prevBuffer);
295 break;
296 }
297 }
298 }
299
300
BufferMapping()301 BufferMapping::BufferMapping() :
302 target(GL_NONE),
303 buffer(0),
304 map_pointer(NULL),
305 unmap(false)
306 {
307 }
308
309 GLvoid *
map(GLenum _target,GLuint _buffer)310 BufferMapping::map(GLenum _target, GLuint _buffer)
311 {
312 if (target == _target && buffer == _buffer) {
313 return map_pointer;
314 }
315
316 target = _target;
317 buffer = _buffer;
318 map_pointer = NULL;
319 unmap = false;
320
321 BufferBinding bb(target, buffer);
322
323 // Recursive mappings of the same buffer are not allowed. And with the
324 // pursuit of persistent mappings for performance this will become more
325 // and more common.
326 GLint mapped = GL_FALSE;
327 glGetBufferParameteriv(target, GL_BUFFER_MAPPED, &mapped);
328 if (mapped) {
329 glGetBufferPointerv(target, GL_BUFFER_MAP_POINTER, &map_pointer);
330 assert(map_pointer != NULL);
331
332 GLint map_offset = 0;
333 glGetBufferParameteriv(target, GL_BUFFER_MAP_OFFSET, &map_offset);
334 if (map_offset != 0) {
335 std::cerr << "warning: " << enumToString(target) << " buffer " << buffer << " is already mapped with offset " << map_offset << "\n";
336 // FIXME: This most likely won't work. We should remap the
337 // buffer with the full range, then re-map when done. This
338 // should never happen in practice with persistent mappings
339 // though.
340 map_pointer = (GLubyte *)map_pointer - map_offset;
341 }
342 } else {
343 map_pointer = glMapBuffer(target, GL_READ_ONLY);
344 if (map_pointer) {
345 unmap = true;
346 }
347 }
348
349 return map_pointer;
350 }
351
~BufferMapping()352 BufferMapping::~BufferMapping() {
353 if (unmap) {
354 BufferBinding bb(target, buffer);
355
356 GLenum ret = glUnmapBuffer(target);
357 assert(ret == GL_TRUE);
358 (void)ret;
359 }
360 }
361
362
363 void
dumpBoolean(StateWriter & writer,GLboolean value)364 dumpBoolean(StateWriter &writer, GLboolean value)
365 {
366 switch (value) {
367 case GL_FALSE:
368 writer.writeString("GL_FALSE");
369 break;
370 case GL_TRUE:
371 writer.writeString("GL_TRUE");
372 break;
373 default:
374 writer.writeInt(static_cast<GLint>(value));
375 break;
376 }
377 }
378
379
380 void
dumpEnum(StateWriter & writer,GLenum pname)381 dumpEnum(StateWriter &writer, GLenum pname)
382 {
383 const char *s = enumToString(pname);
384 if (s) {
385 writer.writeString(s);
386 } else {
387 writer.writeInt(pname);
388 }
389 }
390
391
392 /**
393 * Get a GL_KHR_debug/GL_EXT_debug_label object label.
394 *
395 * The returned string should be destroyed with free() when no longer
396 * necessary.
397 */
398 char *
getObjectLabel(Context & context,GLenum identifier,GLuint name)399 getObjectLabel(Context &context, GLenum identifier, GLuint name)
400 {
401 if (!name) {
402 return NULL;
403 }
404
405 GLsizei length = 0;
406 if (context.KHR_debug) {
407 /*
408 * XXX: According to
409 * http://www.khronos.org/registry/gles/extensions/KHR/debug.txt
410 * description of glGetObjectLabel:
411 *
412 * "If <label> is NULL and <length> is non-NULL then no string will
413 * be returned and the length of the label will be returned in
414 * <length>."
415 *
416 * However NVIDIA 319.60 drivers return a zero length in such
417 * circumstances. 310.14 drivers worked fine though. So, just rely on
418 * GL_MAX_LABEL_LENGTH instead, which might waste a bit of memory, but
419 * should work reliably everywhere.
420 */
421 if (0) {
422 glGetObjectLabel(identifier, name, 0, &length, NULL);
423 } else {
424 glGetIntegerv(GL_MAX_LABEL_LENGTH, &length);
425 }
426 }
427 if (context.EXT_debug_label) {
428 glGetObjectLabelEXT(identifier, name, 0, &length, NULL);
429 }
430 if (!length) {
431 return NULL;
432 }
433
434 char *label = (char *)calloc(length + 1, 1);
435 if (!label) {
436 return NULL;
437 }
438
439 if (context.KHR_debug) {
440 glGetObjectLabel(identifier, name, length + 1, NULL, label);
441 }
442 if (context.EXT_debug_label) {
443 glGetObjectLabelEXT(identifier, name, length + 1, NULL, label);
444 }
445
446 if (label[0] == '\0') {
447 free(label);
448 return NULL;
449 }
450
451 return label;
452 }
453
454
455 /**
456 * Dump a GL_KHR_debug/GL_EXT_debug_label object label.
457 */
458 void
dumpObjectLabel(StateWriter & writer,Context & context,GLenum identifier,GLuint name,const char * member)459 dumpObjectLabel(StateWriter &writer, Context &context, GLenum identifier, GLuint name, const char *member) {
460 char *label = getObjectLabel(context, identifier, name);
461 if (!label) {
462 return;
463 }
464
465 writer.writeStringMember(member, label);
466 free(label);
467 }
468
469
470 static const GLenum bindings[] = {
471 GL_DRAW_BUFFER,
472 GL_READ_BUFFER,
473 GL_PIXEL_PACK_BUFFER_BINDING,
474 GL_PIXEL_UNPACK_BUFFER_BINDING,
475 GL_TEXTURE_BINDING_1D,
476 GL_TEXTURE_BINDING_1D_ARRAY,
477 GL_TEXTURE_BINDING_2D,
478 GL_TEXTURE_BINDING_2D_ARRAY,
479 GL_TEXTURE_BINDING_2D_MULTISAMPLE,
480 GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
481 GL_TEXTURE_BINDING_3D,
482 GL_TEXTURE_BINDING_RECTANGLE,
483 GL_TEXTURE_BINDING_CUBE_MAP,
484 GL_TEXTURE_BINDING_CUBE_MAP_ARRAY,
485 GL_DRAW_FRAMEBUFFER_BINDING,
486 GL_READ_FRAMEBUFFER_BINDING,
487 GL_RENDERBUFFER_BINDING,
488 GL_DRAW_BUFFER0,
489 GL_DRAW_BUFFER1,
490 GL_DRAW_BUFFER2,
491 GL_DRAW_BUFFER3,
492 GL_DRAW_BUFFER4,
493 GL_DRAW_BUFFER5,
494 GL_DRAW_BUFFER6,
495 GL_DRAW_BUFFER7,
496 GL_TRANSFORM_FEEDBACK_BUFFER_BINDING,
497 GL_UNIFORM_BUFFER_BINDING,
498 };
499
500
501 #define NUM_BINDINGS sizeof(bindings)/sizeof(bindings[0])
502
503
504 static void APIENTRY
debugMessageCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)505 debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
506 GLsizei length, const GLchar* message, const void *userParam)
507 {
508 // Ignore NVIDIA buffer usage warnings: when dumping we inevitably use
509 // buffers differently than declared
510 if (source == GL_DEBUG_SOURCE_API &&
511 type == GL_DEBUG_TYPE_OTHER &&
512 id == 131188 &&
513 severity == GL_DEBUG_SEVERITY_LOW) {
514 return;
515 };
516
517 const char *severityStr = "";
518 switch (severity) {
519 case GL_DEBUG_SEVERITY_HIGH:
520 severityStr = " high";
521 break;
522 case GL_DEBUG_SEVERITY_MEDIUM:
523 break;
524 case GL_DEBUG_SEVERITY_LOW:
525 severityStr = " low";
526 break;
527 case GL_DEBUG_SEVERITY_NOTIFICATION:
528 /* ignore */
529 return;
530 default:
531 assert(0);
532 }
533
534 const char *sourceStr = "";
535 switch (source) {
536 case GL_DEBUG_SOURCE_API:
537 break;
538 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
539 sourceStr = " window system";
540 break;
541 case GL_DEBUG_SOURCE_SHADER_COMPILER:
542 sourceStr = " shader compiler";
543 break;
544 case GL_DEBUG_SOURCE_THIRD_PARTY:
545 sourceStr = " third party";
546 break;
547 case GL_DEBUG_SOURCE_APPLICATION:
548 sourceStr = " application";
549 break;
550 case GL_DEBUG_SOURCE_OTHER:
551 break;
552 default:
553 assert(0);
554 }
555
556 const char *typeStr = "";
557 switch (type) {
558 case GL_DEBUG_TYPE_ERROR:
559 typeStr = " error";
560 break;
561 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
562 typeStr = " deprecated behaviour";
563 break;
564 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
565 typeStr = " undefined behaviour";
566 break;
567 case GL_DEBUG_TYPE_PORTABILITY:
568 typeStr = " portability issue";
569 break;
570 case GL_DEBUG_TYPE_PERFORMANCE:
571 return;
572 default:
573 assert(0);
574 /* fall-through */
575 case GL_DEBUG_TYPE_OTHER:
576 typeStr = " issue";
577 break;
578 case GL_DEBUG_TYPE_MARKER:
579 case GL_DEBUG_TYPE_PUSH_GROUP:
580 case GL_DEBUG_TYPE_POP_GROUP:
581 return;
582 }
583
584 std::cerr << "warning: message:" << severityStr << sourceStr << typeStr;
585
586 if (id) {
587 std::cerr << " " << id;
588 }
589
590 std::cerr << ": ";
591
592 std::cerr << message;
593
594 // Write new line if not included in the message already.
595 size_t messageLen = strlen(message);
596 if (!messageLen ||
597 (message[messageLen - 1] != '\n' &&
598 message[messageLen - 1] != '\r')) {
599 std::cerr << std::endl;
600 }
601
602 /* To help debug bugs in glstate. */
603 if (0) {
604 os::breakpoint();
605 }
606 }
607
608
dumpCurrentContext(StateWriter & writer)609 void dumpCurrentContext(StateWriter &writer)
610 {
611
612 #ifndef NDEBUG
613 GLint old_bindings[NUM_BINDINGS];
614 for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
615 old_bindings[i] = 0;
616 glGetIntegerv(bindings[i], &old_bindings[i]);
617 }
618 #endif
619
620 Context context;
621
622 /* Temporarily disable messages, as dumpParameters blindlessly tries to
623 * get state, regardless the respective extension is supported or not.
624 */
625 GLDEBUGPROC prevDebugCallbackFunction = 0;
626 void *prevDebugCallbackUserParam = 0;
627 if (context.KHR_debug) {
628 glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, (GLvoid **) &prevDebugCallbackFunction);
629 glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &prevDebugCallbackUserParam);
630 glDebugMessageCallback(NULL, NULL);
631 }
632
633 dumpParameters(writer, context);
634
635 // Use our own debug-message callback.
636 if (context.KHR_debug) {
637 glDebugMessageCallback(debugMessageCallback, NULL);
638 }
639
640 dumpShadersUniforms(writer, context);
641 dumpTextures(writer, context);
642 dumpFramebuffer(writer, context);
643
644 #ifndef NDEBUG
645 for (unsigned i = 0; i < NUM_BINDINGS; ++i) {
646 GLint new_binding = 0;
647 glGetIntegerv(bindings[i], &new_binding);
648 if (new_binding != old_bindings[i]) {
649 std::cerr << "warning: " << enumToString(bindings[i]) << " was clobbered\n";
650 }
651 }
652 #endif
653
654 // Restore debug message callback
655 if (context.KHR_debug) {
656 glDebugMessageCallback(prevDebugCallbackFunction, prevDebugCallbackUserParam);
657 }
658 }
659
660
661 } /* namespace glstate */
662