From 542edff31077b3f3c24f511b12f7f3832f7768c9 Mon Sep 17 00:00:00 2001 From: Jose Fonseca Date: Thu, 6 Aug 2015 12:28:45 +0100 Subject: cli: Rename trace_analyzer module. To line up with the rest of trim-auto source code. --- cli/CMakeLists.txt | 2 +- cli/cli_trim_auto.cpp | 3 +- cli/cli_trim_auto_analyzer.cpp | 767 +++++++++++++++++++++++++++++++++++++++++ cli/cli_trim_auto_analyzer.hpp | 114 ++++++ cli/trace_analyzer.cpp | 767 ----------------------------------------- cli/trace_analyzer.hpp | 114 ------ 6 files changed, 884 insertions(+), 883 deletions(-) create mode 100644 cli/cli_trim_auto_analyzer.cpp create mode 100644 cli/cli_trim_auto_analyzer.hpp delete mode 100644 cli/trace_analyzer.cpp delete mode 100644 cli/trace_analyzer.hpp (limited to 'cli') diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 6e8703cc..4c13c213 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -29,8 +29,8 @@ add_executable (apitrace cli_trace.cpp cli_trim.cpp cli_trim_auto.cpp + cli_trim_auto_analyzer.cpp cli_resources.cpp - trace_analyzer.cpp ) target_link_libraries (apitrace diff --git a/cli/cli_trim_auto.cpp b/cli/cli_trim_auto.cpp index 5dc62515..685de21d 100644 --- a/cli/cli_trim_auto.cpp +++ b/cli/cli_trim_auto.cpp @@ -35,11 +35,12 @@ #include "os_string.hpp" -#include "trace_analyzer.hpp" #include "trace_callset.hpp" #include "trace_parser.hpp" #include "trace_writer.hpp" +#include "cli_trim_auto_analyzer.hpp" + static const char *synopsis = "Create a new trace by automatically trimming unecessary calls from an existing trace."; static void diff --git a/cli/cli_trim_auto_analyzer.cpp b/cli/cli_trim_auto_analyzer.cpp new file mode 100644 index 00000000..41d90180 --- /dev/null +++ b/cli/cli_trim_auto_analyzer.cpp @@ -0,0 +1,767 @@ +/************************************************************************** + * Copyright 2012 Intel corporation + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include + +#include "cli_trim_auto_analyzer.hpp" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define STRNCMP_LITERAL(var, literal) strncmp((var), (literal), sizeof (literal) -1) + +/* Rendering often has no side effects, but it can in some cases, +* (such as when transform feedback is active, or when rendering +* targets a framebuffer object). */ +bool +TraceAnalyzer::renderingHasSideEffect(void) +{ + return transformFeedbackActive || framebufferObjectActive; +} + +/* Provide: Record that the given call affects the given resource + * as a side effect. */ +void +TraceAnalyzer::provide(std::string resource, trace::CallNo call_no) +{ + resources[resource].insert(call_no); +} + +/* Like provide, but with a simply-formatted string, (appending an + * integer to the given string). */ +void +TraceAnalyzer::providef(std::string resource, + int resource_no, + trace::CallNo call_no) +{ + std::stringstream ss; + ss << resource << resource_no; + provide(ss.str(), call_no); +} + +/* Link: Establish a dependency between resource 'resource' and + * resource 'dependency'. This dependency is captured by name so + * that if the list of calls that provide 'dependency' grows + * before 'resource' is consumed, those calls will still be + * captured. */ +void +TraceAnalyzer::link(std::string resource, std::string dependency) +{ + dependencies[resource].insert(dependency); +} + +/* Like link, but with a simply-formatted string, (appending an + * integer to the given string). */ +void +TraceAnalyzer::linkf(std::string resource, std::string dependency, int dep_no) +{ + + std::stringstream ss; + ss << dependency << dep_no; + link(resource, ss.str()); +} + +/* Unlink: Remove dependency from 'resource' on 'dependency'. */ +void +TraceAnalyzer::unlink(std::string resource, std::string dependency) +{ + dependencies[resource].erase(dependency); + if (dependencies[resource].size() == 0) { + dependencies.erase(resource); + } +} + +/* Like unlink, but with a simply-formated string, (appending an + * integer to the given string). */ +void +TraceAnalyzer::unlinkf(std::string resource, std::string dependency, int dep_no) +{ + + std::stringstream ss; + ss << dependency << dep_no; + unlink(resource, ss.str()); +} + +/* Unlink all: Remove dependencies from 'resource' to all other + * resources. */ +void +TraceAnalyzer::unlinkAll(std::string resource) +{ + dependencies.erase(resource); +} + +/* Resolve: Recursively compute all calls providing 'resource', + * (including linked dependencies of 'resource' on other + * resources). */ +std::set +TraceAnalyzer::resolve(std::string resource) +{ + std::set *deps; + std::set::iterator dep; + + std::set *calls; + std::set::iterator call; + + std::set result, deps_set; + + /* Recursively chase dependencies. */ + if (dependencies.count(resource)) { + deps = &dependencies[resource]; + for (dep = deps->begin(); dep != deps->end(); dep++) { + deps_set = resolve(*dep); + for (call = deps_set.begin(); call != deps_set.end(); call++) { + result.insert(*call); + } + } + } + + /* Also look for calls that directly provide 'resource' */ + if (resources.count(resource)) { + calls = &resources[resource]; + for (call = calls->begin(); call != calls->end(); call++) { + result.insert(*call); + } + } + + return result; +} + +/* Consume: Resolve all calls that provide the given resource, and + * add them to the required list. Then clear the call list for + * 'resource' along with any dependencies. */ +void +TraceAnalyzer::consume(std::string resource) +{ + + std::set calls; + std::set::iterator call; + + calls = resolve(resource); + + dependencies.erase(resource); + resources.erase(resource); + + for (call = calls.begin(); call != calls.end(); call++) { + required.add(*call); + } +} + +void +TraceAnalyzer::stateTrackPreCall(trace::Call *call) +{ + + const char *name = call->name(); + + if (strcmp(name, "glBegin") == 0) { + insideBeginEnd = true; + return; + } + + if (strcmp(name, "glBeginTransformFeedback") == 0) { + transformFeedbackActive = true; + return; + } + + if (strcmp(name, "glActiveTexture") == 0) { + activeTextureUnit = static_cast(call->arg(0).toSInt()); + return; + } + + if (strcmp(name, "glBindTexture") == 0) { + GLenum target; + GLuint texture; + + target = static_cast(call->arg(0).toSInt()); + texture = call->arg(1).toUInt(); + + if (texture == 0) { + texture_map.erase(target); + } else { + texture_map[target] = texture; + } + + return; + } + + if (strcmp(name, "glUseProgram") == 0) { + activeProgram = call->arg(0).toUInt(); + } + + if (strcmp(name, "glBindFramebuffer") == 0) { + GLenum target; + GLuint framebuffer; + + target = static_cast(call->arg(0).toSInt()); + framebuffer = call->arg(1).toUInt(); + + if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) { + if (framebuffer == 0) { + framebufferObjectActive = false; + } else { + framebufferObjectActive = true; + } + } + return; + } + + if (strcmp(name, "glNewList") == 0) { + GLuint list = call->arg(0).toUInt(); + + insideNewEndList = list; + } +} + +void +TraceAnalyzer::stateTrackPostCall(trace::Call *call) +{ + + const char *name = call->name(); + + if (strcmp(name, "glEnd") == 0) { + insideBeginEnd = false; + return; + } + + if (strcmp(name, "glEndTransformFeedback") == 0) { + transformFeedbackActive = false; + return; + } + + /* If this swapbuffers was included in the trace then it will + * have already consumed all framebuffer dependencies. If not, + * then clear them now so that they don't carry over into the + * next frame. */ + if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && + call->flags & trace::CALL_FLAG_END_FRAME) { + dependencies.erase("framebuffer"); + resources.erase("framebuffer"); + return; + } + + if (strcmp(name, "glEndList") == 0) { + insideNewEndList = 0; + } +} + +bool +TraceAnalyzer::callHasNoSideEffects(trace::Call *call, const char *name) +{ + /* If call is flagged as no side effects, then we are done here. */ + if (call->flags & trace::CALL_FLAG_NO_SIDE_EFFECTS) { + return true; + } + + /* Not known as a no-side-effect call. Return false for more analysis. */ + return false; +} + +bool +TraceAnalyzer::recordTextureSideEffects(trace::Call *call, const char *name) +{ + if (strcmp(name, "glGenTextures") == 0) { + const trace::Array *textures = call->arg(1).toArray(); + size_t i; + GLuint texture; + + if (textures) { + for (i = 0; i < textures->size(); i++) { + texture = textures->values[i]->toUInt(); + providef("texture-", texture, call->no); + } + } + return true; + } + + /* FIXME: When we start tracking framebuffer objects as their own + * resources, we will want to link the FBO to the given texture + * resource, (and to this call). For now, just link render state + * to the texture, and force this call to be required. */ + if (strcmp(name, "glFramebufferTexture2D") == 0) { + GLuint texture; + + texture = call->arg(3).toUInt(); + + linkf("render-state", "texture-", texture); + + provide("state", call->no); + } + + if (strcmp(name, "glBindTexture") == 0) { + GLenum target; + GLuint texture; + + std::stringstream ss_target, ss_texture; + + target = static_cast(call->arg(0).toSInt()); + texture = call->arg(1).toUInt(); + + ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; + ss_texture << "texture-" << texture; + + resources.erase(ss_target.str()); + provide(ss_target.str(), call->no); + + unlinkAll(ss_target.str()); + link(ss_target.str(), ss_texture.str()); + + /* FIXME: This really shouldn't be necessary. The effect + * this provide() has is that all glBindTexture calls will + * be preserved in the output trace (never trimmed). Carl + * has a trace ("btr") where a glBindTexture call should + * not be necessary at all, (it's immediately followed + * with a glBindTexture to a different texture and no + * intervening texture-related calls), yet this 'provide' + * makes the difference between a trim_stress test failing + * and passing. + * + * More investigation is necessary, but for now, be + * conservative and don't trim. */ + provide("state", call->no); + + return true; + } + + /* FIXME: Need to handle glMultiTexImage and friends. */ + if (STRNCMP_LITERAL(name, "glTexImage") == 0 || + STRNCMP_LITERAL(name, "glTexSubImage") == 0 || + STRNCMP_LITERAL(name, "glCopyTexImage") == 0 || + STRNCMP_LITERAL(name, "glCopyTexSubImage") == 0 || + STRNCMP_LITERAL(name, "glCompressedTexImage") == 0 || + STRNCMP_LITERAL(name, "glCompressedTexSubImage") == 0 || + strcmp(name, "glInvalidateTexImage") == 0 || + strcmp(name, "glInvalidateTexSubImage") == 0) { + + std::set *calls; + std::set::iterator c; + std::stringstream ss_target, ss_texture; + + GLenum target = static_cast(call->arg(0).toSInt()); + + ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; + ss_texture << "texture-" << texture_map[target]; + + /* The texture resource depends on this call and any calls + * providing the given texture target. */ + provide(ss_texture.str(), call->no); + + if (resources.count(ss_target.str())) { + calls = &resources[ss_target.str()]; + for (c = calls->begin(); c != calls->end(); c++) { + provide(ss_texture.str(), *c); + } + } + + return true; + } + + if (strcmp(name, "glEnable") == 0) { + GLenum cap; + + cap = static_cast(call->arg(0).toSInt()); + + if (cap == GL_TEXTURE_1D || + cap == GL_TEXTURE_2D || + cap == GL_TEXTURE_3D || + cap == GL_TEXTURE_CUBE_MAP) + { + std::stringstream ss; + + ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; + + link("render-state", ss.str()); + } + + provide("state", call->no); + return true; + } + + if (strcmp(name, "glDisable") == 0) { + GLenum cap; + + cap = static_cast(call->arg(0).toSInt()); + + if (cap == GL_TEXTURE_1D || + cap == GL_TEXTURE_2D || + cap == GL_TEXTURE_3D || + cap == GL_TEXTURE_CUBE_MAP) + { + std::stringstream ss; + + ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; + + unlink("render-state", ss.str()); + } + + provide("state", call->no); + return true; + } + + /* No known texture-related side effects. Return false for more analysis. */ + return false; +} + +bool +TraceAnalyzer::recordShaderSideEffects(trace::Call *call, const char *name) +{ + if (strcmp(name, "glCreateShader") == 0 || + strcmp(name, "glCreateShaderObjectARB") == 0) { + + GLuint shader = call->ret->toUInt(); + providef("shader-", shader, call->no); + return true; + } + + if (strcmp(name, "glShaderSource") == 0 || + strcmp(name, "glShaderSourceARB") == 0 || + strcmp(name, "glCompileShader") == 0 || + strcmp(name, "glCompileShaderARB") == 0 || + strcmp(name, "glGetShaderiv") == 0 || + strcmp(name, "glGetShaderInfoLog") == 0) { + + GLuint shader = call->arg(0).toUInt(); + providef("shader-", shader, call->no); + return true; + } + + if (strcmp(name, "glCreateProgram") == 0 || + strcmp(name, "glCreateProgramObjectARB") == 0) { + + GLuint program = call->ret->toUInt(); + providef("program-", program, call->no); + return true; + } + + if (strcmp(name, "glAttachShader") == 0 || + strcmp(name, "glAttachObjectARB") == 0) { + + GLuint program, shader; + std::stringstream ss_program, ss_shader; + + program = call->arg(0).toUInt(); + shader = call->arg(1).toUInt(); + + ss_program << "program-" << program; + ss_shader << "shader-" << shader; + + link(ss_program.str(), ss_shader.str()); + provide(ss_program.str(), call->no); + + return true; + } + + if (strcmp(name, "glDetachShader") == 0 || + strcmp(name, "glDetachObjectARB") == 0) { + + GLuint program, shader; + std::stringstream ss_program, ss_shader; + + program = call->arg(0).toUInt(); + shader = call->arg(1).toUInt(); + + ss_program << "program-" << program; + ss_shader << "shader-" << shader; + + unlink(ss_program.str(), ss_shader.str()); + + return true; + } + + if (strcmp(name, "glUseProgram") == 0 || + strcmp(name, "glUseProgramObjectARB") == 0) { + + GLuint program; + + program = call->arg(0).toUInt(); + + unlinkAll("render-program-state"); + + if (program == 0) { + unlink("render-state", "render-program-state"); + provide("state", call->no); + } else { + std::stringstream ss; + + ss << "program-" << program; + + link("render-state", "render-program-state"); + link("render-program-state", ss.str()); + + provide(ss.str(), call->no); + } + + return true; + } + + if (strcmp(name, "glGetUniformLocation") == 0 || + strcmp(name, "glGetUniformLocationARB") == 0 || + strcmp(name, "glGetFragDataLocation") == 0 || + strcmp(name, "glGetFragDataLocationEXT") == 0 || + strcmp(name, "glGetSubroutineUniformLocation") == 0 || + strcmp(name, "glGetProgramResourceLocation") == 0 || + strcmp(name, "glGetProgramResourceLocationIndex") == 0 || + strcmp(name, "glGetVaryingLocationNV") == 0) { + + GLuint program = call->arg(0).toUInt(); + + providef("program-", program, call->no); + + return true; + } + + /* For any call that accepts 'location' as its first argument, + * perform a lookup in our location->program map and add a + * dependence on the program we find there. */ + if (call->sig->num_args > 0 && + strcmp(call->sig->arg_names[0], "location") == 0) { + + providef("program-", activeProgram, call->no); + + /* We can't easily tell if this uniform is being used to + * associate a sampler in the shader with a texture + * unit. The conservative option is to assume that it is + * and create a link from the active program to any bound + * textures for the given unit number. + * + * FIXME: We should be doing the same thing for calls to + * glUniform1iv. */ + if (strcmp(name, "glUniform1i") == 0 || + strcmp(name, "glUniform1iARB") == 0) { + + GLint max_unit = MAX(GL_MAX_TEXTURE_COORDS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); + + GLint unit = call->arg(1).toSInt(); + std::stringstream ss_program; + std::stringstream ss_texture; + + if (unit < max_unit) { + + ss_program << "program-" << activeProgram; + + ss_texture << "texture-unit-" << GL_TEXTURE0 + unit << "-target-"; + + /* We don't know what target(s) might get bound to + * this texture unit, so conservatively link to + * all. Only bound textures will actually get inserted + * into the output call stream. */ + linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_1D); + linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_2D); + linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_3D); + linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_CUBE_MAP); + } + } + + return true; + } + + /* FIXME: We cut a huge swath by assuming that any unhandled + * call that has a first argument named "program" should not + * be included in the trimmed output unless the program of + * that number is also included. + * + * This heuristic is correct for many cases, but we should + * actually carefully verify if this includes some calls + * inappropriately, or if it misses some. + */ + if (strcmp(name, "glLinkProgram") == 0 || + strcmp(name, "glLinkProgramARB") == 0 || + (call->sig->num_args > 0 && + (strcmp(call->sig->arg_names[0], "program") == 0 || + strcmp(call->sig->arg_names[0], "programObj") == 0))) { + + GLuint program = call->arg(0).toUInt(); + providef("program-", program, call->no); + return true; + } + + /* No known shader-related side effects. Return false for more analysis. */ + return false; +} + +bool +TraceAnalyzer::recordDrawingSideEffects(trace::Call *call, const char *name) +{ + /* Handle all rendering operations, (even though only glEnd is + * flagged as a rendering operation we treat everything from + * glBegin through glEnd as a rendering operation). */ + if (call->flags & trace::CALL_FLAG_RENDER || + insideBeginEnd) { + + std::set calls; + std::set::iterator c; + + provide("framebuffer", call->no); + + calls = resolve("render-state"); + + for (c = calls.begin(); c != calls.end(); c++) { + provide("framebuffer", *c); + } + + /* In some cases, rendering has side effects beyond the + * framebuffer update. */ + if (renderingHasSideEffect()) { + provide("state", call->no); + for (c = calls.begin(); c != calls.end(); c++) { + provide("state", *c); + } + } + + return true; + } + + /* Though it's not flagged as a "RENDER" operation, we also want + * to trim swapbuffers calls when trimming drawing operations. */ + if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && + call->flags & trace::CALL_FLAG_END_FRAME) { + return true; + } + + /* No known drawing-related side effects. Return false for more analysis. */ + return false; +} + +void +TraceAnalyzer::recordSideEffects(trace::Call *call) +{ + + const char *name = call->name(); + + /* FIXME: If we encode the list of commands that are executed + * immediately (as opposed to those that are compiled into a + * display list) then we could generate a "display-list-X" + * resource just as we do for "texture-X" resources and only + * emit it in the trace if a glCallList(X) is emitted. For + * now, simply punt and include anything within glNewList and + * glEndList in the trim output. This guarantees that display + * lists will work, but does not trim out unused display + * lists. */ + if (insideNewEndList != 0) { + provide("state", call->no); + + /* Also, any texture bound inside a display list is + * conservatively considered required. */ + if (strcmp(name, "glBindTexture") == 0) { + GLuint texture = call->arg(1).toUInt(); + + linkf("state", "texture-", texture); + } + + return; + } + + if (trimFlags & TRIM_FLAG_NO_SIDE_EFFECTS) { + + if (callHasNoSideEffects(call, name)) { + return; + } + } + + if (trimFlags & TRIM_FLAG_TEXTURES) { + + if (recordTextureSideEffects(call, name)) { + return; + } + } + + if (trimFlags & TRIM_FLAG_SHADERS) { + + if (recordShaderSideEffects(call, name)) { + return; + } + } + + if (trimFlags & TRIM_FLAG_DRAWING) { + + if (recordDrawingSideEffects(call, name)) { + return; + } + } + + /* By default, assume this call affects the state somehow. */ + resources["state"].insert(call->no); +} + +void +TraceAnalyzer::requireDependencies(trace::Call *call) +{ + + /* Swap-buffers calls depend on framebuffer state. */ + if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && + call->flags & trace::CALL_FLAG_END_FRAME) { + consume("framebuffer"); + } + + /* By default, just assume this call depends on generic state. */ + consume("state"); +} + +TraceAnalyzer::TraceAnalyzer(TrimFlags trimFlagsOpt): + transformFeedbackActive(false), + framebufferObjectActive(false), + insideBeginEnd(false), + insideNewEndList(0), + activeTextureUnit(GL_TEXTURE0), + trimFlags(trimFlagsOpt) +{ + /* Nothing needed. */ +} + +TraceAnalyzer::~TraceAnalyzer() +{ + /* Nothing needed. */ +} + +/* Analyze this call by tracking state and recording all the + * resources provided by this call as side effects.. */ +void +TraceAnalyzer::analyze(trace::Call *call) +{ + + stateTrackPreCall(call); + + recordSideEffects(call); + + stateTrackPostCall(call); +} + +/* Require this call and all of its dependencies to be included in + * the final trace. */ +void +TraceAnalyzer::require(trace::Call *call) +{ + + /* First, find and insert all calls that this call depends on. */ + requireDependencies(call); + + /* Then insert this call itself. */ + required.add(call->no); +} + +/* Return a set of all the required calls, (both those calls added + * explicitly with require() and those implicitly depended + * upon. */ +trace::FastCallSet * +TraceAnalyzer::get_required(void) +{ + return &required; +} diff --git a/cli/cli_trim_auto_analyzer.hpp b/cli/cli_trim_auto_analyzer.hpp new file mode 100644 index 00000000..c344fb6a --- /dev/null +++ b/cli/cli_trim_auto_analyzer.hpp @@ -0,0 +1,114 @@ +/************************************************************************** + * Copyright 2012 Intel corporation + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include + +#include +#include + +#include "trace_callset.hpp" +#include "trace_fast_callset.hpp" +#include "trace_parser.hpp" + +typedef unsigned TrimFlags; + +/** + * Trim flags + */ +enum { + + /* Whether to trim calls that have no side effects. */ + TRIM_FLAG_NO_SIDE_EFFECTS = (1 << 0), + + /* Whether to trim calls to setup textures that are never used. */ + TRIM_FLAG_TEXTURES = (1 << 1), + + /* Whether to trim calls to setup shaders that are never used. */ + TRIM_FLAG_SHADERS = (1 << 2), + + /* Whether to trim drawing operations outside of the desired call-set. */ + TRIM_FLAG_DRAWING = (1 << 3), +}; + +class TraceAnalyzer { +private: + std::map > resources; + std::map > dependencies; + + std::map texture_map; + + trace::FastCallSet required; + + bool transformFeedbackActive; + bool framebufferObjectActive; + bool insideBeginEnd; + GLuint insideNewEndList; + GLenum activeTextureUnit; + GLuint activeProgram; + unsigned int trimFlags; + + void provide(std::string resource, trace::CallNo call_no); + void providef(std::string resource, int resource_no, trace::CallNo call_no); + + void link(std::string resource, std::string dependency); + void linkf(std::string resource, std::string dependency, int dep_no); + + void unlink(std::string resource, std::string dependency); + void unlinkf(std::string resource, std::string dependency, int dep_no); + void unlinkAll(std::string resource); + + void stateTrackPreCall(trace::Call *call); + + void recordSideEffects(trace::Call *call); + bool callHasNoSideEffects(trace::Call *call, const char *name); + bool recordTextureSideEffects(trace::Call *call, const char *name); + bool recordShaderSideEffects(trace::Call *call, const char *name); + bool recordDrawingSideEffects(trace::Call *call, const char *name); + + void stateTrackPostCall(trace::Call *call); + + bool renderingHasSideEffect(void); + std::set resolve(std::string resource); + + void consume(std::string resource); + void requireDependencies(trace::Call *call); + +public: + TraceAnalyzer(TrimFlags trimFlags = -1); + ~TraceAnalyzer(); + + /* Analyze this call by tracking state and recording all the + * resources provided by this call as side effects.. */ + void analyze(trace::Call *call); + + /* Require this call and all of its dependencies to be included in + * the final trace. */ + void require(trace::Call *call); + + /* Return a set of all the required calls, (both those calls added + * explicitly with require() and those implicitly depended + * upon. */ + trace::FastCallSet *get_required(void); +}; diff --git a/cli/trace_analyzer.cpp b/cli/trace_analyzer.cpp deleted file mode 100644 index 79f95579..00000000 --- a/cli/trace_analyzer.cpp +++ /dev/null @@ -1,767 +0,0 @@ -/************************************************************************** - * Copyright 2012 Intel corporation - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include - -#include "trace_analyzer.hpp" - -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define STRNCMP_LITERAL(var, literal) strncmp((var), (literal), sizeof (literal) -1) - -/* Rendering often has no side effects, but it can in some cases, -* (such as when transform feedback is active, or when rendering -* targets a framebuffer object). */ -bool -TraceAnalyzer::renderingHasSideEffect(void) -{ - return transformFeedbackActive || framebufferObjectActive; -} - -/* Provide: Record that the given call affects the given resource - * as a side effect. */ -void -TraceAnalyzer::provide(std::string resource, trace::CallNo call_no) -{ - resources[resource].insert(call_no); -} - -/* Like provide, but with a simply-formatted string, (appending an - * integer to the given string). */ -void -TraceAnalyzer::providef(std::string resource, - int resource_no, - trace::CallNo call_no) -{ - std::stringstream ss; - ss << resource << resource_no; - provide(ss.str(), call_no); -} - -/* Link: Establish a dependency between resource 'resource' and - * resource 'dependency'. This dependency is captured by name so - * that if the list of calls that provide 'dependency' grows - * before 'resource' is consumed, those calls will still be - * captured. */ -void -TraceAnalyzer::link(std::string resource, std::string dependency) -{ - dependencies[resource].insert(dependency); -} - -/* Like link, but with a simply-formatted string, (appending an - * integer to the given string). */ -void -TraceAnalyzer::linkf(std::string resource, std::string dependency, int dep_no) -{ - - std::stringstream ss; - ss << dependency << dep_no; - link(resource, ss.str()); -} - -/* Unlink: Remove dependency from 'resource' on 'dependency'. */ -void -TraceAnalyzer::unlink(std::string resource, std::string dependency) -{ - dependencies[resource].erase(dependency); - if (dependencies[resource].size() == 0) { - dependencies.erase(resource); - } -} - -/* Like unlink, but with a simply-formated string, (appending an - * integer to the given string). */ -void -TraceAnalyzer::unlinkf(std::string resource, std::string dependency, int dep_no) -{ - - std::stringstream ss; - ss << dependency << dep_no; - unlink(resource, ss.str()); -} - -/* Unlink all: Remove dependencies from 'resource' to all other - * resources. */ -void -TraceAnalyzer::unlinkAll(std::string resource) -{ - dependencies.erase(resource); -} - -/* Resolve: Recursively compute all calls providing 'resource', - * (including linked dependencies of 'resource' on other - * resources). */ -std::set -TraceAnalyzer::resolve(std::string resource) -{ - std::set *deps; - std::set::iterator dep; - - std::set *calls; - std::set::iterator call; - - std::set result, deps_set; - - /* Recursively chase dependencies. */ - if (dependencies.count(resource)) { - deps = &dependencies[resource]; - for (dep = deps->begin(); dep != deps->end(); dep++) { - deps_set = resolve(*dep); - for (call = deps_set.begin(); call != deps_set.end(); call++) { - result.insert(*call); - } - } - } - - /* Also look for calls that directly provide 'resource' */ - if (resources.count(resource)) { - calls = &resources[resource]; - for (call = calls->begin(); call != calls->end(); call++) { - result.insert(*call); - } - } - - return result; -} - -/* Consume: Resolve all calls that provide the given resource, and - * add them to the required list. Then clear the call list for - * 'resource' along with any dependencies. */ -void -TraceAnalyzer::consume(std::string resource) -{ - - std::set calls; - std::set::iterator call; - - calls = resolve(resource); - - dependencies.erase(resource); - resources.erase(resource); - - for (call = calls.begin(); call != calls.end(); call++) { - required.add(*call); - } -} - -void -TraceAnalyzer::stateTrackPreCall(trace::Call *call) -{ - - const char *name = call->name(); - - if (strcmp(name, "glBegin") == 0) { - insideBeginEnd = true; - return; - } - - if (strcmp(name, "glBeginTransformFeedback") == 0) { - transformFeedbackActive = true; - return; - } - - if (strcmp(name, "glActiveTexture") == 0) { - activeTextureUnit = static_cast(call->arg(0).toSInt()); - return; - } - - if (strcmp(name, "glBindTexture") == 0) { - GLenum target; - GLuint texture; - - target = static_cast(call->arg(0).toSInt()); - texture = call->arg(1).toUInt(); - - if (texture == 0) { - texture_map.erase(target); - } else { - texture_map[target] = texture; - } - - return; - } - - if (strcmp(name, "glUseProgram") == 0) { - activeProgram = call->arg(0).toUInt(); - } - - if (strcmp(name, "glBindFramebuffer") == 0) { - GLenum target; - GLuint framebuffer; - - target = static_cast(call->arg(0).toSInt()); - framebuffer = call->arg(1).toUInt(); - - if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) { - if (framebuffer == 0) { - framebufferObjectActive = false; - } else { - framebufferObjectActive = true; - } - } - return; - } - - if (strcmp(name, "glNewList") == 0) { - GLuint list = call->arg(0).toUInt(); - - insideNewEndList = list; - } -} - -void -TraceAnalyzer::stateTrackPostCall(trace::Call *call) -{ - - const char *name = call->name(); - - if (strcmp(name, "glEnd") == 0) { - insideBeginEnd = false; - return; - } - - if (strcmp(name, "glEndTransformFeedback") == 0) { - transformFeedbackActive = false; - return; - } - - /* If this swapbuffers was included in the trace then it will - * have already consumed all framebuffer dependencies. If not, - * then clear them now so that they don't carry over into the - * next frame. */ - if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && - call->flags & trace::CALL_FLAG_END_FRAME) { - dependencies.erase("framebuffer"); - resources.erase("framebuffer"); - return; - } - - if (strcmp(name, "glEndList") == 0) { - insideNewEndList = 0; - } -} - -bool -TraceAnalyzer::callHasNoSideEffects(trace::Call *call, const char *name) -{ - /* If call is flagged as no side effects, then we are done here. */ - if (call->flags & trace::CALL_FLAG_NO_SIDE_EFFECTS) { - return true; - } - - /* Not known as a no-side-effect call. Return false for more analysis. */ - return false; -} - -bool -TraceAnalyzer::recordTextureSideEffects(trace::Call *call, const char *name) -{ - if (strcmp(name, "glGenTextures") == 0) { - const trace::Array *textures = call->arg(1).toArray(); - size_t i; - GLuint texture; - - if (textures) { - for (i = 0; i < textures->size(); i++) { - texture = textures->values[i]->toUInt(); - providef("texture-", texture, call->no); - } - } - return true; - } - - /* FIXME: When we start tracking framebuffer objects as their own - * resources, we will want to link the FBO to the given texture - * resource, (and to this call). For now, just link render state - * to the texture, and force this call to be required. */ - if (strcmp(name, "glFramebufferTexture2D") == 0) { - GLuint texture; - - texture = call->arg(3).toUInt(); - - linkf("render-state", "texture-", texture); - - provide("state", call->no); - } - - if (strcmp(name, "glBindTexture") == 0) { - GLenum target; - GLuint texture; - - std::stringstream ss_target, ss_texture; - - target = static_cast(call->arg(0).toSInt()); - texture = call->arg(1).toUInt(); - - ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; - ss_texture << "texture-" << texture; - - resources.erase(ss_target.str()); - provide(ss_target.str(), call->no); - - unlinkAll(ss_target.str()); - link(ss_target.str(), ss_texture.str()); - - /* FIXME: This really shouldn't be necessary. The effect - * this provide() has is that all glBindTexture calls will - * be preserved in the output trace (never trimmed). Carl - * has a trace ("btr") where a glBindTexture call should - * not be necessary at all, (it's immediately followed - * with a glBindTexture to a different texture and no - * intervening texture-related calls), yet this 'provide' - * makes the difference between a trim_stress test failing - * and passing. - * - * More investigation is necessary, but for now, be - * conservative and don't trim. */ - provide("state", call->no); - - return true; - } - - /* FIXME: Need to handle glMultiTexImage and friends. */ - if (STRNCMP_LITERAL(name, "glTexImage") == 0 || - STRNCMP_LITERAL(name, "glTexSubImage") == 0 || - STRNCMP_LITERAL(name, "glCopyTexImage") == 0 || - STRNCMP_LITERAL(name, "glCopyTexSubImage") == 0 || - STRNCMP_LITERAL(name, "glCompressedTexImage") == 0 || - STRNCMP_LITERAL(name, "glCompressedTexSubImage") == 0 || - strcmp(name, "glInvalidateTexImage") == 0 || - strcmp(name, "glInvalidateTexSubImage") == 0) { - - std::set *calls; - std::set::iterator c; - std::stringstream ss_target, ss_texture; - - GLenum target = static_cast(call->arg(0).toSInt()); - - ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; - ss_texture << "texture-" << texture_map[target]; - - /* The texture resource depends on this call and any calls - * providing the given texture target. */ - provide(ss_texture.str(), call->no); - - if (resources.count(ss_target.str())) { - calls = &resources[ss_target.str()]; - for (c = calls->begin(); c != calls->end(); c++) { - provide(ss_texture.str(), *c); - } - } - - return true; - } - - if (strcmp(name, "glEnable") == 0) { - GLenum cap; - - cap = static_cast(call->arg(0).toSInt()); - - if (cap == GL_TEXTURE_1D || - cap == GL_TEXTURE_2D || - cap == GL_TEXTURE_3D || - cap == GL_TEXTURE_CUBE_MAP) - { - std::stringstream ss; - - ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; - - link("render-state", ss.str()); - } - - provide("state", call->no); - return true; - } - - if (strcmp(name, "glDisable") == 0) { - GLenum cap; - - cap = static_cast(call->arg(0).toSInt()); - - if (cap == GL_TEXTURE_1D || - cap == GL_TEXTURE_2D || - cap == GL_TEXTURE_3D || - cap == GL_TEXTURE_CUBE_MAP) - { - std::stringstream ss; - - ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; - - unlink("render-state", ss.str()); - } - - provide("state", call->no); - return true; - } - - /* No known texture-related side effects. Return false for more analysis. */ - return false; -} - -bool -TraceAnalyzer::recordShaderSideEffects(trace::Call *call, const char *name) -{ - if (strcmp(name, "glCreateShader") == 0 || - strcmp(name, "glCreateShaderObjectARB") == 0) { - - GLuint shader = call->ret->toUInt(); - providef("shader-", shader, call->no); - return true; - } - - if (strcmp(name, "glShaderSource") == 0 || - strcmp(name, "glShaderSourceARB") == 0 || - strcmp(name, "glCompileShader") == 0 || - strcmp(name, "glCompileShaderARB") == 0 || - strcmp(name, "glGetShaderiv") == 0 || - strcmp(name, "glGetShaderInfoLog") == 0) { - - GLuint shader = call->arg(0).toUInt(); - providef("shader-", shader, call->no); - return true; - } - - if (strcmp(name, "glCreateProgram") == 0 || - strcmp(name, "glCreateProgramObjectARB") == 0) { - - GLuint program = call->ret->toUInt(); - providef("program-", program, call->no); - return true; - } - - if (strcmp(name, "glAttachShader") == 0 || - strcmp(name, "glAttachObjectARB") == 0) { - - GLuint program, shader; - std::stringstream ss_program, ss_shader; - - program = call->arg(0).toUInt(); - shader = call->arg(1).toUInt(); - - ss_program << "program-" << program; - ss_shader << "shader-" << shader; - - link(ss_program.str(), ss_shader.str()); - provide(ss_program.str(), call->no); - - return true; - } - - if (strcmp(name, "glDetachShader") == 0 || - strcmp(name, "glDetachObjectARB") == 0) { - - GLuint program, shader; - std::stringstream ss_program, ss_shader; - - program = call->arg(0).toUInt(); - shader = call->arg(1).toUInt(); - - ss_program << "program-" << program; - ss_shader << "shader-" << shader; - - unlink(ss_program.str(), ss_shader.str()); - - return true; - } - - if (strcmp(name, "glUseProgram") == 0 || - strcmp(name, "glUseProgramObjectARB") == 0) { - - GLuint program; - - program = call->arg(0).toUInt(); - - unlinkAll("render-program-state"); - - if (program == 0) { - unlink("render-state", "render-program-state"); - provide("state", call->no); - } else { - std::stringstream ss; - - ss << "program-" << program; - - link("render-state", "render-program-state"); - link("render-program-state", ss.str()); - - provide(ss.str(), call->no); - } - - return true; - } - - if (strcmp(name, "glGetUniformLocation") == 0 || - strcmp(name, "glGetUniformLocationARB") == 0 || - strcmp(name, "glGetFragDataLocation") == 0 || - strcmp(name, "glGetFragDataLocationEXT") == 0 || - strcmp(name, "glGetSubroutineUniformLocation") == 0 || - strcmp(name, "glGetProgramResourceLocation") == 0 || - strcmp(name, "glGetProgramResourceLocationIndex") == 0 || - strcmp(name, "glGetVaryingLocationNV") == 0) { - - GLuint program = call->arg(0).toUInt(); - - providef("program-", program, call->no); - - return true; - } - - /* For any call that accepts 'location' as its first argument, - * perform a lookup in our location->program map and add a - * dependence on the program we find there. */ - if (call->sig->num_args > 0 && - strcmp(call->sig->arg_names[0], "location") == 0) { - - providef("program-", activeProgram, call->no); - - /* We can't easily tell if this uniform is being used to - * associate a sampler in the shader with a texture - * unit. The conservative option is to assume that it is - * and create a link from the active program to any bound - * textures for the given unit number. - * - * FIXME: We should be doing the same thing for calls to - * glUniform1iv. */ - if (strcmp(name, "glUniform1i") == 0 || - strcmp(name, "glUniform1iARB") == 0) { - - GLint max_unit = MAX(GL_MAX_TEXTURE_COORDS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); - - GLint unit = call->arg(1).toSInt(); - std::stringstream ss_program; - std::stringstream ss_texture; - - if (unit < max_unit) { - - ss_program << "program-" << activeProgram; - - ss_texture << "texture-unit-" << GL_TEXTURE0 + unit << "-target-"; - - /* We don't know what target(s) might get bound to - * this texture unit, so conservatively link to - * all. Only bound textures will actually get inserted - * into the output call stream. */ - linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_1D); - linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_2D); - linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_3D); - linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_CUBE_MAP); - } - } - - return true; - } - - /* FIXME: We cut a huge swath by assuming that any unhandled - * call that has a first argument named "program" should not - * be included in the trimmed output unless the program of - * that number is also included. - * - * This heuristic is correct for many cases, but we should - * actually carefully verify if this includes some calls - * inappropriately, or if it misses some. - */ - if (strcmp(name, "glLinkProgram") == 0 || - strcmp(name, "glLinkProgramARB") == 0 || - (call->sig->num_args > 0 && - (strcmp(call->sig->arg_names[0], "program") == 0 || - strcmp(call->sig->arg_names[0], "programObj") == 0))) { - - GLuint program = call->arg(0).toUInt(); - providef("program-", program, call->no); - return true; - } - - /* No known shader-related side effects. Return false for more analysis. */ - return false; -} - -bool -TraceAnalyzer::recordDrawingSideEffects(trace::Call *call, const char *name) -{ - /* Handle all rendering operations, (even though only glEnd is - * flagged as a rendering operation we treat everything from - * glBegin through glEnd as a rendering operation). */ - if (call->flags & trace::CALL_FLAG_RENDER || - insideBeginEnd) { - - std::set calls; - std::set::iterator c; - - provide("framebuffer", call->no); - - calls = resolve("render-state"); - - for (c = calls.begin(); c != calls.end(); c++) { - provide("framebuffer", *c); - } - - /* In some cases, rendering has side effects beyond the - * framebuffer update. */ - if (renderingHasSideEffect()) { - provide("state", call->no); - for (c = calls.begin(); c != calls.end(); c++) { - provide("state", *c); - } - } - - return true; - } - - /* Though it's not flagged as a "RENDER" operation, we also want - * to trim swapbuffers calls when trimming drawing operations. */ - if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && - call->flags & trace::CALL_FLAG_END_FRAME) { - return true; - } - - /* No known drawing-related side effects. Return false for more analysis. */ - return false; -} - -void -TraceAnalyzer::recordSideEffects(trace::Call *call) -{ - - const char *name = call->name(); - - /* FIXME: If we encode the list of commands that are executed - * immediately (as opposed to those that are compiled into a - * display list) then we could generate a "display-list-X" - * resource just as we do for "texture-X" resources and only - * emit it in the trace if a glCallList(X) is emitted. For - * now, simply punt and include anything within glNewList and - * glEndList in the trim output. This guarantees that display - * lists will work, but does not trim out unused display - * lists. */ - if (insideNewEndList != 0) { - provide("state", call->no); - - /* Also, any texture bound inside a display list is - * conservatively considered required. */ - if (strcmp(name, "glBindTexture") == 0) { - GLuint texture = call->arg(1).toUInt(); - - linkf("state", "texture-", texture); - } - - return; - } - - if (trimFlags & TRIM_FLAG_NO_SIDE_EFFECTS) { - - if (callHasNoSideEffects(call, name)) { - return; - } - } - - if (trimFlags & TRIM_FLAG_TEXTURES) { - - if (recordTextureSideEffects(call, name)) { - return; - } - } - - if (trimFlags & TRIM_FLAG_SHADERS) { - - if (recordShaderSideEffects(call, name)) { - return; - } - } - - if (trimFlags & TRIM_FLAG_DRAWING) { - - if (recordDrawingSideEffects(call, name)) { - return; - } - } - - /* By default, assume this call affects the state somehow. */ - resources["state"].insert(call->no); -} - -void -TraceAnalyzer::requireDependencies(trace::Call *call) -{ - - /* Swap-buffers calls depend on framebuffer state. */ - if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && - call->flags & trace::CALL_FLAG_END_FRAME) { - consume("framebuffer"); - } - - /* By default, just assume this call depends on generic state. */ - consume("state"); -} - -TraceAnalyzer::TraceAnalyzer(TrimFlags trimFlagsOpt): - transformFeedbackActive(false), - framebufferObjectActive(false), - insideBeginEnd(false), - insideNewEndList(0), - activeTextureUnit(GL_TEXTURE0), - trimFlags(trimFlagsOpt) -{ - /* Nothing needed. */ -} - -TraceAnalyzer::~TraceAnalyzer() -{ - /* Nothing needed. */ -} - -/* Analyze this call by tracking state and recording all the - * resources provided by this call as side effects.. */ -void -TraceAnalyzer::analyze(trace::Call *call) -{ - - stateTrackPreCall(call); - - recordSideEffects(call); - - stateTrackPostCall(call); -} - -/* Require this call and all of its dependencies to be included in - * the final trace. */ -void -TraceAnalyzer::require(trace::Call *call) -{ - - /* First, find and insert all calls that this call depends on. */ - requireDependencies(call); - - /* Then insert this call itself. */ - required.add(call->no); -} - -/* Return a set of all the required calls, (both those calls added - * explicitly with require() and those implicitly depended - * upon. */ -trace::FastCallSet * -TraceAnalyzer::get_required(void) -{ - return &required; -} diff --git a/cli/trace_analyzer.hpp b/cli/trace_analyzer.hpp deleted file mode 100644 index c344fb6a..00000000 --- a/cli/trace_analyzer.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/************************************************************************** - * Copyright 2012 Intel corporation - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#include - -#include -#include - -#include "trace_callset.hpp" -#include "trace_fast_callset.hpp" -#include "trace_parser.hpp" - -typedef unsigned TrimFlags; - -/** - * Trim flags - */ -enum { - - /* Whether to trim calls that have no side effects. */ - TRIM_FLAG_NO_SIDE_EFFECTS = (1 << 0), - - /* Whether to trim calls to setup textures that are never used. */ - TRIM_FLAG_TEXTURES = (1 << 1), - - /* Whether to trim calls to setup shaders that are never used. */ - TRIM_FLAG_SHADERS = (1 << 2), - - /* Whether to trim drawing operations outside of the desired call-set. */ - TRIM_FLAG_DRAWING = (1 << 3), -}; - -class TraceAnalyzer { -private: - std::map > resources; - std::map > dependencies; - - std::map texture_map; - - trace::FastCallSet required; - - bool transformFeedbackActive; - bool framebufferObjectActive; - bool insideBeginEnd; - GLuint insideNewEndList; - GLenum activeTextureUnit; - GLuint activeProgram; - unsigned int trimFlags; - - void provide(std::string resource, trace::CallNo call_no); - void providef(std::string resource, int resource_no, trace::CallNo call_no); - - void link(std::string resource, std::string dependency); - void linkf(std::string resource, std::string dependency, int dep_no); - - void unlink(std::string resource, std::string dependency); - void unlinkf(std::string resource, std::string dependency, int dep_no); - void unlinkAll(std::string resource); - - void stateTrackPreCall(trace::Call *call); - - void recordSideEffects(trace::Call *call); - bool callHasNoSideEffects(trace::Call *call, const char *name); - bool recordTextureSideEffects(trace::Call *call, const char *name); - bool recordShaderSideEffects(trace::Call *call, const char *name); - bool recordDrawingSideEffects(trace::Call *call, const char *name); - - void stateTrackPostCall(trace::Call *call); - - bool renderingHasSideEffect(void); - std::set resolve(std::string resource); - - void consume(std::string resource); - void requireDependencies(trace::Call *call); - -public: - TraceAnalyzer(TrimFlags trimFlags = -1); - ~TraceAnalyzer(); - - /* Analyze this call by tracking state and recording all the - * resources provided by this call as side effects.. */ - void analyze(trace::Call *call); - - /* Require this call and all of its dependencies to be included in - * the final trace. */ - void require(trace::Call *call); - - /* Return a set of all the required calls, (both those calls added - * explicitly with require() and those implicitly depended - * upon. */ - trace::FastCallSet *get_required(void); -}; -- cgit v1.2.3