blob: aeeb32c4620a329e67652ef2741b87a3280179c9 [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{};
John Reck6104cea2019-01-10 14:37:17 -080037static sp<ThreadBase> sUploadThread = nullptr;
John Recke170fb62018-05-07 08:12:07 -070038static 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() {
John Reckd1a491f2018-09-17 15:01:58 -070048 std::lock_guard _lock{sLock};
John Recke170fb62018-05-07 08:12:07 -070049 if (sPendingUploads == 0 && shouldTimeOutLocked()) {
50 sEglManager.destroy();
51 } else {
52 sUploadThread->queue().postDelayed(5000_ms, checkIdleTimeout);
53 }
54}
55
56static void beginUpload() {
John Reckd1a491f2018-09-17 15:01:58 -070057 std::lock_guard _lock{sLock};
John Recke170fb62018-05-07 08:12:07 -070058 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() {
John Reckd1a491f2018-09-17 15:01:58 -070078 std::lock_guard _lock{sLock};
John Recke170fb62018-05-07 08:12:07 -070079 sPendingUploads--;
80 sLastUpload = systemTime();
81}
82
83static EGLDisplay getUploadEglDisplay() {
John Reckd1a491f2018-09-17 15:01:58 -070084 std::lock_guard _lock{sLock};
John Recke170fb62018-05-07 08:12:07 -070085 LOG_ALWAYS_FATAL_IF(!sEglManager.hasEglContext(), "Forgot to begin an upload?");
86 return sEglManager.eglDisplay();
87}
88
Leon Scroggins IIIee3bfe72019-01-31 08:42:23 -050089bool HardwareBitmapUploader::hasFP16Support() {
John Recke170fb62018-05-07 08:12:07 -070090 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
John Recke170fb62018-05-07 08:12:07 -0700110struct FormatInfo {
111 PixelFormat pixelFormat;
112 GLint format, type;
113 bool isSupported = false;
114 bool valid = true;
115};
116
117static FormatInfo determineFormat(const SkBitmap& skBitmap) {
118 FormatInfo formatInfo;
John Recke170fb62018-05-07 08:12:07 -0700119 switch (skBitmap.info().colorType()) {
120 case kRGBA_8888_SkColorType:
121 formatInfo.isSupported = true;
122 // ARGB_4444 is upconverted to RGBA_8888
123 case kARGB_4444_SkColorType:
124 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
125 formatInfo.format = GL_RGBA;
126 formatInfo.type = GL_UNSIGNED_BYTE;
127 break;
128 case kRGBA_F16_SkColorType:
Leon Scroggins IIIee3bfe72019-01-31 08:42:23 -0500129 formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
John Recke170fb62018-05-07 08:12:07 -0700130 if (formatInfo.isSupported) {
131 formatInfo.type = GL_HALF_FLOAT;
132 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
133 } else {
134 formatInfo.type = GL_UNSIGNED_BYTE;
135 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
136 }
137 formatInfo.format = GL_RGBA;
138 break;
139 case kRGB_565_SkColorType:
140 formatInfo.isSupported = true;
141 formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
142 formatInfo.format = GL_RGB;
143 formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
144 break;
145 case kGray_8_SkColorType:
146 formatInfo.isSupported = true;
147 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
148 formatInfo.format = GL_LUMINANCE;
149 formatInfo.type = GL_UNSIGNED_BYTE;
150 break;
151 default:
152 ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
153 formatInfo.valid = false;
154 }
155 return formatInfo;
156}
157
158static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
159 if (format.isSupported) {
160 return source;
161 } else {
162 SkBitmap bitmap;
163 const SkImageInfo& info = source.info();
164 bitmap.allocPixels(
165 SkImageInfo::MakeN32(info.width(), info.height(), info.alphaType(), nullptr));
Derek Sollenberger6e35e632019-01-22 13:56:25 -0500166
167 SkCanvas canvas(bitmap);
168 canvas.drawColor(0);
169 canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
170
John Recke170fb62018-05-07 08:12:07 -0700171 return bitmap;
172 }
173}
174
175class ScopedUploadRequest {
176public:
177 ScopedUploadRequest() { beginUpload(); }
178 ~ScopedUploadRequest() { endUpload(); }
179};
180
181sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
182 ATRACE_CALL();
183
184 FormatInfo format = determineFormat(sourceBitmap);
185 if (!format.valid) {
186 return nullptr;
187 }
188
189 ScopedUploadRequest _uploadRequest{};
190
191 SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
192 sp<GraphicBuffer> buffer = new GraphicBuffer(
193 static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
194 format.pixelFormat,
195 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
196 GraphicBuffer::USAGE_SW_READ_NEVER,
197 std::string("Bitmap::allocateSkiaHardwareBitmap pid [") + std::to_string(getpid()) +
198 "]");
199
200 status_t error = buffer->initCheck();
201 if (error < 0) {
202 ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
203 return nullptr;
204 }
205
206 EGLDisplay display = getUploadEglDisplay();
207
208 LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
209 uirenderer::renderthread::EglManager::eglErrorString());
210 // We use an EGLImage to access the content of the GraphicBuffer
211 // The EGL image is later bound to a 2D texture
212 EGLClientBuffer clientBuffer = (EGLClientBuffer)buffer->getNativeBuffer();
213 AutoEglImage autoImage(display, clientBuffer);
214 if (autoImage.image == EGL_NO_IMAGE_KHR) {
215 ALOGW("Could not create EGL image, err =%s",
216 uirenderer::renderthread::EglManager::eglErrorString());
217 return nullptr;
218 }
219
220 {
221 ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
222 EGLSyncKHR fence = sUploadThread->queue().runSync([&]() -> EGLSyncKHR {
223 AutoSkiaGlTexture glTexture;
224 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
225 GL_CHECKPOINT(MODERATE);
226
227 // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
228 // provide.
229 // But asynchronous in sense that driver may upload texture onto hardware buffer when we
230 // first
231 // use it in drawing
232 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format,
233 format.type, bitmap.getPixels());
234 GL_CHECKPOINT(MODERATE);
235
236 EGLSyncKHR uploadFence =
237 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
238 LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, "Could not create sync fence %#x",
239 eglGetError());
240 glFlush();
241 return uploadFence;
242 });
243
244 EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
245 LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
246 "Failed to wait for the fence %#x", eglGetError());
247
248 eglDestroySyncKHR(display, fence);
249 }
250
Derek Sollenberger6e35e632019-01-22 13:56:25 -0500251 return Bitmap::createFrom(buffer.get(), bitmap.colorType(), bitmap.refColorSpace(),
252 bitmap.alphaType(), Bitmap::computePalette(bitmap));
John Recke170fb62018-05-07 08:12:07 -0700253}
254
John Reck6104cea2019-01-10 14:37:17 -0800255void HardwareBitmapUploader::terminate() {
256 std::lock_guard _lock{sLock};
257 LOG_ALWAYS_FATAL_IF(sPendingUploads, "terminate called while uploads in progress");
258 if (sUploadThread) {
259 sUploadThread->requestExit();
260 sUploadThread->join();
261 sUploadThread = nullptr;
262 }
263 sEglManager.destroy();
264}
265
Chris Blume7b8a8082018-11-30 15:51:58 -0800266} // namespace android::uirenderer