1 /*
2  * Copyright (c) 2021 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 "render_context.h"
17 
18 #include <sstream>
19 #include <string>
20 
21 #include "rs_trace.h"
22 #include "window.h"
23 
24 #ifdef RS_ENABLE_VK
25 #include "platform/ohos/backend/rs_vulkan_context.h"
26 #endif
27 
28 #if defined(RS_ENABLE_GL)
29 #include "EGL/egl.h"
30 #endif
31 
32 #include "memory/rs_tag_tracker.h"
33 
34 #include "render_context_log.h"
35 
36 namespace OHOS {
37 namespace Rosen {
38 using GetPlatformDisplayExt = PFNEGLGETPLATFORMDISPLAYEXTPROC;
39 constexpr const char* EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland";
40 constexpr const char* EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland";
41 constexpr int32_t EGL_CONTEXT_CLIENT_VERSION_NUM = 2;
42 constexpr char CHARACTER_WHITESPACE = ' ';
43 constexpr const char* CHARACTER_STRING_WHITESPACE = " ";
44 constexpr const char* EGL_GET_PLATFORM_DISPLAY_EXT = "eglGetPlatformDisplayEXT";
45 constexpr const char* EGL_KHR_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
46 
47 // use functor to call gel*KHR API
GetEGLSetDamageRegionKHRFunc()48 static PFNEGLSETDAMAGEREGIONKHRPROC GetEGLSetDamageRegionKHRFunc()
49 {
50     static auto func = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(eglGetProcAddress("eglSetDamageRegionKHR"));
51     return func;
52 }
53 
CheckEglExtension(const char * extensions,const char * extension)54 static bool CheckEglExtension(const char* extensions, const char* extension)
55 {
56     size_t extlen = strlen(extension);
57     const char* end = extensions + strlen(extensions);
58 
59     while (extensions < end) {
60         size_t n = 0;
61         /* Skip whitespaces, if any */
62         if (*extensions == CHARACTER_WHITESPACE) {
63             extensions++;
64             continue;
65         }
66 
67         n = strcspn(extensions, CHARACTER_STRING_WHITESPACE);
68 
69         /* Compare strings */
70         if (n == extlen && strncmp(extension, extensions, n) == 0) {
71             return true; /* Found */
72         }
73         extensions += n;
74     }
75     /* Not found */
76     return false;
77 }
78 
GetPlatformEglDisplay(EGLenum platform,void * native_display,const EGLint * attrib_list)79 static EGLDisplay GetPlatformEglDisplay(EGLenum platform, void* native_display, const EGLint* attrib_list)
80 {
81     static GetPlatformDisplayExt eglGetPlatformDisplayExt = NULL;
82 
83     if (!eglGetPlatformDisplayExt) {
84         const char* extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
85         if (extensions &&
86             (CheckEglExtension(extensions, EGL_EXT_PLATFORM_WAYLAND) ||
87                 CheckEglExtension(extensions, EGL_KHR_PLATFORM_WAYLAND))) {
88             eglGetPlatformDisplayExt = reinterpret_cast<GetPlatformDisplayExt>(
89                 eglGetProcAddress(EGL_GET_PLATFORM_DISPLAY_EXT));
90         }
91     }
92 
93     if (eglGetPlatformDisplayExt) {
94         return eglGetPlatformDisplayExt(platform, native_display, attrib_list);
95     }
96 
97     return eglGetDisplay(static_cast<EGLNativeDisplayType>(native_display));
98 }
99 
RenderContext()100 RenderContext::RenderContext()
101     : drGPUContext_(nullptr),
102       surface_(nullptr),
103       nativeWindow_(nullptr),
104       eglDisplay_(EGL_NO_DISPLAY),
105       eglContext_(EGL_NO_CONTEXT),
106       eglSurface_(EGL_NO_SURFACE),
107       config_(nullptr),
108       mHandler_(nullptr)
109 {}
110 
~RenderContext()111 RenderContext::~RenderContext()
112 {
113     if (eglDisplay_ == EGL_NO_DISPLAY) {
114         return;
115     }
116 
117     eglDestroyContext(eglDisplay_, eglContext_);
118     if (pbufferSurface_ != EGL_NO_SURFACE) {
119         eglDestroySurface(eglDisplay_, pbufferSurface_);
120     }
121     eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
122     eglTerminate(eglDisplay_);
123     eglReleaseThread();
124 
125     eglDisplay_ = EGL_NO_DISPLAY;
126     eglContext_ = EGL_NO_CONTEXT;
127     eglSurface_ = EGL_NO_SURFACE;
128     pbufferSurface_ = EGL_NO_SURFACE;
129     drGPUContext_ = nullptr;
130     surface_ = nullptr;
131     mHandler_ = nullptr;
132 }
133 
CreatePbufferSurface()134 void RenderContext::CreatePbufferSurface()
135 {
136     const char* extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
137 
138     if ((extensions != nullptr) &&
139        (!CheckEglExtension(extensions, EGL_KHR_SURFACELESS_CONTEXT)) &&
140        (pbufferSurface_ == EGL_NO_SURFACE)) {
141         EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
142         pbufferSurface_ = eglCreatePbufferSurface(eglDisplay_, config_, attribs);
143         if (pbufferSurface_ == EGL_NO_SURFACE) {
144             LOGE("Failed to create pbuffer surface");
145             return;
146         }
147     }
148 }
149 
InitializeEglContext()150 void RenderContext::InitializeEglContext()
151 {
152     if (IsEglContextReady()) {
153         return;
154     }
155 
156     LOGD("Creating EGLContext!!!");
157     eglDisplay_ = GetPlatformEglDisplay(EGL_PLATFORM_OHOS_KHR, EGL_DEFAULT_DISPLAY, NULL);
158     if (eglDisplay_ == EGL_NO_DISPLAY) {
159         LOGW("Failed to create EGLDisplay gl errno : %{public}x", eglGetError());
160         return;
161     }
162 
163     EGLint major, minor;
164     if (eglInitialize(eglDisplay_, &major, &minor) == EGL_FALSE) {
165         LOGE("Failed to initialize EGLDisplay");
166         return;
167     }
168 
169     if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
170         LOGE("Failed to bind OpenGL ES API");
171         return;
172     }
173 
174     unsigned int ret;
175     EGLint count;
176     EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
177         EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE };
178 
179     ret = eglChooseConfig(eglDisplay_, config_attribs, &config_, 1, &count);
180     if (!(ret && static_cast<unsigned int>(count) >= 1)) {
181         LOGE("Failed to eglChooseConfig");
182         return;
183     }
184 
185     static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE };
186 
187     eglContext_ = eglCreateContext(eglDisplay_, config_, EGL_NO_CONTEXT, context_attribs);
188     if (eglContext_ == EGL_NO_CONTEXT) {
189         LOGE("Failed to create egl context %{public}x", eglGetError());
190         return;
191     }
192     CreatePbufferSurface();
193     if (!eglMakeCurrent(eglDisplay_, pbufferSurface_, pbufferSurface_, eglContext_)) {
194         LOGE("Failed to make current on surface, error is %{public}x", eglGetError());
195         return;
196     }
197 
198     LOGD("Create EGL context successfully, version %{public}d.%{public}d", major, minor);
199 }
200 
MakeCurrent(EGLSurface surface,EGLContext context)201 void RenderContext::MakeCurrent(EGLSurface surface, EGLContext context)
202 {
203     if (surface == EGL_NO_SURFACE) {
204         surface = pbufferSurface_;
205     }
206     if (context == EGL_NO_CONTEXT) {
207         context = eglContext_;
208     }
209     if (!eglMakeCurrent(eglDisplay_, surface, surface, context)) {
210         LOGE("Failed to make current on surface, error is %{public}x", eglGetError());
211     }
212     eglSurface_ = surface;
213 }
214 
SetAndMakeCurrentShareContex(EGLContext shareContext)215 void RenderContext::SetAndMakeCurrentShareContex(EGLContext shareContext)
216 {
217     eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, shareContext);
218     eglContext_ = shareContext;
219 }
220 
ShareMakeCurrent(EGLContext shareContext)221 void RenderContext::ShareMakeCurrent(EGLContext shareContext)
222 {
223     eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, shareContext);
224 }
225 
ShareMakeCurrentNoSurface(EGLContext shareContext)226 void RenderContext::ShareMakeCurrentNoSurface(EGLContext shareContext)
227 {
228     eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, shareContext);
229 }
230 
MakeSelfCurrent()231 void RenderContext::MakeSelfCurrent()
232 {
233     eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_);
234 }
235 
CreateShareContext()236 EGLContext RenderContext::CreateShareContext()
237 {
238     std::unique_lock<std::mutex> lock(shareContextMutex_);
239     static const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE};
240     auto eglShareContext = eglCreateContext(eglDisplay_, config_, eglContext_, context_attribs);
241     return eglShareContext;
242 }
243 
SwapBuffers(EGLSurface surface) const244 void RenderContext::SwapBuffers(EGLSurface surface) const
245 {
246     RS_TRACE_FUNC();
247     if (!eglSwapBuffers(eglDisplay_, surface)) {
248         LOGE("Failed to SwapBuffers on surface, error is %{public}x", eglGetError());
249     } else {
250         LOGD("SwapBuffers successfully");
251     }
252 }
253 
DestroyEGLSurface(EGLSurface surface)254 void RenderContext::DestroyEGLSurface(EGLSurface surface)
255 {
256     if (!eglDestroySurface(eglDisplay_, surface)) {
257         LOGE("Failed to DestroyEGLSurface surface, error is %{public}x", eglGetError());
258     }
259 }
260 
CreateEGLSurface(EGLNativeWindowType eglNativeWindow)261 EGLSurface RenderContext::CreateEGLSurface(EGLNativeWindowType eglNativeWindow)
262 {
263     if (!IsEglContextReady()) {
264         LOGE("EGL context has not initialized");
265         return EGL_NO_SURFACE;
266     }
267     nativeWindow_ = eglNativeWindow;
268 
269     eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
270 
271     EGLSurface surface = eglCreateWindowSurface(eglDisplay_, config_, nativeWindow_, NULL);
272     if (surface == EGL_NO_SURFACE) {
273         LOGW("Failed to create eglsurface!!! %{public}x", eglGetError());
274         return EGL_NO_SURFACE;
275     }
276 
277     LOGD("CreateEGLSurface");
278 
279     eglSurface_ = surface;
280     return surface;
281 }
282 
SetUpGpuContext(std::shared_ptr<Drawing::GPUContext> drawingContext)283 bool RenderContext::SetUpGpuContext(std::shared_ptr<Drawing::GPUContext> drawingContext)
284 {
285     if (drGPUContext_ != nullptr) {
286         LOGD("Drawing GPUContext has already created!!");
287         return true;
288     }
289 #ifdef RS_ENABLE_GL
290     if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
291         mHandler_ = std::make_shared<MemoryHandler>();
292         auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
293         if (isUniRenderMode_) {
294             cacheDir_ = UNIRENDER_CACHE_DIR;
295         }
296         Drawing::GPUContextOptions options;
297         if (glesVersion != nullptr) {
298             auto size = glesVersion ? strlen(glesVersion) : 0;
299             mHandler_->ConfigureContext(&options, glesVersion, size, cacheDir_, isUniRenderMode_);
300         }
301 
302         auto drGPUContext = std::make_shared<Drawing::GPUContext>();
303         if (!drGPUContext->BuildFromGL(options)) {
304             LOGE("SetUpGrContext drGPUContext is null");
305             return false;
306         }
307         drGPUContext_ = std::move(drGPUContext);
308         return true;
309     }
310 #endif
311 #ifdef RS_ENABLE_VK
312     if (RSSystemProperties::IsUseVulkan()) {
313         if (drawingContext == nullptr) {
314             drawingContext = RsVulkanContext::GetSingleton().CreateDrawingContext();
315         }
316         std::shared_ptr<Drawing::GPUContext> drGPUContext(drawingContext);
317         drGPUContext_ = std::move(drGPUContext);
318         return true;
319     }
320 #endif
321     return false;
322 }
323 
324 #ifdef RS_ENABLE_VK
AbandonContext()325 void RenderContext::AbandonContext()
326 {
327     if (!RSSystemProperties::IsUseVulkan()) {
328         return;
329     }
330     if (drGPUContext_ == nullptr) {
331         LOGD("grContext is nullptr.");
332         return;
333     }
334     drGPUContext_->FlushAndSubmit(true);
335     drGPUContext_->PurgeUnlockAndSafeCacheGpuResources();
336 }
337 #endif
338 
AcquireSurface(int width,int height)339 std::shared_ptr<Drawing::Surface> RenderContext::AcquireSurface(int width, int height)
340 {
341     if (!SetUpGpuContext(nullptr)) {
342         LOGE("GrContext is not ready!!!");
343         return nullptr;
344     }
345 
346     std::shared_ptr<Drawing::ColorSpace> colorSpace = nullptr;
347 
348     switch (colorSpace_) {
349         // [planning] in order to stay consistant with the colorspace used before, we disabled
350         // COLOR_GAMUT_SRGB to let the branch to default, then skColorSpace is set to nullptr
351         case GRAPHIC_COLOR_GAMUT_DISPLAY_P3:
352             colorSpace = Drawing::ColorSpace::CreateRGB(Drawing::CMSTransferFuncType::SRGB,
353                 Drawing::CMSMatrixType::DCIP3);
354             break;
355         case GRAPHIC_COLOR_GAMUT_ADOBE_RGB:
356             colorSpace = Drawing::ColorSpace::CreateRGB(Drawing::CMSTransferFuncType::SRGB,
357                 Drawing::CMSMatrixType::ADOBE_RGB);
358             break;
359         case GRAPHIC_COLOR_GAMUT_BT2020:
360             colorSpace = Drawing::ColorSpace::CreateRGB(Drawing::CMSTransferFuncType::SRGB,
361                 Drawing::CMSMatrixType::REC2020);
362             break;
363         default:
364             break;
365     }
366 
367     RSTagTracker tagTracker(GetDrGPUContext(), RSTagTracker::TAGTYPE::TAG_ACQUIRE_SURFACE);
368 
369     struct Drawing::FrameBuffer bufferInfo;
370     bufferInfo.width = width;
371     bufferInfo.height = height;
372     bufferInfo.FBOID = 0;
373     bufferInfo.Format = GL_RGBA8;
374     bufferInfo.gpuContext = drGPUContext_;
375     bufferInfo.colorSpace = colorSpace;
376     bufferInfo.colorType = Drawing::COLORTYPE_RGBA_8888;
377 
378     if (pixelFormat_ == GRAPHIC_PIXEL_FMT_RGBA_1010102) {
379         bufferInfo.Format = GL_RGB10_A2;
380         bufferInfo.colorType = Drawing::COLORTYPE_RGBA_1010102;
381     }
382 
383     surface_ = std::make_shared<Drawing::Surface>();
384     if (!surface_->Bind(bufferInfo)) {
385         LOGW("surface_ is nullptr");
386         surface_ = nullptr;
387         return nullptr;
388     }
389 
390     LOGD("CreateCanvas successfully!!!");
391     return surface_;
392 }
393 
RenderFrame()394 void RenderContext::RenderFrame()
395 {
396     RS_TRACE_FUNC();
397     // flush commands
398     if (surface_ != nullptr && surface_->GetCanvas() != nullptr) {
399         LOGD("RenderFrame: Canvas");
400         RSTagTracker tagTracker(GetDrGPUContext(), RSTagTracker::TAGTYPE::TAG_RENDER_FRAME);
401         surface_->GetCanvas()->Flush();
402     } else {
403         LOGW("canvas is nullptr!!!");
404     }
405 }
406 
QueryEglBufferAge()407 EGLint RenderContext::QueryEglBufferAge()
408 {
409     if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr)) {
410         LOGE("eglDisplay or eglSurface is nullptr");
411         return EGL_UNKNOWN;
412     }
413     EGLint bufferAge = EGL_UNKNOWN;
414     EGLBoolean ret = eglQuerySurface(eglDisplay_, eglSurface_, EGL_BUFFER_AGE_KHR, &bufferAge);
415     if (ret == EGL_FALSE) {
416         LOGE("eglQuerySurface is failed");
417         return EGL_UNKNOWN;
418     }
419     return bufferAge;
420 }
421 
DamageFrame(int32_t left,int32_t top,int32_t width,int32_t height)422 void RenderContext::DamageFrame(int32_t left, int32_t top, int32_t width, int32_t height)
423 {
424     if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr)) {
425         LOGE("eglDisplay or eglSurface is nullptr");
426         return;
427     }
428     RS_TRACE_FUNC();
429 
430     EGLint rect[4];
431     rect[0] = left;
432     rect[1] = top;
433     rect[2] = width;
434     rect[3] = height;
435 
436     EGLBoolean ret = GetEGLSetDamageRegionKHRFunc()(eglDisplay_, eglSurface_, rect, 1);
437     if (ret == EGL_FALSE) {
438         LOGE("eglSetDamageRegionKHR is failed");
439     }
440 }
441 
DamageFrame(const std::vector<RectI> & rects)442 void RenderContext::DamageFrame(const std::vector<RectI> &rects)
443 {
444     if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr)) {
445         LOGE("eglDisplay or eglSurface is nullptr");
446         return;
447     }
448     RS_TRACE_FUNC();
449 
450     size_t size = rects.size();
451     if (size == 0) {
452         LOGD("invalid rects size");
453         return;
454     }
455 
456     EGLint eglRect[size * 4]; // 4 is size of RectI.
457     int index = 0;
458     for (const RectI& rect : rects) {
459         eglRect[index * 4] = rect.left_; // 4 is size of RectI.
460         eglRect[index * 4 + 1] = rect.top_; // 4 is size of RectI.
461         eglRect[index * 4 + 2] = rect.width_; // 4 is size of RectI, 2 is the index of the width_ subscript.
462         eglRect[index * 4 + 3] = rect.height_; // 4 is size of RectI, 3 is the index of the height_ subscript.
463         index++;
464     }
465 
466     EGLBoolean ret = GetEGLSetDamageRegionKHRFunc()(eglDisplay_, eglSurface_, eglRect, size);
467     if (ret == EGL_FALSE) {
468         LOGE("eglSetDamageRegionKHR is failed");
469     }
470 }
471 
ClearRedundantResources()472 void RenderContext::ClearRedundantResources()
473 {
474     RS_TRACE_FUNC();
475     if (drGPUContext_ != nullptr) {
476         LOGD("grContext clear redundant resources");
477         drGPUContext_->Flush();
478         // GPU resources that haven't been used in the past 10 seconds
479         drGPUContext_->PerformDeferredCleanup(std::chrono::seconds(10));
480     }
481 }
482 
ConvertColorGamutToSkColorSpace(GraphicColorGamut colorGamut)483 sk_sp<SkColorSpace> RenderContext::ConvertColorGamutToSkColorSpace(GraphicColorGamut colorGamut)
484 {
485     sk_sp<SkColorSpace> skColorSpace = nullptr;
486     switch (colorGamut) {
487         // [planning] in order to stay consistant with the colorspace used before, we disabled
488         // GRAPHIC_COLOR_GAMUT_SRGB to let the branch to default, then skColorSpace is set to nullptr
489         case GRAPHIC_COLOR_GAMUT_DISPLAY_P3:
490 #if defined(NEW_SKIA)
491             skColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
492 #else
493             skColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3);
494 #endif
495             break;
496         case GRAPHIC_COLOR_GAMUT_ADOBE_RGB:
497             skColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB);
498             break;
499         case GRAPHIC_COLOR_GAMUT_BT2020:
500             skColorSpace = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kRec2020);
501             break;
502         default:
503             skColorSpace = SkColorSpace::MakeSRGB();
504             break;
505     }
506 
507     return skColorSpace;
508 }
509 
510 #if defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)
GetShaderCacheSize() const511 std::string RenderContext::GetShaderCacheSize() const
512 {
513 #ifdef RS_ENABLE_VK
514     if (RSSystemProperties::IsUseVulkan()) {
515         if (RsVulkanContext::GetSingleton().GetMemoryHandler()) {
516             return RsVulkanContext::GetSingleton().GetMemoryHandler()->QuerryShader();
517         }
518     }
519 #else
520     if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
521         if (mHandler_) {
522             return mHandler_->QuerryShader();
523         }
524     }
525 #endif
526     LOGD("GetShaderCacheSize no shader cache");
527     return "";
528 }
529 
CleanAllShaderCache() const530 std::string RenderContext::CleanAllShaderCache() const
531 {
532 #ifdef RS_ENABLE_VK
533     if (RSSystemProperties::IsUseVulkan()) {
534         if (RsVulkanContext::GetSingleton().GetMemoryHandler()) {
535             return RsVulkanContext::GetSingleton().GetMemoryHandler()->ClearShader();
536         }
537     }
538 #else
539     if (RSSystemProperties::GetGpuApiType() == GpuApiType::OPENGL) {
540         if (mHandler_) {
541             return mHandler_->ClearShader();
542         }
543     }
544 #endif
545     LOGD("CleanAllShaderCache no shader cache");
546     return "";
547 }
548 #endif
549 
GetInstance()550 RenderContextFactory& RenderContextFactory::GetInstance()
551 {
552     static RenderContextFactory rf;
553     return rf;
554 }
555 } // namespace Rosen
556 } // namespace OHOS
557