1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "base/logging.h"
18
19 #include "core/gl_env.h"
20 #include "core/gl_frame.h"
21 #include "core/shader_program.h"
22
23 #include <vector>
24
25 namespace android {
26 namespace filterfw {
27
28 static const int kIdentityShaderKey = 1;
29
30 //
31 // A GLFrame stores pixel data on the GPU. It uses two kinds of GL data
32 // containers for this: Textures and Frame Buffer Objects (FBOs). Textures are
33 // used whenever pixel data needs to be read into a shader or the host program,
34 // and when pixel data is uploaded to a GLFrame. The FBO is used as a rendering
35 // target for shaders.
36 //
37
GLFrame(GLEnv * gl_env)38 GLFrame::GLFrame(GLEnv* gl_env)
39 : gl_env_(gl_env),
40 width_(0),
41 height_(0),
42 vp_x_(0),
43 vp_y_(0),
44 vp_width_(0),
45 vp_height_(0),
46 texture_id_(0),
47 fbo_id_(0),
48 texture_target_(GL_TEXTURE_2D),
49 texture_state_(kStateUninitialized),
50 fbo_state_(kStateUninitialized),
51 owns_texture_(false),
52 owns_fbo_(false) {
53 SetDefaultTexParameters();
54 }
55
Init(int width,int height)56 bool GLFrame::Init(int width, int height) {
57 // Make sure we haven't been initialized already
58 if (width_ == 0 && height_ == 0) {
59 InitDimensions(width, height);
60 return true;
61 }
62 return false;
63 }
64
InitWithTexture(GLint texture_id,int width,int height)65 bool GLFrame::InitWithTexture(GLint texture_id, int width, int height) {
66 texture_id_ = texture_id;
67 texture_state_ = glIsTexture(texture_id) ? kStateComplete : kStateGenerated;
68 InitDimensions(width, height);
69 return true;
70 }
71
InitWithFbo(GLint fbo_id,int width,int height)72 bool GLFrame::InitWithFbo(GLint fbo_id, int width, int height) {
73 fbo_id_ = fbo_id;
74 fbo_state_ = glIsFramebuffer(fbo_id) ? kStateComplete : kStateGenerated;
75 texture_state_ = kStateUnmanaged;
76 InitDimensions(width, height);
77 return true;
78 }
79
InitWithExternalTexture()80 bool GLFrame::InitWithExternalTexture() {
81 texture_target_ = GL_TEXTURE_EXTERNAL_OES;
82 width_ = 0;
83 height_ = 0;
84 return GenerateTextureName();
85 }
86
InitDimensions(int width,int height)87 void GLFrame::InitDimensions(int width, int height) {
88 width_ = width;
89 height_ = height;
90 vp_width_ = width;
91 vp_height_ = height;
92 }
93
~GLFrame()94 GLFrame::~GLFrame() {
95 // Delete texture
96 if (owns_texture_) {
97 // Bind FBO so that texture is unbound from it during deletion
98 if (fbo_state_ == kStateComplete) {
99 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
100 }
101 glDeleteTextures(1, &texture_id_);
102 }
103
104 // Delete FBO
105 if (owns_fbo_) {
106 glDeleteFramebuffers(1, &fbo_id_);
107 }
108 }
109
GenerateMipMap()110 bool GLFrame::GenerateMipMap() {
111 if (FocusTexture()) {
112 glGenerateMipmap(GL_TEXTURE_2D);
113 return !GLEnv::CheckGLError("Generating MipMap!");
114 }
115 return false;
116 }
117
SetTextureParameter(GLenum pname,GLint value)118 bool GLFrame::SetTextureParameter(GLenum pname, GLint value) {
119 if (value != tex_params_[pname]) {
120 if (FocusTexture()) {
121 glTexParameteri(GL_TEXTURE_2D, pname, value);
122 if (!GLEnv::CheckGLError("Setting texture parameter!")) {
123 tex_params_[pname] = value;
124 return true;
125 }
126 }
127 } else {
128 return true;
129 }
130 return false;
131 }
132
UpdateTexParameters()133 bool GLFrame::UpdateTexParameters() {
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_params_[GL_TEXTURE_MAG_FILTER]);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_params_[GL_TEXTURE_MIN_FILTER]);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_params_[GL_TEXTURE_WRAP_S]);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_params_[GL_TEXTURE_WRAP_T]);
138 return !GLEnv::CheckGLError("Resetting texture parameters!");
139 }
140
TexParametersModifed()141 bool GLFrame::TexParametersModifed() {
142 return tex_params_[GL_TEXTURE_MAG_FILTER] != GL_LINEAR
143 || tex_params_[GL_TEXTURE_MIN_FILTER] != GL_LINEAR
144 || tex_params_[GL_TEXTURE_WRAP_S] != GL_CLAMP_TO_EDGE
145 || tex_params_[GL_TEXTURE_WRAP_T] != GL_CLAMP_TO_EDGE;
146 }
147
SetDefaultTexParameters()148 void GLFrame::SetDefaultTexParameters() {
149 tex_params_[GL_TEXTURE_MAG_FILTER] = GL_LINEAR;
150 tex_params_[GL_TEXTURE_MIN_FILTER] = GL_LINEAR;
151 tex_params_[GL_TEXTURE_WRAP_S] = GL_CLAMP_TO_EDGE;
152 tex_params_[GL_TEXTURE_WRAP_T] = GL_CLAMP_TO_EDGE;
153 }
154
ResetTexParameters()155 bool GLFrame::ResetTexParameters() {
156 if (TexParametersModifed()) {
157 if (BindTexture()) {
158 SetDefaultTexParameters();
159 return UpdateTexParameters();
160 }
161 return false;
162 }
163 return true;
164 }
165
CopyDataTo(uint8_t * buffer,int size)166 bool GLFrame::CopyDataTo(uint8_t* buffer, int size) {
167 return (size >= Size())
168 ? CopyPixelsTo(buffer)
169 : false;
170 }
171
CopyPixelsTo(uint8_t * buffer)172 bool GLFrame::CopyPixelsTo(uint8_t* buffer) {
173 // Use one of the pixel reading methods below, ordered from most
174 // efficient to least efficient.
175 if (fbo_state_ == kStateComplete)
176 return ReadFboPixels(buffer);
177 else if (texture_state_ == kStateComplete)
178 return ReadTexturePixels(buffer);
179 else
180 return false;
181 }
182
WriteData(const uint8_t * data,int data_size)183 bool GLFrame::WriteData(const uint8_t* data, int data_size) {
184 return (data_size == Size()) ? UploadTexturePixels(data) : false;
185 }
186
SetViewport(int x,int y,int width,int height)187 bool GLFrame::SetViewport(int x, int y, int width, int height) {
188 vp_x_ = x;
189 vp_y_ = y;
190 vp_width_ = width;
191 vp_height_ = height;
192 return true;
193 }
194
Clone() const195 GLFrame* GLFrame::Clone() const {
196 GLFrame* target = new GLFrame(gl_env_);
197 target->Init(width_, height_);
198 target->CopyPixelsFrom(this);
199 return target;
200 }
201
CopyPixelsFrom(const GLFrame * frame)202 bool GLFrame::CopyPixelsFrom(const GLFrame* frame) {
203 if (frame == this) {
204 return true;
205 } else if (frame && frame->width_ == width_ && frame->height_ == height_) {
206 std::vector<const GLFrame*> sources;
207 sources.push_back(frame);
208 GetIdentity()->Process(sources, this);
209 return true;
210 }
211 return false;
212 }
213
Size() const214 int GLFrame::Size() const {
215 return width_ * height_ * 4;
216 }
217
GetIdentity() const218 ShaderProgram* GLFrame::GetIdentity() const {
219 ShaderProgram* stored_shader = gl_env_->ShaderWithKey(kIdentityShaderKey);
220 if (!stored_shader) {
221 stored_shader = ShaderProgram::CreateIdentity(gl_env_);
222 gl_env_->AttachShader(kIdentityShaderKey, stored_shader);
223 }
224 return stored_shader;
225 }
226
BindFrameBuffer() const227 bool GLFrame::BindFrameBuffer() const {
228 // Bind the FBO
229 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
230 if (GLEnv::CheckGLError("FBO Binding")) return false;
231
232 // Set viewport
233 glViewport(vp_x_, vp_y_, vp_width_, vp_height_);
234 if (GLEnv::CheckGLError("ViewPort Setup")) return false;
235
236 return true;
237 }
238
FocusFrameBuffer()239 bool GLFrame::FocusFrameBuffer() {
240 // Create texture backing if necessary
241 if (texture_state_ == kStateUninitialized) {
242 if (!GenerateTextureName())
243 return false;
244 }
245
246 // Create and bind FBO to texture if necessary
247 if (fbo_state_ != kStateComplete) {
248 if (!GenerateFboName() || !AttachTextureToFbo())
249 return false;
250 }
251
252 // And bind it.
253 return BindFrameBuffer();
254 }
255
BindTexture() const256 bool GLFrame::BindTexture() const {
257 glBindTexture(GL_TEXTURE_2D, texture_id_);
258 return !GLEnv::CheckGLError("Texture Binding");
259 }
260
GetTextureId() const261 GLuint GLFrame::GetTextureId() const {
262 return texture_id_;
263 }
264
265 // Returns the held FBO id. Only call this if the GLFrame holds an FBO. You
266 // can check this by calling HoldsFbo().
GetFboId() const267 GLuint GLFrame::GetFboId() const {
268 return fbo_id_;
269 }
270
FocusTexture()271 bool GLFrame::FocusTexture() {
272 // Make sure we have a texture
273 if (!GenerateTextureName())
274 return false;
275
276 // Bind the texture
277 if (!BindTexture())
278 return false;
279
280 return !GLEnv::CheckGLError("Texture Binding");
281 }
282
GenerateTextureName()283 bool GLFrame::GenerateTextureName() {
284 if (texture_state_ == kStateUninitialized) {
285 // Make sure texture not in use already
286 if (glIsTexture(texture_id_)) {
287 ALOGE("GLFrame: Cannot generate texture id %d, as it is in use already!", texture_id_);
288 return false;
289 }
290
291 // Generate the texture
292 glGenTextures (1, &texture_id_);
293 if (GLEnv::CheckGLError("Texture Generation"))
294 return false;
295 texture_state_ = kStateGenerated;
296 owns_texture_ = true;
297 }
298
299 return true;
300 }
301
AllocateTexture()302 bool GLFrame::AllocateTexture() {
303 // Allocate or re-allocate (if texture was deleted externally).
304 if (texture_state_ == kStateGenerated || TextureWasDeleted()) {
305 LOG_FRAME("GLFrame: Allocating texture: %d", texture_id_);
306 glBindTexture(GL_TEXTURE_2D, texture_id_);
307 glTexImage2D(GL_TEXTURE_2D,
308 0,
309 GL_RGBA,
310 width_,
311 height_,
312 0,
313 GL_RGBA,
314 GL_UNSIGNED_BYTE,
315 NULL);
316 if (!GLEnv::CheckGLError("Texture Allocation")) {
317 UpdateTexParameters();
318 texture_state_ = kStateComplete;
319 }
320 }
321 return texture_state_ == kStateComplete;
322 }
323
TextureWasDeleted() const324 bool GLFrame::TextureWasDeleted() const {
325 return texture_state_ == kStateComplete && !glIsTexture(texture_id_);
326 }
327
GenerateFboName()328 bool GLFrame::GenerateFboName() {
329 if (fbo_state_ == kStateUninitialized) {
330 // Make sure FBO not in use already
331 if (glIsFramebuffer(fbo_id_)) {
332 ALOGE("GLFrame: Cannot generate FBO id %d, as it is in use already!", fbo_id_);
333 return false;
334 }
335
336 // Create FBO
337 glGenFramebuffers(1, &fbo_id_);
338 if (GLEnv::CheckGLError("FBO Generation"))
339 return false;
340 fbo_state_ = kStateGenerated;
341 owns_fbo_ = true;
342 }
343
344 return true;
345 }
346
ReadFboPixels(uint8_t * pixels) const347 bool GLFrame::ReadFboPixels(uint8_t* pixels) const {
348 if (fbo_state_ == kStateComplete) {
349 BindFrameBuffer();
350 glReadPixels(0,
351 0,
352 width_,
353 height_,
354 GL_RGBA,
355 GL_UNSIGNED_BYTE,
356 pixels);
357 return !GLEnv::CheckGLError("FBO Pixel Readout");
358 }
359 return false;
360 }
361
ReadTexturePixels(uint8_t * pixels) const362 bool GLFrame::ReadTexturePixels(uint8_t* pixels) const {
363 // Read pixels from texture if we do not have an FBO
364 // NOTE: OpenGL ES does NOT support glGetTexImage() for reading out texture
365 // data. The only way for us to get texture data is to create a new FBO and
366 // render the current texture frame into it. As this is quite inefficient,
367 // and unnecessary (this can only happen if the user is reading out data
368 // that was just set, and not run through a filter), we warn the user about
369 // this here.
370 ALOGW("Warning: Reading pixel data from unfiltered GL frame. This is highly "
371 "inefficient. Please consider using your original pixel buffer "
372 "instead!");
373
374 // Create source frame set (unfortunately this requires an ugly const-cast,
375 // as we need to wrap ourselves in a frame-set. Still, as this set is used
376 // as input only, we are certain we will not be modified).
377 std::vector<const GLFrame*> sources;
378 sources.push_back(this);
379
380 // Create target frame
381 GLFrame target(gl_env_);
382 target.Init(width_, height_);
383
384 // Render the texture to the target
385 GetIdentity()->Process(sources, &target);
386
387 // Get the pixel data
388 return target.ReadFboPixels(pixels);
389 }
390
AttachTextureToFbo()391 bool GLFrame::AttachTextureToFbo() {
392 // Check FBO and texture state. We do not do anything if we are not managing the texture.
393 if (fbo_state_ == kStateComplete || texture_state_ == kStateUnmanaged) {
394 return true;
395 } else if (fbo_state_ != kStateGenerated) {
396 ALOGE("Attempting to attach texture to FBO with no FBO in place!");
397 return false;
398 }
399
400 // If texture has been generated, make sure it is allocated.
401 if (!AllocateTexture())
402 return false;
403
404 // Bind the frame buffer, and check if we it is already bound
405 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
406
407 // Bind the texture to the frame buffer
408 LOG_FRAME("Attaching tex %d w %d h %d to fbo %d", texture_id_, width_, height_, fbo_id_);
409 glFramebufferTexture2D(GL_FRAMEBUFFER,
410 GL_COLOR_ATTACHMENT0,
411 GL_TEXTURE_2D,
412 texture_id_,
413 0);
414
415 // Cleanup
416 glBindTexture(GL_TEXTURE_2D, 0);
417 glBindFramebuffer(GL_FRAMEBUFFER, 0);
418
419 if (GLEnv::CheckGLError("Texture Binding to FBO"))
420 return false;
421 else
422 fbo_state_ = kStateComplete;
423
424 return true;
425 }
426
ReattachTextureToFbo()427 bool GLFrame::ReattachTextureToFbo() {
428 return (fbo_state_ == kStateGenerated) ? AttachTextureToFbo() : true;
429 }
430
DetachTextureFromFbo()431 bool GLFrame::DetachTextureFromFbo() {
432 if (fbo_state_ == kStateComplete && texture_state_ == kStateComplete) {
433 LOG_FRAME("Detaching tex %d w %d h %d from fbo %d", texture_id_, width_, height_, fbo_id_);
434 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
435 glFramebufferTexture2D(GL_FRAMEBUFFER,
436 GL_COLOR_ATTACHMENT0,
437 GL_TEXTURE_2D,
438 0,
439 0);
440 if (GLEnv::CheckGLError("Detaching texture to FBO"))
441 return false;
442 else
443 fbo_state_ = kStateGenerated;
444 }
445 return true;
446 }
447
UploadTexturePixels(const uint8_t * pixels)448 bool GLFrame::UploadTexturePixels(const uint8_t* pixels) {
449 // Bind the texture object
450 FocusTexture();
451
452 // Load mipmap level 0
453 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_,
454 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
455
456 // Set the user specified texture parameters
457 UpdateTexParameters();
458
459 if (GLEnv::CheckGLError("Texture Pixel Upload"))
460 return false;
461
462 texture_state_ = kStateComplete;
463 return true;
464 }
465
466 } // namespace filterfw
467 } // namespace android
468