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