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_blur_node.h"
17
18 #include <base/math/mathf.h>
19 #include <core/property/property_handle_util.h>
20 #include <core/property/property_types.h>
21 #include <core/property_tools/property_api_impl.inl>
22 #include <render/device/intf_gpu_resource_manager.h>
23 #include <render/nodecontext/intf_node_context_descriptor_set_manager.h>
24 #include <render/nodecontext/intf_node_context_pso_manager.h>
25 #include <render/nodecontext/intf_render_command_list.h>
26 #include <render/nodecontext/intf_render_node_context_manager.h>
27 #include <render/nodecontext/intf_render_node_parser_util.h>
28 #include <render/nodecontext/intf_render_node_util.h>
29 #include <render/property/property_types.h>
30
31 #include "default_engine_constants.h"
32 #include "device/gpu_resource_handle_util.h"
33 #include "render/shaders/common/render_post_process_structs_common.h"
34 #include "render_post_process_blur.h"
35 #include "util/log.h"
36
37 using namespace BASE_NS;
38 using namespace CORE_NS;
39 using namespace RENDER_NS;
40
41 CORE_BEGIN_NAMESPACE()
42 DATA_TYPE_METADATA(RenderPostProcessBlurNode::NodeInputs, MEMBER_PROPERTY(input, "input", 0))
43 DATA_TYPE_METADATA(RenderPostProcessBlurNode::NodeOutputs, MEMBER_PROPERTY(output, "output", 0))
44 CORE_END_NAMESPACE()
45
46 RENDER_BEGIN_NAMESPACE()
47 namespace {
48 constexpr DynamicStateEnum DYNAMIC_STATES[] = { CORE_DYNAMIC_STATE_ENUM_VIEWPORT, CORE_DYNAMIC_STATE_ENUM_SCISSOR };
49
50 constexpr string_view SHADER_NAME { "rendershaders://shader/fullscreen_blur.shader" };
51 constexpr uint32_t MAX_MIP_COUNT { 16U };
52 constexpr uint32_t MAX_PASS_PER_LEVEL_COUNT { 3U };
53 constexpr uint32_t MAX_BINDER_COUNT = MAX_MIP_COUNT * MAX_PASS_PER_LEVEL_COUNT;
54
GetImageRenderArea(const IRenderNodeGpuResourceManager & gpuResourceMgr,const RenderHandle handle)55 RenderPassDesc::RenderArea GetImageRenderArea(
56 const IRenderNodeGpuResourceManager& gpuResourceMgr, const RenderHandle handle)
57 {
58 const GpuImageDesc desc = gpuResourceMgr.GetImageDescriptor(handle);
59 return { 0U, 0U, desc.width, desc.height };
60 }
61 } // namespace
62
RenderPostProcessBlurNode()63 RenderPostProcessBlurNode::RenderPostProcessBlurNode()
64 : inputProperties_(
65 &nodeInputsData, array_view(PropertyType::DataType<RenderPostProcessBlurNode::NodeInputs>::properties)),
66 outputProperties_(
67 &nodeOutputsData, array_view(PropertyType::DataType<RenderPostProcessBlurNode::NodeOutputs>::properties))
68
69 {}
70
GetRenderInputProperties()71 IPropertyHandle* RenderPostProcessBlurNode::GetRenderInputProperties()
72 {
73 return inputProperties_.GetData();
74 }
75
GetRenderOutputProperties()76 IPropertyHandle* RenderPostProcessBlurNode::GetRenderOutputProperties()
77 {
78 return outputProperties_.GetData();
79 }
80
GetRenderDescriptorCounts() const81 DescriptorCounts RenderPostProcessBlurNode::GetRenderDescriptorCounts() const
82 {
83 return descriptorCounts_;
84 }
85
SetRenderAreaRequest(const RenderAreaRequest & renderAreaRequest)86 void RenderPostProcessBlurNode::SetRenderAreaRequest(const RenderAreaRequest& renderAreaRequest)
87 {
88 useRequestedRenderArea_ = true;
89 renderAreaRequest_ = renderAreaRequest;
90 }
91
Init(const IRenderPostProcess::Ptr & postProcess,IRenderNodeContextManager & renderNodeContextMgr)92 void RenderPostProcessBlurNode::Init(
93 const IRenderPostProcess::Ptr& postProcess, IRenderNodeContextManager& renderNodeContextMgr)
94 {
95 // clear
96 pipelineData_ = {};
97 binders_.clear();
98
99 renderNodeContextMgr_ = &renderNodeContextMgr;
100 postProcess_ = postProcess;
101
102 // default inputs
103 IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
104 defaultInput_.handle = gpuResourceMgr.GetImageHandle(DefaultEngineGpuResourceConstants::CORE_DEFAULT_GPU_IMAGE);
105 defaultInput_.samplerHandle =
106 gpuResourceMgr.GetSamplerHandle(DefaultEngineGpuResourceConstants::CORE_DEFAULT_SAMPLER_LINEAR_CLAMP);
107
108 // load shaders
109 const IRenderNodeShaderManager& shaderMgr = renderNodeContextMgr_->GetShaderManager();
110 pipelineData_.gsd = shaderMgr.GetGraphicsShaderDataByShaderHandle(shaderMgr.GetShaderHandle(SHADER_NAME));
111
112 // get needed descriptor set counts
113 const IRenderNodeUtil& renderNodeUtil = renderNodeContextMgr_->GetRenderNodeUtil();
114 descriptorCounts_ = renderNodeUtil.GetDescriptorCounts(pipelineData_.gsd.pipelineLayoutData);
115 for (auto& ref : descriptorCounts_.counts) {
116 ref.count *= MAX_BINDER_COUNT;
117 }
118
119 valid_ = true;
120 }
121
PreExecute()122 void RenderPostProcessBlurNode::PreExecute()
123 {
124 if (valid_ && postProcess_) {
125 const array_view<const uint8_t> propertyView = postProcess_->GetData();
126 // this node is directly dependant
127 PLUGIN_ASSERT(propertyView.size_bytes() == sizeof(RenderPostProcessBlurNode::EffectProperties));
128 if (propertyView.size_bytes() == sizeof(RenderPostProcessBlurNode::EffectProperties)) {
129 effectProperties_ = (const RenderPostProcessBlurNode::EffectProperties&)(*propertyView.data());
130 }
131 } else {
132 effectProperties_.enabled = false;
133 }
134
135 if (effectProperties_.enabled) {
136 // check input and output
137 const bool valid = EvaluateInOut();
138 if (valid) {
139 EvaluateTemporaryTargets();
140 mipsBlur = (nodeInputsData.input.handle == nodeOutputsData.output.handle) ||
141 ((effectProperties_.blurConfiguration.maxMipLevel != 0U) &&
142 (effectProperties_.blurConfiguration.filterSize == 1.0f));
143 }
144 valid_ = valid && effectProperties_.enabled;
145 }
146 }
147
GetExecuteFlags() const148 IRenderNode::ExecuteFlags RenderPostProcessBlurNode::GetExecuteFlags() const
149 {
150 if (effectProperties_.enabled) {
151 return 0;
152 } else {
153 return IRenderNode::ExecuteFlagBits::EXECUTE_FLAG_BITS_DO_NOT_EXECUTE;
154 }
155 }
156
Execute(IRenderCommandList & cmdList)157 void RenderPostProcessBlurNode::Execute(IRenderCommandList& cmdList)
158 {
159 PLUGIN_ASSERT(effectProperties_.enabled);
160 if (!valid_) {
161 return;
162 }
163
164 CheckDescriptorSetNeed();
165
166 RENDER_DEBUG_MARKER_COL_SCOPE(cmdList, "RenderBlur", DefaultDebugConstants::CORE_DEFAULT_DEBUG_COLOR);
167
168 if (binders_.empty()) {
169 return;
170 }
171
172 BindableImage currOutput = nodeOutputsData.output;
173 if (!RenderHandleUtil::IsValid(currOutput.handle)) {
174 return;
175 }
176 // get the property values
177
178 // update the output
179 nodeOutputsData.output = currOutput;
180
181 const IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
182 const RenderPassDesc::RenderArea imageArea = GetImageRenderArea(gpuResourceMgr, currOutput.handle);
183 const RenderPassDesc::RenderArea renderArea = useRequestedRenderArea_ ? renderAreaRequest_.area : imageArea;
184 if ((renderArea.extentWidth == 0) || (renderArea.extentHeight == 0)) {
185 return;
186 }
187
188 RenderPass renderPass;
189 renderPass.renderPassDesc.attachmentCount = 1;
190 renderPass.renderPassDesc.renderArea = { 0, 0, imageArea.extentWidth, imageArea.extentHeight };
191 renderPass.renderPassDesc.subpassCount = 1;
192 renderPass.renderPassDesc.attachments[0].loadOp = CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
193 renderPass.renderPassDesc.attachments[0].storeOp = CORE_ATTACHMENT_STORE_OP_STORE;
194 renderPass.renderPassDesc.attachmentHandles[0] = currOutput.handle;
195 renderPass.subpassStartIndex = 0;
196 auto& subpass = renderPass.subpassDesc;
197 subpass.colorAttachmentCount = 1;
198 subpass.colorAttachmentIndices[0] = 0;
199
200 if (!RenderHandleUtil::IsValid(pipelineData_.psoScale)) {
201 const auto& shaderMgr = renderNodeContextMgr_->GetShaderManager();
202 auto& psoMgr = renderNodeContextMgr_->GetPsoManager();
203 const ShaderSpecializationConstantView sscv = shaderMgr.GetReflectionSpecialization(pipelineData_.gsd.shader);
204 {
205 const uint32_t specializationFlags[] = { effectProperties_.blurShaderScaleType };
206 const ShaderSpecializationConstantDataView specDataView { sscv.constants, specializationFlags };
207 pipelineData_.psoScale = psoMgr.GetGraphicsPsoHandle(
208 pipelineData_.gsd, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
209 }
210 {
211 const uint32_t specializationFlags[] = { effectProperties_.blurShaderType };
212 const ShaderSpecializationConstantDataView specDataView { sscv.constants, specializationFlags };
213 pipelineData_.psoBlur = psoMgr.GetGraphicsPsoHandle(
214 pipelineData_.gsd, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
215 }
216 }
217 if (mipsBlur) {
218 if (effectProperties_.blurConfiguration.blurType == BlurConfiguration::BlurType::TYPE_NORMAL) {
219 RenderGaussian(cmdList, renderPass);
220 } else {
221 RenderDirectionalBlur(cmdList, renderPass);
222 }
223 } else {
224 RenderData(cmdList, renderPass);
225 }
226 }
227
228 // constants for RenderBlur::RenderData
229 namespace {
230 constexpr bool USE_CUSTOM_BARRIERS = true;
231
232 constexpr ImageResourceBarrier SRC_UNDEFINED { 0, CORE_PIPELINE_STAGE_TOP_OF_PIPE_BIT, CORE_IMAGE_LAYOUT_UNDEFINED };
233 constexpr ImageResourceBarrier COL_ATTACHMENT { CORE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
234 CORE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, CORE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
235 constexpr ImageResourceBarrier SHDR_READ { CORE_ACCESS_SHADER_READ_BIT, CORE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
236 CORE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
237 // transition the final mip level to read only as well
238 constexpr ImageResourceBarrier FINAL_SRC { CORE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
239 CORE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | CORE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
240 CORE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
241 constexpr ImageResourceBarrier FINAL_DST { CORE_ACCESS_SHADER_READ_BIT,
242 CORE_PIPELINE_STAGE_VERTEX_SHADER_BIT, // first possible shader read stage
243 CORE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
244 } // namespace
245
246 namespace {
DownscaleBarrier(IRenderCommandList & cmdList,const RenderHandle image,const uint32_t mipLevel)247 void DownscaleBarrier(IRenderCommandList& cmdList, const RenderHandle image, const uint32_t mipLevel)
248 {
249 ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
250 PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
251 imgRange.baseMipLevel = mipLevel;
252 cmdList.CustomImageBarrier(image, SRC_UNDEFINED, COL_ATTACHMENT, imgRange);
253 const uint32_t inputMipLevel = mipLevel - 1u;
254 imgRange.baseMipLevel = inputMipLevel;
255 if (inputMipLevel == 0) {
256 cmdList.CustomImageBarrier(image, SHDR_READ, imgRange);
257 } else {
258 cmdList.CustomImageBarrier(image, COL_ATTACHMENT, SHDR_READ, imgRange);
259 }
260 cmdList.AddCustomBarrierPoint();
261 }
262
BlurHorizontalBarrier(IRenderCommandList & cmdList,const RenderHandle realImage,const uint32_t mipLevel,const RenderHandle tmpImage)263 void BlurHorizontalBarrier(
264 IRenderCommandList& cmdList, const RenderHandle realImage, const uint32_t mipLevel, const RenderHandle tmpImage)
265 {
266 ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
267 PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
268 imgRange.baseMipLevel = mipLevel;
269 cmdList.CustomImageBarrier(realImage, COL_ATTACHMENT, SHDR_READ, imgRange);
270 imgRange.baseMipLevel = mipLevel - 1;
271 cmdList.CustomImageBarrier(tmpImage, SRC_UNDEFINED, COL_ATTACHMENT, imgRange);
272 cmdList.AddCustomBarrierPoint();
273 }
274
BlurVerticalBarrier(IRenderCommandList & cmdList,const RenderHandle realImage,const uint32_t mipLevel,const RenderHandle tmpImage)275 void BlurVerticalBarrier(
276 IRenderCommandList& cmdList, const RenderHandle realImage, const uint32_t mipLevel, const RenderHandle tmpImage)
277 {
278 ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
279 PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
280 imgRange.baseMipLevel = mipLevel;
281 cmdList.CustomImageBarrier(realImage, SHDR_READ, COL_ATTACHMENT, imgRange);
282 imgRange.baseMipLevel = mipLevel - 1;
283 cmdList.CustomImageBarrier(tmpImage, COL_ATTACHMENT, SHDR_READ, imgRange);
284 cmdList.AddCustomBarrierPoint();
285 }
286
287 struct ConstDrawInput {
288 IRenderCommandList& cmdList;
289 const RenderPass& renderPass;
290 const PushConstant& pushConstant;
291 const LocalPostProcessPushConstantStruct& pc;
292 RenderHandle sampler;
293 };
BlurPass(const ConstDrawInput & di,IDescriptorSetBinder & binder,const RenderHandle psoHandle,const RenderHandle image,const uint32_t inputMipLevel)294 void BlurPass(const ConstDrawInput& di, IDescriptorSetBinder& binder, const RenderHandle psoHandle,
295 const RenderHandle image, const uint32_t inputMipLevel)
296 {
297 di.cmdList.BeginRenderPass(
298 di.renderPass.renderPassDesc, di.renderPass.subpassStartIndex, di.renderPass.subpassDesc);
299 di.cmdList.BindPipeline(psoHandle);
300
301 binder.ClearBindings();
302 binder.BindSampler(0, di.sampler);
303 binder.BindImage(1u, { image, inputMipLevel });
304 di.cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
305 di.cmdList.BindDescriptorSet(0, binder.GetDescriptorSetHandle());
306
307 di.cmdList.PushConstant(di.pushConstant, reinterpret_cast<const uint8_t*>(&di.pc));
308 di.cmdList.Draw(3u, 1u, 0u, 0u);
309 di.cmdList.EndRenderPass();
310 }
311 } // namespace
312
RenderDirectionalBlur(IRenderCommandList & cmdList,const RenderPass & renderPassBase)313 void RenderPostProcessBlurNode::RenderDirectionalBlur(IRenderCommandList& cmdList, const RenderPass& renderPassBase)
314 {
315 RenderPass renderPass = renderPassBase;
316 if constexpr (USE_CUSTOM_BARRIERS) {
317 cmdList.BeginDisableAutomaticBarrierPoints();
318 }
319 const vec4 factor = (effectProperties_.blurConfiguration.blurType == BlurConfiguration::BlurType::TYPE_HORIZONTAL)
320 ? vec4 { 1.0f, 0.0f, 0.0f, 0.0f }
321 : vec4 { 0.0f, 1.0f, 0.0f, 0.0f };
322
323 // with every mip, first we do a downscale
324 // then a single horizontal/vertical blur
325 LocalPostProcessPushConstantStruct pc { { 1.0f, 0.0f, 0.0f, 0.0f }, factor };
326 uint32_t descIdx = 0U;
327 const uint32_t blurCount = Math::min(effectProperties_.blurConfiguration.maxMipLevel, inputImgData_.mipCount);
328 const ConstDrawInput di { cmdList, renderPass, pipelineData_.gsd.pipelineLayoutData.pushConstant, pc,
329 defaultInput_.samplerHandle };
330 ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
331 PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
332
333 const RenderHandle handle0 = inputImgData_.rawHandle;
334 const RenderHandle handle1 = tmpImgTargets_.rawHandle;
335 for (uint32_t idx = 1U; idx < blurCount; ++idx) {
336 const uint32_t mip = idx;
337
338 const Math::UVec2 size = { Math::max(1u, inputImgData_.size.x >> mip),
339 Math::max(1u, inputImgData_.size.y >> mip) };
340 const Math::Vec2 fSize = { static_cast<float>(size.x), static_cast<float>(size.y) };
341 const Math::Vec4 texSizeInvTexSize { fSize.x * 1.0f, fSize.y * 1.0f, 1.0f / fSize.x, 1.0f / fSize.y };
342 pc = { texSizeInvTexSize, factor };
343
344 renderPass.renderPassDesc.renderArea = { 0, 0, size.x, size.y };
345 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
346 renderPass.renderPassDesc.attachments[0].mipLevel = mip;
347
348 cmdList.SetDynamicStateViewport({ 0.0f, 0.0f, fSize.x, fSize.y, 0.0f, 1.0f });
349 cmdList.SetDynamicStateScissor({ 0, 0, size.x, size.y });
350
351 // downscale
352 if constexpr (USE_CUSTOM_BARRIERS) {
353 DownscaleBarrier(cmdList, handle0, mip);
354 }
355 BlurPass(di, *binders_[descIdx++], pipelineData_.psoScale, handle0, mip - 1u);
356
357 if constexpr (USE_CUSTOM_BARRIERS) {
358 BlurHorizontalBarrier(cmdList, handle0, mip, handle1);
359 }
360
361 // Apply horizontal/vertical blur on main image and store in temp
362 renderPass.renderPassDesc.attachmentHandles[0] = handle1;
363 renderPass.renderPassDesc.attachments[0].mipLevel = mip - 1u;
364 BlurPass(di, *binders_[descIdx++], pipelineData_.psoBlur, handle0, mip);
365
366 if constexpr (USE_CUSTOM_BARRIERS) {
367 BlurVerticalBarrier(cmdList, handle0, mip, handle1);
368 }
369
370 // copy temp (mip-1) to main (mip)
371 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
372 renderPass.renderPassDesc.attachments[0].mipLevel = mip;
373 BlurPass(di, *binders_[descIdx++], pipelineData_.psoScale, handle1, mip - 1u);
374 }
375
376 if constexpr (USE_CUSTOM_BARRIERS) {
377 if (inputImgData_.mipCount > 1U) {
378 // transition the final used mip level
379 if (blurCount > 0) {
380 imgRange.baseMipLevel = blurCount - 1;
381 imgRange.levelCount = 1;
382 cmdList.CustomImageBarrier(handle0, FINAL_SRC, FINAL_DST, imgRange);
383 }
384 if (blurCount < inputImgData_.mipCount) {
385 // transition the final levels which might be in undefined state
386 imgRange.baseMipLevel = blurCount;
387 imgRange.levelCount = inputImgData_.mipCount - blurCount;
388 cmdList.CustomImageBarrier(handle0, SRC_UNDEFINED, FINAL_DST, imgRange);
389 }
390 }
391 cmdList.AddCustomBarrierPoint();
392 cmdList.EndDisableAutomaticBarrierPoints();
393 }
394 }
395
RenderGaussian(IRenderCommandList & cmdList,const RenderPass & renderPassBase)396 void RenderPostProcessBlurNode::RenderGaussian(IRenderCommandList& cmdList, const RenderPass& renderPassBase)
397 {
398 RenderPass renderPass = renderPassBase;
399 if constexpr (USE_CUSTOM_BARRIERS) {
400 cmdList.BeginDisableAutomaticBarrierPoints();
401 }
402
403 // with every mip, first we do a downscale
404 // then a single horizontal and a single vertical blur
405 LocalPostProcessPushConstantStruct pc { { 1.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f } };
406 uint32_t descIdx = 0U;
407 const uint32_t blurCount = Math::min(effectProperties_.blurConfiguration.maxMipLevel, inputImgData_.mipCount);
408 const ConstDrawInput di { cmdList, renderPass, pipelineData_.gsd.pipelineLayoutData.pushConstant, pc,
409 defaultInput_.samplerHandle };
410 const RenderHandle handle0 = inputImgData_.rawHandle;
411 const RenderHandle handle1 = tmpImgTargets_.rawHandle;
412 for (uint32_t idx = 1U; idx < blurCount; ++idx) {
413 const uint32_t mip = idx;
414
415 const Math::UVec2 size = { Math::max(1u, inputImgData_.size.x >> mip),
416 Math::max(1u, inputImgData_.size.y >> mip) };
417 const Math::Vec2 fSize = { static_cast<float>(size.x), static_cast<float>(size.y) };
418 const Math::Vec4 texSizeInvTexSize { fSize.x * 1.0f, fSize.y * 1.0f, 1.0f / fSize.x, 1.0f / fSize.y };
419 pc = { texSizeInvTexSize, { 1.0f, 0.0f, 0.0f, 0.0f } };
420
421 renderPass.renderPassDesc.renderArea = { 0, 0, size.x, size.y };
422 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
423 renderPass.renderPassDesc.attachments[0].mipLevel = mip;
424
425 cmdList.SetDynamicStateViewport({ 0.0f, 0.0f, fSize.x, fSize.y, 0.0f, 1.0f });
426 cmdList.SetDynamicStateScissor({ 0, 0, size.x, size.y });
427
428 // downscale
429 if constexpr (USE_CUSTOM_BARRIERS) {
430 DownscaleBarrier(cmdList, handle0, mip);
431 }
432 BlurPass(di, *binders_[descIdx++], pipelineData_.psoScale, handle0, mip - 1u);
433
434 // horizontal (from real image to temp)
435 if constexpr (USE_CUSTOM_BARRIERS) {
436 BlurHorizontalBarrier(cmdList, handle0, mip, handle1);
437 }
438
439 renderPass.renderPassDesc.attachmentHandles[0] = handle1;
440 renderPass.renderPassDesc.attachments[0].mipLevel = mip - 1u;
441 BlurPass(di, *binders_[descIdx++], pipelineData_.psoBlur, handle0, mip);
442
443 // vertical
444 if constexpr (USE_CUSTOM_BARRIERS) {
445 BlurVerticalBarrier(cmdList, handle0, mip, handle1);
446 }
447
448 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
449 renderPass.renderPassDesc.attachments[0].mipLevel = mip;
450 pc.factor = { 0.0f, 1.0f, 0.0f, 0.0f };
451 BlurPass(di, *binders_[descIdx++], pipelineData_.psoBlur, handle1, mip - 1);
452 }
453
454 if constexpr (USE_CUSTOM_BARRIERS) {
455 if (inputImgData_.mipCount > 1u) {
456 // transition the final used mip level
457 if (blurCount > 0) {
458 const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount - 1, 1, 0,
459 PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
460 cmdList.CustomImageBarrier(handle0, FINAL_SRC, FINAL_DST, imgRange);
461 }
462 if (blurCount < inputImgData_.mipCount) {
463 // transition the final levels which might be in undefined state
464 const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount,
465 inputImgData_.mipCount - blurCount, 0, PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
466 cmdList.CustomImageBarrier(handle0, SRC_UNDEFINED, FINAL_DST, imgRange);
467 }
468 }
469 cmdList.AddCustomBarrierPoint();
470 cmdList.EndDisableAutomaticBarrierPoints();
471 }
472 }
473
RenderData(IRenderCommandList & cmdList,const RenderPass & renderPassBase)474 void RenderPostProcessBlurNode::RenderData(IRenderCommandList& cmdList, const RenderPass& renderPassBase)
475 {
476 if ((!RenderHandleUtil::IsValid(tmpImgTargets_.rawHandle)) ||
477 (!RenderHandleUtil::IsValid(tmpImgTargets_.rawHandleExt))) {
478 return;
479 }
480
481 RenderPass renderPass = renderPassBase;
482
483 // with every mip, first we do a downscale
484 // then a single horizontal and a single vertical blur
485 LocalPostProcessPushConstantStruct pc { { 1.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f } };
486 uint32_t descIdx = 0U;
487 const uint32_t blurCount =
488 Math::min(static_cast<uint32_t>(effectProperties_.blurConfiguration.filterSize), MAX_MIP_COUNT);
489 const ConstDrawInput di { cmdList, renderPass, pipelineData_.gsd.pipelineLayoutData.pushConstant, pc,
490 defaultInput_.samplerHandle };
491 const RenderHandle inputHandle = inputImgData_.rawHandle;
492 const RenderHandle handle0 = tmpImgTargets_.rawHandle;
493 const RenderHandle handle1 = tmpImgTargets_.rawHandleExt;
494 // first downscale -> then ping pong
495 {
496 const Math::UVec2 size = { inputImgData_.size.x / 2U, inputImgData_.size.y / 2U };
497 const Math::Vec2 fSize = { static_cast<float>(size.x), static_cast<float>(size.y) };
498 const Math::Vec4 texSizeInvTexSize { fSize.x * 1.0f, fSize.y * 1.0f, 1.0f / fSize.x, 1.0f / fSize.y };
499 pc = { texSizeInvTexSize, { 1.0f, 0.0f, 0.0f, 0.0f } };
500
501 renderPass.renderPassDesc.renderArea = { 0, 0, size.x, size.y };
502
503 cmdList.SetDynamicStateViewport({ 0.0f, 0.0f, fSize.x, fSize.y, 0.0f, 1.0f });
504 cmdList.SetDynamicStateScissor({ 0, 0, size.x, size.y });
505
506 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
507 BlurPass(di, *binders_[descIdx++], pipelineData_.psoScale, inputHandle, 0U);
508 }
509
510 // output will be handle0
511 const Math::UVec2 size = { inputImgData_.size.x / 2U, inputImgData_.size.y / 2U };
512 const Math::Vec2 fSize = { static_cast<float>(size.x), static_cast<float>(size.y) };
513 const Math::Vec4 texSizeInvTexSize { fSize.x * 1.0f, fSize.y * 1.0f, 1.0f / fSize.x, 1.0f / fSize.y };
514 pc = { texSizeInvTexSize, { 1.0f, 0.0f, 0.0f, 0.0f } };
515 renderPass.renderPassDesc.renderArea = { 0, 0, size.x, size.y };
516
517 cmdList.SetDynamicStateViewport({ 0.0f, 0.0f, fSize.x, fSize.y, 0.0f, 1.0f });
518 cmdList.SetDynamicStateScissor({ 0, 0, size.x, size.y });
519
520 for (uint32_t idx = 0U; idx < blurCount; ++idx) {
521 // horizontal (from real image to temp)
522 pc.factor = { 1.0f, 0.0f, 0.0f, 0.0f };
523 renderPass.renderPassDesc.attachmentHandles[0] = handle1;
524 BlurPass(di, *binders_[descIdx++], pipelineData_.psoBlur, handle0, 0U);
525
526 // vertical
527 renderPass.renderPassDesc.attachmentHandles[0] = handle0;
528 pc.factor = { 0.0f, 1.0f, 0.0f, 0.0f };
529 BlurPass(di, *binders_[descIdx++], pipelineData_.psoBlur, handle1, 0U);
530 }
531
532 // update output
533 nodeOutputsData.output.handle = handle0;
534 }
535
EvaluateInOut()536 bool RenderPostProcessBlurNode::EvaluateInOut()
537 {
538 inputImgData_.rawHandle = nodeInputsData.input.handle;
539 if (RenderHandleUtil::IsValid(nodeInputsData.input.handle)) {
540 nodeOutputsData.output.handle = RenderHandleUtil::IsValid(nodeOutputsData.output.handle)
541 ? nodeOutputsData.output.handle
542 : nodeInputsData.input.handle;
543 return true;
544 }
545 #if (RENDER_VALIDATION_ENABLED == 1)
546 CORE_LOG_ONCE_W(renderNodeContextMgr_->GetName() + "_inout_issue",
547 "RENDER_VALIDATION: render post process input / output issue");
548 #endif
549 return false;
550 }
551
CheckDescriptorSetNeed()552 void RenderPostProcessBlurNode::CheckDescriptorSetNeed()
553 {
554 if (binders_.empty()) {
555 constexpr uint32_t set { 0U };
556 binders_.clear();
557 binders_.resize(MAX_BINDER_COUNT);
558
559 INodeContextDescriptorSetManager& descriptorSetMgr = renderNodeContextMgr_->GetDescriptorSetManager();
560 const auto& bindings = pipelineData_.gsd.pipelineLayoutData.descriptorSetLayouts[set].bindings;
561 for (uint32_t idx = 0; idx < MAX_BINDER_COUNT; ++idx) {
562 const RenderHandle descHandle = descriptorSetMgr.CreateDescriptorSet(bindings);
563 binders_[idx] = descriptorSetMgr.CreateDescriptorSetBinder(descHandle, bindings);
564 }
565 }
566 }
567
EvaluateTemporaryTargets()568 void RenderPostProcessBlurNode::EvaluateTemporaryTargets()
569 {
570 IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr_->GetGpuResourceManager();
571 const GpuImageDesc inDesc = gpuResourceMgr.GetImageDescriptor(nodeInputsData.input.handle);
572 inputImgData_.size = { inDesc.width, inDesc.height };
573 inputImgData_.mipCount = inDesc.mipCount;
574 Math::UVec2 texSize = inputImgData_.size / 2U;
575 texSize.x = Math::max(1u, texSize.x);
576 texSize.y = Math::max(1u, texSize.y);
577 if (texSize.x != tmpImgTargets_.size.x || texSize.y != tmpImgTargets_.size.y) {
578 const bool needsTwoTargets =
579 ((inDesc.usageFlags & ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) == 0);
580 tmpImgTargets_.size = texSize;
581 constexpr ImageUsageFlags usageFlags = ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
582 ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT |
583 ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
584 const GpuImageDesc desc {
585 ImageType::CORE_IMAGE_TYPE_2D,
586 ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,
587 inDesc.format,
588 ImageTiling::CORE_IMAGE_TILING_OPTIMAL,
589 usageFlags,
590 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
591 0U,
592 EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS,
593 texSize.x,
594 texSize.y,
595 1U,
596 Math::max(1U, (inDesc.mipCount - 1U)),
597 1u,
598 SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,
599 {},
600 };
601 #if (RENDER_VALIDATION_ENABLED == 1)
602 const auto additionalName =
603 BASE_NS::to_string(reinterpret_cast<uintptr_t>(reinterpret_cast<void*>(postProcess_.get())));
604 tmpImgTargets_.handle =
605 gpuResourceMgr.Create(renderNodeContextMgr_->GetName() + "_BLUR0_" + additionalName, desc);
606 if (needsTwoTargets) {
607 tmpImgTargets_.handleExt =
608 gpuResourceMgr.Create(renderNodeContextMgr_->GetName() + "_BLUR1" + additionalName, desc);
609 }
610 #else
611 tmpImgTargets_.handle = renderNodeContextMgr_->GetGpuResourceManager().Create(tmpImgTargets_.handle, desc);
612 if (needsTwoTargets) {
613 tmpImgTargets_.handleExt =
614 renderNodeContextMgr_->GetGpuResourceManager().Create(tmpImgTargets_.handleExt, desc);
615 }
616 #endif
617 if (!needsTwoTargets) {
618 tmpImgTargets_.handleExt = {};
619 }
620 tmpImgTargets_.rawHandle = tmpImgTargets_.handle.GetHandle();
621 tmpImgTargets_.rawHandleExt = tmpImgTargets_.handleExt.GetHandle();
622 tmpImgTargets_.mipCount = desc.mipCount;
623 }
624 }
625 RENDER_END_NAMESPACE()
626