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