Home | History | Annotate | Download | only in renderstate
      1 /*
      2  * Copyright (C) 2015 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 #include "renderstate/TextureState.h"
     17 
     18 #include "Caches.h"
     19 #include "utils/TraceUtils.h"
     20 
     21 #include <GLES3/gl3.h>
     22 #include <memory>
     23 #include <SkCanvas.h>
     24 #include <SkBitmap.h>
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 
     29 // Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is
     30 static const int SHADOW_LUT_SIZE = 128;
     31 
     32 // Must define as many texture units as specified by kTextureUnitsCount
     33 const GLenum kTextureUnits[] = {
     34     GL_TEXTURE0,
     35     GL_TEXTURE1,
     36     GL_TEXTURE2,
     37     GL_TEXTURE3
     38 };
     39 
     40 TextureState::TextureState()
     41         : mTextureUnit(0) {
     42     glActiveTexture(kTextureUnits[0]);
     43     resetBoundTextures();
     44 
     45     GLint maxTextureUnits;
     46     glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
     47     LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
     48             "At least %d texture units are required!", kTextureUnitsCount);
     49     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     50 }
     51 
     52 TextureState::~TextureState() {
     53     if (mShadowLutTexture != nullptr) {
     54         mShadowLutTexture->deleteTexture();
     55     }
     56 }
     57 
     58 /**
     59  * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
     60  * darkness at that spot. Input values of 0->1 should be mapped within the same
     61  * range, but can affect the curve for a different visual falloff.
     62  *
     63  * This is used to populate the shadow LUT texture for quick lookup in the
     64  * shadow shader.
     65  */
     66 static float computeShadowOpacity(float ratio) {
     67     // exponential falloff function provided by UX
     68     float val = 1 - ratio;
     69     return exp(-val * val * 4.0) - 0.018;
     70 }
     71 
     72 void TextureState::constructTexture(Caches& caches) {
     73     if (mShadowLutTexture == nullptr) {
     74         mShadowLutTexture.reset(new Texture(caches));
     75 
     76         unsigned char bytes[SHADOW_LUT_SIZE];
     77         for (int i = 0; i < SHADOW_LUT_SIZE; i++) {
     78             float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f);
     79             bytes[i] = computeShadowOpacity(inputRatio) * 255;
     80         }
     81         mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes);
     82         mShadowLutTexture->setFilter(GL_LINEAR);
     83         mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE);
     84     }
     85 }
     86 
     87 void TextureState::activateTexture(GLuint textureUnit) {
     88     LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount,
     89             "Tried to use texture unit index %d, only %d exist",
     90             textureUnit, kTextureUnitsCount);
     91     if (mTextureUnit != textureUnit) {
     92         glActiveTexture(kTextureUnits[textureUnit]);
     93         mTextureUnit = textureUnit;
     94     }
     95 }
     96 
     97 void TextureState::resetActiveTexture() {
     98     mTextureUnit = -1;
     99 }
    100 
    101 void TextureState::bindTexture(GLuint texture) {
    102     if (mBoundTextures[mTextureUnit] != texture) {
    103         glBindTexture(GL_TEXTURE_2D, texture);
    104         mBoundTextures[mTextureUnit] = texture;
    105     }
    106 }
    107 
    108 void TextureState::bindTexture(GLenum target, GLuint texture) {
    109     if (target == GL_TEXTURE_2D) {
    110         bindTexture(texture);
    111     } else {
    112         // GLConsumer directly calls glBindTexture() with
    113         // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
    114         // since the cached state could be stale
    115         glBindTexture(target, texture);
    116     }
    117 }
    118 
    119 void TextureState::deleteTexture(GLuint texture) {
    120     // When glDeleteTextures() is called on a currently bound texture,
    121     // OpenGL ES specifies that the texture is then considered unbound
    122     // Consider the following series of calls:
    123     //
    124     // glGenTextures -> creates texture name 2
    125     // glBindTexture(2)
    126     // glDeleteTextures(2) -> 2 is now unbound
    127     // glGenTextures -> can return 2 again
    128     //
    129     // If we don't call glBindTexture(2) after the second glGenTextures
    130     // call, any texture operation will be performed on the default
    131     // texture (name=0)
    132 
    133     unbindTexture(texture);
    134 
    135     glDeleteTextures(1, &texture);
    136 }
    137 
    138 void TextureState::resetBoundTextures() {
    139     for (int i = 0; i < kTextureUnitsCount; i++) {
    140         mBoundTextures[i] = 0;
    141     }
    142 }
    143 
    144 void TextureState::unbindTexture(GLuint texture) {
    145     for (int i = 0; i < kTextureUnitsCount; i++) {
    146         if (mBoundTextures[i] == texture) {
    147             mBoundTextures[i] = 0;
    148         }
    149     }
    150 }
    151 
    152 } /* namespace uirenderer */
    153 } /* namespace android */
    154 
    155