1 /*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rs_egl_image_manager.h"
17
18 #include "feature/uifirst/rs_sub_thread_manager.h"
19 #include <native_window.h>
20 #include <platform/common/rs_log.h>
21 #include "sync_fence.h"
22 #include "pipeline/main_thread/rs_main_thread.h"
23 #include "pipeline/hardware_thread/rs_hardware_thread.h"
24 #include "rs_trace.h"
25 #include "common/rs_optional_trace.h"
26 #include "pipeline/rs_task_dispatcher.h"
27
28 #ifndef NDEBUG
29 #include <cassert>
30 #endif
31
32 #ifdef USE_M133_SKIA
33 #include "src/gpu/ganesh/gl/GrGLDefines.h"
34 #else
35 #include "src/gpu/gl/GrGLDefines.h"
36 #endif
37
38 namespace OHOS {
39 namespace Rosen {
40 namespace Detail {
41 #ifdef NDEBUG
42 #define RS_ASSERT(exp) (void)((exp))
43 #else
44 #define RS_ASSERT(exp) assert((exp))
45 #endif
46
47 #define RS_EGL_ERR_CASE_STR(value) case value: return #value
EGLErrorString(GLint error)48 const char *EGLErrorString(GLint error)
49 {
50 switch (error) {
51 RS_EGL_ERR_CASE_STR(EGL_SUCCESS);
52 RS_EGL_ERR_CASE_STR(EGL_NOT_INITIALIZED);
53 RS_EGL_ERR_CASE_STR(EGL_BAD_ACCESS);
54 RS_EGL_ERR_CASE_STR(EGL_BAD_ALLOC);
55 RS_EGL_ERR_CASE_STR(EGL_BAD_ATTRIBUTE);
56 RS_EGL_ERR_CASE_STR(EGL_BAD_CONFIG);
57 RS_EGL_ERR_CASE_STR(EGL_BAD_CONTEXT);
58 RS_EGL_ERR_CASE_STR(EGL_BAD_CURRENT_SURFACE);
59 RS_EGL_ERR_CASE_STR(EGL_BAD_DISPLAY);
60 RS_EGL_ERR_CASE_STR(EGL_BAD_MATCH);
61 RS_EGL_ERR_CASE_STR(EGL_BAD_NATIVE_PIXMAP);
62 RS_EGL_ERR_CASE_STR(EGL_BAD_NATIVE_WINDOW);
63 RS_EGL_ERR_CASE_STR(EGL_BAD_PARAMETER);
64 RS_EGL_ERR_CASE_STR(EGL_BAD_SURFACE);
65 RS_EGL_ERR_CASE_STR(EGL_CONTEXT_LOST);
66 default: return "Unknown";
67 }
68 }
69
GetEGLCreateImageKHRFunc()70 static PFNEGLCREATEIMAGEKHRPROC GetEGLCreateImageKHRFunc()
71 {
72 static auto func = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"));
73 return func;
74 }
75
GetEGLDestroyImageKHRFunc()76 static PFNEGLDESTROYIMAGEKHRPROC GetEGLDestroyImageKHRFunc()
77 {
78 static auto func = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR"));
79 return func;
80 }
81
GetGLEGLImageTargetTexture2DOESFunc()82 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC GetGLEGLImageTargetTexture2DOESFunc()
83 {
84 static auto func = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(
85 eglGetProcAddress("glEGLImageTargetTexture2DOES"));
86 return func;
87 }
88
89 // RAII object for NativeWindowBuffer
90 class NativeWindowBufferObject {
91 public:
NativeWindowBufferObject(sptr<OHOS::SurfaceBuffer> buffer)92 explicit NativeWindowBufferObject(sptr<OHOS::SurfaceBuffer> buffer)
93 : handle_(CreateNativeWindowBufferFromSurfaceBuffer(&buffer))
94 {
95 }
NativeWindowBufferObject(NativeWindowBuffer * nativeBuffer)96 explicit NativeWindowBufferObject(NativeWindowBuffer* nativeBuffer)
97 : handle_(nativeBuffer)
98 {
99 }
100
~NativeWindowBufferObject()101 ~NativeWindowBufferObject() noexcept
102 {
103 if (handle_ != nullptr) {
104 DestroyNativeWindowBuffer(handle_);
105 }
106 }
107
108 NativeWindowBufferObject(const NativeWindowBufferObject&) = delete;
109 void operator=(const NativeWindowBufferObject&) = delete;
110
operator ==(std::nullptr_t) const111 bool operator==(std::nullptr_t) const
112 {
113 return handle_ == nullptr;
114 }
operator !=(std::nullptr_t) const115 bool operator!=(std::nullptr_t) const
116 {
117 return handle_ != nullptr;
118 }
119
120 // not explicit so you can use it to do the implicit-cast.
121 // you should not delete or call DestroyNativeWindowBuffer for this pointer.
operator NativeWindowBuffer*() const122 operator NativeWindowBuffer* () const
123 {
124 return Get();
125 }
126 // you should not delete or call DestroyNativeWindowBuffer for this pointer.
Get() const127 NativeWindowBuffer* Get() const
128 {
129 return handle_;
130 }
131
Release()132 NativeWindowBuffer* Release()
133 {
134 NativeWindowBuffer* out = handle_;
135 handle_ = nullptr;
136 return out;
137 }
138
139 private:
140 NativeWindowBuffer* handle_ = nullptr;
141 };
142
CreateNativeWindowBuffer(const sptr<OHOS::SurfaceBuffer> & buffer)143 NativeWindowBufferObject CreateNativeWindowBuffer(const sptr<OHOS::SurfaceBuffer>& buffer)
144 {
145 return NativeWindowBufferObject(buffer);
146 }
147
CastToEGLClientBuffer(NativeWindowBuffer * nativeBuffer)148 EGLClientBuffer CastToEGLClientBuffer(NativeWindowBuffer* nativeBuffer)
149 {
150 return static_cast<EGLClientBuffer>(nativeBuffer);
151 }
152
CastFromEGLClientBuffer(EGLClientBuffer eglClientBuffer)153 NativeWindowBuffer* CastFromEGLClientBuffer(EGLClientBuffer eglClientBuffer)
154 {
155 return static_cast<NativeWindowBuffer*>(eglClientBuffer);
156 }
157
CreateEGLImage(EGLDisplay eglDisplay,EGLContext eglContext,const NativeWindowBufferObject & nativeBuffer)158 EGLImageKHR CreateEGLImage(
159 EGLDisplay eglDisplay,
160 EGLContext eglContext,
161 const NativeWindowBufferObject& nativeBuffer)
162 {
163 EGLint attrs[] = {
164 EGL_IMAGE_PRESERVED_KHR,
165 EGL_TRUE,
166 EGL_NONE,
167 };
168
169 return GetEGLCreateImageKHRFunc()(
170 eglDisplay, eglContext, EGL_NATIVE_BUFFER_OHOS, CastToEGLClientBuffer(nativeBuffer), attrs);
171 }
172 } // namespace Detail
173
~EglImageResource()174 EglImageResource::~EglImageResource() noexcept
175 {
176 if (eglImage_ != EGL_NO_IMAGE_KHR) {
177 Detail::GetEGLDestroyImageKHRFunc()(eglDisplay_, eglImage_);
178 eglImage_ = EGL_NO_IMAGE_KHR;
179 }
180
181 if (textureId_ != 0) {
182 glDeleteTextures(1, &textureId_);
183 textureId_ = 0;
184 }
185
186 // auto dec ref.
187 Detail::NativeWindowBufferObject nBufferDecRef(
188 Detail::CastFromEGLClientBuffer(eglClientBuffer_));
189 }
190
BindToTexture()191 bool EglImageResource::BindToTexture()
192 {
193 // no image check.
194 if (eglImage_ == EGL_NO_IMAGE_KHR) {
195 RS_LOGE("EglImageResource::BindToTexture: eglImage_ is null.");
196 return false;
197 }
198
199 glGenTextures(1, &textureId_);
200 if (textureId_ == 0) {
201 RS_LOGE("EglImageResource::BindToTexture: glGenTextures error.");
202 return false;
203 }
204
205 // bind this eglImage_ to textureId_.
206 glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId_);
207 Detail::GetGLEGLImageTargetTexture2DOESFunc()(GL_TEXTURE_EXTERNAL_OES, eglImage_);
208 return true;
209 }
210
Create(EGLDisplay eglDisplay,EGLContext eglContext,const sptr<OHOS::SurfaceBuffer> & buffer)211 std::unique_ptr<EglImageResource> EglImageResource::Create(
212 EGLDisplay eglDisplay,
213 EGLContext eglContext,
214 const sptr<OHOS::SurfaceBuffer>& buffer)
215 {
216 auto nativeBuffer = Detail::CreateNativeWindowBuffer(buffer);
217 if (nativeBuffer == nullptr) {
218 return nullptr;
219 }
220
221 EGLImageKHR img = Detail::CreateEGLImage(eglDisplay, eglContext, nativeBuffer);
222 if (img == EGL_NO_IMAGE_KHR) {
223 RS_LOGE("EglImageResource::Create: eglCreateImageKHR failed, error %{public}s.",
224 Detail::EGLErrorString(eglGetError()));
225 return nullptr;
226 }
227
228 auto imageCache = std::make_unique<EglImageResource>(
229 eglDisplay, img, Detail::CastToEGLClientBuffer(nativeBuffer.Release()));
230 if (!imageCache->BindToTexture()) {
231 return nullptr;
232 }
233 return imageCache;
234 }
235
WaitAcquireFence(const sptr<SyncFence> & acquireFence)236 void RSEglImageManager::WaitAcquireFence(const sptr<SyncFence>& acquireFence)
237 {
238 if (acquireFence == nullptr) {
239 return;
240 }
241 acquireFence->Wait(3000); // 3000ms
242 }
243
CreateEglImageCacheFromBuffer(const sptr<OHOS::SurfaceBuffer> & buffer,const pid_t threadIndex)244 GLuint RSEglImageManager::CreateEglImageCacheFromBuffer(const sptr<OHOS::SurfaceBuffer>& buffer,
245 const pid_t threadIndex)
246 {
247 auto bufferId = buffer->GetSeqNum();
248 auto imageCache = EglImageResource::Create(eglDisplay_, EGL_NO_CONTEXT, buffer);
249 if (imageCache == nullptr) {
250 RS_LOGE("RSEglImageManager::CreateEglImageCacheFromBuffer:failed to create for buffer id %{public}d.",
251 bufferId);
252 return 0; // return texture id 0.
253 }
254 imageCache->SetThreadIndex(threadIndex);
255 auto textureId = imageCache->GetTextureId();
256 {
257 std::lock_guard<std::mutex> lock(opMutex_);
258 imageCacheSeqs_[bufferId] = std::move(imageCache);
259 }
260 cacheQueue_.push(bufferId);
261 return textureId;
262 }
263
MapEglImageFromSurfaceBuffer(const sptr<OHOS::SurfaceBuffer> & buffer,const sptr<SyncFence> & acquireFence,pid_t threadIndex)264 GLuint RSEglImageManager::MapEglImageFromSurfaceBuffer(const sptr<OHOS::SurfaceBuffer>& buffer,
265 const sptr<SyncFence>& acquireFence, pid_t threadIndex)
266 {
267 WaitAcquireFence(acquireFence);
268 if (buffer == nullptr) {
269 RS_LOGE("RSEglImageManager::MapEglImageFromSurfaceBuffer: buffer is null.");
270 return 0;
271 }
272 auto bufferId = buffer->GetSeqNum();
273 RS_OPTIONAL_TRACE_NAME_FMT("MapEglImage seqNum: %d", bufferId);
274 RS_LOGD("RSEglImageManager::MapEglImageFromSurfaceBuffer: %{public}d", bufferId);
275 {
276 bool isImageCacheNotFound = false;
277 std::lock_guard<std::mutex> lock(opMutex_);
278 isImageCacheNotFound = imageCacheSeqs_.count(bufferId) == 0 || imageCacheSeqs_[bufferId] == nullptr;
279 if (!isImageCacheNotFound) {
280 const auto& imageCache = imageCacheSeqs_[bufferId];
281 return imageCache->GetTextureId();
282 }
283 }
284 // cache not found, create it.
285 return CreateEglImageCacheFromBuffer(buffer, threadIndex);
286 }
287
ShrinkCachesIfNeeded(bool isForUniRedraw)288 void RSEglImageManager::ShrinkCachesIfNeeded(bool isForUniRedraw)
289 {
290 while (cacheQueue_.size() > MAX_CACHE_SIZE) {
291 const int32_t id = cacheQueue_.front();
292 if (isForUniRedraw) {
293 UnMapEglImageFromSurfaceBufferForUniRedraw(id);
294 } else {
295 UnMapImageFromSurfaceBuffer(id);
296 }
297 cacheQueue_.pop();
298 }
299 }
300
UnMapImageFromSurfaceBuffer(int32_t seqNum)301 void RSEglImageManager::UnMapImageFromSurfaceBuffer(int32_t seqNum)
302 {
303 pid_t threadIndex = 0;
304 {
305 std::lock_guard<std::mutex> lock(opMutex_);
306 if (imageCacheSeqs_.count(seqNum) == 0) {
307 return;
308 }
309 if (imageCacheSeqs_[seqNum]) {
310 threadIndex = imageCacheSeqs_[seqNum]->GetThreadIndex();
311 }
312 }
313 auto func = [this, seqNum]() {
314 std::unique_ptr<EglImageResource> imageCacheSeq;
315 {
316 std::lock_guard<std::mutex> lock(opMutex_);
317 if (imageCacheSeqs_.count(seqNum) == 0) {
318 return;
319 }
320 imageCacheSeq = std::move(imageCacheSeqs_[seqNum]);
321 }
322 imageCacheSeq.reset();
323 RS_OPTIONAL_TRACE_NAME_FMT("UnmapEglImage seqNum: %d", seqNum);
324 RS_LOGD("RSEglImageManager::UnMapEglImageFromSurfaceBuffer: %{public}d", seqNum);
325 };
326 RSTaskDispatcher::GetInstance().PostTask(threadIndex, func);
327 }
328
UnMapEglImageFromSurfaceBufferForUniRedraw(int32_t seqNum)329 void RSEglImageManager::UnMapEglImageFromSurfaceBufferForUniRedraw(int32_t seqNum)
330 {
331 RSHardwareThread::Instance().PostTask([this, seqNum]() {
332 std::lock_guard<std::mutex> lock(opMutex_);
333 if (imageCacheSeqs_.count(seqNum) == 0) {
334 return;
335 }
336 (void)imageCacheSeqs_.erase(seqNum);
337 RS_LOGD("RSEglImageManager::UnMapEglImageFromSurfaceBufferForRedraw");
338 });
339 }
340
CreateImageFromBuffer(RSPaintFilterCanvas & canvas,const BufferDrawParam & params,const std::shared_ptr<Drawing::ColorSpace> & drawingColorSpace)341 std::shared_ptr<Drawing::Image> RSEglImageManager::CreateImageFromBuffer(
342 RSPaintFilterCanvas& canvas, const BufferDrawParam& params,
343 const std::shared_ptr<Drawing::ColorSpace>& drawingColorSpace)
344 {
345 const sptr<SurfaceBuffer>& buffer = params.buffer;
346 const sptr<SyncFence>& acquireFence = params.acquireFence;
347 const pid_t threadIndex = params.threadIndex;
348 const Drawing::AlphaType alphaType = params.alphaType;
349
350 #if defined(RS_ENABLE_EGLIMAGE) && defined(RS_ENABLE_GL)
351 if (canvas.GetGPUContext() == nullptr) {
352 RS_LOGE("RSEglImageManager::CreateEglImageFromBuffer GrContext is null!");
353 return nullptr;
354 }
355 if (buffer == nullptr) {
356 RS_LOGE("RSEglImageManager::CreateEglImageFromBuffer buffer is null!");
357 return nullptr;
358 }
359 auto eglTextureId = MapEglImageFromSurfaceBuffer(buffer, acquireFence, threadIndex);
360 if (eglTextureId == 0) {
361 RS_LOGE("RSEglImageManager::CreateEglImageFromBuffer MapEglImageFromSurfaceBuffer return invalid texture ID");
362 return nullptr;
363 }
364 auto pixelFmt = buffer->GetFormat();
365 auto bitmapFormat = RSBaseRenderUtil::GenerateDrawingBitmapFormat(buffer, alphaType);
366
367 auto image = std::make_shared<Drawing::Image>();
368 Drawing::TextureInfo externalTextureInfo;
369 externalTextureInfo.SetWidth(buffer->GetSurfaceBufferWidth());
370 externalTextureInfo.SetHeight(buffer->GetSurfaceBufferHeight());
371
372 auto surfaceOrigin = Drawing::TextureOrigin::TOP_LEFT;
373 externalTextureInfo.SetIsMipMapped(false);
374 externalTextureInfo.SetTarget(GL_TEXTURE_EXTERNAL_OES);
375 externalTextureInfo.SetID(eglTextureId);
376 auto glType = GR_GL_RGBA8;
377 if (pixelFmt == GRAPHIC_PIXEL_FMT_BGRA_8888) {
378 glType = GR_GL_BGRA8;
379 } else if (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010 || pixelFmt == GRAPHIC_PIXEL_FMT_YCRCB_P010 ||
380 pixelFmt == GRAPHIC_PIXEL_FMT_RGBA_1010102) {
381 glType = GR_GL_RGB10_A2;
382 }
383 externalTextureInfo.SetFormat(glType);
384 if (!image->BuildFromTexture(*canvas.GetGPUContext(), externalTextureInfo,
385 surfaceOrigin, bitmapFormat, drawingColorSpace)) {
386 RS_LOGE("RSEglImageManager::CreateEglImageFromBuffer image BuildFromTexture failed");
387 return nullptr;
388 }
389 return image;
390 #else
391 return nullptr;
392 #endif // RS_ENABLE_EGLIMAGE
393 }
394
GetIntersectImage(Drawing::RectI & imgCutRect,const std::shared_ptr<Drawing::GPUContext> & context,const sptr<OHOS::SurfaceBuffer> & buffer,const sptr<SyncFence> & acquireFence,pid_t threadIndex)395 std::shared_ptr<Drawing::Image> RSEglImageManager::GetIntersectImage(Drawing::RectI& imgCutRect,
396 const std::shared_ptr<Drawing::GPUContext>& context, const sptr<OHOS::SurfaceBuffer>& buffer,
397 const sptr<SyncFence>& acquireFence, pid_t threadIndex)
398 {
399 auto eglTextureId = MapEglImageFromSurfaceBuffer(buffer, acquireFence, threadIndex);
400 if (eglTextureId == 0) {
401 RS_LOGE("RSEglImageManager::GetIntersectImageFromGL invalid texture ID");
402 return nullptr;
403 }
404
405 Drawing::BitmapFormat bitmapFormat = RSBaseRenderUtil::GenerateDrawingBitmapFormat(buffer);
406 Drawing::TextureInfo externalTextureInfo;
407 externalTextureInfo.SetWidth(buffer->GetSurfaceBufferWidth());
408 externalTextureInfo.SetHeight(buffer->GetSurfaceBufferHeight());
409 externalTextureInfo.SetIsMipMapped(false);
410 externalTextureInfo.SetTarget(GL_TEXTURE_EXTERNAL_OES);
411 externalTextureInfo.SetID(eglTextureId);
412 auto glType = GR_GL_RGBA8;
413 auto pixelFmt = buffer->GetFormat();
414 if (pixelFmt == GRAPHIC_PIXEL_FMT_BGRA_8888) {
415 glType = GR_GL_BGRA8;
416 } else if (pixelFmt == GRAPHIC_PIXEL_FMT_YCBCR_P010 || pixelFmt == GRAPHIC_PIXEL_FMT_YCRCB_P010) {
417 glType = GL_RGB10_A2;
418 }
419 externalTextureInfo.SetFormat(glType);
420
421 std::shared_ptr<Drawing::Image> layerImage = std::make_shared<Drawing::Image>();
422 if (!layerImage->BuildFromTexture(*context, externalTextureInfo,
423 Drawing::TextureOrigin::TOP_LEFT, bitmapFormat, nullptr)) {
424 RS_LOGE("RSEglImageManager::GetIntersectImageFromGL image BuildFromTexture failed");
425 return nullptr;
426 }
427
428 std::shared_ptr<Drawing::Image> cutDownImage = std::make_shared<Drawing::Image>();
429 bool res = cutDownImage->BuildSubset(layerImage, imgCutRect, *context);
430 if (!res) {
431 ROSEN_LOGE("RSEglImageManager::GetIntersectImageFromGL cutDownImage BuildSubset failed.");
432 return nullptr;
433 }
434 Drawing::ImageInfo info = Drawing::ImageInfo(imgCutRect.GetWidth(), imgCutRect.GetHeight(),
435 Drawing::COLORTYPE_RGBA_8888, Drawing::ALPHATYPE_PREMUL);
436
437 std::shared_ptr<Drawing::Surface> surface = Drawing::Surface::MakeRenderTarget(context.get(), false, info);
438 if (surface == nullptr) {
439 RS_LOGE("RSEglImageManager::GetIntersectImageFromGL MakeRenderTarget failed.");
440 return nullptr;
441 }
442 auto drawCanvas = std::make_shared<RSPaintFilterCanvas>(surface.get());
443 drawCanvas->DrawImage(*cutDownImage, 0.f, 0.f, Drawing::SamplingOptions());
444 surface->FlushAndSubmit(true);
445 return surface.get()->GetImageSnapshot();
446 }
447 } // namespace Rosen
448 } // namespace OHOS
449