blob: 6408ce6c04f4e22ce99ff1f2298662b7d15bc56c [file] [log] [blame]
John Recke170fb62018-05-07 08:12:07 -07001/*
2 * Copyright (C) 2018 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 "HardwareBitmapUploader.h"
18
19#include "hwui/Bitmap.h"
20#include "renderthread/EglManager.h"
21#include "thread/ThreadBase.h"
22#include "utils/TimeUtils.h"
23
24#include <EGL/eglext.h>
25#include <GLES2/gl2.h>
26#include <GLES2/gl2ext.h>
27#include <GLES3/gl3.h>
28#include <SkCanvas.h>
29#include <utils/GLUtils.h>
30#include <utils/Trace.h>
31#include <utils/TraceUtils.h>
32#include <thread>
33
34namespace android::uirenderer {
35
36static std::mutex sLock{};
37static ThreadBase* sUploadThread = nullptr;
38static renderthread::EglManager sEglManager;
39static int sPendingUploads = 0;
40static nsecs_t sLastUpload = 0;
41
42static bool shouldTimeOutLocked() {
43 nsecs_t durationSince = systemTime() - sLastUpload;
44 return durationSince > 2000_ms;
45}
46
47static void checkIdleTimeout() {
48 std::lock_guard{sLock};
49 if (sPendingUploads == 0 && shouldTimeOutLocked()) {
50 sEglManager.destroy();
51 } else {
52 sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
53 }
54}
55
56static void beginUpload() {
57 std::lock_guard{sLock};
58 sPendingUploads++;
59
60 if (!sUploadThread) {
61 sUploadThread = new ThreadBase{};
62 }
63
64 if (!sUploadThread->isRunning()) {
65 sUploadThread->start("GrallocUploadThread");
66 }
67
68 if (!sEglManager.hasEglContext()) {
69 sUploadThread->queue().runSync([]() {
70 sEglManager.initialize();
71 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
72 });
73 sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
74 }
75}
76
77static void endUpload() {
78 std::lock_guard{sLock};
79 sPendingUploads--;
80 sLastUpload = systemTime();
81}
82
83static EGLDisplay getUploadEglDisplay() {
84 std::lock_guard{sLock};
85 LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
86 return sEglManager.eglDisplay();
87}
88
89static bool hasFP16Support() {
90 static std::once_flag sOnce;
91 static bool hasFP16Support = false;
92
93 // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
94 // we don't need to double-check the GLES version/extension.
95 std::call_once(sOnce, []() {
96 sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
97 GraphicBuffer::USAGE_HW_TEXTURE |
98 GraphicBuffer::USAGE_SW_WRITE_NEVER |
99 GraphicBuffer::USAGE_SW_READ_NEVER,
100 "tempFp16Buffer");
101 status_t error = buffer->initCheck();
102 hasFP16Support = !error;
103 });
104
105 return hasFP16Support;
106}
107
108#define FENCE_TIMEOUT 2000000000
109
110class AutoEglImage {
111public:
112 AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer) : mDisplay(display) {
113 EGLint imageAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
114 image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer,
115 imageAttrs);
116 }
117
118 ~AutoEglImage() {
119 if (image != EGL_NO_IMAGE_KHR) {
120 eglDestroyImageKHR(mDisplay, image);
121 }
122 }
123
124 EGLImageKHR image = EGL_NO_IMAGE_KHR;
125
126private:
127 EGLDisplay mDisplay = EGL_NO_DISPLAY;
128};
129
130class AutoSkiaGlTexture {
131public:
132 AutoSkiaGlTexture() {
133 glGenTextures(1, &mTexture);
134 glBindTexture(GL_TEXTURE_2D, mTexture);
135 }
136
137 ~AutoSkiaGlTexture() { glDeleteTextures(1, &mTexture); }
138
139private:
140 GLuint mTexture = 0;
141};
142
143struct FormatInfo {
144 PixelFormat pixelFormat;
145 GLint format, type;
146 bool isSupported = false;
147 bool valid = true;
148};
149
150static FormatInfo determineFormat(const SkBitmap& skBitmap) {
151 FormatInfo formatInfo;
152 // TODO: add support for linear blending (when ANDROID_ENABLE_LINEAR_BLENDING is defined)
153 switch (skBitmap.info().colorType()) {
154 case kRGBA_8888_SkColorType:
155 formatInfo.isSupported = true;
156 // ARGB_4444 is upconverted to RGBA_8888
157 case kARGB_4444_SkColorType:
158 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
159 formatInfo.format = GL_RGBA;
160 formatInfo.type = GL_UNSIGNED_BYTE;
161 break;
162 case kRGBA_F16_SkColorType:
163 formatInfo.isSupported = hasFP16Support();
164 if (formatInfo.isSupported) {
165 formatInfo.type = GL_HALF_FLOAT;
166 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
167 } else {
168 formatInfo.type = GL_UNSIGNED_BYTE;
169 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
170 }
171 formatInfo.format = GL_RGBA;
172 break;
173 case kRGB_565_SkColorType:
174 formatInfo.isSupported = true;
175 formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
176 formatInfo.format = GL_RGB;
177 formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
178 break;
179 case kGray_8_SkColorType:
180 formatInfo.isSupported = true;
181 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
182 formatInfo.format = GL_LUMINANCE;
183 formatInfo.type = GL_UNSIGNED_BYTE;
184 break;
185 default:
186 ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
187 formatInfo.valid = false;
188 }
189 return formatInfo;
190}
191
192static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
193 if (format.isSupported) {
194 return source;
195 } else {
196 SkBitmap bitmap;
197 const SkImageInfo& info = source.info();
198 bitmap.allocPixels(
199 SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
200 bitmap.eraseColor(0);
201 if (info.colorType() == kRGBA_F16_SkColorType) {
202 // Drawing RGBA_F16 onto ARGB_8888 is not supported
203 source.readPixels(bitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
204 bitmap.getPixels(), bitmap.rowBytes(), 0, 0);
205 } else {
206 SkCanvas canvas(bitmap);
207 canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
208 }
209 return bitmap;
210 }
211}
212
213class ScopedUploadRequest {
214public:
215 ScopedUploadRequest() { beginUpload(); }
216 ~ScopedUploadRequest() { endUpload(); }
217};
218
219sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
220 ATRACE_CALL();
221
222 FormatInfo format = determineFormat(sourceBitmap);
223 if (!format.valid) {
224 return nullptr;
225 }
226
227 ScopedUploadRequest _uploadRequest{};
228
229 SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
230 sp<GraphicBuffer> buffer = new GraphicBuffer(
231 static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
232 format.pixelFormat,
233 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
234 GraphicBuffer::USAGE_SW_READ_NEVER,
235 std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
236 "]");
237
238 status_t error = buffer->initCheck();
239 if (error < 0) {
240 ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
241 return nullptr;
242 }
243
244 EGLDisplay display = getUploadEglDisplay();
245
246 LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
247 uirenderer::renderthread::EglManager::eglErrorString());
248 // We use an EGLImage to access the content of the GraphicBuffer
249 // The EGL image is later bound to a 2D texture
250 EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
251 AutoEglImage autoImage(display, clientBuffer);
252 if (autoImage.image == EGL_NO_IMAGE_KHR) {
253 ALOGW("Could not create EGL image, err =%s",
254 uirenderer::renderthread::EglManager::eglErrorString());
255 return nullptr;
256 }
257
258 {
259 ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
260 EGLSyncKHR fence = sUploadThread->queue().runSync([&]() -> EGLSyncKHR {
261 AutoSkiaGlTexture glTexture;
262 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
263 GL_CHECKPOINT(MODERATE);
264
265 // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
266 // provide.
267 // But asynchronous in sense that driver may upload texture onto hardware buffer when we
268 // first
269 // use it in drawing
270 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format,
271 format.type, bitmap.getPixels());
272 GL_CHECKPOINT(MODERATE);
273
274 EGLSyncKHR uploadFence =
275 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
276 LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
277 eglGetError());
278 glFlush();
279 return uploadFence;
280 });
281
282 EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
283 LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
284 "Failed to wait for the fence %#x", eglGetError());
285
286 eglDestroySyncKHR(display, fence);
287 }
288
289 return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info()));
290}
291
292}; // namespace android::uirenderer