no message
This commit is contained in:
85
cocos/renderer/frame-graph/Blackboard.h
Normal file
85
cocos/renderer/frame-graph/Blackboard.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "Handle.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
class Blackboard final {
|
||||
public:
|
||||
Blackboard() = default;
|
||||
~Blackboard() = default;
|
||||
Blackboard(const Blackboard &) = delete;
|
||||
Blackboard(Blackboard &&) noexcept = delete;
|
||||
Blackboard &operator=(const Blackboard &) = delete;
|
||||
Blackboard &operator=(Blackboard &&) noexcept = delete;
|
||||
|
||||
inline ValueType &operator[](const KeyType &name) noexcept;
|
||||
inline void put(const KeyType &name, ValueType handle) noexcept;
|
||||
inline ValueType get(const KeyType &name) const noexcept;
|
||||
inline void clear() noexcept;
|
||||
inline bool has(const KeyType &name) const noexcept;
|
||||
|
||||
private:
|
||||
using Container = ccstd::unordered_map<KeyType, ValueType, typename KeyType::Hasher>;
|
||||
Container _container;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
ValueType &Blackboard<KeyType, ValueType, InvalidValue>::operator[](const KeyType &name) noexcept {
|
||||
const auto it = _container.find(name);
|
||||
return (it == _container.end() ? _container.emplace(name, InvalidValue).first : it)->second;
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
void Blackboard<KeyType, ValueType, InvalidValue>::put(const KeyType &name, ValueType const handle) noexcept {
|
||||
_container[name] = handle;
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
ValueType Blackboard<KeyType, ValueType, InvalidValue>::get(const KeyType &name) const noexcept {
|
||||
const auto it = _container.find(name);
|
||||
return it == _container.end() ? InvalidValue : it->second;
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
void Blackboard<KeyType, ValueType, InvalidValue>::clear() noexcept {
|
||||
_container.clear();
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType, ValueType InvalidValue>
|
||||
bool Blackboard<KeyType, ValueType, InvalidValue>::has(const KeyType &name) const noexcept {
|
||||
return _container.find(name) != _container.end();
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
81
cocos/renderer/frame-graph/CallbackPass.h
Normal file
81
cocos/renderer/frame-graph/CallbackPass.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "DevicePassResourceTable.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class Executable {
|
||||
public:
|
||||
Executable() noexcept = default;
|
||||
virtual ~Executable() = default;
|
||||
Executable(const Executable &) = delete;
|
||||
Executable(Executable &&) noexcept = delete;
|
||||
Executable &operator=(const Executable &) = delete;
|
||||
Executable &operator=(Executable &&) noexcept = delete;
|
||||
|
||||
virtual void execute(const DevicePassResourceTable &resourceTable) noexcept = 0;
|
||||
};
|
||||
|
||||
template <typename Data, typename ExecuteMethodType>
|
||||
class CallbackPass final : public Executable {
|
||||
public:
|
||||
using ExecuteMethod = std::remove_reference_t<ExecuteMethodType>;
|
||||
|
||||
explicit CallbackPass(ExecuteMethod &execute) noexcept;
|
||||
explicit CallbackPass(ExecuteMethod &&execute) noexcept;
|
||||
|
||||
void execute(const DevicePassResourceTable &resourceTable) noexcept override;
|
||||
inline const Data &getData() const noexcept { return _data; }
|
||||
inline Data &getData() noexcept { return _data; }
|
||||
|
||||
private:
|
||||
Data _data;
|
||||
ExecuteMethod _execute;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Data, typename ExecuteMethod>
|
||||
CallbackPass<Data, ExecuteMethod>::CallbackPass(ExecuteMethod &execute) noexcept
|
||||
: Executable(),
|
||||
_execute(execute) {
|
||||
}
|
||||
|
||||
template <typename Data, typename ExecuteMethod>
|
||||
CallbackPass<Data, ExecuteMethod>::CallbackPass(ExecuteMethod &&execute) noexcept
|
||||
: Executable(),
|
||||
_execute(execute) {
|
||||
}
|
||||
|
||||
template <typename Data, typename ExecuteMethod>
|
||||
void CallbackPass<Data, ExecuteMethod>::execute(const DevicePassResourceTable &resourceTable) noexcept {
|
||||
_execute(_data, resourceTable);
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
427
cocos/renderer/frame-graph/DevicePass.cpp
Normal file
427
cocos/renderer/frame-graph/DevicePass.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "DevicePass.h"
|
||||
#include "CallbackPass.h"
|
||||
#include "DevicePassResourceTable.h"
|
||||
#include "FrameGraph.h"
|
||||
#include "PassNode.h"
|
||||
#include "ResourceNode.h"
|
||||
#include "base/Utils.h"
|
||||
#include "gfx-base/GFXCommandBuffer.h"
|
||||
#include "gfx-base/GFXDef-common.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
DevicePass::DevicePass(const FrameGraph &graph, ccstd::vector<PassNode *> const &subpassNodes) {
|
||||
ccstd::vector<RenderTargetAttachment> attachments;
|
||||
|
||||
uint32_t index = 0;
|
||||
for (const PassNode *passNode : subpassNodes) {
|
||||
append(graph, passNode, &attachments);
|
||||
_barriers.push_back(std::cref(passNode->getBarriers()));
|
||||
_subpasses.back().barrierID = index++;
|
||||
}
|
||||
|
||||
auto *device = gfx::Device::getInstance();
|
||||
// _enableAutoBarrier: auto barrier in framegraph
|
||||
// barrierDeduce: deduce barrier gfx internally
|
||||
// to avoid redundant instructions, either inside or outside
|
||||
device->enableAutoBarrier(!gfx::ENABLE_GRAPH_AUTO_BARRIER);
|
||||
|
||||
// Important Notice:
|
||||
// here attchment index has changed.
|
||||
// _attachments is flattened previously, and it is spliced into two parts here:
|
||||
// colorAttachments and depthStencilAttachment
|
||||
// As a result, the indices in subpasses are INVALIDATED.
|
||||
// So the attachment index in subpass should be recalculated.
|
||||
// there should be only ONE depth stencil in the entire Pass
|
||||
auto depthIndex = gfx::INVALID_BINDING;
|
||||
auto depthNewIndex = gfx::INVALID_BINDING;
|
||||
for (uint32_t id = 0; id != attachments.size(); ++id) {
|
||||
if (attachments[id].desc.usage != RenderTargetAttachment::Usage::COLOR) {
|
||||
CC_ASSERT_EQ(depthIndex, gfx::INVALID_BINDING);
|
||||
depthIndex = id;
|
||||
depthNewIndex = static_cast<uint32_t>(attachments.size() - 1);
|
||||
}
|
||||
}
|
||||
std::stable_sort(attachments.begin(), attachments.end(), RenderTargetAttachment::Sorter());
|
||||
|
||||
// update subpass index
|
||||
for (auto &subpass : _subpasses) {
|
||||
// reindex subpass attchment index
|
||||
auto &info = subpass.desc;
|
||||
for (auto &id : info.inputs) {
|
||||
if (id == depthIndex) {
|
||||
id = depthNewIndex;
|
||||
} else if (id > depthIndex) {
|
||||
--id;
|
||||
}
|
||||
}
|
||||
for (auto &id : info.resolves) {
|
||||
if (id == depthIndex) {
|
||||
id = depthNewIndex;
|
||||
} else if (id > depthIndex) {
|
||||
--id;
|
||||
}
|
||||
}
|
||||
for (auto &id : info.preserves) {
|
||||
if (id == depthIndex) {
|
||||
id = depthNewIndex;
|
||||
} else if (id > depthIndex) {
|
||||
--id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ccstd::vector<const gfx::Texture *> renderTargets;
|
||||
|
||||
for (auto &attachment : attachments) {
|
||||
const ResourceNode &resourceNode = graph.getResourceNode(attachment.textureHandle);
|
||||
CC_ASSERT(resourceNode.virtualResource);
|
||||
|
||||
gfx::Texture *resource = static_cast<ResourceEntry<Texture> *>(resourceNode.virtualResource)->getDeviceResource();
|
||||
CC_ASSERT(resource);
|
||||
|
||||
_attachments.emplace_back();
|
||||
_attachments.back().attachment = attachment;
|
||||
_attachments.back().renderTarget = resource;
|
||||
renderTargets.emplace_back(resource);
|
||||
}
|
||||
|
||||
for (PassNode *const passNode : subpassNodes) {
|
||||
_resourceTable.extract(graph, passNode, renderTargets);
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePass::passDependency(gfx::RenderPassInfo &rpInfo) {
|
||||
if constexpr (gfx::ENABLE_GRAPH_AUTO_BARRIER) {
|
||||
uint32_t index = 0;
|
||||
|
||||
thread_local gfx::BufferBarrierList bufferBarriers;
|
||||
thread_local gfx::TextureBarrierList textureBarriers;
|
||||
thread_local gfx::BufferList buffers;
|
||||
thread_local gfx::TextureList textures;
|
||||
|
||||
bufferBarriers.clear();
|
||||
textureBarriers.clear();
|
||||
buffers.clear();
|
||||
textures.clear();
|
||||
|
||||
uint32_t lastBufferIndex{0};
|
||||
uint32_t lastTextureIndex{0};
|
||||
|
||||
auto mergeToDependency = [&](uint32_t barrierID, uint32_t subpassIndex) {
|
||||
for (const auto &frontBarrier : _barriers[barrierID].get().frontBarriers) {
|
||||
const auto &res = getBarrier(frontBarrier, &_resourceTable);
|
||||
if (frontBarrier.resourceType == ResourceType::BUFFER) {
|
||||
bufferBarriers.emplace_back(static_cast<gfx::BufferBarrier *>(res.first));
|
||||
buffers.emplace_back(static_cast<gfx::Buffer *>(res.second));
|
||||
} else if (frontBarrier.resourceType == ResourceType::TEXTURE) {
|
||||
textureBarriers.emplace_back(static_cast<gfx::TextureBarrier *>(res.first));
|
||||
textures.emplace_back(static_cast<gfx::Texture *>(res.second));
|
||||
} else {
|
||||
CC_ABORT();
|
||||
}
|
||||
}
|
||||
|
||||
lastBufferIndex = static_cast<uint32_t>(buffers.size());
|
||||
lastTextureIndex = static_cast<uint32_t>(textures.size());
|
||||
|
||||
rpInfo.dependencies.emplace_back(gfx::SubpassDependency{
|
||||
subpassIndex > 1 ? subpassIndex - 1 : gfx::SUBPASS_EXTERNAL,
|
||||
subpassIndex,
|
||||
nullptr,
|
||||
{},
|
||||
{},
|
||||
});
|
||||
|
||||
for (const auto &rearBarrier : _barriers[barrierID].get().rearBarriers) {
|
||||
const auto &res = getBarrier(rearBarrier, &_resourceTable);
|
||||
if (rearBarrier.resourceType == ResourceType::BUFFER) {
|
||||
bufferBarriers.emplace_back(static_cast<gfx::BufferBarrier *>(res.first));
|
||||
buffers.emplace_back(static_cast<gfx::Buffer *>(res.second));
|
||||
} else if (rearBarrier.resourceType == ResourceType::TEXTURE) {
|
||||
textureBarriers.emplace_back(static_cast<gfx::TextureBarrier *>(res.first));
|
||||
textures.emplace_back(static_cast<gfx::Texture *>(res.second));
|
||||
} else {
|
||||
CC_ABORT();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!_subpasses.empty()) {
|
||||
for (const auto &subpass : _subpasses) {
|
||||
mergeToDependency(subpass.barrierID, index);
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
mergeToDependency(0, 0);
|
||||
}
|
||||
|
||||
if (textureBarriers.size() > lastTextureIndex || bufferBarriers.size() > lastBufferIndex) {
|
||||
rpInfo.dependencies.emplace_back(gfx::SubpassDependency{
|
||||
_subpasses.empty() ? 0 : static_cast<uint32_t>(_subpasses.size() - 1),
|
||||
gfx::SUBPASS_EXTERNAL,
|
||||
nullptr,
|
||||
{},
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePass::execute() {
|
||||
auto *device = gfx::Device::getInstance();
|
||||
auto *cmdBuff = device->getCommandBuffer();
|
||||
|
||||
begin(cmdBuff);
|
||||
|
||||
for (uint32_t i = 0; i < utils::toUint(_subpasses.size()); ++i) {
|
||||
Subpass &subpass = _subpasses[i];
|
||||
_resourceTable._subpassIndex = i;
|
||||
|
||||
for (LogicPass &pass : subpass.logicPasses) {
|
||||
gfx::Viewport &viewport = pass.customViewport ? pass.viewport : _viewport;
|
||||
gfx::Rect &scissor = pass.customViewport ? pass.scissor : _scissor;
|
||||
|
||||
if (viewport != _curViewport) {
|
||||
cmdBuff->setViewport(viewport);
|
||||
_curViewport = viewport;
|
||||
}
|
||||
if (scissor != _curScissor) {
|
||||
cmdBuff->setScissor(scissor);
|
||||
_curScissor = scissor;
|
||||
}
|
||||
|
||||
pass.pass->execute(_resourceTable);
|
||||
}
|
||||
|
||||
if (i < _subpasses.size() - 1) next(cmdBuff);
|
||||
}
|
||||
|
||||
end(cmdBuff);
|
||||
}
|
||||
|
||||
void DevicePass::append(const FrameGraph &graph, const PassNode *passNode, ccstd::vector<RenderTargetAttachment> *attachments) {
|
||||
_subpasses.emplace_back();
|
||||
Subpass &subpass = _subpasses.back();
|
||||
|
||||
do {
|
||||
subpass.logicPasses.emplace_back();
|
||||
LogicPass &logicPass = subpass.logicPasses.back();
|
||||
logicPass.pass = passNode->_pass.get();
|
||||
logicPass.customViewport = passNode->_customViewport;
|
||||
logicPass.viewport = passNode->_viewport;
|
||||
logicPass.scissor = passNode->_scissor;
|
||||
|
||||
for (const auto &attachment : passNode->_attachments) {
|
||||
append(graph, attachment, attachments, &subpass.desc, passNode->_reads);
|
||||
}
|
||||
|
||||
for (const auto &handle : passNode->_reads) {
|
||||
const auto it = std::find_if(attachments->begin(), attachments->end(), [&handle](const RenderTargetAttachment &attachment) {
|
||||
return attachment.textureHandle == handle;
|
||||
});
|
||||
if (it != attachments->end()) {
|
||||
uint32_t input = utils::toUint(it - attachments->begin());
|
||||
if (std::find(subpass.desc.inputs.begin(), subpass.desc.inputs.end(), input) == subpass.desc.inputs.end()) {
|
||||
subpass.desc.inputs.push_back(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
passNode = passNode->_next;
|
||||
} while (passNode);
|
||||
}
|
||||
|
||||
void DevicePass::append(const FrameGraph &graph, const RenderTargetAttachment &attachment,
|
||||
ccstd::vector<RenderTargetAttachment> *attachments, gfx::SubpassInfo *subpass, const ccstd::vector<Handle> &reads) {
|
||||
std::ignore = reads;
|
||||
RenderTargetAttachment::Usage usage{attachment.desc.usage};
|
||||
uint32_t slot{attachment.desc.slot};
|
||||
if (attachment.desc.usage == RenderTargetAttachment::Usage::COLOR) {
|
||||
// should fetch actual color slot from current subpass
|
||||
slot = subpass->colors.size() > attachment.desc.slot ? subpass->colors[attachment.desc.slot] : gfx::INVALID_BINDING;
|
||||
}
|
||||
auto it = std::find_if(attachments->begin(), attachments->end(), [usage, slot](const RenderTargetAttachment &x) {
|
||||
return usage == x.desc.usage && slot == x.desc.slot;
|
||||
});
|
||||
|
||||
RenderTargetAttachment *output{nullptr};
|
||||
|
||||
if (it == attachments->end()) {
|
||||
attachments->emplace_back(attachment);
|
||||
output = &(attachments->back());
|
||||
if (attachment.desc.usage == RenderTargetAttachment::Usage::COLOR) {
|
||||
for (uint8_t i = 0; i < RenderTargetAttachment::DEPTH_STENCIL_SLOT_START; ++i) {
|
||||
if ((_usedRenderTargetSlotMask & (1 << i)) == 0) {
|
||||
attachments->back().desc.slot = i;
|
||||
_usedRenderTargetSlotMask |= 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CC_ASSERT((_usedRenderTargetSlotMask & (1 << attachment.desc.slot)) == 0);
|
||||
_usedRenderTargetSlotMask |= 1 << attachment.desc.slot;
|
||||
}
|
||||
} else {
|
||||
const ResourceNode &resourceNodeA = graph.getResourceNode(it->textureHandle);
|
||||
const ResourceNode &resourceNodeB = graph.getResourceNode(attachment.textureHandle);
|
||||
|
||||
if (resourceNodeA.virtualResource == resourceNodeB.virtualResource) {
|
||||
output = &*it;
|
||||
if (attachment.storeOp != gfx::StoreOp::DISCARD) {
|
||||
output->storeOp = attachment.storeOp;
|
||||
output->desc.endAccesses = attachment.desc.endAccesses;
|
||||
}
|
||||
} else {
|
||||
CC_ASSERT(attachment.desc.usage == RenderTargetAttachment::Usage::COLOR);
|
||||
attachments->emplace_back(attachment);
|
||||
output = &(attachments->back());
|
||||
|
||||
for (uint8_t i = 0; i < RenderTargetAttachment::DEPTH_STENCIL_SLOT_START; ++i) {
|
||||
if ((_usedRenderTargetSlotMask & (1 << i)) == 0) {
|
||||
attachments->back().desc.slot = i;
|
||||
_usedRenderTargetSlotMask |= 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment.desc.usage == RenderTargetAttachment::Usage::COLOR) {
|
||||
if (std::find(subpass->colors.begin(), subpass->colors.end(), output->desc.slot) == subpass->colors.end()) {
|
||||
subpass->colors.push_back(output->desc.slot);
|
||||
}
|
||||
} else {
|
||||
subpass->depthStencil = output->desc.slot;
|
||||
}
|
||||
}
|
||||
|
||||
void DevicePass::begin(gfx::CommandBuffer *cmdBuff) {
|
||||
if (_attachments.empty()) return;
|
||||
|
||||
gfx::RenderPassInfo rpInfo;
|
||||
gfx::FramebufferInfo fboInfo;
|
||||
float clearDepth = 1.F;
|
||||
uint32_t clearStencil = 0;
|
||||
static ccstd::vector<gfx::Color> clearColors;
|
||||
clearColors.clear();
|
||||
|
||||
bool hasDefaultViewport{false};
|
||||
for (auto &subpass : _subpasses) {
|
||||
for (auto &pass : subpass.logicPasses) {
|
||||
if (!pass.customViewport) {
|
||||
hasDefaultViewport = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDefaultViewport) {
|
||||
_viewport = {};
|
||||
_scissor = {0, 0, UINT_MAX, UINT_MAX};
|
||||
} else { // if all passes use customize viewport
|
||||
_scissor = {INT_MAX, INT_MAX, 0, 0};
|
||||
|
||||
for (auto &subpass : _subpasses) {
|
||||
for (auto &pass : subpass.logicPasses) {
|
||||
// calculate the union of all viewports as render area
|
||||
_viewport.left = _scissor.x = std::min(_scissor.x, pass.viewport.left);
|
||||
_viewport.top = _scissor.y = std::min(_scissor.y, pass.viewport.top);
|
||||
_viewport.width = _scissor.width = std::max(_scissor.width, pass.viewport.width + pass.viewport.left - _scissor.x);
|
||||
_viewport.height = _scissor.height = std::max(_scissor.height, pass.viewport.height + pass.viewport.top - _scissor.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &attachElem : _attachments) {
|
||||
gfx::Texture *attachment = attachElem.renderTarget;
|
||||
if (attachElem.attachment.desc.usage == RenderTargetAttachment::Usage::COLOR) {
|
||||
rpInfo.colorAttachments.emplace_back();
|
||||
auto &attachmentInfo = rpInfo.colorAttachments.back();
|
||||
attachmentInfo.format = attachment->getFormat();
|
||||
attachmentInfo.loadOp = attachElem.attachment.desc.loadOp;
|
||||
attachmentInfo.storeOp = attachElem.attachment.storeOp;
|
||||
attachmentInfo.barrier = gfx::Device::getInstance()->getGeneralBarrier({attachElem.attachment.desc.beginAccesses, attachElem.attachment.desc.endAccesses});
|
||||
fboInfo.colorTextures.push_back(attachElem.renderTarget);
|
||||
clearColors.emplace_back(attachElem.attachment.desc.clearColor);
|
||||
} else {
|
||||
auto &attachmentInfo = rpInfo.depthStencilAttachment;
|
||||
attachmentInfo.format = attachment->getFormat();
|
||||
attachmentInfo.depthLoadOp = attachElem.attachment.desc.loadOp;
|
||||
attachmentInfo.stencilLoadOp = attachElem.attachment.desc.loadOp;
|
||||
attachmentInfo.depthStoreOp = attachElem.attachment.storeOp;
|
||||
attachmentInfo.stencilStoreOp = attachElem.attachment.storeOp;
|
||||
attachmentInfo.barrier = gfx::Device::getInstance()->getGeneralBarrier({attachElem.attachment.desc.beginAccesses, attachElem.attachment.desc.endAccesses});
|
||||
fboInfo.depthStencilTexture = attachElem.renderTarget;
|
||||
clearDepth = attachElem.attachment.desc.clearDepth;
|
||||
clearStencil = attachElem.attachment.desc.clearStencil;
|
||||
}
|
||||
if (hasDefaultViewport) {
|
||||
_viewport.width = _scissor.width = std::min(_scissor.width, attachment->getWidth());
|
||||
_viewport.height = _scissor.height = std::min(_scissor.height, attachment->getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &subpass : _subpasses) {
|
||||
rpInfo.subpasses.emplace_back(subpass.desc);
|
||||
}
|
||||
|
||||
passDependency(rpInfo);
|
||||
|
||||
_renderPass = RenderPass(rpInfo);
|
||||
_renderPass.createTransient();
|
||||
_resourceTable._renderPass = _renderPass.get();
|
||||
|
||||
fboInfo.renderPass = _renderPass.get();
|
||||
_fbo = Framebuffer(fboInfo);
|
||||
_fbo.createTransient();
|
||||
|
||||
cmdBuff->beginRenderPass(_renderPass.get(), _fbo.get(), _scissor, clearColors.data(), clearDepth, clearStencil);
|
||||
_curViewport = _viewport;
|
||||
_curScissor = _scissor;
|
||||
}
|
||||
|
||||
void DevicePass::next(gfx::CommandBuffer *cmdBuff) noexcept {
|
||||
if (!_renderPass.get() || !_fbo.get()) return;
|
||||
|
||||
cmdBuff->nextSubpass();
|
||||
}
|
||||
|
||||
void DevicePass::end(gfx::CommandBuffer *cmdBuff) {
|
||||
if (!_renderPass.get() || !_fbo.get()) return;
|
||||
|
||||
cmdBuff->endRenderPass();
|
||||
|
||||
_renderPass.destroyTransient();
|
||||
_fbo.destroyTransient();
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
93
cocos/renderer/frame-graph/DevicePass.h
Normal file
93
cocos/renderer/frame-graph/DevicePass.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 <limits>
|
||||
#include "CallbackPass.h"
|
||||
#include "ImmutableState.h"
|
||||
#include "RenderTargetAttachment.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class DevicePass final {
|
||||
public:
|
||||
DevicePass() = delete;
|
||||
DevicePass(const FrameGraph &graph, ccstd::vector<PassNode *> const &subpassNodes);
|
||||
DevicePass(const DevicePass &) = delete;
|
||||
DevicePass(DevicePass &&) = delete;
|
||||
~DevicePass() = default;
|
||||
DevicePass &operator=(const DevicePass &) = delete;
|
||||
DevicePass &operator=(DevicePass &&) = delete;
|
||||
|
||||
void execute();
|
||||
|
||||
private:
|
||||
struct LogicPass final {
|
||||
Executable *pass{nullptr};
|
||||
bool customViewport{false};
|
||||
gfx::Viewport viewport;
|
||||
gfx::Rect scissor;
|
||||
};
|
||||
|
||||
struct Subpass final {
|
||||
gfx::SubpassInfo desc;
|
||||
ccstd::vector<LogicPass> logicPasses{};
|
||||
uint32_t barrierID{0xFFFFFFFF};
|
||||
};
|
||||
|
||||
struct Attachment final {
|
||||
RenderTargetAttachment attachment;
|
||||
gfx::Texture *renderTarget{nullptr};
|
||||
};
|
||||
|
||||
void append(const FrameGraph &graph, const PassNode *passNode, ccstd::vector<RenderTargetAttachment> *attachments);
|
||||
void append(const FrameGraph &graph, const RenderTargetAttachment &attachment,
|
||||
ccstd::vector<RenderTargetAttachment> *attachments, gfx::SubpassInfo *subpass, const ccstd::vector<Handle> &reads);
|
||||
void begin(gfx::CommandBuffer *cmdBuff);
|
||||
void next(gfx::CommandBuffer *cmdBuff) noexcept;
|
||||
void end(gfx::CommandBuffer *cmdBuff);
|
||||
|
||||
void passDependency(gfx::RenderPassInfo &rpInfo);
|
||||
|
||||
ccstd::vector<Subpass> _subpasses{};
|
||||
ccstd::vector<Attachment> _attachments{};
|
||||
uint16_t _usedRenderTargetSlotMask{0};
|
||||
DevicePassResourceTable _resourceTable;
|
||||
|
||||
gfx::Viewport _viewport;
|
||||
gfx::Rect _scissor;
|
||||
gfx::Viewport _curViewport;
|
||||
gfx::Rect _curScissor;
|
||||
RenderPass _renderPass;
|
||||
Framebuffer _fbo;
|
||||
|
||||
std::vector<std::reference_wrapper<const PassBarrierPair>> _barriers;
|
||||
};
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
87
cocos/renderer/frame-graph/DevicePassResourceTable.cpp
Normal file
87
cocos/renderer/frame-graph/DevicePassResourceTable.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "DevicePassResourceTable.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "FrameGraph.h"
|
||||
#include "PassNode.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
gfx::GFXObject *DevicePassResourceTable::get(const ResourceDictionary &from, Handle handle) noexcept {
|
||||
const auto it = from.find(handle);
|
||||
return it == from.cend() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
void DevicePassResourceTable::extract(const FrameGraph &graph,
|
||||
const PassNode *passNode,
|
||||
ccstd::vector<const gfx::Texture *> const &renderTargets) noexcept {
|
||||
do {
|
||||
extract(graph, passNode->_reads, _reads, false, renderTargets);
|
||||
extract(graph, passNode->_writes, _writes, true, renderTargets);
|
||||
|
||||
passNode = passNode->_next;
|
||||
} while (passNode);
|
||||
}
|
||||
|
||||
void DevicePassResourceTable::extract(const FrameGraph &graph,
|
||||
ccstd::vector<Handle> const &from,
|
||||
ResourceDictionary &to,
|
||||
bool /*ignoreRenderTarget*/,
|
||||
ccstd::vector<const gfx::Texture *> const & /*renderTargets*/) noexcept {
|
||||
std::for_each(from.cbegin(), from.cend(), [&](const Handle handle) {
|
||||
if (to.find(handle) != to.cend()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ResourceNode &resourceNode = graph.getResourceNode(handle);
|
||||
CC_ASSERT(resourceNode.virtualResource);
|
||||
gfx::GFXObject *const deviceResource = resourceNode.virtualResource->getDeviceResource();
|
||||
|
||||
if (!deviceResource) {
|
||||
return;
|
||||
}
|
||||
|
||||
//if (ignoreRenderTarget) {
|
||||
// bool const isRenderTarget =
|
||||
// std::find_if(
|
||||
// renderTargets.cbegin(),
|
||||
// renderTargets.cend(),
|
||||
// [&deviceResource](const gfx::Texture *const x) {
|
||||
// return deviceResource == x;
|
||||
// }) != renderTargets.cend();
|
||||
|
||||
// if (isRenderTarget) {
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
|
||||
to[handle] = deviceResource;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
88
cocos/renderer/frame-graph/DevicePassResourceTable.h
Normal file
88
cocos/renderer/frame-graph/DevicePassResourceTable.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "Handle.h"
|
||||
#include "RenderTargetAttachment.h"
|
||||
#include "Resource.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class PassNode;
|
||||
class FrameGraph;
|
||||
class DevicePass;
|
||||
|
||||
class DevicePassResourceTable final {
|
||||
public:
|
||||
DevicePassResourceTable() = default;
|
||||
~DevicePassResourceTable() = default;
|
||||
DevicePassResourceTable(const DevicePassResourceTable &) = delete;
|
||||
DevicePassResourceTable(DevicePassResourceTable &&) = delete;
|
||||
DevicePassResourceTable &operator=(const DevicePassResourceTable &) = delete;
|
||||
DevicePassResourceTable &operator=(DevicePassResourceTable &&) = delete;
|
||||
|
||||
template <typename Type>
|
||||
std::enable_if_t<std::is_base_of<gfx::GFXObject, typename Type::DeviceResource>::value, typename Type::DeviceResource *>
|
||||
getRead(TypedHandle<Type> handle) const noexcept;
|
||||
|
||||
template <typename Type>
|
||||
std::enable_if_t<std::is_base_of<gfx::GFXObject, typename Type::DeviceResource>::value, typename Type::DeviceResource *>
|
||||
getWrite(TypedHandle<Type> handle) const noexcept;
|
||||
|
||||
gfx::RenderPass *getRenderPass() const { return _renderPass; }
|
||||
uint32_t getSubpassIndex() const { return _subpassIndex; }
|
||||
|
||||
private:
|
||||
using ResourceDictionary = ccstd::unordered_map<Handle, gfx::GFXObject *, Handle::Hasher>;
|
||||
|
||||
static gfx::GFXObject *get(const ResourceDictionary &from, Handle handle) noexcept;
|
||||
void extract(const FrameGraph &graph, const PassNode *passNode, ccstd::vector<const gfx::Texture *> const &renderTargets) noexcept;
|
||||
static void extract(const FrameGraph &graph, ccstd::vector<Handle> const &from, ResourceDictionary &to, bool ignoreRenderTarget, ccstd::vector<const gfx::Texture *> const &renderTargets) noexcept;
|
||||
|
||||
ResourceDictionary _reads{};
|
||||
ResourceDictionary _writes{};
|
||||
|
||||
gfx::RenderPass *_renderPass{nullptr};
|
||||
uint32_t _subpassIndex{0U};
|
||||
|
||||
friend class DevicePass;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
std::enable_if_t<std::is_base_of<gfx::GFXObject, typename Type::DeviceResource>::value, typename Type::DeviceResource *>
|
||||
DevicePassResourceTable::getRead(TypedHandle<Type> handle) const noexcept {
|
||||
return static_cast<typename Type::DeviceResource *>(get(_reads, handle));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
std::enable_if_t<std::is_base_of<gfx::GFXObject, typename Type::DeviceResource>::value, typename Type::DeviceResource *>
|
||||
DevicePassResourceTable::getWrite(TypedHandle<Type> handle) const noexcept {
|
||||
return static_cast<typename Type::DeviceResource *>(get(_writes, handle));
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
659
cocos/renderer/frame-graph/FrameGraph.cpp
Normal file
659
cocos/renderer/frame-graph/FrameGraph.cpp
Normal file
@@ -0,0 +1,659 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "FrameGraph.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include "PassNodeBuilder.h"
|
||||
#include "Resource.h"
|
||||
#include "base/StringUtil.h"
|
||||
#include "base/std/container/set.h"
|
||||
#include "frame-graph/ResourceEntry.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
namespace {
|
||||
// use function scoped static member
|
||||
// to ensure correct initialization order
|
||||
StringPool &getStringPool() {
|
||||
static StringPool pool;
|
||||
return pool;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
StringHandle FrameGraph::stringToHandle(const char *const name) {
|
||||
return getStringPool().stringToHandle(name);
|
||||
}
|
||||
|
||||
const char *FrameGraph::handleToString(const StringHandle &handle) noexcept {
|
||||
return getStringPool().handleToString(handle);
|
||||
}
|
||||
|
||||
void FrameGraph::present(const TextureHandle &input, gfx::Texture *target, bool useMoveSemantic) {
|
||||
static const StringHandle PRESENT_PASS{FrameGraph::stringToHandle("Present")};
|
||||
|
||||
const ResourceNode &resourceNode{getResourceNode(input)};
|
||||
CC_ASSERT(resourceNode.writer);
|
||||
|
||||
struct PassDataPresent {
|
||||
TextureHandle input;
|
||||
};
|
||||
|
||||
addPass<PassDataPresent>(
|
||||
resourceNode.writer->_insertPoint, PRESENT_PASS,
|
||||
[&](PassNodeBuilder &builder, PassDataPresent &data) {
|
||||
data.input = builder.read(input);
|
||||
builder.sideEffect();
|
||||
|
||||
if (useMoveSemantic) {
|
||||
// using a global map here so that the user don't need to worry about importing the targets every frame
|
||||
static ccstd::unordered_map<uint32_t, std::pair<StringHandle, Texture>> presentTargets;
|
||||
if (!presentTargets.count(target->getTypedID())) {
|
||||
auto name = FrameGraph::stringToHandle(StringUtil::format("Present Target %d", target->getTypedID()).c_str());
|
||||
presentTargets.emplace(std::piecewise_construct, std::forward_as_tuple(target->getTypedID()), std::forward_as_tuple(name, Texture{target}));
|
||||
}
|
||||
auto &resourceInfo{presentTargets[target->getTypedID()]};
|
||||
TextureHandle output{getBlackboard().get(resourceInfo.first)};
|
||||
if (!output.isValid()) {
|
||||
output = importExternal(resourceInfo.first, resourceInfo.second);
|
||||
getBlackboard().put(resourceInfo.first, output);
|
||||
}
|
||||
move(data.input, output, 0, 0, 0);
|
||||
data.input = output;
|
||||
}
|
||||
},
|
||||
[target](const PassDataPresent &data, const DevicePassResourceTable &table) {
|
||||
auto *cmdBuff = gfx::Device::getInstance()->getCommandBuffer();
|
||||
|
||||
gfx::Texture *input = table.getRead(data.input);
|
||||
if (input && input != target) {
|
||||
gfx::TextureBlit region;
|
||||
region.srcExtent.width = input->getWidth();
|
||||
region.srcExtent.height = input->getHeight();
|
||||
region.dstExtent.width = target->getWidth();
|
||||
region.dstExtent.height = target->getHeight();
|
||||
cmdBuff->blitTexture(input, target, ®ion, 1, gfx::Filter::POINT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FrameGraph::presentLastVersion(const VirtualResource *const virtualResource, gfx::Texture *target, bool useMoveSemantic) {
|
||||
const auto it = std::find_if(_resourceNodes.rbegin(), _resourceNodes.rend(), [&virtualResource](const ResourceNode &node) {
|
||||
return node.virtualResource == virtualResource;
|
||||
});
|
||||
|
||||
CC_ASSERT(it != _resourceNodes.rend());
|
||||
present(TextureHandle(static_cast<Handle::IndexType>(it.base() - _resourceNodes.begin() - 1)), target, useMoveSemantic);
|
||||
}
|
||||
|
||||
void FrameGraph::presentFromBlackboard(const StringHandle &inputName, gfx::Texture *target, bool useMoveSemantic) {
|
||||
present(TextureHandle(_blackboard.get(inputName)), target, useMoveSemantic);
|
||||
}
|
||||
|
||||
void FrameGraph::compile() {
|
||||
if (_passNodes.empty()) return;
|
||||
sort();
|
||||
cull();
|
||||
computeResourceLifetime();
|
||||
|
||||
if (_merge) {
|
||||
mergePassNodes();
|
||||
}
|
||||
|
||||
computeStoreActionAndMemoryless();
|
||||
generateDevicePasses();
|
||||
}
|
||||
|
||||
void FrameGraph::execute() noexcept {
|
||||
if (_passNodes.empty()) return;
|
||||
for (auto &pass : _devicePasses) {
|
||||
pass->execute();
|
||||
}
|
||||
}
|
||||
|
||||
void FrameGraph::reset() noexcept {
|
||||
_passNodes.clear();
|
||||
_resourceNodes.clear();
|
||||
_virtualResources.clear();
|
||||
_devicePasses.clear();
|
||||
_blackboard.clear();
|
||||
}
|
||||
|
||||
void FrameGraph::gc(uint32_t const unusedFrameCount) noexcept {
|
||||
Buffer::Allocator::getInstance().gc(unusedFrameCount);
|
||||
Framebuffer::Allocator::getInstance().gc(unusedFrameCount);
|
||||
RenderPass::Allocator::getInstance().gc(unusedFrameCount);
|
||||
Texture::Allocator::getInstance().gc(unusedFrameCount);
|
||||
}
|
||||
|
||||
void FrameGraph::move(const TextureHandle from, const TextureHandle to, uint8_t mipmapLevel, uint8_t faceId, uint8_t arrayPosition) noexcept {
|
||||
const ResourceNode &fromResourceNode = getResourceNode(from);
|
||||
const ResourceNode &toResourceNode = getResourceNode(to);
|
||||
|
||||
CC_ASSERT(!fromResourceNode.virtualResource->isImported());
|
||||
CC_ASSERT(fromResourceNode.writer);
|
||||
CC_ASSERT(!toResourceNode.writer);
|
||||
|
||||
const gfx::TextureInfo &toTextureDesc = static_cast<ResourceEntry<Texture> *>(toResourceNode.virtualResource)->get().getDesc();
|
||||
uint32_t const toTextureWidth = toTextureDesc.width >> mipmapLevel;
|
||||
uint32_t const toTextureHeight = toTextureDesc.height >> mipmapLevel;
|
||||
uint32_t const toTextureDepth = toTextureDesc.depth >> mipmapLevel;
|
||||
|
||||
CC_ASSERT(toTextureWidth && toTextureHeight && toTextureDepth);
|
||||
CC_ASSERT(toTextureDesc.levelCount > mipmapLevel && toTextureDesc.layerCount > arrayPosition);
|
||||
CC_ASSERT(toTextureDesc.type == gfx::TextureType::CUBE && faceId < 6 || faceId == 0);
|
||||
|
||||
for (ResourceNode &resourceNode : _resourceNodes) {
|
||||
if (resourceNode.virtualResource == fromResourceNode.virtualResource) {
|
||||
resourceNode.virtualResource = toResourceNode.virtualResource;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
for (auto &attachment : passNode->_attachments) {
|
||||
const ResourceNode &attachmentResourceNode = getResourceNode(attachment.textureHandle);
|
||||
|
||||
if (attachmentResourceNode.virtualResource == toResourceNode.virtualResource) {
|
||||
const gfx::TextureInfo &attachmentTextureDesc = static_cast<ResourceEntry<Texture> *>(attachmentResourceNode.virtualResource)->get().getDesc();
|
||||
CC_ASSERT(attachmentTextureDesc.width >> attachment.level == toTextureWidth &&
|
||||
attachmentTextureDesc.height >> attachment.level == toTextureHeight &&
|
||||
attachmentTextureDesc.depth >> attachment.level == toTextureDepth);
|
||||
attachment.level = mipmapLevel;
|
||||
attachment.layer = faceId;
|
||||
attachment.index = arrayPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FrameGraph::hasPass(StringHandle handle) {
|
||||
return std::any_of(_passNodes.begin(), _passNodes.end(), [&](const auto &passNode) { return passNode->_name == handle; });
|
||||
}
|
||||
|
||||
Handle FrameGraph::create(VirtualResource *const virtualResource) {
|
||||
_virtualResources.emplace_back(virtualResource);
|
||||
return createResourceNode(virtualResource);
|
||||
}
|
||||
|
||||
PassNode &FrameGraph::createPassNode(const PassInsertPoint insertPoint, const StringHandle &name, Executable *const pass) {
|
||||
_passNodes.emplace_back(ccnew PassNode(insertPoint, name, static_cast<ID>(_passNodes.size()), pass));
|
||||
return *_passNodes.back();
|
||||
}
|
||||
|
||||
Handle FrameGraph::createResourceNode(VirtualResource *const virtualResource) {
|
||||
const size_t index = _resourceNodes.size();
|
||||
ResourceNode resourceNode;
|
||||
resourceNode.virtualResource = virtualResource;
|
||||
resourceNode.version = virtualResource->_version;
|
||||
_resourceNodes.emplace_back(resourceNode);
|
||||
|
||||
return Handle{static_cast<Handle::IndexType>(index)};
|
||||
}
|
||||
|
||||
void FrameGraph::sort() noexcept {
|
||||
std::stable_sort(_passNodes.begin(), _passNodes.end(), [](const auto &x, const auto &y) {
|
||||
return x->_insertPoint < y->_insertPoint;
|
||||
});
|
||||
}
|
||||
|
||||
void FrameGraph::cull() {
|
||||
for (const auto &passNode : _passNodes) {
|
||||
passNode->_refCount = static_cast<uint32_t>(passNode->_writes.size()) + passNode->_sideEffect;
|
||||
|
||||
for (const Handle handle : passNode->_reads) {
|
||||
CC_ASSERT(handle.isValid());
|
||||
++_resourceNodes[handle].readerCount;
|
||||
}
|
||||
}
|
||||
|
||||
static ccstd::vector<const ResourceNode *> stack;
|
||||
stack.clear();
|
||||
stack.reserve(_resourceNodes.size());
|
||||
|
||||
for (ResourceNode &resourceNode : _resourceNodes) {
|
||||
if (resourceNode.readerCount == 0 && resourceNode.writer) {
|
||||
stack.push_back(&resourceNode);
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
PassNode *const writerPassNode = stack.back()->writer;
|
||||
stack.pop_back();
|
||||
|
||||
if (writerPassNode) {
|
||||
CC_ASSERT(writerPassNode->_refCount);
|
||||
|
||||
if (--writerPassNode->_refCount == 0) {
|
||||
CC_ASSERT(!writerPassNode->_sideEffect);
|
||||
|
||||
for (const Handle handle : writerPassNode->_reads) {
|
||||
ResourceNode &resourceNode = _resourceNodes[handle];
|
||||
|
||||
if (--resourceNode.readerCount == 0) {
|
||||
stack.push_back(&resourceNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const ResourceNode &resourceNode : _resourceNodes) {
|
||||
resourceNode.virtualResource->_refCount += resourceNode.readerCount;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameGraph::computeResourceLifetime() {
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_refCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const Handle handle : passNode->_reads) {
|
||||
_resourceNodes[handle].virtualResource->updateLifetime(passNode.get());
|
||||
}
|
||||
|
||||
for (const Handle handle : passNode->_writes) {
|
||||
_resourceNodes[handle].virtualResource->updateLifetime(passNode.get());
|
||||
++_resourceNodes[handle].virtualResource->_writerCount;
|
||||
}
|
||||
|
||||
std::sort(passNode->_attachments.begin(), passNode->_attachments.end(), RenderTargetAttachment::Sorter());
|
||||
}
|
||||
|
||||
for (const auto &resource : _virtualResources) {
|
||||
CC_ASSERT(!resource->_firstUsePass == !resource->_lastUsePass);
|
||||
|
||||
if (!resource->_firstUsePass || !resource->_lastUsePass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource->_refCount == 0 && !resource->_lastUsePass->getRenderTargetAttachment(*this, resource.get())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
resource->_firstUsePass->_resourceRequestArray.push_back(resource.get());
|
||||
resource->_lastUsePass->_resourceReleaseArray.push_back(resource.get());
|
||||
}
|
||||
}
|
||||
|
||||
void FrameGraph::mergePassNodes() noexcept {
|
||||
const size_t count = _passNodes.size();
|
||||
size_t currentPassId = 0;
|
||||
size_t lastPassId = 0;
|
||||
|
||||
while (currentPassId < count) {
|
||||
const auto ¤tPassNode = _passNodes[currentPassId];
|
||||
|
||||
if (currentPassNode->_refCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
++currentPassId;
|
||||
}
|
||||
|
||||
lastPassId = currentPassId;
|
||||
|
||||
while (++currentPassId < count) {
|
||||
const auto ¤tPassNode = _passNodes[currentPassId];
|
||||
|
||||
if (currentPassNode->_refCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &lastPassNode = _passNodes[lastPassId];
|
||||
|
||||
if (lastPassNode->canMerge(*this, *currentPassNode)) {
|
||||
auto *prevPassNode = lastPassNode.get();
|
||||
|
||||
uint16_t distance = 1;
|
||||
|
||||
while (prevPassNode->_next) {
|
||||
prevPassNode = prevPassNode->_next;
|
||||
++distance;
|
||||
}
|
||||
|
||||
prevPassNode->_next = currentPassNode.get();
|
||||
currentPassNode->_head = lastPassNode.get();
|
||||
currentPassNode->_distanceToHead = distance;
|
||||
currentPassNode->_refCount = 0;
|
||||
|
||||
const size_t attachmentCount = lastPassNode->_attachments.size();
|
||||
|
||||
for (size_t i = 0; i < attachmentCount; ++i) {
|
||||
const RenderTargetAttachment &attachmentInLastPassNode = lastPassNode->_attachments[i];
|
||||
const RenderTargetAttachment &attachmentInCurrentPassNode = currentPassNode->_attachments[i];
|
||||
|
||||
ResourceNode &resourceNode = _resourceNodes[attachmentInLastPassNode.textureHandle];
|
||||
uint16_t &writeCount = resourceNode.virtualResource->_writerCount;
|
||||
CC_ASSERT_GT(writeCount, 1);
|
||||
--writeCount;
|
||||
|
||||
resourceNode.readerCount += _resourceNodes[attachmentInCurrentPassNode.textureHandle].readerCount;
|
||||
resourceNode.readerCount -= attachmentInCurrentPassNode.desc.loadOp == gfx::LoadOp::LOAD;
|
||||
}
|
||||
} else {
|
||||
lastPassId = currentPassId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameGraph::computeStoreActionAndMemoryless() {
|
||||
ID passId = 0;
|
||||
bool lastPassSubpassEnable = false;
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_refCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ID const oldPassId = passId;
|
||||
passId += !passNode->_subpass || lastPassSubpassEnable != passNode->_subpass;
|
||||
passId += oldPassId == passId ? passNode->_hasClearedAttachment * !passNode->_clearActionIgnorable : 0;
|
||||
passNode->setDevicePassId(passId);
|
||||
lastPassSubpassEnable = passNode->_subpass && !passNode->_subpassEnd;
|
||||
}
|
||||
|
||||
static ccstd::set<VirtualResource *> renderTargets;
|
||||
renderTargets.clear();
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_refCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (RenderTargetAttachment &attachment : passNode->_attachments) {
|
||||
CC_ASSERT(attachment.textureHandle.isValid());
|
||||
ResourceNode &resourceNode = getResourceNode(attachment.textureHandle);
|
||||
|
||||
if (resourceNode.virtualResource->isImported() || resourceNode.readerCount) {
|
||||
if (passNode->_subpass) {
|
||||
if (passNode->_devicePassId != resourceNode.virtualResource->_lastUsePass->_devicePassId) {
|
||||
attachment.storeOp = gfx::StoreOp::STORE;
|
||||
}
|
||||
} else {
|
||||
if (attachment.desc.writeMask) {
|
||||
attachment.storeOp = gfx::StoreOp::STORE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (passNode->_subpass && attachment.desc.loadOp == gfx::LoadOp::LOAD && resourceNode.version > 1) {
|
||||
ResourceNode *const resourceNodePrevVersion = getResourceNode(resourceNode.virtualResource, resourceNode.version - 1);
|
||||
CC_ASSERT(resourceNodePrevVersion);
|
||||
|
||||
if (resourceNodePrevVersion->writer->_devicePassId == passNode->_devicePassId) {
|
||||
attachment.desc.loadOp = gfx::LoadOp::DISCARD;
|
||||
resourceNodePrevVersion->writer->getRenderTargetAttachment(*this, resourceNodePrevVersion->virtualResource)->storeOp = gfx::StoreOp::DISCARD;
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment.desc.loadOp == gfx::LoadOp::LOAD) {
|
||||
resourceNode.virtualResource->_neverLoaded = false;
|
||||
}
|
||||
|
||||
if (attachment.storeOp == gfx::StoreOp::STORE) {
|
||||
resourceNode.virtualResource->_neverStored = false;
|
||||
}
|
||||
|
||||
renderTargets.emplace(resourceNode.virtualResource);
|
||||
}
|
||||
}
|
||||
|
||||
for (VirtualResource *const renderTarget : renderTargets) {
|
||||
const gfx::TextureInfo &textureDesc = static_cast<ResourceEntry<Texture> *>(renderTarget)->get().getDesc();
|
||||
|
||||
renderTarget->_memoryless = renderTarget->_neverLoaded && renderTarget->_neverStored;
|
||||
renderTarget->_memorylessMSAA = textureDesc.samples != gfx::SampleCount::X1 && renderTarget->_writerCount < 2;
|
||||
}
|
||||
}
|
||||
|
||||
void FrameGraph::generateDevicePasses() {
|
||||
Buffer::Allocator::getInstance().tick();
|
||||
Framebuffer::Allocator::getInstance().tick();
|
||||
RenderPass::Allocator::getInstance().tick();
|
||||
Texture::Allocator::getInstance().tick();
|
||||
|
||||
ID passId = 1;
|
||||
|
||||
static ccstd::vector<PassNode *> subpassNodes;
|
||||
subpassNodes.clear();
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_refCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (passId != passNode->_devicePassId) {
|
||||
_devicePasses.emplace_back(ccnew DevicePass(*this, subpassNodes));
|
||||
|
||||
for (PassNode *const p : subpassNodes) {
|
||||
p->releaseTransientResources();
|
||||
}
|
||||
|
||||
subpassNodes.clear();
|
||||
passId = passNode->_devicePassId;
|
||||
}
|
||||
|
||||
passNode->requestTransientResources();
|
||||
subpassNodes.emplace_back(passNode.get());
|
||||
}
|
||||
|
||||
CC_ASSERT(subpassNodes.size() == 1);
|
||||
|
||||
_devicePasses.emplace_back(ccnew DevicePass(*this, subpassNodes));
|
||||
|
||||
for (PassNode *const p : subpassNodes) {
|
||||
p->releaseTransientResources();
|
||||
}
|
||||
}
|
||||
|
||||
// https://dreampuf.github.io/GraphvizOnline/
|
||||
void FrameGraph::exportGraphViz(const ccstd::string &path) {
|
||||
std::ofstream out(path, std::ios::out | std::ios::binary);
|
||||
//out.imbue(std::locale("chs", std::locale::ctype));
|
||||
|
||||
if (out.fail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
out << "digraph framegraph {\n";
|
||||
out << "rankdir = LR\n";
|
||||
out << "bgcolor = black\n";
|
||||
out << "node [shape=rectangle, fontname=\"helvetica\", fontsize=10]\n\n";
|
||||
|
||||
for (const auto &node : _passNodes) {
|
||||
if (node->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out << "\"P" << node->_id << "\" [label=\"" << node->_name.str();
|
||||
|
||||
const PassNode *currPassNode = node.get();
|
||||
|
||||
if (currPassNode->_head) {
|
||||
out << "\\n(merged by pass " << currPassNode->_head->_name.str() << ")";
|
||||
} else {
|
||||
while (currPassNode->_next) {
|
||||
currPassNode = currPassNode->_next;
|
||||
out << " & " << currPassNode->_name.str();
|
||||
}
|
||||
}
|
||||
|
||||
out << "\\nrefs: " << node->_refCount
|
||||
<< "\\nseq: " << node->_id
|
||||
<< "\\ndevice pass id: " << node->_devicePassId
|
||||
<< "\", style=filled, fillcolor="
|
||||
<< (node->_refCount ? "darkorange" : "darkorange4") << "]\n";
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
|
||||
for (const ResourceNode &node : _resourceNodes) {
|
||||
if (node.writer && node.writer->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out << "\"R" << node.virtualResource->_id << "_" << +node.version << "\""
|
||||
"[label=\""
|
||||
<< node.virtualResource->_name.str() << "\\n(version: " << +node.version << ")"
|
||||
<< "\\nrefs:" << node.virtualResource->_refCount;
|
||||
|
||||
if (node.virtualResource->_memoryless) {
|
||||
out << "\\nMemoryless";
|
||||
}
|
||||
|
||||
if (node.virtualResource->_memorylessMSAA) {
|
||||
out << "\\nMemorylessMSAA";
|
||||
}
|
||||
|
||||
PassNode *const writer = (node.writer && node.writer->_head) ? node.writer->_head : node.writer;
|
||||
|
||||
if (writer) {
|
||||
out << "\\n";
|
||||
const RenderTargetAttachment *const attachment = writer->getRenderTargetAttachment(*this, node.virtualResource);
|
||||
|
||||
if (attachment) {
|
||||
switch (attachment->desc.loadOp) {
|
||||
case gfx::LoadOp::DISCARD:
|
||||
out << "DontCare";
|
||||
break;
|
||||
case gfx::LoadOp::CLEAR:
|
||||
out << "Clear";
|
||||
break;
|
||||
default:
|
||||
out << "Load";
|
||||
break;
|
||||
}
|
||||
out << ", ";
|
||||
out << (attachment->storeOp == gfx::StoreOp::DISCARD ? "DontCare" : "Store");
|
||||
out << "\\nWriteMask: 0x" << std::hex << static_cast<uint32_t>(attachment->desc.writeMask) << std::dec;
|
||||
} else {
|
||||
out << "Transfer";
|
||||
}
|
||||
}
|
||||
|
||||
out << "\", style=filled, fillcolor="
|
||||
<< ((node.virtualResource->isImported())
|
||||
? (node.virtualResource->_refCount ? "palegreen" : "palegreen4")
|
||||
: (node.version == 0 ? "pink" : (node.virtualResource->_refCount ? "skyblue" : "skyblue4")))
|
||||
<< "]\n";
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
|
||||
for (const auto &node : _passNodes) {
|
||||
if (node->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out << "P" << node->_id << " -> { ";
|
||||
|
||||
for (const Handle writer : node->_writes) {
|
||||
out << "R" << _resourceNodes[writer].virtualResource->_id << "_" << +_resourceNodes[writer].version << " ";
|
||||
}
|
||||
|
||||
out << "} [color=red]\n";
|
||||
}
|
||||
|
||||
out << "\n";
|
||||
|
||||
for (const ResourceNode &node : _resourceNodes) {
|
||||
if (node.writer && node.writer->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out << "R" << node.virtualResource->_id << "_" << +node.version << " -> { ";
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Handle read : passNode->_reads) {
|
||||
const ResourceNode *readResourceNode = &_resourceNodes[read];
|
||||
|
||||
if (readResourceNode->writer && readResourceNode->writer->_head) {
|
||||
const Handle resourceNodeHandlePrevVersion = readResourceNode->writer->_head->getWriteResourceNodeHandle(*this, readResourceNode->virtualResource);
|
||||
CC_ASSERT(resourceNodeHandlePrevVersion.isValid());
|
||||
readResourceNode = &getResourceNode(resourceNodeHandlePrevVersion);
|
||||
}
|
||||
|
||||
if (readResourceNode->virtualResource->_id == node.virtualResource->_id &&
|
||||
readResourceNode->version == node.version) {
|
||||
out << "P" << passNode->_id << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << "} [color=green]\n";
|
||||
}
|
||||
|
||||
for (const ResourceNode &node : _resourceNodes) {
|
||||
if (node.writer && node.writer->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
out << "R" << node.virtualResource->_id << "_" << +node.version << " -> { ";
|
||||
|
||||
for (const auto &passNode : _passNodes) {
|
||||
if (passNode->_head) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const RenderTargetAttachment &attachment : passNode->_attachments) {
|
||||
const ResourceNode *readResourceNode = &_resourceNodes[attachment.textureHandle];
|
||||
uint16_t const distanceToHead = readResourceNode->writer->_distanceToHead;
|
||||
|
||||
if (readResourceNode->writer && readResourceNode->writer->_head) {
|
||||
const Handle resourceNodeHandleHead = readResourceNode->writer->_head->getWriteResourceNodeHandle(*this, readResourceNode->virtualResource);
|
||||
CC_ASSERT(resourceNodeHandleHead.isValid());
|
||||
readResourceNode = &getResourceNode(resourceNodeHandleHead);
|
||||
}
|
||||
|
||||
if (readResourceNode->virtualResource == node.virtualResource &&
|
||||
readResourceNode->version == node.version + 1 - distanceToHead) {
|
||||
out << "P" << passNode->_id << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << "} [color=red4]\n";
|
||||
}
|
||||
|
||||
out << "}" << std::endl;
|
||||
out.close();
|
||||
}
|
||||
|
||||
ResourceNode *FrameGraph::getResourceNode(const VirtualResource *const virtualResource, uint8_t version) noexcept {
|
||||
const auto it = std::find_if(_resourceNodes.begin(), _resourceNodes.end(), [&](const ResourceNode &node) {
|
||||
return node.version == version && node.virtualResource == virtualResource;
|
||||
});
|
||||
|
||||
return it == _resourceNodes.end() ? nullptr : &(*it);
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
144
cocos/renderer/frame-graph/FrameGraph.h
Normal file
144
cocos/renderer/frame-graph/FrameGraph.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 <cstdint>
|
||||
#include "Blackboard.h"
|
||||
#include "CallbackPass.h"
|
||||
#include "DevicePass.h"
|
||||
#include "PassNode.h"
|
||||
#include "PassNodeBuilder.h"
|
||||
#include "ResourceEntry.h"
|
||||
#include "ResourceNode.h"
|
||||
#include "base/std/container/string.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class FrameGraph final {
|
||||
public:
|
||||
using ResourceHandleBlackboard = Blackboard<StringHandle, Handle::IndexType, Handle::UNINITIALIZED>;
|
||||
|
||||
FrameGraph() = default;
|
||||
~FrameGraph() = default;
|
||||
FrameGraph(const FrameGraph &) = delete;
|
||||
FrameGraph(FrameGraph &&) noexcept = delete;
|
||||
FrameGraph &operator=(const FrameGraph &) = delete;
|
||||
FrameGraph &operator=(FrameGraph &&) noexcept = delete;
|
||||
|
||||
static StringHandle stringToHandle(const char *name);
|
||||
static const char *handleToString(const StringHandle &handle) noexcept;
|
||||
|
||||
void present(const TextureHandle &input, gfx::Texture *target, bool useMoveSemantic = true);
|
||||
void presentLastVersion(const VirtualResource *virtualResource, gfx::Texture *target, bool useMoveSemantic = true);
|
||||
void presentFromBlackboard(const StringHandle &inputName, gfx::Texture *target, bool useMoveSemantic = true);
|
||||
void compile();
|
||||
void execute() noexcept;
|
||||
void reset() noexcept;
|
||||
static void gc(uint32_t unusedFrameCount = 30) noexcept;
|
||||
|
||||
template <typename Data, typename SetupMethod, typename ExecuteMethod>
|
||||
const CallbackPass<Data, ExecuteMethod> &addPass(PassInsertPoint insertPoint, const StringHandle &name, SetupMethod setup, ExecuteMethod &&execute) noexcept;
|
||||
|
||||
template <typename DescriptorType, typename ResourceType = typename ResourceTypeLookupTable<DescriptorType>::Resource>
|
||||
TypedHandle<ResourceType> create(const StringHandle &name, const DescriptorType &desc) noexcept;
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> importExternal(const StringHandle &name, ResourceType &resource) noexcept;
|
||||
void move(TextureHandle from, TextureHandle to, uint8_t mipmapLevel, uint8_t faceId, uint8_t arrayPosition) noexcept;
|
||||
|
||||
inline ResourceNode &getResourceNode(const Handle handle) noexcept { return _resourceNodes[handle]; }
|
||||
inline const ResourceNode &getResourceNode(const Handle handle) const noexcept { return _resourceNodes[handle]; }
|
||||
inline ResourceHandleBlackboard &getBlackboard() noexcept { return _blackboard; }
|
||||
|
||||
void exportGraphViz(const ccstd::string &path);
|
||||
inline void enableMerge(bool enable) noexcept;
|
||||
bool hasPass(StringHandle handle);
|
||||
|
||||
private:
|
||||
Handle create(VirtualResource *virtualResource);
|
||||
PassNode &createPassNode(PassInsertPoint insertPoint, const StringHandle &name, Executable *pass);
|
||||
Handle createResourceNode(VirtualResource *virtualResource);
|
||||
void sort() noexcept;
|
||||
void cull();
|
||||
void computeResourceLifetime();
|
||||
void mergePassNodes() noexcept;
|
||||
void computeStoreActionAndMemoryless();
|
||||
void generateDevicePasses();
|
||||
ResourceNode *getResourceNode(const VirtualResource *virtualResource, uint8_t version) noexcept;
|
||||
|
||||
ccstd::vector<std::unique_ptr<PassNode>> _passNodes{};
|
||||
ccstd::vector<ResourceNode> _resourceNodes{};
|
||||
ccstd::vector<std::unique_ptr<VirtualResource>> _virtualResources{};
|
||||
ccstd::vector<std::unique_ptr<DevicePass>> _devicePasses{};
|
||||
ResourceHandleBlackboard _blackboard;
|
||||
bool _merge{true};
|
||||
|
||||
friend class PassNode;
|
||||
friend class PassNodeBuilder;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Data, typename SetupMethod, typename ExecuteMethod>
|
||||
const CallbackPass<Data, ExecuteMethod> &FrameGraph::addPass(const PassInsertPoint insertPoint, const StringHandle &name, SetupMethod setup, ExecuteMethod &&execute) noexcept {
|
||||
static_assert(sizeof(ExecuteMethod) < 1024, "Execute() lambda is capturing too much data.");
|
||||
auto *const pass = ccnew CallbackPass<Data, ExecuteMethod>(std::forward<ExecuteMethod>(execute));
|
||||
PassNode &passNode = createPassNode(insertPoint, name, pass);
|
||||
PassNodeBuilder builder(*this, passNode);
|
||||
setup(builder, pass->getData());
|
||||
return *pass;
|
||||
}
|
||||
|
||||
template <typename DescriptorType, typename ResourceType>
|
||||
TypedHandle<ResourceType> FrameGraph::create(const StringHandle &name, const DescriptorType &desc) noexcept {
|
||||
auto *const virtualResource = ccnew ResourceEntry<ResourceType>(name, static_cast<ID>(_virtualResources.size()), desc);
|
||||
return TypedHandle<ResourceType>(create(virtualResource));
|
||||
}
|
||||
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> FrameGraph::importExternal(const StringHandle &name, ResourceType &resource) noexcept {
|
||||
CC_ASSERT(resource.get());
|
||||
auto *const virtualResource = ccnew ResourceEntry<ResourceType>(name, static_cast<ID>(_virtualResources.size()), resource);
|
||||
return TypedHandle<ResourceType>(create(virtualResource));
|
||||
}
|
||||
|
||||
void FrameGraph::enableMerge(bool const enable) noexcept {
|
||||
_merge = enable;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename DescriptorType, typename ResourceType>
|
||||
TypedHandle<ResourceType> PassNodeBuilder::create(const StringHandle &name, const DescriptorType &desc) const noexcept {
|
||||
return _graph.create(name, desc);
|
||||
}
|
||||
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> PassNodeBuilder::importExternal(const StringHandle &name, ResourceType &resource) const noexcept {
|
||||
return _graph.importExternal(name, resource);
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
45
cocos/renderer/frame-graph/Handle.h
Normal file
45
cocos/renderer/frame-graph/Handle.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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/IndexHandle.h"
|
||||
#include "base/StringPool.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
using Handle = cc::IndexHandle<uint16_t>;
|
||||
using StringHandle = cc::StringHandle;
|
||||
using StringPool = cc::StringPool<false>;
|
||||
|
||||
template <typename T>
|
||||
class TypedHandle final : public Handle {
|
||||
public:
|
||||
using Type = T;
|
||||
using Handle::Handle;
|
||||
};
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
101
cocos/renderer/frame-graph/ImmutableState.cpp
Normal file
101
cocos/renderer/frame-graph/ImmutableState.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "ImmutableState.h"
|
||||
#include <vector>
|
||||
#include "DevicePassResourceTable.h"
|
||||
#include "gfx-base/GFXBarrier.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
using gfx::AccessFlags;
|
||||
using gfx::BufferUsage;
|
||||
using gfx::hasFlag;
|
||||
using gfx::MemoryAccess;
|
||||
using gfx::MemoryUsage;
|
||||
using gfx::ShaderStageFlags;
|
||||
using gfx::TextureUsage;
|
||||
|
||||
namespace {
|
||||
|
||||
AccessFlags getAccessFlags(BufferUsage usage, MemoryUsage memUsage, const AccessStatus& status) noexcept {
|
||||
return gfx::getAccessFlags(usage, memUsage, status.access, status.visibility);
|
||||
}
|
||||
|
||||
AccessFlags getAccessFlags(TextureUsage usage, const AccessStatus& status) noexcept {
|
||||
return gfx::getAccessFlags(usage, status.access, status.visibility);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::pair<gfx::GFXObject*, gfx::GFXObject*> getBarrier(const ResourceBarrier& barrierInfo, const DevicePassResourceTable* dictPtr) noexcept {
|
||||
std::pair<gfx::GFXObject*, gfx::GFXObject*> res;
|
||||
|
||||
const auto& dict = *dictPtr;
|
||||
if (barrierInfo.resourceType == ResourceType::BUFFER) {
|
||||
gfx::Buffer* gfxBuffer{nullptr};
|
||||
if (hasFlag(barrierInfo.endStatus.access, MemoryAccess::WRITE_ONLY)) {
|
||||
gfxBuffer = dict.getWrite(static_cast<BufferHandle>(barrierInfo.handle));
|
||||
} else {
|
||||
gfxBuffer = dict.getRead(static_cast<BufferHandle>(barrierInfo.handle));
|
||||
}
|
||||
auto usage = gfxBuffer->getUsage();
|
||||
auto memUsage = gfxBuffer->getMemUsage();
|
||||
gfx::BufferBarrierInfo info;
|
||||
info.prevAccesses = getAccessFlags(usage, memUsage, barrierInfo.beginStatus);
|
||||
info.nextAccesses = getAccessFlags(usage, memUsage, barrierInfo.endStatus);
|
||||
info.offset = static_cast<uint32_t>(barrierInfo.bufferRange.base);
|
||||
info.size = static_cast<uint32_t>(barrierInfo.bufferRange.len);
|
||||
info.type = barrierInfo.barrierType;
|
||||
|
||||
res.first = gfx::Device::getInstance()->getBufferBarrier(info);
|
||||
res.second = gfxBuffer;
|
||||
} else if (barrierInfo.resourceType == ResourceType::TEXTURE) {
|
||||
gfx::Texture* gfxTexture{nullptr};
|
||||
if (hasFlag(barrierInfo.beginStatus.access, MemoryAccess::WRITE_ONLY)) {
|
||||
gfxTexture = dict.getWrite(static_cast<TextureHandle>(barrierInfo.handle));
|
||||
} else {
|
||||
gfxTexture = dict.getRead(static_cast<TextureHandle>(barrierInfo.handle));
|
||||
}
|
||||
auto usage = gfxTexture->getInfo().usage;
|
||||
gfx::TextureBarrierInfo info;
|
||||
info.type = barrierInfo.barrierType;
|
||||
info.prevAccesses = getAccessFlags(usage, barrierInfo.beginStatus);
|
||||
info.nextAccesses = getAccessFlags(usage, barrierInfo.endStatus);
|
||||
info.range.mipLevel = static_cast<uint32_t>(barrierInfo.mipRange.base);
|
||||
info.range.levelCount = static_cast<uint32_t>(barrierInfo.mipRange.len);
|
||||
info.range.firstSlice = static_cast<uint32_t>(barrierInfo.layerRange.base);
|
||||
info.range.numSlices = static_cast<uint32_t>(barrierInfo.layerRange.len);
|
||||
|
||||
res.first = gfx::Device::getInstance()->getTextureBarrier(info);
|
||||
res.second = gfxTexture;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
84
cocos/renderer/frame-graph/ImmutableState.h
Normal file
84
cocos/renderer/frame-graph/ImmutableState.h
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "frame-graph/Resource.h"
|
||||
#include "gfx-base/GFXDef-common.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class DevicePassResourceTable;
|
||||
|
||||
struct Range {
|
||||
size_t base{0};
|
||||
size_t len{0};
|
||||
};
|
||||
|
||||
struct AccessStatus {
|
||||
gfx::PassType passType{gfx::PassType::RASTER};
|
||||
gfx::ShaderStageFlagBit visibility{gfx::ShaderStageFlagBit::NONE};
|
||||
gfx::MemoryAccessBit access{gfx::MemoryAccessBit::NONE};
|
||||
};
|
||||
|
||||
enum class ResourceType : uint32_t {
|
||||
UNKNOWN,
|
||||
BUFFER,
|
||||
TEXTURE,
|
||||
};
|
||||
|
||||
struct ResourceBarrier {
|
||||
ResourceType resourceType{ResourceType::UNKNOWN};
|
||||
gfx::BarrierType barrierType{gfx::BarrierType::FULL};
|
||||
|
||||
Handle handle;
|
||||
|
||||
AccessStatus beginStatus;
|
||||
AccessStatus endStatus;
|
||||
Range layerRange;
|
||||
|
||||
union {
|
||||
Range mipRange;
|
||||
Range bufferRange;
|
||||
};
|
||||
};
|
||||
|
||||
struct PassBarrierPair {
|
||||
ccstd::vector<ResourceBarrier> frontBarriers;
|
||||
ccstd::vector<ResourceBarrier> rearBarriers;
|
||||
};
|
||||
|
||||
using BarriersList = std::vector<PassBarrierPair>;
|
||||
|
||||
struct PassBarriers {
|
||||
PassBarrierPair blockBarrier;
|
||||
BarriersList subpassBarriers;
|
||||
};
|
||||
|
||||
std::pair<gfx::GFXObject* /*barrier*/, gfx::GFXObject* /*resource*/> getBarrier(const ResourceBarrier& barrierInfo, const DevicePassResourceTable* dict) noexcept;
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
57
cocos/renderer/frame-graph/PassInsertPointManager.cpp
Normal file
57
cocos/renderer/frame-graph/PassInsertPointManager.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "PassInsertPointManager.h"
|
||||
#include "base/Macros.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
PassInsertPointManager &PassInsertPointManager::getInstance() {
|
||||
static PassInsertPointManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
PassInsertPoint PassInsertPointManager::record(const char *const name, const PassInsertPoint point) {
|
||||
StringHandle nameHandle = _stringPool.find(name);
|
||||
|
||||
if (nameHandle.isValid()) {
|
||||
CC_ASSERT(point == get(nameHandle));
|
||||
return get(nameHandle);
|
||||
}
|
||||
|
||||
nameHandle = _stringPool.stringToHandle(name);
|
||||
CC_ASSERT(nameHandle == _insertPoints.size());
|
||||
_insertPoints.emplace_back(point);
|
||||
return point;
|
||||
}
|
||||
|
||||
PassInsertPoint PassInsertPointManager::get(const char *const name) const {
|
||||
const StringHandle nameHandle = _stringPool.find(name);
|
||||
CC_ASSERT(nameHandle.isValid());
|
||||
return get(nameHandle);
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
64
cocos/renderer/frame-graph/PassInsertPointManager.h
Normal file
64
cocos/renderer/frame-graph/PassInsertPointManager.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "Handle.h"
|
||||
#include "base/Macros.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
using PassInsertPoint = uint16_t;
|
||||
|
||||
class PassInsertPointManager final {
|
||||
public:
|
||||
PassInsertPointManager(const PassInsertPointManager &) = delete;
|
||||
PassInsertPointManager(PassInsertPointManager &&) noexcept = delete;
|
||||
PassInsertPointManager &operator=(const PassInsertPointManager &) = delete;
|
||||
PassInsertPointManager &operator=(PassInsertPointManager &&) noexcept = delete;
|
||||
|
||||
static PassInsertPointManager &getInstance();
|
||||
|
||||
PassInsertPoint record(const char *name, PassInsertPoint point);
|
||||
PassInsertPoint get(const char *name) const;
|
||||
|
||||
private:
|
||||
PassInsertPointManager() = default;
|
||||
~PassInsertPointManager() = default;
|
||||
|
||||
inline PassInsertPoint get(StringHandle name) const;
|
||||
|
||||
StringPool _stringPool;
|
||||
ccstd::vector<PassInsertPoint> _insertPoints{};
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PassInsertPoint PassInsertPointManager::get(const StringHandle name) const {
|
||||
return _insertPoints[name];
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
191
cocos/renderer/frame-graph/PassNode.cpp
Normal file
191
cocos/renderer/frame-graph/PassNode.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "PassNode.h"
|
||||
#include <algorithm>
|
||||
#include "FrameGraph.h"
|
||||
#include "ResourceNode.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
PassNode::PassNode(const PassInsertPoint inserPoint, const StringHandle name, const ID &id, Executable *pass)
|
||||
: _pass(pass),
|
||||
_name(name),
|
||||
_id(id),
|
||||
_insertPoint(inserPoint) {
|
||||
CC_ASSERT(_name.isValid());
|
||||
}
|
||||
|
||||
Handle PassNode::read(FrameGraph & /*graph*/, const Handle &input) {
|
||||
const auto it = std::find_if(_reads.begin(), _reads.end(), [input](const Handle handle) {
|
||||
return input == handle;
|
||||
});
|
||||
|
||||
if (it == _reads.end()) {
|
||||
_reads.push_back(input);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
Handle PassNode::write(FrameGraph &graph, const Handle &output) {
|
||||
CC_ASSERT(std::find_if(_writes.begin(), _writes.end(), [output](const Handle handle) {
|
||||
return output == handle;
|
||||
}) == _writes.end());
|
||||
|
||||
const ResourceNode &nodeOldVersion = graph.getResourceNode(output);
|
||||
nodeOldVersion.virtualResource->newVersion();
|
||||
_sideEffect = _sideEffect || nodeOldVersion.virtualResource->isImported();
|
||||
const Handle handle = graph.createResourceNode(nodeOldVersion.virtualResource);
|
||||
ResourceNode &nodeNewVersion = graph.getResourceNode(handle);
|
||||
nodeNewVersion.writer = this;
|
||||
_writes.push_back(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void PassNode::createRenderTargetAttachment(RenderTargetAttachment &&attachment) {
|
||||
if (attachment.desc.usage == RenderTargetAttachment::Usage::COLOR) {
|
||||
if (attachment.desc.slot == 0xff) {
|
||||
for (uint8_t i = 0; i < RenderTargetAttachment::DEPTH_STENCIL_SLOT_START; ++i) {
|
||||
if ((_usedRenderTargetSlotMask & (1 << i)) == 0) {
|
||||
attachment.desc.slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CC_ASSERT(attachment.desc.slot < RenderTargetAttachment::DEPTH_STENCIL_SLOT_START);
|
||||
}
|
||||
} else {
|
||||
attachment.desc.slot = RenderTargetAttachment::DEPTH_STENCIL_SLOT_START + static_cast<uint8_t>(attachment.desc.usage) - 1;
|
||||
|
||||
if (attachment.desc.usage == RenderTargetAttachment::Usage::DEPTH_STENCIL) {
|
||||
CC_ASSERT((_usedRenderTargetSlotMask & (1 << (RenderTargetAttachment::DEPTH_STENCIL_SLOT_START + static_cast<uint8_t>(RenderTargetAttachment::Usage::DEPTH) - 1))) == 0);
|
||||
CC_ASSERT((_usedRenderTargetSlotMask & (1 << (RenderTargetAttachment::DEPTH_STENCIL_SLOT_START + static_cast<uint8_t>(RenderTargetAttachment::Usage::STENCIL) - 1))) == 0);
|
||||
} else {
|
||||
CC_ASSERT((_usedRenderTargetSlotMask & (1 << (RenderTargetAttachment::DEPTH_STENCIL_SLOT_START + static_cast<uint8_t>(attachment.desc.usage) - 1))) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
CC_ASSERT((_usedRenderTargetSlotMask & (1 << attachment.desc.slot)) == 0);
|
||||
_usedRenderTargetSlotMask |= (1 << attachment.desc.slot);
|
||||
|
||||
_attachments.emplace_back(attachment);
|
||||
_hasClearedAttachment = _hasClearedAttachment || (attachment.desc.loadOp == gfx::LoadOp::CLEAR);
|
||||
}
|
||||
|
||||
bool PassNode::canMerge(const FrameGraph &graph, const PassNode &passNode) const {
|
||||
const size_t attachmentCount = _attachments.size();
|
||||
|
||||
if (passNode._hasClearedAttachment || attachmentCount != passNode._attachments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < attachmentCount; ++i) {
|
||||
const RenderTargetAttachment &attachmentInPassNodeA = _attachments[i];
|
||||
const RenderTargetAttachment &attachmentInPassNodeB = passNode._attachments[i];
|
||||
|
||||
if (attachmentInPassNodeA.desc.usage != attachmentInPassNodeB.desc.usage ||
|
||||
attachmentInPassNodeA.desc.slot != attachmentInPassNodeB.desc.slot ||
|
||||
attachmentInPassNodeA.desc.writeMask != attachmentInPassNodeB.desc.writeMask ||
|
||||
attachmentInPassNodeA.level != attachmentInPassNodeB.level ||
|
||||
attachmentInPassNodeA.layer != attachmentInPassNodeB.layer ||
|
||||
attachmentInPassNodeA.index != attachmentInPassNodeB.index ||
|
||||
graph.getResourceNode(attachmentInPassNodeA.textureHandle).virtualResource != graph.getResourceNode(attachmentInPassNodeB.textureHandle).virtualResource) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderTargetAttachment *PassNode::getRenderTargetAttachment(const Handle &handle) {
|
||||
const auto it = std::find_if(_attachments.begin(), _attachments.end(), [&handle](const RenderTargetAttachment &attachment) {
|
||||
return attachment.textureHandle == handle;
|
||||
});
|
||||
|
||||
return it == _attachments.end() ? nullptr : &(*it);
|
||||
}
|
||||
|
||||
RenderTargetAttachment *PassNode::getRenderTargetAttachment(const FrameGraph &graph, const VirtualResource *const resource) {
|
||||
const auto it = std::find_if(_attachments.begin(), _attachments.end(), [&](const RenderTargetAttachment &attachment) {
|
||||
return graph.getResourceNode(attachment.textureHandle).virtualResource == resource;
|
||||
});
|
||||
|
||||
return it == _attachments.end() ? nullptr : &(*it);
|
||||
}
|
||||
|
||||
void PassNode::requestTransientResources() {
|
||||
PassNode *it = this;
|
||||
|
||||
do {
|
||||
std::for_each(it->_resourceRequestArray.begin(), it->_resourceRequestArray.end(), [](VirtualResource *const resource) {
|
||||
if (!resource->isImported()) {
|
||||
resource->request();
|
||||
}
|
||||
});
|
||||
|
||||
it = it->_next;
|
||||
} while (it);
|
||||
}
|
||||
|
||||
void PassNode::releaseTransientResources() {
|
||||
PassNode *it = this;
|
||||
|
||||
do {
|
||||
// resources should be release in the reverse order to stabilize usages between frames
|
||||
std::for_each(it->_resourceReleaseArray.rbegin(), it->_resourceReleaseArray.rend(), [](VirtualResource *const resource) {
|
||||
if (!resource->isImported()) {
|
||||
resource->release();
|
||||
}
|
||||
});
|
||||
|
||||
it = it->_next;
|
||||
} while (it);
|
||||
}
|
||||
|
||||
void PassNode::setDevicePassId(ID const id) {
|
||||
PassNode *it = this;
|
||||
|
||||
do {
|
||||
it->_devicePassId = id;
|
||||
|
||||
it = it->_next;
|
||||
} while (it);
|
||||
}
|
||||
|
||||
Handle PassNode::getWriteResourceNodeHandle(const FrameGraph &graph, const VirtualResource *const resource) const {
|
||||
const auto it = std::find_if(_writes.begin(), _writes.end(), [&](const Handle handle) {
|
||||
return graph.getResourceNode(handle).virtualResource == resource;
|
||||
});
|
||||
|
||||
return it == _writes.end() ? Handle{} : *it;
|
||||
}
|
||||
|
||||
void PassNode::setBarrier(const PassBarrierPair &barrier) {
|
||||
_barriers = barrier;
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
124
cocos/renderer/frame-graph/PassNode.h
Normal file
124
cocos/renderer/frame-graph/PassNode.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 <memory>
|
||||
#include "CallbackPass.h"
|
||||
#include "Handle.h"
|
||||
#include "ImmutableState.h"
|
||||
#include "PassInsertPointManager.h"
|
||||
#include "RenderTargetAttachment.h"
|
||||
#include "VirtualResource.h"
|
||||
#include "gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class FrameGraph;
|
||||
|
||||
class PassNode final {
|
||||
public:
|
||||
PassNode(PassInsertPoint inserPoint, StringHandle name, const ID &id, Executable *pass);
|
||||
~PassNode() = default;
|
||||
PassNode(PassNode &&) noexcept = default;
|
||||
PassNode(const PassNode &) = delete;
|
||||
PassNode &operator=(const PassNode &) = delete;
|
||||
PassNode &operator=(PassNode &&) noexcept = delete;
|
||||
|
||||
Handle read(FrameGraph &graph, const Handle &input);
|
||||
Handle write(FrameGraph &graph, const Handle &output);
|
||||
void createRenderTargetAttachment(RenderTargetAttachment &&attachment);
|
||||
void setBarrier(const PassBarrierPair &barrier);
|
||||
|
||||
inline void sideEffect();
|
||||
inline void subpass(bool end, bool clearActionIgnorable);
|
||||
inline void setViewport(const gfx::Viewport &viewport, const gfx::Rect &scissor);
|
||||
inline const PassBarrierPair &getBarriers() const;
|
||||
|
||||
private:
|
||||
bool canMerge(const FrameGraph &graph, const PassNode &passNode) const;
|
||||
RenderTargetAttachment *getRenderTargetAttachment(const Handle &handle);
|
||||
RenderTargetAttachment *getRenderTargetAttachment(const FrameGraph &graph, const VirtualResource *resource);
|
||||
void requestTransientResources();
|
||||
void releaseTransientResources();
|
||||
void setDevicePassId(ID id);
|
||||
Handle getWriteResourceNodeHandle(const FrameGraph &graph, const VirtualResource *resource) const;
|
||||
|
||||
std::unique_ptr<Executable> _pass{nullptr};
|
||||
ccstd::vector<Handle> _reads{};
|
||||
ccstd::vector<Handle> _writes{};
|
||||
ccstd::vector<RenderTargetAttachment> _attachments{};
|
||||
ccstd::vector<VirtualResource *> _resourceRequestArray{};
|
||||
ccstd::vector<VirtualResource *> _resourceReleaseArray{};
|
||||
const StringHandle _name;
|
||||
uint32_t _refCount{0};
|
||||
PassNode *_head{nullptr};
|
||||
PassNode *_next{nullptr};
|
||||
uint16_t _distanceToHead{0};
|
||||
uint16_t _usedRenderTargetSlotMask{0};
|
||||
ID const _id{0};
|
||||
ID _devicePassId{0};
|
||||
const PassInsertPoint _insertPoint{0};
|
||||
bool _sideEffect{false};
|
||||
bool _subpass{false};
|
||||
bool _subpassEnd{false};
|
||||
bool _hasClearedAttachment{false};
|
||||
bool _clearActionIgnorable{false};
|
||||
|
||||
bool _customViewport{false};
|
||||
gfx::Viewport _viewport;
|
||||
gfx::Rect _scissor;
|
||||
|
||||
PassBarrierPair _barriers;
|
||||
|
||||
friend class FrameGraph;
|
||||
friend class DevicePass;
|
||||
friend class DevicePassResourceTable;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const PassBarrierPair &PassNode::getBarriers() const {
|
||||
return _barriers;
|
||||
}
|
||||
|
||||
void PassNode::sideEffect() {
|
||||
_sideEffect = true;
|
||||
}
|
||||
|
||||
void PassNode::subpass(bool end, bool clearActionIgnorable) {
|
||||
_subpass = true;
|
||||
_subpassEnd = end;
|
||||
_clearActionIgnorable = clearActionIgnorable;
|
||||
}
|
||||
|
||||
void PassNode::setViewport(const gfx::Viewport &viewport, const gfx::Rect &scissor) {
|
||||
_customViewport = true;
|
||||
_viewport = viewport;
|
||||
_scissor = scissor;
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
71
cocos/renderer/frame-graph/PassNodeBuilder.cpp
Normal file
71
cocos/renderer/frame-graph/PassNodeBuilder.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "PassNodeBuilder.h"
|
||||
#include "FrameGraph.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
PassNodeBuilder::PassNodeBuilder(FrameGraph &graph, PassNode &passNode) noexcept
|
||||
: _graph(graph),
|
||||
_passNode(passNode) {
|
||||
}
|
||||
|
||||
Handle PassNodeBuilder::read(const Handle &input) const noexcept {
|
||||
return _passNode.read(_graph, input);
|
||||
}
|
||||
|
||||
TextureHandle PassNodeBuilder::write(const TextureHandle &output, uint8_t mipmapLevel, uint8_t faceId, uint8_t arrayPosition, const RenderTargetAttachment::Descriptor &attachmentDesc) const noexcept {
|
||||
const TextureHandle handle(_passNode.write(_graph, output));
|
||||
RenderTargetAttachment attachment;
|
||||
attachment.textureHandle = handle;
|
||||
attachment.desc = attachmentDesc;
|
||||
attachment.level = mipmapLevel;
|
||||
attachment.layer = faceId;
|
||||
attachment.index = arrayPosition;
|
||||
_passNode.createRenderTargetAttachment(std::forward<RenderTargetAttachment>(attachment));
|
||||
|
||||
if (attachmentDesc.loadOp == gfx::LoadOp::LOAD) {
|
||||
ResourceNode &outputResourceNode = _graph.getResourceNode(output);
|
||||
++outputResourceNode.readerCount;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
TextureHandle PassNodeBuilder::write(const TextureHandle &output, const RenderTargetAttachment::Descriptor &attachmentDesc) const noexcept {
|
||||
return write(output, 0, 0, 0, attachmentDesc);
|
||||
}
|
||||
|
||||
void PassNodeBuilder::writeToBlackboard(const StringHandle &name, const Handle &handle) const noexcept {
|
||||
_graph.getBlackboard().put(name, handle);
|
||||
}
|
||||
|
||||
Handle PassNodeBuilder::readFromBlackboard(const StringHandle &name) const noexcept {
|
||||
return Handle(_graph.getBlackboard().get(name));
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
103
cocos/renderer/frame-graph/PassNodeBuilder.h
Normal file
103
cocos/renderer/frame-graph/PassNodeBuilder.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "PassNode.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class PassNodeBuilder final {
|
||||
public:
|
||||
PassNodeBuilder(FrameGraph &graph, PassNode &passNode) noexcept;
|
||||
PassNodeBuilder() = delete;
|
||||
~PassNodeBuilder() = default;
|
||||
PassNodeBuilder(const PassNodeBuilder &) = delete;
|
||||
PassNodeBuilder(PassNodeBuilder &&) = delete;
|
||||
PassNodeBuilder &operator=(const PassNodeBuilder &) = delete;
|
||||
PassNodeBuilder &operator=(PassNodeBuilder &&) = delete;
|
||||
|
||||
template <typename DescriptorType, typename ResourceType = typename ResourceTypeLookupTable<DescriptorType>::Resource>
|
||||
TypedHandle<ResourceType> create(const StringHandle &name, const DescriptorType &desc) const noexcept;
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> importExternal(const StringHandle &name, ResourceType &resource) const noexcept;
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> read(TypedHandle<ResourceType> const &input) const noexcept;
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> write(TypedHandle<ResourceType> const &output) const noexcept;
|
||||
TextureHandle write(const TextureHandle &output, uint8_t mipmapLevel, uint8_t faceId, uint8_t arrayPosition, const RenderTargetAttachment::Descriptor &attachmentDesc) const noexcept;
|
||||
TextureHandle write(const TextureHandle &output, const RenderTargetAttachment::Descriptor &attachmentDesc) const noexcept;
|
||||
|
||||
inline void sideEffect() const noexcept;
|
||||
inline void subpass(bool end = false, bool clearActionIgnorable = true) const noexcept;
|
||||
inline void setViewport(const gfx::Rect &scissor) noexcept;
|
||||
inline void setViewport(const gfx::Viewport &viewport, const gfx::Rect &scissor) noexcept;
|
||||
inline void setBarrier(const PassBarrierPair &barrier);
|
||||
|
||||
void writeToBlackboard(const StringHandle &name, const Handle &handle) const noexcept;
|
||||
Handle readFromBlackboard(const StringHandle &name) const noexcept;
|
||||
|
||||
private:
|
||||
Handle read(const Handle &input) const noexcept;
|
||||
|
||||
FrameGraph &_graph;
|
||||
PassNode &_passNode;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> PassNodeBuilder::read(TypedHandle<ResourceType> const &input) const noexcept {
|
||||
return TypedHandle<ResourceType>(read(static_cast<const Handle &>(input)));
|
||||
}
|
||||
|
||||
template <typename ResourceType>
|
||||
TypedHandle<ResourceType> PassNodeBuilder::write(TypedHandle<ResourceType> const &output) const noexcept {
|
||||
return TypedHandle<ResourceType>(_passNode.write(_graph, output));
|
||||
}
|
||||
|
||||
void PassNodeBuilder::sideEffect() const noexcept {
|
||||
_passNode.sideEffect();
|
||||
}
|
||||
|
||||
void PassNodeBuilder::subpass(bool end, bool clearActionIgnorable) const noexcept {
|
||||
_passNode.subpass(end, clearActionIgnorable);
|
||||
}
|
||||
|
||||
void PassNodeBuilder::setViewport(const gfx::Rect &scissor) noexcept {
|
||||
gfx::Viewport viewport{scissor.x, scissor.y, scissor.width, scissor.height, 0.F, 1.F};
|
||||
_passNode.setViewport(viewport, scissor);
|
||||
}
|
||||
|
||||
void PassNodeBuilder::setViewport(const gfx::Viewport &viewport, const gfx::Rect &scissor) noexcept {
|
||||
_passNode.setViewport(viewport, scissor);
|
||||
}
|
||||
|
||||
void PassNodeBuilder::setBarrier(const PassBarrierPair &barrier) {
|
||||
_passNode.setBarrier(barrier);
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
79
cocos/renderer/frame-graph/RenderTargetAttachment.h
Normal file
79
cocos/renderer/frame-graph/RenderTargetAttachment.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "Resource.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
struct RenderTargetAttachment final {
|
||||
using StoreOp = gfx::StoreOp;
|
||||
using LoadOp = gfx::LoadOp;
|
||||
using Color = gfx::Color;
|
||||
|
||||
enum class Usage : uint8_t {
|
||||
COLOR,
|
||||
DEPTH,
|
||||
STENCIL,
|
||||
DEPTH_STENCIL,
|
||||
};
|
||||
|
||||
struct Descriptor final {
|
||||
Usage usage{Usage::COLOR};
|
||||
uint8_t slot{0xff};
|
||||
uint8_t writeMask{0xff};
|
||||
LoadOp loadOp{LoadOp::DISCARD};
|
||||
Color clearColor;
|
||||
float clearDepth{1.F};
|
||||
uint8_t clearStencil{0U};
|
||||
|
||||
gfx::AccessFlags beginAccesses{gfx::AccessFlagBit::NONE};
|
||||
gfx::AccessFlags endAccesses{gfx::AccessFlagBit::NONE};
|
||||
};
|
||||
|
||||
struct Sorter {
|
||||
inline bool operator()(const RenderTargetAttachment &a1, const RenderTargetAttachment &a2) const noexcept;
|
||||
};
|
||||
|
||||
static constexpr uint8_t DEPTH_STENCIL_SLOT_START{13};
|
||||
|
||||
TextureHandle textureHandle{};
|
||||
Descriptor desc;
|
||||
uint8_t level{0};
|
||||
uint8_t layer{0};
|
||||
uint8_t index{0};
|
||||
StoreOp storeOp{StoreOp::DISCARD};
|
||||
};
|
||||
|
||||
inline bool RenderTargetAttachment::Sorter::operator()(const RenderTargetAttachment &a1, const RenderTargetAttachment &a2) const noexcept {
|
||||
if (a1.desc.usage == a2.desc.usage) {
|
||||
return a1.desc.slot < a2.desc.slot;
|
||||
}
|
||||
return a1.desc.usage < a2.desc.usage;
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
155
cocos/renderer/frame-graph/Resource.h
Normal file
155
cocos/renderer/frame-graph/Resource.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "ResourceAllocator.h"
|
||||
|
||||
#include "Handle.h"
|
||||
#include "renderer/gfx-base/GFXBuffer.h"
|
||||
#include "renderer/gfx-base/GFXDevice.h"
|
||||
#include "renderer/gfx-base/GFXFramebuffer.h"
|
||||
#include "renderer/gfx-base/GFXRenderPass.h"
|
||||
#include "renderer/gfx-base/GFXTexture.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType>
|
||||
struct DeviceResourceCreator final {
|
||||
inline DeviceResourceType *operator()(const DescriptorType & /*desc*/) const {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DescriptorType>
|
||||
struct ResourceTypeLookupTable final {};
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType,
|
||||
typename DeviceResourceCreatorType = DeviceResourceCreator<DeviceResourceType, DescriptorType>>
|
||||
class Resource final {
|
||||
public:
|
||||
using Allocator = ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>;
|
||||
using DeviceResource = DeviceResourceType;
|
||||
using Descriptor = DescriptorType;
|
||||
|
||||
Resource() = default;
|
||||
explicit Resource(const Descriptor &desc);
|
||||
~Resource() = default;
|
||||
Resource(const Resource &) = default;
|
||||
Resource(Resource &&) noexcept = default;
|
||||
Resource &operator=(const Resource &) = default;
|
||||
Resource &operator=(Resource &&) noexcept = default;
|
||||
|
||||
void createTransient() noexcept;
|
||||
void createPersistent() noexcept;
|
||||
|
||||
void destroyTransient() noexcept;
|
||||
void destroyPersistent() noexcept;
|
||||
inline DeviceResourceType *get() const noexcept;
|
||||
inline const Descriptor &getDesc() const noexcept;
|
||||
|
||||
private:
|
||||
explicit Resource(DeviceResourceType *external);
|
||||
|
||||
Descriptor _desc;
|
||||
DeviceResourceType *_deviceObject{nullptr};
|
||||
|
||||
friend class FrameGraph;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::Resource(const Descriptor &desc)
|
||||
: _desc(desc) {
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::Resource(DeviceResourceType *external)
|
||||
: _desc(external->getInfo()), _deviceObject(external) {
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::createTransient() noexcept {
|
||||
_deviceObject = Allocator::getInstance().alloc(_desc);
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::createPersistent() noexcept {
|
||||
if (!_deviceObject) {
|
||||
DeviceResourceCreatorType creator;
|
||||
_deviceObject = creator(_desc);
|
||||
_deviceObject->addRef();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::destroyTransient() noexcept {
|
||||
Allocator::getInstance().free(_deviceObject);
|
||||
_deviceObject = nullptr;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::destroyPersistent() noexcept {
|
||||
if (_deviceObject) {
|
||||
_deviceObject->release();
|
||||
_deviceObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
DeviceResourceType *Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::get() const noexcept {
|
||||
return _deviceObject;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
const DescriptorType &Resource<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::getDesc() const noexcept {
|
||||
return _desc;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define DEFINE_GFX_RESOURCE(Type) \
|
||||
template <> \
|
||||
struct DeviceResourceCreator<gfx::Type, gfx::Type##Info> final { \
|
||||
inline gfx::Type *operator()(const gfx::Type##Info &desc) const { \
|
||||
return gfx::Device::getInstance()->create##Type(desc); \
|
||||
} \
|
||||
}; \
|
||||
using Type = Resource<gfx::Type, gfx::Type##Info>; \
|
||||
using Type##Handle = TypedHandle<Type>; \
|
||||
template <> \
|
||||
struct ResourceTypeLookupTable<gfx::Type##Info> final { \
|
||||
using Resource = Type; \
|
||||
};
|
||||
|
||||
// Descriptors must have std::hash specialization and == operators
|
||||
DEFINE_GFX_RESOURCE(Buffer)
|
||||
DEFINE_GFX_RESOURCE(Framebuffer)
|
||||
DEFINE_GFX_RESOURCE(RenderPass)
|
||||
DEFINE_GFX_RESOURCE(Texture)
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
269
cocos/renderer/frame-graph/ResourceAllocator.h
Normal file
269
cocos/renderer/frame-graph/ResourceAllocator.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 <algorithm>
|
||||
#include "base/RefVector.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
#include "gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
class ResourceAllocator final {
|
||||
public:
|
||||
using DeviceResourceCreator = DeviceResourceCreatorType;
|
||||
|
||||
ResourceAllocator(const ResourceAllocator &) = delete;
|
||||
ResourceAllocator(ResourceAllocator &&) noexcept = delete;
|
||||
ResourceAllocator &operator=(const ResourceAllocator &) = delete;
|
||||
ResourceAllocator &operator=(ResourceAllocator &&) noexcept = delete;
|
||||
|
||||
static ResourceAllocator &getInstance() noexcept;
|
||||
DeviceResourceType *alloc(const DescriptorType &desc) noexcept;
|
||||
void free(DeviceResourceType *resource) noexcept;
|
||||
inline void tick() noexcept;
|
||||
void gc(uint32_t unusedFrameCount) noexcept;
|
||||
|
||||
private:
|
||||
using DeviceResourcePool = RefVector<DeviceResourceType *>;
|
||||
|
||||
ResourceAllocator() noexcept = default;
|
||||
~ResourceAllocator() = default;
|
||||
|
||||
ccstd::unordered_map<DescriptorType, DeviceResourcePool, gfx::Hasher<DescriptorType>> _pool{};
|
||||
ccstd::unordered_map<DeviceResourceType *, int64_t> _ages{};
|
||||
uint64_t _age{0};
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType> &
|
||||
ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::getInstance() noexcept {
|
||||
static ResourceAllocator instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
DeviceResourceType *ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::alloc(const DescriptorType &desc) noexcept {
|
||||
DeviceResourcePool &pool{_pool[desc]};
|
||||
|
||||
DeviceResourceType *resource{nullptr};
|
||||
for (DeviceResourceType *res : pool) {
|
||||
if (_ages[res] >= 0) {
|
||||
resource = res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!resource) {
|
||||
DeviceResourceCreator creator;
|
||||
resource = creator(desc);
|
||||
pool.pushBack(resource);
|
||||
}
|
||||
|
||||
_ages[resource] = -1;
|
||||
return resource;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::free(DeviceResourceType *const resource) noexcept {
|
||||
CC_ASSERT(_ages.count(resource) && _ages[resource] < 0);
|
||||
_ages[resource] = _age;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::tick() noexcept {
|
||||
++_age;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DescriptorType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, DescriptorType, DeviceResourceCreatorType>::gc(uint32_t const unusedFrameCount) noexcept {
|
||||
for (auto &pair : _pool) {
|
||||
DeviceResourcePool &pool = pair.second;
|
||||
|
||||
auto count = static_cast<int>(pool.size());
|
||||
|
||||
if (!count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int destroyBegin = count - 1;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int64_t ageI = _ages[pool.at(i)];
|
||||
if (ageI < 0 || _age - ageI < unusedFrameCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j = destroyBegin;
|
||||
|
||||
for (; j > i; --j) {
|
||||
int64_t ageJ = _ages[pool.at(j)];
|
||||
if (ageJ < 0 || _age - ageJ < unusedFrameCount) {
|
||||
std::swap(pool.at(i), pool.at(j));
|
||||
destroyBegin = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= j) {
|
||||
destroyBegin = i - 1;
|
||||
}
|
||||
|
||||
if (i >= destroyBegin) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (++destroyBegin < count) {
|
||||
auto *resource = pool.back();
|
||||
_ages.erase(resource);
|
||||
pool.popBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
class ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType> final {
|
||||
public:
|
||||
using DeviceResourceCreator = DeviceResourceCreatorType;
|
||||
|
||||
ResourceAllocator(const ResourceAllocator &) = delete;
|
||||
ResourceAllocator(ResourceAllocator &&) noexcept = delete;
|
||||
ResourceAllocator &operator=(const ResourceAllocator &) = delete;
|
||||
ResourceAllocator &operator=(ResourceAllocator &&) noexcept = delete;
|
||||
|
||||
static ResourceAllocator &getInstance() noexcept;
|
||||
DeviceResourceType *alloc(const gfx::FramebufferInfo &desc) noexcept;
|
||||
void free(DeviceResourceType *resource) noexcept;
|
||||
inline void tick() noexcept;
|
||||
void gc(uint32_t unusedFrameCount) noexcept;
|
||||
|
||||
private:
|
||||
using DeviceResourcePool = RefVector<DeviceResourceType *>;
|
||||
|
||||
ResourceAllocator() noexcept = default;
|
||||
~ResourceAllocator() = default;
|
||||
|
||||
std::unordered_map<ccstd::hash_t, DeviceResourcePool> _pool{};
|
||||
std::unordered_map<DeviceResourceType *, int64_t> _ages{};
|
||||
uint64_t _age{0};
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType> &
|
||||
ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType>::getInstance() noexcept {
|
||||
static ResourceAllocator instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
DeviceResourceType *ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType>::alloc(const gfx::FramebufferInfo &desc) noexcept {
|
||||
ccstd::hash_t hash = gfx::Hasher<gfx::FramebufferInfo>()(desc);
|
||||
DeviceResourcePool &pool{_pool[hash]};
|
||||
|
||||
DeviceResourceType *resource{nullptr};
|
||||
for (DeviceResourceType *res : pool) {
|
||||
if (_ages[res] >= 0) {
|
||||
resource = res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!resource) {
|
||||
DeviceResourceCreator creator;
|
||||
resource = creator(desc);
|
||||
pool.pushBack(resource);
|
||||
}
|
||||
|
||||
_ages[resource] = -1;
|
||||
return resource;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType>::free(DeviceResourceType *const resource) noexcept {
|
||||
CC_ASSERT(_ages.count(resource) && _ages[resource] < 0);
|
||||
_ages[resource] = _age;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType>::tick() noexcept {
|
||||
++_age;
|
||||
}
|
||||
|
||||
template <typename DeviceResourceType, typename DeviceResourceCreatorType>
|
||||
void ResourceAllocator<DeviceResourceType, gfx::FramebufferInfo, DeviceResourceCreatorType>::gc(uint32_t const unusedFrameCount) noexcept {
|
||||
for (auto &pair : _pool) {
|
||||
DeviceResourcePool &pool = pair.second;
|
||||
|
||||
auto count = static_cast<int>(pool.size());
|
||||
|
||||
if (!count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int destroyBegin = count - 1;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int64_t ageI = _ages[pool.at(i)];
|
||||
if (ageI < 0 || _age - ageI < unusedFrameCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j = destroyBegin;
|
||||
|
||||
for (; j > i; --j) {
|
||||
int64_t ageJ = _ages[pool.at(j)];
|
||||
if (ageJ < 0 || _age - ageJ < unusedFrameCount) {
|
||||
std::swap(pool.at(i), pool.at(j));
|
||||
destroyBegin = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= j) {
|
||||
destroyBegin = i - 1;
|
||||
}
|
||||
|
||||
if (i >= destroyBegin) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (++destroyBegin < count) {
|
||||
auto *resource = pool.back();
|
||||
// delete resource;
|
||||
_ages.erase(resource);
|
||||
pool.popBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
84
cocos/renderer/frame-graph/ResourceEntry.h
Normal file
84
cocos/renderer/frame-graph/ResourceEntry.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "VirtualResource.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
template <typename ResourceType, typename Enable = std::enable_if_t<std::is_base_of<gfx::GFXObject, typename ResourceType::DeviceResource>::value>>
|
||||
class ResourceEntry final : public VirtualResource {
|
||||
public:
|
||||
explicit ResourceEntry(StringHandle name, ID id, const typename ResourceType::Descriptor &desc) noexcept;
|
||||
ResourceEntry(StringHandle name, ID id, const ResourceType &resource) noexcept;
|
||||
ResourceEntry() = delete;
|
||||
~ResourceEntry() override = default;
|
||||
ResourceEntry(const ResourceEntry &) = delete;
|
||||
ResourceEntry(ResourceEntry &&) noexcept = delete;
|
||||
ResourceEntry &operator=(const ResourceEntry &) = delete;
|
||||
ResourceEntry &operator=(ResourceEntry &&) noexcept = delete;
|
||||
|
||||
void request() noexcept override;
|
||||
void release() noexcept override;
|
||||
typename ResourceType::DeviceResource *getDeviceResource() const noexcept override;
|
||||
|
||||
inline const ResourceType &get() const noexcept { return _resource; }
|
||||
|
||||
private:
|
||||
ResourceType _resource;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename ResourceType, typename Enable>
|
||||
ResourceEntry<ResourceType, Enable>::ResourceEntry(const StringHandle name, ID const id, const typename ResourceType::Descriptor &desc) noexcept
|
||||
: VirtualResource(name, id, false),
|
||||
_resource(desc) {
|
||||
}
|
||||
|
||||
template <typename ResourceType, typename Enable>
|
||||
ResourceEntry<ResourceType, Enable>::ResourceEntry(const StringHandle name, ID const id, const ResourceType &resource) noexcept
|
||||
: VirtualResource(name, id, true),
|
||||
_resource(resource) {
|
||||
}
|
||||
|
||||
template <typename ResourceType, typename Enable>
|
||||
void ResourceEntry<ResourceType, Enable>::request() noexcept {
|
||||
_resource.createTransient();
|
||||
}
|
||||
|
||||
template <typename ResourceType, typename Enable>
|
||||
void ResourceEntry<ResourceType, Enable>::release() noexcept {
|
||||
_resource.destroyTransient();
|
||||
}
|
||||
|
||||
template <typename ResourceType, typename Enable>
|
||||
typename ResourceType::DeviceResource *ResourceEntry<ResourceType, Enable>::getDeviceResource() const noexcept {
|
||||
return _resource.get();
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
40
cocos/renderer/frame-graph/ResourceNode.h
Normal file
40
cocos/renderer/frame-graph/ResourceNode.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "VirtualResource.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
struct ResourceNode final {
|
||||
VirtualResource *virtualResource{nullptr};
|
||||
PassNode *writer{nullptr};
|
||||
uint32_t readerCount{0};
|
||||
uint8_t version{0};
|
||||
};
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
43
cocos/renderer/frame-graph/VirtualResource.cpp
Normal file
43
cocos/renderer/frame-graph/VirtualResource.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "VirtualResource.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
VirtualResource::VirtualResource(const StringHandle name, ID const id, bool const imported) noexcept
|
||||
: _name(name),
|
||||
_id(id),
|
||||
_imported(imported) {
|
||||
CC_ASSERT(_name.isValid());
|
||||
}
|
||||
|
||||
void VirtualResource::updateLifetime(PassNode *const passNode) noexcept {
|
||||
_firstUsePass = _firstUsePass ? _firstUsePass : passNode;
|
||||
_lastUsePass = passNode;
|
||||
}
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
73
cocos/renderer/frame-graph/VirtualResource.h
Normal file
73
cocos/renderer/frame-graph/VirtualResource.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-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 "Handle.h"
|
||||
#include "renderer/gfx-base/GFXObject.h"
|
||||
|
||||
namespace cc {
|
||||
namespace framegraph {
|
||||
|
||||
class PassNode;
|
||||
|
||||
using ID = uint16_t;
|
||||
|
||||
class VirtualResource {
|
||||
public:
|
||||
VirtualResource(StringHandle name, ID id, bool imported) noexcept;
|
||||
VirtualResource() noexcept = delete;
|
||||
virtual ~VirtualResource() = default;
|
||||
VirtualResource(const VirtualResource &) = delete;
|
||||
VirtualResource(VirtualResource &&) noexcept = delete;
|
||||
VirtualResource &operator=(const VirtualResource &) = delete;
|
||||
VirtualResource &operator=(VirtualResource &&) noexcept = delete;
|
||||
|
||||
virtual void request() noexcept = 0;
|
||||
virtual void release() noexcept = 0;
|
||||
bool isImported() const noexcept { return _imported; }
|
||||
void updateLifetime(PassNode *passNode) noexcept;
|
||||
void newVersion() noexcept { ++_version; }
|
||||
|
||||
virtual gfx::GFXObject *getDeviceResource() const noexcept = 0;
|
||||
|
||||
private:
|
||||
PassNode *_firstUsePass{nullptr};
|
||||
PassNode *_lastUsePass{nullptr};
|
||||
const StringHandle _name;
|
||||
uint32_t _refCount{0};
|
||||
uint16_t _writerCount{0};
|
||||
ID _id{0};
|
||||
uint8_t _version{0};
|
||||
bool const _imported{false};
|
||||
bool _neverLoaded{true};
|
||||
bool _neverStored{true};
|
||||
bool _memoryless{false};
|
||||
bool _memorylessMSAA{false};
|
||||
|
||||
friend class FrameGraph;
|
||||
};
|
||||
|
||||
} // namespace framegraph
|
||||
} // namespace cc
|
||||
Reference in New Issue
Block a user