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: 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. 58 void initialize() { 59 onInitialize(); 60 } 61 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 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 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 99 bool shouldTimeOutLocked() { 100 nsecs_t durationSince = systemTime() - mLastUpload; 101 return durationSince > 2000_ms; 102 } 103 104 void idleTimeoutCheck() { 105 std::lock_guard _lock{mLock}; 106 if (mPendingUploads == 0 && shouldTimeOutLocked()) { 107 onIdle(); 108 } else { 109 this->postIdleTimeoutCheck(); 110 } 111 } 112 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 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: 141 void onInitialize() override {} 142 void onDestroy() override { 143 mEglManager.destroy(); 144 } 145 void onIdle() override { 146 mEglManager.destroy(); 147 } 148 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 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 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: 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 } 238 void onDestroy() override { 239 mGrContext.reset(); 240 mVulkanManager.destroy(); 241 } 242 void onIdle() override { 243 mGrContext.reset(); 244 } 245 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 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 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 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 // ARGB_4444 is upconverted to RGBA_8888 306 case kARGB_4444_SkColorType: 307 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; 308 formatInfo.format = GL_RGBA; 309 formatInfo.type = GL_UNSIGNED_BYTE; 310 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; 311 break; 312 case kRGBA_F16_SkColorType: 313 formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support(); 314 if (formatInfo.isSupported) { 315 formatInfo.type = GL_HALF_FLOAT; 316 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16; 317 formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; 318 } else { 319 formatInfo.type = GL_UNSIGNED_BYTE; 320 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; 321 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; 322 } 323 formatInfo.format = GL_RGBA; 324 break; 325 case kRGB_565_SkColorType: 326 formatInfo.isSupported = true; 327 formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565; 328 formatInfo.format = GL_RGB; 329 formatInfo.type = GL_UNSIGNED_SHORT_5_6_5; 330 formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; 331 break; 332 case kGray_8_SkColorType: 333 formatInfo.isSupported = usingGL; 334 formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; 335 formatInfo.format = GL_LUMINANCE; 336 formatInfo.type = GL_UNSIGNED_BYTE; 337 formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; 338 break; 339 default: 340 ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType()); 341 formatInfo.valid = false; 342 } 343 return formatInfo; 344 } 345 346 static SkBitmap makeHwCompatible(const FormatInfo& format, const SkBitmap& source) { 347 if (format.isSupported) { 348 return source; 349 } else { 350 SkBitmap bitmap; 351 const SkImageInfo& info = source.info(); 352 bitmap.allocPixels(info.makeColorType(kN32_SkColorType)); 353 354 SkCanvas canvas(bitmap); 355 canvas.drawColor(0); 356 canvas.drawBitmap(source, 0.0f, 0.0f, nullptr); 357 358 return bitmap; 359 } 360 } 361 362 363 static void createUploader(bool usingGL) { 364 static std::mutex lock; 365 std::lock_guard _lock{lock}; 366 if (!sUploader.get()) { 367 if (usingGL) { 368 sUploader = new EGLUploader(); 369 } else { 370 sUploader = new VkUploader(); 371 } 372 } 373 } 374 375 sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sourceBitmap) { 376 ATRACE_CALL(); 377 378 bool usingGL = uirenderer::Properties::getRenderPipelineType() == 379 uirenderer::RenderPipelineType::SkiaGL; 380 381 FormatInfo format = determineFormat(sourceBitmap, usingGL); 382 if (!format.valid) { 383 return nullptr; 384 } 385 386 SkBitmap bitmap = makeHwCompatible(format, sourceBitmap); 387 sp<GraphicBuffer> buffer = new GraphicBuffer( 388 static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()), 389 format.pixelFormat, 390 GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | 391 GraphicBuffer::USAGE_SW_READ_NEVER, 392 std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + 393 "]"); 394 395 status_t error = buffer->initCheck(); 396 if (error < 0) { 397 ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); 398 return nullptr; 399 } 400 401 createUploader(usingGL); 402 403 if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { 404 return nullptr; 405 } 406 return Bitmap::createFrom(buffer, bitmap.colorType(), bitmap.refColorSpace(), 407 bitmap.alphaType(), Bitmap::computePalette(bitmap)); 408 } 409 410 void HardwareBitmapUploader::initialize() { 411 bool usingGL = uirenderer::Properties::getRenderPipelineType() == 412 uirenderer::RenderPipelineType::SkiaGL; 413 createUploader(usingGL); 414 sUploader->initialize(); 415 } 416 417 void HardwareBitmapUploader::terminate() { 418 if (sUploader) { 419 sUploader->destroy(); 420 } 421 } 422 423 } // namespace android::uirenderer 424