1 /*
2  * Copyright (c) 2024 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 "gpu_resource_cache.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 
21 #include <base/containers/fixed_string.h>
22 #include <base/containers/string.h>
23 #include <base/math/mathf.h>
24 #include <render/resource_handle.h>
25 
26 #include "device/gpu_resource_manager.h"
27 #include "util/log.h"
28 
29 RENDER_BEGIN_NAMESPACE()
30 using namespace BASE_NS;
31 
32 namespace {
CreateImage(GpuResourceManager & gpuResourceMgr,const CacheGpuImageDesc & desc)33 RenderHandleReference CreateImage(GpuResourceManager& gpuResourceMgr, const CacheGpuImageDesc& desc)
34 {
35     constexpr ImageUsageFlags USAGE_FLAGS { ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
36                                             ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT };
37     constexpr ImageUsageFlags MSAA_USAGE_FLAGS { ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
38                                                  ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT };
39     constexpr MemoryPropertyFlags MEMORY_FLAGS { MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT };
40     constexpr MemoryPropertyFlags MSAA_MEMORY_FLAGS {
41         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
42         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
43     };
44     const ImageUsageFlags usageFlags =
45         (desc.sampleCountFlags > SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT) ? MSAA_USAGE_FLAGS : USAGE_FLAGS;
46     const MemoryPropertyFlags memoryFlags =
47         (desc.sampleCountFlags > SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT) ? MSAA_MEMORY_FLAGS : MEMORY_FLAGS;
48 
49     const GpuImageDesc newDesc {
50         ImageType::CORE_IMAGE_TYPE_2D,          // imageType
51         ImageViewType::CORE_IMAGE_VIEW_TYPE_2D, // imageViewType
52         desc.format,                            // format
53         ImageTiling::CORE_IMAGE_TILING_OPTIMAL, // imageTiling
54         usageFlags,                             // usageFlags
55         memoryFlags,                            // memoryPropertyFlags
56         0,                                      // createFlags
57         EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS |
58             EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS, // engineCreationFlags
59         desc.width,                                                                               // width
60         desc.height,                                                                              // height
61         1,                                                                                        // depth
62         desc.mipCount,                                                                            // mipCount
63         desc.layerCount,                                                                          // layerCount
64         desc.sampleCountFlags,                                                                    // sampleCountFlags
65         desc.componentMapping,                                                                    // componentMapping
66     };
67 
68     return gpuResourceMgr.CreateShallowHandle(newDesc);
69 }
70 } // namespace
71 
GpuResourceCache(GpuResourceManager & gpuResourceMgr)72 GpuResourceCache::GpuResourceCache(GpuResourceManager& gpuResourceMgr) : gpuResourceMgr_(gpuResourceMgr) {}
73 
~GpuResourceCache()74 GpuResourceCache::~GpuResourceCache()
75 {
76 #if (RENDER_VALIDATION_ENABLED == 1)
77     {
78         const auto clientLock = std::lock_guard(mutex_);
79 
80         uint32_t aliveCounter = 0;
81         const uint32_t readIdx = 1u - writeIdx_;
82         for (const auto& imagesRef : frameData_[writeIdx_].images) {
83             if (imagesRef.handle && (imagesRef.handle.GetRefCount() > 2)) { // 2:count number
84                 aliveCounter++;
85             }
86         }
87         for (const auto& imagesRef : frameData_[readIdx].images) {
88             if (imagesRef.handle && (imagesRef.handle.GetRefCount() > 2)) { // 2: count number
89                 aliveCounter++;
90             }
91         }
92         if (aliveCounter > 0) {
93             PLUGIN_LOG_W("RENDER_VALIDATION: Not all GPU resource cache references released (count: %u)", aliveCounter);
94         }
95     };
96 #endif
97 }
98 
BeginFrame(const uint64_t frameCount)99 void GpuResourceCache::BeginFrame(const uint64_t frameCount)
100 {
101     const auto lock = std::lock_guard(mutex_);
102 
103     // NOTE: does not try to re-use handles
104     if (frameCount == frameCounter_) {
105         return;
106     }
107     frameCounter_ = frameCount;
108     writeIdx_ = 1u - writeIdx_;
109 
110     AllocateAndRemapImages();
111     DestroyOldImages();
112 }
113 
GetImageData() const114 array_view<const GpuResourceCache::ImageData> GpuResourceCache::GetImageData() const
115 {
116     const uint32_t readIdx = 1u - writeIdx_;
117     return frameData_[readIdx].images;
118 }
119 
ReserveGpuImageImpl(const CacheGpuImageDesc & desc)120 RenderHandleReference GpuResourceCache::ReserveGpuImageImpl(const CacheGpuImageDesc& desc)
121 {
122     const auto lock = std::lock_guard(mutex_);
123 
124     auto& fd = frameData_[writeIdx_];
125     fd.images.push_back({ CreateImage(gpuResourceMgr_, desc), HashCacheGpuImageDesc(desc) });
126     return fd.images.back().handle;
127 }
128 
ReserveGpuImage(const CacheGpuImageDesc & desc)129 RenderHandleReference GpuResourceCache::ReserveGpuImage(const CacheGpuImageDesc& desc)
130 {
131     return ReserveGpuImageImpl(desc);
132 }
133 
ReserveGpuImages(const array_view<const CacheGpuImageDesc> descs)134 vector<RenderHandleReference> GpuResourceCache::ReserveGpuImages(const array_view<const CacheGpuImageDesc> descs)
135 {
136     vector<RenderHandleReference> handles(descs.size());
137     for (size_t idx = 0; idx < handles.size(); ++idx) {
138         handles[idx] = ReserveGpuImageImpl(descs[idx]);
139     }
140     return handles;
141 }
142 
GetCacheGpuImageDesc(const RenderHandleReference & gpuImageHandle) const143 CacheGpuImageDesc GpuResourceCache::GetCacheGpuImageDesc(const RenderHandleReference& gpuImageHandle) const
144 {
145     const GpuImageDesc desc = gpuResourceMgr_.GetImageDescriptor(gpuImageHandle);
146     return { desc.format, desc.width, desc.height, desc.mipCount, desc.layerCount, desc.sampleCountFlags,
147         desc.componentMapping };
148 }
149 
ReserveGpuImagePair(const CacheGpuImageDesc & desc,const SampleCountFlags sampleCountFlags)150 CacheGpuImagePair GpuResourceCache::ReserveGpuImagePair(
151     const CacheGpuImageDesc& desc, const SampleCountFlags sampleCountFlags)
152 {
153     // allocate both handles
154     CacheGpuImageDesc secondDesc = desc;
155     secondDesc.sampleCountFlags = sampleCountFlags;
156     return CacheGpuImagePair { ReserveGpuImageImpl(desc), ReserveGpuImageImpl(secondDesc) };
157 }
158 
AllocateAndRemapImages()159 void GpuResourceCache::AllocateAndRemapImages()
160 {
161     const uint32_t readIdx = 1u - writeIdx_;
162     const auto& images = frameData_[readIdx].images;
163     for (const auto& ref : images) {
164         RenderHandle remapHandle;
165         for (auto& gpuRef : gpuBackedImages_) {
166             if ((gpuRef.hash == ref.hash) && (gpuRef.frameUseIndex != frameCounter_)) {
167                 remapHandle = gpuRef.handle.GetHandle();
168                 gpuRef.frameUseIndex = frameCounter_;
169                 break;
170             }
171         }
172         if (!RenderHandleUtil::IsValid(remapHandle)) {
173             GpuImageDesc desc = gpuResourceMgr_.GetImageDescriptor(ref.handle);
174             RenderHandleReference handle = gpuResourceMgr_.Create(desc);
175             remapHandle = handle.GetHandle();
176             gpuBackedImages_.push_back({ ref.hash, frameCounter_, move(handle) });
177         }
178         gpuResourceMgr_.RemapGpuImageHandle(ref.handle.GetHandle(), remapHandle);
179     }
180 }
181 
DestroyOldImages()182 void GpuResourceCache::DestroyOldImages()
183 {
184     // shallow handles
185     {
186         const uint32_t readIdx = 1u - writeIdx_;
187         auto& imagesRef = frameData_[readIdx].images;
188         const auto invalidHandles = std::partition(imagesRef.begin(), imagesRef.end(), [&](const ImageData& imgRef) {
189             if (imgRef.handle.GetRefCount() > 2) {
190                 return true;
191             } else {
192                 PLUGIN_ASSERT(imgRef.handle.GetRefCount() == 2);
193                 return false;
194             }
195         });
196         if (invalidHandles != imagesRef.end()) {
197             imagesRef.erase(invalidHandles, imagesRef.end());
198         }
199     }
200     // gpu backed resources
201     {
202         constexpr uint64_t minAge = 2;
203         const auto ageLimit = (frameCounter_ < minAge) ? 0 : (frameCounter_ - minAge);
204         const auto invalidHandles =
205             std::partition(gpuBackedImages_.begin(), gpuBackedImages_.end(), [&](const GpuBackedData& imgRef) {
206                 if (imgRef.frameUseIndex < ageLimit) {
207                     return false;
208                 } else {
209                     return true;
210                 }
211             });
212         if (invalidHandles != gpuBackedImages_.end()) {
213             gpuBackedImages_.erase(invalidHandles, gpuBackedImages_.end());
214         }
215     }
216 }
217 
HashCacheGpuImageDesc(const CacheGpuImageDesc & desc) const218 uint64_t GpuResourceCache::HashCacheGpuImageDesc(const CacheGpuImageDesc& desc) const
219 {
220     // pack: width, height, mipCount, layerCount to a single 64 bit "hash"
221     const uint32_t sizeHash = (desc.width << 16u) | desc.height;
222     const uint32_t mipLayerSwizzleHash = (desc.componentMapping.r << 28) | (desc.componentMapping.g << 24) |
223                                          (desc.componentMapping.b << 20) | (desc.componentMapping.a << 16) |
224                                          (desc.mipCount << 8u) | desc.layerCount;
225     const uint64_t formatSamplesHash =
226         ((static_cast<uint64_t>(desc.format)) << 32) | ((static_cast<uint64_t>(desc.sampleCountFlags)) & 0xFFFFffff);
227     uint64_t hash = (static_cast<uint64_t>(mipLayerSwizzleHash) << 32u) | sizeHash;
228     HashCombine(hash, formatSamplesHash);
229     return hash;
230 }
231 RENDER_END_NAMESPACE()
232