no message

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

View File

@@ -0,0 +1,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

View 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

View 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

View 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

View 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

View 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

View 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, &region, 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 &currentPassNode = _passNodes[currentPassId];
if (currentPassNode->_refCount) {
break;
}
++currentPassId;
}
lastPassId = currentPassId;
while (++currentPassId < count) {
const auto &currentPassNode = _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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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