X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=glretrace_main.cpp;h=d80775f0ed3dd76c591c632e479065580c573939;hb=8e3c2c0d01c7ec5479845fedc053da00fa88e76a;hp=bfab151fde00f9885aab88cf5ea1c03065ef07d3;hpb=05c3d03723baba7e84457fffabebc05a58933d15;p=apitrace diff --git a/glretrace_main.cpp b/glretrace_main.cpp index bfab151..d80775f 100644 --- a/glretrace_main.cpp +++ b/glretrace_main.cpp @@ -26,6 +26,8 @@ #include +#include "os_string.hpp" +#include "os_time.hpp" #include "image.hpp" #include "retrace.hpp" #include "glproc.hpp" @@ -35,11 +37,10 @@ namespace glretrace { -bool double_buffer = false; +bool double_buffer = true; bool insideGlBeginEnd = false; -Trace::Parser parser; -glws::WindowSystem *ws = NULL; -glws::Visual *visual = NULL; +glws::Profile defaultProfile = glws::PROFILE_COMPAT; +glws::Visual *visual[glws::PROFILE_MAX]; glws::Drawable *drawable = NULL; glws::Context *context = NULL; @@ -50,85 +51,89 @@ bool wait = false; bool benchmark = false; const char *compare_prefix = NULL; const char *snapshot_prefix = NULL; +enum frequency snapshot_frequency = FREQUENCY_NEVER; unsigned dump_state = ~0; void -checkGlError(Trace::Call &call) { - if (benchmark || insideGlBeginEnd) { - return; - } - +checkGlError(trace::Call &call) { GLenum error = glGetError(); if (error == GL_NO_ERROR) { return; } - if (retrace::verbosity == 0) { - std::cout << call; - std::cout.flush(); - } + std::ostream & os = retrace::warning(call); - std::cerr << call.no << ": "; - std::cerr << "warning: glGetError("; - std::cerr << call.name(); - std::cerr << ") = "; + os << "glGetError("; + os << call.name(); + os << ") = "; switch (error) { case GL_INVALID_ENUM: - std::cerr << "GL_INVALID_ENUM"; + os << "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: - std::cerr << "GL_INVALID_VALUE"; + os << "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: - std::cerr << "GL_INVALID_OPERATION"; + os << "GL_INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: - std::cerr << "GL_STACK_OVERFLOW"; + os << "GL_STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: - std::cerr << "GL_STACK_UNDERFLOW"; + os << "GL_STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: - std::cerr << "GL_OUT_OF_MEMORY"; + os << "GL_OUT_OF_MEMORY"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: - std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION"; + os << "GL_INVALID_FRAMEBUFFER_OPERATION"; break; case GL_TABLE_TOO_LARGE: - std::cerr << "GL_TABLE_TOO_LARGE"; + os << "GL_TABLE_TOO_LARGE"; break; default: - std::cerr << error; + os << error; break; } - std::cerr << "\n"; + os << "\n"; } +/** + * Grow the current drawble. + * + * We need to infer the drawable size from GL calls because the drawable sizes + * are specified by OS specific calls which we do not trace. + */ +void +updateDrawable(int width, int height) { + if (!drawable) { + return; + } + + if (drawable->visible && + width <= drawable->width && + height <= drawable->height) { + return; + } -static void color_snapshot(Image::Image &image) { - GLint drawbuffer = double_buffer ? GL_BACK : GL_FRONT; - GLint readbuffer = double_buffer ? GL_BACK : GL_FRONT; - glGetIntegerv(GL_DRAW_BUFFER, &drawbuffer); - glGetIntegerv(GL_READ_BUFFER, &readbuffer); - glReadBuffer(drawbuffer); - - glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); - glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); - glPixelStorei(GL_PACK_SKIP_ROWS, 0); - glPixelStorei(GL_PACK_SKIP_PIXELS, 0); - glPixelStorei(GL_PACK_SKIP_IMAGES, 0); - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - glReadPixels(0, 0, image.width, image.height, GL_RGBA, GL_UNSIGNED_BYTE, image.pixels); - - glPopClientAttrib(); - - glReadBuffer(readbuffer); + // Ignore zero area viewports + if (width == 0 || height == 0) { + return; + } + + // Check for bound framebuffer last, as this may have a performance impact. + GLint draw_framebuffer = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &draw_framebuffer); + if (draw_framebuffer != 0) { + return; + } + + drawable->resize(width, height); + drawable->show(); + + glScissor(0, 0, width, height); } @@ -138,12 +143,11 @@ void snapshot(unsigned call_no) { return; } - Image::Image *ref = NULL; + image::Image *ref = NULL; if (compare_prefix) { - char filename[PATH_MAX]; - snprintf(filename, sizeof filename, "%s%010u.png", compare_prefix, call_no); - ref = Image::readPNG(filename); + os::String filename = os::String::format("%s%010u.png", compare_prefix, call_no); + ref = image::readPNG(filename); if (!ref) { return; } @@ -152,58 +156,70 @@ void snapshot(unsigned call_no) { } } - Image::Image src(drawable->width, drawable->height, true); - color_snapshot(src); + image::Image *src = glstate::getDrawBufferImage(); + if (!src) { + return; + } if (snapshot_prefix) { - char filename[PATH_MAX]; - snprintf(filename, sizeof filename, "%s%010u.png", snapshot_prefix, call_no); - if (src.writePNG(filename) && retrace::verbosity >= 0) { - std::cout << "Wrote " << filename << "\n"; + if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { + char comment[21]; + snprintf(comment, sizeof comment, "%u", call_no); + src->writePNM(std::cout, comment); + } else { + os::String filename = os::String::format("%s%010u.png", snapshot_prefix, call_no); + if (src->writePNG(filename) && retrace::verbosity >= 0) { + std::cout << "Wrote " << filename << "\n"; + } } } if (ref) { - std::cout << "Snapshot " << call_no << " average precision of " << src.compare(*ref) << " bits\n"; + std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n"; delete ref; } + + delete src; } -void frame_complete(unsigned call_no) { +void frame_complete(trace::Call &call) { ++frame; - snapshot(call_no); + if (!drawable) { + return; + } + + if (!drawable->visible) { + retrace::warning(call) << "could not infer drawable size (glViewport never called)\n"; + } + + if (snapshot_frequency == FREQUENCY_FRAME || + snapshot_frequency == FREQUENCY_FRAMEBUFFER) { + snapshot(call.no); + } } static void display(void) { - Trace::Call *call; + retrace::Retracer retracer; - while ((call = parser.parse_call())) { - const std::string &name = call->name(); + retracer.addCallbacks(gl_callbacks); + retracer.addCallbacks(glx_callbacks); + retracer.addCallbacks(wgl_callbacks); + retracer.addCallbacks(cgl_callbacks); + retracer.addCallbacks(egl_callbacks); - if (retrace::verbosity >= 1) { - std::cout << *call; - std::cout.flush(); - } + startTime = os::getTime(); + trace::Call *call; - if (name[0] == 'C' && name[1] == 'G' && name[2] == 'L') { - glretrace::retrace_call_cgl(*call); - } - else if (name[0] == 'w' && name[1] == 'g' && name[2] == 'l') { - glretrace::retrace_call_wgl(*call); - } - else if (name[0] == 'g' && name[1] == 'l' && name[2] == 'X') { - glretrace::retrace_call_glx(*call); - } else { - retrace::retrace_call(*call); - } + while ((call = parser.parse_call())) { + retracer.retrace(*call); if (!insideGlBeginEnd && drawable && context && call->no >= dump_state) { - glstate::state_dump(std::cout); + glstate::dumpCurrentContext(std::cout); exit(0); } @@ -213,8 +229,8 @@ static void display(void) { // Reached the end of trace glFlush(); - long long endTime = OS::GetTime(); - float timeInterval = (endTime - startTime) * 1.0E-6; + long long endTime = os::getTime(); + float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency); if (retrace::verbosity >= -1) { std::cout << @@ -224,7 +240,7 @@ static void display(void) { } if (wait) { - while (ws->processEvents()) {} + while (glws::processEvents()) {} } else { exit(0); } @@ -236,11 +252,14 @@ static void usage(void) { "Usage: glretrace [OPTION] TRACE\n" "Replay TRACE.\n" "\n" - " -b benchmark (no glgeterror; no messages)\n" + " -b benchmark mode (no error checking or warning messages)\n" " -c PREFIX compare against snapshots\n" - " -db use a double buffer visual\n" - " -s PREFIX take snapshots\n" - " -v verbose output\n" + " -core use core profile\n" + " -db use a double buffer visual (default)\n" + " -sb use a single buffer visual\n" + " -s PREFIX take snapshots; `-` for PNM stdout output\n" + " -S FREQUENCY snapshot frequency: frame (default), framebuffer, or draw\n" + " -v increase output verbosity\n" " -D CALLNO dump state at specific call no\n" " -w wait on final frame\n"; } @@ -262,18 +281,48 @@ int main(int argc, char **argv) } else if (!strcmp(arg, "-b")) { benchmark = true; retrace::verbosity = -1; + glws::debug = false; } else if (!strcmp(arg, "-c")) { compare_prefix = argv[++i]; + if (snapshot_frequency == FREQUENCY_NEVER) { + snapshot_frequency = FREQUENCY_FRAME; + } } else if (!strcmp(arg, "-D")) { dump_state = atoi(argv[++i]); retrace::verbosity = -2; + } else if (!strcmp(arg, "-core")) { + defaultProfile = glws::PROFILE_CORE; } else if (!strcmp(arg, "-db")) { double_buffer = true; + } else if (!strcmp(arg, "-sb")) { + double_buffer = false; } else if (!strcmp(arg, "--help")) { usage(); return 0; } else if (!strcmp(arg, "-s")) { snapshot_prefix = argv[++i]; + if (snapshot_frequency == FREQUENCY_NEVER) { + snapshot_frequency = FREQUENCY_FRAME; + } + if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) { + retrace::verbosity = -2; + } + } else if (!strcmp(arg, "-S")) { + arg = argv[++i]; + if (!strcmp(arg, "frame")) { + snapshot_frequency = FREQUENCY_FRAME; + } else if (!strcmp(arg, "framebuffer")) { + snapshot_frequency = FREQUENCY_FRAMEBUFFER; + } else if (!strcmp(arg, "draw")) { + snapshot_frequency = FREQUENCY_DRAW; + } else { + std::cerr << "error: unknown frequency " << arg << "\n"; + usage(); + return 1; + } + if (snapshot_prefix == NULL) { + snapshot_prefix = ""; + } } else if (!strcmp(arg, "-v")) { ++retrace::verbosity; } else if (!strcmp(arg, "-w")) { @@ -285,17 +334,29 @@ int main(int argc, char **argv) } } - ws = glws::createNativeWindowSystem(); - visual = ws->createVisual(double_buffer); + glws::init(); + visual[glws::PROFILE_COMPAT] = glws::createVisual(double_buffer, glws::PROFILE_COMPAT); + visual[glws::PROFILE_CORE] = glws::createVisual(double_buffer, glws::PROFILE_CORE); + visual[glws::PROFILE_ES1] = glws::createVisual(double_buffer, glws::PROFILE_ES1); + visual[glws::PROFILE_ES2] = glws::createVisual(double_buffer, glws::PROFILE_ES2); for ( ; i < argc; ++i) { - if (parser.open(argv[i])) { - startTime = OS::GetTime(); - display(); - parser.close(); + if (!parser.open(argv[i])) { + std::cerr << "error: failed to open " << argv[i] << "\n"; + return 1; } + + display(); + + parser.close(); } + for (int n = 0; n < glws::PROFILE_MAX; n++) { + delete visual[n]; + } + + glws::cleanup(); + return 0; }