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