You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2108 lines
86 KiB
2108 lines
86 KiB
/****************************************************************************
|
|
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 <boost/graph/depth_first_search.hpp>
|
|
#include <boost/graph/filtered_graph.hpp>
|
|
#include "FGDispatcherGraphs.h"
|
|
#include "LayoutGraphGraphs.h"
|
|
#include "LayoutGraphTypes.h"
|
|
#include "LayoutGraphUtils.h"
|
|
#include "NativeBuiltinUtils.h"
|
|
#include "NativePipelineFwd.h"
|
|
#include "NativePipelineTypes.h"
|
|
#include "NativeRenderGraphUtils.h"
|
|
#include "NativeUtils.h"
|
|
#include "PrivateTypes.h"
|
|
#include "RenderGraphGraphs.h"
|
|
#include "RenderGraphTypes.h"
|
|
#include "RenderingModule.h"
|
|
#include "cocos/renderer/gfx-base/GFXBarrier.h"
|
|
#include "cocos/renderer/gfx-base/GFXDef-common.h"
|
|
#include "cocos/renderer/gfx-base/GFXDescriptorSetLayout.h"
|
|
#include "cocos/renderer/gfx-base/GFXDevice.h"
|
|
#include "cocos/renderer/pipeline/Define.h"
|
|
#include "cocos/renderer/pipeline/InstancedBuffer.h"
|
|
#include "cocos/renderer/pipeline/PipelineStateManager.h"
|
|
#include "cocos/scene/Model.h"
|
|
#include "cocos/scene/Octree.h"
|
|
#include "cocos/scene/Pass.h"
|
|
#include "cocos/scene/RenderScene.h"
|
|
#include "cocos/scene/Skybox.h"
|
|
#include "details/GraphView.h"
|
|
#include "details/GslUtils.h"
|
|
#include "details/Range.h"
|
|
|
|
namespace cc {
|
|
|
|
namespace render {
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t INVALID_ID = 0xFFFFFFFF;
|
|
constexpr gfx::Color RASTER_COLOR{0.0, 1.0, 0.0, 1.0};
|
|
constexpr gfx::Color RASTER_UPLOAD_COLOR{1.0, 1.0, 0.0, 1.0};
|
|
constexpr gfx::Color RENDER_QUEUE_COLOR{0.0, 0.5, 0.5, 1.0};
|
|
constexpr gfx::Color COMPUTE_COLOR{0.0, 0.0, 1.0, 1.0};
|
|
|
|
gfx::MarkerInfo makeMarkerInfo(const char* str, const gfx::Color& color) {
|
|
return gfx::MarkerInfo{str, color};
|
|
}
|
|
|
|
struct RenderGraphVisitorContext {
|
|
RenderGraphVisitorContext(RenderGraphVisitorContext&&) = delete;
|
|
RenderGraphVisitorContext(RenderGraphVisitorContext const&) = delete;
|
|
RenderGraphVisitorContext& operator=(RenderGraphVisitorContext&&) = delete;
|
|
RenderGraphVisitorContext& operator=(RenderGraphVisitorContext const&) = delete;
|
|
|
|
NativeRenderContext& context;
|
|
LayoutGraphData& lg;
|
|
const RenderGraph& g;
|
|
ResourceGraph& resourceGraph;
|
|
const FrameGraphDispatcher& fgd;
|
|
const ccstd::pmr::vector<bool>& validPasses;
|
|
gfx::Device* device = nullptr;
|
|
gfx::CommandBuffer* cmdBuff = nullptr;
|
|
NativePipeline* ppl = nullptr;
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>& renderGraphDescriptorSet;
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>& profilerPerPassDescriptorSets;
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>& perInstanceDescriptorSets;
|
|
ProgramLibrary* programLib = nullptr;
|
|
CustomRenderGraphContext customContext;
|
|
boost::container::pmr::memory_resource* scratch = nullptr;
|
|
gfx::RenderPass* currentPass = nullptr;
|
|
uint32_t subpassIndex = 0;
|
|
LayoutGraphData::vertex_descriptor currentPassLayoutID = LayoutGraphData::null_vertex();
|
|
RenderGraph::vertex_descriptor currentInFlightPassID = RenderGraph::null_vertex();
|
|
Mat4 currentProjMatrix{};
|
|
};
|
|
|
|
void clear(gfx::RenderPassInfo& info) {
|
|
info.colorAttachments.clear();
|
|
info.depthStencilAttachment = {};
|
|
info.depthStencilResolveAttachment = {};
|
|
info.subpasses.clear();
|
|
info.dependencies.clear();
|
|
}
|
|
|
|
uint8_t getRasterViewPassInputSlot(const RasterView& view) {
|
|
std::ignore = view;
|
|
CC_EXPECTS(false); // not implemented yet
|
|
return 0;
|
|
}
|
|
|
|
uint8_t getRasterViewPassOutputSlot(const RasterView& view) {
|
|
std::ignore = view;
|
|
CC_EXPECTS(false); // not implemented yet
|
|
return 0;
|
|
}
|
|
|
|
uint32_t getRasterPassInputCount(const RasterPass& pass) {
|
|
uint32_t numInputs = 0;
|
|
for (const auto& [name, view] : pass.rasterViews) {
|
|
if (view.accessType == AccessType::READ || view.accessType == AccessType::READ_WRITE) {
|
|
++numInputs;
|
|
}
|
|
}
|
|
return numInputs;
|
|
}
|
|
|
|
uint32_t getRasterPassOutputCount(const RasterPass& pass) {
|
|
uint32_t numOutputs = 0;
|
|
for (const auto& [name, view] : pass.rasterViews) {
|
|
if (view.attachmentType != AttachmentType::RENDER_TARGET) {
|
|
continue;
|
|
}
|
|
if (view.accessType == AccessType::READ_WRITE || view.accessType == AccessType::WRITE) {
|
|
++numOutputs;
|
|
}
|
|
}
|
|
return numOutputs;
|
|
}
|
|
|
|
uint32_t getRasterPassResolveCount(const RasterPass& pass) {
|
|
std::ignore = pass;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t getRasterPassPreserveCount(const RasterPass& pass) {
|
|
std::ignore = pass;
|
|
return 0;
|
|
}
|
|
|
|
gfx::GeneralBarrier* getGeneralBarrier(gfx::Device* device, const RasterView& view) {
|
|
if (view.accessType != AccessType::WRITE) { // Input
|
|
return device->getGeneralBarrier({
|
|
gfx::AccessFlagBit::COLOR_ATTACHMENT_READ,
|
|
gfx::AccessFlagBit::COLOR_ATTACHMENT_READ,
|
|
});
|
|
}
|
|
|
|
if (view.accessType != AccessType::READ) { // Output
|
|
auto accessFlagBit = view.attachmentType == AttachmentType::RENDER_TARGET
|
|
? gfx::AccessFlagBit::COLOR_ATTACHMENT_WRITE
|
|
: gfx::AccessFlagBit::DEPTH_STENCIL_ATTACHMENT_WRITE;
|
|
return device->getGeneralBarrier({accessFlagBit, accessFlagBit});
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ResourceGraph::vertex_descriptor getResourceID(const ccstd::pmr::string& name, const FrameGraphDispatcher& fgd) {
|
|
return fgd.realResourceID(name);
|
|
}
|
|
|
|
PersistentRenderPassAndFramebuffer createPersistentRenderPassAndFramebuffer(
|
|
RenderGraphVisitorContext& ctx, const RasterPass& pass,
|
|
boost::container::pmr::memory_resource* /*scratch*/) {
|
|
auto& resg = ctx.resourceGraph;
|
|
|
|
PersistentRenderPassAndFramebuffer data(pass.get_allocator());
|
|
auto [rpInfo, fbInfo, clearColors, clearDepth, clearStencil] = ctx.fgd.getRenderPassAndFrameBuffer(ctx.currentInFlightPassID, resg);
|
|
|
|
// CC_ENSURES(rpInfo.colorAttachments.size() == data.clearColors.size());
|
|
CC_ENSURES(rpInfo.colorAttachments.size() == fbInfo.colorTextures.size());
|
|
|
|
data.clearColors = std::move(clearColors);
|
|
data.clearDepth = clearDepth;
|
|
data.clearStencil = clearStencil;
|
|
data.renderPass = ctx.device->createRenderPass(rpInfo);
|
|
fbInfo.renderPass = data.renderPass;
|
|
data.framebuffer = ctx.device->createFramebuffer(fbInfo);
|
|
|
|
return data;
|
|
}
|
|
|
|
PersistentRenderPassAndFramebuffer& fetchOrCreateFramebuffer(
|
|
RenderGraphVisitorContext& ctx, const RasterPass& pass,
|
|
boost::container::pmr::memory_resource* scratch) {
|
|
auto iter = ctx.resourceGraph.renderPasses.find(pass);
|
|
if (iter == ctx.resourceGraph.renderPasses.end()) {
|
|
bool added = false;
|
|
std::tie(iter, added) = ctx.resourceGraph.renderPasses.emplace(
|
|
pass, createPersistentRenderPassAndFramebuffer(ctx, pass, scratch));
|
|
CC_ENSURES(added);
|
|
}
|
|
return iter->second;
|
|
}
|
|
|
|
struct RenderGraphFilter {
|
|
bool operator()(RenderGraph::vertex_descriptor u) const {
|
|
return validPasses->operator[](u);
|
|
}
|
|
const ccstd::pmr::vector<bool>* validPasses = nullptr;
|
|
};
|
|
|
|
void bindDescValue(gfx::DescriptorSet& desc, uint32_t binding, gfx::Buffer* value) {
|
|
desc.bindBuffer(binding, value);
|
|
}
|
|
|
|
void bindDescValue(gfx::DescriptorSet& desc, uint32_t binding, gfx::Texture* value) {
|
|
desc.bindTexture(binding, value);
|
|
}
|
|
|
|
void bindDescValue(gfx::DescriptorSet& desc, uint32_t binding, gfx::Sampler* value) {
|
|
desc.bindSampler(binding, value);
|
|
}
|
|
|
|
template <class T>
|
|
void bindGlobalDesc(gfx::DescriptorSet& desc, uint32_t binding, T* value) {
|
|
bindDescValue(desc, binding, value);
|
|
}
|
|
|
|
uint32_t getDescBinding(uint32_t descId, const DescriptorSetData& descData) {
|
|
const auto& layoutData = descData;
|
|
// find descriptor binding
|
|
for (const auto& block : layoutData.descriptorSetLayoutData.descriptorBlocks) {
|
|
for (uint32_t i = 0; i != block.descriptors.size(); ++i) {
|
|
if (descId == block.descriptors[i].descriptorID.value) {
|
|
return block.offset + i;
|
|
}
|
|
}
|
|
}
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
void updateGlobal(
|
|
RenderGraphVisitorContext& ctx,
|
|
DescriptorSetData& descriptorSetData,
|
|
const RenderData& data,
|
|
bool isUpdate = false) {
|
|
const auto& constants = data.constants;
|
|
const auto& samplers = data.samplers;
|
|
const auto& textures = data.textures;
|
|
auto& device = *ctx.device;
|
|
auto& descriptorSet = *descriptorSetData.descriptorSet;
|
|
for (const auto& [key, value] : constants) {
|
|
const auto bindId = getDescBinding(key, descriptorSetData);
|
|
if (bindId == INVALID_ID) {
|
|
continue;
|
|
}
|
|
auto* buffer = descriptorSet.getBuffer(bindId);
|
|
bool haveBuff = true;
|
|
if (!buffer && !isUpdate) {
|
|
gfx::BufferInfo info{
|
|
gfx::BufferUsageBit::UNIFORM | gfx::BufferUsageBit::TRANSFER_DST,
|
|
gfx::MemoryUsageBit::HOST | gfx::MemoryUsageBit::DEVICE,
|
|
static_cast<uint32_t>(value.size()),
|
|
static_cast<uint32_t>(value.size())};
|
|
buffer = device.createBuffer(info);
|
|
haveBuff = false;
|
|
}
|
|
CC_ENSURES(buffer);
|
|
if (isUpdate) {
|
|
buffer->update(value.data());
|
|
}
|
|
if (!haveBuff) {
|
|
bindGlobalDesc(descriptorSet, bindId, buffer);
|
|
}
|
|
}
|
|
for (const auto& [key, value] : textures) {
|
|
const auto bindId = getDescBinding(key, descriptorSetData);
|
|
if (bindId == INVALID_ID) {
|
|
continue;
|
|
}
|
|
auto* tex = descriptorSet.getTexture(bindId);
|
|
if (!tex || isUpdate) {
|
|
bindGlobalDesc(descriptorSet, bindId, value.get());
|
|
}
|
|
}
|
|
for (const auto& [key, value] : samplers) {
|
|
const auto bindId = getDescBinding(key, descriptorSetData);
|
|
if (bindId == INVALID_ID) {
|
|
continue;
|
|
}
|
|
auto* sampler = descriptorSet.getSampler(bindId);
|
|
if (!sampler || isUpdate) {
|
|
bindGlobalDesc(descriptorSet, bindId, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void updateCpuUniformBuffer(
|
|
const LayoutGraphData& lg,
|
|
const RenderData& user,
|
|
const gfx::UniformBlock& uniformBlock,
|
|
bool bInit,
|
|
ccstd::pmr::vector<char>& buffer) {
|
|
// calculate uniform block size
|
|
const auto bufferSize = uniformBlock.count *
|
|
getUniformBlockSize(uniformBlock.members);
|
|
// check pre-condition
|
|
CC_EXPECTS(buffer.size() == bufferSize);
|
|
|
|
// reset buffer
|
|
if (bInit) {
|
|
std::fill(buffer.begin(), buffer.end(), 0);
|
|
}
|
|
|
|
uint32_t offset = 0;
|
|
for (const auto& value : uniformBlock.members) {
|
|
CC_EXPECTS(value.count);
|
|
const auto typeSize = getTypeSize(value.type);
|
|
const auto totalSize = typeSize * value.count;
|
|
CC_ENSURES(typeSize);
|
|
CC_ENSURES(totalSize);
|
|
|
|
const auto valueID = [&]() {
|
|
auto iter = lg.constantIndex.find(std::string_view{value.name});
|
|
CC_EXPECTS(iter != lg.constantIndex.end());
|
|
return iter->second.value;
|
|
}();
|
|
|
|
const auto iter2 = user.constants.find(valueID);
|
|
if (iter2 == user.constants.end() || iter2->second.empty()) {
|
|
if (bInit) {
|
|
if (value.type == gfx::Type::MAT4) {
|
|
// init matrix to identity
|
|
CC_EXPECTS(sizeof(Mat4) == typeSize);
|
|
const Mat4 id{};
|
|
for (uint32_t i = 0; i != value.count; ++i) {
|
|
memcpy(buffer.data() + offset + i * typeSize, id.m, typeSize);
|
|
}
|
|
}
|
|
}
|
|
offset += totalSize;
|
|
continue;
|
|
}
|
|
const auto& source = iter2->second;
|
|
CC_EXPECTS(source.size() == totalSize);
|
|
CC_EXPECTS(offset + totalSize <= bufferSize);
|
|
memcpy(buffer.data() + offset, source.data(),
|
|
std::min<size_t>(source.size(), totalSize)); // safe guard min
|
|
offset += totalSize;
|
|
}
|
|
CC_ENSURES(offset == bufferSize);
|
|
}
|
|
|
|
void uploadUniformBuffer(
|
|
gfx::DescriptorSet* passSet,
|
|
uint32_t bindID,
|
|
UniformBlockResource& resource,
|
|
gfx::CommandBuffer* cmdBuff) {
|
|
auto* buffer = resource.bufferPool.allocateBuffer();
|
|
CC_ENSURES(buffer);
|
|
|
|
cmdBuff->updateBuffer(buffer, resource.cpuBuffer.data(), static_cast<uint32_t>(resource.cpuBuffer.size()));
|
|
|
|
CC_EXPECTS(passSet);
|
|
passSet->bindBuffer(bindID, buffer);
|
|
}
|
|
|
|
gfx::DescriptorSet* initDescriptorSet(
|
|
ResourceGraph& resg,
|
|
gfx::Device* device,
|
|
gfx::CommandBuffer* cmdBuff,
|
|
const gfx::DefaultResource& defaultResource,
|
|
const LayoutGraphData& lg,
|
|
const PmrFlatMap<NameLocalID, ResourceGraph::vertex_descriptor>& resourceIndex,
|
|
const DescriptorSetData& set,
|
|
const RenderData& user,
|
|
LayoutGraphNodeResource& node,
|
|
const ResourceAccessNode* accessNode = nullptr,
|
|
const SceneResource* sceneResource = nullptr) {
|
|
// update per pass resources
|
|
const auto& data = set.descriptorSetLayoutData;
|
|
|
|
gfx::DescriptorSet* newSet = node.descriptorSetPool.allocateDescriptorSet();
|
|
for (const auto& block : data.descriptorBlocks) {
|
|
CC_EXPECTS(block.descriptors.size() == block.capacity);
|
|
auto bindID = block.offset;
|
|
switch (block.type) {
|
|
case DescriptorTypeOrder::DYNAMIC_UNIFORM_BUFFER:
|
|
case DescriptorTypeOrder::UNIFORM_BUFFER: {
|
|
for (const auto& d : block.descriptors) {
|
|
// get uniform block
|
|
const auto& uniformBlock = data.uniformBlocks.at(d.descriptorID);
|
|
|
|
auto& resource = node.uniformBuffers.at(d.descriptorID);
|
|
updateCpuUniformBuffer(lg, user, uniformBlock, true, resource.cpuBuffer);
|
|
CC_ENSURES(resource.bufferPool.bufferSize == resource.cpuBuffer.size());
|
|
|
|
// upload gfx buffer
|
|
uploadUniformBuffer(newSet, bindID, resource, cmdBuff);
|
|
|
|
// increase slot
|
|
// TODO(zhouzhenglong): here binding will be refactored in the future
|
|
// current implementation is incorrect, and we assume d.count == 1
|
|
CC_EXPECTS(d.count == 1);
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
}
|
|
case DescriptorTypeOrder::SAMPLER_TEXTURE: {
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
CC_EXPECTS(d.type >= gfx::Type::SAMPLER1D &&
|
|
d.type <= gfx::Type::SAMPLER_CUBE);
|
|
// texture
|
|
if (auto iter = resourceIndex.find(d.descriptorID);
|
|
iter != resourceIndex.end()) {
|
|
// render graph textures
|
|
auto* texture = resg.getTexture(iter->second);
|
|
CC_ENSURES(texture);
|
|
newSet->bindTexture(bindID, texture);
|
|
} else {
|
|
// user provided textures
|
|
bool found = false;
|
|
if (auto iter = user.textures.find(d.descriptorID.value);
|
|
iter != user.textures.end()) {
|
|
newSet->bindTexture(bindID, iter->second.get());
|
|
found = true;
|
|
} else if (sceneResource) {
|
|
auto iter = sceneResource->resourceIndex.find(d.descriptorID);
|
|
if (iter != sceneResource->resourceIndex.end()) {
|
|
CC_EXPECTS(iter->second == ResourceType::STORAGE_IMAGE);
|
|
auto* pTex = sceneResource->storageImages.at(d.descriptorID).get();
|
|
newSet->bindTexture(bindID, pTex);
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
// default textures
|
|
gfx::TextureType type{};
|
|
switch (d.type) {
|
|
case gfx::Type::SAMPLER1D:
|
|
type = gfx::TextureType::TEX1D;
|
|
break;
|
|
case gfx::Type::SAMPLER1D_ARRAY:
|
|
type = gfx::TextureType::TEX1D_ARRAY;
|
|
break;
|
|
case gfx::Type::SAMPLER2D:
|
|
type = gfx::TextureType::TEX2D;
|
|
break;
|
|
case gfx::Type::SAMPLER2D_ARRAY:
|
|
type = gfx::TextureType::TEX2D_ARRAY;
|
|
break;
|
|
case gfx::Type::SAMPLER3D:
|
|
type = gfx::TextureType::TEX3D;
|
|
break;
|
|
case gfx::Type::SAMPLER_CUBE:
|
|
type = gfx::TextureType::CUBE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
newSet->bindTexture(bindID, defaultResource.getTexture(type));
|
|
}
|
|
} // texture end
|
|
|
|
// user provided samplers
|
|
if (auto iter = user.samplers.find(d.descriptorID.value);
|
|
iter != user.samplers.end()) {
|
|
newSet->bindSampler(bindID, iter->second);
|
|
}
|
|
|
|
// increase descriptor binding offset
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
}
|
|
case DescriptorTypeOrder::SAMPLER:
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
auto iter = user.samplers.find(d.descriptorID.value);
|
|
if (iter != user.samplers.end()) {
|
|
newSet->bindSampler(bindID, iter->second);
|
|
} else {
|
|
gfx::SamplerInfo info{};
|
|
auto* sampler = device->getSampler(info);
|
|
newSet->bindSampler(bindID, sampler);
|
|
}
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
case DescriptorTypeOrder::TEXTURE:
|
|
// not supported yet
|
|
CC_EXPECTS(false);
|
|
break;
|
|
case DescriptorTypeOrder::DYNAMIC_STORAGE_BUFFER:
|
|
case DescriptorTypeOrder::STORAGE_BUFFER:
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
bool found = false;
|
|
CC_EXPECTS(d.count == 1);
|
|
if (auto iter = resourceIndex.find(d.descriptorID);
|
|
iter != resourceIndex.end()) {
|
|
// render graph textures
|
|
auto* buffer = resg.getBuffer(iter->second);
|
|
CC_ENSURES(buffer);
|
|
newSet->bindBuffer(bindID, buffer);
|
|
found = true;
|
|
} else if (sceneResource) {
|
|
auto iter = sceneResource->resourceIndex.find(d.descriptorID);
|
|
if (iter != sceneResource->resourceIndex.end()) {
|
|
CC_EXPECTS(iter->second == ResourceType::STORAGE_BUFFER);
|
|
auto* pBuffer = sceneResource->storageBuffers.at(d.descriptorID).get();
|
|
newSet->bindBuffer(bindID, pBuffer);
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
newSet->bindBuffer(bindID, defaultResource.getBuffer());
|
|
}
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
case DescriptorTypeOrder::STORAGE_IMAGE:
|
|
// not supported yet
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
CC_EXPECTS(d.type == gfx::Type::IMAGE2D);
|
|
|
|
auto iter = resourceIndex.find(d.descriptorID);
|
|
if (iter != resourceIndex.end()) {
|
|
gfx::AccessFlags access = gfx::AccessFlagBit::NONE;
|
|
if (accessNode != nullptr) {
|
|
const auto& resID = iter->second;
|
|
// whole access only now.
|
|
auto parentID = parent(resID, resg);
|
|
parentID = parentID == ResourceGraph::null_vertex() ? resID : parentID;
|
|
const auto& resName = get(ResourceGraph::NameTag{}, resg, parentID);
|
|
access = accessNode->resourceStatus.at(resName).accessFlag;
|
|
}
|
|
|
|
// render graph textures
|
|
auto* texture = resg.getTexture(iter->second);
|
|
CC_ENSURES(texture);
|
|
newSet->bindTexture(bindID, texture, 0, access);
|
|
}
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
case DescriptorTypeOrder::INPUT_ATTACHMENT: {
|
|
for (const auto& [descID, resID] : resourceIndex) {
|
|
std::ignore = descID;
|
|
// render graph textures
|
|
auto* texture = resg.getTexture(resID);
|
|
gfx::AccessFlags access = gfx::AccessFlagBit::NONE;
|
|
if (accessNode != nullptr) {
|
|
// whole access only now.
|
|
auto parentID = parent(resID, resg);
|
|
parentID = parentID == ResourceGraph::null_vertex() ? resID : parentID;
|
|
const auto& resName = get(ResourceGraph::NameTag{}, resg, parentID);
|
|
access = accessNode->resourceStatus.at(resName).accessFlag;
|
|
}
|
|
|
|
CC_ENSURES(texture);
|
|
newSet->bindTexture(bindID, texture, 0, access);
|
|
bindID += 1;
|
|
}
|
|
};
|
|
break;
|
|
default:
|
|
CC_EXPECTS(false);
|
|
break;
|
|
}
|
|
}
|
|
newSet->update();
|
|
|
|
return newSet;
|
|
}
|
|
|
|
gfx::DescriptorSet* updatePerPassDescriptorSet(
|
|
gfx::CommandBuffer* cmdBuff,
|
|
const LayoutGraphData& lg,
|
|
const DescriptorSetData& set,
|
|
const RenderData& user,
|
|
LayoutGraphNodeResource& node) {
|
|
// update per pass resources
|
|
const auto& data = set.descriptorSetLayoutData;
|
|
|
|
auto& prevSet = node.descriptorSetPool.getCurrentDescriptorSet();
|
|
gfx::DescriptorSet* newSet = node.descriptorSetPool.allocateDescriptorSet();
|
|
for (const auto& block : data.descriptorBlocks) {
|
|
CC_EXPECTS(block.descriptors.size() == block.capacity);
|
|
auto bindID = block.offset;
|
|
switch (block.type) {
|
|
case DescriptorTypeOrder::DYNAMIC_UNIFORM_BUFFER:
|
|
case DescriptorTypeOrder::UNIFORM_BUFFER: {
|
|
for (const auto& d : block.descriptors) {
|
|
// get uniform block
|
|
const auto& uniformBlock = data.uniformBlocks.at(d.descriptorID);
|
|
|
|
auto& resource = node.uniformBuffers.at(d.descriptorID);
|
|
updateCpuUniformBuffer(lg, user, uniformBlock, false, resource.cpuBuffer);
|
|
|
|
// upload gfx buffer
|
|
uploadUniformBuffer(newSet, bindID, resource, cmdBuff);
|
|
|
|
// increase slot
|
|
// TODO(zhouzhenglong): here binding will be refactored in the future
|
|
// current implementation is incorrect, and we assume d.count == 1
|
|
CC_EXPECTS(d.count == 1);
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
}
|
|
case DescriptorTypeOrder::SAMPLER_TEXTURE: {
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
CC_EXPECTS(d.type >= gfx::Type::SAMPLER1D &&
|
|
d.type <= gfx::Type::SAMPLER_CUBE);
|
|
// textures
|
|
if (auto iter = user.textures.find(d.descriptorID.value);
|
|
iter != user.textures.end()) {
|
|
newSet->bindTexture(bindID, iter->second.get());
|
|
} else {
|
|
auto* prevTexture = prevSet.getTexture(bindID);
|
|
CC_ENSURES(prevTexture);
|
|
newSet->bindTexture(bindID, prevTexture);
|
|
}
|
|
|
|
// samplers
|
|
if (auto iter = user.samplers.find(d.descriptorID.value);
|
|
iter != user.samplers.end()) {
|
|
newSet->bindSampler(bindID, iter->second);
|
|
}
|
|
|
|
// increase descriptor binding offset
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
}
|
|
case DescriptorTypeOrder::SAMPLER:
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
auto iter = user.samplers.find(d.descriptorID.value);
|
|
if (iter != user.samplers.end()) {
|
|
newSet->bindSampler(bindID, iter->second);
|
|
} else {
|
|
auto* prevSampler = prevSet.getSampler(bindID);
|
|
CC_ENSURES(prevSampler);
|
|
newSet->bindSampler(bindID, prevSampler);
|
|
}
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
case DescriptorTypeOrder::TEXTURE:
|
|
// not supported yet
|
|
CC_EXPECTS(false);
|
|
break;
|
|
case DescriptorTypeOrder::DYNAMIC_STORAGE_BUFFER:
|
|
case DescriptorTypeOrder::STORAGE_BUFFER:
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
bool found = false;
|
|
CC_EXPECTS(d.count == 1);
|
|
if (auto iter = user.buffers.find(d.descriptorID.value);
|
|
iter != user.buffers.end()) {
|
|
newSet->bindBuffer(bindID, iter->second.get());
|
|
found = true;
|
|
} else {
|
|
auto* prevBuffer = prevSet.getBuffer(bindID);
|
|
CC_ENSURES(prevBuffer);
|
|
newSet->bindBuffer(bindID, prevBuffer);
|
|
}
|
|
auto name = lg.valueNames[d.descriptorID.value];
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
case DescriptorTypeOrder::STORAGE_IMAGE:
|
|
// not supported yet
|
|
CC_EXPECTS(false);
|
|
break;
|
|
case DescriptorTypeOrder::INPUT_ATTACHMENT:
|
|
CC_EXPECTS(newSet);
|
|
for (const auto& d : block.descriptors) {
|
|
CC_EXPECTS(d.count == 1);
|
|
auto iter = user.textures.find(d.descriptorID.value);
|
|
if (iter != user.textures.end()) {
|
|
newSet->bindTexture(bindID, iter->second.get());
|
|
} else {
|
|
auto* prevTexture = prevSet.getTexture(bindID);
|
|
if (prevTexture) {
|
|
newSet->bindTexture(bindID, prevTexture);
|
|
}
|
|
}
|
|
bindID += d.count;
|
|
}
|
|
break;
|
|
default:
|
|
CC_EXPECTS(false);
|
|
break;
|
|
}
|
|
}
|
|
newSet->update();
|
|
|
|
return newSet;
|
|
}
|
|
|
|
gfx::DescriptorSet* updateCameraUniformBufferAndDescriptorSet(
|
|
RenderGraphVisitorContext& ctx, RenderGraph::vertex_descriptor sceneID) {
|
|
gfx::DescriptorSet* perPassSet = nullptr;
|
|
// update states
|
|
CC_EXPECTS(ctx.currentPassLayoutID != LayoutGraphData::null_vertex());
|
|
const auto& passLayoutID = ctx.currentPassLayoutID;
|
|
auto& layout = get(LayoutGraphData::LayoutTag{}, ctx.lg, passLayoutID);
|
|
auto iter = layout.descriptorSets.find(UpdateFrequency::PER_PASS);
|
|
if (iter != layout.descriptorSets.end()) {
|
|
auto& set = iter->second;
|
|
auto& node = ctx.context.layoutGraphResources.at(passLayoutID);
|
|
const auto& user = get(RenderGraph::DataTag{}, ctx.g, sceneID); // notice: sceneID
|
|
perPassSet = updatePerPassDescriptorSet(ctx.cmdBuff, ctx.lg, set, user, node);
|
|
}
|
|
return perPassSet;
|
|
}
|
|
|
|
void submitUICommands(
|
|
gfx::RenderPass* renderPass,
|
|
uint32_t subpassOrPassLayoutID,
|
|
const scene::Camera* camera,
|
|
gfx::CommandBuffer* cmdBuff) {
|
|
const auto& batches = camera->getScene()->getBatches();
|
|
for (auto* batch : batches) {
|
|
if (!(camera->getVisibility() & batch->getVisFlags())) {
|
|
continue;
|
|
}
|
|
const auto& passes = batch->getPasses();
|
|
for (size_t i = 0; i < batch->getShaders().size(); ++i) {
|
|
const scene::Pass* pass = passes[i];
|
|
if (pass->getSubpassOrPassID() != subpassOrPassLayoutID) {
|
|
continue;
|
|
}
|
|
auto* shader = batch->getShaders()[i];
|
|
auto* inputAssembler = batch->getInputAssembler();
|
|
auto* ds = batch->getDescriptorSet();
|
|
auto* pso = pipeline::PipelineStateManager::getOrCreatePipelineState(
|
|
pass, shader, inputAssembler, renderPass);
|
|
cmdBuff->bindPipelineState(pso);
|
|
cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
|
pass->getDescriptorSet());
|
|
cmdBuff->bindInputAssembler(inputAssembler);
|
|
cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::LOCAL), ds);
|
|
cmdBuff->draw(batch->getDrawInfo());
|
|
}
|
|
}
|
|
}
|
|
|
|
void submitProfilerCommands(
|
|
RenderGraphVisitorContext& ctx,
|
|
RenderGraph::vertex_descriptor vertID,
|
|
const RasterPass& rasterPass) {
|
|
const auto* profiler = ctx.ppl->getProfiler();
|
|
if (!profiler || !profiler->isEnabled()) {
|
|
return;
|
|
}
|
|
auto* renderPass = ctx.currentPass;
|
|
auto* cmdBuff = ctx.cmdBuff;
|
|
const auto& submodel = profiler->getSubModels()[0];
|
|
auto* pass = submodel->getPass(0);
|
|
auto* ia = submodel->getInputAssembler();
|
|
auto* pso = pipeline::PipelineStateManager::getOrCreatePipelineState(
|
|
pass, submodel->getShader(0), ia, renderPass);
|
|
|
|
// profiler pass
|
|
gfx::Viewport profilerViewport{};
|
|
gfx::Rect profilerScissor{};
|
|
profilerViewport.width = profilerScissor.width = rasterPass.width;
|
|
profilerViewport.height = profilerScissor.height = rasterPass.height;
|
|
cmdBuff->setViewport(profilerViewport);
|
|
cmdBuff->setScissor(profilerScissor);
|
|
|
|
auto* passSet = ctx.profilerPerPassDescriptorSets.at(vertID);
|
|
CC_ENSURES(passSet);
|
|
|
|
cmdBuff->bindPipelineState(pso);
|
|
cmdBuff->bindDescriptorSet(static_cast<uint32_t>(pipeline::SetIndex::GLOBAL), passSet);
|
|
cmdBuff->bindDescriptorSet(static_cast<uint32_t>(pipeline::SetIndex::MATERIAL), pass->getDescriptorSet());
|
|
cmdBuff->bindDescriptorSet(static_cast<uint32_t>(pipeline::SetIndex::LOCAL), submodel->getDescriptorSet());
|
|
cmdBuff->bindInputAssembler(ia);
|
|
cmdBuff->draw(ia);
|
|
}
|
|
|
|
const PmrTransparentMap<ccstd::pmr::string, ccstd::pmr::vector<ComputeView>>&
|
|
getComputeViews(RenderGraph::vertex_descriptor passID, const RenderGraph& rg) {
|
|
if (holds<RasterPassTag>(passID, rg)) {
|
|
return get(RasterPassTag{}, passID, rg).computeViews;
|
|
}
|
|
if (holds<RasterSubpassTag>(passID, rg)) {
|
|
return get(RasterSubpassTag{}, passID, rg).computeViews;
|
|
}
|
|
CC_EXPECTS(holds<ComputeTag>(passID, rg));
|
|
return get(ComputeTag{}, passID, rg).computeViews;
|
|
}
|
|
|
|
struct RenderGraphUploadVisitor : boost::dfs_visitor<> {
|
|
void updateAndCreatePerPassDescriptorSet(RenderGraph::vertex_descriptor vertID) const {
|
|
auto* perPassSet = updateCameraUniformBufferAndDescriptorSet(ctx, vertID);
|
|
if (perPassSet) {
|
|
ctx.renderGraphDescriptorSet[vertID] = perPassSet;
|
|
}
|
|
}
|
|
void uploadBlitOrDispatchUniformBlokcs(
|
|
RenderGraph::vertex_descriptor vertID,
|
|
scene::Pass& pass) const {
|
|
pass.update();
|
|
|
|
// get shader
|
|
auto& shader = *pass.getShaderVariant();
|
|
// update material ubo and descriptor set
|
|
// get or create program per-instance descriptor set
|
|
auto& node = ctx.context.layoutGraphResources.at(pass.getPhaseID());
|
|
auto iter = node.programResources.find(std::string_view{shader.getName()});
|
|
if (iter == node.programResources.end()) {
|
|
// make program resource
|
|
auto res = node.programResources.emplace(
|
|
std::piecewise_construct,
|
|
std::forward_as_tuple(shader.getName()),
|
|
std::forward_as_tuple());
|
|
CC_ENSURES(res.second);
|
|
iter = res.first;
|
|
auto& instance = res.first->second;
|
|
|
|
// make per-instance layout
|
|
IntrusivePtr<gfx::DescriptorSetLayout> instanceSetLayout =
|
|
&const_cast<gfx::DescriptorSetLayout&>(
|
|
ctx.programLib->getLocalDescriptorSetLayout(
|
|
ctx.device, pass.getPhaseID(), shader.getName()));
|
|
|
|
// init per-instance descriptor set pool
|
|
instance.descriptorSetPool.init(ctx.device, std::move(instanceSetLayout));
|
|
|
|
for (const auto& block : shader.getBlocks()) {
|
|
if (static_cast<pipeline::SetIndex>(block.set) != pipeline::SetIndex::LOCAL) {
|
|
continue;
|
|
}
|
|
const auto& name = block.name;
|
|
auto iter = ctx.lg.attributeIndex.find(std::string_view{name});
|
|
CC_EXPECTS(iter != ctx.lg.attributeIndex.end());
|
|
const auto attrID = iter->second;
|
|
auto sz = getUniformBlockSize(block.members);
|
|
const auto bDynamic = isDynamicUniformBlock(block.name);
|
|
instance.uniformBuffers[attrID].init(ctx.device, sz, bDynamic);
|
|
}
|
|
}
|
|
|
|
// update per-instance buffer and descriptor set
|
|
const auto& programLib = *dynamic_cast<const NativeProgramLibrary*>(ctx.programLib);
|
|
const auto& data = programLib.localLayoutData;
|
|
auto& instance = iter->second;
|
|
auto* set = instance.descriptorSetPool.allocateDescriptorSet();
|
|
CC_ENSURES(set);
|
|
for (const auto& block : shader.getBlocks()) {
|
|
if (static_cast<pipeline::SetIndex>(block.set) != pipeline::SetIndex::LOCAL) {
|
|
continue;
|
|
}
|
|
// find descriptor name ID
|
|
const auto& name = block.name;
|
|
auto iter = ctx.lg.attributeIndex.find(std::string_view{name});
|
|
CC_EXPECTS(iter != ctx.lg.attributeIndex.end());
|
|
const auto attrID = iter->second;
|
|
|
|
// get uniformBuffer
|
|
auto& uniformBuffer = instance.uniformBuffers[attrID];
|
|
CC_EXPECTS(uniformBuffer.cpuBuffer.size() == uniformBuffer.bufferPool.bufferSize);
|
|
|
|
// fill cpu buffer
|
|
auto& cpuData = uniformBuffer.cpuBuffer;
|
|
if (false) { // NOLINT(readability-simplify-boolean-expr)
|
|
for (const auto& v : block.members) {
|
|
CC_LOG_INFO(v.name.c_str());
|
|
}
|
|
}
|
|
|
|
// create and upload buffer
|
|
auto* buffer = uniformBuffer.createFromCpuBuffer();
|
|
|
|
// set buffer descriptor
|
|
const auto binding = data.bindingMap.at(attrID);
|
|
set->bindBuffer(binding, buffer);
|
|
}
|
|
ctx.perInstanceDescriptorSets[vertID] = set;
|
|
}
|
|
|
|
const SceneResource* getFirstSceneResource(RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& g = ctx.g;
|
|
CC_EXPECTS(holds<QueueTag>(vertID, g));
|
|
for (const auto e : makeRange(children(vertID, g))) {
|
|
const auto sceneID = target(e, g);
|
|
if (holds<SceneTag>(sceneID, g)) {
|
|
const auto& sceneData = get(SceneTag{}, sceneID, g);
|
|
auto iter = ctx.context.renderSceneResources.find(sceneData.scene);
|
|
if (iter != ctx.context.renderSceneResources.end()) {
|
|
return &iter->second;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void discover_vertex(
|
|
RenderGraph::vertex_descriptor vertID,
|
|
const boost::filtered_graph<
|
|
AddressableView<RenderGraph>, boost::keep_all, RenderGraphFilter>& gv) const {
|
|
std::ignore = gv;
|
|
CC_EXPECTS(ctx.currentPassLayoutID != LayoutGraphData::null_vertex());
|
|
|
|
if (holds<RasterPassTag>(vertID, ctx.g) || holds<ComputeTag>(vertID, ctx.g)) {
|
|
// const auto& pass = get(RasterPassTag{}, vertID, ctx.g);
|
|
const auto& computeViews =
|
|
holds<RasterPassTag>(vertID, ctx.g)
|
|
? get(RasterPassTag{}, vertID, ctx.g).computeViews
|
|
: get(ComputeTag{}, vertID, ctx.g).computeViews;
|
|
|
|
// render pass
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
const auto& layoutID = locate(LayoutGraphData::null_vertex(), layoutName, ctx.lg);
|
|
CC_EXPECTS(layoutID == ctx.currentPassLayoutID);
|
|
// get layout
|
|
auto& layout = get(LayoutGraphData::LayoutTag{}, ctx.lg, layoutID);
|
|
|
|
// update states
|
|
auto iter = layout.descriptorSets.find(UpdateFrequency::PER_PASS);
|
|
if (iter == layout.descriptorSets.end()) {
|
|
return;
|
|
}
|
|
|
|
// build pass resources
|
|
const auto& resourceIndex = ctx.fgd.buildDescriptorIndex(computeViews, ctx.scratch);
|
|
|
|
// populate set
|
|
auto& set = iter->second;
|
|
const auto& user = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
auto& node = ctx.context.layoutGraphResources.at(layoutID);
|
|
const auto& accessNode = ctx.fgd.getAccessNode(vertID);
|
|
auto* perPassSet = initDescriptorSet(
|
|
ctx.resourceGraph,
|
|
ctx.device, ctx.cmdBuff,
|
|
*ctx.context.defaultResource, ctx.lg,
|
|
resourceIndex, set, user, node, &accessNode);
|
|
CC_ENSURES(perPassSet);
|
|
ctx.renderGraphDescriptorSet[vertID] = perPassSet;
|
|
} else if (holds<QueueTag>(vertID, ctx.g)) {
|
|
const auto& queue = get(QueueTag{}, vertID, ctx.g);
|
|
if (queue.phaseID == LayoutGraphData::null_vertex()) {
|
|
return;
|
|
}
|
|
|
|
const auto layoutID = queue.phaseID;
|
|
|
|
const auto passID = parent(vertID, ctx.g);
|
|
const auto& computeViews = getComputeViews(passID, ctx.g);
|
|
|
|
// get layout
|
|
auto& layout = get(LayoutGraphData::LayoutTag{}, ctx.lg, layoutID);
|
|
auto iter = layout.descriptorSets.find(UpdateFrequency::PER_PHASE);
|
|
if (iter == layout.descriptorSets.end()) {
|
|
return;
|
|
}
|
|
|
|
// build pass resources
|
|
const auto& resourceIndex = ctx.fgd.buildDescriptorIndex(computeViews, ctx.scratch);
|
|
|
|
// find scene resource
|
|
const auto* const sceneResource = getFirstSceneResource(vertID);
|
|
|
|
// populate set
|
|
auto& set = iter->second;
|
|
const auto& user = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
auto& node = ctx.context.layoutGraphResources.at(layoutID);
|
|
|
|
auto* perPhaseSet = initDescriptorSet(
|
|
ctx.resourceGraph,
|
|
ctx.device, ctx.cmdBuff,
|
|
*ctx.context.defaultResource, ctx.lg,
|
|
resourceIndex, set, user, node, nullptr, sceneResource);
|
|
CC_ENSURES(perPhaseSet);
|
|
|
|
ctx.renderGraphDescriptorSet[vertID] = perPhaseSet;
|
|
} else if (holds<SceneTag>(vertID, ctx.g)) {
|
|
const auto& sceneData = get(SceneTag{}, vertID, ctx.g);
|
|
if (sceneData.camera) {
|
|
updateAndCreatePerPassDescriptorSet(vertID);
|
|
ctx.currentProjMatrix = sceneData.camera->getMatProj();
|
|
}
|
|
} else if (holds<BlitTag>(vertID, ctx.g)) {
|
|
const auto& blit = get(BlitTag{}, vertID, ctx.g);
|
|
if (blit.camera) {
|
|
updateAndCreatePerPassDescriptorSet(vertID);
|
|
ctx.currentProjMatrix = blit.camera->getMatProj();
|
|
}
|
|
// get pass
|
|
auto& pass = *blit.material->getPasses()->at(static_cast<size_t>(blit.passID));
|
|
uploadBlitOrDispatchUniformBlokcs(vertID, pass);
|
|
} else if (holds<DispatchTag>(vertID, ctx.g)) {
|
|
const auto& dispatch = get(DispatchTag{}, vertID, ctx.g);
|
|
auto& pass = *dispatch.material->getPasses()->at(static_cast<size_t>(dispatch.passID));
|
|
uploadBlitOrDispatchUniformBlokcs(vertID, pass);
|
|
} else if (holds<RasterSubpassTag>(vertID, ctx.g)) {
|
|
const auto& subpass = get(RasterSubpassTag{}, vertID, ctx.g);
|
|
// render pass
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
auto parentLayoutID = ctx.currentPassLayoutID;
|
|
auto layoutID = parentLayoutID;
|
|
if (!layoutName.empty()) {
|
|
auto parentID = parent(ctx.currentPassLayoutID, ctx.lg);
|
|
if (parentID != LayoutGraphData::null_vertex()) {
|
|
parentLayoutID = parentID;
|
|
}
|
|
layoutID = locate(parentLayoutID, layoutName, ctx.lg);
|
|
}
|
|
|
|
ctx.currentPassLayoutID = layoutID;
|
|
// get layout
|
|
auto& layout = get(LayoutGraphData::LayoutTag{}, ctx.lg, layoutID);
|
|
|
|
// update states
|
|
auto iter = layout.descriptorSets.find(UpdateFrequency::PER_PASS);
|
|
if (iter == layout.descriptorSets.end()) {
|
|
return;
|
|
}
|
|
|
|
const auto& resourceIndex = ctx.fgd.buildDescriptorIndex(subpass.computeViews, subpass.rasterViews, ctx.scratch);
|
|
// populate set
|
|
auto& set = iter->second;
|
|
const auto& user = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
auto& node = ctx.context.layoutGraphResources.at(layoutID);
|
|
const auto& accessNode = ctx.fgd.getAccessNode(vertID);
|
|
|
|
auto* perPassSet = initDescriptorSet(
|
|
ctx.resourceGraph,
|
|
ctx.device, ctx.cmdBuff,
|
|
*ctx.context.defaultResource, ctx.lg,
|
|
resourceIndex, set, user, node, &accessNode);
|
|
CC_ENSURES(perPassSet);
|
|
ctx.renderGraphDescriptorSet[vertID] = perPassSet;
|
|
}
|
|
}
|
|
|
|
RenderGraphVisitorContext& ctx;
|
|
};
|
|
|
|
struct RenderGraphVisitor : boost::dfs_visitor<> {
|
|
void submitBarriers(const std::vector<Barrier>& barriers) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
auto sz = barriers.size();
|
|
ccstd::pmr::vector<const gfx::Buffer*> buffers(ctx.scratch);
|
|
ccstd::pmr::vector<const gfx::BufferBarrier*> bufferBarriers(ctx.scratch);
|
|
ccstd::pmr::vector<const gfx::Texture*> textures(ctx.scratch);
|
|
ccstd::pmr::vector<const gfx::TextureBarrier*> textureBarriers(ctx.scratch);
|
|
buffers.reserve(sz);
|
|
bufferBarriers.reserve(sz);
|
|
textures.reserve(sz);
|
|
textureBarriers.reserve(sz);
|
|
for (const auto& barrier : barriers) {
|
|
const auto resID = barrier.resourceID;
|
|
const auto& desc = get(ResourceGraph::DescTag{}, resg, resID);
|
|
const auto& resource = get(ResourceGraph::DescTag{}, resg, resID);
|
|
switch (desc.dimension) {
|
|
case ResourceDimension::BUFFER: {
|
|
auto* buffer = resg.getBuffer(resID);
|
|
const auto* bufferBarrier = static_cast<gfx::BufferBarrier*>(barrier.barrier);
|
|
buffers.emplace_back(buffer);
|
|
bufferBarriers.emplace_back(bufferBarrier);
|
|
break;
|
|
}
|
|
case ResourceDimension::TEXTURE1D:
|
|
case ResourceDimension::TEXTURE2D:
|
|
case ResourceDimension::TEXTURE3D:
|
|
default: {
|
|
auto* texture = resg.getTexture(resID);
|
|
const auto* textureBarrier = static_cast<gfx::TextureBarrier*>(barrier.barrier);
|
|
textures.emplace_back(texture);
|
|
textureBarriers.emplace_back(textureBarrier);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CC_EXPECTS(buffers.size() == bufferBarriers.size());
|
|
CC_EXPECTS(textures.size() == textureBarriers.size());
|
|
|
|
ctx.cmdBuff->pipelineBarrier(
|
|
nullptr,
|
|
bufferBarriers.data(), buffers.data(), static_cast<uint32_t>(bufferBarriers.size()),
|
|
textureBarriers.data(), textures.data(), static_cast<uint32_t>(textureBarriers.size()));
|
|
}
|
|
void frontBarriers(RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& barrier = ctx.fgd.getBarrier(vertID);
|
|
if (!barrier.frontBarriers.empty()) {
|
|
submitBarriers(barrier.frontBarriers);
|
|
}
|
|
}
|
|
void rearBarriers(RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& barrier = ctx.fgd.getBarrier(vertID);
|
|
if (!barrier.rearBarriers.empty()) {
|
|
submitBarriers(barrier.rearBarriers);
|
|
}
|
|
}
|
|
void tryBindPerPassDescriptorSet(RenderGraph::vertex_descriptor vertID) const {
|
|
auto iter = ctx.renderGraphDescriptorSet.find(vertID);
|
|
if (iter != ctx.renderGraphDescriptorSet.end()) {
|
|
CC_ENSURES(iter->second);
|
|
ctx.cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::GLOBAL),
|
|
iter->second);
|
|
}
|
|
}
|
|
void tryBindPerPhaseDescriptorSet(RenderGraph::vertex_descriptor vertID) const {
|
|
auto iter = ctx.renderGraphDescriptorSet.find(vertID);
|
|
if (iter != ctx.renderGraphDescriptorSet.end()) {
|
|
CC_ENSURES(iter->second);
|
|
static_assert(static_cast<uint32_t>(pipeline::SetIndex::COUNT) == 3);
|
|
ctx.cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::COUNT),
|
|
iter->second);
|
|
}
|
|
}
|
|
void begin(const RasterPass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& passes = ctx.ppl->custom.renderPasses;
|
|
auto iter = passes.find(renderData.custom);
|
|
if (iter != passes.end()) {
|
|
iter->second->beginRenderPass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// viewport
|
|
auto vp = pass.viewport;
|
|
if (vp.width == 0 && vp.height == 0) {
|
|
vp.width = pass.width;
|
|
vp.height = pass.height;
|
|
}
|
|
// scissor
|
|
gfx::Rect scissor{0, 0, vp.width, vp.height};
|
|
|
|
// render pass
|
|
{
|
|
ctx.currentInFlightPassID = vertID;
|
|
auto& res = fetchOrCreateFramebuffer(ctx, pass, ctx.scratch);
|
|
const auto& data = res;
|
|
auto* cmdBuff = ctx.cmdBuff;
|
|
cmdBuff->beginRenderPass(
|
|
data.renderPass.get(),
|
|
data.framebuffer.get(),
|
|
scissor, data.clearColors.data(),
|
|
data.clearDepth, data.clearStencil);
|
|
ctx.currentPass = data.renderPass.get();
|
|
}
|
|
|
|
// PerPass DescriptorSet
|
|
tryBindPerPassDescriptorSet(vertID);
|
|
}
|
|
void begin(const RasterSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->insertMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR));
|
|
#endif
|
|
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& subpasses = ctx.ppl->custom.renderSubpasses;
|
|
auto iter = subpasses.find(renderData.custom);
|
|
if (iter != subpasses.end()) {
|
|
iter->second->beginRenderSubpass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// PerPass DescriptorSet
|
|
if (subpass.subpassID) {
|
|
ctx.cmdBuff->nextSubpass();
|
|
}
|
|
// ctx.cmdBuff->setViewport(subpass);
|
|
tryBindPerPassDescriptorSet(vertID);
|
|
ctx.subpassIndex = subpass.subpassID;
|
|
// noop
|
|
}
|
|
void begin(const ComputeSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& subpasses = ctx.ppl->custom.computeSubpasses;
|
|
auto iter = subpasses.find(renderData.custom);
|
|
if (iter != subpasses.end()) {
|
|
iter->second->beginComputeSubpass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::ignore = subpass;
|
|
std::ignore = vertID;
|
|
// noop
|
|
}
|
|
void begin(const ComputePass& pass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), COMPUTE_COLOR));
|
|
#endif
|
|
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& passes = ctx.ppl->custom.computePasses;
|
|
auto iter = passes.find(renderData.custom);
|
|
if (iter != passes.end()) {
|
|
iter->second->beginComputePass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
for (const auto& view : views) {
|
|
if (view.clearFlags != gfx::ClearFlags::NONE) {
|
|
// clear resources
|
|
}
|
|
}
|
|
}
|
|
tryBindPerPassDescriptorSet(vertID);
|
|
}
|
|
void begin(const ResolvePass& pass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
for (const auto& copy : pass.resolvePairs) {
|
|
// TODO(zhenglong.zhou): resolve
|
|
}
|
|
}
|
|
void copyTexture(
|
|
const CopyPair& copy,
|
|
ResourceGraph::vertex_descriptor srcID,
|
|
ResourceGraph::vertex_descriptor dstID) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
std::vector<gfx::TextureCopy> copyInfos(copy.mipLevels, gfx::TextureCopy{});
|
|
|
|
gfx::Texture* srcTexture = resg.getTexture(srcID);
|
|
gfx::Texture* dstTexture = resg.getTexture(dstID);
|
|
CC_ENSURES(srcTexture);
|
|
CC_ENSURES(dstTexture);
|
|
if (!srcTexture || !dstTexture) {
|
|
return;
|
|
}
|
|
const auto& srcInfo = srcTexture->getInfo();
|
|
const auto& dstInfo = dstTexture->getInfo();
|
|
CC_ENSURES(srcInfo.width == dstInfo.width);
|
|
CC_ENSURES(srcInfo.height == dstInfo.height);
|
|
CC_ENSURES(srcInfo.depth == dstInfo.depth);
|
|
|
|
for (uint32_t i = 0; i < copy.mipLevels; ++i) {
|
|
auto& copyInfo = copyInfos[i];
|
|
copyInfo.srcSubres.mipLevel = copy.sourceMostDetailedMip + i;
|
|
copyInfo.srcSubres.baseArrayLayer = copy.sourceFirstSlice;
|
|
copyInfo.srcSubres.layerCount = copy.numSlices;
|
|
|
|
copyInfo.dstSubres.mipLevel = copy.targetMostDetailedMip + i;
|
|
copyInfo.dstSubres.baseArrayLayer = copy.targetFirstSlice;
|
|
copyInfo.dstSubres.layerCount = copy.numSlices;
|
|
|
|
copyInfo.srcOffset = {0, 0, 0};
|
|
copyInfo.dstOffset = {0, 0, 0};
|
|
copyInfo.extent = {srcInfo.width, srcInfo.height, srcInfo.depth};
|
|
}
|
|
|
|
ctx.cmdBuff->copyTexture(srcTexture, dstTexture, copyInfos.data(), static_cast<uint32_t>(copyInfos.size()));
|
|
}
|
|
void copyBuffer( // NOLINT(readability-convert-member-functions-to-static)
|
|
const CopyPair& copy,
|
|
ResourceGraph::vertex_descriptor srcID,
|
|
ResourceGraph::vertex_descriptor dstID) const {
|
|
// TODO(zhouzhenglong): add impl
|
|
std::ignore = copy;
|
|
std::ignore = srcID;
|
|
std::ignore = dstID;
|
|
}
|
|
void uploadTexture( // NOLINT(readability-convert-member-functions-to-static)
|
|
const UploadPair& upload,
|
|
ResourceGraph::vertex_descriptor dstID) const {
|
|
// TODO(zhouzhenglong): add impl
|
|
std::ignore = upload;
|
|
std::ignore = dstID;
|
|
}
|
|
void uploadBuffer(
|
|
const UploadPair& upload,
|
|
ResourceGraph::vertex_descriptor dstID) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
gfx::Buffer* dstBuffer = resg.getBuffer(dstID);
|
|
CC_ENSURES(dstBuffer);
|
|
if (!dstBuffer) {
|
|
return;
|
|
}
|
|
ctx.cmdBuff->updateBuffer(dstBuffer, upload.source.data(), static_cast<uint32_t>(upload.source.size()));
|
|
}
|
|
void begin(const CopyPass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
auto& resg = ctx.resourceGraph;
|
|
|
|
// currently, only texture to texture supported.
|
|
for (const auto& copy : pass.copyPairs) {
|
|
auto srcID = findVertex(copy.source, resg);
|
|
auto dstID = findVertex(copy.target, resg);
|
|
CC_ENSURES(srcID != RenderGraph::null_vertex());
|
|
CC_ENSURES(dstID != RenderGraph::null_vertex());
|
|
if (srcID == RenderGraph::null_vertex() || dstID == RenderGraph::null_vertex()) {
|
|
continue;
|
|
}
|
|
const bool sourceIsTexture = resg.isTexture(srcID);
|
|
const bool targetIsTexture = resg.isTexture(dstID);
|
|
CC_ENSURES(sourceIsTexture == targetIsTexture);
|
|
if (sourceIsTexture != targetIsTexture) {
|
|
continue;
|
|
}
|
|
if (targetIsTexture) {
|
|
copyTexture(copy, srcID, dstID);
|
|
} else {
|
|
copyBuffer(copy, srcID, dstID);
|
|
}
|
|
}
|
|
// copy from cpu
|
|
for (const auto& upload : pass.uploadPairs) {
|
|
auto dstID = findVertex(upload.target, resg);
|
|
CC_ENSURES(dstID != RenderGraph::null_vertex());
|
|
if (dstID == RenderGraph::null_vertex()) {
|
|
continue;
|
|
}
|
|
const bool targetIsTexture = resg.isTexture(dstID);
|
|
if (targetIsTexture) {
|
|
uploadTexture(upload, dstID);
|
|
} else {
|
|
uploadBuffer(upload, dstID);
|
|
}
|
|
}
|
|
}
|
|
void begin(const MovePass& pass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
// if fully optimized, move pass should have been removed from graph
|
|
// here we just do copy
|
|
for (const auto& copy : pass.movePairs) {
|
|
}
|
|
}
|
|
void begin(const RaytracePass& pass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
// not implemented yet
|
|
CC_EXPECTS(false);
|
|
}
|
|
void begin(const RenderQueue& queue, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RENDER_QUEUE_COLOR));
|
|
#endif
|
|
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& queues = ctx.ppl->custom.renderQueues;
|
|
auto iter = queues.find(renderData.custom);
|
|
if (iter != queues.end()) {
|
|
iter->second->beginRenderQueue(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
if (queue.viewport.width != 0 && queue.viewport.height != 0) {
|
|
ctx.cmdBuff->setViewport(queue.viewport);
|
|
}
|
|
|
|
// PerPhase DescriptorSet
|
|
tryBindPerPhaseDescriptorSet(vertID);
|
|
}
|
|
void begin(const SceneData& sceneData, RenderGraph::vertex_descriptor sceneID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
const auto* const camera = sceneData.camera;
|
|
CC_EXPECTS(camera);
|
|
if (camera) { // update camera data
|
|
tryBindPerPassDescriptorSet(sceneID);
|
|
}
|
|
const auto* scene = camera->getScene();
|
|
const auto& queueDesc = ctx.context.sceneCulling.renderQueueIndex.at(sceneID);
|
|
const auto& queue = ctx.context.sceneCulling.renderQueues[queueDesc.renderQueueTarget.value];
|
|
|
|
queue.recordCommands(ctx.cmdBuff, ctx.currentPass, 0);
|
|
|
|
if (any(sceneData.flags & SceneFlags::REFLECTION_PROBE)) {
|
|
queue.probeQueue.removeMacro();
|
|
}
|
|
if (any(sceneData.flags & SceneFlags::UI)) {
|
|
submitUICommands(ctx.currentPass,
|
|
ctx.currentPassLayoutID, camera, ctx.cmdBuff);
|
|
}
|
|
}
|
|
void begin(const Blit& blit, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& commands = ctx.ppl->custom.renderCommands;
|
|
auto iter = commands.find(renderData.custom);
|
|
if (iter != commands.end()) {
|
|
iter->second->beginRenderCommand(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const auto& programLib = *dynamic_cast<const NativeProgramLibrary*>(ctx.programLib);
|
|
CC_EXPECTS(blit.material);
|
|
CC_EXPECTS(blit.material->getPasses());
|
|
if (blit.camera) {
|
|
tryBindPerPassDescriptorSet(vertID);
|
|
}
|
|
// get pass
|
|
auto& pass = *blit.material->getPasses()->at(static_cast<size_t>(blit.passID));
|
|
// get shader
|
|
auto& shader = *pass.getShaderVariant();
|
|
// get pso
|
|
auto* pso = pipeline::PipelineStateManager::getOrCreatePipelineState(
|
|
&pass, &shader, ctx.context.fullscreenQuad.quadIA.get(), ctx.currentPass, ctx.subpassIndex);
|
|
if (!pso) {
|
|
return;
|
|
}
|
|
// auto* perInstanceSet = ctx.perInstanceDescriptorSets.at(vertID);
|
|
// execution
|
|
ctx.cmdBuff->bindPipelineState(pso);
|
|
ctx.cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL), pass.getDescriptorSet());
|
|
// ctx.cmdBuff->bindDescriptorSet(
|
|
// static_cast<uint32_t>(pipeline::SetIndex::LOCAL), perInstanceSet);
|
|
ctx.cmdBuff->bindInputAssembler(ctx.context.fullscreenQuad.quadIA.get());
|
|
ctx.cmdBuff->draw(ctx.context.fullscreenQuad.quadIA.get());
|
|
}
|
|
void begin(const Dispatch& dispatch, RenderGraph::vertex_descriptor vertID) const {
|
|
std::ignore = vertID;
|
|
auto& programLib = *ctx.programLib;
|
|
CC_EXPECTS(dispatch.material);
|
|
CC_EXPECTS(dispatch.material->getPasses());
|
|
// get pass
|
|
auto& pass = *dispatch.material->getPasses()->at(static_cast<size_t>(dispatch.passID));
|
|
// get shader
|
|
auto& shader = *pass.getShaderVariant();
|
|
// get pso
|
|
auto* pso = programLib.getComputePipelineState(
|
|
pass.getDevice(), pass.getPhaseID(), pass.getProgram(), pass.getDefines(), nullptr);
|
|
CC_EXPECTS(pso);
|
|
// auto* perInstanceSet = ctx.perInstanceDescriptorSets.at(vertID);
|
|
// execution
|
|
ctx.cmdBuff->bindPipelineState(pso);
|
|
ctx.cmdBuff->bindDescriptorSet(
|
|
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL), pass.getDescriptorSet());
|
|
// ctx.cmdBuff->bindDescriptorSet(
|
|
// static_cast<uint32_t>(pipeline::SetIndex::LOCAL), perInstanceSet);
|
|
ctx.cmdBuff->dispatch(gfx::DispatchInfo{
|
|
dispatch.threadGroupCountX,
|
|
dispatch.threadGroupCountY,
|
|
dispatch.threadGroupCountZ,
|
|
});
|
|
}
|
|
void begin(const ccstd::pmr::vector<ClearView>& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void begin(const gfx::Viewport& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const RasterPass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& passes = ctx.ppl->custom.renderPasses;
|
|
auto iter = passes.find(renderData.custom);
|
|
if (iter != passes.end()) {
|
|
iter->second->endRenderPass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pass.showStatistics) {
|
|
submitProfilerCommands(ctx, vertID, pass);
|
|
}
|
|
ctx.cmdBuff->endRenderPass();
|
|
ctx.currentPass = nullptr;
|
|
ctx.currentPassLayoutID = LayoutGraphData::null_vertex();
|
|
}
|
|
void end(const RasterSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& subpasses = ctx.ppl->custom.renderSubpasses;
|
|
auto iter = subpasses.find(renderData.custom);
|
|
if (iter != subpasses.end()) {
|
|
iter->second->endRenderSubpass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::ignore = subpass;
|
|
std::ignore = vertID;
|
|
ctx.subpassIndex = 0;
|
|
// noop
|
|
}
|
|
void end(const ComputeSubpass& subpass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& subpasses = ctx.ppl->custom.computeSubpasses;
|
|
auto iter = subpasses.find(renderData.custom);
|
|
if (iter != subpasses.end()) {
|
|
iter->second->endComputeSubpass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::ignore = subpass;
|
|
std::ignore = vertID;
|
|
// noop
|
|
}
|
|
void end(const ComputePass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& passes = ctx.ppl->custom.computePasses;
|
|
auto iter = passes.find(renderData.custom);
|
|
if (iter != passes.end()) {
|
|
iter->second->endComputePass(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->endMarker();
|
|
#endif
|
|
std::ignore = pass;
|
|
}
|
|
void end(const ResolvePass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const CopyPass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const MovePass& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const RaytracePass& pass, RenderGraph::vertex_descriptor vertID) const { // NOLINT(readability-convert-member-functions-to-static)
|
|
std::ignore = pass;
|
|
std::ignore = vertID;
|
|
// not implemented yet
|
|
CC_EXPECTS(false);
|
|
}
|
|
void end(const RenderQueue& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& queues = ctx.ppl->custom.renderQueues;
|
|
auto iter = queues.find(renderData.custom);
|
|
if (iter != queues.end()) {
|
|
iter->second->endRenderQueue(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->endMarker();
|
|
#endif
|
|
std::ignore = pass;
|
|
}
|
|
void end(const SceneData& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const Blit& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
const auto& renderData = get(RenderGraph::DataTag{}, ctx.g, vertID);
|
|
if (!renderData.custom.empty()) {
|
|
const auto& commands = ctx.ppl->custom.renderCommands;
|
|
auto iter = commands.find(renderData.custom);
|
|
if (iter != commands.end()) {
|
|
iter->second->endRenderCommand(ctx.customContext, vertID);
|
|
return;
|
|
}
|
|
}
|
|
std::ignore = pass;
|
|
}
|
|
void end(const Dispatch& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const ccstd::pmr::vector<ClearView>& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
void end(const gfx::Viewport& pass, RenderGraph::vertex_descriptor vertID) const {
|
|
}
|
|
|
|
void mountResource(const ccstd::pmr::string& name) const { // NOLINT(misc-no-recursion)
|
|
auto resIter = ctx.fgd.resourceAccessGraph.resourceIndex.find(name);
|
|
if (resIter != ctx.fgd.resourceAccessGraph.resourceIndex.end()) {
|
|
auto resID = resIter->second;
|
|
auto& resg = ctx.resourceGraph;
|
|
resg.mount(ctx.device, resID);
|
|
for (const auto& subres : makeRange(children(resID, resg))) {
|
|
const auto& subresName = get(ResourceGraph::NameTag{}, resg, subres.target);
|
|
mountResource(subresName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void mountResources(const Subpass& pass) const {
|
|
// mount managed resources
|
|
for (const auto& [name, view] : pass.rasterViews) {
|
|
mountResource(name);
|
|
}
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
mountResource(name);
|
|
}
|
|
for (const auto& resolve : pass.resolvePairs) {
|
|
mountResource(resolve.target);
|
|
}
|
|
}
|
|
|
|
void mountResources(const RasterPass& pass) const {
|
|
// mount managed resources
|
|
for (const auto& [name, view] : pass.rasterViews) {
|
|
mountResource(name);
|
|
}
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
mountResource(name);
|
|
}
|
|
for (const auto& subpass : pass.subpassGraph.subpasses) {
|
|
mountResources(subpass);
|
|
}
|
|
}
|
|
|
|
void mountResources(const ComputePass& pass) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
PmrFlatSet<ResourceGraph::vertex_descriptor> mounted(ctx.scratch);
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
mountResource(name);
|
|
}
|
|
}
|
|
|
|
void mountResources(const ComputeSubpass& pass) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
PmrFlatSet<ResourceGraph::vertex_descriptor> mounted(ctx.scratch);
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
mountResource(name);
|
|
}
|
|
}
|
|
|
|
void mountResources(const RaytracePass& pass) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
PmrFlatSet<ResourceGraph::vertex_descriptor> mounted(ctx.scratch);
|
|
for (const auto& [name, views] : pass.computeViews) {
|
|
mountResource(name);
|
|
}
|
|
}
|
|
|
|
void mountResources(const ResolvePass& pass) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
PmrFlatSet<ResourceGraph::vertex_descriptor> mounted(ctx.scratch);
|
|
for (const auto& pair : pass.resolvePairs) {
|
|
mountResource(pair.source);
|
|
mountResource(pair.target);
|
|
}
|
|
}
|
|
|
|
void mountResources(const CopyPass& pass) const {
|
|
auto& resg = ctx.resourceGraph;
|
|
PmrFlatSet<ResourceGraph::vertex_descriptor> mounted(ctx.scratch);
|
|
for (const auto& pair : pass.copyPairs) {
|
|
mountResource(pair.source);
|
|
mountResource(pair.target);
|
|
}
|
|
for (const auto& pair : pass.uploadPairs) {
|
|
mountResource(pair.target);
|
|
}
|
|
}
|
|
|
|
void mountResources(const MovePass& pass) const {
|
|
// not supported yet
|
|
}
|
|
|
|
void discover_vertex(
|
|
RenderGraph::vertex_descriptor vertID,
|
|
const boost::filtered_graph<AddressableView<RenderGraph>, boost::keep_all, RenderGraphFilter>& gv) const {
|
|
std::ignore = gv;
|
|
|
|
visitObject(
|
|
vertID, ctx.g,
|
|
[&](const RasterPass& pass) {
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->beginMarker(makeMarkerInfo(get(RenderGraph::NameTag{}, ctx.g, vertID).c_str(), RASTER_COLOR));
|
|
#endif
|
|
mountResources(pass);
|
|
{
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
const auto& layoutID = locate(LayoutGraphData::null_vertex(), layoutName, ctx.lg);
|
|
ctx.currentPassLayoutID = layoutID;
|
|
}
|
|
// update UniformBuffers and DescriptorSets in all children
|
|
{
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->beginMarker(makeMarkerInfo("Upload", RASTER_UPLOAD_COLOR));
|
|
#endif
|
|
auto colors = ctx.g.colors(ctx.scratch);
|
|
RenderGraphUploadVisitor visitor{{}, ctx};
|
|
boost::depth_first_visit(gv, vertID, visitor, get(colors, ctx.g));
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->endMarker();
|
|
#endif
|
|
}
|
|
if (pass.showStatistics) {
|
|
const auto* profiler = ctx.ppl->getProfiler();
|
|
if (profiler && profiler->isEnabled()) {
|
|
// current pass
|
|
RenderData user(ctx.scratch);
|
|
setMat4Impl(user, ctx.lg, "cc_matProj", ctx.currentProjMatrix);
|
|
|
|
auto* renderPass = ctx.currentPass;
|
|
auto* cmdBuff = ctx.cmdBuff;
|
|
const auto& submodel = profiler->getSubModels()[0];
|
|
auto* pass = submodel->getPass(0);
|
|
auto& layout = get(LayoutGraphData::LayoutTag{}, ctx.lg, pass->getSubpassOrPassID());
|
|
auto iter = layout.descriptorSets.find(UpdateFrequency::PER_PASS);
|
|
if (iter != layout.descriptorSets.end()) {
|
|
auto& set = iter->second;
|
|
auto& node = ctx.context.layoutGraphResources.at(pass->getSubpassOrPassID());
|
|
PmrFlatMap<NameLocalID, ResourceGraph::vertex_descriptor> resourceIndex(ctx.scratch);
|
|
auto* perPassSet = initDescriptorSet(
|
|
ctx.resourceGraph,
|
|
ctx.device, ctx.cmdBuff,
|
|
*ctx.context.defaultResource, ctx.lg,
|
|
resourceIndex, set, user, node);
|
|
CC_ENSURES(perPassSet);
|
|
ctx.profilerPerPassDescriptorSets[vertID] = perPassSet;
|
|
} else {
|
|
CC_EXPECTS(false);
|
|
// TODO(zhouzhenglong): set descriptor set to empty
|
|
}
|
|
}
|
|
}
|
|
|
|
// execute render pass
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const RasterSubpass& subpass) {
|
|
// mountResources(subpass);
|
|
{
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
const auto& layoutID = locate(LayoutGraphData::null_vertex(), layoutName, ctx.lg);
|
|
ctx.currentPassLayoutID = layoutID;
|
|
}
|
|
begin(subpass, vertID);
|
|
},
|
|
[&](const ComputeSubpass& subpass) {
|
|
// mountResources(subpass);
|
|
{
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
const auto& layoutID = locate(LayoutGraphData::null_vertex(), layoutName, ctx.lg);
|
|
ctx.currentPassLayoutID = layoutID;
|
|
}
|
|
begin(subpass, vertID);
|
|
},
|
|
[&](const ComputePass& pass) {
|
|
mountResources(pass);
|
|
|
|
{
|
|
const auto& layoutName = get(RenderGraph::LayoutTag{}, ctx.g, vertID);
|
|
const auto& layoutID = locate(LayoutGraphData::null_vertex(), layoutName, ctx.lg);
|
|
ctx.currentPassLayoutID = layoutID;
|
|
}
|
|
{
|
|
auto colors = ctx.g.colors(ctx.scratch);
|
|
RenderGraphUploadVisitor visitor{{}, ctx};
|
|
boost::depth_first_visit(gv, vertID, visitor, get(colors, ctx.g));
|
|
}
|
|
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const ResolvePass& pass) {
|
|
mountResources(pass);
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const CopyPass& pass) {
|
|
mountResources(pass);
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const MovePass& pass) {
|
|
mountResources(pass);
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const RaytracePass& pass) {
|
|
mountResources(pass);
|
|
frontBarriers(vertID);
|
|
begin(pass, vertID);
|
|
},
|
|
[&](const auto& queue) {
|
|
begin(queue, vertID);
|
|
});
|
|
}
|
|
|
|
void finish_vertex(
|
|
RenderGraph::vertex_descriptor vertID,
|
|
const boost::filtered_graph<AddressableView<RenderGraph>, boost::keep_all, RenderGraphFilter>& gv) const {
|
|
std::ignore = gv;
|
|
visitObject(
|
|
vertID, ctx.g,
|
|
[&](const RasterPass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
#if CC_DEBUG
|
|
ctx.cmdBuff->endMarker();
|
|
#endif
|
|
},
|
|
[&](const RasterSubpass& subpass) {
|
|
end(subpass, vertID);
|
|
},
|
|
[&](const ComputeSubpass& subpass) {
|
|
end(subpass, vertID);
|
|
},
|
|
[&](const ComputePass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
},
|
|
[&](const ResolvePass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
},
|
|
[&](const CopyPass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
},
|
|
[&](const MovePass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
},
|
|
[&](const RaytracePass& pass) {
|
|
end(pass, vertID);
|
|
rearBarriers(vertID);
|
|
},
|
|
[&](const auto& queue) {
|
|
end(queue, vertID);
|
|
});
|
|
}
|
|
|
|
RenderGraphVisitorContext& ctx;
|
|
};
|
|
|
|
struct RenderGraphCullVisitor : boost::dfs_visitor<> {
|
|
void discover_vertex(
|
|
// NOLINTNEXTLINE(misc-unused-parameters)
|
|
RenderGraph::vertex_descriptor vertID, const AddressableView<RenderGraph>& gv) const {
|
|
validPasses[vertID] = false;
|
|
}
|
|
ccstd::pmr::vector<bool>& validPasses;
|
|
};
|
|
|
|
struct ResourceCleaner {
|
|
explicit ResourceCleaner(ResourceGraph& resourceGraphIn) noexcept
|
|
: resourceGraph(resourceGraphIn),
|
|
prevFenceValue(resourceGraph.nextFenceValue) {
|
|
++resourceGraph.nextFenceValue;
|
|
}
|
|
ResourceCleaner(const ResourceCleaner&) = delete;
|
|
ResourceCleaner& operator=(const ResourceCleaner&) = delete;
|
|
~ResourceCleaner() noexcept {
|
|
resourceGraph.unmount(prevFenceValue);
|
|
}
|
|
|
|
ResourceGraph& resourceGraph;
|
|
uint64_t prevFenceValue = 0;
|
|
};
|
|
|
|
struct RenderGraphContextCleaner {
|
|
explicit RenderGraphContextCleaner(NativeRenderContext& contextIn) noexcept
|
|
: context(contextIn),
|
|
prevFenceValue(context.nextFenceValue) {
|
|
++context.nextFenceValue;
|
|
context.clearPreviousResources(prevFenceValue);
|
|
context.renderSceneResources.clear();
|
|
context.sceneCulling.clear();
|
|
}
|
|
RenderGraphContextCleaner(const RenderGraphContextCleaner&) = delete;
|
|
RenderGraphContextCleaner& operator=(const RenderGraphContextCleaner&) = delete;
|
|
~RenderGraphContextCleaner() noexcept = default;
|
|
NativeRenderContext& context;
|
|
uint64_t prevFenceValue = 0;
|
|
};
|
|
|
|
struct CommandSubmitter {
|
|
CommandSubmitter(gfx::Device* deviceIn, const std::vector<gfx::CommandBuffer*>& cmdBuffersIn)
|
|
: device(deviceIn), cmdBuffers(cmdBuffersIn) {
|
|
CC_EXPECTS(cmdBuffers.size() == 1);
|
|
primaryCommandBuffer = cmdBuffers.at(0);
|
|
primaryCommandBuffer->begin();
|
|
}
|
|
CommandSubmitter(const CommandSubmitter&) = delete;
|
|
CommandSubmitter& operator=(const CommandSubmitter&) = delete;
|
|
~CommandSubmitter() noexcept {
|
|
primaryCommandBuffer->end();
|
|
device->flushCommands(cmdBuffers);
|
|
device->getQueue()->submit(cmdBuffers);
|
|
}
|
|
gfx::Device* device = nullptr;
|
|
const std::vector<gfx::CommandBuffer*>& cmdBuffers;
|
|
gfx::CommandBuffer* primaryCommandBuffer = nullptr;
|
|
};
|
|
|
|
void extendResourceLifetime(const NativeRenderQueue& queue, ResourceGroup& group) {
|
|
// keep instanceBuffers
|
|
for (const auto& batch : queue.opaqueInstancingQueue.sortedBatches) {
|
|
group.instancingBuffers.emplace(batch);
|
|
}
|
|
for (const auto& batch : queue.transparentInstancingQueue.sortedBatches) {
|
|
group.instancingBuffers.emplace(batch);
|
|
}
|
|
}
|
|
|
|
void collectStatistics(const NativePipeline& ppl, PipelineStatistics& stats) {
|
|
// resources
|
|
stats.numRenderPasses = static_cast<uint32_t>(ppl.resourceGraph.renderPasses.size());
|
|
stats.totalManagedTextures = static_cast<uint32_t>(ppl.resourceGraph.managedTextures.size());
|
|
stats.numManagedTextures = 0;
|
|
for (const auto& tex : ppl.resourceGraph.managedTextures) {
|
|
if (tex.texture) {
|
|
++stats.numManagedTextures;
|
|
}
|
|
}
|
|
// layout graph
|
|
stats.numUploadBuffers = 0;
|
|
stats.numUploadBufferViews = 0;
|
|
stats.numFreeUploadBuffers = 0;
|
|
stats.numFreeUploadBufferViews = 0;
|
|
stats.numDescriptorSets = 0;
|
|
stats.numFreeDescriptorSets = 0;
|
|
for (const auto& node : ppl.nativeContext.layoutGraphResources) {
|
|
for (const auto& [nameID, buffer] : node.uniformBuffers) {
|
|
stats.numUploadBuffers += static_cast<uint32_t>(buffer.bufferPool.currentBuffers.size());
|
|
stats.numUploadBufferViews += static_cast<uint32_t>(buffer.bufferPool.currentBufferViews.size());
|
|
stats.numFreeUploadBuffers += static_cast<uint32_t>(buffer.bufferPool.freeBuffers.size());
|
|
stats.numFreeUploadBufferViews += static_cast<uint32_t>(buffer.bufferPool.freeBufferViews.size());
|
|
}
|
|
stats.numDescriptorSets += static_cast<uint32_t>(node.descriptorSetPool.currentDescriptorSets.size());
|
|
stats.numFreeDescriptorSets += static_cast<uint32_t>(node.descriptorSetPool.freeDescriptorSets.size());
|
|
}
|
|
// scene
|
|
stats.numInstancingBuffers = 0;
|
|
stats.numInstancingUniformBlocks = 0;
|
|
for (const auto& [key, group] : ppl.nativeContext.resourceGroups) {
|
|
stats.numInstancingBuffers += group.instancingBuffers.size();
|
|
for (const auto& buffer : group.instancingBuffers) {
|
|
stats.numInstancingUniformBlocks += static_cast<uint32_t>(buffer->getInstances().size());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void NativePipeline::executeRenderGraph(const RenderGraph& rg) {
|
|
auto& ppl = *this;
|
|
auto* scratch = &ppl.unsyncPool;
|
|
|
|
ppl.resourceGraph.validateSwapchains();
|
|
|
|
RenderGraphContextCleaner contextCleaner(ppl.nativeContext);
|
|
ResourceCleaner cleaner(ppl.resourceGraph);
|
|
|
|
auto& lg = ppl.programLibrary->layoutGraph;
|
|
FrameGraphDispatcher fgd(
|
|
ppl.resourceGraph, rg,
|
|
lg, &ppl.unsyncPool, scratch);
|
|
fgd.enableMemoryAliasing(false);
|
|
fgd.enablePassReorder(false);
|
|
fgd.setParalellWeight(0);
|
|
fgd.run();
|
|
|
|
AddressableView<RenderGraph> graphView(rg);
|
|
ccstd::pmr::vector<bool> validPasses(num_vertices(rg), true, scratch);
|
|
auto colors = rg.colors(scratch);
|
|
{ // Mark all culled vertices
|
|
RenderGraphCullVisitor visitor{{}, validPasses};
|
|
for (const auto& vertID : fgd.resourceAccessGraph.culledPasses) {
|
|
const auto nodeID = get(ResourceAccessGraph::PassIDTag{}, fgd.resourceAccessGraph, vertID);
|
|
if (nodeID == RenderGraph::null_vertex()) {
|
|
continue;
|
|
}
|
|
const auto passID = fgd.resourceAccessGraph.passIndex.at(nodeID);
|
|
boost::depth_first_visit(graphView, passID, visitor, get(colors, rg));
|
|
}
|
|
colors.clear();
|
|
colors.resize(num_vertices(rg), boost::white_color);
|
|
}
|
|
|
|
// scene culling
|
|
{
|
|
auto& context = ppl.nativeContext;
|
|
auto& sceneCulling = context.sceneCulling;
|
|
sceneCulling.buildRenderQueues(rg, lg, ppl);
|
|
auto& group = ppl.nativeContext.resourceGroups[context.nextFenceValue];
|
|
// notice: we cannot use ranged-for of sceneCulling.renderQueues
|
|
CC_EXPECTS(sceneCulling.numRenderQueues <= sceneCulling.renderQueues.size());
|
|
for (uint32_t queueID = 0; queueID != sceneCulling.numRenderQueues; ++queueID) {
|
|
const auto& queue = sceneCulling.renderQueues[queueID];
|
|
extendResourceLifetime(queue, group);
|
|
}
|
|
}
|
|
|
|
// light manangement
|
|
{
|
|
auto& ctx = ppl.nativeContext;
|
|
ctx.lightResources.clear();
|
|
ctx.lightResources.buildLights(
|
|
ctx.sceneCulling,
|
|
ppl.pipelineSceneData->isHDR(),
|
|
ppl.pipelineSceneData->getShadows());
|
|
}
|
|
|
|
// gpu driven
|
|
if constexpr (ENABLE_GPU_DRIVEN) {
|
|
// TODO(jilin): consider populating renderSceneResources here
|
|
const scene::RenderScene* const ptr = nullptr;
|
|
auto& sceneResource = ppl.nativeContext.renderSceneResources[ptr];
|
|
const auto& nameID = lg.attributeIndex.find("cc_xxxDescriptor")->second;
|
|
sceneResource.resourceIndex.emplace(nameID, ResourceType::STORAGE_BUFFER);
|
|
sceneResource.storageBuffers.emplace(nameID, nullptr);
|
|
}
|
|
|
|
// Execute all valid passes
|
|
{
|
|
boost::filtered_graph<
|
|
AddressableView<RenderGraph>,
|
|
boost::keep_all, RenderGraphFilter>
|
|
fg(graphView, boost::keep_all{}, RenderGraphFilter{&validPasses});
|
|
|
|
CommandSubmitter submit(ppl.device, ppl.getCommandBuffers());
|
|
|
|
// upload buffers
|
|
{
|
|
auto& ctx = ppl.nativeContext;
|
|
#if CC_DEBUG
|
|
submit.primaryCommandBuffer->beginMarker(makeMarkerInfo("Internal Upload", RASTER_UPLOAD_COLOR));
|
|
#endif
|
|
// scene
|
|
const auto& sceneCulling = ppl.nativeContext.sceneCulling;
|
|
for (uint32_t queueID = 0; queueID != sceneCulling.numRenderQueues; ++queueID) {
|
|
// notice: we cannot use ranged-for of sceneCulling.renderQueues
|
|
CC_EXPECTS(sceneCulling.numRenderQueues <= sceneCulling.renderQueues.size());
|
|
const auto& queue = sceneCulling.renderQueues[queueID];
|
|
queue.opaqueInstancingQueue.uploadBuffers(submit.primaryCommandBuffer);
|
|
queue.transparentInstancingQueue.uploadBuffers(submit.primaryCommandBuffer);
|
|
}
|
|
|
|
// lights
|
|
ctx.lightResources.buildLightBuffer(submit.primaryCommandBuffer);
|
|
ctx.lightResources.tryUpdateRenderSceneLocalDescriptorSet(sceneCulling);
|
|
#if CC_DEBUG
|
|
submit.primaryCommandBuffer->endMarker();
|
|
#endif
|
|
}
|
|
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>
|
|
renderGraphDescriptorSet(scratch);
|
|
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>
|
|
profilerPerPassDescriptorSets(scratch);
|
|
|
|
ccstd::pmr::unordered_map<
|
|
RenderGraph::vertex_descriptor,
|
|
gfx::DescriptorSet*>
|
|
perInstanceDescriptorSets(scratch);
|
|
|
|
// submit commands
|
|
RenderGraphVisitorContext ctx{
|
|
ppl.nativeContext,
|
|
lg, rg, ppl.resourceGraph,
|
|
fgd,
|
|
validPasses,
|
|
ppl.device, submit.primaryCommandBuffer,
|
|
&ppl,
|
|
renderGraphDescriptorSet,
|
|
profilerPerPassDescriptorSets,
|
|
perInstanceDescriptorSets,
|
|
programLibrary,
|
|
CustomRenderGraphContext{
|
|
custom.currentContext,
|
|
&rg,
|
|
&ppl.resourceGraph,
|
|
submit.primaryCommandBuffer,
|
|
},
|
|
scratch};
|
|
|
|
RenderGraphVisitor visitor{{}, ctx};
|
|
auto colors = rg.colors(scratch);
|
|
for (const auto vertID : ctx.g.sortedVertices) {
|
|
if (holds<RasterPassTag>(vertID, ctx.g) || holds<ComputeTag>(vertID, ctx.g) || holds<CopyTag>(vertID, ctx.g)) {
|
|
boost::depth_first_visit(fg, vertID, visitor, get(colors, ctx.g));
|
|
}
|
|
}
|
|
}
|
|
|
|
// collect statistics
|
|
collectStatistics(*this, statistics);
|
|
}
|
|
|
|
} // namespace render
|
|
|
|
} // namespace cc
|
|
|