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_blur.h"
17
18#include <render/device/intf_gpu_resource_manager.h>
19#include <render/device/intf_shader_manager.h>
20#include <render/device/pipeline_layout_desc.h>
21#include <render/device/pipeline_state_desc.h>
22#include <render/namespace.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_pipeline_descriptor_set_binder.h>
26#include <render/nodecontext/intf_render_command_list.h>
27#include <render/nodecontext/intf_render_node_context_manager.h>
28#include <render/nodecontext/intf_render_node_util.h>
29#include <render/shaders/common/render_blur_common.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 "util/log.h"
35
36using namespace BASE_NS;
37
38RENDER_BEGIN_NAMESPACE()
39namespace {
40constexpr DynamicStateEnum DYNAMIC_STATES[] = { CORE_DYNAMIC_STATE_ENUM_VIEWPORT, CORE_DYNAMIC_STATE_ENUM_SCISSOR };
41
42constexpr uint32_t MAX_MIP_COUNT { 16u };
43constexpr uint32_t MAX_PASS_PER_LEVEL_COUNT { 3u };
44constexpr bool GAUSSIAN_TYPE { true };
45} // namespace
46
47void RenderBlur::Init(IRenderNodeContextManager& renderNodeContextMgr, const BlurInfo& blurInfo)
48{
49    blurInfo_ = blurInfo;
50    {
51        const IRenderNodeShaderManager& shaderMgr = renderNodeContextMgr.GetShaderManager();
52        renderData_ = {};
53        renderData_.shader = shaderMgr.GetShaderHandle("rendershaders://shader/fullscreen_blur.shader");
54        renderData_.pipelineLayout = shaderMgr.GetReflectionPipelineLayout(renderData_.shader);
55    }
56    {
57        imageData_ = {};
58        imageData_.mipImage = blurInfo.blurTarget.handle;
59        samplerHandle_ = renderNodeContextMgr.GetGpuResourceManager().GetSamplerHandle(
60            DefaultEngineGpuResourceConstants::CORE_DEFAULT_SAMPLER_LINEAR_CLAMP);
61    }
62    {
63        constexpr uint32_t globalSet = 0u;
64        constexpr uint32_t localSet = 1u;
65
66        constexpr uint32_t maxBinderCount = MAX_MIP_COUNT * MAX_PASS_PER_LEVEL_COUNT;
67        binders_.clear();
68        binders_.resize(maxBinderCount);
69
70        INodeContextDescriptorSetManager& descriptorSetMgr = renderNodeContextMgr.GetDescriptorSetManager();
71        {
72            const auto& bindings = renderData_.pipelineLayout.descriptorSetLayouts[globalSet].bindings;
73            const RenderHandle descHandle = descriptorSetMgr.CreateDescriptorSet(bindings);
74            globalSet0_ = descriptorSetMgr.CreateDescriptorSetBinder(descHandle, bindings);
75        }
76        const auto& bindings = renderData_.pipelineLayout.descriptorSetLayouts[localSet].bindings;
77        for (uint32_t idx = 0; idx < maxBinderCount; ++idx) {
78            const RenderHandle descHandle = descriptorSetMgr.CreateDescriptorSet(bindings);
79            binders_[idx] = descriptorSetMgr.CreateDescriptorSetBinder(descHandle, bindings);
80        }
81    }
82}
83
84void RenderBlur::PreExecute(
85    IRenderNodeContextManager& renderNodeContextMgr, const BlurInfo& blurInfo, const PostProcessConfiguration& ppConfig)
86{
87    blurInfo_ = blurInfo;
88    imageData_.mipImage = blurInfo.blurTarget.handle;
89    globalUbo_ = blurInfo.globalUbo;
90
91    const IRenderNodeGpuResourceManager& gpuResourceMgr = renderNodeContextMgr.GetGpuResourceManager();
92    const GpuImageDesc imageDesc = gpuResourceMgr.GetImageDescriptor(imageData_.mipImage);
93    imageData_.mipCount = imageDesc.mipCount;
94    imageData_.format = imageDesc.format;
95    imageData_.size = { imageDesc.width, imageDesc.height };
96    if (GAUSSIAN_TYPE) {
97        CreateTargets(renderNodeContextMgr, imageData_.size);
98    }
99}
100
101void RenderBlur::Execute(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList,
102    const PostProcessConfiguration& ppConfig)
103{
104    if (!RenderHandleUtil::IsGpuImage(imageData_.mipImage)) {
105        return;
106    }
107
108    UpdateGlobalSet(cmdList);
109
110    RenderPass renderPass;
111    renderPass.renderPassDesc.attachmentCount = 1;
112    renderPass.renderPassDesc.renderArea = { 0, 0, imageData_.size.x, imageData_.size.y };
113    renderPass.renderPassDesc.subpassCount = 1;
114    renderPass.renderPassDesc.attachments[0].loadOp = CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
115    renderPass.renderPassDesc.attachments[0].storeOp = CORE_ATTACHMENT_STORE_OP_STORE;
116    renderPass.renderPassDesc.attachmentHandles[0] = imageData_.mipImage;
117    renderPass.subpassStartIndex = 0;
118    auto& subpass = renderPass.subpassDesc;
119    subpass.colorAttachmentCount = 1;
120    subpass.colorAttachmentIndices[0] = 0;
121
122    if (!RenderHandleUtil::IsValid(renderData_.psoScale)) {
123        const auto& shaderMgr = renderNodeContextMgr.GetShaderManager();
124        const ShaderSpecializationConstantView sscv = shaderMgr.GetReflectionSpecialization(renderData_.shader);
125        const RenderHandle graphicsState = shaderMgr.GetGraphicsStateHandleByShaderHandle(renderData_.shader);
126        {
127            const uint32_t specializationFlags[] = { blurInfo_.scaleType };
128            const ShaderSpecializationConstantDataView specDataView { sscv.constants, specializationFlags };
129            renderData_.psoScale =
130                renderNodeContextMgr.GetPsoManager().GetGraphicsPsoHandle(renderData_.shader, graphicsState,
131                    renderData_.pipelineLayout, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
132        }
133        {
134            const uint32_t specializationFlags[] = { blurInfo_.blurType };
135            const ShaderSpecializationConstantDataView specDataView { sscv.constants, specializationFlags };
136            renderData_.psoBlur =
137                renderNodeContextMgr.GetPsoManager().GetGraphicsPsoHandle(renderData_.shader, graphicsState,
138                    renderData_.pipelineLayout, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
139        }
140    }
141
142    if (GAUSSIAN_TYPE) {
143        RenderGaussian(renderNodeContextMgr, cmdList, renderPass, ppConfig);
144    } else {
145        RenderData(renderNodeContextMgr, cmdList, renderPass, ppConfig);
146    }
147}
148
149void RenderBlur::UpdateGlobalSet(IRenderCommandList& cmdList)
150{
151    auto& binder = *globalSet0_;
152    binder.ClearBindings();
153    uint32_t binding = 0u;
154    binder.BindBuffer(binding++, globalUbo_, 0);
155    binder.BindBuffer(binding++, globalUbo_, sizeof(GlobalPostProcessStruct));
156    cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
157}
158
159DescriptorCounts RenderBlur::GetDescriptorCounts() const
160{
161    // expected high max mip count
162    return DescriptorCounts { {
163        { CORE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, MAX_MIP_COUNT },
164        { CORE_DESCRIPTOR_TYPE_SAMPLER, MAX_MIP_COUNT },
165        { CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u * MAX_MIP_COUNT },
166    } };
167}
168
169// constants for RenderBlur::RenderData
170namespace {
171constexpr bool USE_CUSTOM_BARRIERS = true;
172
173constexpr ImageResourceBarrier SRC_UNDEFINED { 0, CORE_PIPELINE_STAGE_TOP_OF_PIPE_BIT, CORE_IMAGE_LAYOUT_UNDEFINED };
174constexpr ImageResourceBarrier COL_ATTACHMENT { CORE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
175    CORE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, CORE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
176constexpr ImageResourceBarrier SHDR_READ { CORE_ACCESS_SHADER_READ_BIT, CORE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
177    CORE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
178// transition the final mip level to read only as well
179constexpr ImageResourceBarrier FINAL_SRC { CORE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
180    CORE_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | CORE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
181    CORE_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
182constexpr ImageResourceBarrier FINAL_DST { CORE_ACCESS_SHADER_READ_BIT,
183    CORE_PIPELINE_STAGE_VERTEX_SHADER_BIT, // first possible shader read stage
184    CORE_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
185} // namespace
186
187void RenderBlur::RenderData(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList,
188    const RenderPass& renderPassBase, const PostProcessConfiguration& ppConfig)
189{
190    RenderPass renderPass = renderPassBase;
191    const GpuImageDesc imageDesc = renderNodeContextMgr.GetGpuResourceManager().GetImageDescriptor(imageData_.mipImage);
192
193    if (USE_CUSTOM_BARRIERS) {
194        cmdList.BeginDisableAutomaticBarrierPoints();
195    }
196
197    RenderHandle sets[2u] {};
198    sets[0] = globalSet0_->GetDescriptorSetHandle();
199
200    const uint32_t blurCount = Math::min(ppConfig.blurConfiguration.maxMipLevel, imageData_.mipCount);
201    // NOTE: for smoother results, first downscale -> then horiz / vert -> downscale and so on
202    ImageSubresourceRange imageSubresourceRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
203        PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
204    for (uint32_t idx = 1; idx < blurCount; ++idx) {
205        const uint32_t renderPassMipLevel = blurInfo_.upScale ? (blurCount - idx - 1) : idx;
206        const uint32_t inputMipLevel = blurInfo_.upScale ? (blurCount - idx) : (idx - 1);
207
208        const uint32_t currWidth = Math::max(1u, imageDesc.width >> renderPassMipLevel);
209        const uint32_t currHeight = Math::max(1u, imageDesc.height >> renderPassMipLevel);
210        const float fCurrWidth = static_cast<float>(currWidth);
211        const float fCurrHeight = static_cast<float>(currHeight);
212
213        renderPass.renderPassDesc.renderArea = { 0, 0, currWidth, currHeight };
214        renderPass.renderPassDesc.attachments[0].mipLevel = renderPassMipLevel;
215
216        if (USE_CUSTOM_BARRIERS) {
217            imageSubresourceRange.baseMipLevel = renderPassMipLevel;
218            cmdList.CustomImageBarrier(imageData_.mipImage, SRC_UNDEFINED, COL_ATTACHMENT, imageSubresourceRange);
219            imageSubresourceRange.baseMipLevel = inputMipLevel;
220            if (inputMipLevel == 0) {
221                cmdList.CustomImageBarrier(imageData_.mipImage, SHDR_READ, imageSubresourceRange);
222            } else {
223                cmdList.CustomImageBarrier(imageData_.mipImage, COL_ATTACHMENT, SHDR_READ, imageSubresourceRange);
224            }
225            cmdList.AddCustomBarrierPoint();
226        }
227
228        cmdList.BeginRenderPass(renderPass.renderPassDesc, renderPass.subpassStartIndex, renderPass.subpassDesc);
229
230        cmdList.SetDynamicStateViewport(ViewportDesc { 0.0f, 0.0f, fCurrWidth, fCurrHeight, 0.0f, 0.0f });
231        cmdList.SetDynamicStateScissor(ScissorDesc { 0, 0, currWidth, currHeight });
232
233        cmdList.BindPipeline(renderData_.psoScale);
234
235        {
236            auto& binder = *binders_[idx];
237            sets[1u] = binder.GetDescriptorSetHandle();
238            binder.ClearBindings();
239            binder.BindSampler(0, samplerHandle_);
240            binder.BindImage(1, { imageData_.mipImage, inputMipLevel });
241            cmdList.UpdateDescriptorSet(
242                binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
243        }
244        cmdList.BindDescriptorSets(0u, sets);
245
246        const LocalPostProcessPushConstantStruct pc {
247            { fCurrWidth, fCurrHeight, 1.0f / (fCurrWidth), 1.0f / (fCurrHeight) }, { 1.0f, 0.0f, 0.0f, 0.0f }
248        };
249        cmdList.PushConstant(renderData_.pipelineLayout.pushConstant, reinterpret_cast<const uint8_t*>(&pc));
250
251        cmdList.Draw(3u, 1u, 0u, 0u);
252        cmdList.EndRenderPass();
253    }
254
255    if (USE_CUSTOM_BARRIERS) {
256        if (imageData_.mipCount > 1u) {
257            // transition the final used mip level
258            if (blurCount > 0) {
259                const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount - 1, 1, 0,
260                    PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
261                cmdList.CustomImageBarrier(imageData_.mipImage, FINAL_SRC, FINAL_DST, imgRange);
262            }
263            if (blurCount < imageData_.mipCount) {
264                // transition the final levels which might be in undefined state
265                const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount,
266                    imageData_.mipCount - blurCount, 0, PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
267                cmdList.CustomImageBarrier(imageData_.mipImage, SRC_UNDEFINED, FINAL_DST, imgRange);
268            }
269        }
270        cmdList.AddCustomBarrierPoint();
271        cmdList.EndDisableAutomaticBarrierPoints();
272    }
273}
274
275namespace {
276void DownscaleBarrier(IRenderCommandList& cmdList, const RenderHandle image, const uint32_t mipLevel)
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(image, SRC_UNDEFINED, COL_ATTACHMENT, imgRange);
282    const uint32_t inputMipLevel = mipLevel - 1u;
283    imgRange.baseMipLevel = inputMipLevel;
284    if (inputMipLevel == 0) {
285        cmdList.CustomImageBarrier(image, SHDR_READ, imgRange);
286    } else {
287        cmdList.CustomImageBarrier(image, COL_ATTACHMENT, SHDR_READ, imgRange);
288    }
289    cmdList.AddCustomBarrierPoint();
290}
291
292void BlurHorizontalBarrier(
293    IRenderCommandList& cmdList, const RenderHandle realImage, const uint32_t mipLevel, const RenderHandle tmpImage)
294{
295    ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
296        PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
297    imgRange.baseMipLevel = mipLevel;
298    cmdList.CustomImageBarrier(realImage, COL_ATTACHMENT, SHDR_READ, imgRange);
299    imgRange.baseMipLevel = mipLevel - 1;
300    cmdList.CustomImageBarrier(tmpImage, SRC_UNDEFINED, COL_ATTACHMENT, imgRange);
301    cmdList.AddCustomBarrierPoint();
302}
303
304void BlurVerticalBarrier(
305    IRenderCommandList& cmdList, const RenderHandle realImage, const uint32_t mipLevel, const RenderHandle tmpImage)
306{
307    ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
308        PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
309    imgRange.baseMipLevel = mipLevel;
310    cmdList.CustomImageBarrier(realImage, SHDR_READ, COL_ATTACHMENT, imgRange);
311    imgRange.baseMipLevel = mipLevel - 1;
312    cmdList.CustomImageBarrier(tmpImage, COL_ATTACHMENT, SHDR_READ, imgRange);
313    cmdList.AddCustomBarrierPoint();
314}
315
316struct ConstDrawInput {
317    IRenderCommandList& cmdList;
318    const RenderPass& renderPass;
319    const PushConstant& pushConstant;
320    const LocalPostProcessPushConstantStruct& pc;
321    RenderHandle sampler;
322};
323void BlurPass(const ConstDrawInput& di, IDescriptorSetBinder& binder, IDescriptorSetBinder& globalBinder,
324    const RenderHandle psoHandle, const RenderHandle image, const uint32_t inputMipLevel)
325{
326    di.cmdList.BeginRenderPass(
327        di.renderPass.renderPassDesc, di.renderPass.subpassStartIndex, di.renderPass.subpassDesc);
328    di.cmdList.BindPipeline(psoHandle);
329
330    RenderHandle sets[2u] {};
331    sets[0] = globalBinder.GetDescriptorSetHandle();
332    {
333        binder.ClearBindings();
334        sets[1u] = binder.GetDescriptorSetHandle();
335        binder.BindSampler(0, di.sampler);
336        binder.BindImage(1u, { image, inputMipLevel });
337        di.cmdList.UpdateDescriptorSet(
338            binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
339    }
340    di.cmdList.BindDescriptorSets(0, sets);
341
342    di.cmdList.PushConstant(di.pushConstant, reinterpret_cast<const uint8_t*>(&di.pc));
343    di.cmdList.Draw(3u, 1u, 0u, 0u);
344    di.cmdList.EndRenderPass();
345}
346} // namespace
347
348void RenderBlur::RenderGaussian(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList,
349    const RenderPass& renderPassBase, const PostProcessConfiguration& ppConfig)
350{
351    RenderPass renderPass = renderPassBase;
352    if (USE_CUSTOM_BARRIERS) {
353        cmdList.BeginDisableAutomaticBarrierPoints();
354    }
355
356    // with every mip, first we do a downscale
357    // then a single horizontal and a single vertical blur
358    LocalPostProcessPushConstantStruct pc { { 1.0f, 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 0.0f } };
359    uint32_t descIdx = 0;
360    const uint32_t blurCount = Math::min(ppConfig.blurConfiguration.maxMipLevel, imageData_.mipCount);
361    const ConstDrawInput di { cmdList, renderPass, renderData_.pipelineLayout.pushConstant, pc, samplerHandle_ };
362    for (uint32_t idx = 1; idx < blurCount; ++idx) {
363        const uint32_t mip = idx;
364
365        const Math::UVec2 size = { Math::max(1u, imageData_.size.x >> mip), Math::max(1u, imageData_.size.y >> mip) };
366        const Math::Vec2 fSize = { static_cast<float>(size.x), static_cast<float>(size.y) };
367        const Math::Vec4 texSizeInvTexSize { fSize.x * 1.0f, fSize.y * 1.0f, 1.0f / fSize.x, 1.0f / fSize.y };
368        pc = { texSizeInvTexSize, { 1.0f, 0.0f, 0.0f, 0.0f } };
369
370        renderPass.renderPassDesc.renderArea = { 0, 0, size.x, size.y };
371        renderPass.renderPassDesc.attachments[0].mipLevel = mip;
372
373        cmdList.SetDynamicStateViewport({ 0.0f, 0.0f, fSize.x, fSize.y, 0.0f, 1.0f });
374        cmdList.SetDynamicStateScissor({ 0, 0, size.x, size.y });
375
376        // downscale
377        if (USE_CUSTOM_BARRIERS) {
378            DownscaleBarrier(cmdList, imageData_.mipImage, mip);
379        }
380        BlurPass(di, *binders_[descIdx++], *globalSet0_, renderData_.psoScale, imageData_.mipImage, mip - 1u);
381
382        // horizontal (from real image to temp)
383        if (USE_CUSTOM_BARRIERS) {
384            BlurHorizontalBarrier(cmdList, imageData_.mipImage, mip, tempTarget_.tex.GetHandle());
385        }
386
387        renderPass.renderPassDesc.attachmentHandles[0] = tempTarget_.tex.GetHandle();
388        renderPass.renderPassDesc.attachments[0].mipLevel = mip - 1u;
389        BlurPass(di, *binders_[descIdx++], *globalSet0_, renderData_.psoBlur, imageData_.mipImage, mip);
390
391        // vertical
392        if (USE_CUSTOM_BARRIERS) {
393            BlurVerticalBarrier(cmdList, imageData_.mipImage, mip, tempTarget_.tex.GetHandle());
394        }
395
396        renderPass.renderPassDesc.attachmentHandles[0] = imageData_.mipImage;
397        renderPass.renderPassDesc.attachments[0].mipLevel = mip;
398        pc.factor = { 0.0f, 1.0f, 0.0f, 0.0f };
399        BlurPass(di, *binders_[descIdx++], *globalSet0_, renderData_.psoBlur, tempTarget_.tex.GetHandle(), mip - 1);
400    }
401
402    if (USE_CUSTOM_BARRIERS) {
403        if (imageData_.mipCount > 1u) {
404            // transition the final used mip level
405            if (blurCount > 0) {
406                const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount - 1, 1, 0,
407                    PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
408                cmdList.CustomImageBarrier(imageData_.mipImage, FINAL_SRC, FINAL_DST, imgRange);
409            }
410            if (blurCount < imageData_.mipCount) {
411                // transition the final levels which might be in undefined state
412                const ImageSubresourceRange imgRange { CORE_IMAGE_ASPECT_COLOR_BIT, blurCount,
413                    imageData_.mipCount - blurCount, 0, PipelineStateConstants::GPU_IMAGE_ALL_LAYERS };
414                cmdList.CustomImageBarrier(imageData_.mipImage, SRC_UNDEFINED, FINAL_DST, imgRange);
415            }
416        }
417        cmdList.AddCustomBarrierPoint();
418        cmdList.EndDisableAutomaticBarrierPoints();
419    }
420}
421
422void RenderBlur::CreateTargets(IRenderNodeContextManager& renderNodeContextMgr, const Math::UVec2 baseSize)
423{
424    Math::UVec2 texSize = baseSize / 2u;
425    texSize.x = Math::max(1u, texSize.x);
426    texSize.y = Math::max(1u, texSize.y);
427    if (texSize.x != tempTarget_.texSize.x || texSize.y != tempTarget_.texSize.y) {
428        tempTarget_.texSize = texSize;
429        tempTarget_.format = imageData_.format;
430
431        constexpr ImageUsageFlags usageFlags = ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
432                                               ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT |
433                                               ImageUsageFlagBits::CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
434
435        const GpuImageDesc desc {
436            ImageType::CORE_IMAGE_TYPE_2D,
437            ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,
438            tempTarget_.format,
439            ImageTiling::CORE_IMAGE_TILING_OPTIMAL,
440            usageFlags,
441            MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
442            0,
443            EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS,
444            tempTarget_.texSize.x,
445            tempTarget_.texSize.y,
446            1u,
447            Math::max(1u, (imageData_.mipCount - 1u)),
448            1u,
449            SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,
450            {},
451        };
452#if (RENDER_VALIDATION_ENABLED == 1)
453        tempTarget_.tex =
454            renderNodeContextMgr.GetGpuResourceManager().Create(renderNodeContextMgr.GetName() + "_BLUR_TARGET", desc);
455#else
456        tempTarget_.tex = renderNodeContextMgr.GetGpuResourceManager().Create(tempTarget_.tex, desc);
457#endif
458    }
459}
460RENDER_END_NAMESPACE()
461