gl_frame.cpp revision 776102d45a18a5df53d2ec76c5d93f20b3e99da1
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 25namespace android { 26namespace filterfw { 27 28static 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 38GLFrame::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 56bool 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 65bool 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 72bool GLFrame::InitWithFbo(GLint fbo_id, int width, int height) { 73 fbo_id_ = fbo_id; 74 fbo_state_ = glIsFramebuffer(fbo_id) ? kStateComplete : kStateGenerated; 75 InitDimensions(width, height); 76 return true; 77} 78 79bool GLFrame::InitWithExternalTexture() { 80 texture_target_ = GL_TEXTURE_EXTERNAL_OES; 81 width_ = 0; 82 height_ = 0; 83 return CreateTexture(); 84} 85 86void GLFrame::InitDimensions(int width, int height) { 87 width_ = width; 88 height_ = height; 89 vp_width_ = width; 90 vp_height_ = height; 91} 92 93GLFrame::~GLFrame() { 94 // Delete texture 95 if (owns_texture_) { 96 // Bind FBO so that texture is unbound from it during deletion 97 if (fbo_state_ == kStateComplete) { 98 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); 99 } 100 glDeleteTextures(1, &texture_id_); 101 } 102 103 // Delete FBO 104 if (owns_fbo_) { 105 glDeleteFramebuffers(1, &fbo_id_); 106 } 107} 108 109bool GLFrame::GenerateMipMap() { 110 if (FocusTexture()) { 111 glGenerateMipmap(GL_TEXTURE_2D); 112 return !GLEnv::CheckGLError("Generating MipMap!"); 113 } 114 return false; 115} 116 117bool GLFrame::SetTextureParameter(GLenum pname, GLint value) { 118 if (value != tex_params_[pname]) { 119 if (FocusTexture()) { 120 glTexParameteri(GL_TEXTURE_2D, pname, value); 121 if (!GLEnv::CheckGLError("Setting texture parameter!")) { 122 tex_params_[pname] = value; 123 return true; 124 } 125 } 126 } else { 127 return true; 128 } 129 return false; 130} 131 132bool GLFrame::UpdateTexParameters() { 133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_params_[GL_TEXTURE_MAG_FILTER]); 134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_params_[GL_TEXTURE_MIN_FILTER]); 135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_params_[GL_TEXTURE_WRAP_S]); 136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_params_[GL_TEXTURE_WRAP_T]); 137 return !GLEnv::CheckGLError("Resetting texture parameters!"); 138} 139 140bool GLFrame::TexParametersModifed() { 141 return tex_params_[GL_TEXTURE_MAG_FILTER] != GL_LINEAR 142 || tex_params_[GL_TEXTURE_MIN_FILTER] != GL_LINEAR 143 || tex_params_[GL_TEXTURE_WRAP_S] != GL_CLAMP_TO_EDGE 144 || tex_params_[GL_TEXTURE_WRAP_T] != GL_CLAMP_TO_EDGE; 145} 146 147void GLFrame::SetDefaultTexParameters() { 148 tex_params_[GL_TEXTURE_MAG_FILTER] = GL_LINEAR; 149 tex_params_[GL_TEXTURE_MIN_FILTER] = GL_LINEAR; 150 tex_params_[GL_TEXTURE_WRAP_S] = GL_CLAMP_TO_EDGE; 151 tex_params_[GL_TEXTURE_WRAP_T] = GL_CLAMP_TO_EDGE; 152} 153 154bool GLFrame::ResetTexParameters() { 155 if (TexParametersModifed()) { 156 if (BindTexture()) { 157 SetDefaultTexParameters(); 158 return UpdateTexParameters(); 159 } 160 return false; 161 } 162 return true; 163} 164 165bool GLFrame::CopyDataTo(uint8_t* buffer, int size) { 166 return (size >= Size()) 167 ? CopyPixelsTo(buffer) 168 : false; 169} 170 171bool GLFrame::CopyPixelsTo(uint8_t* buffer) { 172 if (texture_state_ == kStateComplete) 173 return ReadTexturePixels(buffer); 174 else if (fbo_state_ == kStateComplete) 175 return ReadFboPixels(buffer); 176 else 177 return false; 178} 179 180bool GLFrame::WriteData(const uint8_t* data, int data_size) { 181 return (data_size == Size()) ? UploadTexturePixels(data) : false; 182} 183 184bool GLFrame::SetViewport(int x, int y, int width, int height) { 185 vp_x_ = x; 186 vp_y_ = y; 187 vp_width_ = width; 188 vp_height_ = height; 189 return true; 190} 191 192GLFrame* GLFrame::Clone() const { 193 GLFrame* target = new GLFrame(gl_env_); 194 target->Init(width_, height_); 195 target->CopyPixelsFrom(this); 196 return target; 197} 198 199bool GLFrame::CopyPixelsFrom(const GLFrame* frame) { 200 if (frame == this) { 201 return true; 202 } else if (frame && frame->width_ == width_ && frame->height_ == height_) { 203 std::vector<const GLFrame*> sources; 204 sources.push_back(frame); 205 GetIdentity()->Process(sources, this); 206 return true; 207 } 208 return false; 209} 210 211int GLFrame::Size() const { 212 return width_ * height_ * 4; 213} 214 215ShaderProgram* GLFrame::GetIdentity() const { 216 ShaderProgram* stored_shader = gl_env_->ShaderWithKey(kIdentityShaderKey); 217 if (!stored_shader) { 218 stored_shader = ShaderProgram::CreateIdentity(gl_env_); 219 gl_env_->AttachShader(kIdentityShaderKey, stored_shader); 220 } 221 return stored_shader; 222} 223 224bool GLFrame::BindFrameBuffer() const { 225 // Bind the FBO 226 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); 227 if (GLEnv::CheckGLError("FBO Binding")) return false; 228 229 // Set viewport 230 glViewport(vp_x_, vp_y_, vp_width_, vp_height_); 231 if (GLEnv::CheckGLError("ViewPort Setup")) return false; 232 233 return true; 234} 235 236bool GLFrame::FocusFrameBuffer() { 237 // Create texture backing if necessary 238 if (texture_state_ == kStateUninitialized) { 239 if (!CreateTexture()) 240 return false; 241 } 242 243 // Create and bind FBO to texture if necessary 244 if (fbo_state_ != kStateComplete) { 245 if (!CreateFBO() || !AttachTextureToFBO()) 246 return false; 247 } 248 249 // And bind it. 250 return BindFrameBuffer(); 251} 252 253bool GLFrame::BindTexture() const { 254 glBindTexture(GL_TEXTURE_2D, texture_id_); 255 return !GLEnv::CheckGLError("Texture Binding"); 256} 257 258GLuint GLFrame::GetTextureId() const { 259 return texture_id_; 260} 261 262// Returns the held FBO id. Only call this if the GLFrame holds an FBO. You 263// can check this by calling HoldsFbo(). 264GLuint GLFrame::GetFboId() const { 265 return fbo_id_; 266} 267 268bool GLFrame::FocusTexture() { 269 // Make sure we have a texture 270 if (!CreateTexture()) 271 return false; 272 273 // Bind the texture 274 if (!BindTexture()) 275 return false; 276 277 return !GLEnv::CheckGLError("Texture Binding"); 278} 279 280bool GLFrame::CreateTexture() { 281 if (texture_state_ == kStateUninitialized) { 282 // Make sure texture not in use already 283 if (glIsTexture(texture_id_)) { 284 LOGE("GLFrame: Cannot generate texture id %d, as it is in use already!", texture_id_); 285 return false; 286 } 287 288 // Generate the texture 289 glGenTextures (1, &texture_id_); 290 if (GLEnv::CheckGLError("Texture Generation")) 291 return false; 292 texture_state_ = kStateGenerated; 293 owns_texture_ = true; 294 } 295 296 return true; 297} 298 299bool GLFrame::CreateFBO() { 300 if (fbo_state_ == kStateUninitialized) { 301 // Make sure FBO not in use already 302 if (glIsFramebuffer(fbo_id_)) { 303 LOGE("GLFrame: Cannot generate FBO id %d, as it is in use already!", fbo_id_); 304 return false; 305 } 306 307 // Create FBO 308 glGenFramebuffers(1, &fbo_id_); 309 if (GLEnv::CheckGLError("FBO Generation")) 310 return false; 311 fbo_state_ = kStateGenerated; 312 owns_fbo_ = true; 313 } 314 315 return true; 316} 317 318bool GLFrame::ReadFboPixels(uint8_t* pixels) const { 319 if (fbo_state_ == kStateComplete) { 320 BindFrameBuffer(); 321 glReadPixels(0, 322 0, 323 width_, 324 height_, 325 GL_RGBA, 326 GL_UNSIGNED_BYTE, 327 pixels); 328 return !GLEnv::CheckGLError("FBO Pixel Readout"); 329 } 330 return false; 331} 332 333bool GLFrame::ReadTexturePixels(uint8_t* pixels) const { 334 // Read pixels from texture if we do not have an FBO 335 // NOTE: OpenGL ES does NOT support glGetTexImage() for reading out texture 336 // data. The only way for us to get texture data is to create a new FBO and 337 // render the current texture frame into it. As this is quite inefficient, 338 // and unnecessary (this can only happen if the user is reading out data 339 // that was just set, and not run through a filter), we warn the user about 340 // this here. 341 LOGW("Warning: Reading pixel data from unfiltered GL frame. This is highly " 342 "inefficient. Please consider using your original pixel buffer " 343 "instead!"); 344 345 // Create source frame set (unfortunately this requires an ugly const-cast, 346 // as we need to wrap ourselves in a frame-set. Still, as this set is used 347 // as input only, we are certain we will not be modified). 348 std::vector<const GLFrame*> sources; 349 sources.push_back(this); 350 351 // Create target frame 352 GLFrame target(gl_env_); 353 target.Init(width_, height_); 354 355 // Render the texture to the target 356 GetIdentity()->Process(sources, &target); 357 358 // Get the pixel data 359 return target.ReadFboPixels(pixels); 360} 361 362bool GLFrame::AttachTextureToFBO() { 363 // Check if we are already bound 364 if (fbo_state_ == kStateComplete) 365 return true; 366 367 // Otherwise check if the texture and fbo states are acceptable 368 if ((texture_state_ != kStateGenerated && texture_state_ != kStateComplete) 369 || (fbo_state_ != kStateGenerated)) 370 return false; 371 372 // Bind the frame buffer, and check if we it is already bound 373 glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); 374 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 375 // Bind texture 376 glBindTexture(GL_TEXTURE_2D, texture_id_); 377 378 // Set the texture format 379 glTexImage2D(GL_TEXTURE_2D, 380 0, 381 GL_RGBA, 382 width_, 383 height_, 384 0, 385 GL_RGBA, 386 GL_UNSIGNED_BYTE, 387 NULL); 388 389 // Set the user specified texture parameters 390 UpdateTexParameters(); 391 392 // Bind the texture to the frame buffer 393 LOG_FRAME("Binding tex %d w %d h %d to fbo %d", texture_id_, width_, height_, fbo_id_); 394 glFramebufferTexture2D(GL_FRAMEBUFFER, 395 GL_COLOR_ATTACHMENT0, 396 GL_TEXTURE_2D, 397 texture_id_, 398 0); 399 } 400 401 // Cleanup 402 glBindTexture(GL_TEXTURE_2D, 0); 403 glBindFramebuffer(GL_FRAMEBUFFER, 0); 404 405 if (GLEnv::CheckGLError("Texture Binding to FBO")) 406 return false; 407 408 // FBO is now complete 409 fbo_state_ = kStateComplete; 410 return true; 411} 412 413bool GLFrame::UploadTexturePixels(const uint8_t* pixels) { 414 // Bind the texture object 415 FocusTexture(); 416 417 // Load mipmap level 0 418 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 419 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 420 421 // Set the user specified texture parameters 422 UpdateTexParameters(); 423 424 if (GLEnv::CheckGLError("Texture Pixel Upload")) 425 return false; 426 427 texture_state_ = kStateComplete; 428 return true; 429} 430 431} // namespace filterfw 432} // namespace android 433