#include <epoxy/gl.h>
#include <assert.h>
-#include <locale.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stack>
#include <utility>
#include <vector>
-#if defined(__APPLE__)
-#include <xlocale.h>
-#endif
#include "alpha_division_effect.h"
#include "alpha_multiplication_effect.h"
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;
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;
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.
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,
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);
// Now propagate from the inputs towards the end, and inform as we go.
// The rules are simple:
//
- // 1. Don't touch effects that already have given sizes (ie., inputs).
+ // 1. Don't touch effects that already have given sizes (ie., inputs
+ // or effects that change the output size).
// 2. If all of your inputs have the same size, that will be your output size.
// 3. Otherwise, your output size is 0x0.
for (unsigned i = 0; i < phase->effects.size(); ++i) {
this_output_height = 0;
}
}
- node->output_width = this_output_width;
- node->output_height = this_output_height;
+ if (node->effect->changes_output_size()) {
+ // We cannot call get_output_size() before we've done inform_input_size()
+ // on all inputs.
+ unsigned real_width_ignored, real_height_ignored;
+ node->effect->get_output_size(&real_width_ignored, &real_height_ignored,
+ &node->output_width, &node->output_height);
+ } else {
+ node->output_width = this_output_width;
+ node->output_height = this_output_height;
+ }
}
}
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.
-#if defined(__MINGW32__)
- // Note that the OpenGL driver might call setlocale() behind-the-scenes,
- // and that might corrupt the returned pointer, so we need to take our own
- // copy of it here.
- char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL));
- setlocale(LC_NUMERIC, "C");
-#else
- locale_t c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
- locale_t saved_locale = uselocale(c_locale);
-#endif
-
// Output the graph as it is before we do any conversions on it.
output_dot("step0-start.dot");
assert(phases[0]->inputs.empty());
finalized = true;
-#if defined(__MINGW32__)
- setlocale(LC_NUMERIC, saved_locale);
- free(saved_locale);
-#else
- uselocale(saved_locale);
- freelocale(c_locale);
-#endif
}
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);
}
}
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();
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)
// Now draw!
float vertices[] = {
- 0.0f, 1.0f,
+ 0.0f, 2.0f,
0.0f, 0.0f,
- 1.0f, 1.0f,
- 1.0f, 0.0f
+ 2.0f, 0.0f
};
GLuint vao;
GLuint position_vbo = fill_vertex_attribute(glsl_program_num, "position", 2, GL_FLOAT, sizeof(vertices), vertices);
GLuint texcoord_vbo = fill_vertex_attribute(glsl_program_num, "texcoord", 2, GL_FLOAT, sizeof(vertices), vertices); // Same as vertices.
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
check_error();
cleanup_vertex_attribute(glsl_program_num, "position", position_vbo);