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