1 /*
2  * Copyright 2019 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 "AutoBackendTextureRelease.h"
18 
19 #include "renderthread/RenderThread.h"
20 #include "utils/Color.h"
21 #include "utils/PaintUtils.h"
22 
23 using namespace android::uirenderer::renderthread;
24 
25 namespace android {
26 namespace uirenderer {
27 
AutoBackendTextureRelease(GrDirectContext * context,AHardwareBuffer * buffer)28 AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context,
29                                                      AHardwareBuffer* buffer) {
30     AHardwareBuffer_Desc desc;
31     AHardwareBuffer_describe(buffer, &desc);
32     bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
33     GrBackendFormat backendFormat =
34             GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
35     mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture(
36             context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx,
37             createProtectedImage, backendFormat, false);
38 }
39 
unref(bool releaseImage)40 void AutoBackendTextureRelease::unref(bool releaseImage) {
41     if (!RenderThread::isCurrent()) {
42         // EGLImage needs to be destroyed on RenderThread to prevent memory leak.
43         // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not
44         // thread safe.
45         RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); });
46         return;
47     }
48 
49     if (releaseImage) {
50         mImage.reset();
51     }
52 
53     mUsageCount--;
54     if (mUsageCount <= 0) {
55         if (mBackendTexture.isValid()) {
56             mDeleteProc(mImageCtx);
57             mBackendTexture = {};
58         }
59         delete this;
60     }
61 }
62 
63 // releaseProc is invoked by SkImage, when texture is no longer in use.
64 // "releaseContext" contains an "AutoBackendTextureRelease*".
releaseProc(SkImage::ReleaseContext releaseContext)65 static void releaseProc(SkImage::ReleaseContext releaseContext) {
66     AutoBackendTextureRelease* textureRelease =
67             reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
68     textureRelease->unref(false);
69 }
70 
makeImage(AHardwareBuffer * buffer,android_dataspace dataspace,GrDirectContext * context)71 void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer,
72                                           android_dataspace dataspace,
73                                           GrDirectContext* context) {
74     AHardwareBuffer_Desc desc;
75     AHardwareBuffer_describe(buffer, &desc);
76     SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
77     mImage = SkImage::MakeFromTexture(
78             context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType,
79             uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this);
80     if (mImage.get()) {
81         // The following ref will be counteracted by releaseProc, when SkImage is discarded.
82         ref();
83     }
84 }
85 
newBufferContent(GrDirectContext * context)86 void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) {
87     if (mBackendTexture.isValid()) {
88         mUpdateProc(mImageCtx, context);
89     }
90 }
91 
releaseQueueOwnership(GrDirectContext * context)92 void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) {
93     if (!context) {
94         return;
95     }
96 
97     LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
98     if (mBackendTexture.isValid()) {
99         // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
100         GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED,
101                                               VK_QUEUE_FAMILY_FOREIGN_EXT);
102 
103         // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The
104         // releaseProc callback will be made when the work to set the new state has finished on the
105         // gpu.
106         ref();
107         // Note that we don't have an explicit call to set the backend texture back onto the
108         // graphics queue when we use the VkImage again. Internally, Skia will notice that the image
109         // is not on the graphics queue and will do the transition automatically.
110         context->setBackendTextureState(mBackendTexture, newState, nullptr, releaseProc, this);
111     }
112 }
113 
114 } /* namespace uirenderer */
115 } /* namespace android */
116