blob: 7365b0e661e4a23a1c906f3776ff5c43ba571e1b [file] [log] [blame]
Alex Vakulenkoe4eec202017-01-27 14:41:04 -08001#include "include/private/dvr/graphics/blur.h"
2
3// clang-format off
4#include <EGL/egl.h>
5#include <EGL/eglext.h>
6#include <GLES/gl.h>
7#include <GLES/glext.h>
8#include <GLES2/gl2.h>
9// clang-format on
10#include <hardware/gralloc.h>
11
12#include <string>
13
14#include <base/logging.h>
15#include <base/strings/string_number_conversions.h>
16#include <private/dvr/debug.h>
17#include <private/dvr/graphics/egl_image.h>
18#include <private/dvr/graphics/shader_program.h>
19#include <private/dvr/types.h>
20
21#define POSITION_ATTR 0
22#define OFFSET_BINDING 0
23#define SAMPLER_BINDING 1
24
25namespace {
26
27std::string screen_space_vert_shader = SHADER0([]() { // NOLINT
28 layout(location = 0) in vec4 position_uv;
29 out vec2 texCoords;
30
31 void main() {
32 gl_Position = vec4(position_uv.xy, 0.0, 1.0);
33 texCoords = position_uv.zw;
34 }
35});
36
37std::string kawase_blur_frag_shader = SHADER0([]() { // NOLINT
38 precision mediump float;
39 layout(location = 0) uniform vec2 uSampleOffsets[4];
40 layout(binding = 1) uniform APP_SAMPLER_2D uTexture;
41 in vec2 texCoords;
42 out vec4 color;
43
44 void main() {
45 vec2 tc = texCoords;
46 color = texture(uTexture, tc + uSampleOffsets[0]);
47 color += texture(uTexture, tc + uSampleOffsets[1]);
48 color += texture(uTexture, tc + uSampleOffsets[2]);
49 color += texture(uTexture, tc + uSampleOffsets[3]);
50 color *= (1.0 / 4.0);
51 }
52});
53
54constexpr int g_num_samples = 4;
55
56// Modified kernel patterns originally based on:
57// https://software.intel.com/en-us/blogs/2014/07/15/an-investigation-of-fast-real-time-gpu-based-image-blur-algorithms
58// The modification is left and right rotations of the 3rd and 4th patterns.
59const android::dvr::vec2 g_blur_samples[][g_num_samples] = {
60 {{0.5f, 0.5f}, {-0.5f, 0.5f}, {0.5f, -0.5f}, {-0.5f, -0.5f}},
61 {{1.5f, 1.5f}, {-1.5f, 1.5f}, {1.5f, -1.5f}, {-1.5f, -1.5f}},
62 {{2.5f, 1.5f}, {-1.5f, 2.5f}, {1.5f, -2.5f}, {-2.5f, -1.5f}},
63 {{2.5f, 3.5f}, {-3.5f, 2.5f}, {3.5f, -2.5f}, {-2.5f, -3.5f}},
64 // Last pass disabled, because it is more blur than we need.
65 // {{3.5f, 3.5f}, {-3.5f, 3.5f}, {3.5f, -3.5f}, {-3.5f, -3.5f}},
66};
67
68} // namespace
69
70namespace android {
71namespace dvr {
72
73Blur::Blur(int w, int h, GLuint source_texture, GLint source_texture_target,
74 GLint target_texture_target, bool is_external, EGLDisplay display,
75 int num_blur_outputs)
76 : display_(display),
77 target_texture_target_(target_texture_target),
78 width_(w),
79 height_(h),
80 fbo_q_free_(1 + num_blur_outputs) {
81 CHECK(num_blur_outputs > 0);
82 source_fbo_ =
83 CreateFbo(w, h, source_texture, source_texture_target, is_external);
84 fbo_half_ = CreateFbo(w / 2, h / 2, 0, target_texture_target, is_external);
85 // Create the quarter res fbos.
86 for (size_t i = 0; i < fbo_q_free_.GetCapacity(); ++i)
87 fbo_q_.push_back(
88 CreateFbo(w / 4, h / 4, 0, target_texture_target, is_external));
89 scale_ = 1.0f;
90}
91
92Blur::~Blur() {
93 glFinish();
94 glDeleteFramebuffers(1, &source_fbo_.fbo);
95 glDeleteFramebuffers(1, &fbo_half_.fbo);
96 // Note: source_fbo_.texture is not deleted because it was created externally.
97 glDeleteTextures(1, &fbo_half_.texture);
98 if (fbo_half_.egl_image)
99 eglDestroyImageKHR(display_, fbo_half_.egl_image);
100 for (const auto& fbo : fbo_q_) {
101 glDeleteFramebuffers(1, &fbo.fbo);
102 glDeleteTextures(1, &fbo.texture);
103 if (fbo.egl_image)
104 eglDestroyImageKHR(display_, fbo.egl_image);
105 }
106 CHECK_GL();
107}
108
109void Blur::StartFrame() {
110 fbo_q_free_.Clear();
111 for (const auto& fbo : fbo_q_)
112 fbo_q_free_.Append(fbo);
113}
114
115GLuint Blur::DrawBlur(GLuint source_texture) {
116 CHECK(fbo_q_free_.GetSize() >= 2);
117
118 // Downsample to half w x half h.
119 glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fbo_.fbo);
120 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_half_.fbo);
121 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
122 target_texture_target_, source_texture, 0);
123 glBlitFramebuffer(0, 0, width_, height_, 0, 0, width_ / 2, height_ / 2,
124 GL_COLOR_BUFFER_BIT, GL_LINEAR);
125 CHECK_GL();
126
127 // Downsample to quarter w x quarter h.
128 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_half_.fbo);
129 Fbo fbo_out = fbo_q_free_.Front();
130 fbo_q_free_.PopFront();
131 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_out.fbo);
132 glBlitFramebuffer(0, 0, width_ / 2, height_ / 2, 0, 0, width_ / 4,
133 height_ / 4, GL_COLOR_BUFFER_BIT, GL_LINEAR);
134 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
135 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
136 CHECK_GL();
137
138 // Blur shader is initialized statically to share between multiple blur
139 // instances.
140 static ShaderProgram kawase_prog[2];
141 int prog_index = (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) ? 1 : 0;
142 if (!kawase_prog[prog_index].IsUsable()) {
143 std::string prefix = "#version 310 es\n";
144 if (target_texture_target_ == GL_TEXTURE_EXTERNAL_OES) {
145 prefix += "#extension GL_OES_EGL_image_external_essl3 : require\n";
146 prefix += "#define APP_SAMPLER_2D samplerExternalOES\n";
147 } else {
148 prefix += "#define APP_SAMPLER_2D sampler2D\n";
149 }
150 std::string vert = prefix + screen_space_vert_shader;
151 std::string frag = prefix + kawase_blur_frag_shader;
152 kawase_prog[prog_index].Link(vert, frag);
153 CHECK_GL();
154 }
155
156 int blur_w = width_ / 4;
157 int blur_h = height_ / 4;
158 float pix_w = 1.0f / static_cast<float>(blur_w);
159 float pix_h = 1.0f / static_cast<float>(blur_h);
160 vec2 pixel_size(pix_w, pix_h);
161 constexpr int num_passes = sizeof(g_blur_samples) / sizeof(g_blur_samples[0]);
162 vec2 blur_offsets[num_passes][g_num_samples];
163 for (int i = 0; i < num_passes; ++i) {
164 for (int dir = 0; dir < g_num_samples; ++dir) {
165 blur_offsets[i][dir] = pixel_size.array() *
166 g_blur_samples[i][dir].array() * scale_;
167 }
168 }
169
170 kawase_prog[prog_index].Use();
171
172 vec4 screen_tri_strip[4] = {vec4(-1, 1, 0, 1), vec4(-1, -1, 0, 0),
173 vec4(1, 1, 1, 1), vec4(1, -1, 1, 0)};
174
175 glViewport(0, 0, blur_w, blur_h);
176 glVertexAttribPointer(POSITION_ATTR, 4, GL_FLOAT, GL_FALSE, sizeof(vec4),
177 screen_tri_strip);
178 glEnableVertexAttribArray(POSITION_ATTR);
179 CHECK_GL();
180
181 // Ping-pong between fbos from fbo_q_free_ to compute the passes.
182 Fbo fbo_in = fbo_out;
183 for (int i = 0; i < num_passes; ++i) {
184 fbo_out = fbo_q_free_.Front();
185 fbo_q_free_.PopFront();
186 glBindFramebuffer(GL_FRAMEBUFFER, fbo_out.fbo);
187 glActiveTexture(GL_TEXTURE0 + SAMPLER_BINDING);
188 glBindTexture(target_texture_target_, fbo_in.texture);
189 glUniform2fv(OFFSET_BINDING, 4, &blur_offsets[i][0][0]);
190 glClear(GL_COLOR_BUFFER_BIT);
191 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
192 CHECK_GL();
193 // Put fbo_in back into the free fbo pool.
194 fbo_q_free_.Append(fbo_in);
195 // Next iteration's in buffer is this iteration's out buffer.
196 fbo_in = fbo_out;
197 }
198 glDisableVertexAttribArray(POSITION_ATTR);
199 glBindTexture(target_texture_target_, 0);
200 glUseProgram(0);
201 glActiveTexture(GL_TEXTURE0);
202 CHECK_GL();
203 // fbo_out remains out of the fbo_q_free_ list, since the application will be
204 // using it as a texture.
205 return fbo_out.texture;
206}
207
208Blur::Fbo Blur::CreateFbo(int w, int h, GLuint source_texture, GLint tex_target,
209 bool is_external) {
210 Fbo fbo;
211 glGenFramebuffers(1, &fbo.fbo);
212 if (source_texture) {
213 fbo.texture = source_texture;
214 } else {
215 glGenTextures(1, &fbo.texture);
216 }
217
218 glBindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
219 CHECK_GL();
220
221 if (!source_texture) {
222 glBindTexture(tex_target, fbo.texture);
223 glTexParameteri(tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
224 glTexParameteri(tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
225 glTexParameteri(tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
226 glTexParameteri(tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
227 if (is_external) {
228 fbo.egl_image =
229 CreateEglImage(display_, w, h, HAL_PIXEL_FORMAT_RGBA_8888,
230 GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER);
231 glEGLImageTargetTexture2DOES(tex_target, fbo.egl_image);
232 } else {
233 glTexImage2D(tex_target, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
234 nullptr);
235 }
236 }
237 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_target,
238 fbo.texture, 0);
239 CHECK_GL();
240 CHECK_GL_FBO();
241
242 glBindFramebuffer(GL_FRAMEBUFFER, 0);
243 return fbo;
244}
245
246} // namespace dvr
247} // namespace android