1 /*
2  * Copyright (c) 2023 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 "surface_ohos_vulkan.h"
17 
18 #include <iostream>
19 #ifdef RS_ENABLE_OLD_VK
20 #include <vulkan_native_surface_ohos.h>
21 #include <vulkan_window.h>
22 #include <vulkan_proc_table.h>
23 #include <hilog/log.h>
24 #include <display_type.h>
25 #include "window.h"
26 #endif
27 
28 #include "native_buffer_inner.h"
29 #include "native_window.h"
30 #include "vulkan/vulkan_core.h"
31 
32 #include "rs_vulkan_context.h"
33 #include "include/gpu/GrBackendSemaphore.h"
34 
35 
36 #include "engine_adapter/skia_adapter/skia_surface.h"
37 
38 namespace OHOS {
39 namespace Rosen {
SurfaceOhosVulkan(const sptr<Surface> & producer)40 SurfaceOhosVulkan::SurfaceOhosVulkan(const sptr<Surface>& producer)
41     : SurfaceOhos(producer), frame_(nullptr)
42 {
43 }
44 
~SurfaceOhosVulkan()45 SurfaceOhosVulkan::~SurfaceOhosVulkan()
46 {
47     frame_ = nullptr;
48     if (mNativeWindow_ != nullptr) {
49         DestoryNativeWindow(mNativeWindow_);
50         mNativeWindow_ = nullptr;
51     }
52 #ifdef RS_ENABLE_OLD_VK
53     if (mVulkanWindow_ != nullptr) {
54         delete mVulkanWindow_;
55         mVulkanWindow_ = nullptr;
56     }
57 #endif
58 }
59 
SetNativeWindowInfo(int32_t width,int32_t height)60 void SurfaceOhosVulkan::SetNativeWindowInfo(int32_t width, int32_t height)
61 {
62     uint64_t bufferUsage = BUFFER_USAGE_HW_RENDER | BUFFER_USAGE_HW_TEXTURE |
63         BUFFER_USAGE_HW_COMPOSER | BUFFER_USAGE_MEM_DMA;
64     NativeWindowHandleOpt(mNativeWindow_, SET_FORMAT, GRAPHIC_PIXEL_FMT_RGBA_8888);
65 #ifdef RS_ENABLE_AFBC
66     if (RSSystemProperties::GetAFBCEnabled()) {
67         int32_t format = 0;
68         NativeWindowHandleOpt(mNativeWindow_, GET_FORMAT, &format);
69         if (format == GRAPHIC_PIXEL_FMT_RGBA_8888) {
70             bufferUsage =
71                 (BUFFER_USAGE_HW_RENDER | BUFFER_USAGE_HW_TEXTURE | BUFFER_USAGE_HW_COMPOSER | BUFFER_USAGE_MEM_DMA);
72         }
73     }
74 #endif
75 
76     NativeWindowHandleOpt(mNativeWindow_, SET_USAGE, bufferUsage);
77     NativeWindowHandleOpt(mNativeWindow_, SET_BUFFER_GEOMETRY, width, height);
78     NativeWindowHandleOpt(mNativeWindow_, SET_COLOR_GAMUT, colorSpace_);
79 }
80 
RequestFrame(int32_t width,int32_t height)81 std::unique_ptr<SurfaceFrame> SurfaceOhosVulkan::RequestFrame(int32_t width, int32_t height)
82 {
83 #ifdef RS_ENABLE_OLD_VK
84     if (mNativeWindow_ == nullptr) {
85         mNativeWindow_ = CreateNativeWindowFromSurface(&producer_);
86         if (mNativeWindow_ == nullptr) {
87             LOGE("SurfaceOhosVulkan nativewindow is null");
88             return nullptr;
89         }
90     }
91 
92     if (mVulkanWindow_ == nullptr) {
93         auto vulkan_surface_ohos = std::make_unique<vulkan::RSVulkanNativeSurfaceOHOS>(mNativeWindow_);
94         mVulkanWindow_ = new vulkan::RSVulkanWindow(std::move(vulkan_surface_ohos));
95     }
96 
97     surface_ = std::make_shared<Drawing::Surface>();
98 #ifdef ENABLE_DDGR_OPTIMIZE
99     auto ddgrSurface = mVulkanWindow_->AcquireSurface();
100     if (!ddgrSurface) {
101         LOGE("SurfaceOhosVulkan: ddgrSurface or skSurface is null, return");
102         return nullptr;
103     }
104 
105     if (!ddgrCanvas_) {
106         ddgrCanvas_ = std::make_shared<DDGR::DDGRCanvasV2>(*(mVulkanWindow_->GetDDGRContext()));
107     }
108     ddgrCanvas_->SetCurSurface(ddgrSurface);
109     ddgrCanvas_->BeginFrame();
110     surface_->GetImpl<Drawing::DDGRSurface>()->ImportDDGRSurface(ddgrSurface);
111     surface_->GetImpl<Drawing::DDGRSurface>()->SetGrContext(mVulkanWindow_->GetDDGRContext());
112 #else // ENABLE_DDGR_OPTIMIZE
113     sk_sp<SkSurface> skSurface = mVulkanWindow_->AcquireSurface();
114     surface_->GetImpl<Drawing::SkiaSurface>()->SetSkSurface(skSurface);
115 #endif // ENABLE_DDGR_OPTIMIZE
116 
117     frame_ = std::make_unique<SurfaceFrameOhosVulkan>(surface_, width, height);
118 
119     frame_->SetColorSpace(GraphicColorGamut::GRAPHIC_COLOR_GAMUT_SRGB);
120 
121     NativeWindowHandleOpt(mNativeWindow_, SET_BUFFER_GEOMETRY, width, height);
122     NativeWindowHandleOpt(mNativeWindow_, SET_COLOR_GAMUT, frame_->GetColorSpace());
123 
124     std::unique_ptr<SurfaceFrame> ret(std::move(frame_));
125     return ret;
126 #else
127     return nullptr;
128 #endif
129 }
130 
CreateVkSemaphore(VkSemaphore * semaphore,RsVulkanContext & vkContext,NativeBufferUtils::NativeSurfaceInfo & nativeSurface)131 void SurfaceOhosVulkan::CreateVkSemaphore(
132     VkSemaphore* semaphore, RsVulkanContext& vkContext, NativeBufferUtils::NativeSurfaceInfo& nativeSurface)
133 {
134     VkSemaphoreCreateInfo semaphoreInfo;
135     semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
136     semaphoreInfo.pNext = nullptr;
137     semaphoreInfo.flags = 0;
138     auto& rsInterface = vkContext.GetRsVulkanInterface();
139     rsInterface.vkCreateSemaphore(rsInterface.GetDevice(), &semaphoreInfo, nullptr, semaphore);
140 
141     VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo;
142     importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
143     importSemaphoreFdInfo.pNext = nullptr;
144     importSemaphoreFdInfo.semaphore = *semaphore;
145     importSemaphoreFdInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
146     importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
147     importSemaphoreFdInfo.fd = nativeSurface.fence->Dup();
148     rsInterface.vkImportSemaphoreFdKHR(rsInterface.GetDevice(), &importSemaphoreFdInfo);
149 }
150 
RequestNativeWindowBuffer(NativeWindowBuffer ** nativeWindowBuffer,int32_t width,int32_t height,int & fenceFd)151 int32_t SurfaceOhosVulkan::RequestNativeWindowBuffer(
152     NativeWindowBuffer** nativeWindowBuffer, int32_t width, int32_t height, int& fenceFd)
153 {
154     SetNativeWindowInfo(width, height);
155     struct timespec curTime = {0, 0};
156     clock_gettime(CLOCK_MONOTONIC, &curTime);
157     // 1000000000 is used for transfer second to nsec
158     uint64_t duration = static_cast<uint64_t>(curTime.tv_sec) * 1000000000 + static_cast<uint64_t>(curTime.tv_nsec);
159     NativeWindowHandleOpt(mNativeWindow_, SET_UI_TIMESTAMP, duration);
160 
161     auto res = NativeWindowRequestBuffer(mNativeWindow_, nativeWindowBuffer, &fenceFd);
162     if (res != OHOS::GSERROR_OK) {
163         LOGE("RSSurfaceOhosVulkan: OH_NativeWindow_NativeWindowRequestBuffer failed %{public}d", res);
164         NativeWindowCancelBuffer(mNativeWindow_, *nativeWindowBuffer);
165     }
166     return res;
167 }
168 
NativeRequestFrame(int32_t width,int32_t height)169 std::unique_ptr<SurfaceFrame> SurfaceOhosVulkan::NativeRequestFrame(int32_t width, int32_t height)
170 {
171     if (mNativeWindow_ == nullptr) {
172         mNativeWindow_ = CreateNativeWindowFromSurface(&producer_);
173         LOGI("RSSurfaceOhosVulkan: create native window");
174     }
175 
176     if (drContext_ == nullptr) {
177         drContext_ = RsVulkanContext::GetSingleton().CreateDrawingContext();
178     }
179     if (!drContext_) {
180         LOGI("RSSurfaceOhosVulkan: skia context is nullptr");
181         return nullptr;
182     }
183 
184     NativeWindowBuffer* nativeWindowBuffer = nullptr;
185     int fenceFd = -1;
186     if (RequestNativeWindowBuffer(&nativeWindowBuffer, width, height, fenceFd) != OHOS::GSERROR_OK) {
187         return nullptr;
188     }
189 
190     surfaceList_.emplace_back(nativeWindowBuffer);
191     NativeBufferUtils::NativeSurfaceInfo& nativeSurface = surfaceMap_[nativeWindowBuffer];
192 
193     if (nativeSurface.drawingSurface == nullptr) {
194         nativeSurface.window = mNativeWindow_;
195         nativeSurface.graphicColorGamut = colorSpace_;
196         if (!NativeBufferUtils::MakeFromNativeWindowBuffer(
197             drContext_, nativeWindowBuffer, nativeSurface, width, height)) {
198             LOGE("RSSurfaceOhosVulkan: MakeFromeNativeWindow failed");
199             NativeWindowCancelBuffer(mNativeWindow_, nativeWindowBuffer);
200             return nullptr;
201         }
202 
203         if (!nativeSurface.drawingSurface) {
204             LOGE("RSSurfaceOhosVulkan: skSurface is null, return");
205             surfaceList_.pop_back();
206             return nullptr;
207         } else {
208             LOGI("RSSurfaceOhosVulkan: skSurface create success %{public}zu", surfaceMap_.size());
209         }
210     }
211 
212     if (fenceFd >= 0) {
213         nativeSurface.fence = std::make_unique<SyncFence>(fenceFd);
214         auto status = nativeSurface.fence->GetStatus();
215         if (status != SIGNALED) {
216             auto& vkContext = RsVulkanContext::GetSingleton();
217             VkSemaphore semaphore;
218             CreateVkSemaphore(&semaphore, vkContext, nativeSurface);
219             nativeSurface.drawingSurface->Wait(1, semaphore);
220         }
221     }
222     frame_ = std::make_unique<SurfaceFrameOhosVulkan>(nativeSurface.drawingSurface, width, height);
223     frame_->SetColorSpace(GraphicColorGamut::GRAPHIC_COLOR_GAMUT_SRGB);
224     std::unique_ptr<SurfaceFrame> ret(std::move(frame_));
225     surface_ = nativeSurface.drawingSurface;
226     return ret;
227 }
228 
FlushFrame(std::unique_ptr<SurfaceFrame> & frame)229 bool SurfaceOhosVulkan::FlushFrame(std::unique_ptr<SurfaceFrame>& frame)
230 {
231 #ifdef RS_ENABLE_OLD_VK
232     if (drawingProxy_ == nullptr) {
233         LOGE("drawingProxy_ is nullptr, can not FlushFrame");
234         return false;
235     }
236     // gpu render flush
237     drawingProxy_->RenderFrame();
238     if (mVulkanWindow_ != nullptr) {
239 #ifdef ENABLE_DDGR_OPTIMIZE
240         mVulkanWindow_->SwapBuffers(ddgrCanvas_);
241 #else
242         mVulkanWindow_->SwapBuffers();
243 #endif
244     } else {
245         LOGE("mVulkanWindow_ is null");
246     }
247 #endif
248     return true;
249 }
250 
NativeFlushFrame(std::unique_ptr<SurfaceFrame> & frame)251 bool SurfaceOhosVulkan::NativeFlushFrame(std::unique_ptr<SurfaceFrame> &frame)
252 {
253     if (drawingProxy_ == nullptr) {
254         LOGE("drawingProxy_ is nullptr, can not FlushFrame");
255         return false;
256     }
257     // gpu render flush
258     drawingProxy_->RenderFrame();
259 
260     if (surfaceList_.empty()) {
261         return false;
262     }
263 
264     if (!drContext_) {
265         LOGE("RSSurfaceOhosVulkan: FlushFrame mSkContext is nullptr");
266         return false;
267     }
268     auto& vkContext = RsVulkanContext::GetSingleton().GetRsVulkanInterface();
269 
270     VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo;
271     exportSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
272     exportSemaphoreCreateInfo.pNext = nullptr;
273     exportSemaphoreCreateInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
274 
275     VkSemaphoreCreateInfo semaphoreInfo;
276     semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
277     semaphoreInfo.pNext = &exportSemaphoreCreateInfo;
278     semaphoreInfo.flags = 0;
279     VkSemaphore semaphore;
280     vkContext.vkCreateSemaphore(vkContext.GetDevice(), &semaphoreInfo, nullptr, &semaphore);
281 
282     GrBackendSemaphore backendSemaphore;
283     backendSemaphore.initVulkan(semaphore);
284 
285     GrFlushInfo flushInfo;
286     flushInfo.fNumSemaphores = 1;
287     flushInfo.fSignalSemaphores = &backendSemaphore;
288 
289     auto& surface = surfaceMap_[surfaceList_.front()];
290     Drawing::FlushInfo drawingFlushInfo;
291     drawingFlushInfo.backendSurfaceAccess = true;
292     drawingFlushInfo.numSemaphores = 1;
293     drawingFlushInfo.backendSemaphore = static_cast<void*>(&backendSemaphore);
294     surface.drawingSurface->Flush(&drawingFlushInfo);
295     drContext_->Submit();
296 
297     int fenceFd = -1;
298 
299     auto queue = vkContext.GetQueue();
300     if (vkContext.GetHardWareGrContext().get() == drContext_.get()) {
301         queue = vkContext.GetHardwareQueue();
302     }
303     auto err = RsVulkanContext::HookedVkQueueSignalReleaseImageOHOS(
304         queue, 1, &semaphore, surface.image, &fenceFd);
305     if (err != VK_SUCCESS) {
306         LOGE("RSSurfaceOhosVulkan QueueSignalReleaseImageOHOS failed %{public}d", err);
307         return false;
308     }
309 
310     auto ret = NativeWindowFlushBuffer(surface.window, surface.nativeWindowBuffer, fenceFd, {});
311     if (ret != OHOS::GSERROR_OK) {
312         LOGE("RSSurfaceOhosVulkan NativeWindowFlushBuffer failed");
313         return false;
314     }
315     surfaceList_.pop_front();
316     vkContext.vkDestroySemaphore(vkContext.GetDevice(), semaphore, nullptr);
317     surface.fence.reset();
318     return true;
319 }
320 
GetCanvas(std::unique_ptr<SurfaceFrame> & frame)321 Drawing::Canvas* SurfaceOhosVulkan::GetCanvas(std::unique_ptr<SurfaceFrame>& frame)
322 {
323     if (drawingProxy_ == nullptr) {
324         LOGE("drawingProxy_ is nullptr, can not GetCanvas");
325         return nullptr;
326     }
327     return drawingProxy_->AcquireDrCanvas(frame);
328 }
GetSkCanvas(std::unique_ptr<SurfaceFrame> & frame)329 SkCanvas* SurfaceOhosVulkan::GetSkCanvas(std::unique_ptr<SurfaceFrame>& frame)
330 {
331     if (drawingProxy_ == nullptr) {
332         LOGE("drawingProxy_ is nullptr, can not GetCanvas");
333         return nullptr;
334     }
335     return drawingProxy_->AcquireSkCanvas(frame);
336 }
337 } // namespace Rosen
338 } // namespace OHOS
339