no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,476 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "BloomStage.h"
#include "../PipelineStateManager.h"
#include "../PipelineUBO.h"
#include "../RenderPipeline.h"
#include "../RenderQueue.h"
#include "frame-graph/DevicePass.h"
#include "frame-graph/PassNodeBuilder.h"
#include "frame-graph/Resource.h"
#include "gfx-base/GFXBuffer.h"
#include "gfx-base/GFXCommandBuffer.h"
#include "gfx-base/GFXDef-common.h"
#include "gfx-base/GFXDevice.h"
#include "gfx-base/GFXFramebuffer.h"
#include "gfx-base/states/GFXSampler.h"
#include "pipeline/Define.h"
#include "pipeline/UIPhase.h"
#include "profiler/Profiler.h"
#include "renderer/pipeline/deferred/DeferredPipelineSceneData.h"
#include "scene/Camera.h"
#include "scene/Pass.h"
#include "scene/RenderScene.h"
#include "scene/SubModel.h"
namespace cc {
namespace pipeline {
namespace {
const ccstd::string BLOOM_STAGE_NAME = "BloomStage";
framegraph::StringHandle prefilterTexHandle = framegraph::FrameGraph::stringToHandle("prefilterTex");
framegraph::StringHandle downsampleTexHandles[MAX_BLOOM_FILTER_PASS_NUM];
framegraph::StringHandle upsampleTexHandles[MAX_BLOOM_FILTER_PASS_NUM];
framegraph::StringHandle prefilterPassHandle;
framegraph::StringHandle downsamplePassHandles[MAX_BLOOM_FILTER_PASS_NUM];
framegraph::StringHandle upsamplePassHandles[MAX_BLOOM_FILTER_PASS_NUM];
framegraph::StringHandle combinePassHandle;
void initStrHandle() {
prefilterPassHandle = framegraph::FrameGraph::stringToHandle("bloomPrefilterPass");
ccstd::string tmp;
for (int i = 0; i < MAX_BLOOM_FILTER_PASS_NUM; ++i) {
tmp = ccstd::string("bloomDownsamplePass") + std::to_string(i);
downsamplePassHandles[i] = framegraph::FrameGraph::stringToHandle(tmp.c_str());
tmp = ccstd::string("bloomDownsampleTex") + std::to_string(i);
downsampleTexHandles[i] = framegraph::FrameGraph::stringToHandle(tmp.c_str());
tmp = ccstd::string("bloomUpsamplePass") + std::to_string(i);
upsamplePassHandles[i] = framegraph::FrameGraph::stringToHandle(tmp.c_str());
tmp = ccstd::string("bloomUpsampleTex") + std::to_string(i);
upsampleTexHandles[i] = framegraph::FrameGraph::stringToHandle(tmp.c_str());
}
combinePassHandle = framegraph::FrameGraph::stringToHandle("bloomCombinePass");
}
} // namespace
RenderStageInfo BloomStage::initInfo = {
BLOOM_STAGE_NAME,
static_cast<uint32_t>(DeferredStagePriority::BLOOM),
0,
{ccnew RenderQueueDesc{true, RenderQueueSortMode::BACK_TO_FRONT, {"default"}}},
};
const RenderStageInfo &BloomStage::getInitializeInfo() { return BloomStage::initInfo; }
BloomStage::BloomStage() = default;
bool BloomStage::initialize(const RenderStageInfo &info) {
RenderStage::initialize(info);
initStrHandle();
return true;
}
void BloomStage::activate(RenderPipeline *pipeline, RenderFlow *flow) {
RenderStage::activate(pipeline, flow);
_phaseID = getPhaseID("default");
}
void BloomStage::destroy() {
CC_SAFE_DELETE(_prefilterUBO);
CC_SAFE_DELETE(_combineUBO);
for (int i = 0; i < MAX_BLOOM_FILTER_PASS_NUM; ++i) {
CC_SAFE_DELETE(_downsampleUBO[i]);
CC_SAFE_DELETE(_upsampleUBO[i]);
}
}
void BloomStage::render(scene::Camera *camera) {
CC_PROFILE(BloomStageRender);
auto *pipeline = _pipeline;
CC_ASSERT_NOT_NULL(pipeline);
if (!pipeline->isBloomEnabled() || pipeline->getPipelineSceneData()->getRenderObjects().empty()) return;
if (_prefilterUBO == nullptr) {
_prefilterUBO = _device->createBuffer({gfx::BufferUsage::UNIFORM, gfx::MemoryUsage::DEVICE | gfx::MemoryUsage::HOST, UBOBloom::SIZE});
_combineUBO = _device->createBuffer({gfx::BufferUsage::UNIFORM, gfx::MemoryUsage::DEVICE | gfx::MemoryUsage::HOST, UBOBloom::SIZE});
for (int i = 0; i < MAX_BLOOM_FILTER_PASS_NUM; ++i) {
_downsampleUBO[i] = _device->createBuffer({gfx::BufferUsage::UNIFORM, gfx::MemoryUsage::DEVICE | gfx::MemoryUsage::HOST, UBOBloom::SIZE});
_upsampleUBO[i] = _device->createBuffer({gfx::BufferUsage::UNIFORM, gfx::MemoryUsage::DEVICE | gfx::MemoryUsage::HOST, UBOBloom::SIZE});
}
gfx::SamplerInfo info{
gfx::Filter::LINEAR,
gfx::Filter::LINEAR,
gfx::Filter::NONE,
gfx::Address::CLAMP,
gfx::Address::CLAMP,
gfx::Address::CLAMP,
};
_sampler = pipeline->getDevice()->getSampler(info);
}
if (hasFlag(static_cast<gfx::ClearFlags>(camera->getClearFlag()), gfx::ClearFlagBit::COLOR)) {
_clearColors[0].x = camera->getClearColor().x;
_clearColors[0].y = camera->getClearColor().y;
_clearColors[0].z = camera->getClearColor().z;
}
_clearColors[0].w = camera->getClearColor().w;
framegraph::RenderTargetAttachment::Descriptor colorAttachmentInfo;
colorAttachmentInfo.usage = framegraph::RenderTargetAttachment::Usage::COLOR;
colorAttachmentInfo.loadOp = gfx::LoadOp::CLEAR;
colorAttachmentInfo.clearColor = _clearColors[0];
colorAttachmentInfo.endAccesses = gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE;
auto insertPoint = static_cast<uint32_t>(CommonInsertPoint::DIP_BLOOM);
// prefilter pass
struct PrefilterRenderData {
framegraph::TextureHandle inputTexHandle;
framegraph::TextureHandle outputTexHandle;
gfx::Sampler *sampler;
gfx::Buffer *bloomUBO;
float textureSize[4];
};
auto *stage = static_cast<BloomStage *>(pipeline->getRenderstageByName(BLOOM_STAGE_NAME));
CC_ASSERT_NOT_NULL(stage);
int iterations = stage->getIterations();
float intensity = stage->getIntensity();
float threshold = stage->getThreshold();
_renderArea = RenderPipeline::getRenderArea(camera);
_inputAssembler = pipeline->getIAByRenderArea(_renderArea);
_renderArea.width >>= 1;
_renderArea.height >>= 1;
float shadingScale{_pipeline->getPipelineSceneData()->getShadingScale()};
auto prefilterSetup = [&](framegraph::PassNodeBuilder &builder, PrefilterRenderData &data) {
data.sampler = _sampler;
// read lightingout as input
data.inputTexHandle = framegraph::TextureHandle(
builder.readFromBlackboard(RenderPipeline::fgStrHandleOutColorTexture));
if (!data.inputTexHandle.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale);
data.inputTexHandle = builder.create(
RenderPipeline::fgStrHandleOutColorTexture, colorTexInfo);
}
data.inputTexHandle = builder.read(data.inputTexHandle);
builder.writeToBlackboard(RenderPipeline::fgStrHandleOutColorTexture, data.inputTexHandle);
// write to bloom prefilter texture
data.outputTexHandle = framegraph::TextureHandle(builder.readFromBlackboard(prefilterTexHandle));
if (!data.outputTexHandle.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale);
data.outputTexHandle = builder.create(prefilterTexHandle, colorTexInfo);
}
data.outputTexHandle = builder.write(data.outputTexHandle, colorAttachmentInfo);
builder.writeToBlackboard(prefilterTexHandle, data.outputTexHandle);
// Update threshold
data.bloomUBO = stage->getPrefilterUBO();
data.textureSize[2] = threshold;
};
auto prefilterExec = [this, camera](PrefilterRenderData const &data, const framegraph::DevicePassResourceTable &table) {
auto *pipeline = _pipeline;
gfx::RenderPass *renderPass = table.getRenderPass();
auto *cmdBf = pipeline->getCommandBuffers()[0];
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBf->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
auto *sceneData = static_cast<DeferredPipelineSceneData *>(pipeline->getPipelineSceneData());
scene::Pass *pass = sceneData->getBloomPrefilterPass();
gfx::Shader *shader = sceneData->getBloomPrefilterPassShader();
gfx::PipelineState *pso = PipelineStateManager::getOrCreatePipelineState(
pass, shader, _inputAssembler, renderPass);
CC_ASSERT_NOT_NULL(pso);
data.bloomUBO->update(data.textureSize, sizeof(data.textureSize));
pass->getDescriptorSet()->bindBuffer(0, data.bloomUBO);
pass->getDescriptorSet()->bindTexture(1, table.getRead(data.inputTexHandle));
pass->getDescriptorSet()->bindSampler(1, data.sampler);
pass->getDescriptorSet()->update();
cmdBf->bindDescriptorSet(materialSet, pass->getDescriptorSet());
cmdBf->bindPipelineState(pso);
cmdBf->bindInputAssembler(_inputAssembler);
cmdBf->draw(_inputAssembler);
};
pipeline->getFrameGraph().addPass<PrefilterRenderData>(insertPoint, prefilterPassHandle, prefilterSetup, prefilterExec);
struct ScalingSampleRenderData {
framegraph::TextureHandle inputTexHandle;
framegraph::TextureHandle outputTexHandle;
gfx::Sampler *sampler;
gfx::Buffer *bloomUBO;
float textureSize[4];
int index;
};
// downsample pass
for (int i = 0; i < iterations; ++i) {
_renderArea.width >>= 1;
_renderArea.height >>= 1;
auto downsampleSetup = [&, i](framegraph::PassNodeBuilder &builder, ScalingSampleRenderData &data) {
data.sampler = _sampler;
data.index = i;
// read from prefilter texture or last downsample texture
if (data.index == 0) {
data.inputTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(prefilterTexHandle)));
builder.writeToBlackboard(prefilterTexHandle, data.inputTexHandle);
} else {
data.inputTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(downsampleTexHandles[data.index - 1])));
builder.writeToBlackboard(downsampleTexHandles[data.index - 1], data.inputTexHandle);
}
// write to downsample texture
data.outputTexHandle = framegraph::TextureHandle(
builder.readFromBlackboard(downsampleTexHandles[data.index]));
if (!data.outputTexHandle.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale);
data.outputTexHandle = builder.create(downsampleTexHandles[data.index], colorTexInfo);
}
data.outputTexHandle = builder.write(data.outputTexHandle, colorAttachmentInfo);
builder.writeToBlackboard(downsampleTexHandles[data.index], data.outputTexHandle);
// Update cc_textureSize
data.bloomUBO = stage->getDownsampleUBO()[data.index];
data.textureSize[0] = static_cast<float>(static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale) << 1);
data.textureSize[1] = static_cast<float>(static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale) << 1);
};
auto downsampleExec = [this, camera](ScalingSampleRenderData const &data, const framegraph::DevicePassResourceTable &table) {
auto *pipeline = _pipeline;
gfx::RenderPass *renderPass = table.getRenderPass();
auto *cmdBf = pipeline->getCommandBuffers()[0];
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBf->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
auto *const sceneData = static_cast<DeferredPipelineSceneData *>(pipeline->getPipelineSceneData());
scene::Pass *pass = sceneData->getBloomDownSamplePasses()[data.index];
gfx::Shader *shader = sceneData->getBloomDownSamplePassShader();
gfx::PipelineState *pso = PipelineStateManager::getOrCreatePipelineState(
pass, shader, _inputAssembler, renderPass);
CC_ASSERT_NOT_NULL(pso);
data.bloomUBO->update(data.textureSize, sizeof(data.textureSize));
pass->getDescriptorSet()->bindBuffer(0, data.bloomUBO);
pass->getDescriptorSet()->bindTexture(1, table.getRead(data.inputTexHandle));
pass->getDescriptorSet()->bindSampler(1, data.sampler);
pass->getDescriptorSet()->update();
cmdBf->bindDescriptorSet(materialSet, pass->getDescriptorSet());
cmdBf->bindPipelineState(pso);
cmdBf->bindInputAssembler(_inputAssembler);
cmdBf->draw(_inputAssembler);
};
pipeline->getFrameGraph().addPass<ScalingSampleRenderData>(++insertPoint, downsamplePassHandles[i], downsampleSetup, downsampleExec);
}
// upsample pass
for (int i = 0; i < iterations; ++i) {
_renderArea.width <<= 1;
_renderArea.height <<= 1;
auto upsampleSetup = [&, i](framegraph::PassNodeBuilder &builder, ScalingSampleRenderData &data) {
data.index = i;
data.sampler = _sampler;
// read from last downsample texture or last upsample texture
if (data.index == 0) {
data.inputTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(downsampleTexHandles[iterations - 1])));
builder.writeToBlackboard(downsampleTexHandles[iterations - 1], data.inputTexHandle);
} else {
data.inputTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(upsampleTexHandles[data.index - 1])));
builder.writeToBlackboard(upsampleTexHandles[data.index - 1], data.inputTexHandle);
}
// write to downsample texture
data.outputTexHandle = framegraph::TextureHandle(
builder.readFromBlackboard(upsampleTexHandles[data.index]));
if (!data.outputTexHandle.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale);
data.outputTexHandle = builder.create(
upsampleTexHandles[data.index], colorTexInfo);
}
data.outputTexHandle = builder.write(data.outputTexHandle, colorAttachmentInfo);
builder.writeToBlackboard(upsampleTexHandles[data.index], data.outputTexHandle);
// Update cc_textureSize
data.bloomUBO = stage->getUpsampleUBO()[data.index];
data.textureSize[0] = static_cast<float>(static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale) >> 1);
data.textureSize[1] = static_cast<float>(static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale) >> 1);
};
auto upsampleExec = [this, camera](ScalingSampleRenderData const &data, const framegraph::DevicePassResourceTable &table) {
auto *pipeline = _pipeline;
gfx::RenderPass *renderPass = table.getRenderPass();
auto *cmdBf = pipeline->getCommandBuffers()[0];
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBf->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
auto *const sceneData = static_cast<DeferredPipelineSceneData *>(pipeline->getPipelineSceneData());
scene::Pass *pass = sceneData->getBloomUpSamplePasses()[data.index];
gfx::Shader *shader = sceneData->getBloomUpSamplePassShader();
gfx::PipelineState *pso = PipelineStateManager::getOrCreatePipelineState(
pass, shader, _inputAssembler, renderPass);
CC_ASSERT_NOT_NULL(pso);
data.bloomUBO->update(data.textureSize, sizeof(data.textureSize));
pass->getDescriptorSet()->bindBuffer(0, data.bloomUBO);
pass->getDescriptorSet()->bindTexture(1, table.getRead(data.inputTexHandle));
pass->getDescriptorSet()->bindSampler(1, data.sampler);
pass->getDescriptorSet()->update();
cmdBf->bindDescriptorSet(materialSet, pass->getDescriptorSet());
cmdBf->bindPipelineState(pso);
cmdBf->bindInputAssembler(_inputAssembler);
cmdBf->draw(_inputAssembler);
};
pipeline->getFrameGraph().addPass<ScalingSampleRenderData>(++insertPoint, upsamplePassHandles[i], upsampleSetup, upsampleExec);
}
// combine pass
struct CombineRenderData {
framegraph::TextureHandle lightingOutTexHandle;
framegraph::TextureHandle upsampleTexHandle;
framegraph::TextureHandle bloomOutTexHandle;
gfx::Sampler *sampler;
gfx::Buffer *bloomUBO;
float textureSize[4];
};
_renderArea.width <<= 1;
_renderArea.height <<= 1;
auto combineSetup = [&](framegraph::PassNodeBuilder &builder, CombineRenderData &data) {
data.sampler = _sampler;
// read lighting result or last upsample texture
data.lightingOutTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(RenderPipeline::fgStrHandleOutColorTexture)));
builder.writeToBlackboard(RenderPipeline::fgStrHandleOutColorTexture, data.lightingOutTexHandle);
data.upsampleTexHandle = builder.read(framegraph::TextureHandle(
builder.readFromBlackboard(upsampleTexHandles[iterations - 1])));
builder.writeToBlackboard(upsampleTexHandles[iterations - 1], data.upsampleTexHandle);
// write to bloomOut texture
data.bloomOutTexHandle = framegraph::TextureHandle(
builder.readFromBlackboard(RenderPipeline::fgStrHandleBloomOutTexture));
if (!data.bloomOutTexHandle.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(_renderArea.width) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(_renderArea.height) * shadingScale);
data.bloomOutTexHandle = builder.create(
RenderPipeline::fgStrHandleBloomOutTexture, colorTexInfo);
}
data.bloomOutTexHandle = builder.write(data.bloomOutTexHandle, colorAttachmentInfo);
builder.writeToBlackboard(RenderPipeline::fgStrHandleBloomOutTexture, data.bloomOutTexHandle);
// Update intensity
data.bloomUBO = stage->getCombineUBO();
data.textureSize[3] = intensity;
};
auto combineExec = [this, camera](CombineRenderData const &data, const framegraph::DevicePassResourceTable &table) {
auto *pipeline = _pipeline;
gfx::RenderPass *renderPass = table.getRenderPass();
auto *cmdBf = pipeline->getCommandBuffers()[0];
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBf->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
auto *const sceneData = static_cast<DeferredPipelineSceneData *>(pipeline->getPipelineSceneData());
scene::Pass *pass = sceneData->getBloomCombinePass();
gfx::Shader *shader = sceneData->getBloomCombinePassShader();
gfx::PipelineState *pso = PipelineStateManager::getOrCreatePipelineState(
pass, shader, _inputAssembler, renderPass);
CC_ASSERT_NOT_NULL(pso);
data.bloomUBO->update(data.textureSize, sizeof(data.textureSize));
pass->getDescriptorSet()->bindBuffer(0, data.bloomUBO);
pass->getDescriptorSet()->bindTexture(1, table.getRead(data.lightingOutTexHandle));
pass->getDescriptorSet()->bindTexture(2, table.getRead(data.upsampleTexHandle));
pass->getDescriptorSet()->bindSampler(1, data.sampler);
pass->getDescriptorSet()->bindSampler(2, data.sampler);
pass->getDescriptorSet()->update();
cmdBf->bindDescriptorSet(materialSet, pass->getDescriptorSet());
cmdBf->bindPipelineState(pso);
cmdBf->bindInputAssembler(_inputAssembler);
cmdBf->draw(_inputAssembler);
};
pipeline->getFrameGraph().addPass<CombineRenderData>(++insertPoint, combinePassHandle, combineSetup, combineExec);
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,88 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "../RenderStage.h"
#include "base/std/container/array.h"
#include "frame-graph/Handle.h"
#include "pipeline/Enum.h"
#define MAX_BLOOM_FILTER_PASS_NUM 6
namespace cc {
namespace pipeline {
struct CC_DLL UBOBloom {
static constexpr uint32_t TEXTURE_SIZE_OFFSET = 0;
static constexpr uint32_t COUNT = UBOBloom::TEXTURE_SIZE_OFFSET + 4;
static constexpr uint32_t SIZE = UBOBloom::COUNT * 4;
};
class CC_DLL BloomStage : public RenderStage {
public:
using SampleUBOArray = ccstd::array<gfx::Buffer *, MAX_BLOOM_FILTER_PASS_NUM>;
BloomStage();
~BloomStage() override = default;
static const RenderStageInfo &getInitializeInfo();
bool initialize(const RenderStageInfo &info) override;
void activate(RenderPipeline *pipeline, RenderFlow *flow) override;
void destroy() override;
void render(scene::Camera *camera) override;
gfx::Buffer *getPrefilterUBO() { return _prefilterUBO; }
SampleUBOArray &getDownsampleUBO() { return _downsampleUBO; }
SampleUBOArray &getUpsampleUBO() { return _upsampleUBO; }
gfx::Buffer *getCombineUBO() { return _combineUBO; }
gfx::Sampler *getSampler() const { return _sampler; }
inline float getThreshold() const { return _threshold; }
inline void setThreshold(float value) { _threshold = value; }
inline float getIntensity() const { return _intensity; }
inline void setIntensity(float value) { _intensity = value; }
inline int getIterations() const { return _iterations; }
inline void setIterations(int value) {
_iterations = std::max(1, std::min(value, MAX_BLOOM_FILTER_PASS_NUM));
}
private:
uint32_t _phaseID = 0;
static RenderStageInfo initInfo;
float _threshold = 1.0F;
float _intensity = 0.8F;
int _iterations = 2;
gfx::Sampler *_sampler = nullptr;
gfx::Buffer *_prefilterUBO = nullptr;
SampleUBOArray _downsampleUBO{};
SampleUBOArray _upsampleUBO{};
gfx::Buffer *_combineUBO = nullptr;
framegraph::StringHandle _fgStrHandleBloomOut;
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,221 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "DeferredPipeline.h"
#include "../GlobalDescriptorSetManager.h"
#include "../PipelineUBO.h"
#include "../RenderPipeline.h"
#include "../SceneCulling.h"
#include "../helper/Utils.h"
#include "../shadow/ShadowFlow.h"
#include "DeferredPipelineSceneData.h"
#include "MainFlow.h"
#include "gfx-base/GFXBuffer.h"
#include "gfx-base/GFXCommandBuffer.h"
#include "gfx-base/GFXDef.h"
#include "gfx-base/GFXDescriptorSet.h"
#include "gfx-base/GFXDevice.h"
#include "gfx-base/GFXSwapchain.h"
#include "gfx-base/states/GFXTextureBarrier.h"
#include "pipeline/ClusterLightCulling.h"
#include "profiler/Profiler.h"
#include "scene/RenderWindow.h"
namespace cc {
namespace pipeline {
#define TO_VEC3(dst, src, offset) \
dst[offset] = (src).x; \
(dst)[(offset) + 1] = (src).y; \
(dst)[(offset) + 2] = (src).z;
#define TO_VEC4(dst, src, offset) \
dst[offset] = (src).x; \
(dst)[(offset) + 1] = (src).y; \
(dst)[(offset) + 2] = (src).z; \
(dst)[(offset) + 3] = (src).w;
DeferredPipeline::DeferredPipeline() {
_pipelineSceneData = ccnew DeferredPipelineSceneData();
}
DeferredPipeline::~DeferredPipeline() = default;
framegraph::StringHandle DeferredPipeline::fgStrHandleGbufferTexture[GBUFFER_COUNT] = {
framegraph::FrameGraph::stringToHandle("gbufferAlbedoTexture"),
framegraph::FrameGraph::stringToHandle("gbufferNormalTexture"),
framegraph::FrameGraph::stringToHandle("gbufferEmissiveTexture")};
framegraph::StringHandle DeferredPipeline::fgStrHandleGbufferPass = framegraph::FrameGraph::stringToHandle("deferredGbufferPass");
framegraph::StringHandle DeferredPipeline::fgStrHandleLightingPass = framegraph::FrameGraph::stringToHandle("deferredLightingPass");
framegraph::StringHandle DeferredPipeline::fgStrHandleTransparentPass = framegraph::FrameGraph::stringToHandle("deferredTransparentPass");
framegraph::StringHandle DeferredPipeline::fgStrHandleSsprPass = framegraph::FrameGraph::stringToHandle("deferredSSPRPass");
bool DeferredPipeline::initialize(const RenderPipelineInfo &info) {
RenderPipeline::initialize(info);
if (_flows.empty()) {
auto *shadowFlow = ccnew ShadowFlow;
shadowFlow->initialize(ShadowFlow::getInitializeInfo());
_flows.emplace_back(shadowFlow);
auto *mainFlow = ccnew MainFlow;
mainFlow->initialize(MainFlow::getInitializeInfo());
_flows.emplace_back(mainFlow);
}
return true;
}
bool DeferredPipeline::activate(gfx::Swapchain *swapchain) {
_macros["CC_PIPELINE_TYPE"] = 1;
if (!RenderPipeline::activate(swapchain)) {
CC_LOG_ERROR("RenderPipeline active failed.");
return false;
}
if (!activeRenderer(swapchain)) {
CC_LOG_ERROR("DeferredPipeline startup failed!");
return false;
}
return true;
}
void DeferredPipeline::render(const ccstd::vector<scene::Camera *> &cameras) {
CC_PROFILE(DeferredPipelineRender);
#if CC_USE_GEOMETRY_RENDERER
updateGeometryRenderer(cameras); // for capability
#endif
auto *device = gfx::Device::getInstance();
bool enableOcclusionQuery = isOcclusionQueryEnabled();
if (enableOcclusionQuery) {
device->getQueryPoolResults(_queryPools[0]);
}
_commandBuffers[0]->begin();
if (enableOcclusionQuery) {
_commandBuffers[0]->resetQueryPool(_queryPools[0]);
}
_pipelineUBO->updateMultiCameraUBO(_globalDSManager, cameras);
_pipelineUBO->updateGlobalUBO(cameras[0]);
ensureEnoughSize(cameras);
decideProfilerCamera(cameras);
for (auto *camera : cameras) {
sceneCulling(this, camera);
if (_clusterEnabled) {
_clusterComp->clusterLightCulling(camera);
}
for (auto const &flow : _flows) {
flow->render(camera);
}
_fg.compile();
_fg.execute();
_fg.reset();
_pipelineUBO->incCameraUBOOffset();
}
if (enableOcclusionQuery) {
_commandBuffers[0]->completeQueryPool(_queryPools[0]);
}
_commandBuffers[0]->end();
_device->flushCommands(_commandBuffers);
_device->getQueue()->submit(_commandBuffers);
RenderPipeline::framegraphGC();
}
void DeferredPipeline::onGlobalPipelineStateChanged() {
_pipelineSceneData->updatePipelineSceneData();
}
bool DeferredPipeline::activeRenderer(gfx::Swapchain *swapchain) {
_commandBuffers.push_back(_device->getCommandBuffer());
_queryPools.push_back(_device->getQueryPool());
gfx::Sampler *const sampler = getGlobalDSManager()->getPointSampler();
// Main light sampler binding
_descriptorSet->bindSampler(SHADOWMAP::BINDING, sampler);
_descriptorSet->bindSampler(SPOTSHADOWMAP::BINDING, sampler);
_descriptorSet->update();
// update global defines when all states initialized.
_macros["CC_USE_HDR"] = static_cast<bool>(_pipelineSceneData->isHDR());
_macros["CC_SUPPORT_FLOAT_TEXTURE"] = hasAnyFlags(_device->getFormatFeatures(gfx::Format::RGBA32F), gfx::FormatFeature::RENDER_TARGET | gfx::FormatFeature::SAMPLED_TEXTURE);
// step 2 create index buffer
uint32_t ibStride = 4;
uint32_t ibSize = ibStride * 6;
if (_quadIB == nullptr) {
_quadIB = _device->createBuffer({gfx::BufferUsageBit::INDEX | gfx::BufferUsageBit::TRANSFER_DST,
gfx::MemoryUsageBit::DEVICE, ibSize, ibStride});
}
if (_quadIB == nullptr) {
return false;
}
unsigned int ibData[] = {0, 1, 2, 1, 3, 2};
_quadIB->update(ibData, sizeof(ibData));
_width = swapchain->getWidth();
_height = swapchain->getHeight();
if (_clusterEnabled) {
// cluster component resource
_clusterComp = ccnew ClusterLightCulling(this);
_clusterComp->initialize(this->getDevice());
}
return true;
}
bool DeferredPipeline::destroy() {
destroyQuadInputAssembler();
for (auto &it : _renderPasses) {
it.second->destroy();
}
_renderPasses.clear();
_queryPools.clear();
_commandBuffers.clear();
CC_SAFE_DELETE(_clusterComp);
return RenderPipeline::destroy();
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,84 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "base/std/container/array.h"
#include "base/std/container/unordered_map.h"
#include "pipeline/Enum.h"
#include "pipeline/RenderPipeline.h"
namespace cc {
namespace pipeline {
class ClusterLightCulling;
struct UBOGlobal;
struct UBOCamera;
struct UBOShadow;
class CC_DLL DeferredPipeline : public RenderPipeline {
public:
DeferredPipeline();
~DeferredPipeline() override;
bool initialize(const RenderPipelineInfo &info) override;
bool destroy() override;
bool activate(gfx::Swapchain *swapchain) override;
void render(const ccstd::vector<scene::Camera *> &cameras) override;
void onGlobalPipelineStateChanged() override;
inline gfx::Buffer *getLightsUBO() const { return _lightsUBO; }
inline const LightList &getValidLights() const { return _validLights; }
inline const gfx::BufferList &getLightBuffers() const { return _lightBuffers; }
inline const UintList &getLightIndexOffsets() const { return _lightIndexOffsets; }
inline const UintList &getLightIndices() const { return _lightIndices; }
private:
bool activeRenderer(gfx::Swapchain *swapchain);
gfx::Buffer *_lightsUBO = nullptr;
LightList _validLights;
gfx::BufferList _lightBuffers;
UintList _lightIndexOffsets;
UintList _lightIndices;
ClusterLightCulling *_clusterComp{nullptr};
public:
static constexpr uint32_t GBUFFER_COUNT = 3;
// deferred resource names
static framegraph::StringHandle fgStrHandleGbufferTexture[GBUFFER_COUNT];
// deferred pass names
static framegraph::StringHandle fgStrHandleGbufferPass;
static framegraph::StringHandle fgStrHandleLightingPass;
static framegraph::StringHandle fgStrHandleTransparentPass;
static framegraph::StringHandle fgStrHandleSsprPass;
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,177 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "renderer/pipeline/deferred/DeferredPipelineSceneData.h"
#include "renderer/pipeline/RenderPipeline.h"
#include "renderer/pipeline/deferred/BloomStage.h"
#include "scene/Pass.h"
#include "scene/Shadow.h"
namespace cc {
namespace pipeline {
DeferredPipelineSceneData::DeferredPipelineSceneData() = default;
DeferredPipelineSceneData::~DeferredPipelineSceneData() = default;
void DeferredPipelineSceneData::activate(gfx::Device *device) {
PipelineSceneData::activate(device);
initPipelinePassInfo();
}
void DeferredPipelineSceneData::updatePipelineSceneData() {
updatePipelinePassInfo();
}
void DeferredPipelineSceneData::initPipelinePassInfo() {
// builtin deferred material
_lightingMaterial = ccnew Material();
_lightingMaterial->setUuid("builtin-deferred-material");
IMaterialInfo materialInfo;
materialInfo.effectName = "pipeline/deferred-lighting";
_lightingMaterial->initialize(materialInfo);
for (const auto &pass : *_lightingMaterial->getPasses()) {
pass->tryCompile();
}
_bloomMaterial = ccnew Material();
_bloomMaterial->setUuid("builtin-bloom-material");
materialInfo.effectName = "pipeline/bloom";
_bloomMaterial->initialize(materialInfo);
for (const auto &pass : *_bloomMaterial->getPasses()) {
pass->tryCompile();
}
_postProcessMaterial = ccnew Material();
_postProcessMaterial->setUuid("builtin-post-process-material");
#if ENABLE_ANTIALIAS_FXAA > 0
_antiAliasing = AntiAliasing::FXAA;
#endif
materialInfo.effectName = "pipeline/post-process";
MacroRecord record{{"ANTIALIAS_TYPE", static_cast<int32_t>(_antiAliasing)}};
materialInfo.defines = record;
_postProcessMaterial->initialize(materialInfo);
for (const auto &pass : *_postProcessMaterial->getPasses()) {
pass->tryCompile();
}
updatePipelinePassInfo();
}
void DeferredPipelineSceneData::setAntiAliasing(AntiAliasing value) {
_antiAliasing = value;
if (_postProcessMaterial) {
auto &defines = (*_postProcessMaterial->getPasses())[0]->getDefines();
defines.emplace("ANTIALIAS_TYPE", static_cast<int32_t>(value));
auto *renderMat = ccnew Material();
IMaterialInfo materialInfo;
materialInfo.effectAsset = _postProcessMaterial->getEffectAsset();
materialInfo.defines = defines;
renderMat->initialize(materialInfo);
for (const auto &pass : *renderMat->getPasses()) {
pass->tryCompile();
}
_postProcessMaterial = renderMat;
}
}
void DeferredPipelineSceneData::updateBloomPass() {
if (!_bloomMaterial) {
return;
}
auto &bloomPasses = *_bloomMaterial->getPasses();
_bloomPrefilterPass = bloomPasses[BLOOM_PREFILTERPASS_INDEX];
_bloomPrefilterPass->beginChangeStatesSilently();
_bloomPrefilterPass->tryCompile();
_bloomPrefilterPass->endChangeStatesSilently();
_bloomPrefilterPassShader = _bloomPrefilterPass->getShaderVariant();
for (uint32_t i = 0; i < MAX_BLOOM_FILTER_PASS_NUM; ++i) {
scene::Pass *downsamplePass = bloomPasses[BLOOM_DOWNSAMPLEPASS_INDEX + i];
downsamplePass->beginChangeStatesSilently();
downsamplePass->tryCompile();
downsamplePass->endChangeStatesSilently();
scene::Pass *upsamplePass = bloomPasses[BLOOM_UPSAMPLEPASS_INDEX + i];
upsamplePass->beginChangeStatesSilently();
upsamplePass->tryCompile();
upsamplePass->endChangeStatesSilently();
_bloomUpSamplePasses.emplace_back(upsamplePass);
_bloomDownSamplePasses.emplace_back(downsamplePass);
}
_bloomCombinePass = bloomPasses[BLOOM_COMBINEPASS_INDEX];
_bloomCombinePass->beginChangeStatesSilently();
_bloomCombinePass->tryCompile();
_bloomCombinePass->endChangeStatesSilently();
_bloomCombinePassShader = _bloomCombinePass->getShaderVariant();
_bloomUpSamplePassShader = bloomPasses[BLOOM_UPSAMPLEPASS_INDEX]->getShaderVariant();
_bloomDownSamplePassShader = bloomPasses[BLOOM_DOWNSAMPLEPASS_INDEX]->getShaderVariant();
}
void DeferredPipelineSceneData::updatePostProcessPass() {
if (!_postProcessMaterial) {
return;
}
_postPass = (*_postProcessMaterial->getPasses())[0];
_postPass->beginChangeStatesSilently();
_postPass->tryCompile();
_postPass->endChangeStatesSilently();
_postPassShader = _postPass->getShaderVariant();
}
void DeferredPipelineSceneData::updatePipelinePassInfo() {
updateBloomPass();
updatePostProcessPass();
updateDeferredPassInfo();
}
void DeferredPipelineSceneData::updateDeferredPassInfo() {
updateDeferredLightPass();
}
void DeferredPipelineSceneData::updateDeferredLightPass() {
if (!_lightingMaterial) {
return;
}
// It's temporary solution for main light shadowmap
if (RenderPipeline::getInstance()) {
RenderPipeline::getInstance()->setValue("CC_RECEIVE_SHADOW", 1);
}
_lightPass = (*_lightingMaterial->getPasses())[0];
_lightPass->beginChangeStatesSilently();
_lightPass->tryCompile();
_lightPass->endChangeStatesSilently();
_lightPassShader = _lightPass->getShaderVariant();
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,119 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "base/Ptr.h"
#include "renderer/pipeline/PipelineSceneData.h"
#define BLOOM_PREFILTERPASS_INDEX 0
#define BLOOM_DOWNSAMPLEPASS_INDEX 1
#define BLOOM_UPSAMPLEPASS_INDEX (BLOOM_DOWNSAMPLEPASS_INDEX + 1)
#define BLOOM_COMBINEPASS_INDEX (BLOOM_UPSAMPLEPASS_INDEX + 1)
namespace cc {
namespace pipeline {
enum class AntiAliasing {
NONE,
FXAA
};
class DeferredPipelineSceneData : public PipelineSceneData {
public:
DeferredPipelineSceneData();
~DeferredPipelineSceneData() override;
void activate(gfx::Device *device) override;
void updatePipelineSceneData() override;
void initPipelinePassInfo();
void setAntiAliasing(AntiAliasing value);
inline AntiAliasing getAntiAliasing() const { return _antiAliasing; }
inline Material *getBloomMaterial() const { return _bloomMaterial; }
inline void setBloomMaterial(Material *mat) {
if (mat == _bloomMaterial.get()) {
return;
}
_bloomMaterial = mat;
updatePipelinePassInfo();
}
inline Material *getPostProcessMaterial() const { return _postProcessMaterial; }
inline void setPostProcessMaterial(Material *mat) {
if (mat == _postProcessMaterial.get()) {
return;
}
_postProcessMaterial = mat;
updatePipelinePassInfo();
}
inline void setLightingMaterial(Material *mat) { _lightingMaterial = mat; }
inline Material *getLightingMaterial() const { return _lightingMaterial; }
inline gfx::Shader *getLightPassShader() const { return _lightPassShader; }
inline scene::Pass *getLightPass() const { return _lightPass; }
inline gfx::Shader *getBloomPrefilterPassShader() const { return _bloomPrefilterPassShader; }
inline scene::Pass *getBloomPrefilterPass() const { return _bloomPrefilterPass; }
inline const ccstd::vector<scene::Pass *> &getBloomUpSamplePasses() const { return _bloomUpSamplePasses; }
inline gfx::Shader *getBloomUpSamplePassShader() const { return _bloomUpSamplePassShader; }
inline const ccstd::vector<scene::Pass *> &getBloomDownSamplePasses() const { return _bloomDownSamplePasses; }
inline gfx::Shader *getBloomDownSamplePassShader() const { return _bloomDownSamplePassShader; }
inline scene::Pass *getBloomCombinePass() const { return _bloomCombinePass; }
inline gfx::Shader *getBloomCombinePassShader() const { return _bloomCombinePassShader; }
inline gfx::Shader *getPostPassShader() const { return _postPassShader; }
inline scene::Pass *getPostPass() const { return _postPass; }
private:
void updateBloomPass();
void updatePostProcessPass();
void updatePipelinePassInfo();
void updateDeferredPassInfo();
void updateDeferredLightPass();
IntrusivePtr<Material> _postProcessMaterial;
gfx::Shader *_postPassShader{nullptr}; // weak reference
scene::Pass *_postPass{nullptr}; // weak reference
IntrusivePtr<Material> _lightingMaterial;
gfx::Shader *_lightPassShader{nullptr}; // weak reference
scene::Pass *_lightPass{nullptr}; // weak reference
IntrusivePtr<Material> _bloomMaterial;
scene::Pass *_bloomPrefilterPass{nullptr}; // weak reference
gfx::Shader *_bloomPrefilterPassShader{nullptr}; // weak reference
scene::Pass *_bloomCombinePass{nullptr}; // weak reference
gfx::Shader *_bloomCombinePassShader{nullptr}; // weak reference
ccstd::vector<scene::Pass *> _bloomUpSamplePasses; // weak reference
gfx::Shader *_bloomUpSamplePassShader{nullptr}; // weak reference
ccstd::vector<scene::Pass *> _bloomDownSamplePasses; // weak reference
gfx::Shader *_bloomDownSamplePassShader{nullptr}; // weak reference
AntiAliasing _antiAliasing{AntiAliasing::NONE};
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,235 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "GbufferStage.h"
#include "../InstancedBuffer.h"
#include "../PipelineSceneData.h"
#include "../PipelineUBO.h"
#include "../PlanarShadowQueue.h"
#include "../RenderInstancedQueue.h"
#include "../RenderQueue.h"
#include "DeferredPipeline.h"
#include "MainFlow.h"
#include "frame-graph/DevicePass.h"
#include "frame-graph/DevicePassResourceTable.h"
#include "frame-graph/Resource.h"
#include "gfx-base/GFXDevice.h"
#include "profiler/Profiler.h"
#include "scene/Camera.h"
#include "scene/Model.h"
namespace cc {
namespace pipeline {
RenderStageInfo GbufferStage::initInfo = {
"GbufferStage",
static_cast<uint32_t>(DeferredStagePriority::GBUFFER),
static_cast<uint32_t>(RenderFlowTag::SCENE),
{ccnew RenderQueueDesc{false, RenderQueueSortMode::FRONT_TO_BACK, {"default"}},
ccnew RenderQueueDesc{true, RenderQueueSortMode::BACK_TO_FRONT, {"default", "planarShadow"}}}};
const RenderStageInfo &GbufferStage::getInitializeInfo() { return GbufferStage::initInfo; }
GbufferStage::GbufferStage() {
_instancedQueue = ccnew RenderInstancedQueue;
}
GbufferStage::~GbufferStage() = default;
bool GbufferStage::initialize(const RenderStageInfo &info) {
RenderStage::initialize(info);
_renderQueueDescriptors = info.renderQueues;
_phaseID = getPhaseID("default");
return true;
}
void GbufferStage::activate(RenderPipeline *pipeline, RenderFlow *flow) {
RenderStage::activate(pipeline, flow);
for (const auto &descriptor : _renderQueueDescriptors) {
uint32_t phase = convertPhase(descriptor->stages);
RenderQueueSortFunc sortFunc = convertQueueSortFunc(descriptor->sortMode);
RenderQueueCreateInfo info = {descriptor->isTransparent, phase, sortFunc};
_renderQueues.emplace_back(ccnew RenderQueue(_pipeline, std::move(info), true));
}
_planarShadowQueue = ccnew PlanarShadowQueue(_pipeline);
}
void GbufferStage::destroy() {
CC_SAFE_DELETE(_instancedQueue);
CC_SAFE_DESTROY_AND_DELETE(_planarShadowQueue);
RenderStage::destroy();
}
void GbufferStage::dispenseRenderObject2Queues() {
_instancedQueue->clear();
const auto &renderObjects = _pipeline->getPipelineSceneData()->getRenderObjects();
for (auto *queue : _renderQueues) {
queue->clear();
}
uint32_t subModelIdx = 0;
uint32_t passIdx = 0;
size_t k = 0;
for (auto ro : renderObjects) {
const auto *const model = ro.model;
const auto &subModels = model->getSubModels();
const auto subModelCount = subModels.size();
for (subModelIdx = 0; subModelIdx < subModelCount; ++subModelIdx) {
const auto &subModel = subModels[subModelIdx];
const auto &passes = *(subModel->getPasses());
const auto passCount = passes.size();
for (passIdx = 0; passIdx < passCount; ++passIdx) {
const auto &pass = passes[passIdx];
if (pass->getPhase() != _phaseID) continue;
if (pass->getBatchingScheme() == scene::BatchingSchemes::INSTANCING) {
auto *instancedBuffer = pass->getInstancedBuffer();
instancedBuffer->merge(subModel, passIdx);
_instancedQueue->add(instancedBuffer);
} else {
for (k = 0; k < _renderQueues.size(); k++) {
_renderQueues[k]->insertRenderPass(ro, subModelIdx, passIdx);
}
}
}
}
}
for (auto *queue : _renderQueues) {
queue->sort();
}
}
void GbufferStage::recordCommands(DeferredPipeline *pipeline, scene::Camera *camera, gfx::RenderPass *renderPass) {
auto *cmdBuff = pipeline->getCommandBuffers()[0];
// DescriptorSet bindings
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBuff->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
// record commands
_renderQueues[0]->recordCommandBuffer(_device, camera, renderPass, cmdBuff);
_instancedQueue->recordCommandBuffer(_device, renderPass, cmdBuff);
}
void GbufferStage::render(scene::Camera *camera) {
CC_PROFILE(GbufferStageRender);
struct RenderData {
framegraph::TextureHandle gbuffer[4];
framegraph::TextureHandle depth;
};
auto *pipeline = static_cast<DeferredPipeline *>(_pipeline);
float shadingScale{_pipeline->getPipelineSceneData()->getShadingScale()};
_renderArea = RenderPipeline::getRenderArea(camera);
auto gbufferSetup = [&](framegraph::PassNodeBuilder &builder, RenderData &data) {
builder.subpass();
// gbuffer setup
gfx::TextureInfo gbufferInfo = {
gfx::TextureType::TEX2D,
gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::INPUT_ATTACHMENT,
gfx::Format::RGBA8,
static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale),
static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale),
};
gfx::TextureInfo gbufferInfoFloat = {
gfx::TextureType::TEX2D,
gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::INPUT_ATTACHMENT,
gfx::Format::RGBA16F,
static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale),
static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale),
};
for (int i = 0; i < DeferredPipeline::GBUFFER_COUNT - 1; ++i) {
if (i != 0) { // normals need more precision
data.gbuffer[i] = builder.create(DeferredPipeline::fgStrHandleGbufferTexture[i], gbufferInfoFloat);
} else {
data.gbuffer[i] = builder.create(DeferredPipeline::fgStrHandleGbufferTexture[i], gbufferInfo);
}
}
auto subpassEnabled = _device->hasFeature(gfx::Feature::INPUT_ATTACHMENT_BENEFIT);
if (subpassEnabled) {
// when subpass enabled, the color result (gles2/gles3) will write to gbuffer[2] and the blit to color texture, so the format should be RGBA16F
data.gbuffer[2] = builder.create(DeferredPipeline::fgStrHandleGbufferTexture[2], gbufferInfoFloat);
} else {
data.gbuffer[2] = builder.create(DeferredPipeline::fgStrHandleGbufferTexture[2], gbufferInfo);
}
gfx::Color clearColor{0.0, 0.0, 0.0, 0.0};
framegraph::RenderTargetAttachment::Descriptor colorInfo;
colorInfo.usage = framegraph::RenderTargetAttachment::Usage::COLOR;
colorInfo.loadOp = gfx::LoadOp::CLEAR;
colorInfo.clearColor = clearColor;
colorInfo.beginAccesses = gfx::AccessFlagBit::FRAGMENT_SHADER_READ_COLOR_INPUT_ATTACHMENT;
colorInfo.endAccesses = gfx::AccessFlagBit::FRAGMENT_SHADER_READ_COLOR_INPUT_ATTACHMENT;
for (int i = 0; i < DeferredPipeline::GBUFFER_COUNT; ++i) {
data.gbuffer[i] = builder.write(data.gbuffer[i], colorInfo);
builder.writeToBlackboard(DeferredPipeline::fgStrHandleGbufferTexture[i], data.gbuffer[i]);
}
// depth setup
gfx::TextureInfo depthTexInfo = {
gfx::TextureType::TEX2D,
gfx::TextureUsageBit::DEPTH_STENCIL_ATTACHMENT | gfx::TextureUsageBit::SAMPLED,
gfx::Format::DEPTH_STENCIL,
static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale),
static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale),
};
depthTexInfo.usage |= gfx::TextureUsageBit::INPUT_ATTACHMENT;
data.depth = builder.create(DeferredPipeline::fgStrHandleOutDepthTexture, depthTexInfo);
framegraph::RenderTargetAttachment::Descriptor depthInfo;
depthInfo.usage = framegraph::RenderTargetAttachment::Usage::DEPTH_STENCIL;
depthInfo.loadOp = gfx::LoadOp::CLEAR;
depthInfo.clearDepth = camera->getClearDepth();
depthInfo.clearStencil = camera->getClearStencil();
depthInfo.endAccesses = gfx::AccessFlagBit::DEPTH_STENCIL_ATTACHMENT_WRITE;
data.depth = builder.write(data.depth, depthInfo);
builder.writeToBlackboard(DeferredPipeline::fgStrHandleOutDepthTexture, data.depth);
// viewport setup
builder.setViewport(pipeline->getViewport(camera), pipeline->getScissor(camera));
};
auto gbufferExec = [this, camera](const RenderData & /*data*/, const framegraph::DevicePassResourceTable &table) {
recordCommands(static_cast<DeferredPipeline *>(_pipeline), camera, table.getRenderPass());
};
// Command 'updateBuffer' must be recorded outside render passes, cannot put them in execute lambda
dispenseRenderObject2Queues();
auto *cmdBuff = pipeline->getCommandBuffers()[0];
_instancedQueue->uploadBuffers(cmdBuff);
// if empty == true, gbuffer and lightig passes will be ignored
bool empty = _renderQueues[0]->empty() && _instancedQueue->empty();
if (!empty) {
pipeline->getFrameGraph().addPass<RenderData>(static_cast<uint32_t>(DeferredInsertPoint::DIP_GBUFFER), DeferredPipeline::fgStrHandleGbufferPass, gbufferSetup, gbufferExec);
}
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,67 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "pipeline/RenderStage.h"
namespace cc {
namespace scene {
class Camera;
}
namespace pipeline {
class RenderFlow;
class RenderInstancedQueue;
class RenderAdditiveLightQueue;
class PlanarShadowQueue;
struct DeferredRenderData;
class DeferredPipeline;
struct RenderPass;
class CC_DLL GbufferStage : public RenderStage {
public:
static const RenderStageInfo &getInitializeInfo();
GbufferStage();
~GbufferStage() override;
bool initialize(const RenderStageInfo &info) override;
void activate(RenderPipeline *pipeline, RenderFlow *flow) override;
void destroy() override;
void render(scene::Camera *camera) override;
private:
void dispenseRenderObject2Queues();
void recordCommands(DeferredPipeline *pipeline, scene::Camera *camera, gfx::RenderPass *renderPass);
static RenderStageInfo initInfo;
PlanarShadowQueue *_planarShadowQueue = nullptr;
RenderInstancedQueue *_instancedQueue = nullptr;
uint32_t _phaseID = 0;
};
} // namespace pipeline
} // namespace cc

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "../RenderStage.h"
#include "ReflectionComp.h"
#include "scene/Camera.h"
namespace cc {
namespace pipeline {
class RenderFlow;
class RenderInstancedQueue;
class RenderAdditiveLightQueue;
class PlanarShadowQueue;
struct DeferredRenderData;
class DeferredPipeline;
struct RenderElem {
RenderObject renderObject;
gfx::DescriptorSet *set;
uint32_t modelIndex;
uint32_t passIndex;
};
class CC_DLL LightingStage : public RenderStage {
public:
static const RenderStageInfo &getInitializeInfo();
LightingStage();
~LightingStage() override;
bool initialize(const RenderStageInfo &info) override;
void activate(RenderPipeline *pipeline, RenderFlow *flow) override;
void destroy() override;
void render(scene::Camera *camera) override;
private:
void gatherLights(scene::Camera *camera);
void initLightingBuffer();
void fgLightingPass(scene::Camera *camera);
void fgTransparent(scene::Camera *camera);
void fgSsprPass(scene::Camera *camera);
void putTransparentObj2Queue();
static RenderStageInfo initInfo;
PlanarShadowQueue *_planarShadowQueue{nullptr};
uint32_t _phaseID{0};
gfx::Buffer *_deferredLitsBufs{nullptr};
gfx::Buffer *_deferredLitsBufView{nullptr};
ccstd::vector<float> _lightBufferData;
uint32_t _lightBufferStride{0};
uint32_t _lightBufferElementCount{0};
bool _isTransparentQueueEmpty{true};
float _lightMeterScale{10000.0};
gfx::DescriptorSet *_descriptorSet{nullptr};
gfx::DescriptorSetLayout *_descLayout{nullptr};
uint32_t _maxDeferredLights{UBODeferredLight::LIGHTS_PER_PASS};
ReflectionComp *_reflectionComp{nullptr};
RenderQueue *_reflectionRenderQueue{nullptr};
uint32_t _reflectionPhaseID{0};
ccstd::vector<RenderElem> _reflectionElems;
uint32_t _denoiseIndex = 0; // use to get corrrect texture string handle
gfx::Sampler *_defaultSampler{nullptr};
// SSPR texture size
uint32_t _ssprTexWidth = 0;
uint32_t _ssprTexHeight = 0;
Mat4 _matViewProj;
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,86 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "MainFlow.h"
#include "BloomStage.h"
#include "DeferredPipeline.h"
#include "GbufferStage.h"
#include "LightingStage.h"
#include "PostProcessStage.h"
#include "gfx-base/GFXDescriptorSet.h"
#include "gfx-base/GFXDevice.h"
#include "pipeline/SceneCulling.h"
#include "profiler/Profiler.h"
namespace cc {
namespace pipeline {
RenderFlowInfo MainFlow::initInfo = {
"MainFlow",
static_cast<uint32_t>(DeferredFlowPriority::MAIN),
static_cast<uint32_t>(RenderFlowTag::SCENE),
{},
};
const RenderFlowInfo &MainFlow::getInitializeInfo() { return MainFlow::initInfo; }
MainFlow::~MainFlow() = default;
bool MainFlow::initialize(const RenderFlowInfo &info) {
RenderFlow::initialize(info);
if (_stages.empty()) {
_isResourceOwner = true;
auto *gbufferStage = ccnew GbufferStage;
gbufferStage->initialize(GbufferStage::getInitializeInfo());
_stages.emplace_back(gbufferStage);
auto *lightingStage = ccnew LightingStage;
lightingStage->initialize(LightingStage::getInitializeInfo());
_stages.emplace_back(lightingStage);
auto *bloomStage = ccnew BloomStage;
bloomStage->initialize(BloomStage::getInitializeInfo());
_stages.emplace_back(bloomStage);
auto *postProcessStage = ccnew PostProcessStage;
postProcessStage->initialize(PostProcessStage::getInitializeInfo());
_stages.emplace_back(postProcessStage);
}
return true;
}
void MainFlow::activate(RenderPipeline *pipeline) {
RenderFlow::activate(pipeline);
}
void MainFlow::render(scene::Camera *camera) {
CC_PROFILE(MainFlowRender);
RenderFlow::render(camera);
}
void MainFlow::destroy() {
RenderFlow::destroy();
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,52 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "../RenderFlow.h"
namespace cc {
namespace pipeline {
class GbufferStage;
class CC_DLL MainFlow : public RenderFlow {
public:
static const RenderFlowInfo &getInitializeInfo();
MainFlow() = default;
~MainFlow() override;
bool initialize(const RenderFlowInfo &info) override;
void activate(RenderPipeline *pipeline) override;
void destroy() override;
void render(scene::Camera *camera) override;
private:
static RenderFlowInfo initInfo;
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,241 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "PostProcessStage.h"
#include "frame-graph/DevicePass.h"
#include "frame-graph/PassNodeBuilder.h"
#include "frame-graph/Resource.h"
#include "gfx-base/GFXDevice.h"
#include "pipeline/Define.h"
#include "pipeline/UIPhase.h"
#include "pipeline/helper/Utils.h"
#include "profiler/Profiler.h"
#include "renderer/pipeline/GlobalDescriptorSetManager.h"
#include "renderer/pipeline/PipelineStateManager.h"
#include "renderer/pipeline/PipelineUBO.h"
#include "renderer/pipeline/RenderPipeline.h"
#include "renderer/pipeline/RenderQueue.h"
#include "renderer/pipeline/UIPhase.h"
#include "renderer/pipeline/deferred/DeferredPipelineSceneData.h"
#include "scene/Camera.h"
#include "scene/Pass.h"
#include "scene/RenderWindow.h"
#include "scene/SubModel.h"
namespace cc {
namespace pipeline {
namespace {
const ccstd::string STAGE_NAME = "PostProcessStage";
}
RenderStageInfo PostProcessStage::initInfo = {
STAGE_NAME,
static_cast<uint32_t>(DeferredStagePriority::POSTPROCESS),
0,
{ccnew RenderQueueDesc{true, RenderQueueSortMode::BACK_TO_FRONT, {"default"}}},
};
const RenderStageInfo &PostProcessStage::getInitializeInfo() { return PostProcessStage::initInfo; }
PostProcessStage::PostProcessStage() {
_uiPhase = ccnew UIPhase;
}
bool PostProcessStage::initialize(const RenderStageInfo &info) {
RenderStage::initialize(info);
_renderQueueDescriptors = info.renderQueues;
return true;
}
void PostProcessStage::activate(RenderPipeline *pipeline, RenderFlow *flow) {
RenderStage::activate(pipeline, flow);
_uiPhase->activate(pipeline);
_phaseID = getPhaseID("default");
for (const auto &descriptor : _renderQueueDescriptors) {
uint32_t phase = 0;
for (const auto &stage : descriptor->stages) {
phase |= getPhaseID(stage);
}
std::function<int(const RenderPass &, const RenderPass &)> sortFunc = opaqueCompareFn;
switch (descriptor->sortMode) {
case RenderQueueSortMode::BACK_TO_FRONT:
sortFunc = transparentCompareFn;
break;
case RenderQueueSortMode::FRONT_TO_BACK:
sortFunc = opaqueCompareFn;
default:
break;
}
RenderQueueCreateInfo info = {descriptor->isTransparent, phase, sortFunc};
_renderQueues.emplace_back(ccnew RenderQueue(_pipeline, std::move(info)));
}
}
void PostProcessStage::destroy() {
CC_SAFE_DELETE(_uiPhase);
}
void PostProcessStage::render(scene::Camera *camera) {
CC_PROFILE(PostProcessStageRender);
static framegraph::StringHandle fgStrHandlePostProcessOutTexture = framegraph::FrameGraph::stringToHandle("postProcessOutputTexture");
struct RenderData {
framegraph::TextureHandle outColorTex; // read from lighting output
framegraph::TextureHandle backBuffer; // write to back buffer
framegraph::TextureHandle depth;
};
if (hasFlag(static_cast<gfx::ClearFlags>(camera->getClearFlag()), gfx::ClearFlagBit::COLOR)) {
_clearColors[0].x = camera->getClearColor().x;
_clearColors[0].y = camera->getClearColor().y;
_clearColors[0].z = camera->getClearColor().z;
}
_clearColors[0].w = camera->getClearColor().w;
_renderArea = RenderPipeline::getRenderArea(camera);
_inputAssembler = _pipeline->getIAByRenderArea(_renderArea);
auto *pipeline = _pipeline;
float shadingScale{_pipeline->getPipelineSceneData()->getShadingScale()};
auto postSetup = [&](framegraph::PassNodeBuilder &builder, RenderData &data) {
if (pipeline->isBloomEnabled()) {
data.outColorTex = framegraph::TextureHandle(builder.readFromBlackboard(RenderPipeline::fgStrHandleBloomOutTexture));
} else {
data.outColorTex = framegraph::TextureHandle(builder.readFromBlackboard(RenderPipeline::fgStrHandleOutColorTexture));
}
if (!data.outColorTex.isValid()) {
framegraph::Texture::Descriptor colorTexInfo;
colorTexInfo.format = gfx::Format::RGBA16F;
colorTexInfo.usage = gfx::TextureUsageBit::COLOR_ATTACHMENT | gfx::TextureUsageBit::SAMPLED;
colorTexInfo.width = static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale);
colorTexInfo.height = static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale);
data.outColorTex = builder.create(RenderPipeline::fgStrHandleOutColorTexture, colorTexInfo);
}
data.outColorTex = builder.read(data.outColorTex);
builder.writeToBlackboard(RenderPipeline::fgStrHandleOutColorTexture, data.outColorTex);
framegraph::RenderTargetAttachment::Descriptor colorAttachmentInfo;
colorAttachmentInfo.usage = framegraph::RenderTargetAttachment::Usage::COLOR;
colorAttachmentInfo.clearColor = _clearColors[0];
colorAttachmentInfo.loadOp = gfx::LoadOp::CLEAR;
auto clearFlags = static_cast<gfx::ClearFlagBit>(camera->getClearFlag());
if (!hasFlag(clearFlags, gfx::ClearFlagBit::COLOR)) {
if (hasFlag(clearFlags, static_cast<gfx::ClearFlagBit>(skyboxFlag))) {
colorAttachmentInfo.loadOp = gfx::LoadOp::DISCARD;
} else {
colorAttachmentInfo.loadOp = gfx::LoadOp::LOAD;
}
}
colorAttachmentInfo.beginAccesses = colorAttachmentInfo.endAccesses =
camera->getWindow()->getSwapchain()
? gfx::AccessFlagBit::COLOR_ATTACHMENT_WRITE
: gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE;
gfx::TextureInfo textureInfo = {
gfx::TextureType::TEX2D,
gfx::TextureUsageBit::COLOR_ATTACHMENT,
gfx::Format::RGBA8,
static_cast<uint32_t>(static_cast<float>(camera->getWindow()->getWidth()) * shadingScale),
static_cast<uint32_t>(static_cast<float>(camera->getWindow()->getHeight()) * shadingScale),
};
if (shadingScale != 1.F) {
textureInfo.usage |= gfx::TextureUsageBit::TRANSFER_SRC;
}
data.backBuffer = builder.create(fgStrHandlePostProcessOutTexture, textureInfo);
data.backBuffer = builder.write(data.backBuffer, colorAttachmentInfo);
builder.writeToBlackboard(fgStrHandlePostProcessOutTexture, data.backBuffer);
// depth
framegraph::RenderTargetAttachment::Descriptor depthAttachmentInfo;
depthAttachmentInfo.usage = framegraph::RenderTargetAttachment::Usage::DEPTH_STENCIL;
depthAttachmentInfo.loadOp = gfx::LoadOp::CLEAR;
depthAttachmentInfo.beginAccesses = depthAttachmentInfo.endAccesses = gfx::AccessFlagBit::DEPTH_STENCIL_ATTACHMENT_WRITE;
data.depth = framegraph::TextureHandle(builder.readFromBlackboard(RenderPipeline::fgStrHandleOutDepthTexture));
if (!data.depth.isValid()) {
gfx::TextureInfo depthTexInfo{
gfx::TextureType::TEX2D,
gfx::TextureUsageBit::DEPTH_STENCIL_ATTACHMENT,
gfx::Format::DEPTH_STENCIL,
static_cast<uint32_t>(static_cast<float>(pipeline->getWidth()) * shadingScale),
static_cast<uint32_t>(static_cast<float>(pipeline->getHeight()) * shadingScale),
};
data.depth = builder.create(RenderPipeline::fgStrHandleOutDepthTexture, depthTexInfo);
}
data.depth = builder.write(data.depth, depthAttachmentInfo);
builder.writeToBlackboard(RenderPipeline::fgStrHandleOutDepthTexture, data.depth);
builder.setViewport(pipeline->getViewport(camera), pipeline->getScissor(camera));
};
auto postExec = [this, camera](RenderData const &data, const framegraph::DevicePassResourceTable &table) {
auto *pipeline = _pipeline;
gfx::RenderPass *renderPass = table.getRenderPass();
auto *cmdBuff = pipeline->getCommandBuffers()[0];
const ccstd::array<uint32_t, 1> globalOffsets = {_pipeline->getPipelineUBO()->getCurrentCameraUBOOffset()};
cmdBuff->bindDescriptorSet(globalSet, pipeline->getDescriptorSet(), utils::toUint(globalOffsets.size()), globalOffsets.data());
if (!pipeline->getPipelineSceneData()->getRenderObjects().empty()) {
// post process
auto *const sceneData = static_cast<DeferredPipelineSceneData *>(pipeline->getPipelineSceneData());
scene::Pass *pv = sceneData->getPostPass();
gfx::Shader *sd = sceneData->getPostPassShader();
float shadingScale{sceneData->getShadingScale()};
// get pso and draw quad
gfx::PipelineState *pso = PipelineStateManager::getOrCreatePipelineState(pv, sd, _inputAssembler, renderPass);
pipeline::GlobalDSManager *globalDS = pipeline->getGlobalDSManager();
gfx::Sampler *sampler = shadingScale < 1.F ? globalDS->getPointSampler() : globalDS->getLinearSampler();
pv->getDescriptorSet()->bindTexture(0, table.getRead(data.outColorTex));
pv->getDescriptorSet()->bindSampler(0, sampler);
pv->getDescriptorSet()->update();
cmdBuff->bindPipelineState(pso);
cmdBuff->bindDescriptorSet(materialSet, pv->getDescriptorSet());
cmdBuff->bindInputAssembler(_inputAssembler);
cmdBuff->draw(_inputAssembler);
}
_uiPhase->render(camera, renderPass);
renderProfiler(renderPass, cmdBuff, pipeline->getProfiler(), camera);
#if CC_USE_DEBUG_RENDERER
renderDebugRenderer(renderPass, cmdBuff, pipeline->getPipelineSceneData(), camera);
#endif
};
// add pass
pipeline->getFrameGraph().addPass<RenderData>(static_cast<uint32_t>(CommonInsertPoint::DIP_POSTPROCESS), RenderPipeline::fgStrHandlePostprocessPass, postSetup, postExec);
pipeline->getFrameGraph().presentFromBlackboard(fgStrHandlePostProcessOutTexture, camera->getWindow()->getFramebuffer()->getColorTextures()[0], shadingScale == 1.F);
}
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,55 @@
/****************************************************************************
Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "../RenderStage.h"
#include "frame-graph/Handle.h"
#include "pipeline/Enum.h"
namespace cc {
namespace pipeline {
class UIPhase;
class CC_DLL PostProcessStage : public RenderStage {
public:
PostProcessStage();
~PostProcessStage() override = default;
static const RenderStageInfo &getInitializeInfo();
bool initialize(const RenderStageInfo &info) override;
void activate(RenderPipeline *pipeline, RenderFlow *flow) override;
void destroy() override;
void render(scene::Camera *camera) override;
private:
UIPhase *_uiPhase = nullptr;
uint32_t _phaseID = 0;
static RenderStageInfo initInfo;
};
} // namespace pipeline
} // namespace cc

View File

@@ -0,0 +1,593 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "ReflectionComp.h"
#include "../Define.h"
#include "base/Log.h"
#include "base/StringUtil.h"
namespace cc {
ReflectionComp::~ReflectionComp() {
CC_SAFE_DESTROY_AND_DELETE(_compShader[0]);
CC_SAFE_DESTROY_AND_DELETE(_compShader[1]);
CC_SAFE_DESTROY_AND_DELETE(_compDescriptorSetLayout);
CC_SAFE_DESTROY_AND_DELETE(_compPipelineLayout);
CC_SAFE_DESTROY_AND_DELETE(_compPipelineState[0]);
CC_SAFE_DESTROY_AND_DELETE(_compPipelineState[1]);
CC_SAFE_DESTROY_AND_DELETE(_compDescriptorSet);
CC_SAFE_DESTROY_AND_DELETE(_compDenoiseShader[0]);
CC_SAFE_DESTROY_AND_DELETE(_compDenoiseShader[1]);
CC_SAFE_DESTROY_AND_DELETE(_compDenoiseDescriptorSetLayout);
CC_SAFE_DESTROY_AND_DELETE(_compDenoisePipelineLayout);
CC_SAFE_DESTROY_AND_DELETE(_compDenoisePipelineState[0]);
CC_SAFE_DESTROY_AND_DELETE(_compDenoisePipelineState[1]);
CC_SAFE_DESTROY_AND_DELETE(_compDenoiseDescriptorSet);
CC_SAFE_DESTROY_AND_DELETE(_localDescriptorSetLayout);
CC_SAFE_DESTROY_AND_DELETE(_compConstantsBuffer);
}
namespace {
struct ConstantBuffer {
Mat4 matView;
Mat4 matProjInv;
Mat4 matViewProj;
Mat4 matViewProjInv;
Vec4 viewPort; // viewport of lighting pass
Vec2 texSize; // texture size of reflect texture
};
} // namespace
void ReflectionComp::applyTexSize(uint32_t width, uint32_t height, const Mat4 &matView,
const Mat4 &matViewProj, const Mat4 &matViewProjInv,
const Mat4 &matProjInv, const Vec4 &viewPort) {
uint32_t globalWidth = width;
uint32_t globalHeight = height;
uint32_t groupWidth = this->getGroupSizeX();
uint32_t groupHeight = this->getGroupSizeY();
_dispatchInfo = {(globalWidth - 1) / groupWidth + 1, (globalHeight - 1) / groupHeight + 1, 1};
_denoiseDispatchInfo = {((globalWidth - 1) / 2) / groupWidth + 1, ((globalHeight - 1) / 2) / groupHeight + 1, 1};
ConstantBuffer constants;
constants.matView = matView;
constants.matProjInv = matProjInv;
constants.matViewProj = matViewProj;
constants.matViewProjInv = matViewProjInv;
constants.viewPort = viewPort;
constants.texSize = {float(width), float(height)};
constants.viewPort = viewPort;
if (_compConstantsBuffer) {
_compConstantsBuffer->update(&constants, sizeof(constants));
}
}
void ReflectionComp::init(gfx::Device *dev, uint32_t groupSizeX, uint32_t groupSizeY) {
if (!dev->hasFeature(gfx::Feature::COMPUTE_SHADER)) return;
_device = dev;
_groupSizeX = groupSizeX;
_groupSizeY = groupSizeY;
gfx::SamplerInfo samplerInfo;
samplerInfo.minFilter = gfx::Filter::POINT;
samplerInfo.magFilter = gfx::Filter::POINT;
_sampler = _device->getSampler(samplerInfo);
uint32_t maxInvocations = _device->getCapabilities().maxComputeWorkGroupInvocations;
CC_ASSERT(_groupSizeX * _groupSizeY <= maxInvocations); // maxInvocations is too small
CC_LOG_INFO(" work group size: %dx%d", _groupSizeX, _groupSizeY);
gfx::DescriptorSetLayoutInfo layoutInfo = {pipeline::localDescriptorSetLayout.bindings};
_localDescriptorSetLayout = _device->createDescriptorSetLayout(layoutInfo);
gfx::GeneralBarrierInfo infoPre = {
gfx::AccessFlagBit::COLOR_ATTACHMENT_WRITE,
gfx::AccessFlagBit::COMPUTE_SHADER_READ_TEXTURE,
};
gfx::TextureBarrierInfo infoBeforeDenoise = {
gfx::AccessFlagBit::COMPUTE_SHADER_WRITE,
gfx::AccessFlagBit::COMPUTE_SHADER_READ_TEXTURE,
};
gfx::TextureBarrierInfo infoBeforeDenoise2 = {
gfx::AccessFlagBit::NONE,
gfx::AccessFlagBit::COMPUTE_SHADER_WRITE,
};
gfx::TextureBarrierInfo infoAfterDenoise = {
gfx::AccessFlagBit::COMPUTE_SHADER_WRITE,
gfx::AccessFlagBit::FRAGMENT_SHADER_READ_TEXTURE,
};
_barrierPre = _device->getGeneralBarrier(infoPre);
_barrierBeforeDenoise.push_back(_device->getTextureBarrier(infoBeforeDenoise));
_barrierBeforeDenoise.push_back(_device->getTextureBarrier(infoBeforeDenoise2));
_barrierAfterDenoise.push_back(_device->getTextureBarrier(infoAfterDenoise));
_compConstantsBuffer = _device->createBuffer({gfx::BufferUsage::UNIFORM,
gfx::MemoryUsage::DEVICE | gfx::MemoryUsage::HOST,
(sizeof(Mat4) * 4 + sizeof(Vec2) + sizeof(Vec4) + 15) / 16 * 16});
initReflectionRes();
initDenoiseRes();
}
void ReflectionComp::getReflectorShader(ShaderSources<ComputeShaderSource> &sources, bool useEnvmap) const {
sources.glsl4 = StringUtil::format(
R"(
#define CC_USE_ENVMAP %d
layout(local_size_x = %d, local_size_y = %d, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform Constants
{
mat4 matView;
mat4 matProjInv;
mat4 matViewProj;
mat4 matViewProjInv;
vec4 viewPort;
vec2 texSize;
};
layout(set = 0, binding = 1, std140) uniform CCLocal
{
mat4 cc_matWorld;
mat4 cc_matWorldIT;
vec4 cc_lightingMapUVParam;
};
layout(set = 0, binding = 2) uniform sampler2D lightingTex;
layout(set = 0, binding = 3) uniform sampler2D depth;
layout(set = 0, binding = 4, rgba8) writeonly uniform lowp image2D reflectionTex;
vec4 screen2WS(vec3 coord) {
vec4 ndc = vec4(
2.0 * (coord.x - viewPort.x) / viewPort.z - 1.0,
2.0 * (coord.y - viewPort.y) / viewPort.w - 1.0,
coord.z,
1.0
);
vec4 world = matViewProjInv * ndc;
world = world / world.w;
return world;
}
void main() {
float _HorizontalPlaneHeightWS = 0.01;
_HorizontalPlaneHeightWS = (cc_matWorld * vec4(0,0,0,1)).y;
vec2 uv = vec2(gl_GlobalInvocationID.xy) / texSize;
vec4 depValue = texture(depth, uv);
vec2 screenPos = vec2(uv * vec2(viewPort.z, viewPort.w) + vec2(viewPort.x, viewPort.y));
vec3 posWS = screen2WS(vec3(screenPos, depValue.r)).xyz;
if(posWS.y <= _HorizontalPlaneHeightWS) return;
#if CC_USE_ENVMAP
imageStore(reflectionTex, ivec2(gl_GlobalInvocationID.xy), vec4(0, 0, 0, 1));
#endif
vec3 reflectedPosWS = posWS;
reflectedPosWS.y = reflectedPosWS.y - _HorizontalPlaneHeightWS;
reflectedPosWS.y = reflectedPosWS.y * -1.0;
reflectedPosWS.y = reflectedPosWS.y + _HorizontalPlaneHeightWS;
vec4 reflectedPosCS = matViewProj * vec4(reflectedPosWS, 1);
vec2 reflectedPosNDCxy = reflectedPosCS.xy / reflectedPosCS.w;//posCS -> posNDC
vec2 reflectedScreenUV = reflectedPosNDCxy * 0.5 + 0.5; //posNDC
vec2 earlyExitTest = abs(reflectedScreenUV - 0.5);
if (earlyExitTest.x >= 0.5 || earlyExitTest.y >= 0.5) return;
vec4 inputPixelSceneColor = texture(lightingTex, uv);
imageStore(reflectionTex, ivec2(reflectedScreenUV * texSize), inputPixelSceneColor);
})",
useEnvmap, _groupSizeX, _groupSizeY);
sources.glsl3 = StringUtil::format(
R"(
#define CC_USE_ENVMAP %d
layout(local_size_x = %d, local_size_y = %d, local_size_z = 1) in;
layout(std140) uniform Constants
{
mat4 matView;
mat4 matProjInv;
mat4 matViewProj;
mat4 matViewProjInv;
vec4 viewPort;
vec2 texSize;
};
uniform sampler2D lightingTex;
uniform sampler2D depth;
layout(rgba8) writeonly uniform lowp image2D reflectionTex;
layout(std140) uniform CCLocal
{
mat4 cc_matWorld;
mat4 cc_matWorldIT;
vec4 cc_lightingMapUVParam;
};
vec4 screen2WS(vec3 coord) {
vec4 ndc = vec4(
2.0 * (coord.x - viewPort.x) / viewPort.z - 1.0,
2.0 * (coord.y - viewPort.y) / viewPort.w - 1.0,
2.0 * coord.z - 1.0,
1.0
);
vec4 world = matViewProjInv * ndc;
world = world / world.w;
return world;
}
void main() {
float _HorizontalPlaneHeightWS = 0.01;
_HorizontalPlaneHeightWS = (cc_matWorld * vec4(0,0,0,1)).y;
vec2 uv = vec2(gl_GlobalInvocationID.xy) / texSize;
vec4 depValue = texture(depth, uv);
vec2 screenPos = uv * vec2(viewPort.z, viewPort.w) + vec2(viewPort.x, viewPort.y);
vec3 posWS = screen2WS(vec3(screenPos, depValue.r)).xyz;
if(posWS.y <= _HorizontalPlaneHeightWS) return;
#if CC_USE_ENVMAP
if (posWS.y - 0.5 > _HorizontalPlaneHeightWS) imageStore(reflectionTex, ivec2(gl_GlobalInvocationID.xy), vec4(0, 0, 0, 1));
#endif
vec3 reflectedPosWS = posWS;
reflectedPosWS.y = reflectedPosWS.y - _HorizontalPlaneHeightWS;
reflectedPosWS.y = reflectedPosWS.y * -1.0;
reflectedPosWS.y = reflectedPosWS.y + _HorizontalPlaneHeightWS;
vec4 reflectedPosCS = matViewProj * vec4(reflectedPosWS, 1);
vec2 reflectedPosNDCxy = reflectedPosCS.xy / reflectedPosCS.w;//posCS -> posNDC
vec2 reflectedScreenUV = reflectedPosNDCxy * 0.5 + 0.5; //posNDC
vec2 earlyExitTest = abs(reflectedScreenUV - 0.5);
if (earlyExitTest.x >= 0.5 || earlyExitTest.y >= 0.5) return;
vec4 inputPixelSceneColor = texture(lightingTex, uv);
imageStore(reflectionTex, ivec2(reflectedScreenUV * texSize), inputPixelSceneColor);
})",
useEnvmap, _groupSizeX, _groupSizeY);
}
void ReflectionComp::initReflectionRes() {
for (int i = 0; i < 2; ++i) {
ShaderSources<ComputeShaderSource> sources;
getReflectorShader(sources, i);
gfx::ShaderInfo shaderInfo;
shaderInfo.name = "Compute ";
shaderInfo.stages = {{gfx::ShaderStageFlagBit::COMPUTE, getAppropriateShaderSource(sources)}};
shaderInfo.blocks = {
{0, 0, "Constants", {
{"matView", gfx::Type::MAT4, 1},
{"matProjInv", gfx::Type::MAT4, 1},
{"matViewProj", gfx::Type::MAT4, 1},
{"matViewProjInv", gfx::Type::MAT4, 1},
{"viewPort", gfx::Type::FLOAT4, 1},
{"texSize", gfx::Type::FLOAT2, 1},
},
1},
{0, 1, "CCLocal", {{"cc_matWorld", gfx::Type::MAT4, 1}, {"cc_matWorldIT", gfx::Type::MAT4, 1}, {"cc_lightingMapUVParam", gfx::Type::FLOAT4, 1}}, 1}};
shaderInfo.samplerTextures = {
{0, 2, "lightingTex", gfx::Type::SAMPLER2D, 1},
{0, 3, "depth", gfx::Type::SAMPLER2D, 1}};
shaderInfo.images = {
{0, 4, "reflectionTex", gfx::Type::IMAGE2D, 1, gfx::MemoryAccessBit::WRITE_ONLY}};
_compShader[i] = _device->createShader(shaderInfo);
}
gfx::DescriptorSetLayoutInfo dslInfo;
dslInfo.bindings.push_back({0, gfx::DescriptorType::UNIFORM_BUFFER, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({1, gfx::DescriptorType::UNIFORM_BUFFER, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({2, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({3, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({4, gfx::DescriptorType::STORAGE_IMAGE, 1, gfx::ShaderStageFlagBit::COMPUTE});
_compDescriptorSetLayout = _device->createDescriptorSetLayout(dslInfo);
_compDescriptorSet = _device->createDescriptorSet({_compDescriptorSetLayout});
_compPipelineLayout = _device->createPipelineLayout({{_compDescriptorSetLayout}});
for (int i = 0; i < 2; ++i) {
gfx::PipelineStateInfo pipelineInfo;
pipelineInfo.shader = _compShader[i];
pipelineInfo.pipelineLayout = _compPipelineLayout;
pipelineInfo.bindPoint = gfx::PipelineBindPoint::COMPUTE;
_compPipelineState[i] = _device->createPipelineState(pipelineInfo);
}
}
void ReflectionComp::getDenoiseShader(ShaderSources<ComputeShaderSource> &sources, bool useEnvmap) const {
sources.glsl4 = StringUtil::format(
R"(
#define CC_USE_ENVMAP %d
layout(local_size_x = %d, local_size_y = %d, local_size_z = 1) in;
layout(set = 0, binding = 1) uniform sampler2D reflectionTex;
layout(set = 1, binding = %d, rgba8) writeonly uniform lowp image2D denoiseTex;
#if CC_USE_ENVMAP == 1
layout(set = 0, binding = 2) uniform samplerCube envMap;
layout(set = 0, binding = 3) uniform sampler2D depth;
layout(set = 0, binding = 0) uniform Constants
{
mat4 matView;
mat4 matProjInv;
mat4 matViewProj;
mat4 matViewProjInv;
vec4 viewPort;
vec2 texSize;
};
vec4 screen2ES(vec3 coord) {
vec4 ndc = vec4(2.0 * (coord.x - viewPort.x) / viewPort.z - 1.0,
2.0 * (coord.y - viewPort.y) / viewPort.w - 1.0,
coord.z,
1.0);
vec4 eye = matProjInv * ndc;
eye = eye / eye.w;
return eye;
}
vec3 calEnvmapUV(vec3 eyeCoord) {
vec4 planeNornalWS = vec4(0, 1.0, 0, 1.0);
vec3 planeNormalES = normalize((matView * planeNornalWS).xyz);
vec3 incidenceES = normalize(eyeCoord);
return normalize(reflect(incidenceES, planeNormalES));
}
vec4 sampleEnvmap(ivec2 id) {
vec2 uv = vec2(id) / texSize;
vec4 depValue = texture(depth, uv);
vec2 screenPos = uv * vec2(viewPort.z, viewPort.w) + vec2(viewPort.x, viewPort.y);
vec3 posES = screen2ES(vec3(screenPos, depValue.r)).xyz;
vec3 envmapUV = calEnvmapUV(posES);
return texture(envMap, envmapUV);
}
#endif
void main() {
ivec2 id = ivec2(gl_GlobalInvocationID.xy) * 2;
vec4 center = texelFetch(reflectionTex, id + ivec2(0, 0), 0);
vec4 right = texelFetch(reflectionTex, id + ivec2(0, 1), 0);
vec4 bottom = texelFetch(reflectionTex, id + ivec2(1, 0), 0);
vec4 bottomRight = texelFetch(reflectionTex, id + ivec2(1, 1), 0);
vec4 best = center;
best = right.a > best.a + 0.1 ? right : best;
best = bottom.a > best.a + 0.1 ? bottom : best;
best = bottomRight.a > best.a + 0.1 ? bottomRight : best;
#if !CC_USE_ENVMAP
vec4 res = best.a > center.a + 0.1 ? best : center;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(0, 0), res);
res = best.a > right.a + 0.1 ? best : right;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(0, 1), res);
res = best.a > bottom.a + 0.1 ? best : bottom;
if (res.xyz != vec3(0,0, 0)) imageStore(denoiseTex, id + ivec2(1, 0), res);
res = best.a > bottomRight.a + 0.1 ? best : bottomRight;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(1, 1), res);
#else
vec4 res = best.a > center.a + 0.1 ? best : center;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id) : res;
imageStore(denoiseTex, id + ivec2(0, 0), res);
res = best.a > right.a + 0.1 ? best : right;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(0, 1)) : res;
imageStore(denoiseTex, id + ivec2(0, 1), res);
res = best.a > bottom.a + 0.1 ? best : bottom;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(1, 0)) : res;
imageStore(denoiseTex, id + ivec2(1, 0), res);
res = best.a > bottomRight.a + 0.1 ? best : bottomRight;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(1, 1)) : res;
imageStore(denoiseTex, id + ivec2(1, 1), res);
#endif
})",
useEnvmap, _groupSizeX, _groupSizeY, pipeline::REFLECTIONSTORAGE::BINDING);
sources.glsl3 = StringUtil::format(
R"(
#define CC_USE_ENVMAP %d
layout(local_size_x = %d, local_size_y = %d, local_size_z = 1) in;
uniform sampler2D reflectionTex;
#if CC_USE_ENVMAP
uniform samplerCube envMap;
uniform sampler2D depth;
layout(std140) uniform Constants
{
mat4 matView;
mat4 matProjInv;
mat4 matViewProj;
mat4 matViewProjInv;
vec4 viewPort;
vec2 texSize;
};
#endif
layout(rgba8) writeonly uniform lowp image2D denoiseTex;
#if CC_USE_ENVMAP
vec4 screen2ES(vec3 coord) {
vec4 ndc = vec4(2.0 * (coord.x - viewPort.x) / viewPort.z - 1.0,
2.0 * (coord.y - viewPort.y) / viewPort.w - 1.0,
2.0 * coord.z - 1.0,
1.0);
vec4 eye = matProjInv * ndc;
eye = eye / eye.w;
return eye;
}
vec3 calEnvmapUV(vec3 eyeCoord) {
vec4 planeNornalWS = vec4(0, 1.0, 0, 1.0);
vec3 planeNormalES = normalize((matView * planeNornalWS).xyz);
vec3 incidenceES = normalize(eyeCoord);
return normalize(reflect(incidenceES, planeNormalES));
}
vec4 sampleEnvmap(ivec2 id) {
vec2 uv = vec2(id) / texSize;
vec4 depValue = texture(depth, uv);
vec2 screenPos = uv * vec2(viewPort.z, viewPort.w) + vec2(viewPort.x, viewPort.y);
vec3 posES = screen2ES(vec3(screenPos, depValue.r)).xyz;
vec3 envmapUV = calEnvmapUV(posES);
return texture(envMap, envmapUV);
}
#endif
void main() {
ivec2 id = ivec2(gl_GlobalInvocationID.xy) * 2;
vec4 center = texelFetch(reflectionTex, id + ivec2(0, 0), 0);
vec4 right = texelFetch(reflectionTex, id + ivec2(0, 1), 0);
vec4 bottom = texelFetch(reflectionTex, id + ivec2(1, 0), 0);
vec4 bottomRight = texelFetch(reflectionTex, id + ivec2(1, 1), 0);
vec4 best = center;
best = right.a > best.a + 0.1 ? right : best;
best = bottom.a > best.a + 0.1 ? bottom : best;
best = bottomRight.a > best.a + 0.1 ? bottomRight : best;
#if !CC_USE_ENVMAP
vec4 res = best.a > center.a + 0.1 ? best : center;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(0, 0), res);
res = best.a > right.a + 0.1 ? best : right;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(0, 1), res);
res = best.a > bottom.a + 0.1 ? best : bottom;
if (res.xyz != vec3(0,0, 0)) imageStore(denoiseTex, id + ivec2(1, 0), res);
res = best.a > bottomRight.a + 0.1 ? best : bottomRight;
if (res.xyz != vec3(0, 0, 0)) imageStore(denoiseTex, id + ivec2(1, 1), res);
#else
vec4 res = best.a > center.a + 0.1 ? best : center;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id) : res;
imageStore(denoiseTex, id + ivec2(0, 0), res);
res = best.a > right.a + 0.1 ? best : right;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(0, 1)) : res;
imageStore(denoiseTex, id + ivec2(0, 1), res);
res = best.a > bottom.a + 0.1 ? best : bottom;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(1, 0)) : res;
imageStore(denoiseTex, id + ivec2(1, 0), res);
res = best.a > bottomRight.a + 0.1 ? best : bottomRight;
res = res == vec4(0, 0, 0, 0) ? sampleEnvmap(id + ivec2(1, 1)) : res;
imageStore(denoiseTex, id + ivec2(1, 1), res);
#endif
})",
useEnvmap, _groupSizeX, _groupSizeY);
}
void ReflectionComp::initDenoiseRes() {
for (int i = 0; i < 2; ++i) {
ShaderSources<ComputeShaderSource> sources;
getDenoiseShader(sources, i);
gfx::ShaderInfo shaderInfo;
shaderInfo.name = "Compute ";
shaderInfo.stages = {{gfx::ShaderStageFlagBit::COMPUTE, getAppropriateShaderSource(sources)}};
if (i == 0) {
shaderInfo.blocks = {};
shaderInfo.samplerTextures = {
{0, 1, "reflectionTex", gfx::Type::SAMPLER2D, 1}};
} else {
shaderInfo.blocks = {
{0, 0, "Constants", {
{"matView", gfx::Type::MAT4, 1},
{"matProjInv", gfx::Type::MAT4, 1},
{"matViewProj", gfx::Type::MAT4, 1},
{"matViewProjInv", gfx::Type::MAT4, 1},
{"viewPort", gfx::Type::FLOAT4, 1},
{"texSize", gfx::Type::FLOAT2, 1},
},
1},
};
shaderInfo.samplerTextures = {
{0, 1, "reflectionTex", gfx::Type::SAMPLER2D, 1},
{0, 2, "envMap", gfx::Type::SAMPLER_CUBE, 1},
{0, 3, "depth", gfx::Type::SAMPLER2D, 1}};
}
shaderInfo.images = {
{1, 12, "denoiseTex", gfx::Type::IMAGE2D, 1, gfx::MemoryAccessBit::WRITE_ONLY}};
_compDenoiseShader[i] = _device->createShader(shaderInfo);
}
gfx::DescriptorSetLayoutInfo dslInfo;
dslInfo.bindings.push_back({0, gfx::DescriptorType::UNIFORM_BUFFER, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({1, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({2, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::COMPUTE});
dslInfo.bindings.push_back({3, gfx::DescriptorType::SAMPLER_TEXTURE, 1, gfx::ShaderStageFlagBit::COMPUTE});
_compDenoiseDescriptorSetLayout = _device->createDescriptorSetLayout(dslInfo);
_compDenoisePipelineLayout = _device->createPipelineLayout({{_compDenoiseDescriptorSetLayout, _localDescriptorSetLayout}});
_compDenoiseDescriptorSet = _device->createDescriptorSet({_compDenoiseDescriptorSetLayout});
for (int i = 0; i < 2; ++i) {
gfx::PipelineStateInfo pipelineInfo;
pipelineInfo.shader = _compDenoiseShader[i];
pipelineInfo.pipelineLayout = _compDenoisePipelineLayout;
pipelineInfo.bindPoint = gfx::PipelineBindPoint::COMPUTE;
_compDenoisePipelineState[i] = _device->createPipelineState(pipelineInfo);
}
}
template <typename T>
T &ReflectionComp::getAppropriateShaderSource(ShaderSources<T> &sources) {
switch (_device->getGfxAPI()) {
case gfx::API::GLES2:
return sources.glsl1;
case gfx::API::GLES3:
return sources.glsl3;
case gfx::API::METAL:
case gfx::API::VULKAN:
return sources.glsl4;
default: break;
}
return sources.glsl4;
}
} // namespace cc

View File

@@ -0,0 +1,111 @@
/****************************************************************************
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#pragma once
#include "base/TypeDef.h"
#include "math/Mat4.h"
#include "math/Vec2.h"
#include "math/Vec4.h"
#include "renderer/gfx-base/GFXDef.h"
#include "renderer/gfx-base/GFXDevice.h"
namespace cc {
using ComputeShaderSource = ccstd::string;
template <typename T>
struct ShaderSources {
T glsl4;
T glsl3;
T glsl1;
};
class ReflectionComp {
public:
ReflectionComp() = default;
~ReflectionComp();
void init(gfx::Device *dev, uint32_t groupSizeX, uint32_t groupSizeY);
void getReflectorShader(ShaderSources<ComputeShaderSource> &sources, bool useEnvmap) const;
void getDenoiseShader(ShaderSources<ComputeShaderSource> &sources, bool useEnvmap) const;
void initReflectionRes();
void initDenoiseRes();
void initDenoiseResEnvmap();
void applyTexSize(uint32_t width, uint32_t height, const Mat4 &matView,
const Mat4 &matViewProj, const Mat4 &matViewProjInv,
const Mat4 &matProjInv, const Vec4 &viewPort);
inline gfx::DescriptorSet *getDescriptorSet() { return _compDescriptorSet; }
inline const gfx::PipelineState *getPipelineState(bool useEnvmap) { return _compPipelineState[useEnvmap]; }
inline gfx::DescriptorSet *getDenoiseDescriptorSet() { return _compDenoiseDescriptorSet; }
inline const gfx::PipelineState *getDenoisePipelineState(bool useEnvmap) { return _compDenoisePipelineState[useEnvmap]; }
inline const gfx::PipelineState *getDenoisePipelineStateEnvmap() { return _compDenoisePipelineStateEnvmap; }
inline const gfx::GeneralBarrier *getBarrierPre() { return _barrierPre; }
inline const gfx::TextureBarrierList &getBarrierBeforeDenoise() { return _barrierBeforeDenoise; }
inline const gfx::TextureBarrierList &getBarrierAfterDenoise() { return _barrierAfterDenoise; }
inline const gfx::DispatchInfo &getDispatchInfo() { return _dispatchInfo; }
inline const gfx::DispatchInfo &getDenoiseDispatchInfo() { return _denoiseDispatchInfo; }
inline uint32_t getGroupSizeX() const { return _groupSizeX; }
inline uint32_t getGroupSizeY() const { return _groupSizeY; }
inline gfx::Buffer *getConstantsBuffer() { return _compConstantsBuffer; }
inline gfx::Sampler *getSampler() { return _sampler; }
private:
template <typename T>
T &getAppropriateShaderSource(ShaderSources<T> &sources);
gfx::Device *_device{nullptr};
gfx::Shader *_compShader[2]{nullptr};
gfx::DescriptorSetLayout *_compDescriptorSetLayout{nullptr};
gfx::PipelineLayout *_compPipelineLayout{nullptr};
gfx::PipelineState *_compPipelineState[2]{nullptr};
gfx::DescriptorSet *_compDescriptorSet{nullptr};
gfx::Shader *_compDenoiseShader[2]{nullptr};
gfx::DescriptorSetLayout *_compDenoiseDescriptorSetLayout{nullptr};
gfx::PipelineLayout *_compDenoisePipelineLayout{nullptr};
gfx::PipelineState *_compDenoisePipelineState[2]{nullptr};
gfx::PipelineState *_compDenoisePipelineStateEnvmap{nullptr};
gfx::DescriptorSet *_compDenoiseDescriptorSet{nullptr};
gfx::DescriptorSetLayout *_localDescriptorSetLayout{nullptr};
gfx::Buffer *_compConstantsBuffer{nullptr};
gfx::Sampler *_sampler{nullptr};
gfx::GeneralBarrier *_barrierPre{nullptr};
gfx::TextureBarrierList _barrierBeforeDenoise;
gfx::TextureBarrierList _barrierAfterDenoise;
gfx::DispatchInfo _dispatchInfo;
gfx::DispatchInfo _denoiseDispatchInfo;
uint32_t _groupSizeX{8};
uint32_t _groupSizeY{8};
};
} // namespace cc