1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright 2019 Google LLC
3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT
4bf215546Sopenharmony_ci *
5bf215546Sopenharmony_ci * based in part on anv and radv which are:
6bf215546Sopenharmony_ci * Copyright © 2015 Intel Corporation
7bf215546Sopenharmony_ci * Copyright © 2016 Red Hat.
8bf215546Sopenharmony_ci * Copyright © 2016 Bas Nieuwenhuizen
9bf215546Sopenharmony_ci */
10bf215546Sopenharmony_ci
11bf215546Sopenharmony_ci#include "vn_wsi.h"
12bf215546Sopenharmony_ci
13bf215546Sopenharmony_ci#include "drm-uapi/drm_fourcc.h"
14bf215546Sopenharmony_ci#include "vk_enum_to_str.h"
15bf215546Sopenharmony_ci#include "wsi_common_entrypoints.h"
16bf215546Sopenharmony_ci
17bf215546Sopenharmony_ci#include "vn_device.h"
18bf215546Sopenharmony_ci#include "vn_image.h"
19bf215546Sopenharmony_ci#include "vn_instance.h"
20bf215546Sopenharmony_ci#include "vn_physical_device.h"
21bf215546Sopenharmony_ci#include "vn_queue.h"
22bf215546Sopenharmony_ci
23bf215546Sopenharmony_ci/* The common WSI support makes some assumptions about the driver.
24bf215546Sopenharmony_ci *
25bf215546Sopenharmony_ci * In wsi_device_init, it assumes VK_EXT_pci_bus_info is available.  In
26bf215546Sopenharmony_ci * wsi_create_native_image and wsi_create_prime_image, it assumes
27bf215546Sopenharmony_ci * VK_KHR_external_memory_fd and VK_EXT_external_memory_dma_buf are enabled.
28bf215546Sopenharmony_ci *
29bf215546Sopenharmony_ci * In wsi_create_native_image, if wsi_device::supports_modifiers is set and
30bf215546Sopenharmony_ci * the window system supports modifiers, it assumes
31bf215546Sopenharmony_ci * VK_EXT_image_drm_format_modifier is enabled.  Otherwise, it assumes that
32bf215546Sopenharmony_ci * wsi_image_create_info can be chained to VkImageCreateInfo and
33bf215546Sopenharmony_ci * vkGetImageSubresourceLayout can be called even the tiling is
34bf215546Sopenharmony_ci * VK_IMAGE_TILING_OPTIMAL.
35bf215546Sopenharmony_ci *
36bf215546Sopenharmony_ci * Together, it knows how to share dma-bufs, with explicit or implicit
37bf215546Sopenharmony_ci * modifiers, to the window system.
38bf215546Sopenharmony_ci *
39bf215546Sopenharmony_ci * For venus, we use explicit modifiers when the renderer and the window
40bf215546Sopenharmony_ci * system support them.  Otherwise, we have to fall back to
41bf215546Sopenharmony_ci * VK_IMAGE_TILING_LINEAR (or trigger the prime blit path).  But the fallback
42bf215546Sopenharmony_ci * can be problematic when the memory is scanned out directly and special
43bf215546Sopenharmony_ci * requirements (e.g., alignments) must be met.
44bf215546Sopenharmony_ci *
45bf215546Sopenharmony_ci * The common WSI support makes other assumptions about the driver to support
46bf215546Sopenharmony_ci * implicit fencing.  In wsi_create_native_image and wsi_create_prime_image,
47bf215546Sopenharmony_ci * it assumes wsi_memory_allocate_info can be chained to VkMemoryAllocateInfo.
48bf215546Sopenharmony_ci * In wsi_common_queue_present, it assumes wsi_memory_signal_submit_info can
49bf215546Sopenharmony_ci * be chained to VkSubmitInfo.  Finally, in wsi_common_acquire_next_image2, it
50bf215546Sopenharmony_ci * calls wsi_device::signal_semaphore_for_memory, and
51bf215546Sopenharmony_ci * wsi_device::signal_fence_for_memory if the driver provides them.
52bf215546Sopenharmony_ci *
53bf215546Sopenharmony_ci * Some drivers use wsi_memory_allocate_info to set up implicit fencing.
54bf215546Sopenharmony_ci * Others use wsi_memory_signal_submit_info to set up implicit IN-fences and
55bf215546Sopenharmony_ci * use wsi_device::signal_*_for_memory to set up implicit OUT-fences.
56bf215546Sopenharmony_ci *
57bf215546Sopenharmony_ci * For venus, implicit fencing is broken (and there is no explicit fencing
58bf215546Sopenharmony_ci * support yet).  The kernel driver assumes everything is in the same fence
59bf215546Sopenharmony_ci * context and no synchronization is needed.  It should be fixed for
60bf215546Sopenharmony_ci * correctness, but it is still not ideal.  venus requires explicit fencing
61bf215546Sopenharmony_ci * (and renderer-side synchronization) to work well.
62bf215546Sopenharmony_ci */
63bf215546Sopenharmony_ci
64bf215546Sopenharmony_ci/* cast a WSI object to a pointer for logging */
65bf215546Sopenharmony_ci#define VN_WSI_PTR(obj) ((const void *)(uintptr_t)(obj))
66bf215546Sopenharmony_ci
67bf215546Sopenharmony_cistatic PFN_vkVoidFunction
68bf215546Sopenharmony_civn_wsi_proc_addr(VkPhysicalDevice physicalDevice, const char *pName)
69bf215546Sopenharmony_ci{
70bf215546Sopenharmony_ci   struct vn_physical_device *physical_dev =
71bf215546Sopenharmony_ci      vn_physical_device_from_handle(physicalDevice);
72bf215546Sopenharmony_ci   return vk_instance_get_proc_addr_unchecked(
73bf215546Sopenharmony_ci      &physical_dev->instance->base.base, pName);
74bf215546Sopenharmony_ci}
75bf215546Sopenharmony_ci
76bf215546Sopenharmony_ciVkResult
77bf215546Sopenharmony_civn_wsi_init(struct vn_physical_device *physical_dev)
78bf215546Sopenharmony_ci{
79bf215546Sopenharmony_ci   const VkAllocationCallbacks *alloc =
80bf215546Sopenharmony_ci      &physical_dev->instance->base.base.alloc;
81bf215546Sopenharmony_ci   VkResult result = wsi_device_init(
82bf215546Sopenharmony_ci      &physical_dev->wsi_device, vn_physical_device_to_handle(physical_dev),
83bf215546Sopenharmony_ci      vn_wsi_proc_addr, alloc, -1, &physical_dev->instance->dri_options,
84bf215546Sopenharmony_ci      false);
85bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
86bf215546Sopenharmony_ci      return result;
87bf215546Sopenharmony_ci
88bf215546Sopenharmony_ci   if (physical_dev->base.base.supported_extensions
89bf215546Sopenharmony_ci          .EXT_image_drm_format_modifier)
90bf215546Sopenharmony_ci      physical_dev->wsi_device.supports_modifiers = true;
91bf215546Sopenharmony_ci
92bf215546Sopenharmony_ci   physical_dev->base.base.wsi_device = &physical_dev->wsi_device;
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_ci   return VK_SUCCESS;
95bf215546Sopenharmony_ci}
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_civoid
98bf215546Sopenharmony_civn_wsi_fini(struct vn_physical_device *physical_dev)
99bf215546Sopenharmony_ci{
100bf215546Sopenharmony_ci   const VkAllocationCallbacks *alloc =
101bf215546Sopenharmony_ci      &physical_dev->instance->base.base.alloc;
102bf215546Sopenharmony_ci   physical_dev->base.base.wsi_device = NULL;
103bf215546Sopenharmony_ci   wsi_device_finish(&physical_dev->wsi_device, alloc);
104bf215546Sopenharmony_ci}
105bf215546Sopenharmony_ci
106bf215546Sopenharmony_ciVkResult
107bf215546Sopenharmony_civn_wsi_create_image(struct vn_device *dev,
108bf215546Sopenharmony_ci                    const VkImageCreateInfo *create_info,
109bf215546Sopenharmony_ci                    const struct wsi_image_create_info *wsi_info,
110bf215546Sopenharmony_ci                    const VkAllocationCallbacks *alloc,
111bf215546Sopenharmony_ci                    struct vn_image **out_img)
112bf215546Sopenharmony_ci{
113bf215546Sopenharmony_ci   /* TODO This is the legacy path used by wsi_create_native_image when there
114bf215546Sopenharmony_ci    * is no modifier support.  Instead of forcing linear tiling, we should ask
115bf215546Sopenharmony_ci    * wsi to use wsi_create_prime_image instead.
116bf215546Sopenharmony_ci    *
117bf215546Sopenharmony_ci    * In fact, this is not enough when the image is truely used for scanout by
118bf215546Sopenharmony_ci    * the host compositor.  There can be requirements we fail to meet.  We
119bf215546Sopenharmony_ci    * should require modifier support at some point.
120bf215546Sopenharmony_ci    */
121bf215546Sopenharmony_ci   const uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
122bf215546Sopenharmony_ci   const VkImageDrmFormatModifierListCreateInfoEXT mod_list_info = {
123bf215546Sopenharmony_ci      .sType =
124bf215546Sopenharmony_ci         VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
125bf215546Sopenharmony_ci      .pNext = create_info->pNext,
126bf215546Sopenharmony_ci      .drmFormatModifierCount = 1,
127bf215546Sopenharmony_ci      .pDrmFormatModifiers = &modifier,
128bf215546Sopenharmony_ci   };
129bf215546Sopenharmony_ci   VkImageCreateInfo local_create_info;
130bf215546Sopenharmony_ci   if (wsi_info->scanout) {
131bf215546Sopenharmony_ci      assert(!vk_find_struct_const(
132bf215546Sopenharmony_ci         create_info->pNext, IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT));
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci      local_create_info = *create_info;
135bf215546Sopenharmony_ci      local_create_info.pNext = &mod_list_info;
136bf215546Sopenharmony_ci      local_create_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
137bf215546Sopenharmony_ci      create_info = &local_create_info;
138bf215546Sopenharmony_ci
139bf215546Sopenharmony_ci      if (VN_DEBUG(WSI))
140bf215546Sopenharmony_ci         vn_log(dev->instance, "forcing scanout image linear");
141bf215546Sopenharmony_ci   }
142bf215546Sopenharmony_ci
143bf215546Sopenharmony_ci   struct vn_image *img;
144bf215546Sopenharmony_ci   VkResult result = vn_image_create(dev, create_info, alloc, &img);
145bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
146bf215546Sopenharmony_ci      return result;
147bf215546Sopenharmony_ci
148bf215546Sopenharmony_ci   img->wsi.is_wsi = true;
149bf215546Sopenharmony_ci   img->wsi.is_prime_blit_src = wsi_info->buffer_blit_src;
150bf215546Sopenharmony_ci   img->wsi.tiling_override = create_info->tiling;
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci   if (create_info->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
153bf215546Sopenharmony_ci      VkDevice dev_handle = vn_device_to_handle(dev);
154bf215546Sopenharmony_ci      VkImage img_handle = vn_image_to_handle(img);
155bf215546Sopenharmony_ci
156bf215546Sopenharmony_ci      VkImageDrmFormatModifierPropertiesEXT props = {
157bf215546Sopenharmony_ci         .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
158bf215546Sopenharmony_ci      };
159bf215546Sopenharmony_ci      result = vn_GetImageDrmFormatModifierPropertiesEXT(dev_handle,
160bf215546Sopenharmony_ci                                                         img_handle, &props);
161bf215546Sopenharmony_ci      if (result != VK_SUCCESS) {
162bf215546Sopenharmony_ci         vn_DestroyImage(dev_handle, img_handle, alloc);
163bf215546Sopenharmony_ci         return result;
164bf215546Sopenharmony_ci      }
165bf215546Sopenharmony_ci
166bf215546Sopenharmony_ci      img->wsi.drm_format_modifier = props.drmFormatModifier;
167bf215546Sopenharmony_ci   }
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_ci   *out_img = img;
170bf215546Sopenharmony_ci   return VK_SUCCESS;
171bf215546Sopenharmony_ci}
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ciVkResult
174bf215546Sopenharmony_civn_wsi_create_image_from_swapchain(
175bf215546Sopenharmony_ci   struct vn_device *dev,
176bf215546Sopenharmony_ci   const VkImageCreateInfo *create_info,
177bf215546Sopenharmony_ci   const VkImageSwapchainCreateInfoKHR *swapchain_info,
178bf215546Sopenharmony_ci   const VkAllocationCallbacks *alloc,
179bf215546Sopenharmony_ci   struct vn_image **out_img)
180bf215546Sopenharmony_ci{
181bf215546Sopenharmony_ci   const struct vn_image *swapchain_img = vn_image_from_handle(
182bf215546Sopenharmony_ci      wsi_common_get_image(swapchain_info->swapchain, 0));
183bf215546Sopenharmony_ci   assert(swapchain_img->wsi.is_wsi);
184bf215546Sopenharmony_ci
185bf215546Sopenharmony_ci   /* must match what the common WSI and vn_wsi_create_image do */
186bf215546Sopenharmony_ci   VkImageCreateInfo local_create_info = *create_info;
187bf215546Sopenharmony_ci
188bf215546Sopenharmony_ci   /* match external memory */
189bf215546Sopenharmony_ci   const VkExternalMemoryImageCreateInfo local_external_info = {
190bf215546Sopenharmony_ci      .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
191bf215546Sopenharmony_ci      .pNext = local_create_info.pNext,
192bf215546Sopenharmony_ci      .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
193bf215546Sopenharmony_ci   };
194bf215546Sopenharmony_ci   local_create_info.pNext = &local_external_info;
195bf215546Sopenharmony_ci
196bf215546Sopenharmony_ci   /* match image tiling */
197bf215546Sopenharmony_ci   local_create_info.tiling = swapchain_img->wsi.tiling_override;
198bf215546Sopenharmony_ci
199bf215546Sopenharmony_ci   VkImageDrmFormatModifierListCreateInfoEXT local_mod_info;
200bf215546Sopenharmony_ci   if (local_create_info.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
201bf215546Sopenharmony_ci      local_mod_info = (const VkImageDrmFormatModifierListCreateInfoEXT){
202bf215546Sopenharmony_ci         .sType =
203bf215546Sopenharmony_ci            VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT,
204bf215546Sopenharmony_ci         .pNext = local_create_info.pNext,
205bf215546Sopenharmony_ci         .drmFormatModifierCount = 1,
206bf215546Sopenharmony_ci         .pDrmFormatModifiers = &swapchain_img->wsi.drm_format_modifier,
207bf215546Sopenharmony_ci      };
208bf215546Sopenharmony_ci      local_create_info.pNext = &local_mod_info;
209bf215546Sopenharmony_ci   }
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci   /* match image usage */
212bf215546Sopenharmony_ci   if (swapchain_img->wsi.is_prime_blit_src)
213bf215546Sopenharmony_ci      local_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
214bf215546Sopenharmony_ci
215bf215546Sopenharmony_ci   create_info = &local_create_info;
216bf215546Sopenharmony_ci
217bf215546Sopenharmony_ci   struct vn_image *img;
218bf215546Sopenharmony_ci   VkResult result = vn_image_create(dev, create_info, alloc, &img);
219bf215546Sopenharmony_ci   if (result != VK_SUCCESS)
220bf215546Sopenharmony_ci      return result;
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci   img->wsi.is_wsi = true;
223bf215546Sopenharmony_ci   img->wsi.tiling_override = swapchain_img->wsi.tiling_override;
224bf215546Sopenharmony_ci   img->wsi.drm_format_modifier = swapchain_img->wsi.drm_format_modifier;
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci   *out_img = img;
227bf215546Sopenharmony_ci   return VK_SUCCESS;
228bf215546Sopenharmony_ci}
229bf215546Sopenharmony_ci
230bf215546Sopenharmony_ci/* swapchain commands */
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ciVkResult
233bf215546Sopenharmony_civn_CreateSwapchainKHR(VkDevice device,
234bf215546Sopenharmony_ci                      const VkSwapchainCreateInfoKHR *pCreateInfo,
235bf215546Sopenharmony_ci                      const VkAllocationCallbacks *pAllocator,
236bf215546Sopenharmony_ci                      VkSwapchainKHR *pSwapchain)
237bf215546Sopenharmony_ci{
238bf215546Sopenharmony_ci   struct vn_device *dev = vn_device_from_handle(device);
239bf215546Sopenharmony_ci
240bf215546Sopenharmony_ci   VkResult result =
241bf215546Sopenharmony_ci      wsi_CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
242bf215546Sopenharmony_ci   if (VN_DEBUG(WSI) && result == VK_SUCCESS) {
243bf215546Sopenharmony_ci      vn_log(dev->instance,
244bf215546Sopenharmony_ci             "swapchain %p: created with surface %p, min count %d, size "
245bf215546Sopenharmony_ci             "%dx%d, mode %s, old %p",
246bf215546Sopenharmony_ci             VN_WSI_PTR(*pSwapchain), VN_WSI_PTR(pCreateInfo->surface),
247bf215546Sopenharmony_ci             pCreateInfo->minImageCount, pCreateInfo->imageExtent.width,
248bf215546Sopenharmony_ci             pCreateInfo->imageExtent.height,
249bf215546Sopenharmony_ci             vk_PresentModeKHR_to_str(pCreateInfo->presentMode),
250bf215546Sopenharmony_ci             VN_WSI_PTR(pCreateInfo->oldSwapchain));
251bf215546Sopenharmony_ci   }
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_ci   return vn_result(dev->instance, result);
254bf215546Sopenharmony_ci}
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_civoid
257bf215546Sopenharmony_civn_DestroySwapchainKHR(VkDevice device,
258bf215546Sopenharmony_ci                       VkSwapchainKHR swapchain,
259bf215546Sopenharmony_ci                       const VkAllocationCallbacks *pAllocator)
260bf215546Sopenharmony_ci{
261bf215546Sopenharmony_ci   struct vn_device *dev = vn_device_from_handle(device);
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_ci   wsi_DestroySwapchainKHR(device, swapchain, pAllocator);
264bf215546Sopenharmony_ci   if (VN_DEBUG(WSI))
265bf215546Sopenharmony_ci      vn_log(dev->instance, "swapchain %p: destroyed", VN_WSI_PTR(swapchain));
266bf215546Sopenharmony_ci}
267bf215546Sopenharmony_ci
268bf215546Sopenharmony_ciVkResult
269bf215546Sopenharmony_civn_QueuePresentKHR(VkQueue _queue, const VkPresentInfoKHR *pPresentInfo)
270bf215546Sopenharmony_ci{
271bf215546Sopenharmony_ci   VN_TRACE_FUNC();
272bf215546Sopenharmony_ci   struct vn_queue *queue = vn_queue_from_handle(_queue);
273bf215546Sopenharmony_ci
274bf215546Sopenharmony_ci   VkResult result =
275bf215546Sopenharmony_ci      wsi_common_queue_present(&queue->device->physical_device->wsi_device,
276bf215546Sopenharmony_ci                               vn_device_to_handle(queue->device), _queue,
277bf215546Sopenharmony_ci                               queue->family, pPresentInfo);
278bf215546Sopenharmony_ci   if (VN_DEBUG(WSI) && result != VK_SUCCESS) {
279bf215546Sopenharmony_ci      for (uint32_t i = 0; i < pPresentInfo->swapchainCount; i++) {
280bf215546Sopenharmony_ci         const VkResult r =
281bf215546Sopenharmony_ci            pPresentInfo->pResults ? pPresentInfo->pResults[i] : result;
282bf215546Sopenharmony_ci         vn_log(queue->device->instance,
283bf215546Sopenharmony_ci                "swapchain %p: presented image %d: %s",
284bf215546Sopenharmony_ci                VN_WSI_PTR(pPresentInfo->pSwapchains[i]),
285bf215546Sopenharmony_ci                pPresentInfo->pImageIndices[i], vk_Result_to_str(r));
286bf215546Sopenharmony_ci      }
287bf215546Sopenharmony_ci   }
288bf215546Sopenharmony_ci
289bf215546Sopenharmony_ci   return vn_result(queue->device->instance, result);
290bf215546Sopenharmony_ci}
291bf215546Sopenharmony_ci
292bf215546Sopenharmony_ciVkResult
293bf215546Sopenharmony_civn_AcquireNextImage2KHR(VkDevice device,
294bf215546Sopenharmony_ci                        const VkAcquireNextImageInfoKHR *pAcquireInfo,
295bf215546Sopenharmony_ci                        uint32_t *pImageIndex)
296bf215546Sopenharmony_ci{
297bf215546Sopenharmony_ci   VN_TRACE_FUNC();
298bf215546Sopenharmony_ci   struct vn_device *dev = vn_device_from_handle(device);
299bf215546Sopenharmony_ci
300bf215546Sopenharmony_ci   VkResult result = wsi_common_acquire_next_image2(
301bf215546Sopenharmony_ci      &dev->physical_device->wsi_device, device, pAcquireInfo, pImageIndex);
302bf215546Sopenharmony_ci   if (VN_DEBUG(WSI) && result != VK_SUCCESS) {
303bf215546Sopenharmony_ci      const int idx = result >= VK_SUCCESS ? *pImageIndex : -1;
304bf215546Sopenharmony_ci      vn_log(dev->instance, "swapchain %p: acquired image %d: %s",
305bf215546Sopenharmony_ci             VN_WSI_PTR(pAcquireInfo->swapchain), idx,
306bf215546Sopenharmony_ci             vk_Result_to_str(result));
307bf215546Sopenharmony_ci   }
308bf215546Sopenharmony_ci
309bf215546Sopenharmony_ci   /* XXX this relies on implicit sync */
310bf215546Sopenharmony_ci   if (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) {
311bf215546Sopenharmony_ci      struct vn_semaphore *sem =
312bf215546Sopenharmony_ci         vn_semaphore_from_handle(pAcquireInfo->semaphore);
313bf215546Sopenharmony_ci      if (sem)
314bf215546Sopenharmony_ci         vn_semaphore_signal_wsi(dev, sem);
315bf215546Sopenharmony_ci
316bf215546Sopenharmony_ci      struct vn_fence *fence = vn_fence_from_handle(pAcquireInfo->fence);
317bf215546Sopenharmony_ci      if (fence)
318bf215546Sopenharmony_ci         vn_fence_signal_wsi(dev, fence);
319bf215546Sopenharmony_ci   }
320bf215546Sopenharmony_ci
321bf215546Sopenharmony_ci   return vn_result(dev->instance, result);
322bf215546Sopenharmony_ci}
323