#include <stack>
#include <utility>
#include <vector>
+#include <Eigen/Core>
#include "alpha_division_effect.h"
#include "alpha_multiplication_effect.h"
#include "dither_effect.h"
#include "effect.h"
#include "effect_chain.h"
+#include "effect_util.h"
#include "gamma_compression_effect.h"
#include "gamma_expansion_effect.h"
#include "init.h"
#include "input.h"
#include "resource_pool.h"
#include "util.h"
+#include "ycbcr_conversion_effect.h"
+using namespace Eigen;
using namespace std;
namespace movit {
assert(!finalized);
output_format = format;
output_alpha_format = alpha_format;
+ output_color_type = OUTPUT_COLOR_RGB;
+}
+
+void EffectChain::add_ycbcr_output(const ImageFormat &format, OutputAlphaFormat alpha_format,
+ const YCbCrFormat &ycbcr_format)
+{
+ assert(!finalized);
+ output_format = format;
+ output_alpha_format = alpha_format;
+ output_color_type = OUTPUT_COLOR_YCBCR;
+ output_ycbcr_format = ycbcr_format;
+
+ assert(ycbcr_format.chroma_subsampling_x == 1);
+ assert(ycbcr_format.chroma_subsampling_y == 1);
}
Node *EffectChain::add_node(Effect *effect)
void EffectChain::compile_glsl_program(Phase *phase)
{
- string frag_shader = read_version_dependent_file("header", "frag");
+ string frag_shader_header = read_version_dependent_file("header", "frag");
+ string frag_shader = "";
// Create functions for all the texture inputs that we need.
for (unsigned i = 0; i < phase->inputs.size(); ++i) {
frag_shader += "\n";
}
+ // Give each effect in the phase its own ID.
for (unsigned i = 0; i < phase->effects.size(); ++i) {
Node *node = phase->effects[i];
char effect_id[256];
sprintf(effect_id, "eff%u", i);
phase->effect_ids.insert(make_pair(node, effect_id));
+ }
+ for (unsigned i = 0; i < phase->effects.size(); ++i) {
+ Node *node = phase->effects[i];
+ const string effect_id = phase->effect_ids[node];
if (node->incoming_links.size() == 1) {
frag_shader += string("#define INPUT ") + phase->effect_ids[node->incoming_links[0]] + "\n";
} else {
frag_shader += "\n";
frag_shader += string("#define FUNCNAME ") + effect_id + "\n";
- frag_shader += replace_prefix(node->effect->output_convenience_uniforms(), effect_id);
frag_shader += replace_prefix(node->effect->output_fragment_shader(), effect_id);
frag_shader += "#undef PREFIX\n";
frag_shader += "#undef FUNCNAME\n";
frag_shader += string("#define INPUT ") + phase->effect_ids[phase->effects.back()] + "\n";
frag_shader.append(read_version_dependent_file("footer", "frag"));
+ // Collect uniforms from all effects and output them. Note that this needs
+ // to happen after output_fragment_shader(), even though the uniforms come
+ // before in the output source, since output_fragment_shader() is allowed
+ // to register new uniforms (e.g. arrays that are of unknown length until
+ // finalization time).
+ // TODO: Make a uniform block for platforms that support it.
+ string frag_shader_uniforms = "";
+ for (unsigned i = 0; i < phase->effects.size(); ++i) {
+ Node *node = phase->effects[i];
+ Effect *effect = node->effect;
+ const string effect_id = phase->effect_ids[node];
+ for (unsigned j = 0; j < effect->uniforms_bool.size(); ++j) {
+ phase->uniforms_bool.push_back(effect->uniforms_bool[j]);
+ phase->uniforms_bool.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform bool ") + effect_id
+ + "_" + effect->uniforms_bool[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_int.size(); ++j) {
+ phase->uniforms_int.push_back(effect->uniforms_int[j]);
+ phase->uniforms_int.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform int ") + effect_id
+ + "_" + effect->uniforms_int[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_sampler2d.size(); ++j) {
+ phase->uniforms_int.push_back(effect->uniforms_sampler2d[j]);
+ phase->uniforms_int.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform sampler2D ") + effect_id
+ + "_" + effect->uniforms_sampler2d[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_float.size(); ++j) {
+ phase->uniforms_float.push_back(effect->uniforms_float[j]);
+ phase->uniforms_float.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform float ") + effect_id
+ + "_" + effect->uniforms_float[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec2.size(); ++j) {
+ phase->uniforms_vec2.push_back(effect->uniforms_vec2[j]);
+ phase->uniforms_vec2.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec2 ") + effect_id
+ + "_" + effect->uniforms_vec2[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec3.size(); ++j) {
+ phase->uniforms_vec3.push_back(effect->uniforms_vec3[j]);
+ phase->uniforms_vec3.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec3 ") + effect_id
+ + "_" + effect->uniforms_vec3[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec4.size(); ++j) {
+ phase->uniforms_vec4.push_back(effect->uniforms_vec4[j]);
+ phase->uniforms_vec4.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform vec4 ") + effect_id
+ + "_" + effect->uniforms_vec4[j].name + ";\n";
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec2_array.size(); ++j) {
+ char buf[256];
+ phase->uniforms_vec2.push_back(effect->uniforms_vec2_array[j]);
+ phase->uniforms_vec2.back().prefix = effect_id;
+ snprintf(buf, sizeof(buf), "uniform vec2 %s_%s[%d];\n",
+ effect_id.c_str(), effect->uniforms_vec2_array[j].name.c_str(),
+ int(effect->uniforms_vec2_array[j].num_values));
+ frag_shader_uniforms += buf;
+ }
+ for (unsigned j = 0; j < effect->uniforms_vec4_array.size(); ++j) {
+ char buf[256];
+ phase->uniforms_vec4.push_back(effect->uniforms_vec4_array[j]);
+ phase->uniforms_vec4.back().prefix = effect_id;
+ snprintf(buf, sizeof(buf), "uniform vec4 %s_%s[%d];\n",
+ effect_id.c_str(), effect->uniforms_vec4_array[j].name.c_str(),
+ int(effect->uniforms_vec4_array[j].num_values));
+ frag_shader_uniforms += buf;
+ }
+ for (unsigned j = 0; j < effect->uniforms_mat3.size(); ++j) {
+ phase->uniforms_mat3.push_back(effect->uniforms_mat3[j]);
+ phase->uniforms_mat3.back().prefix = effect_id;
+ frag_shader_uniforms += string("uniform mat3 ") + effect_id
+ + "_" + effect->uniforms_mat3[j].name + ";\n";
+ }
+ }
+
+ frag_shader = frag_shader_header + frag_shader_uniforms + frag_shader;
+
string vert_shader = read_version_dependent_file("vs", "vert");
phase->glsl_program_num = resource_pool->compile_glsl_program(vert_shader, frag_shader);
+
+ // Collect the resulting program numbers for each uniform.
+ for (unsigned i = 0; i < phase->uniforms_bool.size(); ++i) {
+ Uniform<bool> &uniform = phase->uniforms_bool[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_int.size(); ++i) {
+ Uniform<int> &uniform = phase->uniforms_int[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_float.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_float[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec2.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec2[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec3.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec3[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_vec4.size(); ++i) {
+ Uniform<float> &uniform = phase->uniforms_vec4[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
+ for (unsigned i = 0; i < phase->uniforms_mat3.size(); ++i) {
+ Uniform<Matrix3d> &uniform = phase->uniforms_mat3[i];
+ uniform.location = get_uniform_location(phase->glsl_program_num, uniform.prefix, uniform.name);
+ }
}
// Construct GLSL programs, starting at the given effect and following
connect_nodes(output, conversion);
}
}
+
+// If the user has requested Y'CbCr output, we need to do this conversion
+// _after_ GammaCompressionEffect etc., but before dither (see below).
+// This is because Y'CbCr, with the exception of a special optional mode
+// in Rec. 2020 (which we currently don't support), is defined to work on
+// gamma-encoded data.
+void EffectChain::add_ycbcr_conversion_if_needed()
+{
+ assert(output_color_type == OUTPUT_COLOR_RGB || output_color_type == OUTPUT_COLOR_YCBCR);
+ if (output_color_type != OUTPUT_COLOR_YCBCR) {
+ return;
+ }
+ Node *output = find_output_node();
+ Node *ycbcr = add_node(new YCbCrConversionEffect(output_ycbcr_format));
+ connect_nodes(output, ycbcr);
+}
// If the user has requested dither, add a DitherEffect right at the end
// (after GammaCompressionEffect etc.). This needs to be done after everything else,
fix_internal_gamma_by_asking_inputs(15);
fix_internal_gamma_by_inserting_nodes(16);
- output_dot("step17-before-dither.dot");
+ output_dot("step17-before-ycbcr.dot");
+ add_ycbcr_conversion_if_needed();
+ output_dot("step18-before-dither.dot");
add_dither_if_needed();
- output_dot("step18-final.dot");
+ output_dot("step19-final.dot");
// Construct all needed GLSL programs, starting at the output.
// We need to keep track of which effects have already been computed,
map<Node *, Phase *> completed_effects;
construct_phase(find_output_node(), &completed_effects);
- output_dot("step19-split-to-phases.dot");
+ output_dot("step20-split-to-phases.dot");
assert(phases[0]->inputs.empty());
}
}
+ // Uniforms need to come after set_gl_state(), since they can be updated
+ // from there.
+ setup_uniforms(phase);
+
// Now draw!
float vertices[] = {
0.0f, 2.0f,
check_error();
}
+void EffectChain::setup_uniforms(Phase *phase)
+{
+ // TODO: Use UBO blocks.
+ for (size_t i = 0; i < phase->uniforms_bool.size(); ++i) {
+ const Uniform<bool> &uniform = phase->uniforms_bool[i];
+ assert(uniform.num_values == 1);
+ if (uniform.location != -1) {
+ glUniform1i(uniform.location, *uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_int.size(); ++i) {
+ const Uniform<int> &uniform = phase->uniforms_int[i];
+ if (uniform.location != -1) {
+ glUniform1iv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_float.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_float[i];
+ if (uniform.location != -1) {
+ glUniform1fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec2.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec2[i];
+ if (uniform.location != -1) {
+ glUniform2fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec3.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec3[i];
+ if (uniform.location != -1) {
+ glUniform3fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_vec4.size(); ++i) {
+ const Uniform<float> &uniform = phase->uniforms_vec4[i];
+ if (uniform.location != -1) {
+ glUniform4fv(uniform.location, uniform.num_values, uniform.value);
+ }
+ }
+ for (size_t i = 0; i < phase->uniforms_mat3.size(); ++i) {
+ const Uniform<Matrix3d> &uniform = phase->uniforms_mat3[i];
+ assert(uniform.num_values == 1);
+ if (uniform.location != -1) {
+ // Convert to float (GLSL has no double matrices).
+ float matrixf[9];
+ for (unsigned y = 0; y < 3; ++y) {
+ for (unsigned x = 0; x < 3; ++x) {
+ matrixf[y + x * 3] = (*uniform.value)(y, x);
+ }
+ }
+ glUniformMatrix3fv(uniform.location, 1, GL_FALSE, matrixf);
+ }
+ }
+}
+
void EffectChain::setup_rtt_sampler(GLuint glsl_program_num, int sampler_num, const string &effect_id, bool use_mipmaps)
{
glActiveTexture(GL_TEXTURE0 + sampler_num);