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,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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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> &macros) {
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

View 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

View 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

View 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