]> git.sesse.net Git - nageru/blob - nageru/v210_converter.cpp
Fix a dangling reference (found by GCC 14).
[nageru] / nageru / v210_converter.cpp
1 #include "v210_converter.h"
2
3 #include <stdio.h>
4 #include <epoxy/gl.h>
5 #include <movit/util.h>
6 #include <string>
7
8 using namespace std;
9
10 v210Converter::~v210Converter()
11 {
12         for (const auto &shader : shaders) {
13                 glDeleteProgram(shader.second.glsl_program_num);
14                 check_error();
15         }
16 }
17
18 bool v210Converter::has_hardware_support()
19 {
20         // We don't have a GLES version of this, although GLSL ES 3.1 supports
21         // compute shaders. Note that GLSL ES has some extra restrictions,
22         // like requiring that the images are allocated with glTexStorage*(),
23         // or that binding= is effectively mandatory.
24         if (!epoxy_is_desktop_gl()) {
25                 return false;
26         }
27         if (epoxy_gl_version() >= 43) {
28                 // Supports compute shaders natively.
29                 return true;
30         }
31         return epoxy_has_gl_extension("GL_ARB_compute_shader") &&
32                epoxy_has_gl_extension("GL_ARB_shader_image_load_store");
33 }
34
35 void v210Converter::precompile_shader(unsigned width)
36 {
37         unsigned num_local_work_groups = (width + 5) / 6;
38         if (shaders.count(num_local_work_groups)) {
39                 // Already exists.
40                 return;
41         }
42
43         char buf[16];
44         snprintf(buf, sizeof(buf), "%u", num_local_work_groups);
45         string shader_src = R"(#version 150
46 #extension GL_ARB_compute_shader : enable
47 #extension GL_ARB_shader_image_load_store : enable
48 layout(local_size_x = )" + string(buf) + R"() in;
49 layout(rgb10_a2) uniform restrict readonly image2D inbuf;
50 layout(rgb10_a2) uniform restrict writeonly image2D outbuf;
51 uniform int max_cbcr_x;
52 shared vec2 cbcr[gl_WorkGroupSize.x * 3u];
53
54 void main()
55 {
56         int xb = int(gl_LocalInvocationID.x);  // X block.
57         int y = int(gl_GlobalInvocationID.y);  // Y (actual line).
58
59         // Load our pixel group, containing data for six pixels.
60         vec3 indata[4];
61         for (int i = 0; i < 4; ++i) {
62                 indata[i] = imageLoad(inbuf, ivec2(xb * 4 + i, y)).xyz;
63         }
64
65         // Decode Cb and Cr to shared memory, because neighboring blocks need it for interpolation.
66         cbcr[xb * 3 + 0] = indata[0].xz;
67         cbcr[xb * 3 + 1] = vec2(indata[1].y, indata[2].x);
68         cbcr[xb * 3 + 2] = vec2(indata[2].z, indata[3].y);
69         memoryBarrierShared();
70
71         float pix_y[6];
72         pix_y[0] = indata[0].y;
73         pix_y[1] = indata[1].x;
74         pix_y[2] = indata[1].z;
75         pix_y[3] = indata[2].y;
76         pix_y[4] = indata[3].x;
77         pix_y[5] = indata[3].z;
78
79         barrier();
80
81         // Interpolate the missing Cb/Cr pixels, taking care not to read past the end of the screen
82         // for pixels that we use for interpolation.
83         vec2 pix_cbcr[7];
84         pix_cbcr[0] = indata[0].xz;
85         pix_cbcr[2] = cbcr[min(xb * 3 + 1, max_cbcr_x)];
86         pix_cbcr[4] = cbcr[min(xb * 3 + 2, max_cbcr_x)];
87         pix_cbcr[6] = cbcr[min(xb * 3 + 3, max_cbcr_x)];
88         pix_cbcr[1] = 0.5 * (pix_cbcr[0] + pix_cbcr[2]);
89         pix_cbcr[3] = 0.5 * (pix_cbcr[2] + pix_cbcr[4]);
90         pix_cbcr[5] = 0.5 * (pix_cbcr[4] + pix_cbcr[6]);
91
92         // Write the decoded pixels to the destination texture.
93         for (int i = 0; i < 6; ++i) {
94                 vec4 outdata = vec4(pix_y[i], pix_cbcr[i].x, pix_cbcr[i].y, 1.0f);
95                 imageStore(outbuf, ivec2(xb * 6 + i, y), outdata);
96         }
97 }
98 )";
99
100         Shader shader;
101
102         GLuint shader_num = movit::compile_shader(shader_src, GL_COMPUTE_SHADER);
103         check_error();
104         shader.glsl_program_num = glCreateProgram();
105         check_error();
106         glAttachShader(shader.glsl_program_num, shader_num);
107         check_error();
108         glLinkProgram(shader.glsl_program_num);
109         check_error();
110
111         GLint success;
112         glGetProgramiv(shader.glsl_program_num, GL_LINK_STATUS, &success);
113         check_error();
114         if (success == GL_FALSE) {
115                 GLchar error_log[1024] = {0};
116                 glGetProgramInfoLog(shader.glsl_program_num, 1024, nullptr, error_log);
117                 fprintf(stderr, "Error linking program: %s\n", error_log);
118                 abort();
119         }
120
121         shader.max_cbcr_x_pos = glGetUniformLocation(shader.glsl_program_num, "max_cbcr_x");
122         check_error();
123         shader.inbuf_pos = glGetUniformLocation(shader.glsl_program_num, "inbuf");
124         check_error();
125         shader.outbuf_pos = glGetUniformLocation(shader.glsl_program_num, "outbuf");
126         check_error();
127
128         shaders.emplace(num_local_work_groups, shader);
129 }
130
131 void v210Converter::convert(GLuint tex_src, GLuint tex_dst, unsigned width, unsigned height)
132 {
133         precompile_shader(width);
134         unsigned num_local_work_groups = (width + 5) / 6;
135         const Shader &shader = shaders[num_local_work_groups];
136
137         glUseProgram(shader.glsl_program_num);
138         check_error();
139         glUniform1i(shader.max_cbcr_x_pos, width / 2 - 1);
140         check_error();
141
142         // Bind the textures.
143         glUniform1i(shader.inbuf_pos, 0);
144         check_error();
145         glUniform1i(shader.outbuf_pos, 1);
146         check_error();
147         glBindImageTexture(0, tex_src, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGB10_A2);
148         check_error();
149         glBindImageTexture(1, tex_dst, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGB10_A2);
150         check_error();
151
152         // Actually run the shader.
153         glDispatchCompute(1, height, 1);
154         check_error();
155
156         glUseProgram(0);
157         check_error();
158 }