blob: c2d2ff87481659b5368de20c42f0373b8a888080 [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
Alec Mouri45238012020-01-29 11:04:40 -080019#include <EGL/egl.h>
John Recke170fb62018-05-07 08:12:07 -070020#include <EGL/eglext.h>
21#include <GLES2/gl2.h>
22#include <GLES2/gl2ext.h>
23#include <GLES3/gl3.h>
Greg Danielc0732522019-02-20 08:31:03 -050024#include <GrContext.h>
John Recke170fb62018-05-07 08:12:07 -070025#include <SkCanvas.h>
Greg Danielc0732522019-02-20 08:31:03 -050026#include <SkImage.h>
John Recke170fb62018-05-07 08:12:07 -070027#include <utils/GLUtils.h>
Alec Mouri45238012020-01-29 11:04:40 -080028#include <utils/NdkUtils.h>
John Recke170fb62018-05-07 08:12:07 -070029#include <utils/Trace.h>
30#include <utils/TraceUtils.h>
Alec Mouri45238012020-01-29 11:04:40 -080031
John Recke170fb62018-05-07 08:12:07 -070032#include <thread>
33
Alec Mouri45238012020-01-29 11:04:40 -080034#include "hwui/Bitmap.h"
35#include "renderthread/EglManager.h"
36#include "renderthread/VulkanManager.h"
37#include "thread/ThreadBase.h"
38#include "utils/TimeUtils.h"
39
John Recke170fb62018-05-07 08:12:07 -070040namespace android::uirenderer {
41
Greg Danielc0732522019-02-20 08:31:03 -050042class AHBUploader;
43// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
44// interface.
45static sp<AHBUploader> sUploader = nullptr;
John Recke170fb62018-05-07 08:12:07 -070046
Greg Danielc0732522019-02-20 08:31:03 -050047struct FormatInfo {
Alec Mouri45238012020-01-29 11:04:40 -080048 AHardwareBuffer_Format bufferFormat;
Greg Danielc0732522019-02-20 08:31:03 -050049 GLint format, type;
50 VkFormat vkFormat;
51 bool isSupported = false;
52 bool valid = true;
53};
John Recke170fb62018-05-07 08:12:07 -070054
Greg Danielc0732522019-02-20 08:31:03 -050055class AHBUploader : public RefBase {
56public:
57 virtual ~AHBUploader() {}
John Recke170fb62018-05-07 08:12:07 -070058
Greg Danielc0732522019-02-20 08:31:03 -050059 // Called to start creation of the Vulkan and EGL contexts on another thread before we actually
60 // need to do an upload.
61 void initialize() {
62 onInitialize();
John Recke170fb62018-05-07 08:12:07 -070063 }
64
Greg Danielc0732522019-02-20 08:31:03 -050065 void destroy() {
66 std::lock_guard _lock{mLock};
67 LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress");
68 if (mUploadThread) {
69 mUploadThread->requestExit();
70 mUploadThread->join();
71 mUploadThread = nullptr;
72 }
73 onDestroy();
John Recke170fb62018-05-07 08:12:07 -070074 }
75
Greg Danielc0732522019-02-20 08:31:03 -050076 bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
Alec Mouri45238012020-01-29 11:04:40 -080077 AHardwareBuffer* ahb) {
Greg Danielc0732522019-02-20 08:31:03 -050078 ATRACE_CALL();
79 beginUpload();
Alec Mouri45238012020-01-29 11:04:40 -080080 bool result = onUploadHardwareBitmap(bitmap, format, ahb);
Greg Danielc0732522019-02-20 08:31:03 -050081 endUpload();
82 return result;
83 }
84
85 void postIdleTimeoutCheck() {
86 mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
87 }
88
89protected:
90 std::mutex mLock;
91 sp<ThreadBase> mUploadThread = nullptr;
92
93private:
94 virtual void onInitialize() = 0;
95 virtual void onIdle() = 0;
96 virtual void onDestroy() = 0;
97
98 virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
Alec Mouri45238012020-01-29 11:04:40 -080099 AHardwareBuffer* ahb) = 0;
Greg Danielc0732522019-02-20 08:31:03 -0500100 virtual void onBeginUpload() = 0;
101
102 bool shouldTimeOutLocked() {
103 nsecs_t durationSince = systemTime() - mLastUpload;
104 return durationSince > 2000_ms;
105 }
106
107 void idleTimeoutCheck() {
108 std::lock_guard _lock{mLock};
109 if (mPendingUploads == 0 && shouldTimeOutLocked()) {
110 onIdle();
111 } else {
112 this->postIdleTimeoutCheck();
113 }
114 }
115
116 void beginUpload() {
117 std::lock_guard _lock{mLock};
118 mPendingUploads++;
119
120 if (!mUploadThread) {
121 mUploadThread = new ThreadBase{};
122 }
123 if (!mUploadThread->isRunning()) {
124 mUploadThread->start("GrallocUploadThread");
125 }
126
127 onBeginUpload();
128 }
129
130 void endUpload() {
131 std::lock_guard _lock{mLock};
132 mPendingUploads--;
133 mLastUpload = systemTime();
134 }
135
136 int mPendingUploads = 0;
137 nsecs_t mLastUpload = 0;
138};
139
140#define FENCE_TIMEOUT 2000000000
141
142class EGLUploader : public AHBUploader {
143private:
144 void onInitialize() override {}
145 void onDestroy() override {
146 mEglManager.destroy();
147 }
148 void onIdle() override {
149 mEglManager.destroy();
150 }
151
152 void onBeginUpload() override {
153 if (!mEglManager.hasEglContext()) {
154 mUploadThread->queue().runSync([this]() {
155 this->mEglManager.initialize();
156 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
157 });
158
159 this->postIdleTimeoutCheck();
160 }
161 }
162
163
164 EGLDisplay getUploadEglDisplay() {
165 std::lock_guard _lock{mLock};
166 LOG_ALWAYS_FATAL_IF(!mEglManager.hasEglContext(), "Forgot to begin an upload?");
167 return mEglManager.eglDisplay();
168 }
169
170 bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
Alec Mouri45238012020-01-29 11:04:40 -0800171 AHardwareBuffer* ahb) override {
Greg Danielc0732522019-02-20 08:31:03 -0500172 ATRACE_CALL();
173
174 EGLDisplay display = getUploadEglDisplay();
175
176 LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
177 uirenderer::renderthread::EglManager::eglErrorString());
Alec Mouri45238012020-01-29 11:04:40 -0800178 // We use an EGLImage to access the content of the buffer
Greg Danielc0732522019-02-20 08:31:03 -0500179 // The EGL image is later bound to a 2D texture
Alec Mouri70463a62020-03-30 15:10:17 -0700180 const EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(ahb);
Greg Danielc0732522019-02-20 08:31:03 -0500181 AutoEglImage autoImage(display, clientBuffer);
182 if (autoImage.image == EGL_NO_IMAGE_KHR) {
183 ALOGW("Could not create EGL image, err =%s",
184 uirenderer::renderthread::EglManager::eglErrorString());
185 return false;
186 }
187
188 {
189 ATRACE_FORMAT("CPU -> gralloc transfer (%dx%d)", bitmap.width(), bitmap.height());
190 EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR {
191 AutoSkiaGlTexture glTexture;
192 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
John Reck996773a2020-02-03 16:30:56 -0800193 if (GLUtils::dumpGLErrors()) {
194 return EGL_NO_SYNC_KHR;
195 }
Greg Danielc0732522019-02-20 08:31:03 -0500196
197 // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we
198 // provide.
199 // But asynchronous in sense that driver may upload texture onto hardware buffer
200 // when we first use it in drawing
201 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
202 format.format, format.type, bitmap.getPixels());
John Reck996773a2020-02-03 16:30:56 -0800203 if (GLUtils::dumpGLErrors()) {
204 return EGL_NO_SYNC_KHR;
205 }
Greg Danielc0732522019-02-20 08:31:03 -0500206
207 EGLSyncKHR uploadFence =
208 eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
John Reck996773a2020-02-03 16:30:56 -0800209 if (uploadFence == EGL_NO_SYNC_KHR) {
210 ALOGW("Could not create sync fence %#x", eglGetError());
211 };
Greg Danielc0732522019-02-20 08:31:03 -0500212 glFlush();
John Reck996773a2020-02-03 16:30:56 -0800213 GLUtils::dumpGLErrors();
Greg Danielc0732522019-02-20 08:31:03 -0500214 return uploadFence;
215 });
216
John Reck996773a2020-02-03 16:30:56 -0800217 if (fence == EGL_NO_SYNC_KHR) {
218 return false;
219 }
Greg Danielc0732522019-02-20 08:31:03 -0500220 EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT);
John Reck996773a2020-02-03 16:30:56 -0800221 ALOGE_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
222 "Failed to wait for the fence %#x", eglGetError());
Greg Danielc0732522019-02-20 08:31:03 -0500223
224 eglDestroySyncKHR(display, fence);
225 }
226 return true;
227 }
228
229 renderthread::EglManager mEglManager;
230};
231
232class VkUploader : public AHBUploader {
233private:
234 void onInitialize() override {
235 std::lock_guard _lock{mLock};
236 if (!mUploadThread) {
237 mUploadThread = new ThreadBase{};
238 }
239 if (!mUploadThread->isRunning()) {
240 mUploadThread->start("GrallocUploadThread");
241 }
242
243 mUploadThread->queue().post([this]() {
244 std::lock_guard _lock{mVkLock};
245 if (!mVulkanManager.hasVkContext()) {
246 mVulkanManager.initialize();
247 }
John Recke170fb62018-05-07 08:12:07 -0700248 });
John Recke170fb62018-05-07 08:12:07 -0700249 }
Greg Danielc0732522019-02-20 08:31:03 -0500250 void onDestroy() override {
251 mGrContext.reset();
252 mVulkanManager.destroy();
253 }
254 void onIdle() override {
255 mGrContext.reset();
256 }
John Recke170fb62018-05-07 08:12:07 -0700257
Greg Danielc0732522019-02-20 08:31:03 -0500258 void onBeginUpload() override {
259 {
260 std::lock_guard _lock{mVkLock};
261 if (!mVulkanManager.hasVkContext()) {
262 LOG_ALWAYS_FATAL_IF(mGrContext,
263 "GrContext exists with no VulkanManager for vulkan uploads");
264 mUploadThread->queue().runSync([this]() {
265 mVulkanManager.initialize();
266 });
267 }
268 }
269 if (!mGrContext) {
270 GrContextOptions options;
271 mGrContext = mVulkanManager.createContext(options);
272 LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads");
273 this->postIdleTimeoutCheck();
274 }
275 }
John Recke170fb62018-05-07 08:12:07 -0700276
Greg Danielc0732522019-02-20 08:31:03 -0500277 bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format,
Alec Mouri45238012020-01-29 11:04:40 -0800278 AHardwareBuffer* ahb) override {
Greg Danielc0732522019-02-20 08:31:03 -0500279 ATRACE_CALL();
280
281 std::lock_guard _lock{mLock};
282
Alec Mouri45238012020-01-29 11:04:40 -0800283 sk_sp<SkImage> image =
284 SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb);
Greg Danielc0732522019-02-20 08:31:03 -0500285 return (image.get() != nullptr);
286 }
287
288 sk_sp<GrContext> mGrContext;
289 renderthread::VulkanManager mVulkanManager;
290 std::mutex mVkLock;
291};
John Recke170fb62018-05-07 08:12:07 -0700292
Leon Scroggins IIIee3bfe72019-01-31 08:42:23 -0500293bool HardwareBitmapUploader::hasFP16Support() {
John Recke170fb62018-05-07 08:12:07 -0700294 static std::once_flag sOnce;
295 static bool hasFP16Support = false;
296
297 // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
298 // we don't need to double-check the GLES version/extension.
299 std::call_once(sOnce, []() {
Alec Mouri45238012020-01-29 11:04:40 -0800300 AHardwareBuffer_Desc desc = {
301 .width = 1,
302 .height = 1,
303 .layers = 1,
304 .format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
305 .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
306 AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
307 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
308 };
309 UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc);
310 hasFP16Support = buffer != nullptr;
John Recke170fb62018-05-07 08:12:07 -0700311 });
312
313 return hasFP16Support;
314}
315
Greg Danielc0732522019-02-20 08:31:03 -0500316static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
John Recke170fb62018-05-07 08:12:07 -0700317 FormatInfo formatInfo;
John Recke170fb62018-05-07 08:12:07 -0700318 switch (skBitmap.info().colorType()) {
319 case kRGBA_8888_SkColorType:
320 formatInfo.isSupported = true;
Colin Crossf4e74b82019-10-31 13:47:42 -0700321 [[fallthrough]];
John Recke170fb62018-05-07 08:12:07 -0700322 // ARGB_4444 is upconverted to RGBA_8888
323 case kARGB_4444_SkColorType:
Alec Mouri45238012020-01-29 11:04:40 -0800324 formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700325 formatInfo.format = GL_RGBA;
326 formatInfo.type = GL_UNSIGNED_BYTE;
Greg Danielc0732522019-02-20 08:31:03 -0500327 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700328 break;
329 case kRGBA_F16_SkColorType:
Leon Scroggins IIIee3bfe72019-01-31 08:42:23 -0500330 formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
John Recke170fb62018-05-07 08:12:07 -0700331 if (formatInfo.isSupported) {
332 formatInfo.type = GL_HALF_FLOAT;
Alec Mouri45238012020-01-29 11:04:40 -0800333 formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
Greg Danielc0732522019-02-20 08:31:03 -0500334 formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
John Recke170fb62018-05-07 08:12:07 -0700335 } else {
336 formatInfo.type = GL_UNSIGNED_BYTE;
Alec Mouri45238012020-01-29 11:04:40 -0800337 formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
Greg Danielc0732522019-02-20 08:31:03 -0500338 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700339 }
340 formatInfo.format = GL_RGBA;
341 break;
342 case kRGB_565_SkColorType:
343 formatInfo.isSupported = true;
Alec Mouri45238012020-01-29 11:04:40 -0800344 formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700345 formatInfo.format = GL_RGB;
346 formatInfo.type = GL_UNSIGNED_SHORT_5_6_5;
Greg Danielc0732522019-02-20 08:31:03 -0500347 formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
John Recke170fb62018-05-07 08:12:07 -0700348 break;
349 case kGray_8_SkColorType:
Greg Danielc0732522019-02-20 08:31:03 -0500350 formatInfo.isSupported = usingGL;
Alec Mouri45238012020-01-29 11:04:40 -0800351 formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700352 formatInfo.format = GL_LUMINANCE;
353 formatInfo.type = GL_UNSIGNED_BYTE;
Greg Danielc0732522019-02-20 08:31:03 -0500354 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
John Recke170fb62018-05-07 08:12:07 -0700355 break;
356 default:
357 ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
358 formatInfo.valid = false;
359 }
360 return formatInfo;
361}
362
363static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) {
364 if (format.isSupported) {
365 return source;
366 } else {
367 SkBitmap bitmap;
368 const SkImageInfo& info = source.info();
Greg Daniel78b7ddc2019-03-27 12:52:43 -0400369 bitmap.allocPixels(info.makeColorType(kN32_SkColorType));
Derek Sollenberger6e35e632019-01-22 13:56:25 -0500370
371 SkCanvas canvas(bitmap);
372 canvas.drawColor(0);
373 canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
374
John Recke170fb62018-05-07 08:12:07 -0700375 return bitmap;
376 }
377}
378
Greg Danielc0732522019-02-20 08:31:03 -0500379
380static void createUploader(bool usingGL) {
381 static std::mutex lock;
382 std::lock_guard _lock{lock};
383 if (!sUploader.get()) {
384 if (usingGL) {
385 sUploader = new EGLUploader();
386 } else {
387 sUploader = new VkUploader();
388 }
389 }
390}
John Recke170fb62018-05-07 08:12:07 -0700391
392sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) {
393 ATRACE_CALL();
394
Greg Danielc0732522019-02-20 08:31:03 -0500395 bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
396 uirenderer::RenderPipelineType::SkiaGL;
397
398 FormatInfo format = determineFormat(sourceBitmap, usingGL);
John Recke170fb62018-05-07 08:12:07 -0700399 if (!format.valid) {
400 return nullptr;
401 }
402
John Recke170fb62018-05-07 08:12:07 -0700403 SkBitmap bitmap = makeHwCompatible(format, sourceBitmap);
Alec Mouri45238012020-01-29 11:04:40 -0800404 AHardwareBuffer_Desc desc = {
405 .width = static_cast<uint32_t>(bitmap.width()),
406 .height = static_cast<uint32_t>(bitmap.height()),
407 .layers = 1,
408 .format = format.bufferFormat,
409 .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
410 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
411 };
412 UniqueAHardwareBuffer ahb = allocateAHardwareBuffer(desc);
413 if (!ahb) {
414 ALOGW("allocateHardwareBitmap() failed in AHardwareBuffer_allocate()");
John Recke170fb62018-05-07 08:12:07 -0700415 return nullptr;
Alec Mouri45238012020-01-29 11:04:40 -0800416 };
John Recke170fb62018-05-07 08:12:07 -0700417
Greg Danielc0732522019-02-20 08:31:03 -0500418 createUploader(usingGL);
John Recke170fb62018-05-07 08:12:07 -0700419
Alec Mouri45238012020-01-29 11:04:40 -0800420 if (!sUploader->uploadHardwareBitmap(bitmap, format, ahb.get())) {
John Recke170fb62018-05-07 08:12:07 -0700421 return nullptr;
422 }
Alec Mouri45238012020-01-29 11:04:40 -0800423 return Bitmap::createFrom(ahb.get(), bitmap.colorType(), bitmap.refColorSpace(),
424 bitmap.alphaType(), Bitmap::computePalette(bitmap));
John Recke170fb62018-05-07 08:12:07 -0700425}
426
Greg Danielc0732522019-02-20 08:31:03 -0500427void HardwareBitmapUploader::initialize() {
428 bool usingGL = uirenderer::Properties::getRenderPipelineType() ==
429 uirenderer::RenderPipelineType::SkiaGL;
430 createUploader(usingGL);
431 sUploader->initialize();
432}
433
John Reck6104cea2019-01-10 14:37:17 -0800434void HardwareBitmapUploader::terminate() {
Greg Daniel78b7ddc2019-03-27 12:52:43 -0400435 if (sUploader) {
436 sUploader->destroy();
437 }
John Reck6104cea2019-01-10 14:37:17 -0800438}
439
Chris Blume7b8a8082018-11-30 15:51:58 -0800440} // namespace android::uirenderer