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 
27 namespace android {
28 namespace uirenderer {
29 
30 ///////////////////////////////////////////////////////////////////////////////
31 // CPU pixel buffer
32 ///////////////////////////////////////////////////////////////////////////////
33 
34 class CpuPixelBuffer : public PixelBuffer {
35 public:
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 
42 protected:
43     void unmap() override;
44 
45 private:
46     std::unique_ptr<uint8_t[]> mBuffer;
47 };
48 
CpuPixelBuffer(GLenum format,uint32_t width,uint32_t height)49 CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
50         : PixelBuffer(format, width, height)
51         , mBuffer(new uint8_t[width * height * formatSize(format)]) {}
52 
map(AccessMode mode)53 uint8_t* CpuPixelBuffer::map(AccessMode mode) {
54     if (mAccessMode == kAccessMode_None) {
55         mAccessMode = mode;
56     }
57     return mBuffer.get();
58 }
59 
unmap()60 void CpuPixelBuffer::unmap() {
61     mAccessMode = kAccessMode_None;
62 }
63 
upload(uint32_t x,uint32_t y,uint32_t width,uint32_t height,int offset)64 void 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 
73 class GpuPixelBuffer : public PixelBuffer {
74 public:
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 
82 protected:
83     void unmap() override;
84 
85 private:
86     GLuint mBuffer;
87     uint8_t* mMappedPointer;
88     Caches& mCaches;
89 };
90 
GpuPixelBuffer(GLenum format,uint32_t width,uint32_t height)91 GpuPixelBuffer::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 
~GpuPixelBuffer()102 GpuPixelBuffer::~GpuPixelBuffer() {
103     glDeleteBuffers(1, &mBuffer);
104 }
105 
map(AccessMode mode)106 uint8_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 
unmap()121 void 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 
upload(uint32_t x,uint32_t y,uint32_t width,uint32_t height,int offset)135 void 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 
create(GLenum format,uint32_t width,uint32_t height,BufferType type)148 PixelBuffer* 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