]> git.sesse.net Git - movit/blob - ycbcr_input.cpp
Support chaining certain effects after compute shaders.
[movit] / ycbcr_input.cpp
1 #include <Eigen/Core>
2 #include <Eigen/LU>
3 #include <epoxy/gl.h>
4 #include <assert.h>
5 #include <stdio.h>
6 #include <string.h>
7
8 #include "effect_util.h"
9 #include "resource_pool.h"
10 #include "util.h"
11 #include "ycbcr.h"
12 #include "ycbcr_input.h"
13
14 using namespace Eigen;
15 using namespace std;
16
17 namespace movit {
18
19 YCbCrInput::YCbCrInput(const ImageFormat &image_format,
20                        const YCbCrFormat &ycbcr_format,
21                        unsigned width, unsigned height,
22                        YCbCrInputSplitting ycbcr_input_splitting,
23                        GLenum type)
24         : image_format(image_format),
25           ycbcr_format(ycbcr_format),
26           ycbcr_input_splitting(ycbcr_input_splitting),
27           needs_mipmaps(false),
28           type(type),
29           width(width),
30           height(height),
31           resource_pool(nullptr)
32 {
33         pbos[0] = pbos[1] = pbos[2] = 0;
34         texture_num[0] = texture_num[1] = texture_num[2] = 0;
35
36         set_width(width);
37         set_height(height);
38
39         pixel_data[0] = pixel_data[1] = pixel_data[2] = nullptr;
40         owns_texture[0] = owns_texture[1] = owns_texture[2] = false;
41
42         register_uniform_sampler2d("tex_y", &uniform_tex_y);
43
44         if (ycbcr_input_splitting == YCBCR_INPUT_INTERLEAVED) {
45                 num_channels = 1;
46                 assert(ycbcr_format.chroma_subsampling_x == 1);
47                 assert(ycbcr_format.chroma_subsampling_y == 1);
48         } else if (ycbcr_input_splitting == YCBCR_INPUT_SPLIT_Y_AND_CBCR) {
49                 num_channels = 2;
50                 register_uniform_sampler2d("tex_cbcr", &uniform_tex_cb);
51         } else {
52                 assert(ycbcr_input_splitting == YCBCR_INPUT_PLANAR);
53                 num_channels = 3;
54                 register_uniform_sampler2d("tex_cb", &uniform_tex_cb);
55                 register_uniform_sampler2d("tex_cr", &uniform_tex_cr);
56         }
57
58         register_int("needs_mipmaps", &needs_mipmaps);
59         register_uniform_mat3("inv_ycbcr_matrix", &uniform_ycbcr_matrix);
60         register_uniform_vec3("offset", uniform_offset);
61         register_uniform_vec2("cb_offset", (float *)&uniform_cb_offset);
62         register_uniform_vec2("cr_offset", (float *)&uniform_cr_offset);
63 }
64
65 YCbCrInput::~YCbCrInput()
66 {
67         for (unsigned channel = 0; channel < num_channels; ++channel) {
68                 possibly_release_texture(channel);
69         }
70 }
71
72 void YCbCrInput::set_gl_state(GLuint glsl_program_num, const string& prefix, unsigned *sampler_num)
73 {
74         compute_ycbcr_matrix(ycbcr_format, uniform_offset, &uniform_ycbcr_matrix, type);
75
76         uniform_cb_offset.x = compute_chroma_offset(
77                 ycbcr_format.cb_x_position, ycbcr_format.chroma_subsampling_x, widths[1]);
78         uniform_cb_offset.y = compute_chroma_offset(
79                 ycbcr_format.cb_y_position, ycbcr_format.chroma_subsampling_y, heights[1]);
80
81         uniform_cr_offset.x = compute_chroma_offset(
82                 ycbcr_format.cr_x_position, ycbcr_format.chroma_subsampling_x, widths[2]);
83         uniform_cr_offset.y = compute_chroma_offset(
84                 ycbcr_format.cr_y_position, ycbcr_format.chroma_subsampling_y, heights[2]);
85
86         for (unsigned channel = 0; channel < num_channels; ++channel) {
87                 glActiveTexture(GL_TEXTURE0 + *sampler_num + channel);
88                 check_error();
89
90                 if (texture_num[channel] == 0 && (pbos[channel] != 0 || pixel_data[channel] != nullptr)) {
91                         GLenum format, internal_format;
92                         if (channel == 0 && ycbcr_input_splitting == YCBCR_INPUT_INTERLEAVED) {
93                                 if (type == GL_UNSIGNED_INT_2_10_10_10_REV) {
94                                         format = GL_RGBA;
95                                         internal_format = GL_RGB10_A2;
96                                 } else if (type == GL_UNSIGNED_SHORT) {
97                                         format = GL_RGB;
98                                         internal_format = GL_RGB16;
99                                 } else {
100                                         assert(type == GL_UNSIGNED_BYTE);
101                                         format = GL_RGB;
102                                         internal_format = GL_RGB8;
103                                 }
104                         } else if (channel == 1 && ycbcr_input_splitting == YCBCR_INPUT_SPLIT_Y_AND_CBCR) {
105                                 format = GL_RG;
106                                 if (type == GL_UNSIGNED_SHORT) {
107                                         internal_format = GL_RG16;
108                                 } else {
109                                         assert(type == GL_UNSIGNED_BYTE);
110                                         internal_format = GL_RG8;
111                                 }
112                         } else {
113                                 format = GL_RED;
114                                 if (type == GL_UNSIGNED_SHORT) {
115                                         internal_format = GL_R16;
116                                 } else {
117                                         assert(type == GL_UNSIGNED_BYTE);
118                                         internal_format = GL_R8;
119                                 }
120                         }
121
122                         // (Re-)upload the texture.
123                         texture_num[channel] = resource_pool->create_2d_texture(internal_format, widths[channel], heights[channel]);
124                         glBindTexture(GL_TEXTURE_2D, texture_num[channel]);
125                         check_error();
126                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, needs_mipmaps ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
127                         check_error();
128                         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbos[channel]);
129                         check_error();
130                         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
131                         check_error();
132                         glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch[channel]);
133                         check_error();
134                         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, widths[channel], heights[channel], format, type, pixel_data[channel]);
135                         check_error();
136                         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
137                         check_error();
138                         if (needs_mipmaps) {
139                                 glGenerateMipmap(GL_TEXTURE_2D);
140                                 check_error();
141                         }
142                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
143                         check_error();
144                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
145                         check_error();
146                         owns_texture[channel] = true;
147                 } else {
148                         glBindTexture(GL_TEXTURE_2D, texture_num[channel]);
149                         check_error();
150                 }
151         }
152
153         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
154         check_error();
155
156         // Bind samplers.
157         uniform_tex_y = *sampler_num + 0;
158         if (ycbcr_input_splitting != YCBCR_INPUT_INTERLEAVED) {
159                 uniform_tex_cb = *sampler_num + 1;
160         }
161         if (ycbcr_input_splitting == YCBCR_INPUT_PLANAR) {
162                 uniform_tex_cr = *sampler_num + 2;
163         }
164
165         *sampler_num += num_channels;
166 }
167
168 string YCbCrInput::output_fragment_shader()
169 {
170         string frag_shader;
171
172         if (ycbcr_input_splitting == YCBCR_INPUT_INTERLEAVED) {
173                 frag_shader += "#define Y_CB_CR_SAME_TEXTURE 1\n";
174         } else if (ycbcr_input_splitting == YCBCR_INPUT_SPLIT_Y_AND_CBCR) {
175                 cb_cr_offsets_equal =
176                         (fabs(ycbcr_format.cb_x_position - ycbcr_format.cr_x_position) < 1e-6) &&
177                         (fabs(ycbcr_format.cb_y_position - ycbcr_format.cr_y_position) < 1e-6);
178                 char buf[256];
179                 snprintf(buf, sizeof(buf), "#define Y_CB_CR_SAME_TEXTURE 0\n#define CB_CR_SAME_TEXTURE 1\n#define CB_CR_OFFSETS_EQUAL %d\n",
180                         cb_cr_offsets_equal);
181                 frag_shader += buf;
182         } else {
183                 frag_shader += "#define Y_CB_CR_SAME_TEXTURE 0\n#define CB_CR_SAME_TEXTURE 0\n";
184         }
185
186         frag_shader += read_file("ycbcr_input.frag");
187         frag_shader += "#undef CB_CR_SAME_TEXTURE\n#undef Y_CB_CR_SAME_TEXTURE\n";
188         return frag_shader;
189 }
190
191 void YCbCrInput::change_ycbcr_format(const YCbCrFormat &ycbcr_format)
192 {
193         if (cb_cr_offsets_equal) {
194                 assert((fabs(ycbcr_format.cb_x_position - ycbcr_format.cr_x_position) < 1e-6) &&
195                        (fabs(ycbcr_format.cb_y_position - ycbcr_format.cr_y_position) < 1e-6));
196         }
197         if (ycbcr_input_splitting == YCBCR_INPUT_INTERLEAVED) {
198                 assert(ycbcr_format.chroma_subsampling_x == 1);
199                 assert(ycbcr_format.chroma_subsampling_y == 1);
200         }
201         this->ycbcr_format = ycbcr_format;
202 }
203
204 void YCbCrInput::invalidate_pixel_data()
205 {
206         for (unsigned channel = 0; channel < 3; ++channel) {
207                 possibly_release_texture(channel);
208         }
209 }
210
211 bool YCbCrInput::set_int(const std::string& key, int value)
212 {
213         if (key == "needs_mipmaps") {
214                 if (ycbcr_input_splitting != YCBCR_INPUT_INTERLEAVED && value != 0) {
215                         // We do not currently support this.
216                         return false;
217                 }
218         }
219         return Effect::set_int(key, value);
220 }
221
222 void YCbCrInput::possibly_release_texture(unsigned channel)
223 {
224         if (texture_num[channel] != 0 && owns_texture[channel]) {
225                 resource_pool->release_2d_texture(texture_num[channel]);
226                 texture_num[channel] = 0;
227                 owns_texture[channel] = false;
228         }
229 }
230
231 }  // namespace movit