1/*
2 * Copyright (C) 2013 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 "PixelBuffer.h"
18
19#include "Debug.h"
20#include "Extensions.h"
21#include "Properties.h"
22#include "renderstate/RenderState.h"
23#include "utils/GLUtils.h"
24
25#include <utils/Log.h>
26
27namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// CPU pixel buffer
32///////////////////////////////////////////////////////////////////////////////
33
34class CpuPixelBuffer : public PixelBuffer {
35public:
36    CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
37
38    uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
39
40    void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
41
42protected:
43    void unmap() override;
44
45private:
46    std::unique_ptr<uint8_t[]> mBuffer;
47};
48
49CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
50        : PixelBuffer(format, width, height)
51        , mBuffer(new uint8_t[width * height * formatSize(format)]) {}
52
53uint8_t* CpuPixelBuffer::map(AccessMode mode) {
54    if (mAccessMode == kAccessMode_None) {
55        mAccessMode = mode;
56    }
57    return mBuffer.get();
58}
59
60void CpuPixelBuffer::unmap() {
61    mAccessMode = kAccessMode_None;
62}
63
64void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
65    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
66                    &mBuffer[offset]);
67}
68
69///////////////////////////////////////////////////////////////////////////////
70// GPU pixel buffer
71///////////////////////////////////////////////////////////////////////////////
72
73class GpuPixelBuffer : public PixelBuffer {
74public:
75    GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
76    ~GpuPixelBuffer();
77
78    uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
79
80    void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
81
82protected:
83    void unmap() override;
84
85private:
86    GLuint mBuffer;
87    uint8_t* mMappedPointer;
88    Caches& mCaches;
89};
90
91GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
92        : PixelBuffer(format, width, height)
93        , mMappedPointer(nullptr)
94        , mCaches(Caches::getInstance()) {
95    glGenBuffers(1, &mBuffer);
96
97    mCaches.pixelBufferState().bind(mBuffer);
98    glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
99    mCaches.pixelBufferState().unbind();
100}
101
102GpuPixelBuffer::~GpuPixelBuffer() {
103    glDeleteBuffers(1, &mBuffer);
104}
105
106uint8_t* GpuPixelBuffer::map(AccessMode mode) {
107    if (mAccessMode == kAccessMode_None) {
108        mCaches.pixelBufferState().bind(mBuffer);
109        mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
110        if (CC_UNLIKELY(!mMappedPointer)) {
111            GLUtils::dumpGLErrors();
112            LOG_ALWAYS_FATAL("Failed to map PBO");
113        }
114        mAccessMode = mode;
115        mCaches.pixelBufferState().unbind();
116    }
117
118    return mMappedPointer;
119}
120
121void GpuPixelBuffer::unmap() {
122    if (mAccessMode != kAccessMode_None) {
123        if (mMappedPointer) {
124            mCaches.pixelBufferState().bind(mBuffer);
125            GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
126            if (status == GL_FALSE) {
127                ALOGE("Corrupted GPU pixel buffer");
128            }
129        }
130        mAccessMode = kAccessMode_None;
131        mMappedPointer = nullptr;
132    }
133}
134
135void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
136    // If the buffer is not mapped, unmap() will not bind it
137    mCaches.pixelBufferState().bind(mBuffer);
138    unmap();
139    glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
140                    reinterpret_cast<void*>(offset));
141    mCaches.pixelBufferState().unbind();
142}
143
144///////////////////////////////////////////////////////////////////////////////
145// Factory
146///////////////////////////////////////////////////////////////////////////////
147
148PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
149    if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
150        return new GpuPixelBuffer(format, width, height);
151    }
152    return new CpuPixelBuffer(format, width, height);
153}
154
155};  // namespace uirenderer
156};  // namespace android
157