1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#ifndef GrVkImage_DEFINED 9cb93a386Sopenharmony_ci#define GrVkImage_DEFINED 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 12cb93a386Sopenharmony_ci#include "include/gpu/GrBackendSurface.h" 13cb93a386Sopenharmony_ci#include "include/gpu/vk/GrVkTypes.h" 14cb93a386Sopenharmony_ci#include "include/private/GrTypesPriv.h" 15cb93a386Sopenharmony_ci#include "include/private/GrVkTypesPriv.h" 16cb93a386Sopenharmony_ci#include "src/gpu/GrAttachment.h" 17cb93a386Sopenharmony_ci#include "src/gpu/GrBackendSurfaceMutableStateImpl.h" 18cb93a386Sopenharmony_ci#include "src/gpu/GrManagedResource.h" 19cb93a386Sopenharmony_ci#include "src/gpu/GrRefCnt.h" 20cb93a386Sopenharmony_ci#include "src/gpu/GrTexture.h" 21cb93a386Sopenharmony_ci#include "src/gpu/vk/GrVkDescriptorSet.h" 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci#include <cinttypes> 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_ciclass GrVkGpu; 26cb93a386Sopenharmony_ciclass GrVkImageView; 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ciclass SK_API GrVkImage : public GrAttachment { 29cb93a386Sopenharmony_ciprivate: 30cb93a386Sopenharmony_ci class Resource; 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_cipublic: 33cb93a386Sopenharmony_ci static sk_sp<GrVkImage> MakeStencil(GrVkGpu* gpu, 34cb93a386Sopenharmony_ci SkISize dimensions, 35cb93a386Sopenharmony_ci int sampleCnt, 36cb93a386Sopenharmony_ci VkFormat format); 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci static sk_sp<GrVkImage> MakeMSAA(GrVkGpu* gpu, 39cb93a386Sopenharmony_ci SkISize dimensions, 40cb93a386Sopenharmony_ci int numSamples, 41cb93a386Sopenharmony_ci VkFormat format, 42cb93a386Sopenharmony_ci GrProtected isProtected, 43cb93a386Sopenharmony_ci GrMemoryless memoryless); 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_ci static sk_sp<GrVkImage> MakeTexture(GrVkGpu* gpu, 46cb93a386Sopenharmony_ci SkISize dimensions, 47cb93a386Sopenharmony_ci VkFormat format, 48cb93a386Sopenharmony_ci uint32_t mipLevels, 49cb93a386Sopenharmony_ci GrRenderable renderable, 50cb93a386Sopenharmony_ci int numSamples, 51cb93a386Sopenharmony_ci SkBudgeted budgeted, 52cb93a386Sopenharmony_ci GrProtected isProtected); 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci static sk_sp<GrVkImage> MakeWrapped(GrVkGpu* gpu, 55cb93a386Sopenharmony_ci SkISize dimensions, 56cb93a386Sopenharmony_ci const GrVkImageInfo&, 57cb93a386Sopenharmony_ci sk_sp<GrBackendSurfaceMutableStateImpl>, 58cb93a386Sopenharmony_ci UsageFlags attachmentUsages, 59cb93a386Sopenharmony_ci GrWrapOwnership, 60cb93a386Sopenharmony_ci GrWrapCacheable, 61cb93a386Sopenharmony_ci bool forSecondaryCB = false); 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci // OH ISSUE: Integrate Destroy and Free 64cb93a386Sopenharmony_ci static void DestroyAndFreeImageMemory(const GrVkGpu* gpu, const GrVkAlloc& alloc, const VkImage& image); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci ~GrVkImage() override; 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ci VkImage image() const { 69cb93a386Sopenharmony_ci // Should only be called when we have a real fResource object, i.e. never when being used as 70cb93a386Sopenharmony_ci // a RT in an external secondary command buffer. 71cb93a386Sopenharmony_ci SkASSERT(fResource); 72cb93a386Sopenharmony_ci return fInfo.fImage; 73cb93a386Sopenharmony_ci } 74cb93a386Sopenharmony_ci const GrVkAlloc& alloc() const { 75cb93a386Sopenharmony_ci // Should only be called when we have a real fResource object, i.e. never when being used as 76cb93a386Sopenharmony_ci // a RT in an external secondary command buffer. 77cb93a386Sopenharmony_ci SkASSERT(fResource); 78cb93a386Sopenharmony_ci return fInfo.fAlloc; 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci const GrVkImageInfo& vkImageInfo() const { return fInfo; } 81cb93a386Sopenharmony_ci VkFormat imageFormat() const { return fInfo.fFormat; } 82cb93a386Sopenharmony_ci GrBackendFormat backendFormat() const override { 83cb93a386Sopenharmony_ci bool usesDRMModifier = 84cb93a386Sopenharmony_ci this->vkImageInfo().fImageTiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; 85cb93a386Sopenharmony_ci if (fResource && this->ycbcrConversionInfo().isValid()) { 86cb93a386Sopenharmony_ci SkASSERT(this->imageFormat() == this->ycbcrConversionInfo().fFormat); 87cb93a386Sopenharmony_ci return GrBackendFormat::MakeVk(this->ycbcrConversionInfo(), usesDRMModifier); 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci SkASSERT(this->imageFormat() != VK_FORMAT_UNDEFINED); 90cb93a386Sopenharmony_ci return GrBackendFormat::MakeVk(this->imageFormat(), usesDRMModifier); 91cb93a386Sopenharmony_ci } 92cb93a386Sopenharmony_ci uint32_t mipLevels() const { return fInfo.fLevelCount; } 93cb93a386Sopenharmony_ci const GrVkYcbcrConversionInfo& ycbcrConversionInfo() const { 94cb93a386Sopenharmony_ci // Should only be called when we have a real fResource object, i.e. never when being used as 95cb93a386Sopenharmony_ci // a RT in an external secondary command buffer. 96cb93a386Sopenharmony_ci SkASSERT(fResource); 97cb93a386Sopenharmony_ci return fInfo.fYcbcrConversionInfo; 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci VkImageUsageFlags vkUsageFlags() { return fInfo.fImageUsageFlags; } 100cb93a386Sopenharmony_ci bool supportsInputAttachmentUsage() const { 101cb93a386Sopenharmony_ci return fInfo.fImageUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; 102cb93a386Sopenharmony_ci } 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ci const GrVkImageView* framebufferView() const { return fFramebufferView.get(); } 105cb93a386Sopenharmony_ci const GrVkImageView* textureView() const { return fTextureView.get(); } 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci // So that we don't need to rewrite descriptor sets each time, we keep cached input descriptor 108cb93a386Sopenharmony_ci // sets on the attachment and simply reuse those descriptor sets for this attachment only. These 109cb93a386Sopenharmony_ci // calls will fail if the attachment does not support being used as an input attachment. These 110cb93a386Sopenharmony_ci // calls do not ref the GrVkDescriptorSet so they called will need to manually ref them if they 111cb93a386Sopenharmony_ci // need to be kept alive. 112cb93a386Sopenharmony_ci gr_rp<const GrVkDescriptorSet> inputDescSetForBlending(GrVkGpu* gpu); 113cb93a386Sopenharmony_ci // Input descripotr set used when needing to read a resolve attachment to load data into a 114cb93a386Sopenharmony_ci // discardable msaa attachment. 115cb93a386Sopenharmony_ci gr_rp<const GrVkDescriptorSet> inputDescSetForMSAALoad(GrVkGpu* gpu); 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci const Resource* resource() const { 118cb93a386Sopenharmony_ci SkASSERT(fResource); 119cb93a386Sopenharmony_ci return fResource; 120cb93a386Sopenharmony_ci } 121cb93a386Sopenharmony_ci bool isLinearTiled() const { 122cb93a386Sopenharmony_ci // Should only be called when we have a real fResource object, i.e. never when being used as 123cb93a386Sopenharmony_ci // a RT in an external secondary command buffer. 124cb93a386Sopenharmony_ci SkASSERT(fResource); 125cb93a386Sopenharmony_ci return SkToBool(VK_IMAGE_TILING_LINEAR == fInfo.fImageTiling); 126cb93a386Sopenharmony_ci } 127cb93a386Sopenharmony_ci bool isBorrowed() const { return fIsBorrowed; } 128cb93a386Sopenharmony_ci 129cb93a386Sopenharmony_ci sk_sp<GrBackendSurfaceMutableStateImpl> getMutableState() const { return fMutableState; } 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_ci VkImageLayout currentLayout() const { return fMutableState->getImageLayout(); } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci void setImageLayoutAndQueueIndex(const GrVkGpu* gpu, 134cb93a386Sopenharmony_ci VkImageLayout newLayout, 135cb93a386Sopenharmony_ci VkAccessFlags dstAccessMask, 136cb93a386Sopenharmony_ci VkPipelineStageFlags dstStageMask, 137cb93a386Sopenharmony_ci bool byRegion, 138cb93a386Sopenharmony_ci uint32_t newQueueFamilyIndex); 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci void setImageLayout(const GrVkGpu* gpu, 141cb93a386Sopenharmony_ci VkImageLayout newLayout, 142cb93a386Sopenharmony_ci VkAccessFlags dstAccessMask, 143cb93a386Sopenharmony_ci VkPipelineStageFlags dstStageMask, 144cb93a386Sopenharmony_ci bool byRegion) { 145cb93a386Sopenharmony_ci this->setImageLayoutAndQueueIndex(gpu, newLayout, dstAccessMask, dstStageMask, byRegion, 146cb93a386Sopenharmony_ci VK_QUEUE_FAMILY_IGNORED); 147cb93a386Sopenharmony_ci } 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci uint32_t currentQueueFamilyIndex() const { return fMutableState->getQueueFamilyIndex(); } 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci void setQueueFamilyIndex(uint32_t queueFamilyIndex) { 152cb93a386Sopenharmony_ci fMutableState->setQueueFamilyIndex(queueFamilyIndex); 153cb93a386Sopenharmony_ci } 154cb93a386Sopenharmony_ci 155cb93a386Sopenharmony_ci // Returns the image to its original queue family and changes the layout to present if the queue 156cb93a386Sopenharmony_ci // family is not external or foreign. 157cb93a386Sopenharmony_ci void prepareForPresent(GrVkGpu* gpu); 158cb93a386Sopenharmony_ci 159cb93a386Sopenharmony_ci // Returns the image to its original queue family 160cb93a386Sopenharmony_ci void prepareForExternal(GrVkGpu* gpu); 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci // This simply updates our tracking of the image layout and does not actually do any gpu work. 163cb93a386Sopenharmony_ci // This is only used for mip map generation where we are manually changing the layouts as we 164cb93a386Sopenharmony_ci // blit each layer, and then at the end need to update our tracking. 165cb93a386Sopenharmony_ci void updateImageLayout(VkImageLayout newLayout) { 166cb93a386Sopenharmony_ci // Should only be called when we have a real fResource object, i.e. never when being used as 167cb93a386Sopenharmony_ci // a RT in an external secondary command buffer. 168cb93a386Sopenharmony_ci SkASSERT(fResource); 169cb93a386Sopenharmony_ci fMutableState->setImageLayout(newLayout); 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci struct ImageDesc { 173cb93a386Sopenharmony_ci VkImageType fImageType; 174cb93a386Sopenharmony_ci VkFormat fFormat; 175cb93a386Sopenharmony_ci uint32_t fWidth; 176cb93a386Sopenharmony_ci uint32_t fHeight; 177cb93a386Sopenharmony_ci uint32_t fLevels; 178cb93a386Sopenharmony_ci uint32_t fSamples; 179cb93a386Sopenharmony_ci VkImageTiling fImageTiling; 180cb93a386Sopenharmony_ci VkImageUsageFlags fUsageFlags; 181cb93a386Sopenharmony_ci VkFlags fMemProps; 182cb93a386Sopenharmony_ci GrProtected fIsProtected; 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_ci ImageDesc() 185cb93a386Sopenharmony_ci : fImageType(VK_IMAGE_TYPE_2D) 186cb93a386Sopenharmony_ci , fFormat(VK_FORMAT_UNDEFINED) 187cb93a386Sopenharmony_ci , fWidth(0) 188cb93a386Sopenharmony_ci , fHeight(0) 189cb93a386Sopenharmony_ci , fLevels(1) 190cb93a386Sopenharmony_ci , fSamples(1) 191cb93a386Sopenharmony_ci , fImageTiling(VK_IMAGE_TILING_OPTIMAL) 192cb93a386Sopenharmony_ci , fUsageFlags(0) 193cb93a386Sopenharmony_ci , fMemProps(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) 194cb93a386Sopenharmony_ci , fIsProtected(GrProtected::kNo) {} 195cb93a386Sopenharmony_ci }; 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_ci static bool InitImageInfo(GrVkGpu* gpu, const ImageDesc& imageDesc, GrVkImageInfo*); 198cb93a386Sopenharmony_ci // Destroys the internal VkImage and VkDeviceMemory in the GrVkImageInfo 199cb93a386Sopenharmony_ci static void DestroyImageInfo(const GrVkGpu* gpu, GrVkImageInfo*); 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci // These match the definitions in SkImage, for whence they came 202cb93a386Sopenharmony_ci typedef void* ReleaseCtx; 203cb93a386Sopenharmony_ci typedef void (*ReleaseProc)(ReleaseCtx); 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci void setResourceRelease(sk_sp<GrRefCntedCallback> releaseHelper); 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci // Helpers to use for setting the layout of the VkImage 208cb93a386Sopenharmony_ci static VkPipelineStageFlags LayoutToPipelineSrcStageFlags(const VkImageLayout layout); 209cb93a386Sopenharmony_ci static VkAccessFlags LayoutToSrcAccessMask(const VkImageLayout layout); 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci size_t onGpuMemorySize() const override; 212cb93a386Sopenharmony_ci#if GR_TEST_UTILS 213cb93a386Sopenharmony_ci void setCurrentQueueFamilyToGraphicsQueue(GrVkGpu* gpu); 214cb93a386Sopenharmony_ci#endif 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci#ifdef SKIA_OHOS 217cb93a386Sopenharmony_ci SkBudgeted GetBudgeted() const { return fBudgeted; } 218cb93a386Sopenharmony_ci#endif 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ciprivate: 221cb93a386Sopenharmony_ci static sk_sp<GrVkImage> Make(GrVkGpu* gpu, 222cb93a386Sopenharmony_ci SkISize dimensions, 223cb93a386Sopenharmony_ci UsageFlags attachmentUsages, 224cb93a386Sopenharmony_ci int sampleCnt, 225cb93a386Sopenharmony_ci VkFormat format, 226cb93a386Sopenharmony_ci uint32_t mipLevels, 227cb93a386Sopenharmony_ci VkImageUsageFlags vkUsageFlags, 228cb93a386Sopenharmony_ci GrProtected isProtected, 229cb93a386Sopenharmony_ci GrMemoryless, 230cb93a386Sopenharmony_ci SkBudgeted); 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci GrVkImage(GrVkGpu* gpu, 233cb93a386Sopenharmony_ci SkISize dimensions, 234cb93a386Sopenharmony_ci UsageFlags supportedUsages, 235cb93a386Sopenharmony_ci const GrVkImageInfo&, 236cb93a386Sopenharmony_ci sk_sp<GrBackendSurfaceMutableStateImpl> mutableState, 237cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> framebufferView, 238cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> textureView, 239cb93a386Sopenharmony_ci SkBudgeted); 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci GrVkImage(GrVkGpu* gpu, 242cb93a386Sopenharmony_ci SkISize dimensions, 243cb93a386Sopenharmony_ci UsageFlags supportedUsages, 244cb93a386Sopenharmony_ci const GrVkImageInfo&, 245cb93a386Sopenharmony_ci sk_sp<GrBackendSurfaceMutableStateImpl> mutableState, 246cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> framebufferView, 247cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> textureView, 248cb93a386Sopenharmony_ci GrBackendObjectOwnership, 249cb93a386Sopenharmony_ci GrWrapCacheable, 250cb93a386Sopenharmony_ci bool forSecondaryCB); 251cb93a386Sopenharmony_ci 252cb93a386Sopenharmony_ci void init(GrVkGpu*, bool forSecondaryCB); 253cb93a386Sopenharmony_ci 254cb93a386Sopenharmony_ci void onRelease() override; 255cb93a386Sopenharmony_ci void onAbandon() override; 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci void releaseImage(); 258cb93a386Sopenharmony_ci bool hasResource() const { return fResource; } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci GrVkGpu* getVkGpu() const; 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci GrVkImageInfo fInfo; 263cb93a386Sopenharmony_ci uint32_t fInitialQueueFamily; 264cb93a386Sopenharmony_ci sk_sp<GrBackendSurfaceMutableStateImpl> fMutableState; 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> fFramebufferView; 267cb93a386Sopenharmony_ci sk_sp<const GrVkImageView> fTextureView; 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci#ifdef SKIA_OHOS 270cb93a386Sopenharmony_ci SkBudgeted fBudgeted = SkBudgeted::kNo; 271cb93a386Sopenharmony_ci#endif 272cb93a386Sopenharmony_ci bool fIsBorrowed; 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci // Descriptor set used when this is used as an input attachment for reading the dst in blending. 275cb93a386Sopenharmony_ci gr_rp<const GrVkDescriptorSet> fCachedBlendingInputDescSet; 276cb93a386Sopenharmony_ci // Descriptor set used when this is used as an input attachment for loading an msaa attachment. 277cb93a386Sopenharmony_ci gr_rp<const GrVkDescriptorSet> fCachedMSAALoadInputDescSet; 278cb93a386Sopenharmony_ci 279cb93a386Sopenharmony_ci class Resource : public GrTextureResource { 280cb93a386Sopenharmony_ci public: 281cb93a386Sopenharmony_ci explicit Resource(const GrVkGpu* gpu) 282cb93a386Sopenharmony_ci : fGpu(gpu) 283cb93a386Sopenharmony_ci , fImage(VK_NULL_HANDLE) { 284cb93a386Sopenharmony_ci fAlloc.fMemory = VK_NULL_HANDLE; 285cb93a386Sopenharmony_ci fAlloc.fOffset = 0; 286cb93a386Sopenharmony_ci } 287cb93a386Sopenharmony_ci 288cb93a386Sopenharmony_ci Resource(const GrVkGpu* gpu, VkImage image, const GrVkAlloc& alloc, VkImageTiling tiling) 289cb93a386Sopenharmony_ci : fGpu(gpu) 290cb93a386Sopenharmony_ci , fImage(image) 291cb93a386Sopenharmony_ci , fAlloc(alloc) {} 292cb93a386Sopenharmony_ci 293cb93a386Sopenharmony_ci ~Resource() override {} 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci#ifdef SK_TRACE_MANAGED_RESOURCES 296cb93a386Sopenharmony_ci void dumpInfo() const override { 297cb93a386Sopenharmony_ci SkDebugf("GrVkImage: %" PRIdPTR " (%d refs)\n", (intptr_t)fImage, this->getRefCnt()); 298cb93a386Sopenharmony_ci } 299cb93a386Sopenharmony_ci#endif 300cb93a386Sopenharmony_ci 301cb93a386Sopenharmony_ci#ifdef SK_DEBUG 302cb93a386Sopenharmony_ci const GrManagedResource* asVkImageResource() const override { return this; } 303cb93a386Sopenharmony_ci#endif 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci private: 306cb93a386Sopenharmony_ci void freeGPUData() const override; 307cb93a386Sopenharmony_ci 308cb93a386Sopenharmony_ci const GrVkGpu* fGpu; 309cb93a386Sopenharmony_ci VkImage fImage; 310cb93a386Sopenharmony_ci GrVkAlloc fAlloc; 311cb93a386Sopenharmony_ci 312cb93a386Sopenharmony_ci using INHERITED = GrTextureResource; 313cb93a386Sopenharmony_ci }; 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci // for wrapped textures 316cb93a386Sopenharmony_ci class BorrowedResource : public Resource { 317cb93a386Sopenharmony_ci public: 318cb93a386Sopenharmony_ci BorrowedResource(const GrVkGpu* gpu, VkImage image, const GrVkAlloc& alloc, 319cb93a386Sopenharmony_ci VkImageTiling tiling) 320cb93a386Sopenharmony_ci : Resource(gpu, image, alloc, tiling) { 321cb93a386Sopenharmony_ci } 322cb93a386Sopenharmony_ci private: 323cb93a386Sopenharmony_ci void freeGPUData() const override; 324cb93a386Sopenharmony_ci }; 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_ci Resource* fResource; 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci friend class GrVkRenderTarget; 329cb93a386Sopenharmony_ci}; 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci#endif 332