/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "render_bloom.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "util/log.h"
// shaders
#include
using namespace BASE_NS;
RENDER_BEGIN_NAMESPACE()
namespace {
constexpr DynamicStateEnum DYNAMIC_STATES[] = { CORE_DYNAMIC_STATE_ENUM_VIEWPORT, CORE_DYNAMIC_STATE_ENUM_SCISSOR };
}
void RenderBloom::Init(IRenderNodeContextManager& renderNodeContextMgr, const BloomInfo& bloomInfo)
{
bloomInfo_ = bloomInfo;
// NOTE: target counts etc. should probably be resized based on configuration
CreatePsos(renderNodeContextMgr);
auto& gpuResourceMgr = renderNodeContextMgr.GetGpuResourceManager();
samplerHandle_ = gpuResourceMgr.Create(samplerHandle_,
GpuSamplerDesc {
Filter::CORE_FILTER_LINEAR, // magFilter
Filter::CORE_FILTER_LINEAR, // minFilter
Filter::CORE_FILTER_LINEAR, // mipMapMode
SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeU
SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeV
SamplerAddressMode::CORE_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // addressModeW
});
}
void RenderBloom::PreExecute(IRenderNodeContextManager& renderNodeContextMgr, const BloomInfo& bloomInfo,
const PostProcessConfiguration& ppConfig)
{
bloomInfo_ = bloomInfo;
const GpuImageDesc& imgDesc =
renderNodeContextMgr.GetGpuResourceManager().GetImageDescriptor(bloomInfo_.input.handle);
uint32_t sizeDenom = 1u;
if (ppConfig.bloomConfiguration.bloomQualityType == BloomConfiguration::QUALITY_TYPE_LOW) {
sizeDenom = 2u;
}
CreateTargets(renderNodeContextMgr, Math::UVec2(imgDesc.width, imgDesc.height) / sizeDenom);
}
void RenderBloom::Execute(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList,
const PostProcessConfiguration& ppConfig)
{
bloomEnabled_ = false;
BloomConfiguration bloomConfiguration;
if (ppConfig.enableFlags & PostProcessConfiguration::ENABLE_BLOOM_BIT) {
bloomConfiguration.thresholdHard = ppConfig.bloomConfiguration.thresholdHard;
bloomConfiguration.thresholdSoft = ppConfig.bloomConfiguration.thresholdSoft;
bloomConfiguration.amountCoefficient = ppConfig.bloomConfiguration.amountCoefficient;
bloomConfiguration.dirtMaskCoefficient = ppConfig.bloomConfiguration.dirtMaskCoefficient;
bloomEnabled_ = true;
}
const auto bloomQualityType = ppConfig.bloomConfiguration.bloomQualityType;
PLUGIN_ASSERT(bloomQualityType < CORE_BLOOM_QUALITY_COUNT);
if (bloomInfo_.useCompute) {
psos_.downscale = psos_.downscaleHandlesCompute[bloomQualityType].regular;
psos_.downscaleAndThreshold = psos_.downscaleHandlesCompute[bloomQualityType].threshold;
} else {
psos_.downscale = psos_.downscaleHandles[bloomQualityType].regular;
psos_.downscaleAndThreshold = psos_.downscaleHandles[bloomQualityType].threshold;
}
if (!bloomEnabled_) {
bloomConfiguration.amountCoefficient = 0.0f;
}
bloomParameters_ = Math::Vec4(
// .x = thresholdHard, luma values below this won't bloom
bloomConfiguration.thresholdHard,
// .y = thresholdSoft, luma values from this value to hard threshold will reduce bloom input from 1.0 -> 0.0
// i.e. this creates softer threshold for bloom
bloomConfiguration.thresholdSoft,
// .z = amountCoefficient, will multiply the colors from the bloom textures when combined with original color
// target
bloomConfiguration.amountCoefficient,
// .w = -will multiply the dirt mask effect
bloomConfiguration.dirtMaskCoefficient);
const bool validBinders = binders_.globalSet0.get() != nullptr;
if (validBinders) {
if (bloomInfo_.useCompute) {
ComputeBloom(renderNodeContextMgr, cmdList);
} else {
GraphicsBloom(renderNodeContextMgr, cmdList);
}
}
}
DescriptorCounts RenderBloom::GetDescriptorCounts() const
{
// NOTE: when added support for various bloom target counts, might need to be calculated for max
return DescriptorCounts { {
{ CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 32u },
{ CORE_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 32u },
{ CORE_DESCRIPTOR_TYPE_STORAGE_IMAGE, 32u },
{ CORE_DESCRIPTOR_TYPE_SAMPLER, 24u },
{ CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u },
} };
}
RenderHandle RenderBloom::GetFinalTarget() const
{
if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
return bloomInfo_.output.handle;
} else {
// output tex1 on compute and tex2 on graphics
return bloomInfo_.useCompute ? (targets_.tex1[0u].GetHandle()) : (targets_.tex2[0u].GetHandle());
}
}
void RenderBloom::UpdateGlobalSet(IRenderCommandList& cmdList)
{
auto& binder = *binders_.globalSet0;
binder.ClearBindings();
uint32_t binding = 0u;
binder.BindBuffer(binding++, bloomInfo_.globalUbo, 0);
binder.BindBuffer(binding++, bloomInfo_.globalUbo, sizeof(GlobalPostProcessStruct));
cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
void RenderBloom::ComputeBloom(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList)
{
constexpr PushConstant pc { ShaderStageFlagBits::CORE_SHADER_STAGE_COMPUTE_BIT,
sizeof(LocalPostProcessPushConstantStruct) };
UpdateGlobalSet(cmdList);
if (bloomEnabled_) {
ComputeDownscaleAndThreshold(pc, cmdList);
ComputeDownscale(pc, cmdList);
ComputeUpscale(pc, cmdList);
}
// needs to be done even when bloom is disabled if node is in use
if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
ComputeCombine(pc, cmdList);
}
}
void RenderBloom::ComputeDownscaleAndThreshold(const PushConstant& pc, IRenderCommandList& cmdList)
{
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
{
auto& binder = *binders_.downscaleAndThreshold;
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
uint32_t binding = 0;
binder.BindImage(binding++, { targets_.tex1[0].GetHandle() });
binder.BindImage(binding++, { bloomInfo_.input });
binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindPipeline(psos_.downscaleAndThreshold);
const ShaderThreadGroup tgs = psos_.downscaleAndThresholdTGS;
// bind all sets
cmdList.BindDescriptorSets(0, sets);
const auto targetSize = targets_.tex1Size[0];
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
}
void RenderBloom::ComputeDownscale(const PushConstant& pc, IRenderCommandList& cmdList)
{
cmdList.BindPipeline(psos_.downscale);
const ShaderThreadGroup tgs = psos_.downscaleTGS;
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
for (size_t i = 1; i < targets_.tex1.size(); ++i) {
{
auto& binder = *binders_.downscale[i];
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
uint32_t binding = 0;
binder.BindImage(binding++, { targets_.tex1[i].GetHandle() });
binder.BindImage(binding++, { targets_.tex1[i - 1].GetHandle() });
binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(
binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
const auto targetSize = targets_.tex1Size[i];
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
}
}
void RenderBloom::ComputeUpscale(const PushConstant& pc, IRenderCommandList& cmdList)
{
cmdList.BindPipeline(psos_.upscale);
const ShaderThreadGroup tgs = psos_.upscaleTGS;
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
for (size_t i = targets_.tex1.size() - 1; i != 0; --i) {
{
auto& binder = *binders_.upscale[i];
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
binder.BindImage(0u, { targets_.tex1[i - 1].GetHandle() });
binder.BindImage(1u, { targets_.tex1[i].GetHandle() });
binder.BindSampler(2u, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(
binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
const auto targetSize = targets_.tex1Size[i - 1];
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
}
}
void RenderBloom::ComputeCombine(const PushConstant& pc, IRenderCommandList& cmdList)
{
cmdList.BindPipeline(psos_.combine);
const ShaderThreadGroup tgs = psos_.combineTGS;
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
{
auto& binder = *binders_.combine;
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
// bind resources to set 1
uint32_t binding = 0;
binder.BindImage(binding++, { bloomInfo_.output });
binder.BindImage(binding++, { bloomInfo_.input });
binder.BindImage(binding++, { targets_.tex1[0].GetHandle() });
binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
// update the descriptor set bindings for set 1
cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
const auto targetSize = baseSize_;
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Dispatch((targetSize.x + tgs.x - 1) / tgs.x, (targetSize.y + tgs.y - 1) / tgs.y, 1);
}
void RenderBloom::GraphicsBloom(IRenderNodeContextManager& renderNodeContextMgr, IRenderCommandList& cmdList)
{
RenderPass renderPass;
renderPass.renderPassDesc.attachmentCount = 1;
renderPass.renderPassDesc.subpassCount = 1;
renderPass.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
renderPass.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
RenderPassSubpassDesc& subpassDesc = renderPass.subpassDesc;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.colorAttachmentIndices[0] = 0;
constexpr PushConstant pc { ShaderStageFlagBits::CORE_SHADER_STAGE_FRAGMENT_BIT,
sizeof(LocalPostProcessPushConstantStruct) };
UpdateGlobalSet(cmdList);
if (bloomEnabled_) {
RenderDownscaleAndThreshold(renderPass, pc, cmdList);
RenderDownscale(renderPass, pc, cmdList);
RenderUpscale(renderPass, pc, cmdList);
}
// combine (needs to be done even when bloom is disabled if node is in use
if (RenderHandleUtil::IsValid(bloomInfo_.output.handle)) {
RenderCombine(renderPass, pc, cmdList);
}
}
void RenderBloom::RenderDownscaleAndThreshold(
RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
{
const auto targetSize = targets_.tex1Size[0];
const ViewportDesc viewportDesc { 0, 0, static_cast(targetSize.x), static_cast(targetSize.y) };
const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
renderPass.renderPassDesc.attachmentHandles[0] = targets_.tex1[0].GetHandle();
renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
cmdList.SetDynamicStateViewport(viewportDesc);
cmdList.SetDynamicStateScissor(scissorDesc);
cmdList.BindPipeline(psos_.downscaleAndThreshold);
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
{
auto& binder = *binders_.downscaleAndThreshold;
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
binder.BindImage(0u, { bloomInfo_.input });
binder.BindSampler(1u, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Draw(3u, 1u, 0u, 0u);
cmdList.EndRenderPass();
}
void RenderBloom::RenderDownscale(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
{
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
for (size_t idx = 1; idx < targets_.tex1.size(); ++idx) {
const auto targetSize = targets_.tex1Size[idx];
const ViewportDesc viewportDesc { 0, 0, static_cast(targetSize.x), static_cast(targetSize.y) };
const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
renderPass.renderPassDesc.attachmentHandles[0] = targets_.tex1[idx].GetHandle();
renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
cmdList.SetDynamicStateViewport(viewportDesc);
cmdList.SetDynamicStateScissor(scissorDesc);
cmdList.BindPipeline(psos_.downscale);
{
auto& binder = *binders_.downscale[idx];
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
binder.BindImage(0u, { targets_.tex1[idx - 1].GetHandle() });
binder.BindSampler(1u, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(
binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Draw(3u, 1u, 0u, 0u);
cmdList.EndRenderPass();
}
}
void RenderBloom::RenderUpscale(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
{
RenderPass renderPassUpscale = renderPass;
renderPassUpscale.subpassDesc.inputAttachmentCount = 1;
renderPassUpscale.subpassDesc.inputAttachmentIndices[0] = 0;
renderPassUpscale.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
renderPassUpscale.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
PLUGIN_ASSERT(targets_.tex1.size() == targets_.tex2.size());
RenderHandle input;
if (targets_.tex1.size() >= 1) {
input = targets_.tex1[targets_.tex1.size() - 1].GetHandle();
}
for (size_t idx = targets_.tex1.size() - 1; idx != 0; --idx) {
const auto targetSize = targets_.tex1Size[idx - 1];
const ViewportDesc viewportDesc { 0, 0, static_cast(targetSize.x), static_cast(targetSize.y) };
const ScissorDesc scissorDesc = { 0, 0, targetSize.x, targetSize.y };
// tex2 as output
renderPassUpscale.renderPassDesc.attachmentHandles[0] = targets_.tex2[idx - 1].GetHandle();
renderPassUpscale.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
cmdList.BeginRenderPass(renderPassUpscale.renderPassDesc, 0, renderPassUpscale.subpassDesc);
cmdList.SetDynamicStateViewport(viewportDesc);
cmdList.SetDynamicStateScissor(scissorDesc);
cmdList.BindPipeline(psos_.upscale);
{
auto& binder = *binders_.upscale[idx];
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
uint32_t binding = 0;
binder.BindImage(binding++, { input });
binder.BindImage(binding++, { targets_.tex1[idx - 1].GetHandle() });
binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(
binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Draw(3u, 1u, 0u, 0u);
cmdList.EndRenderPass();
// next pass input
input = renderPassUpscale.renderPassDesc.attachmentHandles[0];
}
}
void RenderBloom::RenderCombine(RenderPass& renderPass, const PushConstant& pc, IRenderCommandList& cmdList)
{
const auto targetSize = baseSize_;
renderPass.renderPassDesc.attachmentHandles[0] = bloomInfo_.output.handle;
renderPass.renderPassDesc.renderArea = { 0, 0, targetSize.x, targetSize.y };
cmdList.BeginRenderPass(renderPass.renderPassDesc, 0, renderPass.subpassDesc);
cmdList.SetDynamicStateViewport(baseViewportDesc_);
cmdList.SetDynamicStateScissor(baseScissorDesc_);
cmdList.BindPipeline(psos_.combine);
RenderHandle sets[2u] {};
sets[0u] = binders_.globalSet0->GetDescriptorSetHandle();
{
auto& binder = *binders_.combine;
sets[1u] = binder.GetDescriptorSetHandle();
binder.ClearBindings();
uint32_t binding = 0;
binder.BindImage(binding++, { bloomInfo_.input });
// tex2 handle has the final result
binder.BindImage(binding++, { targets_.tex2[0].GetHandle() });
binder.BindSampler(binding++, { samplerHandle_.GetHandle() });
cmdList.UpdateDescriptorSet(binder.GetDescriptorSetHandle(), binder.GetDescriptorSetLayoutBindingResources());
}
cmdList.BindDescriptorSets(0u, sets);
LocalPostProcessPushConstantStruct uPc;
uPc.factor = bloomParameters_;
uPc.viewportSizeInvSize = Math::Vec4(static_cast(targetSize.x), static_cast(targetSize.y),
1.0f / static_cast(targetSize.x), 1.0f / static_cast(targetSize.y));
cmdList.PushConstantData(pc, arrayviewU8(uPc));
cmdList.Draw(3u, 1u, 0u, 0u);
cmdList.EndRenderPass();
}
void RenderBloom::CreateTargets(IRenderNodeContextManager& renderNodeContextMgr, const Math::UVec2 baseSize)
{
if (baseSize.x != baseSize_.x || baseSize.y != baseSize_.y) {
baseSize_ = baseSize;
format_ = Format::BASE_FORMAT_B10G11R11_UFLOAT_PACK32;
ImageUsageFlags usageFlags = CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT |
CORE_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
if (bloomInfo_.useCompute) {
format_ = Format::BASE_FORMAT_R16G16B16A16_SFLOAT; // used due to GLES
usageFlags = CORE_IMAGE_USAGE_STORAGE_BIT | CORE_IMAGE_USAGE_SAMPLED_BIT;
} else {
baseViewportDesc_ = { 0.0f, 0.0f, static_cast(baseSize.x), static_cast(baseSize.y), 0.0f,
1.0f };
baseScissorDesc_ = { 0, 0, baseSize.x, baseSize.y };
}
// create target image
const Math::UVec2 startTargetSize = baseSize_;
GpuImageDesc desc {
ImageType::CORE_IMAGE_TYPE_2D,
ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,
format_,
ImageTiling::CORE_IMAGE_TILING_OPTIMAL,
usageFlags,
MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
0,
EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS |
EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_RESET_STATE_ON_FRAME_BORDERS,
startTargetSize.x,
startTargetSize.y,
1u,
1u,
1u,
SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,
{},
};
auto& gpuResourceMgr = renderNodeContextMgr.GetGpuResourceManager();
#if (RENDER_VALIDATION_ENABLED == 1)
const string_view nodeName = renderNodeContextMgr.GetName();
#endif
for (size_t idx = 0; idx < targets_.tex1.size(); ++idx) {
// every bloom target is half the size of the original/ previous bloom target
desc.width /= 2u;
desc.height /= 2u;
desc.width = (desc.width >= 1u) ? desc.width : 1u;
desc.height = (desc.height >= 1u) ? desc.height : 1u;
targets_.tex1Size[idx] = Math::UVec2(desc.width, desc.height);
#if (RENDER_VALIDATION_ENABLED == 1)
const auto baseTargetName = nodeName + "_Bloom_" + to_string(idx);
targets_.tex1[idx] = gpuResourceMgr.Create(baseTargetName + "_A", desc);
if (!bloomInfo_.useCompute) {
targets_.tex2[idx] = gpuResourceMgr.Create(baseTargetName + "_B", desc);
}
#else
targets_.tex1[idx] = gpuResourceMgr.Create(targets_.tex1[idx], desc);
if (!bloomInfo_.useCompute) {
targets_.tex2[idx] = gpuResourceMgr.Create(targets_.tex2[idx], desc);
}
#endif
}
}
}
void RenderBloom::CreatePsos(IRenderNodeContextManager& renderNodeContextMgr)
{
if (bloomInfo_.useCompute) {
CreateComputePsos(renderNodeContextMgr);
} else {
CreateRenderPsos(renderNodeContextMgr);
}
}
void RenderBloom::CreateComputePsos(IRenderNodeContextManager& renderNodeContextMgr)
{
const auto& shaderMgr = renderNodeContextMgr.GetShaderManager();
INodeContextPsoManager& psoMgr = renderNodeContextMgr.GetPsoManager();
INodeContextDescriptorSetManager& dSetMgr = renderNodeContextMgr.GetDescriptorSetManager();
constexpr BASE_NS::pair configurations[] = {
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_LOW, RenderBloom::CORE_BLOOM_QUALITY_LOW },
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_NORMAL, RenderBloom::CORE_BLOOM_QUALITY_NORMAL },
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_HIGH, RenderBloom::CORE_BLOOM_QUALITY_HIGH }
};
for (const auto& configuration : configurations) {
{
auto shader = shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
const ShaderSpecializationConstantDataView specDataView {
{ specializations.constants.data(), specializations.constants.size() },
{ &configuration.second, 1u },
};
psos_.downscaleHandlesCompute[configuration.first].regular =
psoMgr.GetComputePsoHandle(shader, pl, specDataView);
}
{
auto shader = shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale_threshold.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
const ShaderSpecializationConstantDataView specDataView {
{ specializations.constants.data(), specializations.constants.size() },
{ &configuration.second, 1u },
};
psos_.downscaleHandlesCompute[configuration.first].threshold =
psoMgr.GetComputePsoHandle(shader, pl, specDataView);
}
}
constexpr uint32_t globalSet = 0u;
constexpr uint32_t localSetIdx = 1u;
// the first one creates the global set as well
{
const RenderHandle shaderHandle =
shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale_threshold.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
psos_.downscaleAndThreshold = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
psos_.downscaleAndThresholdTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
const auto& gBinds = pl.descriptorSetLayouts[globalSet].bindings;
binders_.globalSet0 = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(gBinds), gBinds);
const auto& lBinds = pl.descriptorSetLayouts[localSetIdx].bindings;
binders_.downscaleAndThreshold = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(lBinds), lBinds);
}
{
const RenderHandle shaderHandle =
shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_downscale.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
psos_.downscale = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
psos_.downscaleTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
PLUGIN_ASSERT(binders_.downscale.size() >= TARGET_COUNT);
const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
binders_.downscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
{
const RenderHandle shaderHandle =
shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_upscale.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
psos_.upscale = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
psos_.upscaleTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
PLUGIN_ASSERT(binders_.upscale.size() >= TARGET_COUNT);
const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
binders_.upscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
{
const RenderHandle shaderHandle =
shaderMgr.GetShaderHandle("rendershaders://computeshader/bloom_combine.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
psos_.combine = psoMgr.GetComputePsoHandle(shaderHandle, pl, {});
psos_.combineTGS = shaderMgr.GetReflectionThreadGroupSize(shaderHandle);
const auto& binds = pl.descriptorSetLayouts[localSetIdx].bindings;
binders_.combine = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
std::pair RenderBloom::CreateAndReflectRenderPso(
IRenderNodeContextManager& renderNodeContextMgr, const string_view shader, const RenderPass& renderPass)
{
const auto& shaderMgr = renderNodeContextMgr.GetShaderManager();
const RenderHandle shaderHandle = shaderMgr.GetShaderHandle(shader.data());
const RenderHandle graphicsStateHandle = shaderMgr.GetGraphicsStateHandleByShaderHandle(shaderHandle);
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shaderHandle);
auto& psoMgr = renderNodeContextMgr.GetPsoManager();
const RenderHandle pso = psoMgr.GetGraphicsPsoHandle(
shaderHandle, graphicsStateHandle, pl, {}, {}, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
return { pso, pl };
}
void RenderBloom::CreateRenderPsos(IRenderNodeContextManager& renderNodeContextMgr)
{
RenderPass renderPass;
renderPass.renderPassDesc.attachmentCount = 1;
renderPass.renderPassDesc.attachmentHandles[0] = bloomInfo_.input.handle;
renderPass.renderPassDesc.renderArea = { 0, 0, baseSize_.x, baseSize_.y };
renderPass.renderPassDesc.subpassCount = 1;
renderPass.renderPassDesc.attachments[0].loadOp = AttachmentLoadOp::CORE_ATTACHMENT_LOAD_OP_DONT_CARE;
renderPass.renderPassDesc.attachments[0].storeOp = AttachmentStoreOp::CORE_ATTACHMENT_STORE_OP_STORE;
RenderPassSubpassDesc subpassDesc = renderPass.subpassDesc;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.colorAttachmentIndices[0] = 0;
constexpr BASE_NS::pair configurations[] = {
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_LOW, RenderBloom::CORE_BLOOM_QUALITY_LOW },
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_NORMAL, RenderBloom::CORE_BLOOM_QUALITY_NORMAL },
{ BloomConfiguration::BloomQualityType::QUALITY_TYPE_HIGH, RenderBloom::CORE_BLOOM_QUALITY_HIGH }
};
const IRenderNodeShaderManager& shaderMgr = renderNodeContextMgr.GetShaderManager();
INodeContextPsoManager& psoMgr = renderNodeContextMgr.GetPsoManager();
for (const auto& configuration : configurations) {
{
auto shader = shaderMgr.GetShaderHandle("rendershaders://shader/bloom_downscale.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
const ShaderSpecializationConstantDataView specDataView {
{ specializations.constants.data(), specializations.constants.size() },
{ &configuration.second, 1u },
};
const RenderHandle graphicsState = shaderMgr.GetGraphicsStateHandleByShaderHandle(shader);
psos_.downscaleHandles[configuration.first].regular = psoMgr.GetGraphicsPsoHandle(
shader, graphicsState, pl, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
}
{
auto shader = shaderMgr.GetShaderHandle("rendershaders://shader/bloom_downscale_threshold.shader");
const PipelineLayout& pl = shaderMgr.GetReflectionPipelineLayout(shader);
ShaderSpecializationConstantView specializations = shaderMgr.GetReflectionSpecialization(shader);
const ShaderSpecializationConstantDataView specDataView {
{ specializations.constants.data(), specializations.constants.size() },
{ &configuration.second, 1u },
};
const RenderHandle graphicsState = shaderMgr.GetGraphicsStateHandleByShaderHandle(shader);
psos_.downscaleHandles[configuration.first].threshold = psoMgr.GetGraphicsPsoHandle(
shader, graphicsState, pl, {}, specDataView, { DYNAMIC_STATES, countof(DYNAMIC_STATES) });
}
}
INodeContextDescriptorSetManager& dSetMgr = renderNodeContextMgr.GetDescriptorSetManager();
constexpr uint32_t globalSet = 0u;
constexpr uint32_t localSet = 1u;
// the first one creates the global set as well
{
const auto [pso, pipelineLayout] = CreateAndReflectRenderPso(
renderNodeContextMgr, "rendershaders://shader/bloom_downscale_threshold.shader", renderPass);
psos_.downscaleAndThreshold = pso;
const auto& gBinds = pipelineLayout.descriptorSetLayouts[globalSet].bindings;
binders_.globalSet0 = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(gBinds), gBinds);
const auto& lBinds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
binders_.downscaleAndThreshold = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(lBinds), lBinds);
}
{
const auto [pso, pipelineLayout] = CreateAndReflectRenderPso(
renderNodeContextMgr, "rendershaders://shader/bloom_downscale.shader", renderPass);
psos_.downscale = pso;
const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
binders_.downscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
{
const auto [pso, pipelineLayout] =
CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_upscale.shader", renderPass);
psos_.upscale = pso;
const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
for (uint32_t idx = 0; idx < TARGET_COUNT; ++idx) {
binders_.upscale[idx] = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
{
const auto [pso, pipelineLayout] =
CreateAndReflectRenderPso(renderNodeContextMgr, "rendershaders://shader/bloom_combine.shader", renderPass);
psos_.combine = pso;
const auto& binds = pipelineLayout.descriptorSetLayouts[localSet].bindings;
binders_.combine = dSetMgr.CreateDescriptorSetBinder(dSetMgr.CreateDescriptorSet(binds), binds);
}
}
RENDER_END_NAMESPACE()