]> git.sesse.net Git - movit/blobdiff - deinterlace_effect.cpp
Implement a compute shdaer version of DeinterlaceEffect.
[movit] / deinterlace_effect.cpp
index 8d9a96c344b647cb7c9018b51920779c891e2354..078983afabdf896e63de9fb5579bf0c8e816983a 100644 (file)
@@ -1,6 +1,8 @@
 #include <epoxy/gl.h>
 
 #include "deinterlace_effect.h"
+#include "effect_chain.h"
+#include "init.h"
 #include "util.h"
 
 using namespace std;
@@ -12,13 +14,18 @@ DeinterlaceEffect::DeinterlaceEffect()
          current_field_position(TOP),
          num_lines(1080)
 {
-       register_int("enable_spatial_interlacing_check", (int *)&enable_spatial_interlacing_check);
-       register_int("current_field_position", (int *)&current_field_position);
-       register_uniform_float("num_lines", &num_lines);
-       register_uniform_float("inv_width", &inv_width);
-       register_uniform_float("self_offset", &self_offset);
-       register_uniform_float_array("current_offset", current_offset, 2);
-       register_uniform_float_array("other_offset", other_offset, 3);
+       if (movit_compute_shaders_supported) {
+               compute_effect_owner.reset(new DeinterlaceComputeEffect);
+               compute_effect = compute_effect_owner.get();
+       } else {
+               register_int("enable_spatial_interlacing_check", (int *)&enable_spatial_interlacing_check);
+               register_int("current_field_position", (int *)&current_field_position);
+               register_uniform_float("num_lines", &num_lines);
+               register_uniform_float("inv_width", &inv_width);
+               register_uniform_float("self_offset", &self_offset);
+               register_uniform_float_array("current_offset", current_offset, 2);
+               register_uniform_float_array("other_offset", other_offset, 3);
+       }
 }
 
 string DeinterlaceEffect::output_fragment_shader()
@@ -32,6 +39,25 @@ string DeinterlaceEffect::output_fragment_shader()
        return frag_shader;
 }
 
+void DeinterlaceEffect::rewrite_graph(EffectChain *graph, Node *self)
+{
+       if (compute_effect != nullptr) {
+               Node *compute_node = graph->add_node(compute_effect_owner.release());
+               graph->replace_receiver(self, compute_node);
+               graph->replace_sender(self, compute_node);
+               self->disabled = true;
+       }
+}
+
+bool DeinterlaceEffect::set_int(const std::string &key, int value)
+{
+       if (compute_effect != nullptr) {
+               return compute_effect->set_int(key, value);
+       } else {
+               return Effect::set_int(key, value);
+       }
+}
+
 void DeinterlaceEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
 {
        assert(input_num >= 0 && input_num < 5);
@@ -125,4 +151,96 @@ void DeinterlaceEffect::set_gl_state(GLuint glsl_program_num, const string &pref
        other_offset[2] = center_offset + 1.0 / heights[0];
 }
 
+// Implementation of DeinterlaceComputeEffect.
+
+DeinterlaceComputeEffect::DeinterlaceComputeEffect()
+       : enable_spatial_interlacing_check(true),
+         current_field_position(TOP)
+{
+       register_int("enable_spatial_interlacing_check", (int *)&enable_spatial_interlacing_check);
+       register_int("current_field_position", (int *)&current_field_position);
+       register_uniform_float("inv_width", &inv_width);
+       register_uniform_float("inv_height", &inv_height);
+       register_uniform_float("current_field_vertical_offset", &current_field_vertical_offset);
+}
+
+string DeinterlaceComputeEffect::output_fragment_shader()
+{
+       char buf[256];
+       snprintf(buf, sizeof(buf), "#define YADIF_ENABLE_SPATIAL_INTERLACING_CHECK %d\n",
+               enable_spatial_interlacing_check);
+       string frag_shader = buf;
+
+       frag_shader += read_file("deinterlace_effect.comp");
+       return frag_shader;
+}
+
+void DeinterlaceComputeEffect::inform_input_size(unsigned input_num, unsigned width, unsigned height)
+{
+       assert(input_num >= 0 && input_num < 5);
+       widths[input_num] = width;
+       heights[input_num] = height;
+}
+
+void DeinterlaceComputeEffect::get_output_size(unsigned *width, unsigned *height,
+                                        unsigned *virtual_width, unsigned *virtual_height) const
+{
+       assert(widths[0] == widths[1]);
+       assert(widths[1] == widths[2]);
+       assert(widths[2] == widths[3]);
+       assert(widths[3] == widths[4]);
+       assert(heights[0] == heights[1]);
+       assert(heights[1] == heights[2]);
+       assert(heights[2] == heights[3]);
+       assert(heights[3] == heights[4]);
+       *width = *virtual_width = widths[0];
+       *height = *virtual_height = heights[0] * 2;
+}
+
+void DeinterlaceComputeEffect::set_gl_state(GLuint glsl_program_num, const string &prefix, unsigned *sampler_num)
+{
+       Effect::set_gl_state(glsl_program_num, prefix, sampler_num);
+
+       inv_width = 1.0 / widths[0];
+       inv_height = 1.0 / heights[0];
+
+       // For the compute shader, we need to load a block of pixels. Marking off the
+       // ones we are supposed to interpolate (looking only at one column):
+       //
+       //  field_pos==0            field_pos==1
+       //
+       //  6     x      ↑          6     .      ↑
+       //  6     .      |          6     x      |
+       //  5     x      |          5     .      |
+       //  5     .      |          5     x      |
+       //  4     x      |          4     .      |
+       //  4     .      |          4     x      |
+       //  3     x      | y        3     o      | y
+       //  3     o      |          3     x      |
+       //  2     x      |          2     o      |
+       //  2     o      |          2     x      |
+       //  1     x      |          1     .      |
+       //  1     .      |          1     x      |
+       //  0     x      |          0     .      |
+       //  0     .      |          0     x      |
+       //
+       // So if we are to compute e.g. output samples [2,4), we load input samples
+       // [1,3] for TFF and samples [2,4] for BFF.
+       if (current_field_position == 0) {
+               current_field_vertical_offset = -1.0 / heights[0];
+       } else {
+               current_field_vertical_offset =  0.0 / heights[0];
+       }
+}
+
+void DeinterlaceComputeEffect::get_compute_dimensions(unsigned output_width, unsigned output_height,
+                                               unsigned *x, unsigned *y, unsigned *z) const
+{
+       // Each workgroup outputs 8x32 pixels (see GROUP_W and GROUP_H in the shader),
+       // so figure out the number of groups by simply rounding up.
+       *x = (output_width + 7) / 8;
+       *y = (output_height + 31) / 32;
+       *z = 1;
+}
+
 }  // namespace movit