]> git.sesse.net Git - movit/blobdiff - effect_chain.cpp
Support timer queries for phases.
[movit] / effect_chain.cpp
index 0a01bd356e6066261910a60e4ee2af2813d428f5..d33cdaeb4a42e6aab680b62c684ccf8dbac2bf4e 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <epoxy/gl.h>
 #include <assert.h>
-#include <locale.h>
 #include <math.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -37,7 +36,8 @@ EffectChain::EffectChain(float aspect_nom, float aspect_denom, ResourcePool *res
          dither_effect(NULL),
          num_dither_bits(0),
          finalized(false),
-         resource_pool(resource_pool) {
+         resource_pool(resource_pool),
+         do_phase_timing(false) {
        if (resource_pool == NULL) {
                this->resource_pool = new ResourcePool();
                owns_resource_pool = true;
@@ -88,6 +88,7 @@ Node *EffectChain::add_node(Effect *effect)
        node->output_color_space = COLORSPACE_INVALID;
        node->output_gamma_curve = GAMMA_INVALID;
        node->output_alpha_type = ALPHA_INVALID;
+       node->needs_mipmaps = false;
 
        nodes.push_back(node);
        node_map[effect] = node;
@@ -311,6 +312,10 @@ Phase *EffectChain::construct_phase(Node *output, map<Node *, Phase *> *complete
                Node *node = effects_todo_this_phase.top();
                effects_todo_this_phase.pop();
 
+               if (node->effect->needs_mipmaps()) {
+                       node->needs_mipmaps = true;
+               }
+
                // This should currently only happen for effects that are inputs
                // (either true inputs or phase outputs). We special-case inputs,
                // and then deduplicate phase outputs below.
@@ -335,6 +340,21 @@ Phase *EffectChain::construct_phase(Node *output, map<Node *, Phase *> *complete
                                start_new_phase = true;
                        }
 
+                       // Propagate information about needing mipmaps down the chain,
+                       // breaking the phase if we notice an incompatibility.
+                       //
+                       // Note that we cannot do this propagation as a normal pass,
+                       // because it needs information about where the phases end
+                       // (we should not propagate the flag across phases).
+                       if (node->needs_mipmaps) {
+                               if (deps[i]->effect->num_inputs() == 0) {
+                                       Input *input = static_cast<Input *>(deps[i]->effect);
+                                       start_new_phase |= !input->can_supply_mipmaps();
+                               } else {
+                                       deps[i]->needs_mipmaps = true;
+                               }
+                       }
+
                        if (deps[i]->outgoing_links.size() > 1) {
                                if (!deps[i]->effect->is_single_texture()) {
                                        // More than one effect uses this as the input,
@@ -393,13 +413,22 @@ Phase *EffectChain::construct_phase(Node *output, map<Node *, Phase *> *complete
        for (unsigned i = 0; i < phase->effects.size(); ++i) {
                Node *node = phase->effects[i];
                if (node->effect->num_inputs() == 0) {
-                       CHECK(node->effect->set_int("needs_mipmaps", phase->input_needs_mipmaps));
+                       Input *input = static_cast<Input *>(node->effect);
+                       assert(!phase->input_needs_mipmaps || input->can_supply_mipmaps());
+                       CHECK(input->set_int("needs_mipmaps", phase->input_needs_mipmaps));
                }
        }
 
        // Actually make the shader for this phase.
        compile_glsl_program(phase);
 
+       // Initialize timer objects.
+       if (movit_timer_queries_supported) {
+               glGenQueries(1, &phase->timer_query_object);
+               phase->time_elapsed_ns = 0;
+               phase->num_measured_iterations = 0;
+       }
+
        assert(completed_effects->count(output) == 0);
        completed_effects->insert(make_pair(output, phase));
        phases.push_back(phase);
@@ -1330,10 +1359,6 @@ Node *EffectChain::find_output_node()
 
 void EffectChain::finalize()
 {
-       // Save the current locale, and set it to C, so that we can output decimal
-       // numbers with printf and be sure to get them in the format mandated by GLSL.
-       char *saved_locale = setlocale(LC_NUMERIC, "C");
-
        // Output the graph as it is before we do any conversions on it.
        output_dot("step0-start.dot");
 
@@ -1396,7 +1421,6 @@ void EffectChain::finalize()
        assert(phases[0]->inputs.empty());
        
        finalized = true;
-       setlocale(LC_NUMERIC, saved_locale);
 }
 
 void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height)
@@ -1432,6 +1456,9 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
        for (unsigned phase_num = 0; phase_num < phases.size(); ++phase_num) {
                Phase *phase = phases[phase_num];
 
+               if (do_phase_timing) {
+                       glBeginQuery(GL_TIME_ELAPSED, phase->timer_query_object);
+               }
                if (phase_num == phases.size() - 1) {
                        // Last phase goes to the output the user specified.
                        glBindFramebuffer(GL_FRAMEBUFFER, dest_fbo);
@@ -1445,6 +1472,9 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
                        }
                }
                execute_phase(phase, phase_num == phases.size() - 1, &output_textures, &generated_mipmaps);
+               if (do_phase_timing) {
+                       glEndQuery(GL_TIME_ELAPSED);
+               }
        }
 
        for (map<Phase *, GLuint>::const_iterator texture_it = output_textures.begin();
@@ -1457,6 +1487,57 @@ void EffectChain::render_to_fbo(GLuint dest_fbo, unsigned width, unsigned height
        check_error();
        glUseProgram(0);
        check_error();
+
+       if (do_phase_timing) {
+               // Get back the timer queries.
+               for (unsigned phase_num = 0; phase_num < phases.size(); ++phase_num) {
+                       Phase *phase = phases[phase_num];
+                       GLint available = 0;
+                       while (!available) {
+                               glGetQueryObjectiv(phase->timer_query_object, GL_QUERY_RESULT_AVAILABLE, &available);
+                       }
+                       GLuint64 time_elapsed;
+                       glGetQueryObjectui64v(phase->timer_query_object, GL_QUERY_RESULT, &time_elapsed);
+                       phase->time_elapsed_ns += time_elapsed;
+                       ++phase->num_measured_iterations;
+               }
+       }
+}
+
+void EffectChain::enable_phase_timing(bool enable)
+{
+       if (enable) {
+               assert(movit_timer_queries_supported);
+       }
+       this->do_phase_timing = enable;
+}
+
+void EffectChain::reset_phase_timing()
+{
+       for (unsigned phase_num = 0; phase_num < phases.size(); ++phase_num) {
+               Phase *phase = phases[phase_num];
+               phase->time_elapsed_ns = 0;
+               phase->num_measured_iterations = 0;
+       }
+}
+
+void EffectChain::print_phase_timing()
+{
+       double total_time_ms = 0.0;
+       for (unsigned phase_num = 0; phase_num < phases.size(); ++phase_num) {
+               Phase *phase = phases[phase_num];
+               double avg_time_ms = phase->time_elapsed_ns * 1e-6 / phase->num_measured_iterations;
+               printf("Phase %d: %5.1f ms  [", phase_num, avg_time_ms);
+               for (unsigned effect_num = 0; effect_num < phase->effects.size(); ++effect_num) {
+                       if (effect_num != 0) {
+                               printf(", ");
+                       }
+                       printf("%s", phase->effects[effect_num]->effect->effect_type_id().c_str());
+               }
+               printf("]\n");
+               total_time_ms += avg_time_ms;
+       }
+       printf("Total:   %5.1f ms\n", total_time_ms);
 }
 
 void EffectChain::execute_phase(Phase *phase, bool last_phase, map<Phase *, GLuint> *output_textures, set<Phase *> *generated_mipmaps)