Home | History | Annotate | Download | only in core
      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 
     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 
     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 
     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 
     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 
     80 bool GLFrame::InitWithExternalTexture() {
     81   texture_target_ = GL_TEXTURE_EXTERNAL_OES;
     82   width_ = 0;
     83   height_ = 0;
     84   return GenerateTextureName();
     85 }
     86 
     87 void GLFrame::InitDimensions(int width, int height) {
     88   width_ = width;
     89   height_ = height;
     90   vp_width_ = width;
     91   vp_height_ = height;
     92 }
     93 
     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 
    110 bool GLFrame::GenerateMipMap() {
    111   if (FocusTexture()) {
    112     glGenerateMipmap(GL_TEXTURE_2D);
    113     return !GLEnv::CheckGLError("Generating MipMap!");
    114   }
    115   return false;
    116 }
    117 
    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 
    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 
    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 
    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 
    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 
    166 bool GLFrame::CopyDataTo(uint8_t* buffer, int size) {
    167   return (size >= Size())
    168     ? CopyPixelsTo(buffer)
    169     : false;
    170 }
    171 
    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 
    183 bool GLFrame::WriteData(const uint8_t* data, int data_size) {
    184   return (data_size == Size()) ? UploadTexturePixels(data) : false;
    185 }
    186 
    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 
    195 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 
    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 
    214 int GLFrame::Size() const {
    215   return width_ * height_ * 4;
    216 }
    217 
    218 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 
    227 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 
    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 
    256 bool GLFrame::BindTexture() const {
    257   glBindTexture(GL_TEXTURE_2D, texture_id_);
    258   return !GLEnv::CheckGLError("Texture Binding");
    259 }
    260 
    261 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().
    267 GLuint GLFrame::GetFboId() const {
    268   return fbo_id_;
    269 }
    270 
    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 
    283 bool GLFrame::GenerateTextureName() {
    284   if (texture_state_ == kStateUninitialized) {
    285     // Make sure texture not in use already
    286     if (glIsTexture(texture_id_)) {
    287       LOGE("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 
    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 
    324 bool GLFrame::TextureWasDeleted() const {
    325   return texture_state_ == kStateComplete && !glIsTexture(texture_id_);
    326 }
    327 
    328 bool GLFrame::GenerateFboName() {
    329   if (fbo_state_ == kStateUninitialized) {
    330     // Make sure FBO not in use already
    331     if (glIsFramebuffer(fbo_id_)) {
    332       LOGE("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 
    347 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 
    362 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   LOGW("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 
    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     LOGE("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 
    427 bool GLFrame::ReattachTextureToFbo() {
    428   return (fbo_state_ == kStateGenerated) ? AttachTextureToFbo() : true;
    429 }
    430 
    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 
    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