xref: /aosp_15_r20/frameworks/base/libs/hwui/AutoBackendTextureRelease.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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 <SkImage.h>
20 #include <include/gpu/MutableTextureState.h>
21 #include <include/gpu/ganesh/GrBackendSurface.h>
22 #include <include/gpu/ganesh/GrDirectContext.h>
23 #include <include/gpu/ganesh/SkImageGanesh.h>
24 #include <include/gpu/vk/VulkanMutableTextureState.h>
25 
26 #include "renderthread/RenderThread.h"
27 #include "utils/Color.h"
28 #include "utils/PaintUtils.h"
29 
30 using namespace android::uirenderer::renderthread;
31 
32 namespace android {
33 namespace uirenderer {
34 
AutoBackendTextureRelease(GrDirectContext * context,AHardwareBuffer * buffer)35 AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context,
36                                                      AHardwareBuffer* buffer) {
37     AHardwareBuffer_Desc desc;
38     AHardwareBuffer_describe(buffer, &desc);
39     bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
40 
41     GrBackendFormat backendFormat;
42     GrBackendApi backend = context->backend();
43     if (backend == GrBackendApi::kOpenGL) {
44         backendFormat =
45                 GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
46         mBackendTexture =
47                 GrAHardwareBufferUtils::MakeGLBackendTexture(context,
48                                                              buffer,
49                                                              desc.width,
50                                                              desc.height,
51                                                              &mDeleteProc,
52                                                              &mUpdateProc,
53                                                              &mImageCtx,
54                                                              createProtectedImage,
55                                                              backendFormat,
56                                                              false);
57     } else if (backend == GrBackendApi::kVulkan) {
58         backendFormat =
59                 GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
60                                                                buffer,
61                                                                desc.format,
62                                                                false);
63         mBackendTexture =
64                 GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
65                                                                  buffer,
66                                                                  desc.width,
67                                                                  desc.height,
68                                                                  &mDeleteProc,
69                                                                  &mUpdateProc,
70                                                                  &mImageCtx,
71                                                                  createProtectedImage,
72                                                                  backendFormat,
73                                                                  false);
74     } else {
75         LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
76     }
77     LOG_ALWAYS_FATAL_IF(!backendFormat.isValid(),
78                         __FILE__ " Invalid GrBackendFormat. GrBackendApi==%" PRIu32
79                                  ", AHardwareBuffer_Format==%" PRIu32 ".",
80                         static_cast<int>(context->backend()), desc.format);
81     LOG_ALWAYS_FATAL_IF(!mBackendTexture.isValid(),
82                         __FILE__ " Invalid GrBackendTexture. Width==%" PRIu32 ", height==%" PRIu32
83                                  ", protected==%d",
84                         desc.width, desc.height, createProtectedImage);
85 }
86 
unref(bool releaseImage)87 void AutoBackendTextureRelease::unref(bool releaseImage) {
88     if (!RenderThread::isCurrent()) {
89         // EGLImage needs to be destroyed on RenderThread to prevent memory leak.
90         // ~SkImage dtor for both pipelines needs to be invoked on RenderThread, because it is not
91         // thread safe.
92         RenderThread::getInstance().queue().post([this, releaseImage]() { unref(releaseImage); });
93         return;
94     }
95 
96     if (releaseImage) {
97         mImage.reset();
98     }
99 
100     mUsageCount--;
101     if (mUsageCount <= 0) {
102         if (mBackendTexture.isValid()) {
103             mDeleteProc(mImageCtx);
104             mBackendTexture = {};
105         }
106         delete this;
107     }
108 }
109 
110 // releaseProc is invoked by SkImage, when texture is no longer in use.
111 // "releaseContext" contains an "AutoBackendTextureRelease*".
releaseProc(SkImages::ReleaseContext releaseContext)112 static void releaseProc(SkImages::ReleaseContext releaseContext) {
113     AutoBackendTextureRelease* textureRelease =
114             reinterpret_cast<AutoBackendTextureRelease*>(releaseContext);
115     textureRelease->unref(false);
116 }
117 
makeImage(AHardwareBuffer * buffer,android_dataspace dataspace,GrDirectContext * context)118 void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer,
119                                           android_dataspace dataspace,
120                                           GrDirectContext* context) {
121     AHardwareBuffer_Desc desc;
122     AHardwareBuffer_describe(buffer, &desc);
123     SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
124     // The following ref will be counteracted by Skia calling releaseProc, either during
125     // BorrowTextureFrom if there is a failure, or later when SkImage is discarded. It must
126     // be called before BorrowTextureFrom, otherwise Skia may remove HWUI's ref on failure.
127     ref();
128     mImage = SkImages::BorrowTextureFrom(
129             context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType,
130             uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this);
131 }
132 
newBufferContent(GrDirectContext * context)133 void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) {
134     if (mBackendTexture.isValid()) {
135         mUpdateProc(mImageCtx, context);
136     }
137 }
138 
releaseQueueOwnership(GrDirectContext * context)139 void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) {
140     if (!context) {
141         return;
142     }
143 
144     if (!RenderThread::isCurrent()) {
145         // releaseQueueOwnership needs to run on RenderThread to prevent multithread calling
146         // setBackendTextureState will operate skia resource cache which need single owner
147         RenderThread::getInstance().queue().post([this, context]() { releaseQueueOwnership(context); });
148         return;
149     }
150 
151     LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
152     if (mBackendTexture.isValid()) {
153         // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
154         skgpu::MutableTextureState newState = skgpu::MutableTextureStates::MakeVulkan(
155                                                                   VK_IMAGE_LAYOUT_UNDEFINED,
156                                                                   VK_QUEUE_FAMILY_FOREIGN_EXT);
157 
158         // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The
159         // releaseProc callback will be made when the work to set the new state has finished on the
160         // gpu.
161         ref();
162         // Note that we don't have an explicit call to set the backend texture back onto the
163         // graphics queue when we use the VkImage again. Internally, Skia will notice that the image
164         // is not on the graphics queue and will do the transition automatically.
165         context->setBackendTextureState(mBackendTexture, newState, nullptr, releaseProc, this);
166     }
167 }
168 
169 } /* namespace uirenderer */
170 } /* namespace android */
171