no message
This commit is contained in:
116
cocos/renderer/core/MaterialInstance.cpp
Normal file
116
cocos/renderer/core/MaterialInstance.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
***************************************************************************/
|
||||
|
||||
#include "renderer/core/MaterialInstance.h"
|
||||
// #include "core/components/RenderableComponent.h"
|
||||
#include "renderer/core/PassInstance.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
MaterialInstance::MaterialInstance(const IMaterialInstanceInfo &info) {
|
||||
_parent = info.parent;
|
||||
// _owner = info.owner;
|
||||
_subModelIdx = info.subModelIdx;
|
||||
copy(_parent);
|
||||
}
|
||||
|
||||
void MaterialInstance::recompileShaders(const MacroRecord &overrides, index_t passIdx /* = CC_INVALID_INDEX */) {
|
||||
auto &passes = *_passes;
|
||||
if (passes.empty() || _effectAsset == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (passIdx == CC_INVALID_INDEX) {
|
||||
for (const auto &pass : passes) {
|
||||
pass->tryCompile(overrides);
|
||||
}
|
||||
} else {
|
||||
if (passIdx < passes.size()) {
|
||||
auto *pass = passes[passIdx].get();
|
||||
pass->tryCompile(overrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialInstance::overridePipelineStates(const PassOverrides &overrides, index_t passIdx /* = CC_INVALID_INDEX */) {
|
||||
auto &passes = *_passes;
|
||||
if (passes.empty() || _effectAsset == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ccstd::vector<IPassInfoFull> &passInfos = _effectAsset->_techniques[getTechniqueIndex()].passes;
|
||||
if (passIdx == CC_INVALID_INDEX) {
|
||||
for (size_t i = 0, len = passes.size(); i < len; i++) {
|
||||
auto *pass = passes[i].get();
|
||||
if (i >= _states.size()) {
|
||||
_states.resize(i + 1);
|
||||
}
|
||||
auto &state = _states[i];
|
||||
state.overrides(IPassInfoFull(overrides));
|
||||
pass->overridePipelineStates(passInfos[pass->getPassIndex()], state);
|
||||
}
|
||||
} else {
|
||||
if (passIdx >= _states.size()) {
|
||||
_states.resize(passIdx + 1);
|
||||
}
|
||||
auto &state = _states[passIdx];
|
||||
state.overrides(IPassInfoFull(overrides));
|
||||
passes[passIdx]->overridePipelineStates(passInfos[passIdx], state);
|
||||
}
|
||||
}
|
||||
|
||||
bool MaterialInstance::destroy() {
|
||||
doDestroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
ccstd::vector<IntrusivePtr<scene::Pass>> MaterialInstance::createPasses() {
|
||||
ccstd::vector<IntrusivePtr<scene::Pass>> passes;
|
||||
auto &parentPasses = _parent->getPasses();
|
||||
|
||||
passes.reserve(parentPasses->size());
|
||||
for (auto &parentPass : *parentPasses) {
|
||||
passes.emplace_back(ccnew PassInstance(parentPass, this));
|
||||
}
|
||||
return passes;
|
||||
}
|
||||
|
||||
void MaterialInstance::onPassStateChange(bool dontNotify) {
|
||||
_hash = Material::getHashForMaterial(this);
|
||||
if (!dontNotify) {
|
||||
if (_rebuildPSOCallback != nullptr) {
|
||||
_rebuildPSOCallback(_subModelIdx, this);
|
||||
}
|
||||
|
||||
// if (_owner != nullptr) {
|
||||
// _owner->onRebuildPSO(_subModelIdx, this);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialInstance::setRebuildPSOCallback(const RebuildPSOCallback &cb) {
|
||||
_rebuildPSOCallback = cb;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
90
cocos/renderer/core/MaterialInstance.h
Normal file
90
cocos/renderer/core/MaterialInstance.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
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/TypeDef.h"
|
||||
#include "base/std/optional.h"
|
||||
|
||||
#include "core/assets/Material.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
struct IMaterialInstanceInfo {
|
||||
Material *parent{nullptr};
|
||||
// RenderableComponent *owner{nullptr};
|
||||
index_t subModelIdx{0};
|
||||
};
|
||||
|
||||
class PassInstance;
|
||||
|
||||
/**
|
||||
* @zh
|
||||
* 材质实例,当有材质修改需求时,根据材质资源创建的,可任意定制的实例。
|
||||
*/
|
||||
class MaterialInstance final : public Material {
|
||||
public:
|
||||
using Super = Material;
|
||||
|
||||
explicit MaterialInstance(const IMaterialInstanceInfo &info);
|
||||
|
||||
Material *getParent() const override {
|
||||
return _parent.get();
|
||||
}
|
||||
|
||||
// RenderableComponent *getOwner() const override {
|
||||
// return _owner;
|
||||
// }
|
||||
|
||||
void recompileShaders(const MacroRecord &overrides) override {
|
||||
MaterialInstance::recompileShaders(overrides, CC_INVALID_INDEX);
|
||||
}
|
||||
void recompileShaders(const MacroRecord &overrides, index_t passIdx) override;
|
||||
|
||||
void overridePipelineStates(const PassOverrides &overrides) override {
|
||||
MaterialInstance::overridePipelineStates(overrides, CC_INVALID_INDEX);
|
||||
}
|
||||
void overridePipelineStates(const PassOverrides &overrides, index_t passIdx) override;
|
||||
|
||||
bool destroy() override;
|
||||
|
||||
void onPassStateChange(bool dontNotify);
|
||||
|
||||
// For JS
|
||||
using RebuildPSOCallback = std::function<void(index_t index, Material *material)>;
|
||||
void setRebuildPSOCallback(const RebuildPSOCallback &cb);
|
||||
//
|
||||
|
||||
protected:
|
||||
ccstd::vector<IntrusivePtr<scene::Pass>> createPasses() override;
|
||||
|
||||
private:
|
||||
IntrusivePtr<Material> _parent;
|
||||
// RenderableComponent *_owner{nullptr};
|
||||
index_t _subModelIdx{0};
|
||||
|
||||
RebuildPSOCallback _rebuildPSOCallback{nullptr};
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
115
cocos/renderer/core/PassInstance.cpp
Normal file
115
cocos/renderer/core/PassInstance.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/****************************************************************************
|
||||
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 "cocos/renderer/core/PassInstance.h"
|
||||
#include "cocos/renderer/core/MaterialInstance.h"
|
||||
#include "cocos/renderer/core/ProgramLib.h"
|
||||
#include "cocos/renderer/pipeline/InstancedBuffer.h"
|
||||
#include "cocos/renderer/pipeline/custom/RenderingModule.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
PassInstance::PassInstance(scene::Pass *parent, MaterialInstance *owner)
|
||||
: Super(parent->getRoot()), _parent(parent), _owner(owner) {
|
||||
doInit(_parent->getPassInfoFull());
|
||||
for (const auto &b : _shaderInfo->blocks) {
|
||||
scene::IBlockRef &block = _blocks[b.binding];
|
||||
const scene::IBlockRef &parentBlock = _parent->getBlocks()[b.binding];
|
||||
CC_ASSERT(block.count == parentBlock.count);
|
||||
memcpy(block.data, parentBlock.data, parentBlock.count * 4);
|
||||
}
|
||||
|
||||
_rootBufferDirty = true;
|
||||
gfx::DescriptorSet *parentDescriptorSet = _parent->getDescriptorSet();
|
||||
|
||||
auto *programLib = render::getProgramLibrary();
|
||||
if (programLib) {
|
||||
const auto &set = _shaderInfo->descriptors.at(
|
||||
static_cast<size_t>(pipeline::SetIndex::MATERIAL));
|
||||
for (const auto &samplerTexture : set.samplerTextures) {
|
||||
for (uint32_t i = 0; i < samplerTexture.count; ++i) {
|
||||
auto *sampler = parentDescriptorSet->getSampler(samplerTexture.binding, i);
|
||||
auto *texture = parentDescriptorSet->getTexture(samplerTexture.binding, i);
|
||||
_descriptorSet->bindSampler(samplerTexture.binding, sampler, i);
|
||||
_descriptorSet->bindTexture(samplerTexture.binding, texture, i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &samplerTexture : _shaderInfo->samplerTextures) {
|
||||
for (uint32_t i = 0; i < samplerTexture.count; ++i) {
|
||||
auto *sampler = parentDescriptorSet->getSampler(samplerTexture.binding, i);
|
||||
auto *texture = parentDescriptorSet->getTexture(samplerTexture.binding, i);
|
||||
_descriptorSet->bindSampler(samplerTexture.binding, sampler, i);
|
||||
_descriptorSet->bindTexture(samplerTexture.binding, texture, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Super::tryCompile();
|
||||
}
|
||||
|
||||
PassInstance::~PassInstance() = default;
|
||||
|
||||
scene::Pass *PassInstance::getParent() const {
|
||||
return _parent.get();
|
||||
}
|
||||
|
||||
void PassInstance::overridePipelineStates(const IPassInfo &original, const PassOverrides &override) {
|
||||
_blendState.reset();
|
||||
_rs.reset();
|
||||
_depthStencilState.reset();
|
||||
|
||||
scene::Pass::fillPipelineInfo(this, original);
|
||||
scene::Pass::fillPipelineInfo(this, IPassInfoFull(override));
|
||||
onStateChange();
|
||||
}
|
||||
|
||||
bool PassInstance::tryCompile(const ccstd::optional<MacroRecord> &defineOverrides) {
|
||||
if (defineOverrides.has_value()) {
|
||||
if (!overrideMacros(_defines, defineOverrides.value())) return false;
|
||||
}
|
||||
bool ret = Super::tryCompile();
|
||||
onStateChange();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PassInstance::beginChangeStatesSilently() {
|
||||
_dontNotify = true;
|
||||
}
|
||||
|
||||
void PassInstance::endChangeStatesSilently() {
|
||||
_dontNotify = false;
|
||||
}
|
||||
|
||||
void PassInstance::syncBatchingScheme() {
|
||||
_defines["USE_INSTANCING"] = false;
|
||||
_batchingScheme = scene::BatchingSchemes::NONE;
|
||||
}
|
||||
|
||||
void PassInstance::onStateChange() {
|
||||
_hash = scene::Pass::getPassHash(this);
|
||||
_owner->onPassStateChange(_dontNotify);
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
83
cocos/renderer/core/PassInstance.h
Normal file
83
cocos/renderer/core/PassInstance.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
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/std/optional.h"
|
||||
#include "scene/Pass.h"
|
||||
|
||||
namespace cc {
|
||||
class MaterialInstance;
|
||||
/**
|
||||
* @en A pass instance defines an variant version of the [[Pass]]
|
||||
* @zh 表示 [[Pass]] 的一种特殊实例
|
||||
*/
|
||||
class PassInstance final : public scene::Pass {
|
||||
public:
|
||||
using Super = scene::Pass;
|
||||
|
||||
PassInstance(scene::Pass *parent, MaterialInstance *owner);
|
||||
~PassInstance() override;
|
||||
/**
|
||||
* @en The parent pass
|
||||
* @zh 相关联的原始 Pass
|
||||
*/
|
||||
scene::Pass *getParent() const;
|
||||
|
||||
/**
|
||||
* @en Override pipeline states with the given pass override info.
|
||||
* This won't affect the original pass
|
||||
* @zh 重载当前 Pass 的管线状态。这不会影响原始 Pass
|
||||
* @param original The original pass info
|
||||
* @param value The override pipeline state info
|
||||
*/
|
||||
void overridePipelineStates(const IPassInfo &original, const PassOverrides &override) override;
|
||||
|
||||
bool tryCompile(const ccstd::optional<MacroRecord> &defineOverrides) override;
|
||||
|
||||
/**
|
||||
* @en Prepare to change states of the pass and do not notify the material to rebuild the pipeline state object
|
||||
* @zh 开始静默修改 Pass 相关状态,不会通知材质去重新构建管线状态对象。
|
||||
*/
|
||||
void beginChangeStatesSilently() override;
|
||||
|
||||
/**
|
||||
* @en End the silent states changing process, all state changes will be notified.
|
||||
* @zh 结束静默状态修改,所有修改将会开始通知材质。
|
||||
*/
|
||||
void endChangeStatesSilently() override;
|
||||
|
||||
protected:
|
||||
void syncBatchingScheme() override;
|
||||
|
||||
void onStateChange();
|
||||
|
||||
private:
|
||||
IntrusivePtr<scene::Pass> _parent;
|
||||
// Weak reference.
|
||||
MaterialInstance *_owner{nullptr};
|
||||
bool _dontNotify{false};
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
365
cocos/renderer/core/PassUtils.cpp
Normal file
365
cocos/renderer/core/PassUtils.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/****************************************************************************
|
||||
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 "renderer/core/PassUtils.h"
|
||||
#include <cstdint>
|
||||
#include "base/Log.h"
|
||||
#include "core/Types.h"
|
||||
#include "core/assets/TextureBase.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
const ccstd::unordered_map<gfx::Type, GFXTypeReaderCallback> type2reader = {
|
||||
{gfx::Type::UNKNOWN, [](const float * /*a*/, MaterialProperty & /*v*/, index_t /*idx*/) {
|
||||
CC_LOG_ERROR("type2reader unknown type");
|
||||
}},
|
||||
{gfx::Type::INT, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = static_cast<int32_t>(a[idx]);
|
||||
}},
|
||||
{gfx::Type::INT2, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec2(a[idx], a[idx + 1]);
|
||||
}},
|
||||
{gfx::Type::INT3, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec3(a[idx], a[idx + 1], a[idx + 2]);
|
||||
}},
|
||||
{gfx::Type::INT4, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec4(a[idx], a[idx + 1], a[idx + 2], a[idx + 3]);
|
||||
}},
|
||||
{gfx::Type::FLOAT, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = a[idx];
|
||||
}},
|
||||
{gfx::Type::FLOAT2, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec2(a[idx], a[idx + 1]);
|
||||
}},
|
||||
{gfx::Type::FLOAT3, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec3(a[idx], a[idx + 1], a[idx + 2]);
|
||||
}},
|
||||
{gfx::Type::FLOAT4, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
v = Vec4(a[idx], a[idx + 1], a[idx + 2], a[idx + 3]);
|
||||
}},
|
||||
{gfx::Type::MAT3, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
Mat3 mat3;
|
||||
memcpy(&mat3.m[0], &a[idx], sizeof(Mat3));
|
||||
v = mat3;
|
||||
}},
|
||||
{gfx::Type::MAT4, [](const float *a, MaterialProperty &v, index_t idx) {
|
||||
Mat4 mat4;
|
||||
memcpy(&mat4.m[0], &a[idx], sizeof(Mat4));
|
||||
v = mat4;
|
||||
}},
|
||||
};
|
||||
|
||||
const ccstd::unordered_map<gfx::Type, GFXTypeWriterCallback> type2writer = {
|
||||
{gfx::Type::UNKNOWN, [](float * /*a*/, const MaterialProperty & /*v*/, index_t /*idx*/) {
|
||||
CC_LOG_ERROR("type2writer unknown type");
|
||||
}},
|
||||
{gfx::Type::INT, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const int32_t *p = ccstd::get_if<int32_t>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
a[idx] = static_cast<float>(*p);
|
||||
}},
|
||||
{gfx::Type::INT2, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Vec2>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
a[idx] = p->x;
|
||||
a[idx + 1] = p->y;
|
||||
}},
|
||||
{gfx::Type::INT3, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Vec3>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
a[idx] = p->x;
|
||||
a[idx + 1] = p->y;
|
||||
a[idx + 2] = p->z;
|
||||
}},
|
||||
{gfx::Type::INT4, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Vec4>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
a[idx] = p->x;
|
||||
a[idx + 1] = p->y;
|
||||
a[idx + 2] = p->z;
|
||||
a[idx + 3] = p->w;
|
||||
}},
|
||||
{gfx::Type::FLOAT, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const float *p = ccstd::get_if<float>(&v);
|
||||
const int32_t *pInt = nullptr;
|
||||
if (p != nullptr) {
|
||||
a[idx] = *p;
|
||||
} else {
|
||||
pInt = ccstd::get_if<int32_t>(&v);
|
||||
if (pInt != nullptr) {
|
||||
a[idx] = static_cast<float>(*pInt);
|
||||
}
|
||||
}
|
||||
CC_ASSERT(p != nullptr || pInt != nullptr);
|
||||
}},
|
||||
{gfx::Type::FLOAT2, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Vec2>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
a[idx] = p->x;
|
||||
a[idx + 1] = p->y;
|
||||
}},
|
||||
{gfx::Type::FLOAT3, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
if (ccstd::holds_alternative<Vec3>(v)) {
|
||||
const auto &vec3 = ccstd::get<Vec3>(v);
|
||||
a[idx] = vec3.x;
|
||||
a[idx + 1] = vec3.y;
|
||||
a[idx + 2] = vec3.z;
|
||||
} else {
|
||||
CC_ABORT();
|
||||
}
|
||||
}},
|
||||
{gfx::Type::FLOAT4, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
if (ccstd::holds_alternative<Vec4>(v)) {
|
||||
const auto &vec4 = ccstd::get<Vec4>(v);
|
||||
a[idx] = vec4.x;
|
||||
a[idx + 1] = vec4.y;
|
||||
a[idx + 2] = vec4.z;
|
||||
a[idx + 3] = vec4.w;
|
||||
} else if (ccstd::holds_alternative<Color>(v)) {
|
||||
const auto &color = ccstd::get<Color>(v);
|
||||
Vec4 colorFloat{color.toVec4()};
|
||||
a[idx] = colorFloat.x;
|
||||
a[idx + 1] = colorFloat.y;
|
||||
a[idx + 2] = colorFloat.z;
|
||||
a[idx + 3] = colorFloat.w;
|
||||
} else if (ccstd::holds_alternative<Quaternion>(v)) {
|
||||
const auto &quat = ccstd::get<Quaternion>(v);
|
||||
a[idx] = quat.x;
|
||||
a[idx + 1] = quat.y;
|
||||
a[idx + 2] = quat.z;
|
||||
a[idx + 3] = quat.w;
|
||||
} else {
|
||||
CC_ABORT();
|
||||
}
|
||||
}},
|
||||
{gfx::Type::MAT3, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Mat3>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
memcpy(&a[idx], &p->m[0], sizeof(Mat3));
|
||||
}},
|
||||
{gfx::Type::MAT4, [](float *a, const MaterialProperty &v, index_t idx) {
|
||||
const auto *p = ccstd::get_if<Mat4>(&v);
|
||||
CC_ASSERT_NOT_NULL(p);
|
||||
memcpy(&a[idx], &p->m[0], sizeof(Mat4));
|
||||
}},
|
||||
};
|
||||
|
||||
const ccstd::unordered_map<gfx::Type, GFXTypeValidatorCallback> type2validator = {
|
||||
{gfx::Type::UNKNOWN, [](const MaterialProperty & /*v*/) -> bool {
|
||||
CC_LOG_ERROR("type2validator unknown type");
|
||||
return false;
|
||||
}},
|
||||
{gfx::Type::INT, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<int32_t>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::INT2, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec2>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::INT3, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec3>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::INT4, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec4>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::FLOAT, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<float>(&v);
|
||||
const auto *pInt = ccstd::get_if<int32_t>(&v);
|
||||
return p != nullptr || pInt != nullptr;
|
||||
}},
|
||||
{gfx::Type::FLOAT2, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec2>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::FLOAT3, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec3>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::FLOAT4, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Vec4>(&v);
|
||||
const auto *pColor = ccstd::get_if<Color>(&v);
|
||||
const auto *pQuat = ccstd::get_if<Quaternion>(&v);
|
||||
return p != nullptr || pColor != nullptr || pQuat != nullptr;
|
||||
}},
|
||||
{gfx::Type::MAT3, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Mat3>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
{gfx::Type::MAT4, [](const MaterialProperty &v) -> bool {
|
||||
const auto *p = ccstd::get_if<Mat4>(&v);
|
||||
return p != nullptr;
|
||||
}},
|
||||
};
|
||||
|
||||
const ccstd::vector<float> &getDefaultFloatArrayFromType(gfx::Type type) {
|
||||
static const ccstd::vector<float> DEFAULT_FLOAT_VALUES[] = {
|
||||
{0},
|
||||
{0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
|
||||
|
||||
switch (type) {
|
||||
case gfx::Type::BOOL:
|
||||
case gfx::Type::INT:
|
||||
case gfx::Type::UINT:
|
||||
case gfx::Type::FLOAT:
|
||||
return DEFAULT_FLOAT_VALUES[0];
|
||||
case gfx::Type::BOOL2:
|
||||
case gfx::Type::INT2:
|
||||
case gfx::Type::UINT2:
|
||||
case gfx::Type::FLOAT2:
|
||||
return DEFAULT_FLOAT_VALUES[1];
|
||||
case gfx::Type::BOOL4:
|
||||
case gfx::Type::INT4:
|
||||
case gfx::Type::UINT4:
|
||||
case gfx::Type::FLOAT4:
|
||||
return DEFAULT_FLOAT_VALUES[2];
|
||||
case gfx::Type::MAT4:
|
||||
return DEFAULT_FLOAT_VALUES[3];
|
||||
default:
|
||||
return DEFAULT_FLOAT_VALUES[0];
|
||||
}
|
||||
}
|
||||
|
||||
const ccstd::string &getDefaultStringFromType(gfx::Type type) {
|
||||
static const ccstd::string DEFAULT_TEXTURE_STR{"default-texture"};
|
||||
static const ccstd::string DEFAULT_CUBE_TEXTURE_STR{"default-cube-texture"};
|
||||
|
||||
switch (type) {
|
||||
case gfx::Type::SAMPLER2D:
|
||||
return DEFAULT_TEXTURE_STR;
|
||||
case gfx::Type::SAMPLER_CUBE:
|
||||
return DEFAULT_CUBE_TEXTURE_STR;
|
||||
default:
|
||||
return DEFAULT_TEXTURE_STR;
|
||||
}
|
||||
}
|
||||
|
||||
const ccstd::string &getStringFromType(gfx::Type type) {
|
||||
static const ccstd::string TEXTURE_2D_STR{"-texture"};
|
||||
static const ccstd::string TEXTURE_CUBE_STR{"-cube-texture"};
|
||||
static const ccstd::string TEXTURE_2D_ARRAY_STR{"-array-texture"};
|
||||
static const ccstd::string TEXTURE_3D_STR{"-3d-texture"};
|
||||
static const ccstd::string UNKNOWN_STR{"-unknown"};
|
||||
|
||||
switch (type) {
|
||||
case gfx::Type::SAMPLER2D:
|
||||
return TEXTURE_2D_STR;
|
||||
case gfx::Type::SAMPLER_CUBE:
|
||||
return TEXTURE_CUBE_STR;
|
||||
case gfx::Type::SAMPLER2D_ARRAY:
|
||||
return TEXTURE_2D_ARRAY_STR;
|
||||
case gfx::Type::SAMPLER3D:
|
||||
return TEXTURE_3D_STR;
|
||||
default:
|
||||
return UNKNOWN_STR;
|
||||
}
|
||||
}
|
||||
|
||||
bool overrideMacros(MacroRecord &target, const MacroRecord &source) {
|
||||
bool isDifferent = false;
|
||||
for (const auto &p : source) {
|
||||
if (target[p.first] != p.second) {
|
||||
target[p.first] = p.second;
|
||||
isDifferent = true;
|
||||
}
|
||||
}
|
||||
return isDifferent;
|
||||
}
|
||||
|
||||
MaterialProperty toMaterialProperty(gfx::Type type, const ccstd::vector<float> &vec) {
|
||||
MaterialProperty ret;
|
||||
size_t size = vec.size();
|
||||
|
||||
switch (type) {
|
||||
case gfx::Type::FLOAT:
|
||||
CC_ASSERT_GE(size, 1);
|
||||
ret = vec[0];
|
||||
break;
|
||||
case gfx::Type::FLOAT2:
|
||||
CC_ASSERT_GE(size, 2);
|
||||
ret = Vec2(vec[0], vec[1]);
|
||||
break;
|
||||
case gfx::Type::FLOAT3:
|
||||
CC_ASSERT_GE(size, 3);
|
||||
ret = Vec3(vec[0], vec[1], vec[2]);
|
||||
break;
|
||||
case gfx::Type::FLOAT4:
|
||||
CC_ASSERT_GE(size, 4);
|
||||
ret = Vec4(vec[0], vec[1], vec[2], vec[3]);
|
||||
break;
|
||||
case gfx::Type::MAT3:
|
||||
CC_ASSERT_GE(size, 9);
|
||||
ret = Mat3(vec.data());
|
||||
break;
|
||||
case gfx::Type::MAT4:
|
||||
CC_ASSERT_GE(size, 16);
|
||||
ret = Mat4(vec.data());
|
||||
break;
|
||||
case gfx::Type::INT:
|
||||
case gfx::Type::INT2:
|
||||
case gfx::Type::INT3:
|
||||
case gfx::Type::INT4:
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool macroRecordAsBool(const MacroRecord::mapped_type &v) {
|
||||
if (ccstd::holds_alternative<bool>(v)) {
|
||||
return ccstd::get<bool>(v);
|
||||
}
|
||||
|
||||
if (ccstd::holds_alternative<int32_t>(v)) {
|
||||
return ccstd::get<int32_t>(v) != 0;
|
||||
}
|
||||
|
||||
if (ccstd::holds_alternative<ccstd::string>(v)) {
|
||||
return ccstd::get<ccstd::string>(v) == "true";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ccstd::string macroRecordAsString(const MacroRecord::mapped_type &v) {
|
||||
if (ccstd::holds_alternative<ccstd::string>(v)) {
|
||||
return ccstd::get<ccstd::string>(v);
|
||||
}
|
||||
|
||||
if (ccstd::holds_alternative<bool>(v)) {
|
||||
return ccstd::get<bool>(v) ? "1" : "0";
|
||||
}
|
||||
|
||||
if (ccstd::holds_alternative<int32_t>(v)) {
|
||||
return std::to_string(ccstd::get<int32_t>(v));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}; // namespace cc
|
||||
118
cocos/renderer/core/PassUtils.h
Normal file
118
cocos/renderer/core/PassUtils.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/****************************************************************************
|
||||
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/Ptr.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "base/std/container/vector.h"
|
||||
#include "base/std/variant.h"
|
||||
|
||||
#include "math/Color.h"
|
||||
#include "math/Mat3.h"
|
||||
#include "math/Mat4.h"
|
||||
#include "math/Quaternion.h"
|
||||
#include "math/Vec2.h"
|
||||
#include "math/Vec3.h"
|
||||
#include "math/Vec4.h"
|
||||
|
||||
#include "renderer/gfx-base/GFXDef.h"
|
||||
#include "renderer/gfx-base/GFXTexture.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class TextureBase;
|
||||
|
||||
constexpr uint32_t TYPE_MASK = 0xfc000000; // 6 bits => 64 types
|
||||
constexpr uint32_t BINDING_MASK = 0x03f00000; // 6 bits => 64 bindings
|
||||
constexpr uint32_t COUNT_MASK = 0x000ff000; // 8 bits => 256 vectors
|
||||
constexpr uint32_t OFFSET_MASK = 0x00000fff; // 12 bits => 1024 vectors
|
||||
|
||||
constexpr uint32_t genHandle(uint32_t binding, gfx::Type type, uint32_t count, uint32_t offset = 0) {
|
||||
return ((static_cast<uint32_t>(type) << 26) & TYPE_MASK) |
|
||||
((binding << 20) & BINDING_MASK) |
|
||||
((count << 12) & COUNT_MASK) |
|
||||
(offset & OFFSET_MASK);
|
||||
}
|
||||
|
||||
constexpr gfx::Type getTypeFromHandle(uint32_t handle) { return static_cast<gfx::Type>((handle & TYPE_MASK) >> 26); }
|
||||
constexpr uint32_t getBindingFromHandle(uint32_t handle) { return (handle & BINDING_MASK) >> 20; }
|
||||
constexpr uint32_t getCountFromHandle(uint32_t handle) { return (handle & COUNT_MASK) >> 12; }
|
||||
constexpr uint32_t getOffsetFromHandle(uint32_t handle) { return (handle & OFFSET_MASK); }
|
||||
constexpr uint32_t customizeType(uint32_t handle, gfx::Type type) {
|
||||
return (handle & ~TYPE_MASK) | ((static_cast<uint32_t>(type) << 26) & TYPE_MASK);
|
||||
}
|
||||
|
||||
using MacroValue = ccstd::variant<ccstd::monostate, int32_t, bool, ccstd::string>;
|
||||
|
||||
/**
|
||||
* @en Combination of preprocess macros
|
||||
* @zh 预处理宏组合
|
||||
*/
|
||||
using MacroRecord = ccstd::unordered_map<ccstd::string, MacroValue>;
|
||||
|
||||
using MaterialProperty = ccstd::variant<ccstd::monostate /*0*/, float /*1*/, int32_t /*2*/, Vec2 /*3*/, Vec3 /*4*/, Vec4 /*5*/, Color, /*6*/ Mat3 /*7*/, Mat4 /*8*/, Quaternion /*9*/, IntrusivePtr<TextureBase> /*10*/, IntrusivePtr<gfx::Texture> /*11*/>;
|
||||
|
||||
using MaterialPropertyList = ccstd::vector<MaterialProperty>;
|
||||
|
||||
using MaterialPropertyVariant = ccstd::variant<ccstd::monostate /*0*/, MaterialProperty /*1*/, MaterialPropertyList /*2*/>;
|
||||
|
||||
#define MATERIAL_PROPERTY_INDEX_SINGLE 1
|
||||
#define MATERIAL_PROPERTY_INDEX_LIST 2
|
||||
|
||||
using GFXTypeReaderCallback = void (*)(const float *, MaterialProperty &, index_t);
|
||||
using GFXTypeWriterCallback = void (*)(float *, const MaterialProperty &, index_t);
|
||||
using GFXTypeValidatorCallback = bool (*)(const MaterialProperty &);
|
||||
|
||||
extern const ccstd::unordered_map<gfx::Type, GFXTypeReaderCallback> type2reader; // NOLINT(readability-identifier-naming)
|
||||
extern const ccstd::unordered_map<gfx::Type, GFXTypeWriterCallback> type2writer; // NOLINT(readability-identifier-naming)
|
||||
extern const ccstd::unordered_map<gfx::Type, GFXTypeValidatorCallback> type2validator; // NOLINT(readability-identifier-naming)
|
||||
|
||||
/**
|
||||
* @en Gets the default values for the given type of uniform
|
||||
* @zh 根据指定的 Uniform 类型来获取默认值
|
||||
* @param type The type of the uniform
|
||||
*/
|
||||
const ccstd::vector<float> &getDefaultFloatArrayFromType(gfx::Type type);
|
||||
const ccstd::string &getDefaultStringFromType(gfx::Type type);
|
||||
const ccstd::string &getStringFromType(gfx::Type type);
|
||||
|
||||
/**
|
||||
* @en Combination of preprocess macros
|
||||
* @zh 预处理宏组合
|
||||
*/
|
||||
/**
|
||||
* @en Override the preprocess macros
|
||||
* @zh 覆写预处理宏
|
||||
* @param target Target preprocess macros to be overridden
|
||||
* @param source Preprocess macros used for override
|
||||
*/
|
||||
bool overrideMacros(MacroRecord &target, const MacroRecord &source);
|
||||
|
||||
MaterialProperty toMaterialProperty(gfx::Type type, const ccstd::vector<float> &vec);
|
||||
|
||||
bool macroRecordAsBool(const MacroRecord::mapped_type &v);
|
||||
ccstd::string macroRecordAsString(const MacroRecord::mapped_type &v);
|
||||
|
||||
} // namespace cc
|
||||
498
cocos/renderer/core/ProgramLib.cpp
Normal file
498
cocos/renderer/core/ProgramLib.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/****************************************************************************
|
||||
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 "renderer/core/ProgramLib.h"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include "ProgramUtils.h"
|
||||
#include "base/Log.h"
|
||||
#include "core/assets/EffectAsset.h"
|
||||
#include "renderer/gfx-base/GFXDevice.h"
|
||||
#include "renderer/pipeline/custom/RenderInterfaceTypes.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
namespace {
|
||||
|
||||
void insertBuiltinBindings(const IProgramInfo &tmpl, ITemplateInfo &tmplInfo, const pipeline::DescriptorSetLayoutInfos &source,
|
||||
const ccstd::string &type, ccstd::vector<gfx::DescriptorSetLayoutBinding> *outBindings) {
|
||||
CC_ASSERT(type == "locals" || type == "globals");
|
||||
const auto &target = type == "globals" ? tmpl.builtins.globals : tmpl.builtins.locals;
|
||||
|
||||
// Blocks
|
||||
ccstd::vector<gfx::UniformBlock> tempBlocks{};
|
||||
for (const auto &b : target.blocks) {
|
||||
auto infoIt = source.blocks.find(b.name);
|
||||
if (infoIt == source.blocks.end()) {
|
||||
CC_LOG_WARNING("builtin UBO '%s' not available !", b.name.c_str());
|
||||
continue;
|
||||
}
|
||||
const auto &info = infoIt->second;
|
||||
const auto bindingsIter = std::find_if(source.bindings.begin(), source.bindings.end(), [&info](const auto &bd) -> bool { return bd.binding == info.binding; });
|
||||
if (bindingsIter == source.bindings.end()) {
|
||||
CC_LOG_WARNING("builtin UBO '%s' not available !", b.name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
tempBlocks.emplace_back(info);
|
||||
|
||||
if (outBindings != nullptr && std::count_if(outBindings->begin(), outBindings->end(), [&bindingsIter](const auto &b) { return b.binding == bindingsIter->binding; }) == 0) {
|
||||
outBindings->emplace_back(*bindingsIter);
|
||||
}
|
||||
}
|
||||
tmplInfo.shaderInfo.blocks.insert(tmplInfo.shaderInfo.blocks.begin(), tempBlocks.begin(), tempBlocks.end());
|
||||
|
||||
// SamplerTextures
|
||||
ccstd::vector<gfx::UniformSamplerTexture> tempSamplerTextures;
|
||||
for (const auto &s : target.samplerTextures) {
|
||||
auto infoIt = source.samplers.find(s.name);
|
||||
if (infoIt == source.samplers.end()) {
|
||||
CC_LOG_WARNING("builtin samplerTexture '%s' not available !", s.name.c_str());
|
||||
continue;
|
||||
}
|
||||
const auto &info = infoIt->second;
|
||||
const auto binding = std::find_if(source.bindings.begin(), source.bindings.end(), [&info](const auto &bd) {
|
||||
return bd.binding == info.binding;
|
||||
});
|
||||
if (binding == source.bindings.end() || !(binding->descriptorType & gfx::DESCRIPTOR_SAMPLER_TYPE)) {
|
||||
CC_LOG_WARNING("builtin samplerTexture '%s' not available !", s.name.c_str());
|
||||
continue;
|
||||
}
|
||||
tempSamplerTextures.emplace_back(info);
|
||||
if (outBindings != nullptr && std::count_if(outBindings->begin(), outBindings->end(), [&binding](const auto &b) { return b.binding == binding->binding; }) == 0) {
|
||||
outBindings->emplace_back(*binding);
|
||||
}
|
||||
}
|
||||
|
||||
tmplInfo.shaderInfo.samplerTextures.insert(tmplInfo.shaderInfo.samplerTextures.begin(), tempSamplerTextures.begin(), tempSamplerTextures.end());
|
||||
if (outBindings != nullptr) {
|
||||
std::stable_sort(outBindings->begin(), outBindings->end(), [](const auto &a, const auto &b) {
|
||||
return a.binding < b.binding;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int32_t getSize(const IBlockInfo &block) {
|
||||
auto s = 0;
|
||||
for (const auto &m : block.members) {
|
||||
s += static_cast<int>(getTypeSize(m.type) * m.count);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const char *getDeviceShaderVersion(const gfx::Device *device) {
|
||||
switch (device->getGfxAPI()) {
|
||||
case gfx::API::GLES2:
|
||||
case gfx::API::WEBGL:
|
||||
return "glsl1";
|
||||
case gfx::API::GLES3:
|
||||
case gfx::API::WEBGL2:
|
||||
return "glsl3";
|
||||
default:
|
||||
return "glsl4";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
static void copyDefines(const ccstd::vector<IDefineInfo> &from, ccstd::vector<IDefineRecord> &to) {
|
||||
to.resize(from.size());
|
||||
for (size_t i = 0, len = from.size(); i < len; ++i) {
|
||||
to[i].name = from[i].name;
|
||||
to[i].type = from[i].type;
|
||||
to[i].range = from[i].range;
|
||||
to[i].options = from[i].options;
|
||||
to[i].defaultVal = from[i].defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
// IProgramInfo
|
||||
void IProgramInfo::copyFrom(const IShaderInfo &o) {
|
||||
name = o.name;
|
||||
hash = o.hash;
|
||||
glsl4 = o.glsl4;
|
||||
glsl3 = o.glsl3;
|
||||
glsl1 = o.glsl1;
|
||||
builtins = o.builtins;
|
||||
copyDefines(o.defines, defines);
|
||||
blocks = o.blocks;
|
||||
samplerTextures = o.samplerTextures;
|
||||
attributes = o.attributes;
|
||||
samplers = o.samplers;
|
||||
textures = o.textures;
|
||||
buffers = o.buffers;
|
||||
images = o.images;
|
||||
subpassInputs = o.subpassInputs;
|
||||
descriptors = o.descriptors;
|
||||
}
|
||||
|
||||
ProgramLib::ProgramLib() {
|
||||
ProgramLib::instance = this;
|
||||
}
|
||||
|
||||
ProgramLib::~ProgramLib() {
|
||||
ProgramLib::instance = nullptr;
|
||||
#if CC_DEBUG
|
||||
for (const auto &cache : _cache) {
|
||||
if (cache.second->getRefCount() > 1) {
|
||||
CC_LOG_WARNING("ProgramLib cache: %s ref_count is %d and may leak", cache.second->getName().c_str(), cache.second->getRefCount());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
/*static*/
|
||||
|
||||
ProgramLib *ProgramLib::instance = nullptr;
|
||||
|
||||
ProgramLib *ProgramLib::getInstance() {
|
||||
return ProgramLib::instance;
|
||||
}
|
||||
|
||||
void ProgramLib::registerEffect(EffectAsset *effect) {
|
||||
for (auto &shader : effect->_shaders) {
|
||||
auto *tmpl = define(shader);
|
||||
tmpl->effectName = effect->getName();
|
||||
}
|
||||
render::addEffectDefaultProperties(*effect);
|
||||
}
|
||||
|
||||
IProgramInfo *ProgramLib::define(IShaderInfo &shader) {
|
||||
auto itCurrTmpl = _templates.find(shader.name);
|
||||
if (itCurrTmpl != _templates.end() && itCurrTmpl->second.hash == shader.hash) {
|
||||
return &itCurrTmpl->second;
|
||||
}
|
||||
|
||||
IProgramInfo &tmpl = _templates[shader.name];
|
||||
tmpl.copyFrom(shader);
|
||||
|
||||
render::populateMacros(tmpl);
|
||||
|
||||
if (_templateInfos.count(tmpl.hash) == 0) {
|
||||
ITemplateInfo tmplInfo{};
|
||||
// cache material-specific descriptor set layout
|
||||
tmplInfo.samplerStartBinding = static_cast<int32_t>(tmpl.blocks.size());
|
||||
tmplInfo.bindings = {};
|
||||
tmplInfo.blockSizes = {};
|
||||
for (const auto &block : tmpl.blocks) {
|
||||
tmplInfo.blockSizes.emplace_back(getSize(block));
|
||||
tmplInfo.bindings.emplace_back();
|
||||
auto &bindingsInfo = tmplInfo.bindings.back();
|
||||
bindingsInfo.binding = block.binding;
|
||||
bindingsInfo.descriptorType = gfx::DescriptorType::UNIFORM_BUFFER;
|
||||
bindingsInfo.count = 1;
|
||||
bindingsInfo.stageFlags = block.stageFlags;
|
||||
ccstd::vector<gfx::Uniform> uniforms;
|
||||
{
|
||||
// construct uniforms
|
||||
uniforms.reserve(block.members.size());
|
||||
for (const auto &member : block.members) {
|
||||
uniforms.emplace_back();
|
||||
auto &info = uniforms.back();
|
||||
info.name = member.name;
|
||||
info.type = member.type;
|
||||
info.count = member.count;
|
||||
}
|
||||
}
|
||||
tmplInfo.shaderInfo.blocks.emplace_back();
|
||||
auto &blocksInfo = tmplInfo.shaderInfo.blocks.back();
|
||||
blocksInfo.set = static_cast<uint32_t>(pipeline::SetIndex::MATERIAL);
|
||||
blocksInfo.binding = block.binding;
|
||||
blocksInfo.name = block.name;
|
||||
blocksInfo.members = uniforms;
|
||||
blocksInfo.count = 1; // effect compiler guarantees block count = 1
|
||||
}
|
||||
for (const auto &samplerTexture : tmpl.samplerTextures) {
|
||||
tmplInfo.bindings.emplace_back();
|
||||
auto &descriptorLayoutBindingInfo = tmplInfo.bindings.back();
|
||||
descriptorLayoutBindingInfo.binding = samplerTexture.binding;
|
||||
descriptorLayoutBindingInfo.descriptorType = gfx::DescriptorType::SAMPLER_TEXTURE;
|
||||
descriptorLayoutBindingInfo.count = samplerTexture.count;
|
||||
descriptorLayoutBindingInfo.stageFlags = samplerTexture.stageFlags;
|
||||
|
||||
tmplInfo.shaderInfo.samplerTextures.emplace_back();
|
||||
auto &samplerTextureInfo = tmplInfo.shaderInfo.samplerTextures.back();
|
||||
samplerTextureInfo.set = static_cast<uint32_t>(pipeline::SetIndex::MATERIAL);
|
||||
samplerTextureInfo.binding = samplerTexture.binding;
|
||||
samplerTextureInfo.name = samplerTexture.name;
|
||||
samplerTextureInfo.type = samplerTexture.type;
|
||||
samplerTextureInfo.count = samplerTexture.count;
|
||||
}
|
||||
|
||||
for (const auto &sampler : tmpl.samplers) {
|
||||
tmplInfo.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{
|
||||
sampler.binding,
|
||||
gfx::DescriptorType::SAMPLER,
|
||||
sampler.count,
|
||||
sampler.stageFlags});
|
||||
|
||||
tmplInfo.shaderInfo.samplers.emplace_back(gfx::UniformSampler{
|
||||
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
||||
sampler.binding,
|
||||
sampler.name,
|
||||
sampler.count,
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto &texture : tmpl.textures) {
|
||||
tmplInfo.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{
|
||||
texture.binding,
|
||||
gfx::DescriptorType::TEXTURE,
|
||||
texture.count,
|
||||
texture.stageFlags});
|
||||
|
||||
tmplInfo.shaderInfo.textures.emplace_back(gfx::UniformTexture{
|
||||
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
||||
texture.binding,
|
||||
texture.name,
|
||||
texture.type,
|
||||
texture.count,
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto &buffer : tmpl.buffers) {
|
||||
tmplInfo.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{
|
||||
buffer.binding,
|
||||
gfx::DescriptorType::STORAGE_BUFFER,
|
||||
1,
|
||||
buffer.stageFlags});
|
||||
|
||||
tmplInfo.shaderInfo.buffers.emplace_back(gfx::UniformStorageBuffer{
|
||||
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
||||
buffer.binding,
|
||||
buffer.name,
|
||||
1,
|
||||
buffer.memoryAccess}); // effect compiler guarantees buffer count = 1
|
||||
}
|
||||
|
||||
for (const auto &image : tmpl.images) {
|
||||
tmplInfo.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{
|
||||
image.binding,
|
||||
gfx::DescriptorType::STORAGE_IMAGE,
|
||||
image.count,
|
||||
image.stageFlags});
|
||||
|
||||
tmplInfo.shaderInfo.images.emplace_back(gfx::UniformStorageImage{
|
||||
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
||||
image.binding,
|
||||
image.name,
|
||||
image.type,
|
||||
image.count,
|
||||
image.memoryAccess});
|
||||
}
|
||||
|
||||
for (const auto &subpassInput : tmpl.subpassInputs) {
|
||||
tmplInfo.bindings.emplace_back(gfx::DescriptorSetLayoutBinding{
|
||||
subpassInput.binding,
|
||||
gfx::DescriptorType::INPUT_ATTACHMENT,
|
||||
subpassInput.count,
|
||||
subpassInput.stageFlags});
|
||||
|
||||
tmplInfo.shaderInfo.subpassInputs.emplace_back(gfx::UniformInputAttachment{
|
||||
static_cast<uint32_t>(pipeline::SetIndex::MATERIAL),
|
||||
subpassInput.binding,
|
||||
subpassInput.name,
|
||||
subpassInput.count});
|
||||
}
|
||||
|
||||
tmplInfo.gfxAttributes = {};
|
||||
for (auto &attr : tmpl.attributes) {
|
||||
tmplInfo.gfxAttributes.emplace_back();
|
||||
auto &info = tmplInfo.gfxAttributes.back();
|
||||
info.name = attr.name;
|
||||
info.format = attr.format;
|
||||
info.isNormalized = attr.isNormalized;
|
||||
info.stream = 0;
|
||||
info.isInstanced = attr.isInstanced;
|
||||
info.location = attr.location;
|
||||
}
|
||||
insertBuiltinBindings(tmpl, tmplInfo, pipeline::localDescriptorSetLayout, "locals", nullptr);
|
||||
|
||||
tmplInfo.shaderInfo.stages.emplace_back();
|
||||
auto &vertexShaderInfo = tmplInfo.shaderInfo.stages.back();
|
||||
vertexShaderInfo.stage = gfx::ShaderStageFlagBit::VERTEX;
|
||||
vertexShaderInfo.source = "";
|
||||
tmplInfo.shaderInfo.stages.emplace_back();
|
||||
auto &fragmentShaderInfo = tmplInfo.shaderInfo.stages.back();
|
||||
fragmentShaderInfo.stage = gfx::ShaderStageFlagBit::FRAGMENT;
|
||||
fragmentShaderInfo.source = "";
|
||||
tmplInfo.handleMap = render::genHandles(tmpl);
|
||||
tmplInfo.setLayouts = {};
|
||||
|
||||
_templateInfos[tmpl.hash] = tmplInfo;
|
||||
}
|
||||
return &tmpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @en Gets the shader template with its name
|
||||
* @zh 通过名字获取 Shader 模板
|
||||
* @param name Target shader name
|
||||
*/
|
||||
|
||||
const IProgramInfo *ProgramLib::getTemplate(const ccstd::string &name) const {
|
||||
auto it = _templates.find(name);
|
||||
return it != _templates.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @en Gets the shader template info with its name
|
||||
* @zh 通过名字获取 Shader 模版信息
|
||||
* @param name Target shader name
|
||||
*/
|
||||
|
||||
ITemplateInfo *ProgramLib::getTemplateInfo(const ccstd::string &name) {
|
||||
auto it = _templates.find(name);
|
||||
CC_ASSERT(it != _templates.end());
|
||||
auto hash = it->second.hash;
|
||||
auto itInfo = _templateInfos.find(hash);
|
||||
return itInfo != _templateInfos.end() ? &itInfo->second : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @en Gets the pipeline layout of the shader template given its name
|
||||
* @zh 通过名字获取 Shader 模板相关联的管线布局
|
||||
* @param name Target shader name
|
||||
*/
|
||||
gfx::DescriptorSetLayout *ProgramLib::getDescriptorSetLayout(gfx::Device *device, const ccstd::string &name, bool isLocal) {
|
||||
auto itTmpl = _templates.find(name);
|
||||
CC_ASSERT(itTmpl != _templates.end());
|
||||
const auto &tmpl = itTmpl->second;
|
||||
auto itTplInfo = _templateInfos.find(tmpl.hash);
|
||||
if (itTplInfo == _templateInfos.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto &tmplInfo = itTplInfo->second;
|
||||
if (tmplInfo.setLayouts.empty()) {
|
||||
gfx::DescriptorSetLayoutInfo info;
|
||||
tmplInfo.setLayouts.resize(static_cast<size_t>(pipeline::SetIndex::COUNT));
|
||||
info.bindings = tmplInfo.bindings;
|
||||
tmplInfo.setLayouts.replace(static_cast<index_t>(pipeline::SetIndex::MATERIAL), device->createDescriptorSetLayout(info));
|
||||
info.bindings = pipeline::localDescriptorSetLayout.bindings;
|
||||
tmplInfo.setLayouts.replace(static_cast<index_t>(pipeline::SetIndex::LOCAL), device->createDescriptorSetLayout(info));
|
||||
}
|
||||
return tmplInfo.setLayouts.at(isLocal ? static_cast<uint32_t>(pipeline::SetIndex::LOCAL) : static_cast<uint32_t>(pipeline::SetIndex::MATERIAL));
|
||||
}
|
||||
|
||||
ccstd::string ProgramLib::getKey(const ccstd::string &name, const MacroRecord &defines) {
|
||||
auto itTpl = _templates.find(name);
|
||||
CC_ASSERT(itTpl != _templates.end());
|
||||
auto &tmpl = itTpl->second;
|
||||
return render::getVariantKey(tmpl, defines);
|
||||
}
|
||||
|
||||
void ProgramLib::destroyShaderByDefines(const MacroRecord &defines) {
|
||||
if (defines.empty()) return;
|
||||
ccstd::vector<ccstd::string> defineValues;
|
||||
for (const auto &i : defines) {
|
||||
defineValues.emplace_back(i.first + macroRecordAsString(i.second));
|
||||
}
|
||||
ccstd::vector<ccstd::string> matchedKeys;
|
||||
for (const auto &i : _cache) {
|
||||
bool matched = true;
|
||||
for (const auto &v : defineValues) {
|
||||
if (i.first.find(v) == ccstd::string::npos) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
matchedKeys.emplace_back(i.first);
|
||||
}
|
||||
}
|
||||
for (const auto &key : matchedKeys) {
|
||||
CC_LOG_DEBUG("destroyed shader %s", key.c_str());
|
||||
_cache[key]->destroy();
|
||||
_cache.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Shader *ProgramLib::getGFXShader(gfx::Device *device, const ccstd::string &name, MacroRecord &defines,
|
||||
render::PipelineRuntime *pipeline, ccstd::string *keyOut) {
|
||||
for (const auto &it : pipeline->getMacros()) {
|
||||
defines[it.first] = it.second;
|
||||
}
|
||||
|
||||
ccstd::string key;
|
||||
if (!keyOut) {
|
||||
key = getKey(name, defines);
|
||||
} else {
|
||||
key = *keyOut;
|
||||
}
|
||||
auto itRes = _cache.find(key);
|
||||
if (itRes != _cache.end()) {
|
||||
// CC_LOG_DEBUG("Found ProgramLib::_cache[%s]=%p, defines: %d", key.c_str(), itRes->second, defines.size());
|
||||
return itRes->second;
|
||||
}
|
||||
|
||||
auto itTpl = _templates.find(name);
|
||||
CC_ASSERT(itTpl != _templates.end());
|
||||
|
||||
const auto &tmpl = itTpl->second;
|
||||
const auto itTplInfo = _templateInfos.find(tmpl.hash);
|
||||
CC_ASSERT(itTplInfo != _templateInfos.end());
|
||||
auto &tmplInfo = itTplInfo->second;
|
||||
|
||||
if (!tmplInfo.pipelineLayout) {
|
||||
getDescriptorSetLayout(device, name); // ensure set layouts have been created
|
||||
insertBuiltinBindings(tmpl, tmplInfo, pipeline::globalDescriptorSetLayout, "globals", nullptr);
|
||||
tmplInfo.setLayouts.replace(static_cast<index_t>(pipeline::SetIndex::GLOBAL), pipeline->getDescriptorSetLayout());
|
||||
tmplInfo.pipelineLayout = device->createPipelineLayout(gfx::PipelineLayoutInfo{tmplInfo.setLayouts.get()});
|
||||
}
|
||||
|
||||
ccstd::vector<IMacroInfo> macroArray = render::prepareDefines(defines, tmpl.defines);
|
||||
std::stringstream ss;
|
||||
ss << std::endl;
|
||||
for (const auto &m : macroArray) {
|
||||
ss << "#define " << m.name << " " << m.value << std::endl;
|
||||
}
|
||||
auto prefix = pipeline->getConstantMacros() + tmpl.constantMacros + ss.str();
|
||||
|
||||
const IShaderSource *src = &tmpl.glsl3;
|
||||
const auto *deviceShaderVersion = getDeviceShaderVersion(device);
|
||||
if (deviceShaderVersion) {
|
||||
src = tmpl.getSource(deviceShaderVersion);
|
||||
} else {
|
||||
CC_LOG_ERROR("Invalid GFX API!");
|
||||
}
|
||||
tmplInfo.shaderInfo.stages[0].source = prefix + src->vert;
|
||||
tmplInfo.shaderInfo.stages[1].source = prefix + src->frag;
|
||||
|
||||
// strip out the active attributes only, instancing depend on this
|
||||
tmplInfo.shaderInfo.attributes = render::getActiveAttributes(tmpl, tmplInfo.gfxAttributes, defines);
|
||||
|
||||
tmplInfo.shaderInfo.name = render::getShaderInstanceName(name, macroArray);
|
||||
tmplInfo.shaderInfo.hash = tmpl.hash;
|
||||
auto *shader = device->createShader(tmplInfo.shaderInfo);
|
||||
_cache[key] = shader;
|
||||
// CC_LOG_DEBUG("ProgramLib::_cache[%s]=%p, defines: %d", key.c_str(), shader, defines.size());
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
168
cocos/renderer/core/ProgramLib.h
Normal file
168
cocos/renderer/core/ProgramLib.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/****************************************************************************
|
||||
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 <cmath>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include "base/RefVector.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
#include "base/std/optional.h"
|
||||
#include "core/Types.h"
|
||||
#include "core/assets/EffectAsset.h"
|
||||
#include "renderer/gfx-base/GFXDef-common.h"
|
||||
#include "renderer/pipeline/Define.h"
|
||||
#include "renderer/pipeline/RenderPipeline.h"
|
||||
|
||||
namespace cc {
|
||||
class EffectAsset;
|
||||
|
||||
namespace render {
|
||||
class PipelineRuntime;
|
||||
} // namespace render
|
||||
|
||||
struct IDefineRecord : public IDefineInfo {
|
||||
std::function<int32_t(const MacroValue &)> map{nullptr};
|
||||
int32_t offset{0};
|
||||
};
|
||||
struct IMacroInfo {
|
||||
ccstd::string name;
|
||||
ccstd::string value;
|
||||
bool isDefault{false};
|
||||
};
|
||||
|
||||
struct ITemplateInfo {
|
||||
ccstd::vector<gfx::Attribute> gfxAttributes;
|
||||
gfx::ShaderInfo shaderInfo;
|
||||
ccstd::vector<int32_t> blockSizes;
|
||||
RefVector<gfx::DescriptorSetLayout *> setLayouts;
|
||||
IntrusivePtr<gfx::PipelineLayout> pipelineLayout;
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> handleMap;
|
||||
ccstd::vector<gfx::DescriptorSetLayoutBinding> bindings;
|
||||
int32_t samplerStartBinding{-1};
|
||||
};
|
||||
|
||||
struct IProgramInfo : public IShaderInfo {
|
||||
ccstd::string effectName;
|
||||
ccstd::vector<IDefineRecord> defines;
|
||||
ccstd::string constantMacros;
|
||||
bool uber{false}; // macro number exceeds default limits, will fallback to string hash
|
||||
|
||||
void copyFrom(const IShaderInfo &o);
|
||||
};
|
||||
|
||||
const char *getDeviceShaderVersion(const gfx::Device *device);
|
||||
|
||||
/**
|
||||
* @en The global maintainer of all shader resources.
|
||||
* @zh 维护 shader 资源实例的全局管理器。
|
||||
*/
|
||||
class ProgramLib final {
|
||||
public:
|
||||
static ProgramLib *getInstance();
|
||||
|
||||
ProgramLib();
|
||||
~ProgramLib();
|
||||
|
||||
void registerEffect(EffectAsset *effect);
|
||||
|
||||
/**
|
||||
* @en Register the shader template with the given info
|
||||
* @zh 注册 shader 模板。
|
||||
*/
|
||||
IProgramInfo *define(IShaderInfo &shader);
|
||||
|
||||
/**
|
||||
* @en Gets the shader template with its name
|
||||
* @zh 通过名字获取 Shader 模板
|
||||
* @param name Target shader name
|
||||
*/
|
||||
|
||||
const IProgramInfo *getTemplate(const ccstd::string &name) const;
|
||||
|
||||
/**
|
||||
* @en Gets the shader template info with its name
|
||||
* @zh 通过名字获取 Shader 模版信息
|
||||
* @param name Target shader name
|
||||
*/
|
||||
|
||||
ITemplateInfo *getTemplateInfo(const ccstd::string &name);
|
||||
|
||||
/**
|
||||
* @en Gets the pipeline layout of the shader template given its name
|
||||
* @zh 通过名字获取 Shader 模板相关联的管线布局
|
||||
* @param name Target shader name
|
||||
*/
|
||||
gfx::DescriptorSetLayout *getDescriptorSetLayout(gfx::Device *device, const ccstd::string &name, bool isLocal = false);
|
||||
|
||||
/**
|
||||
* @en
|
||||
* Does this library has the specified program
|
||||
* @zh
|
||||
* 当前是否有已注册的指定名字的 shader
|
||||
* @param name Target shader name
|
||||
*/
|
||||
inline bool hasProgram(const ccstd::string &name) const {
|
||||
return _templates.count(name) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @en Gets the shader key with the name and a macro combination
|
||||
* @zh 根据 shader 名和预处理宏列表获取 shader key。
|
||||
* @param name Target shader name
|
||||
* @param defines The combination of preprocess macros
|
||||
*/
|
||||
ccstd::string getKey(const ccstd::string &name, const MacroRecord &defines);
|
||||
|
||||
/**
|
||||
* @en Destroy all shader instance match the preprocess macros
|
||||
* @zh 销毁所有完全满足指定预处理宏特征的 shader 实例。
|
||||
* @param defines The preprocess macros as filter
|
||||
*/
|
||||
|
||||
void destroyShaderByDefines(const MacroRecord &defines);
|
||||
|
||||
/**
|
||||
* @en Gets the shader resource instance with given information
|
||||
* @zh 获取指定 shader 的渲染资源实例
|
||||
* @param name Shader name
|
||||
* @param defines Preprocess macros
|
||||
* @param pipeline The [[RenderPipeline]] which owns the render command
|
||||
* @param key The shader cache key, if already known
|
||||
*/
|
||||
gfx::Shader *getGFXShader(gfx::Device *device, const ccstd::string &name, MacroRecord &defines,
|
||||
render::PipelineRuntime *pipeline, ccstd::string *key = nullptr);
|
||||
|
||||
private:
|
||||
CC_DISALLOW_COPY_MOVE_ASSIGN(ProgramLib);
|
||||
|
||||
static ProgramLib *instance;
|
||||
ccstd::unordered_map<ccstd::string, IProgramInfo> _templates; // per shader
|
||||
ccstd::unordered_map<ccstd::string, IntrusivePtr<gfx::Shader>> _cache;
|
||||
ccstd::unordered_map<uint64_t, ITemplateInfo> _templateInfos;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
264
cocos/renderer/core/ProgramUtils.cpp
Normal file
264
cocos/renderer/core/ProgramUtils.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/****************************************************************************
|
||||
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 "ProgramUtils.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
namespace render {
|
||||
|
||||
namespace {
|
||||
|
||||
int32_t getBitCount(int32_t cnt) {
|
||||
return std::ceil(std::log2(std::max(cnt, 2))); // std::max checks number types
|
||||
}
|
||||
|
||||
template <class ShaderInfoT>
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> genHandlesImpl(const ShaderInfoT &tmpl) {
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> handleMap{};
|
||||
// block member handles
|
||||
for (const auto &block : tmpl.blocks) {
|
||||
const auto members = block.members;
|
||||
uint32_t offset = 0;
|
||||
for (const auto &uniform : members) {
|
||||
handleMap[uniform.name] = genHandle(block.binding,
|
||||
uniform.type,
|
||||
uniform.count,
|
||||
offset);
|
||||
offset += (getTypeSize(uniform.type) >> 2) * uniform.count; // assumes no implicit padding, which is guaranteed by effect compiler
|
||||
}
|
||||
}
|
||||
// samplerTexture handles
|
||||
for (const auto &samplerTexture : tmpl.samplerTextures) {
|
||||
handleMap[samplerTexture.name] = genHandle(samplerTexture.binding,
|
||||
samplerTexture.type,
|
||||
samplerTexture.count);
|
||||
}
|
||||
return handleMap;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void populateMacros(IProgramInfo &tmpl) {
|
||||
// calculate option mask offset
|
||||
int32_t offset = 0;
|
||||
for (auto &def : tmpl.defines) {
|
||||
int32_t cnt = 1;
|
||||
if (def.type == "number") {
|
||||
auto &range = def.range.value();
|
||||
cnt = getBitCount(range[1] - range[0] + 1); // inclusive on both ends
|
||||
def.map = [=](const MacroValue &value) -> int32_t {
|
||||
if (ccstd::holds_alternative<int32_t>(value)) {
|
||||
return ccstd::get<int32_t>(value) - range[0];
|
||||
}
|
||||
if (ccstd::holds_alternative<bool>(value)) {
|
||||
return (ccstd::get<bool>(value) ? 1 : 0) - range[0];
|
||||
}
|
||||
CC_ABORT(); // We only support macro with int32_t type now.
|
||||
return 0;
|
||||
};
|
||||
} else if (def.type == "string") {
|
||||
cnt = getBitCount(static_cast<int32_t>(def.options.value().size()));
|
||||
def.map = [=](const MacroValue &value) -> int32_t {
|
||||
const auto *pValue = ccstd::get_if<ccstd::string>(&value);
|
||||
if (pValue != nullptr) {
|
||||
auto idx = static_cast<int32_t>(std::find(def.options.value().begin(), def.options.value().end(), *pValue) - def.options.value().begin());
|
||||
return std::max(0, idx);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
} else if (def.type == "boolean") {
|
||||
def.map = [](const MacroValue &value) -> int32_t {
|
||||
const auto *pBool = ccstd::get_if<bool>(&value);
|
||||
if (pBool != nullptr) {
|
||||
return *pBool ? 1 : 0;
|
||||
}
|
||||
const auto *pInt = ccstd::get_if<int32_t>(&value);
|
||||
if (pInt != nullptr) {
|
||||
return *pInt ? 1 : 0;
|
||||
}
|
||||
const auto *pString = ccstd::get_if<ccstd::string>(&value);
|
||||
if (pString != nullptr) {
|
||||
return *pString != "0" || !(*pString).empty() ? 1 : 0;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
def.offset = offset;
|
||||
offset += cnt;
|
||||
}
|
||||
if (offset > 31) {
|
||||
tmpl.uber = true;
|
||||
}
|
||||
// generate constant macros
|
||||
{
|
||||
tmpl.constantMacros.clear();
|
||||
std::stringstream ss;
|
||||
for (auto &key : tmpl.builtins.statistics) {
|
||||
ss << "#define " << key.first << " " << key.second << std::endl;
|
||||
}
|
||||
tmpl.constantMacros = ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> genHandles(const IProgramInfo &tmpl) {
|
||||
return genHandlesImpl(tmpl);
|
||||
}
|
||||
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> genHandles(const gfx::ShaderInfo &tmpl) {
|
||||
return genHandlesImpl(tmpl);
|
||||
}
|
||||
|
||||
ccstd::string getVariantKey(const IProgramInfo &tmpl, const MacroRecord &defines) {
|
||||
const auto &tmplDefs = tmpl.defines;
|
||||
if (tmpl.uber) {
|
||||
std::stringstream key;
|
||||
for (const auto &tmplDef : tmplDefs) {
|
||||
auto itDef = defines.find(tmplDef.name);
|
||||
if (itDef == defines.end() || !tmplDef.map) {
|
||||
continue;
|
||||
}
|
||||
const auto &value = itDef->second;
|
||||
auto mapped = tmplDef.map(value);
|
||||
auto offset = tmplDef.offset;
|
||||
key << offset << mapped << "|";
|
||||
}
|
||||
ccstd::string ret{key.str() + std::to_string(tmpl.hash)};
|
||||
return ret;
|
||||
}
|
||||
uint32_t key = 0;
|
||||
std::stringstream ss;
|
||||
for (const auto &tmplDef : tmplDefs) {
|
||||
auto itDef = defines.find(tmplDef.name);
|
||||
if (itDef == defines.end() || !tmplDef.map) {
|
||||
continue;
|
||||
}
|
||||
const auto &value = itDef->second;
|
||||
auto mapped = tmplDef.map(value);
|
||||
auto offset = tmplDef.offset;
|
||||
key |= (mapped << offset);
|
||||
}
|
||||
ss << std::hex << key << "|" << std::to_string(tmpl.hash);
|
||||
ccstd::string ret{ss.str()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
ccstd::string mapDefine(const IDefineInfo &info, const ccstd::optional<MacroRecord::mapped_type> &def) {
|
||||
if (info.type == "boolean") {
|
||||
return def.has_value() ? (macroRecordAsBool(def.value()) ? "1" : "0") : "0";
|
||||
}
|
||||
if (info.type == "string") {
|
||||
return def.has_value() ? macroRecordAsString(def.value()) : info.options.value()[0];
|
||||
}
|
||||
if (info.type == "number") {
|
||||
return def.has_value() ? macroRecordAsString(def.value()) : std::to_string(info.range.value()[0]);
|
||||
}
|
||||
CC_LOG_WARNING("unknown define type '%s', name: %s", info.type.c_str(), info.name.c_str());
|
||||
return "-1"; // should neven happen
|
||||
}
|
||||
|
||||
bool dependencyCheck(const ccstd::vector<ccstd::string> &dependencies, const MacroRecord &defines) {
|
||||
for (const auto &d : dependencies) { // NOLINT(readability-use-anyofallof)
|
||||
if (d[0] == '!') { // negative dependency
|
||||
if (defines.find(d.substr(1)) != defines.end()) {
|
||||
return false;
|
||||
}
|
||||
} else if (defines.count(d) == 0 ? true : !macroRecordAsBool(defines.at(d))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Vector>
|
||||
ccstd::vector<gfx::Attribute> getActiveAttributesImpl(
|
||||
const IProgramInfo &tmpl,
|
||||
const Vector &gfxAttributes, const MacroRecord &defines) {
|
||||
ccstd::vector<gfx::Attribute> out{};
|
||||
const auto &attributes = tmpl.attributes;
|
||||
for (auto i = 0; i < attributes.size(); i++) {
|
||||
if (!dependencyCheck(attributes[i].defines, defines)) {
|
||||
continue;
|
||||
}
|
||||
out.emplace_back(gfxAttributes[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ccstd::vector<IMacroInfo> prepareDefines(const MacroRecord &records, const ccstd::vector<IDefineRecord> &defList) {
|
||||
ccstd::vector<IMacroInfo> macros{};
|
||||
for (const auto &tmp : defList) {
|
||||
const auto &name = tmp.name;
|
||||
auto it = records.find(name);
|
||||
auto value = mapDefine(tmp, it == records.end() ? ccstd::nullopt : ccstd::optional<MacroValue>(it->second));
|
||||
bool isDefault = it == records.end() || (ccstd::holds_alternative<ccstd::string>(it->second) && ccstd::get<ccstd::string>(it->second) == "0");
|
||||
macros.emplace_back();
|
||||
auto &info = macros.back();
|
||||
info.name = name;
|
||||
info.value = value;
|
||||
info.isDefault = isDefault;
|
||||
}
|
||||
return macros;
|
||||
}
|
||||
|
||||
ccstd::vector<gfx::Attribute> getActiveAttributes(
|
||||
const IProgramInfo &tmpl,
|
||||
const ccstd::vector<gfx::Attribute> &gfxAttributes, const MacroRecord &defines) {
|
||||
return getActiveAttributesImpl(tmpl, gfxAttributes, defines);
|
||||
}
|
||||
|
||||
ccstd::vector<gfx::Attribute> getActiveAttributes(
|
||||
const IProgramInfo &tmpl,
|
||||
const ccstd::pmr::vector<gfx::Attribute> &gfxAttributes, const MacroRecord &defines) {
|
||||
return getActiveAttributesImpl(tmpl, gfxAttributes, defines);
|
||||
}
|
||||
|
||||
ccstd::string getShaderInstanceName(const ccstd::string &name, const ccstd::vector<IMacroInfo> ¯os) {
|
||||
std::stringstream ret;
|
||||
ret << name;
|
||||
for (const auto &cur : macros) {
|
||||
if (!cur.isDefault) {
|
||||
ret << "|" << cur.name << cur.value;
|
||||
}
|
||||
}
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
void addEffectDefaultProperties(EffectAsset &effect) {
|
||||
for (auto &tech : effect._techniques) {
|
||||
for (auto &pass : tech.passes) {
|
||||
// grab default property declaration if there is none
|
||||
if (pass.propertyIndex.has_value() && !pass.properties.has_value()) {
|
||||
pass.properties = tech.passes[pass.propertyIndex.value()].properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace render
|
||||
|
||||
} // namespace cc
|
||||
55
cocos/renderer/core/ProgramUtils.h
Normal file
55
cocos/renderer/core/ProgramUtils.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************************
|
||||
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 "cocos/renderer/core/ProgramLib.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
namespace render {
|
||||
|
||||
void populateMacros(IProgramInfo& tmpl);
|
||||
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> genHandles(const IProgramInfo& tmpl);
|
||||
ccstd::unordered_map<ccstd::string, uint32_t> genHandles(const gfx::ShaderInfo& tmpl);
|
||||
ccstd::string getVariantKey(const IProgramInfo& tmpl, const MacroRecord& defines);
|
||||
ccstd::vector<IMacroInfo> prepareDefines(
|
||||
const MacroRecord& records, const ccstd::vector<IDefineRecord>& defList);
|
||||
|
||||
ccstd::vector<gfx::Attribute> getActiveAttributes(
|
||||
const IProgramInfo& tmpl,
|
||||
const ccstd::vector<gfx::Attribute>& gfxAttributes, const MacroRecord& defines);
|
||||
|
||||
ccstd::vector<gfx::Attribute> getActiveAttributes(
|
||||
const IProgramInfo& tmpl,
|
||||
const ccstd::pmr::vector<gfx::Attribute>& gfxAttributes, const MacroRecord& defines);
|
||||
|
||||
ccstd::string getShaderInstanceName(
|
||||
const ccstd::string& name, const ccstd::vector<IMacroInfo>& macros);
|
||||
|
||||
void addEffectDefaultProperties(EffectAsset& effect);
|
||||
|
||||
} // namespace render
|
||||
|
||||
} // namespace cc
|
||||
316
cocos/renderer/core/TextureBufferPool.cpp
Normal file
316
cocos/renderer/core/TextureBufferPool.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/****************************************************************************
|
||||
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 "renderer/core/TextureBufferPool.h"
|
||||
#include <cmath>
|
||||
#include "core/ArrayBuffer.h"
|
||||
#include "core/TypedArray.h"
|
||||
#include "renderer/gfx-base/GFXDevice.h"
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t roundUp(uint32_t n, uint32_t alignment) {
|
||||
return static_cast<uint32_t>(std::ceil(n / alignment)) * alignment;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace cc {
|
||||
|
||||
TextureBufferPool::TextureBufferPool() = default;
|
||||
|
||||
TextureBufferPool::TextureBufferPool(gfx::Device *device) {
|
||||
_device = device;
|
||||
}
|
||||
|
||||
TextureBufferPool::~TextureBufferPool() = default;
|
||||
|
||||
void TextureBufferPool::initialize(const ITextureBufferPoolInfo &info) {
|
||||
const auto &formatInfo = gfx::GFX_FORMAT_INFOS[static_cast<uint32_t>(info.format)];
|
||||
_format = info.format;
|
||||
_formatSize = formatInfo.size;
|
||||
_channels = formatInfo.count;
|
||||
_roundUpFn = info.roundUpFn.has_value() ? info.roundUpFn.value() : nullptr;
|
||||
_alignment = info.alignment.has_value() ? info.alignment.value() : 1;
|
||||
_useMcDonaldAlloc = info.alignment.has_value() && info.alignment.value();
|
||||
}
|
||||
|
||||
void TextureBufferPool::destroy() {
|
||||
for (auto &chunk : _chunks) {
|
||||
CC_SAFE_DESTROY_AND_DELETE(chunk.texture);
|
||||
}
|
||||
_chunks.clear();
|
||||
_handles.clear();
|
||||
}
|
||||
|
||||
ITextureBufferHandle TextureBufferPool::alloc(uint32_t size) {
|
||||
if (_useMcDonaldAlloc) {
|
||||
return mcDonaldAlloc(size);
|
||||
}
|
||||
|
||||
size = roundUp(size, _alignment);
|
||||
index_t index = CC_INVALID_INDEX;
|
||||
index_t start = CC_INVALID_INDEX;
|
||||
if (start < 0) {
|
||||
for (index_t i = 0; i < _chunkCount; ++i) {
|
||||
index = i;
|
||||
start = findAvailableSpace(size, index);
|
||||
if (start >= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start >= 0) {
|
||||
auto chunk = _chunks[index];
|
||||
chunk.start += static_cast<index_t>(size);
|
||||
ITextureBufferHandle handle;
|
||||
handle.chunkIdx = index;
|
||||
handle.start = start;
|
||||
handle.end = static_cast<index_t>(start + size);
|
||||
handle.texture = chunk.texture;
|
||||
|
||||
_handles.emplace_back(handle);
|
||||
return handle;
|
||||
}
|
||||
// create a new one
|
||||
auto targetSize = static_cast<int32_t>(std::sqrt(size / _formatSize));
|
||||
uint32_t texLength = _roundUpFn ? _roundUpFn(targetSize, _formatSize) : std::max(1024, static_cast<int>(utils::nextPOT(targetSize)));
|
||||
auto newChunk = _chunks[createChunk(texLength)];
|
||||
|
||||
newChunk.start += static_cast<index_t>(size);
|
||||
ITextureBufferHandle texHandle;
|
||||
texHandle.chunkIdx = static_cast<index_t>(_chunkCount - 1);
|
||||
texHandle.start = 0;
|
||||
texHandle.end = static_cast<index_t>(size);
|
||||
texHandle.texture = newChunk.texture;
|
||||
_handles.emplace_back(texHandle);
|
||||
return texHandle;
|
||||
}
|
||||
|
||||
ITextureBufferHandle TextureBufferPool::alloc(uint32_t size, index_t chunkIdx) {
|
||||
size = roundUp(size, _alignment);
|
||||
index_t index = chunkIdx;
|
||||
index_t start = findAvailableSpace(size, index);
|
||||
|
||||
if (start < 0) {
|
||||
for (index_t i = 0; i < _chunkCount; ++i) {
|
||||
index = i;
|
||||
start = findAvailableSpace(size, index);
|
||||
if (start >= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start >= 0) {
|
||||
auto chunk = _chunks[index];
|
||||
chunk.start += static_cast<index_t>(size);
|
||||
ITextureBufferHandle handle;
|
||||
handle.chunkIdx = index;
|
||||
handle.start = start;
|
||||
handle.end = static_cast<index_t>(start + size);
|
||||
handle.texture = chunk.texture;
|
||||
|
||||
_handles.emplace_back(handle);
|
||||
return handle;
|
||||
}
|
||||
// create a new one
|
||||
auto targetSize = static_cast<int32_t>(std::sqrt(size / _formatSize));
|
||||
uint32_t texLength = _roundUpFn ? _roundUpFn(targetSize, _formatSize) : std::max(1024, static_cast<int>(utils::nextPOT(targetSize)));
|
||||
auto newChunk = _chunks[createChunk(texLength)];
|
||||
|
||||
newChunk.start += static_cast<index_t>(size);
|
||||
ITextureBufferHandle texHandle;
|
||||
texHandle.chunkIdx = static_cast<index_t>(_chunkCount - 1);
|
||||
texHandle.start = 0;
|
||||
texHandle.end = static_cast<index_t>(size);
|
||||
texHandle.texture = newChunk.texture;
|
||||
_handles.emplace_back(texHandle);
|
||||
return texHandle;
|
||||
}
|
||||
|
||||
void TextureBufferPool::free(const ITextureBufferHandle &handle) {
|
||||
auto iter = std::find(_handles.begin(), _handles.end(), handle);
|
||||
if (iter != _handles.end()) {
|
||||
_chunks[handle.chunkIdx].end = handle.end;
|
||||
_handles.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TextureBufferPool::createChunk(uint32_t length) {
|
||||
uint32_t texSize = length * length * _formatSize;
|
||||
// debug(`TextureBufferPool: Allocate chunk $device->createTexture({gfx::TextureType::TEX2D,
|
||||
auto *texture = _device->createTexture({gfx::TextureType::TEX2D,
|
||||
gfx::TextureUsageBit::SAMPLED | gfx::TextureUsageBit::TRANSFER_DST,
|
||||
_format,
|
||||
length,
|
||||
length});
|
||||
|
||||
ITextureBuffer chunk;
|
||||
chunk.texture = texture;
|
||||
chunk.size = texSize;
|
||||
chunk.start = 0;
|
||||
chunk.end = static_cast<index_t>(texSize);
|
||||
_chunks[_chunkCount] = chunk;
|
||||
return _chunkCount++;
|
||||
}
|
||||
|
||||
void TextureBufferPool::update(const ITextureBufferHandle &handle, ArrayBuffer *buffer) {
|
||||
gfx::BufferDataList buffers;
|
||||
uint8_t *bufferData = buffer->getData();
|
||||
gfx::BufferTextureCopyList regions;
|
||||
auto start = static_cast<int32_t>(handle.start / _formatSize);
|
||||
|
||||
uint32_t remainSize = buffer->byteLength() / _formatSize;
|
||||
int32_t offsetX = start % static_cast<int32_t>(handle.texture->getWidth());
|
||||
int32_t offsetY = std::floor(start / handle.texture->getWidth());
|
||||
uint32_t copySize = std::min(handle.texture->getWidth() - offsetX, remainSize);
|
||||
uint32_t begin = 0;
|
||||
|
||||
if (offsetX > 0) {
|
||||
_region0.texOffset.x = offsetX;
|
||||
_region0.texOffset.y = offsetY;
|
||||
_region0.texExtent.width = copySize;
|
||||
_region0.texExtent.height = 1;
|
||||
buffers.emplace_back(bufferData + begin * _formatSize);
|
||||
regions.emplace_back(_region0);
|
||||
offsetX = 0;
|
||||
offsetY += 1;
|
||||
remainSize -= copySize;
|
||||
begin += copySize;
|
||||
}
|
||||
|
||||
if (remainSize > 0) {
|
||||
_region1.texOffset.x = offsetX;
|
||||
_region1.texOffset.y = offsetY;
|
||||
|
||||
if (remainSize > handle.texture->getWidth()) {
|
||||
_region1.texExtent.width = handle.texture->getWidth();
|
||||
_region1.texExtent.height = std::floor(remainSize / handle.texture->getWidth());
|
||||
copySize = _region1.texExtent.width * _region1.texExtent.height;
|
||||
} else {
|
||||
copySize = remainSize;
|
||||
_region1.texExtent.width = copySize;
|
||||
_region1.texExtent.height = 1;
|
||||
}
|
||||
|
||||
buffers.emplace_back(bufferData + begin * _formatSize);
|
||||
regions.emplace_back(_region1);
|
||||
|
||||
offsetX = 0;
|
||||
offsetY += static_cast<int32_t>(_region1.texExtent.height);
|
||||
remainSize -= copySize;
|
||||
begin += copySize;
|
||||
}
|
||||
|
||||
if (remainSize > 0) {
|
||||
_region2.texOffset.x = offsetX;
|
||||
_region2.texOffset.y = offsetY;
|
||||
_region2.texExtent.width = remainSize;
|
||||
_region2.texExtent.height = 1;
|
||||
|
||||
buffers.emplace_back(bufferData + begin * _formatSize);
|
||||
regions.emplace_back(_region2);
|
||||
}
|
||||
_device->copyBuffersToTexture(buffers, handle.texture, regions);
|
||||
}
|
||||
|
||||
index_t TextureBufferPool::findAvailableSpace(uint32_t size, index_t chunkIdx) const {
|
||||
auto chunk = _chunks[chunkIdx];
|
||||
bool isFound = false;
|
||||
index_t start = chunk.start;
|
||||
if ((start + size) <= chunk.size) {
|
||||
isFound = true;
|
||||
} else {
|
||||
start = 0; // try to find from head again
|
||||
ccstd::vector<ITextureBufferHandle> handles;
|
||||
for (auto h : _handles) {
|
||||
if (h.chunkIdx == chunkIdx) {
|
||||
handles.emplace_back(h);
|
||||
}
|
||||
}
|
||||
std::sort(handles.begin(), handles.end(), [](const ITextureBufferHandle &a, const ITextureBufferHandle &b) { return a.start - b.start; });
|
||||
for (auto handle : handles) {
|
||||
if ((start + size) <= handle.start) {
|
||||
isFound = true;
|
||||
break;
|
||||
}
|
||||
start = handle.end;
|
||||
}
|
||||
if (!isFound && (start + size) <= chunk.size) {
|
||||
isFound = true;
|
||||
}
|
||||
}
|
||||
return isFound ? start : CC_INVALID_INDEX;
|
||||
}
|
||||
|
||||
ITextureBufferHandle TextureBufferPool::mcDonaldAlloc(uint32_t size) {
|
||||
size = roundUp(size, _alignment);
|
||||
for (index_t i = 0; i < _chunkCount; ++i) {
|
||||
auto chunk = _chunks[i];
|
||||
bool isFound = false;
|
||||
index_t start = chunk.start;
|
||||
if ((start + size) <= chunk.end) {
|
||||
isFound = true;
|
||||
} else if (start > chunk.end) {
|
||||
if ((start + size) <= chunk.size) {
|
||||
isFound = true;
|
||||
} else if (size <= chunk.end) {
|
||||
// Try to find from head again.
|
||||
start = 0;
|
||||
chunk.start = 0;
|
||||
isFound = true;
|
||||
}
|
||||
} else if (start == chunk.end) {
|
||||
start = 0;
|
||||
chunk.start = 0;
|
||||
chunk.end = static_cast<index_t>(chunk.size);
|
||||
if (size <= chunk.end) {
|
||||
isFound = true;
|
||||
}
|
||||
}
|
||||
if (isFound) {
|
||||
chunk.start += static_cast<index_t>(size);
|
||||
|
||||
ITextureBufferHandle handle;
|
||||
handle.chunkIdx = i;
|
||||
handle.start = start;
|
||||
handle.end = static_cast<index_t>(size);
|
||||
handle.texture = chunk.texture;
|
||||
_handles.emplace_back(handle);
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
// create a new one
|
||||
auto targetSize = static_cast<int32_t>(std::sqrt(size / _formatSize));
|
||||
uint32_t texLength = _roundUpFn ? _roundUpFn(targetSize, _formatSize) : std::max(1024, static_cast<int>(utils::nextPOT(targetSize)));
|
||||
auto newChunk = _chunks[createChunk(texLength)];
|
||||
|
||||
newChunk.start += static_cast<index_t>(size);
|
||||
ITextureBufferHandle texHandle;
|
||||
texHandle.chunkIdx = static_cast<index_t>(_chunkCount);
|
||||
texHandle.start = 0;
|
||||
texHandle.end = static_cast<index_t>(size),
|
||||
texHandle.texture = newChunk.texture;
|
||||
_handles.emplace_back(texHandle);
|
||||
return texHandle;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
97
cocos/renderer/core/TextureBufferPool.h
Normal file
97
cocos/renderer/core/TextureBufferPool.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
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 <functional>
|
||||
#include "audio/android/PcmBufferProvider.h"
|
||||
#include "base/std/optional.h"
|
||||
#include "core/ArrayBuffer.h"
|
||||
#include "renderer/gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
using roundUpType = std::function<uint32_t(uint32_t size, uint32_t formatSize)>;
|
||||
|
||||
struct ITextureBuffer {
|
||||
gfx::Texture *texture{nullptr};
|
||||
uint32_t size{0};
|
||||
index_t start{0};
|
||||
index_t end{0};
|
||||
};
|
||||
|
||||
struct ITextureBufferHandle {
|
||||
index_t chunkIdx{0};
|
||||
index_t start{0};
|
||||
index_t end{0};
|
||||
gfx::Texture *texture{nullptr};
|
||||
|
||||
bool operator==(const ITextureBufferHandle &handle) const {
|
||||
return chunkIdx == handle.chunkIdx && start == handle.start && end == handle.end && texture == handle.texture;
|
||||
}
|
||||
};
|
||||
|
||||
struct ITextureBufferPoolInfo {
|
||||
gfx::Format format{gfx::Format::UNKNOWN}; // target texture format
|
||||
ccstd::optional<bool> inOrderFree; // will the handles be freed exactly in the order of their allocation?
|
||||
ccstd::optional<uint32_t> alignment; // the data alignment for each handle allocated, in bytes
|
||||
ccstd::optional<roundUpType> roundUpFn; // given a target size, how will the actual texture size round up?
|
||||
};
|
||||
|
||||
class TextureBufferPool : public RefCounted {
|
||||
public:
|
||||
TextureBufferPool();
|
||||
explicit TextureBufferPool(gfx::Device *device);
|
||||
~TextureBufferPool() override;
|
||||
|
||||
void initialize(const ITextureBufferPoolInfo &info);
|
||||
void destroy();
|
||||
ITextureBufferHandle alloc(uint32_t size);
|
||||
ITextureBufferHandle alloc(uint32_t size, index_t chunkIdx);
|
||||
|
||||
void free(const ITextureBufferHandle &handle);
|
||||
uint32_t createChunk(uint32_t length);
|
||||
void update(const ITextureBufferHandle &handle, ArrayBuffer *buffer);
|
||||
|
||||
private:
|
||||
index_t findAvailableSpace(uint32_t size, index_t chunkIdx) const;
|
||||
|
||||
// [McDonald 12] Efficient Buffer Management
|
||||
ITextureBufferHandle mcDonaldAlloc(uint32_t size);
|
||||
|
||||
gfx::Device *_device{nullptr};
|
||||
gfx::Format _format{gfx::Format::UNKNOWN};
|
||||
uint32_t _formatSize{0};
|
||||
ccstd::vector<ITextureBuffer> _chunks;
|
||||
uint32_t _chunkCount{0};
|
||||
ccstd::vector<ITextureBufferHandle> _handles;
|
||||
gfx::BufferTextureCopy _region0;
|
||||
gfx::BufferTextureCopy _region1;
|
||||
gfx::BufferTextureCopy _region2;
|
||||
roundUpType _roundUpFn{nullptr};
|
||||
bool _useMcDonaldAlloc{false};
|
||||
uint32_t _channels{4};
|
||||
uint32_t _alignment{1};
|
||||
CC_DISALLOW_COPY_MOVE_ASSIGN(TextureBufferPool);
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
Reference in New Issue
Block a user