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 "render_bloom.h"
17
18 #include <base/containers/fixed_string.h>
19 #include <base/containers/unordered_map.h>
20 #include <base/math/vector.h>
21 #include <render/datastore/intf_render_data_store_manager.h>
22 #include <render/datastore/intf_render_data_store_pod.h>
23 #include <render/datastore/render_data_store_render_pods.h>
24 #include <render/device/intf_gpu_resource_manager.h>
25 #include <render/device/intf_shader_manager.h>
26 #include <render/namespace.h>
27 #include <render/nodecontext/intf_node_context_descriptor_set_manager.h>
28 #include <render/nodecontext/intf_node_context_pso_manager.h>
29 #include <render/nodecontext/intf_pipeline_descriptor_set_binder.h>
30 #include <render/nodecontext/intf_render_command_list.h>
31 #include <render/nodecontext/intf_render_node_context_manager.h>
32 #include <render/nodecontext/intf_render_node_util.h>
33
34 #include "default_engine_constants.h"
35 #include "util/log.h"
36
37 // shaders
38 #include <render/shaders/common/render_post_process_structs_common.h>
39
40 using namespace BASE_NS;
41
42 RENDER_BEGIN_NAMESPACE()
43 namespace {
44 constexpr DynamicStateEnum DYNAMIC_STATES[] = { CORE_DYNAMIC_STATE_ENUM_VIEWPORT, CORE_DYNAMIC_STATE_ENUM_SCISSOR };
45 }
46
Init(IRenderNodeContextManager & renderNodeContextMgr,const BloomInfo & bloomInfo)47 void RenderBloom::Init(IRenderNodeContextManager& renderNodeContextMgr, const BloomInfo& bloomInfo)
48 {
49 bloomInfo_ = bloomInfo;
50
51 // NOTE: target counts etc. should probably be resized based on configuration
52 CreatePsos(renderNodeContextMgr);
53
54 auto& gpuResourceMgr = renderNodeContextMgr.GetGpuResourceManager();
55 samplerHandle_ = gpuResourceMgr.Create(samplerHandle_,
56 GpuSamplerDesc {
57 Filter::CORE_FILTER_LINEAR, // magFilter
58 Filter::CORE_FILTER_LINEAR, // minFilter
59 Filter::CORE_FILTER_LINEAR, // mipMapMode
60 SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeU
61 SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeV
62 SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeW
63 });
64 }
65
PreExecute(IRenderNodeContextManager & renderNodeContextMgr,const BloomInfo & bloomInfo,const PostProcessConfiguration & ppConfig)66 void RenderBloom::PreExecute(IRenderNodeContextManager& renderNodeContextMgr, const BloomInfo& bloomInfo,
67 const PostProcessConfiguration& ppConfig)
68 {
69 bloomInfo_ = bloomInfo;
70
71 const GpuImageDesc& imgDesc =
72 renderNodeContextMgr.GetGpuResourceManager().GetImageDescriptor(bloomInfo_.input.handle);
73 uint32_t sizeDenom = 1u;
74 if (ppConfig.bloomConfiguration.bloomQualityType == BloomConfiguration::QUALITY_TYPE_LOW) {
75 sizeDenom = 2u;
76 }
77 CreateTargets(renderNodeContextMgr, Math::UVec2(imgDesc.width, imgDesc.height) / sizeDenom);
78 }
79
Execute(IRenderNodeContextManager & renderNodeContextMgr,IRenderCommandList & cmdList,const PostProcessConfiguration & ppConfig)80 void RenderBloom::Execute(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList,
81 const PostProcessConfiguration& ppConfig)
82 {
83 RENDER_DEBUG_MARKER_COL_SCOPE(cmdList, "RenderBloom", DefaultDebugConstants::CORE_DEFAULT_DEBUG_COLOR);
84
85 bloomEnabled_ = false;
86 BloomConfiguration bloomConfiguration;
87 if (ppConfig.enableFlags & PostProcessConfiguration::ENABLE_BLOOM_BIT) {
88 bloomConfiguration.thresholdHard = ppConfig.bloomConfiguration.thresholdHard;
89 bloomConfiguration.thresholdSoft = ppConfig.bloomConfiguration.thresholdSoft;
90 bloomConfiguration.amountCoefficient = ppConfig.bloomConfiguration.amountCoefficient;
91 bloomConfiguration.dirtMaskCoefficient = ppConfig.bloomConfiguration.dirtMaskCoefficient;
92 bloomConfiguration.scatter = ppConfig.bloomConfiguration.scatter;
93 bloomConfiguration.scaleFactor = ppConfig.bloomConfiguration.scaleFactor;
94
95 bloomEnabled_ = true;
96 }
97
98 const auto bloomQualityType = ppConfig.bloomConfiguration.bloomQualityType;
99 PLUGIN_ASSERT(bloomQualityType < CORE_BLOOM_QUALITY_COUNT);
100 if (bloomInfo_.useCompute) {
101 psos_.downscale = psos_.downscaleHandlesCompute[bloomQualityType].regular;
102 psos_.downscaleAndThreshold = psos_.downscaleHandlesCompute[bloomQualityType].threshold;
103 } else {
104 psos_.downscale = psos_.downscaleHandles[bloomQualityType].regular;
105 psos_.downscaleAndThreshold = psos_.downscaleHandles[bloomQualityType].threshold;
106 }
107
108 if (!bloomEnabled_) {
109 bloomConfiguration.amountCoefficient = 0.0f;
110 }
111
112 bloomParameters_ = Math::Vec4(
113 // .x = thresholdHard, luma values below this won't bloom
114 bloomConfiguration.thresholdHard,
115 // .y = thresholdSoft, luma values from this value to hard threshold will reduce bloom input from 1.0 -> 0.0
116 // i.e. this creates softer threshold for bloom
117 bloomConfiguration.thresholdSoft,
118 // .z = amountCoefficient, will multiply the colors from the bloom textures when combined with original color
119 // target
120 bloomConfiguration.amountCoefficient,
121 // .w = scatter value (in .w dirt mask coefficient is used in combine)
122 bloomConfiguration.scatter);
123 scaleFactor_ = Math::min(1.0f, bloomConfiguration.scaleFactor);
124
125 const bool validBinders = binders_.globalSet0.get() != nullptr;
126 if (validBinders) {
127 const float scaleFactor = Math::max(0.01f, scaleFactor_);
128 const auto fTexCount = static_cast<float>(targets_.tex1.size());
129 frameScaleMaxCount_ = static_cast<size_t>(Math::min(fTexCount, fTexCount * scaleFactor));
130 frameScaleMaxCount_ = Math::max(frameScaleMaxCount_, size_t(2)); // 2: count
131
132 if (bloomInfo_.useCompute) {
133 ComputeBloom(renderNodeContextMgr, cmdList);
134 } else {
135 GraphicsBloom(cmdList);
136 }
137 }
138 }
139
GetDescriptorCounts()140 DescriptorCounts RenderBloom::GetDescriptorCounts()
141 {
142 // NOTE: when added support for various bloom target counts, might need to be calculated for max
143 return DescriptorCounts { {
144 { CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 32u },
145 { CORE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 32u },
146 { CORE_DESCRIPTOR_TYPE_STORAGE_IMAGE, 32u },
147 { CORE_DESCRIPTOR_TYPE_SAMPLER, 24u },
148 { CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u },
149 } };
150 }
151
GetFinalTarget() const152 RenderHandle RenderBloom::GetFinalTarget() const
153 {
154 if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
155 return bloomInfo_.output.handle;
156 } else {
157 // output tex1 on compute and tex2 on graphics
158 return bloomInfo_.useCompute ? (targets_.tex1[0u].GetHandle()) : (targets_.tex2[0u].GetHandle());
159 }
160 }
161
UpdateGlobalSet(IRenderCommandList & cmdList)162 void RenderBloom::UpdateGlobalSet(IRenderCommandList& cmdList)
163 {
164 auto& binder = *binders_.globalSet0;
165 binder.ClearBindings();
166 uint32_t binding = 0u;
167 binder.BindBuffer(binding++, bloomInfo_.globalUbo, 0);
168 binder.BindBuffer(binding++, bloomInfo_.globalUbo, sizeof(GlobalPostProcessStruct));
169 cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
170 }
171
ComputeBloom(IRenderNodeContextManager & renderNodeContextMgr,IRenderCommandList & cmdList)172 void RenderBloom::ComputeBloom(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList)
173 {
174 constexpr PushConstant pc { ShaderStageFlagBits::CORE_SHADER_STAGE_COMPUTE_BIT,
175 sizeof(LocalPostProcessPushConstantStruct) };
176
177 UpdateGlobalSet(cmdList);
178 if (bloomEnabled_) {
179 ComputeDownscaleAndThreshold(pc, cmdList);
180 ComputeDownscale(pc, cmdList);
181 ComputeUpscale(pc, cmdList);
182 }
183 // needs to be done even when bloom is disabled if node is in use
184 if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
185 ComputeCombine(pc, cmdList);
186 }
187 }
188
ComputeDownscaleAndThreshold(const PushConstant & pc,IRenderCommandList & cmdList)189 void RenderBloom::ComputeDownscaleAndThreshold(const PushConstant& pc, IRenderCommandList& cmdList)
190 {
191 RenderHandle sets[2u] {};
192 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
193 {
194 auto& binder = *binders_.downscaleAndThreshold;
195 sets[1u] = binder.GetDescriptorSetHandle();
196 binder.ClearBindings();
197
198 uint32_t binding = 0;
199 binder.BindImage(binding++, { targets_.tex1[0].GetHandle() });
200 binder.BindImage(binding++, { bloomInfo_.input });
201 binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
202
203 cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
204 }
205
206 cmdList.BindPipeline(psos_.downscaleAndThreshold);
207 const ShaderThreadGroup tgs = psos_.downscaleAndThresholdTGS;
208
209 // bind all sets
210 cmdList.BindDescriptorSets(0, sets);
211
212 const auto targetSize = targets_.tex1Size[0];
213
214 LocalPostProcessPushConstantStruct uPc;
215 uPc.factor = bloomParameters_;
216 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
217 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
218
219 cmdList.PushConstantData(pc, arrayviewU8(uPc));
220
221 cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
222 }
223
ComputeDownscale(const PushConstant & pc,IRenderCommandList & cmdList)224 void RenderBloom::ComputeDownscale(const PushConstant& pc, IRenderCommandList& cmdList)
225 {
226 cmdList.BindPipeline(psos_.downscale);
227 const ShaderThreadGroup tgs = psos_.downscaleTGS;
228
229 RenderHandle sets[2u] {};
230 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
231 for (size_t i = 1; i < frameScaleMaxCount_; ++i) {
232 {
233 auto& binder = *binders_.downscale[i];
234 sets[1u] = binder.GetDescriptorSetHandle();
235 binder.ClearBindings();
236
237 uint32_t binding = 0;
238 binder.BindImage(binding++, { targets_.tex1[i].GetHandle() });
239 binder.BindImage(binding++, { targets_.tex1[i - 1].GetHandle() });
240 binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
241
242 cmdList.UpdateDescriptorSet(
243 binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
244 }
245 cmdList.BindDescriptorSets(0u, sets);
246
247 const auto targetSize = targets_.tex1Size[i];
248
249 LocalPostProcessPushConstantStruct uPc;
250 uPc.factor = bloomParameters_;
251 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
252 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
253 cmdList.PushConstantData(pc, arrayviewU8(uPc));
254
255 cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
256 }
257 }
258
ComputeUpscale(const PushConstant & pc,IRenderCommandList & cmdList)259 void RenderBloom::ComputeUpscale(const PushConstant& pc, IRenderCommandList& cmdList)
260 {
261 cmdList.BindPipeline(psos_.upscale);
262 const ShaderThreadGroup tgs = psos_.upscaleTGS;
263
264 RenderHandle sets[2u] {};
265 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
266
267 for (size_t i = frameScaleMaxCount_ - 1; i != 0; --i) {
268 {
269 auto& binder = *binders_.upscale[i];
270 sets[1u] = binder.GetDescriptorSetHandle();
271 binder.ClearBindings();
272
273 binder.BindImage(0u, { targets_.tex1[i - 1].GetHandle() });
274 binder.BindImage(1u, { targets_.tex1[i].GetHandle() });
275 binder.BindSampler(2u, { samplerHandle_.GetHandle() });
276
277 cmdList.UpdateDescriptorSet(
278 binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
279 }
280 cmdList.BindDescriptorSets(0u, sets);
281
282 const auto targetSize = targets_.tex1Size[i - 1];
283
284 LocalPostProcessPushConstantStruct uPc;
285 uPc.factor = bloomParameters_;
286 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
287 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
288 cmdList.PushConstantData(pc, arrayviewU8(uPc));
289
290 cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
291 }
292 }
293
ComputeCombine(const PushConstant & pc,IRenderCommandList & cmdList)294 void RenderBloom::ComputeCombine(const PushConstant& pc, IRenderCommandList& cmdList)
295 {
296 cmdList.BindPipeline(psos_.combine);
297 const ShaderThreadGroup tgs = psos_.combineTGS;
298
299 RenderHandle sets[2u] {};
300 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
301 {
302 auto& binder = *binders_.combine;
303 sets[1u] = binder.GetDescriptorSetHandle();
304 binder.ClearBindings();
305 // bind resources to set 1
306 uint32_t binding = 0;
307 binder.BindImage(binding++, { bloomInfo_.output });
308 binder.BindImage(binding++, { bloomInfo_.input });
309 binder.BindImage(binding++, { targets_.tex1[0].GetHandle() });
310 binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
311
312 // update the descriptor set bindings for set 1
313 cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
314 }
315
316 cmdList.BindDescriptorSets(0u, sets);
317
318 const auto targetSize = baseSize_;
319
320 LocalPostProcessPushConstantStruct uPc;
321 uPc.factor = bloomParameters_;
322 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
323 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
324 cmdList.PushConstantData(pc, arrayviewU8(uPc));
325
326 cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
327 }
328
GraphicsBloom(IRenderCommandList & cmdList)329 void RenderBloom::GraphicsBloom(IRenderCommandList& cmdList)
330 {
331 RenderPass renderPass;
332 renderPass.renderPassDesc.attachmentCount = 1;
333 renderPass.renderPassDesc.subpassCount = 1;
334 renderPass.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
335 renderPass.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
336
337 RenderPassSubpassDesc& subpassDesc = renderPass.subpassDesc;
338 subpassDesc.colorAttachmentCount = 1;
339 subpassDesc.colorAttachmentIndices[0] = 0;
340
341 constexpr PushConstant pc { ShaderStageFlagBits::CORE_SHADER_STAGE_FRAGMENT_BIT,
342 sizeof(LocalPostProcessPushConstantStruct) };
343
344 UpdateGlobalSet(cmdList);
345 if (bloomEnabled_) {
346 RenderDownscaleAndThreshold(renderPass, pc, cmdList);
347 RenderDownscale(renderPass, pc, cmdList);
348 RenderUpscale(renderPass, pc, cmdList);
349 }
350 // combine (needs to be done even when bloom is disabled if node is in use
351 if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
352 RenderCombine(renderPass, pc, cmdList);
353 }
354 }
355
RenderDownscaleAndThreshold(RenderPass & renderPass,const PushConstant & pc,IRenderCommandList & cmdList)356 void RenderBloom::RenderDownscaleAndThreshold(
357 RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
358 {
359 const auto targetSize = targets_.tex1Size[0];
360 const ViewportDesc viewportDesc { 0, 0, static_cast<float>(targetSize.x), static_cast<float>(targetSize.y) };
361 const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
362
363 renderPass.renderPassDesc.attachmentHandles[0] = targets_.tex1[0].GetHandle();
364 renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
365 cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
366
367 cmdList.SetDynamicStateViewport(viewportDesc);
368 cmdList.SetDynamicStateScissor(scissorDesc);
369 cmdList.BindPipeline(psos_.downscaleAndThreshold);
370
371 RenderHandle sets[2u] {};
372 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
373 {
374 auto& binder = *binders_.downscaleAndThreshold;
375 sets[1u] = binder.GetDescriptorSetHandle();
376 binder.ClearBindings();
377
378 binder.BindImage(0u, { bloomInfo_.input });
379 binder.BindSampler(1u, { samplerHandle_.GetHandle() });
380 cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
381 }
382 cmdList.BindDescriptorSets(0u, sets);
383
384 LocalPostProcessPushConstantStruct uPc;
385 uPc.factor = bloomParameters_;
386 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
387 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
388
389 cmdList.PushConstantData(pc, arrayviewU8(uPc));
390 cmdList.Draw(3u, 1u, 0u, 0u);
391 cmdList.EndRenderPass();
392 }
393
RenderDownscale(RenderPass & renderPass,const PushConstant & pc,IRenderCommandList & cmdList)394 void RenderBloom::RenderDownscale(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
395 {
396 LocalPostProcessPushConstantStruct uPc;
397 uPc.factor = bloomParameters_;
398
399 RenderHandle sets[2u] {};
400 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
401 for (size_t idx = 1; idx < frameScaleMaxCount_; ++idx) {
402 const auto targetSize = targets_.tex1Size[idx];
403 const ViewportDesc viewportDesc { 0, 0, static_cast<float>(targetSize.x), static_cast<float>(targetSize.y) };
404 const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
405
406 renderPass.renderPassDesc.attachmentHandles[0] = targets_.tex1[idx].GetHandle();
407 renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
408 cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
409
410 cmdList.SetDynamicStateViewport(viewportDesc);
411 cmdList.SetDynamicStateScissor(scissorDesc);
412
413 cmdList.BindPipeline(psos_.downscale);
414
415 {
416 auto& binder = *binders_.downscale[idx];
417 sets[1u] = binder.GetDescriptorSetHandle();
418 binder.ClearBindings();
419 binder.BindImage(0u, { targets_.tex1[idx - 1].GetHandle() });
420 binder.BindSampler(1u, { samplerHandle_.GetHandle() });
421 cmdList.UpdateDescriptorSet(
422 binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
423 }
424 cmdList.BindDescriptorSets(0u, sets);
425
426 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
427 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
428
429 cmdList.PushConstantData(pc, arrayviewU8(uPc));
430 cmdList.Draw(3u, 1u, 0u, 0u);
431 cmdList.EndRenderPass();
432 }
433 }
434
RenderUpscale(RenderPass & renderPass,const PushConstant & pc,IRenderCommandList & cmdList)435 void RenderBloom::RenderUpscale(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
436 {
437 RenderPass renderPassUpscale = renderPass;
438 renderPassUpscale.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
439 renderPassUpscale.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
440
441 RenderHandle sets[2u] {};
442 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
443 PLUGIN_ASSERT(targets_.tex1.size() == targets_.tex2.size());
444 RenderHandle input;
445 if (frameScaleMaxCount_ >= 1) {
446 input = targets_.tex1[frameScaleMaxCount_ - 1].GetHandle();
447 }
448 for (size_t idx = frameScaleMaxCount_ - 1; idx != 0; --idx) {
449 const auto targetSize = targets_.tex1Size[idx - 1];
450 const ViewportDesc viewportDesc { 0, 0, static_cast<float>(targetSize.x), static_cast<float>(targetSize.y) };
451 const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
452
453 // tex2 as output
454 renderPassUpscale.renderPassDesc.attachmentHandles[0] = targets_.tex2[idx - 1].GetHandle();
455 renderPassUpscale.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
456 cmdList.BeginRenderPass(renderPassUpscale.renderPassDesc, 0, renderPassUpscale.subpassDesc);
457
458 cmdList.SetDynamicStateViewport(viewportDesc);
459 cmdList.SetDynamicStateScissor(scissorDesc);
460
461 cmdList.BindPipeline(psos_.upscale);
462
463 {
464 auto& binder = *binders_.upscale[idx];
465 sets[1u] = binder.GetDescriptorSetHandle();
466 binder.ClearBindings();
467
468 uint32_t binding = 0;
469 binder.BindImage(binding++, { input });
470 binder.BindImage(binding++, { targets_.tex1[idx - 1].GetHandle() });
471 binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
472 cmdList.UpdateDescriptorSet(
473 binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
474 }
475 cmdList.BindDescriptorSets(0u, sets);
476 LocalPostProcessPushConstantStruct uPc;
477 uPc.factor = bloomParameters_;
478 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
479 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
480
481 cmdList.PushConstantData(pc, arrayviewU8(uPc));
482 cmdList.Draw(3u, 1u, 0u, 0u);
483 cmdList.EndRenderPass();
484
485 // next pass input
486 input = renderPassUpscale.renderPassDesc.attachmentHandles[0];
487 }
488 }
489
RenderCombine(RenderPass & renderPass,const PushConstant & pc,IRenderCommandList & cmdList)490 void RenderBloom::RenderCombine(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
491 {
492 const auto targetSize = baseSize_;
493
494 renderPass.renderPassDesc.attachmentHandles[0] = bloomInfo_.output.handle;
495 renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
496 cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
497
498 cmdList.SetDynamicStateViewport(baseViewportDesc_);
499 cmdList.SetDynamicStateScissor(baseScissorDesc_);
500
501 cmdList.BindPipeline(psos_.combine);
502
503 RenderHandle sets[2u] {};
504 sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
505 {
506 auto& binder = *binders_.combine;
507 sets[1u] = binder.GetDescriptorSetHandle();
508 binder.ClearBindings();
509
510 uint32_t binding = 0;
511 binder.BindImage(binding++, { bloomInfo_.input });
512 // tex2 handle has the final result
513 binder.BindImage(binding++, { targets_.tex2[0].GetHandle() });
514 binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
515
516 cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
517 }
518 cmdList.BindDescriptorSets(0u, sets);
519
520 LocalPostProcessPushConstantStruct uPc;
521 uPc.factor = bloomParameters_;
522 uPc.viewportSizeInvSize = Math::Vec4(static_cast<float>(targetSize.x), static_cast<float>(targetSize.y),
523 1.0f / static_cast<float>(targetSize.x), 1.0f / static_cast<float>(targetSize.y));
524
525 cmdList.PushConstantData(pc, arrayviewU8(uPc));
526 cmdList.Draw(3u, 1u, 0u, 0u);
527 cmdList.EndRenderPass();
528 }
529
CreateTargets(IRenderNodeContextManager & renderNodeContextMgr,const Math::UVec2 baseSize)530 void RenderBloom::CreateTargets(IRenderNodeContextManager& renderNodeContextMgr, const Math::UVec2 baseSize)
531 {
532 if (baseSize.x != baseSize_.x || baseSize.y != baseSize_.y) {
533 baseSize_ = baseSize;
534
535 format_ = Format::BASE_FORMAT_B10G11R11_UFLOAT_PACK32;
536 ImageUsageFlags usageFlags = CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT |
537 CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
538
539 if (bloomInfo_.useCompute) {
540 format_ = Format::BASE_FORMAT_R16G16B16A16_SFLOAT; // used due to GLES
541 usageFlags = CORE_IMAGE_USAGE_STORAGE_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT;
542 } else {
543 baseViewportDesc_ = { 0.0f, 0.0f, static_cast<float>(baseSize.x), static_cast<float>(baseSize.y), 0.0f,
544 1.0f };
545 baseScissorDesc_ = { 0, 0, baseSize.x, baseSize.y };
546 }
547
548 // create target image
549 const Math::UVec2 startTargetSize = baseSize_;
550 GpuImageDesc desc {
551 ImageType::CORE_IMAGE_TYPE_2D,
552 ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,
553 format_,
554 ImageTiling::CORE_IMAGE_TILING_OPTIMAL,
555 usageFlags,
556 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
557 0,
558 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS |
559 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS,
560 startTargetSize.x,
561 startTargetSize.y,
562 1u,
563 1u,
564 1u,
565 SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,
566 {},
567 };
568
569 auto& gpuResourceMgr = renderNodeContextMgr.GetGpuResourceManager();
570 #if (RENDER_VALIDATION_ENABLED == 1)
571 const string_view nodeName = renderNodeContextMgr.GetName();
572 #endif
573 for (size_t idx = 0; idx < targets_.tex1.size(); ++idx) {
574 // every bloom target is half the size of the original/ previous bloom target
575 desc.width /= 2u;
576 desc.height /= 2u;
577 desc.width = (desc.width >= 1u) ? desc.width : 1u;
578 desc.height = (desc.height >= 1u) ? desc.height : 1u;
579 targets_.tex1Size[idx] = Math::UVec2(desc.width, desc.height);
580 #if (RENDER_VALIDATION_ENABLED == 1)
581 const auto baseTargetName = nodeName + "_Bloom_" + to_string(idx);
582 targets_.tex1[idx] = gpuResourceMgr.Create(baseTargetName + "_A", desc);
583 if (!bloomInfo_.useCompute) {
584 targets_.tex2[idx] = gpuResourceMgr.Create(baseTargetName + "_B", desc);
585 }
586 #else
587 targets_.tex1[idx] = gpuResourceMgr.Create(targets_.tex1[idx], desc);
588 if (!bloomInfo_.useCompute) {
589 targets_.tex2[idx] = gpuResourceMgr.Create(targets_.tex2[idx], desc);
590 }
591 #endif
592 }
593 }
594 }
595
CreatePsos(IRenderNodeContextManager & renderNodeContextMgr)596 void RenderBloom::CreatePsos(IRenderNodeContextManager& renderNodeContextMgr)
597 {
598 if (bloomInfo_.useCompute) {
599 CreateComputePsos(renderNodeContextMgr);
600 } else {
601 CreateRenderPsos(renderNodeContextMgr);
602 }
603 }
604
CreateComputePsos(IRenderNodeContextManager & renderNodeContextMgr)605 void RenderBloom::CreateComputePsos(IRenderNodeContextManager& renderNodeContextMgr)
606 {
607 const auto& shaderMgr = renderNodeContextMgr.GetShaderManager();
608 INodeContextPsoManager& psoMgr = renderNodeContextMgr.GetPsoManager();
609 INodeContextDescriptorSetManager& dSetMgr = renderNodeContextMgr.GetDescriptorSetManager();
610
611 constexpr BASE_NS::pair<BloomConfiguration::BloomQualityType, uint32_t> configurations[] = {
612 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_LOW, RenderBloom::CORE_BLOOM_QUALITY_LOW },
613 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_NORMAL, RenderBloom::CORE_BLOOM_QUALITY_NORMAL },
614 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_HIGH, RenderBloom::CORE_BLOOM_QUALITY_HIGH }
615 };
616 for (const auto& configuration : configurations) {
617 {
618 auto shader = shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale.shader");
619 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
620 ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
621 const ShaderSpecializationConstantDataView specDataView {
622 { specializations.constants.data(), specializations.constants.size() },
623 { &configuration.second, 1u },
624 };
625
626 psos_.downscaleHandlesCompute[configuration.first].regular =
627 psoMgr.GetComputePsoHandle(shader, pl, specDataView);
628 }
629 {
630 auto shader = shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale_threshold.shader");
631 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
632
633 ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
634 const ShaderSpecializationConstantDataView specDataView {
635 { specializations.constants.data(), specializations.constants.size() },
636 { &configuration.second, 1u },
637 };
638 psos_.downscaleHandlesCompute[configuration.first].threshold =
639 psoMgr.GetComputePsoHandle(shader, pl, specDataView);
640 }
641 }
642
643 constexpr uint32_t globalSet = 0u;
644 constexpr uint32_t localSetIdx = 1u;
645 // the first one creates the global set as well
646 {
647 const RenderHandle shaderHandle =
648 shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale_threshold.shader");
649 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
650 psos_.downscaleAndThreshold = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
651 psos_.downscaleAndThresholdTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
652
653 const auto& gBinds = pl.descriptorSetLayouts[globalSet].bindings;
654 binders_.globalSet0 = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(gBinds), gBinds);
655
656 const auto& lBinds = pl.descriptorSetLayouts[localSetIdx].bindings;
657 binders_.downscaleAndThreshold = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(lBinds), lBinds);
658 }
659 {
660 const RenderHandle shaderHandle =
661 shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale.shader");
662 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
663 psos_.downscale = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
664 psos_.downscaleTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
665
666 PLUGIN_ASSERT(binders_.downscale.size() >= TARGET_COUNT);
667 const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
668 for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
669 binders_.downscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
670 }
671 }
672 {
673 const RenderHandle shaderHandle =
674 shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_upscale.shader");
675 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
676 psos_.upscale = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
677 psos_.upscaleTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
678
679 PLUGIN_ASSERT(binders_.upscale.size() >= TARGET_COUNT);
680 const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
681 for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
682 binders_.upscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
683 }
684 }
685 {
686 const RenderHandle shaderHandle =
687 shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_combine.shader");
688 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
689 psos_.combine = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
690 psos_.combineTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
691
692 const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
693 binders_.combine = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
694 }
695 }
696
CreateAndReflectRenderPso(IRenderNodeContextManager & renderNodeContextMgr,const string_view shader)697 std::pair<RenderHandle, const PipelineLayout&> RenderBloom::CreateAndReflectRenderPso(
698 IRenderNodeContextManager& renderNodeContextMgr, const string_view shader)
699 {
700 const auto& shaderMgr = renderNodeContextMgr.GetShaderManager();
701 const RenderHandle shaderHandle = shaderMgr.GetShaderHandle(shader.data());
702 const RenderHandle graphicsStateHandle = shaderMgr.GetGraphicsStateHandleByShaderHandle(shaderHandle);
703 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
704
705 auto& psoMgr = renderNodeContextMgr.GetPsoManager();
706 const RenderHandle pso = psoMgr.GetGraphicsPsoHandle(
707 shaderHandle, graphicsStateHandle, pl, {}, {}, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
708 return { pso, pl };
709 }
710
CreateRenderPsos(IRenderNodeContextManager & renderNodeContextMgr)711 void RenderBloom::CreateRenderPsos(IRenderNodeContextManager& renderNodeContextMgr)
712 {
713 RenderPass renderPass;
714 renderPass.renderPassDesc.attachmentCount = 1;
715 renderPass.renderPassDesc.attachmentHandles[0] = bloomInfo_.input.handle;
716 renderPass.renderPassDesc.renderArea = { 0, 0, baseSize_.x, baseSize_.y };
717 renderPass.renderPassDesc.subpassCount = 1;
718 renderPass.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
719 renderPass.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
720
721 RenderPassSubpassDesc subpassDesc = renderPass.subpassDesc;
722 subpassDesc.colorAttachmentCount = 1;
723 subpassDesc.colorAttachmentIndices[0] = 0;
724
725 constexpr BASE_NS::pair<BloomConfiguration::BloomQualityType, uint32_t> configurations[] = {
726 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_LOW, RenderBloom::CORE_BLOOM_QUALITY_LOW },
727 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_NORMAL, RenderBloom::CORE_BLOOM_QUALITY_NORMAL },
728 { BloomConfiguration::BloomQualityType::QUALITY_TYPE_HIGH, RenderBloom::CORE_BLOOM_QUALITY_HIGH }
729 };
730
731 const IRenderNodeShaderManager& shaderMgr = renderNodeContextMgr.GetShaderManager();
732 INodeContextPsoManager& psoMgr = renderNodeContextMgr.GetPsoManager();
733
734 for (const auto& configuration : configurations) {
735 {
736 auto shader = shaderMgr.GetShaderHandle("rendershaders://shader/bloom_downscale.shader");
737 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
738 ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
739 const ShaderSpecializationConstantDataView specDataView {
740 { specializations.constants.data(), specializations.constants.size() },
741 { &configuration.second, 1u },
742 };
743 const RenderHandle graphicsState = shaderMgr.GetGraphicsStateHandleByShaderHandle(shader);
744 psos_.downscaleHandles[configuration.first].regular = psoMgr.GetGraphicsPsoHandle(
745 shader, graphicsState, pl, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
746 }
747
748 {
749 auto shader = shaderMgr.GetShaderHandle("rendershaders://shader/bloom_downscale_threshold.shader");
750 const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
751 ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
752 const ShaderSpecializationConstantDataView specDataView {
753 { specializations.constants.data(), specializations.constants.size() },
754 { &configuration.second, 1u },
755 };
756 const RenderHandle graphicsState = shaderMgr.GetGraphicsStateHandleByShaderHandle(shader);
757 psos_.downscaleHandles[configuration.first].threshold = psoMgr.GetGraphicsPsoHandle(
758 shader, graphicsState, pl, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
759 }
760 }
761
762 INodeContextDescriptorSetManager& dSetMgr = renderNodeContextMgr.GetDescriptorSetManager();
763 constexpr uint32_t globalSet = 0u;
764 constexpr uint32_t localSet = 1u;
765 // the first one creates the global set as well
766 {
767 const auto [pso, pipelineLayout] =
768 CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_downscale_threshold.shader");
769 psos_.downscaleAndThreshold = pso;
770
771 const auto& gBinds = pipelineLayout.descriptorSetLayouts[globalSet].bindings;
772 binders_.globalSet0 = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(gBinds), gBinds);
773
774 const auto& lBinds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
775 binders_.downscaleAndThreshold = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(lBinds), lBinds);
776 }
777 {
778 const auto [pso, pipelineLayout] =
779 CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_downscale.shader");
780 psos_.downscale = pso;
781 const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
782 for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
783 binders_.downscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
784 }
785 }
786 {
787 const auto [pso, pipelineLayout] =
788 CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_upscale.shader");
789 psos_.upscale = pso;
790 const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
791 for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
792 binders_.upscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
793 }
794 }
795 {
796 const auto [pso, pipelineLayout] =
797 CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_combine.shader");
798 psos_.combine = pso;
799 const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
800 binders_.combine = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
801 }
802 }
803 RENDER_END_NAMESPACE()
804