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 
     17 #include "OffscreenBufferPool.h"
     18 
     19 #include "Caches.h"
     20 #include "Properties.h"
     21 #include "renderstate/RenderState.h"
     22 #include "utils/FatVector.h"
     23 #include "utils/TraceUtils.h"
     24 
     25 #include <utils/Log.h>
     26 
     27 #include <GLES2/gl2.h>
     28 
     29 namespace android {
     30 namespace uirenderer {
     31 
     32 ////////////////////////////////////////////////////////////////////////////////
     33 // OffscreenBuffer
     34 ////////////////////////////////////////////////////////////////////////////////
     35 
     36 OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
     37         uint32_t viewportWidth, uint32_t viewportHeight)
     38         : GpuMemoryTracker(GpuObjectType::OffscreenBuffer)
     39         , renderState(renderState)
     40         , viewportWidth(viewportWidth)
     41         , viewportHeight(viewportHeight)
     42         , texture(caches) {
     43     uint32_t width = computeIdealDimension(viewportWidth);
     44     uint32_t height = computeIdealDimension(viewportHeight);
     45     ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
     46     caches.textureState().activateTexture(0);
     47     texture.resize(width, height, GL_RGBA);
     48     texture.blend = true;
     49     texture.setWrap(GL_CLAMP_TO_EDGE);
     50     // not setting filter on texture, since it's set when drawing, based on transform
     51 }
     52 
     53 Rect OffscreenBuffer::getTextureCoordinates() {
     54     const float texX = 1.0f / static_cast<float>(texture.width());
     55     const float texY = 1.0f / static_cast<float>(texture.height());
     56     return Rect(0, viewportHeight * texY, viewportWidth * texX, 0);
     57 }
     58 
     59 void OffscreenBuffer::dirty(Rect dirtyArea) {
     60     dirtyArea.doIntersect(0, 0, viewportWidth, viewportHeight);
     61     if (!dirtyArea.isEmpty()) {
     62         region.orSelf(android::Rect(dirtyArea.left, dirtyArea.top,
     63                 dirtyArea.right, dirtyArea.bottom));
     64     }
     65 }
     66 
     67 void OffscreenBuffer::updateMeshFromRegion() {
     68     // avoid T-junctions as they cause artifacts in between the resultant
     69     // geometry when complex transforms occur.
     70     // TODO: generate the safeRegion only if necessary based on drawing transform
     71     Region safeRegion = Region::createTJunctionFreeRegion(region);
     72 
     73     size_t count;
     74     const android::Rect* rects = safeRegion.getArray(&count);
     75 
     76     const float texX = 1.0f / float(texture.width());
     77     const float texY = 1.0f / float(texture.height());
     78 
     79     FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
     80     TextureVertex* mesh = &meshVector[0];
     81     for (size_t i = 0; i < count; i++) {
     82         const android::Rect* r = &rects[i];
     83 
     84         const float u1 = r->left * texX;
     85         const float v1 = (viewportHeight - r->top) * texY;
     86         const float u2 = r->right * texX;
     87         const float v2 = (viewportHeight - r->bottom) * texY;
     88 
     89         TextureVertex::set(mesh++, r->left, r->top, u1, v1);
     90         TextureVertex::set(mesh++, r->right, r->top, u2, v1);
     91         TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
     92         TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
     93     }
     94     elementCount = count * 6;
     95     renderState.meshState().genOrUpdateMeshBuffer(&vbo,
     96             sizeof(TextureVertex) * count * 4,
     97             &meshVector[0],
     98             GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
     99 }
    100 
    101 uint32_t OffscreenBuffer::computeIdealDimension(uint32_t dimension) {
    102     return uint32_t(ceilf(dimension / float(LAYER_SIZE)) * LAYER_SIZE);
    103 }
    104 
    105 OffscreenBuffer::~OffscreenBuffer() {
    106     ATRACE_FORMAT("Destroy %ux%u HW Layer", texture.width(), texture.height());
    107     texture.deleteTexture();
    108     renderState.meshState().deleteMeshBuffer(vbo);
    109     elementCount = 0;
    110     vbo = 0;
    111 }
    112 
    113 ///////////////////////////////////////////////////////////////////////////////
    114 // OffscreenBufferPool
    115 ///////////////////////////////////////////////////////////////////////////////
    116 
    117 OffscreenBufferPool::OffscreenBufferPool()
    118     : mMaxSize(Properties::layerPoolSize) {
    119 }
    120 
    121 OffscreenBufferPool::~OffscreenBufferPool() {
    122     clear(); // TODO: unique_ptr?
    123 }
    124 
    125 int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
    126     int deltaInt = int(lhs.width) - int(rhs.width);
    127     if (deltaInt != 0) return deltaInt;
    128 
    129     return int(lhs.height) - int(rhs.height);
    130 }
    131 
    132 void OffscreenBufferPool::clear() {
    133     for (auto& entry : mPool) {
    134         delete entry.layer;
    135     }
    136     mPool.clear();
    137     mSize = 0;
    138 }
    139 
    140 OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState,
    141         const uint32_t width, const uint32_t height) {
    142     OffscreenBuffer* layer = nullptr;
    143 
    144     Entry entry(width, height);
    145     auto iter = mPool.find(entry);
    146 
    147     if (iter != mPool.end()) {
    148         entry = *iter;
    149         mPool.erase(iter);
    150 
    151         layer = entry.layer;
    152         layer->viewportWidth = width;
    153         layer->viewportHeight = height;
    154         mSize -= layer->getSizeInBytes();
    155     } else {
    156         layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height);
    157     }
    158 
    159     return layer;
    160 }
    161 
    162 OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
    163         const uint32_t width, const uint32_t height) {
    164     RenderState& renderState = layer->renderState;
    165     if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width)
    166             && layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) {
    167         // resize in place
    168         layer->viewportWidth = width;
    169         layer->viewportHeight = height;
    170 
    171         // entire area will be repainted (and may be smaller) so clear usage region
    172         layer->region.clear();
    173         return layer;
    174     }
    175     putOrDelete(layer);
    176     return get(renderState, width, height);
    177 }
    178 
    179 void OffscreenBufferPool::dump() {
    180     for (auto entry : mPool) {
    181         ALOGD("  Layer size %dx%d", entry.width, entry.height);
    182     }
    183 }
    184 
    185 void OffscreenBufferPool::putOrDelete(OffscreenBuffer* layer) {
    186     const uint32_t size = layer->getSizeInBytes();
    187     // Don't even try to cache a layer that's bigger than the cache
    188     if (size < mMaxSize) {
    189         // TODO: Use an LRU
    190         while (mSize + size > mMaxSize) {
    191             OffscreenBuffer* victim = mPool.begin()->layer;
    192             mSize -= victim->getSizeInBytes();
    193             delete victim;
    194             mPool.erase(mPool.begin());
    195         }
    196 
    197         // clear region, since it's no longer valid
    198         layer->region.clear();
    199 
    200         Entry entry(layer);
    201 
    202         mPool.insert(entry);
    203         mSize += size;
    204     } else {
    205         delete layer;
    206     }
    207 }
    208 
    209 }; // namespace uirenderer
    210 }; // namespace android
    211