1 /*
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 "renderthread/VulkanManager.h"
22 #include "thread/ThreadBase.h"
23 #include "utils/TimeUtils.h"
24
25 #include <EGL/eglext.h>
26 #include <GLES2/gl2.h>
27 #include <GLES2/gl2ext.h>
28 #include <GLES3/gl3.h>
29 #include <GrContext.h>
30 #include <SkCanvas.h>
31 #include <SkImage.h>
32 #include <utils/GLUtils.h>
33 #include <utils/Trace.h>
34 #include <utils/TraceUtils.h>
35 #include <thread>
36
37 namespace android::uirenderer {
38
39 class AHBUploader;
40 // This helper uploader classes allows us to upload using either EGL or Vulkan using the same
41 // interface.
42 static sp<AHBUploader> sUploader = nullptr;
43
44 struct FormatInfo {
45 PixelFormat pixelFormat;
46 GLint format, type;
47 VkFormat vkFormat;
48 bool isSupported = false;
49 bool valid = true;
50 };
51
52 class AHBUploader : public RefBase {
53 public:
~AHBUploader()54 virtual ~AHBUploader() {}
55
56 // Called to start creation of the Vulkan and EGL contexts on another thread before we actually
57 // need to do an upload.
initialize()58 void initialize() {
59 onInitialize();
60 }
61
destroy()62 void destroy() {
63 std::lock_guard _lock{mLock};
64 LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress");
65 if (mUploadThread) {
66 mUploadThread->requestExit();
67 mUploadThread->join();
68 mUploadThread = nullptr;
69 }
70 onDestroy();
71 }
72
uploadHardwareBitmap(const SkBitmap & bitmap,const FormatInfo & format,sp<GraphicBuffer> graphicBuffer)73 bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
74 sp<GraphicBuffer> graphicBuffer) {
75 ATRACE_CALL();
76 beginUpload();
77 bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer);
78 endUpload();
79 return result;
80 }
81
postIdleTimeoutCheck()82 void postIdleTimeoutCheck() {
83 mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
84 }
85
86 protected:
87 std::mutex mLock;
88 sp<ThreadBase> mUploadThread = nullptr;
89
90 private:
91 virtual void onInitialize() = 0;
92 virtual void onIdle() = 0;
93 virtual void onDestroy() = 0;
94
95 virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
96 sp<GraphicBuffer> graphicBuffer) = 0;
97 virtual void onBeginUpload() = 0;
98
shouldTimeOutLocked()99 bool shouldTimeOutLocked() {
100 nsecs_t durationSince = systemTime() - mLastUpload;
101 return durationSince > 2000_ms;
102 }
103
idleTimeoutCheck()104 void idleTimeoutCheck() {
105 std::lock_guard _lock{mLock};
106 if (mPendingUploads == 0 && shouldTimeOutLocked()) {
107 onIdle();
108 } else {
109 this->postIdleTimeoutCheck();
110 }
111 }
112
beginUpload()113 void beginUpload() {
114 std::lock_guard _lock{mLock};
115 mPendingUploads++;
116
117 if (!mUploadThread) {
118 mUploadThread = new ThreadBase{};
119 }
120 if (!mUploadThread->isRunning()) {
121 mUploadThread->start("GrallocUploadThread");
122 }
123
124 onBeginUpload();
125 }
126
endUpload()127 void endUpload() {
128 std::lock_guard _lock{mLock};
129 mPendingUploads--;
130 mLastUpload = systemTime();
131 }
132
133 int mPendingUploads = 0;
134 nsecs_t mLastUpload = 0;
135 };
136
137 #define FENCE_TIMEOUT 2000000000
138
139 class EGLUploader : public AHBUploader {
140 private:
onInitialize()141 void onInitialize() override {}
onDestroy()142 void onDestroy() override {
143 mEglManager.destroy();
144 }
onIdle()145 void onIdle() override {
146 mEglManager.destroy();
147 }
148
onBeginUpload()149 void onBeginUpload() override {
150 if (!mEglManager.hasEglContext()) {
151 mUploadThread->queue().runSync([this]() {
152 this->mEglManager.initialize();
153 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
154 });
155
156 this->postIdleTimeoutCheck();
157 }
158 }
159
160
getUploadEglDisplay()161 EGLDisplay getUploadEglDisplay() {
162 std::lock_guard _lock{mLock};
163 LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), "Forgot to begin an upload?");
164 return mEglManager.eglDisplay();
165 }
166
onUploadHardwareBitmap(const SkBitmap & bitmap,const FormatInfo & format,sp<GraphicBuffer> graphicBuffer)167 bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
168 sp<GraphicBuffer> graphicBuffer) override {
169 ATRACE_CALL();
170
171 EGLDisplay display = getUploadEglDisplay();
172
173 LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
174 uirenderer::renderthread::EglManager::eglErrorString());
175 // We use an EGLImage to access the content of the GraphicBuffer
176 // The EGL image is later bound to a 2D texture
177 EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
178 AutoEglImage autoImage(display, clientBuffer);
179 if (autoImage.image == EGL_NO_IMAGE_KHR) {
180 ALOGW("Could not create EGL image, err =%s",
181 uirenderer::renderthread::EglManager::eglErrorString());
182 return false;
183 }
184
185 {
186 ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
187 EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR {
188 AutoSkiaGlTexture glTexture;
189 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
190 GL_CHECKPOINT(MODERATE);
191
192 // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
193 // provide.
194 // But asynchronous in sense that driver may upload texture onto hardware buffer
195 // when we first use it in drawing
196 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
197 format.format, format.type, bitmap.getPixels());
198 GL_CHECKPOINT(MODERATE);
199
200 EGLSyncKHR uploadFence =
201 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
202 LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR,
203 "Could not create sync fence %#x", eglGetError());
204 glFlush();
205 return uploadFence;
206 });
207
208 EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
209 LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
210 "Failed to wait for the fence %#x", eglGetError());
211
212 eglDestroySyncKHR(display, fence);
213 }
214 return true;
215 }
216
217 renderthread::EglManager mEglManager;
218 };
219
220 class VkUploader : public AHBUploader {
221 private:
onInitialize()222 void onInitialize() override {
223 std::lock_guard _lock{mLock};
224 if (!mUploadThread) {
225 mUploadThread = new ThreadBase{};
226 }
227 if (!mUploadThread->isRunning()) {
228 mUploadThread->start("GrallocUploadThread");
229 }
230
231 mUploadThread->queue().post([this]() {
232 std::lock_guard _lock{mVkLock};
233 if (!mVulkanManager.hasVkContext()) {
234 mVulkanManager.initialize();
235 }
236 });
237 }
onDestroy()238 void onDestroy() override {
239 mGrContext.reset();
240 mVulkanManager.destroy();
241 }
onIdle()242 void onIdle() override {
243 mGrContext.reset();
244 }
245
onBeginUpload()246 void onBeginUpload() override {
247 {
248 std::lock_guard _lock{mVkLock};
249 if (!mVulkanManager.hasVkContext()) {
250 LOG_ALWAYS_FATAL_IF(mGrContext,
251 "GrContext exists with no VulkanManager for vulkan uploads");
252 mUploadThread->queue().runSync([this]() {
253 mVulkanManager.initialize();
254 });
255 }
256 }
257 if (!mGrContext) {
258 GrContextOptions options;
259 mGrContext = mVulkanManager.createContext(options);
260 LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads");
261 this->postIdleTimeoutCheck();
262 }
263 }
264
onUploadHardwareBitmap(const SkBitmap & bitmap,const FormatInfo & format,sp<GraphicBuffer> graphicBuffer)265 bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
266 sp<GraphicBuffer> graphicBuffer) override {
267 ATRACE_CALL();
268
269 std::lock_guard _lock{mLock};
270
271 sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(),
272 bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()));
273 return (image.get() != nullptr);
274 }
275
276 sk_sp<GrContext> mGrContext;
277 renderthread::VulkanManager mVulkanManager;
278 std::mutex mVkLock;
279 };
280
hasFP16Support()281 bool HardwareBitmapUploader::hasFP16Support() {
282 static std::once_flag sOnce;
283 static bool hasFP16Support = false;
284
285 // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
286 // we don't need to double-check the GLES version/extension.
287 std::call_once(sOnce, []() {
288 sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16,
289 GraphicBuffer::USAGE_HW_TEXTURE |
290 GraphicBuffer::USAGE_SW_WRITE_NEVER |
291 GraphicBuffer::USAGE_SW_READ_NEVER,
292 "tempFp16Buffer");
293 status_t error = buffer->initCheck();
294 hasFP16Support = !error;
295 });
296
297 return hasFP16Support;
298 }
299
determineFormat(const SkBitmap & skBitmap,bool usingGL)300 static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
301 FormatInfo formatInfo;
302 switch (skBitmap.info().colorType()) {
303 case kRGBA_8888_SkColorType:
304 formatInfo.isSupported = true;
305 [[fallthrough]];
306 // ARGB_4444 is upconverted to RGBA_8888
307 case kARGB_4444_SkColorType:
308 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
309 formatInfo.format = GL_RGBA;
310 formatInfo.type = GL_UNSIGNED_BYTE;
311 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
312 break;
313 case kRGBA_F16_SkColorType:
314 formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
315 if (formatInfo.isSupported) {
316 formatInfo.type = GL_HALF_FLOAT;
317 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
318 formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
319 } else {
320 formatInfo.type = GL_UNSIGNED_BYTE;
321 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
322 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
323 }
324 formatInfo.format = GL_RGBA;
325 break;
326 case kRGB_565_SkColorType:
327 formatInfo.isSupported = true;
328 formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565;
329 formatInfo.format = GL_RGB;
330 formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
331 formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
332 break;
333 case kGray_8_SkColorType:
334 formatInfo.isSupported = usingGL;
335 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888;
336 formatInfo.format = GL_LUMINANCE;
337 formatInfo.type = GL_UNSIGNED_BYTE;
338 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
339 break;
340 default:
341 ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
342 formatInfo.valid = false;
343 }
344 return formatInfo;
345 }
346
makeHwCompatible(const FormatInfo & format,const SkBitmap & source)347 static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
348 if (format.isSupported) {
349 return source;
350 } else {
351 SkBitmap bitmap;
352 const SkImageInfo& info = source.info();
353 bitmap.allocPixels(info.makeColorType(kN32_SkColorType));
354
355 SkCanvas canvas(bitmap);
356 canvas.drawColor(0);
357 canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
358
359 return bitmap;
360 }
361 }
362
363
createUploader(bool usingGL)364 static void createUploader(bool usingGL) {
365 static std::mutex lock;
366 std::lock_guard _lock{lock};
367 if (!sUploader.get()) {
368 if (usingGL) {
369 sUploader = new EGLUploader();
370 } else {
371 sUploader = new VkUploader();
372 }
373 }
374 }
375
allocateHardwareBitmap(const SkBitmap & sourceBitmap)376 sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
377 ATRACE_CALL();
378
379 bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
380 uirenderer::RenderPipelineType::SkiaGL;
381
382 FormatInfo format = determineFormat(sourceBitmap, usingGL);
383 if (!format.valid) {
384 return nullptr;
385 }
386
387 SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
388 sp<GraphicBuffer> buffer = new GraphicBuffer(
389 static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()),
390 format.pixelFormat,
391 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
392 GraphicBuffer::USAGE_SW_READ_NEVER,
393 std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) +
394 "]");
395
396 status_t error = buffer->initCheck();
397 if (error < 0) {
398 ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
399 return nullptr;
400 }
401
402 createUploader(usingGL);
403
404 if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) {
405 return nullptr;
406 }
407 return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(),
408 bitmap.alphaType(), Bitmap::computePalette(bitmap));
409 }
410
initialize()411 void HardwareBitmapUploader::initialize() {
412 bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
413 uirenderer::RenderPipelineType::SkiaGL;
414 createUploader(usingGL);
415 sUploader->initialize();
416 }
417
terminate()418 void HardwareBitmapUploader::terminate() {
419 if (sUploader) {
420 sUploader->destroy();
421 }
422 }
423
424 } // namespace android::uirenderer
425