: aspect_nom(aspect_nom),
aspect_denom(aspect_denom),
output_color_rgba(false),
- output_color_ycbcr(false),
+ num_output_color_ycbcr(0),
dither_effect(NULL),
+ ycbcr_conversion_effect_node(NULL),
intermediate_format(GL_RGBA16F),
intermediate_transformation(NO_FRAMEBUFFER_TRANSFORMATION),
num_dither_bits(0),
}
void EffectChain::add_ycbcr_output(const ImageFormat &format, OutputAlphaFormat alpha_format,
- const YCbCrFormat &ycbcr_format, YCbCrOutputSplitting output_splitting)
+ const YCbCrFormat &ycbcr_format, YCbCrOutputSplitting output_splitting,
+ GLenum output_type)
{
assert(!finalized);
- assert(!output_color_ycbcr);
+ assert(num_output_color_ycbcr < 2);
output_format = format;
output_alpha_format = alpha_format;
- output_color_ycbcr = true;
- output_ycbcr_format = ycbcr_format;
- output_ycbcr_splitting = output_splitting;
+
+ if (num_output_color_ycbcr == 1) {
+ // Check that the format is the same.
+ assert(output_ycbcr_format.luma_coefficients == ycbcr_format.luma_coefficients);
+ assert(output_ycbcr_format.full_range == ycbcr_format.full_range);
+ assert(output_ycbcr_format.num_levels == ycbcr_format.num_levels);
+ assert(output_ycbcr_format.chroma_subsampling_x == 1);
+ assert(output_ycbcr_format.chroma_subsampling_y == 1);
+ assert(output_ycbcr_type == output_type);
+ } else {
+ output_ycbcr_format = ycbcr_format;
+ output_ycbcr_type = output_type;
+ }
+ output_ycbcr_splitting[num_output_color_ycbcr++] = output_splitting;
assert(ycbcr_format.chroma_subsampling_x == 1);
assert(ycbcr_format.chroma_subsampling_y == 1);
void EffectChain::change_ycbcr_output_format(const YCbCrFormat &ycbcr_format)
{
- assert(output_color_ycbcr);
- assert(output_ycbcr_format.chroma_subsampling_x == ycbcr_format.chroma_subsampling_x);
- assert(output_ycbcr_format.chroma_subsampling_y == ycbcr_format.chroma_subsampling_y);
- assert(fabs(output_ycbcr_format.cb_x_position - ycbcr_format.cb_x_position) < 1e-3);
- assert(fabs(output_ycbcr_format.cb_y_position - ycbcr_format.cb_y_position) < 1e-3);
- assert(fabs(output_ycbcr_format.cr_x_position - ycbcr_format.cr_x_position) < 1e-3);
- assert(fabs(output_ycbcr_format.cr_y_position - ycbcr_format.cr_y_position) < 1e-3);
+ assert(num_output_color_ycbcr > 0);
+ assert(output_ycbcr_format.chroma_subsampling_x == 1);
+ assert(output_ycbcr_format.chroma_subsampling_y == 1);
output_ycbcr_format = ycbcr_format;
if (finalized) {
- // Find the YCbCrConversionEffect node. We don't store it to avoid
- // an unneeded ABI break (this can be fixed on next break).
- for (Node *node : nodes) {
- if (node->effect->effect_type_id() == "YCbCrConversionEffect") {
- YCbCrConversionEffect *effect = (YCbCrConversionEffect *)(node->effect);
- effect->change_output_format(ycbcr_format);
- }
- }
+ YCbCrConversionEffect *effect = (YCbCrConversionEffect *)(ycbcr_conversion_effect_node->effect);
+ effect->change_output_format(ycbcr_format);
}
}
// If we're the last phase, add the right #defines for Y'CbCr multi-output as needed.
vector<string> frag_shader_outputs; // In order.
- if (phase->output_node->outgoing_links.empty() && output_color_ycbcr) {
- switch (output_ycbcr_splitting) {
+ if (phase->output_node->outgoing_links.empty() && num_output_color_ycbcr > 0) {
+ switch (output_ycbcr_splitting[0]) {
case YCBCR_OUTPUT_INTERLEAVED:
// No #defines set.
frag_shader_outputs.push_back("FragColor");
assert(false);
}
+ if (num_output_color_ycbcr > 1) {
+ switch (output_ycbcr_splitting[1]) {
+ case YCBCR_OUTPUT_INTERLEAVED:
+ frag_shader += "#define SECOND_YCBCR_OUTPUT_INTERLEAVED 1\n";
+ frag_shader_outputs.push_back("YCbCr2");
+ break;
+ case YCBCR_OUTPUT_SPLIT_Y_AND_CBCR:
+ frag_shader += "#define SECOND_YCBCR_OUTPUT_SPLIT_Y_AND_CBCR 1\n";
+ frag_shader_outputs.push_back("Y2");
+ frag_shader_outputs.push_back("Chroma2");
+ break;
+ case YCBCR_OUTPUT_PLANAR:
+ frag_shader += "#define SECOND_YCBCR_OUTPUT_PLANAR 1\n";
+ frag_shader_outputs.push_back("Y2");
+ frag_shader_outputs.push_back("Cb2");
+ frag_shader_outputs.push_back("Cr2");
+ break;
+ default:
+ assert(false);
+ }
+ }
+
if (output_color_rgba) {
// Note: Needs to come in the header, because not only the
// output needs to see it (YCbCrConversionEffect and DitherEffect
// gamma-encoded data.
void EffectChain::add_ycbcr_conversion_if_needed()
{
- assert(output_color_rgba || output_color_ycbcr);
- if (!output_color_ycbcr) {
+ assert(output_color_rgba || num_output_color_ycbcr > 0);
+ if (num_output_color_ycbcr == 0) {
return;
}
Node *output = find_output_node();
- Node *ycbcr = add_node(new YCbCrConversionEffect(output_ycbcr_format));
- connect_nodes(output, ycbcr);
+ ycbcr_conversion_effect_node = add_node(new YCbCrConversionEffect(output_ycbcr_format, output_ycbcr_type));
+ connect_nodes(output, ycbcr_conversion_effect_node);
}
// If the user has requested dither, add a DitherEffect right at the end
check_error();
glDisable(GL_DITHER);
check_error();
- glEnable(GL_FRAMEBUFFER_SRGB);
+
+ const bool final_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB);
check_error();
+ bool current_srgb = final_srgb;
// Save original viewport.
GLuint x = 0, y = 0;
glDepthMask(GL_FALSE);
check_error();
- // Generate a VAO that will be used during the entire execution,
- // and bind the VBO, since it contains all the data.
- GLuint vao;
- glGenVertexArrays(1, &vao);
- check_error();
- glBindVertexArray(vao);
- check_error();
- glBindBuffer(GL_ARRAY_BUFFER, vbo);
- check_error();
- set<GLint> bound_attribute_indices;
-
set<Phase *> generated_mipmaps;
// We choose the simplest option of having one texture per output,
CHECK(dither_effect->set_int("output_height", height));
}
}
- execute_phase(phase, phase_num == phases.size() - 1, &bound_attribute_indices, &output_textures, &generated_mipmaps);
+ bool last_phase = (phase_num == phases.size() - 1);
+
+ // Enable sRGB rendering for intermediates in case we are
+ // rendering to an sRGB format.
+ bool needs_srgb = last_phase ? final_srgb : true;
+ if (needs_srgb && !current_srgb) {
+ glEnable(GL_FRAMEBUFFER_SRGB);
+ check_error();
+ current_srgb = true;
+ } else if (!needs_srgb && current_srgb) {
+ glDisable(GL_FRAMEBUFFER_SRGB);
+ check_error();
+ current_srgb = true;
+ }
+
+ execute_phase(phase, last_phase, &output_textures, &generated_mipmaps);
if (do_phase_timing) {
glEndQuery(GL_TIME_ELAPSED);
}
check_error();
glBindVertexArray(0);
check_error();
- glDeleteVertexArrays(1, &vao);
- check_error();
if (do_phase_timing) {
// Get back the timer queries.
}
void EffectChain::execute_phase(Phase *phase, bool last_phase,
- set<GLint> *bound_attribute_indices,
map<Phase *, GLuint> *output_textures,
set<Phase *> *generated_mipmaps)
{
// from there.
setup_uniforms(phase);
- // Clean up old attributes if they are no longer needed.
- for (set<GLint>::iterator attr_it = bound_attribute_indices->begin();
- attr_it != bound_attribute_indices->end(); ) {
- if (phase->attribute_indexes.count(*attr_it) == 0) {
- glDisableVertexAttribArray(*attr_it);
- check_error();
- bound_attribute_indices->erase(attr_it++);
- } else {
- ++attr_it;
- }
- }
-
- // Set up the new attributes, if needed.
- for (set<GLint>::iterator attr_it = phase->attribute_indexes.begin();
- attr_it != phase->attribute_indexes.end();
- ++attr_it) {
- if (bound_attribute_indices->count(*attr_it) == 0) {
- glEnableVertexAttribArray(*attr_it);
- check_error();
- glVertexAttribPointer(*attr_it, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
- check_error();
- bound_attribute_indices->insert(*attr_it);
- }
- }
+ // Bind the vertex data.
+ GLuint vao = resource_pool->create_vec2_vao(phase->attribute_indexes, vbo);
+ glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
check_error();
}
resource_pool->unuse_glsl_program(instance_program_num);
+ resource_pool->release_vec2_vao(vao);
if (!last_phase) {
resource_pool->release_fbo(fbo);