You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1802 lines
68 KiB
1802 lines
68 KiB
/****************************************************************************
|
|
Copyright (c) 2019-2022 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 engine source code (the "Software"), a limited,
|
|
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
|
to use Cocos Creator solely to develop games on your target platforms. You shall
|
|
not use Cocos Creator software for developing other software or tools that's
|
|
used for developing games. You are not granted to publish, distribute,
|
|
sublicense, and/or sell copies of Cocos Creator.
|
|
|
|
The software or tools in this License Agreement are licensed, not sold.
|
|
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
|
|
|
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 "MTLUtils.h"
|
|
|
|
#include "MTLDevice.h"
|
|
#include "MTLGPUObjects.h"
|
|
#include "MTLPipelineState.h"
|
|
#include "MTLRenderPass.h"
|
|
#include "MTLShader.h"
|
|
#include "glslang/StandAlone/ResourceLimits.h"
|
|
#include "glslang/SPIRV/GlslangToSpv.h"
|
|
#include "spirv_cross/spirv_msl.hpp"
|
|
#include "TargetConditionals.h"
|
|
#include "base/Log.h"
|
|
#include <regex>
|
|
|
|
namespace cc {
|
|
namespace gfx {
|
|
namespace {
|
|
|
|
ccstd::unordered_map<size_t, PipelineState *> pipelineMap;
|
|
ccstd::unordered_map<size_t, RenderPass *> renderPassMap;
|
|
|
|
EShLanguage getShaderStage(ShaderStageFlagBit type) {
|
|
switch (type) {
|
|
case ShaderStageFlagBit::VERTEX: return EShLangVertex;
|
|
case ShaderStageFlagBit::CONTROL: return EShLangTessControl;
|
|
case ShaderStageFlagBit::EVALUATION: return EShLangTessEvaluation;
|
|
case ShaderStageFlagBit::GEOMETRY: return EShLangGeometry;
|
|
case ShaderStageFlagBit::FRAGMENT: return EShLangFragment;
|
|
case ShaderStageFlagBit::COMPUTE: return EShLangCompute;
|
|
default: {
|
|
CC_ABORT();
|
|
return EShLangVertex;
|
|
}
|
|
}
|
|
}
|
|
|
|
glslang::EShTargetClientVersion getClientVersion(int vulkanMinorVersion) {
|
|
switch (vulkanMinorVersion) {
|
|
case 0: return glslang::EShTargetVulkan_1_0;
|
|
case 1: return glslang::EShTargetVulkan_1_1;
|
|
case 2: return glslang::EShTargetVulkan_1_2;
|
|
default: {
|
|
CC_ABORT();
|
|
return glslang::EShTargetVulkan_1_0;
|
|
}
|
|
}
|
|
}
|
|
|
|
glslang::EShTargetLanguageVersion getTargetVersion(int vulkanMinorVersion) {
|
|
switch (vulkanMinorVersion) {
|
|
case 0: return glslang::EShTargetSpv_1_0;
|
|
case 1: return glslang::EShTargetSpv_1_3;
|
|
case 2: return glslang::EShTargetSpv_1_5;
|
|
default: {
|
|
CC_ABORT();
|
|
return glslang::EShTargetSpv_1_0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//See more details at https://developer.apple.com/documentation/metal/mtlfeatureset
|
|
enum class GPUFamily {
|
|
Apple1, // A7,
|
|
Apple2, // A8
|
|
Apple3, // A9, A10
|
|
Apple4, // A11
|
|
Apple5, // A12
|
|
Apple6, // A13
|
|
|
|
Mac1,
|
|
Mac2,
|
|
};
|
|
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
ccstd::string getIOSFeatureSetToString(MTLFeatureSet featureSet) {
|
|
if (@available(iOS 8.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v1:
|
|
return "MTLFeatureSet_iOS_GPUFamily1_v1";
|
|
case MTLFeatureSet_iOS_GPUFamily2_v1:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v1";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 9.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v2:
|
|
return "MTLFeatureSet_iOS_GPUFamily1_v2";
|
|
case MTLFeatureSet_iOS_GPUFamily2_v2:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v2";
|
|
case MTLFeatureSet_iOS_GPUFamily3_v1:
|
|
return "MTLFeatureSet_iOS_GPUFamily3_v1";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 10.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v3:
|
|
return "MTLFeatureSet_iOS_GPUFamily1_v3";
|
|
case MTLFeatureSet_iOS_GPUFamily2_v3:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v3";
|
|
case MTLFeatureSet_iOS_GPUFamily3_v2:
|
|
return "MTLFeatureSet_iOS_GPUFamily3_v2";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 11.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v4:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v4";
|
|
case MTLFeatureSet_iOS_GPUFamily2_v3:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v3";
|
|
case MTLFeatureSet_iOS_GPUFamily3_v3:
|
|
return "MTLFeatureSet_iOS_GPUFamily3_v3";
|
|
case MTLFeatureSet_iOS_GPUFamily4_v1:
|
|
return "MTLFeatureSet_iOS_GPUFamily4_v1";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 12.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v5:
|
|
return "MTLFeatureSet_iOS_GPUFamily1_v5";
|
|
case MTLFeatureSet_iOS_GPUFamily2_v5:
|
|
return "MTLFeatureSet_iOS_GPUFamily2_v5";
|
|
case MTLFeatureSet_iOS_GPUFamily3_v4:
|
|
return "MTLFeatureSet_iOS_GPUFamily3_v4";
|
|
case MTLFeatureSet_iOS_GPUFamily4_v2:
|
|
return "MTLFeatureSet_iOS_GPUFamily4_v2";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return "Invalid metal feature set";
|
|
}
|
|
|
|
GPUFamily getIOSGPUFamily(MTLFeatureSet featureSet) {
|
|
if (@available(iOS 12.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v5:
|
|
return GPUFamily::Apple1;
|
|
case MTLFeatureSet_iOS_GPUFamily2_v5:
|
|
return GPUFamily::Apple2;
|
|
case MTLFeatureSet_iOS_GPUFamily3_v4:
|
|
return GPUFamily::Apple3;
|
|
case MTLFeatureSet_iOS_GPUFamily4_v2:
|
|
return GPUFamily::Apple4;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 11.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v4:
|
|
return GPUFamily::Apple1;
|
|
case MTLFeatureSet_iOS_GPUFamily2_v4:
|
|
return GPUFamily::Apple2;
|
|
case MTLFeatureSet_iOS_GPUFamily3_v3:
|
|
return GPUFamily::Apple3;
|
|
case MTLFeatureSet_iOS_GPUFamily4_v1:
|
|
return GPUFamily::Apple4;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 10.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v3:
|
|
return GPUFamily::Apple1;
|
|
case MTLFeatureSet_iOS_GPUFamily2_v3:
|
|
return GPUFamily::Apple2;
|
|
case MTLFeatureSet_iOS_GPUFamily3_v2:
|
|
return GPUFamily::Apple3;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 9.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v2:
|
|
return GPUFamily::Apple1;
|
|
case MTLFeatureSet_iOS_GPUFamily2_v2:
|
|
return GPUFamily::Apple2;
|
|
case MTLFeatureSet_iOS_GPUFamily3_v1:
|
|
return GPUFamily::Apple3;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(iOS 8.0, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_iOS_GPUFamily1_v1:
|
|
return GPUFamily::Apple1;
|
|
case MTLFeatureSet_iOS_GPUFamily2_v1:
|
|
return GPUFamily::Apple2;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return GPUFamily::Apple1;
|
|
}
|
|
#else
|
|
ccstd::string getMacFeatureSetToString(MTLFeatureSet featureSet) {
|
|
if (@available(macOS 10.11, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_macOS_GPUFamily1_v1:
|
|
return "MTLFeatureSet_macOS_GPUFamily1_v1";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(macOS 10.12, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_macOS_GPUFamily1_v2:
|
|
return "MTLFeatureSet_macOS_GPUFamily1_v2";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(macOS 10.13, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_macOS_GPUFamily1_v3:
|
|
return "MTLFeatureSet_macOS_GPUFamily1_v3";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (@available(macOS 10.14, *)) {
|
|
switch (featureSet) {
|
|
case MTLFeatureSet_macOS_GPUFamily1_v4:
|
|
return "MTLFeatureSet_macOS_GPUFamily1_v4";
|
|
case MTLFeatureSet_macOS_GPUFamily2_v1:
|
|
return "MTLFeatureSet_macOS_GPUFamily2_v1";
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return "Invalid metal feature set";
|
|
}
|
|
|
|
GPUFamily getMacGPUFamily(MTLFeatureSet featureSet) {
|
|
if (@available(macOS 10.14, *)) {
|
|
if (MTLFeatureSet_macOS_GPUFamily2_v1 <= featureSet) {
|
|
return GPUFamily::Mac2;
|
|
}
|
|
}
|
|
return GPUFamily::Mac1;
|
|
}
|
|
#endif
|
|
|
|
bool isASTCFormat(Format format) {
|
|
switch (format) {
|
|
case Format::ASTC_RGBA_4X4:
|
|
case Format::ASTC_SRGBA_4X4:
|
|
case Format::ASTC_RGBA_5X4:
|
|
case Format::ASTC_SRGBA_5X4:
|
|
case Format::ASTC_RGBA_5X5:
|
|
case Format::ASTC_SRGBA_5X5:
|
|
case Format::ASTC_RGBA_6X5:
|
|
case Format::ASTC_SRGBA_6X5:
|
|
case Format::ASTC_RGBA_6X6:
|
|
case Format::ASTC_SRGBA_6X6:
|
|
case Format::ASTC_RGBA_8X5:
|
|
case Format::ASTC_SRGBA_8X5:
|
|
case Format::ASTC_RGBA_8X6:
|
|
case Format::ASTC_SRGBA_8X6:
|
|
case Format::ASTC_RGBA_8X8:
|
|
case Format::ASTC_SRGBA_8X8:
|
|
case Format::ASTC_RGBA_10X5:
|
|
case Format::ASTC_SRGBA_10X5:
|
|
case Format::ASTC_RGBA_10X6:
|
|
case Format::ASTC_SRGBA_10X6:
|
|
case Format::ASTC_RGBA_10X8:
|
|
case Format::ASTC_SRGBA_10X8:
|
|
case Format::ASTC_RGBA_10X10:
|
|
case Format::ASTC_SRGBA_10X10:
|
|
case Format::ASTC_RGBA_12X10:
|
|
case Format::ASTC_SRGBA_12X10:
|
|
case Format::ASTC_RGBA_12X12:
|
|
case Format::ASTC_SRGBA_12X12:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
gfx::Shader *createShader(CCMTLDevice *device, CCMTLRenderPass *renderPass) {
|
|
ccstd::string vs = R"(
|
|
layout(location = 0) in vec2 a_position;
|
|
void main() {
|
|
gl_Position = vec4(a_position, 1.0, 1.0);
|
|
}
|
|
)";
|
|
// ccstd::string fs = R"(
|
|
// precision mediump float;
|
|
// layout(set = 0, binding = 0) uniform Color {
|
|
// vec4 u_color;
|
|
// };
|
|
// layout(location = 0) out vec4 o_color;
|
|
//
|
|
// void main() {
|
|
// o_color = u_color;
|
|
// }
|
|
// )";
|
|
|
|
ccstd::string fs = R"(
|
|
precision mediump float;
|
|
layout(set = 0, binding = 0) uniform Color {
|
|
vec4 u_color;
|
|
};
|
|
)";
|
|
|
|
//TODO_Zeqiang: gather info in framegraph.
|
|
if (renderPass->getSubpasses().empty()) {
|
|
for (size_t i = 0; i < renderPass->getColorAttachments().size(); ++i) {
|
|
fs += "\n layout(location = " + std::to_string(i) + ") out vec4 o_color" + std::to_string(i) + ";";
|
|
}
|
|
|
|
fs += "\nvoid main() {\n o_color0 = u_color;\n";
|
|
|
|
for (size_t i = 1; i < renderPass->getColorAttachments().size(); ++i) {
|
|
fs += " o_color" + std::to_string(i) + " = vec4(0.0);\n";
|
|
}
|
|
} else {
|
|
const auto &subpasses = renderPass->getSubpasses();
|
|
for (size_t i = 0; i < subpasses[renderPass->getCurrentSubpassIndex()].colors.size(); ++i) {
|
|
fs += "\n layout(location = " + std::to_string(i) + ") out vec4 o_color" + std::to_string(i) + ";";
|
|
}
|
|
|
|
fs += "\nvoid main() {\n o_color0 = u_color;\n";
|
|
|
|
for (size_t i = 1; i < subpasses[renderPass->getCurrentSubpassIndex()].colors.size(); ++i) {
|
|
fs += " o_color" + std::to_string(i) + " = vec4(0.0);\n";
|
|
}
|
|
}
|
|
|
|
fs += "}";
|
|
|
|
gfx::ShaderStageList shaderStageList;
|
|
gfx::ShaderStage vertexShaderStage;
|
|
vertexShaderStage.stage = gfx::ShaderStageFlagBit::VERTEX;
|
|
vertexShaderStage.source = std::move(vs);
|
|
shaderStageList.emplace_back(std::move(vertexShaderStage));
|
|
|
|
gfx::ShaderStage fragmentShaderStage;
|
|
fragmentShaderStage.stage = gfx::ShaderStageFlagBit::FRAGMENT;
|
|
fragmentShaderStage.source = std::move(fs);
|
|
shaderStageList.emplace_back(std::move(fragmentShaderStage));
|
|
|
|
gfx::UniformBlockList uniformBlockList = {
|
|
{0, 0, "Color", {{"u_color", gfx::Type::FLOAT4, 1}}, 1},
|
|
};
|
|
gfx::AttributeList attributeList = {{"a_position", gfx::Format::RG32F, false, 0, false, 0}};
|
|
|
|
gfx::ShaderInfo shaderInfo;
|
|
shaderInfo.name = "Clear Render Area";
|
|
shaderInfo.stages = std::move(shaderStageList);
|
|
shaderInfo.attributes = std::move(attributeList);
|
|
shaderInfo.blocks = std::move(uniformBlockList);
|
|
return device->createShader(shaderInfo);
|
|
}
|
|
|
|
CCMTLGPUPipelineState *getClearRenderPassPipelineState(CCMTLDevice *device, RenderPass *curPass) {
|
|
ccstd::hash_t rpHash = curPass->getHash();
|
|
const auto iter = pipelineMap.find(rpHash);
|
|
if (iter != pipelineMap.end()) {
|
|
auto *ccMtlPiplineState = static_cast<CCMTLPipelineState *>(iter->second);
|
|
return ccMtlPiplineState->getGPUPipelineState();
|
|
}
|
|
|
|
gfx::Attribute position = {"a_position", gfx::Format::RG32F, false, 0, false};
|
|
gfx::PipelineStateInfo pipelineInfo;
|
|
pipelineInfo.primitive = gfx::PrimitiveMode::TRIANGLE_LIST;
|
|
pipelineInfo.shader = createShader(device, static_cast<CCMTLRenderPass *>(curPass));
|
|
pipelineInfo.inputState = {{position}};
|
|
pipelineInfo.renderPass = curPass;
|
|
|
|
DepthStencilState dsState;
|
|
dsState.depthWrite = curPass->getDepthStencilAttachment().format != Format::UNKNOWN;
|
|
dsState.depthTest = 0;
|
|
dsState.depthFunc = ComparisonFunc::ALWAYS;
|
|
pipelineInfo.depthStencilState = dsState;
|
|
|
|
PipelineState *pipelineState = device->createPipelineState(std::move(pipelineInfo));
|
|
pipelineMap.emplace(curPass->getHash(), pipelineState);
|
|
((CCMTLPipelineState *)pipelineState)->check();
|
|
delete pipelineInfo.shader;
|
|
return static_cast<CCMTLPipelineState *>(pipelineState)->getGPUPipelineState();
|
|
}
|
|
} // namespace
|
|
|
|
MTLResourceOptions mu::toMTLResourceOption(MemoryUsage usage) {
|
|
if (usage & MemoryUsage::HOST && usage & MemoryUsage::DEVICE)
|
|
return MTLResourceStorageModeShared;
|
|
else if (hasFlag(MemoryUsage::DEVICE, usage))
|
|
return MTLResourceStorageModePrivate;
|
|
else
|
|
#if (CC_PLATFORM == CC_PLATFORM_IOS)
|
|
return MTLResourceStorageModeShared;
|
|
#else
|
|
return MTLResourceStorageModeManaged;
|
|
#endif
|
|
}
|
|
|
|
MTLLoadAction mu::toMTLLoadAction(LoadOp op) {
|
|
switch (op) {
|
|
case LoadOp::CLEAR: return MTLLoadActionClear;
|
|
case LoadOp::LOAD: return MTLLoadActionLoad;
|
|
case LoadOp::DISCARD: return MTLLoadActionDontCare;
|
|
}
|
|
}
|
|
|
|
MTLStoreAction mu::toMTLStoreAction(StoreOp op) {
|
|
switch (op) {
|
|
case StoreOp::STORE: return MTLStoreActionStore;
|
|
case StoreOp::DISCARD: return MTLStoreActionDontCare;
|
|
}
|
|
}
|
|
|
|
MTLStoreAction mu::toMTLMSAAStoreAction(StoreOp op) {
|
|
switch (op) {
|
|
case StoreOp::STORE: return MTLStoreActionMultisampleResolve;
|
|
case StoreOp::DISCARD: return MTLStoreActionStoreAndMultisampleResolve;
|
|
default: return MTLStoreActionUnknown;
|
|
}
|
|
}
|
|
|
|
MTLClearColor mu::toMTLClearColor(const Color &clearColor) {
|
|
return MTLClearColorMake(clearColor.x, clearColor.y, clearColor.z, clearColor.w);
|
|
}
|
|
|
|
MTLMultisampleDepthResolveFilter mu::toMTLDepthResolveMode(ResolveMode mode) {
|
|
switch (mode) {
|
|
case ResolveMode::SAMPLE_ZERO:
|
|
return MTLMultisampleDepthResolveFilterSample0;
|
|
case ResolveMode::MIN:
|
|
return MTLMultisampleDepthResolveFilterMin;
|
|
case ResolveMode::MAX:
|
|
return MTLMultisampleDepthResolveFilterMax;
|
|
default:
|
|
return MTLMultisampleDepthResolveFilterSample0;
|
|
}
|
|
}
|
|
|
|
API_AVAILABLE(ios(12.0))
|
|
MTLMultisampleStencilResolveFilter mu::toMTLStencilResolveMode(ResolveMode mode) {
|
|
switch (mode) {
|
|
case ResolveMode::SAMPLE_ZERO:
|
|
return MTLMultisampleStencilResolveFilterSample0;
|
|
case ResolveMode::MIN:
|
|
case ResolveMode::MAX:
|
|
default:
|
|
return MTLMultisampleStencilResolveFilterDepthResolvedSample;
|
|
}
|
|
}
|
|
|
|
MTLVertexFormat mu::toMTLVertexFormat(Format format, bool isNormalized) {
|
|
switch (format) {
|
|
case Format::R32F: return MTLVertexFormatFloat;
|
|
case Format::R32I: return MTLVertexFormatInt;
|
|
case Format::R32UI: return MTLVertexFormatUInt;
|
|
case Format::RG8: return isNormalized ? MTLVertexFormatUChar2Normalized : MTLVertexFormatUChar2;
|
|
case Format::RG8I: return isNormalized ? MTLVertexFormatChar2Normalized : MTLVertexFormatChar2;
|
|
case Format::RG16F: return MTLVertexFormatHalf2;
|
|
case Format::RG16UI: return isNormalized ? MTLVertexFormatUShort2Normalized : MTLVertexFormatUShort2;
|
|
case Format::RG16I: return isNormalized ? MTLVertexFormatShort2Normalized : MTLVertexFormatShort2;
|
|
case Format::RG32I: return MTLVertexFormatInt2;
|
|
case Format::RG32UI: return MTLVertexFormatUInt2;
|
|
case Format::RG32F: return MTLVertexFormatFloat2;
|
|
case Format::RGB8: return isNormalized ? MTLVertexFormatUChar3Normalized : MTLVertexFormatUChar3;
|
|
case Format::RGB8I: return isNormalized ? MTLVertexFormatChar3Normalized : MTLVertexFormatChar3;
|
|
case Format::RGB16I: return isNormalized ? MTLVertexFormatShort3Normalized : MTLVertexFormatShort3;
|
|
case Format::RGB16UI: return isNormalized ? MTLVertexFormatUShort3Normalized : MTLVertexFormatUShort3;
|
|
case Format::RGB16F: return MTLVertexFormatHalf3;
|
|
case Format::RGB32I: return MTLVertexFormatInt3;
|
|
case Format::RGB32UI: return MTLVertexFormatUInt3;
|
|
case Format::RGB32F: return MTLVertexFormatFloat3;
|
|
case Format::RGBA8: return isNormalized ? MTLVertexFormatUChar4Normalized : MTLVertexFormatUChar4;
|
|
case Format::RGBA8I: return isNormalized ? MTLVertexFormatChar4Normalized : MTLVertexFormatChar4;
|
|
case Format::RGBA16I: return isNormalized ? MTLVertexFormatShort4Normalized : MTLVertexFormatShort4;
|
|
case Format::RGBA16UI: return isNormalized ? MTLVertexFormatUShort4Normalized : MTLVertexFormatUShort4;
|
|
case Format::RGBA16F: return MTLVertexFormatHalf4;
|
|
case Format::RGBA32I: return MTLVertexFormatInt4;
|
|
case Format::RGBA32UI: return MTLVertexFormatUInt4;
|
|
case Format::RGBA32F: return MTLVertexFormatFloat4;
|
|
case Format::RGB10A2: return isNormalized ? MTLVertexFormatInt1010102Normalized : MTLVertexFormatInvalid;
|
|
case Format::RGB10A2UI: return isNormalized ? MTLVertexFormatUInt1010102Normalized : MTLVertexFormatInvalid;
|
|
case Format::BGRA8: {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
if (@available(iOS 11.0, *)) {
|
|
if (isNormalized) {
|
|
return MTLVertexFormatUChar4Normalized_BGRA;
|
|
} else {
|
|
CC_LOG_ERROR("Invalid metal vertex format %u", format);
|
|
return MTLVertexFormatInvalid;
|
|
}
|
|
}
|
|
#else
|
|
if (@available(macOS 10.13, *)) {
|
|
if (isNormalized) {
|
|
return MTLVertexFormatUChar4Normalized_BGRA;
|
|
} else {
|
|
CC_LOG_ERROR("Invalid metal vertex format %u", format);
|
|
return MTLVertexFormatInvalid;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
default: {
|
|
CC_LOG_ERROR("Invalid vertex format %u", format);
|
|
return MTLVertexFormatInvalid;
|
|
}
|
|
}
|
|
}
|
|
|
|
Format mu::convertGFXPixelFormat(Format format) {
|
|
switch (format) {
|
|
case Format::RGB8: return Format::RGBA8;
|
|
case Format::RGB32F: return Format::RGBA32F;
|
|
default: return format;
|
|
}
|
|
}
|
|
|
|
MTLPixelFormat mu::toMTLPixelFormat(Format format) {
|
|
switch (format) {
|
|
case Format::A8: return MTLPixelFormatA8Unorm;
|
|
case Format::R8: return MTLPixelFormatR8Unorm;
|
|
case Format::R8SN: return MTLPixelFormatR8Snorm;
|
|
case Format::R8UI: return MTLPixelFormatR8Uint;
|
|
case Format::R16F: return MTLPixelFormatR16Float;
|
|
case Format::R32F: return MTLPixelFormatR32Float;
|
|
case Format::R32UI: return MTLPixelFormatR32Uint;
|
|
case Format::R32I: return MTLPixelFormatR32Sint;
|
|
|
|
case Format::RG8: return MTLPixelFormatRG8Unorm;
|
|
case Format::RG8SN: return MTLPixelFormatRG8Snorm;
|
|
case Format::RG8UI: return MTLPixelFormatRG8Uint;
|
|
case Format::RG8I: return MTLPixelFormatRG8Sint;
|
|
case Format::RG16F: return MTLPixelFormatRG16Float;
|
|
case Format::RG16UI: return MTLPixelFormatRG16Uint;
|
|
case Format::RG16I:
|
|
return MTLPixelFormatRG16Sint;
|
|
|
|
// case Format::RGB8SN: return MTLPixelFormatRGBA8Snorm;
|
|
// case Format::RGB8UI: return MTLPixelFormatRGBA8Uint;
|
|
// case Format::RGB8I: return MTLPixelFormatRGBA8Sint;
|
|
// case Format::RGB16F: return MTLPixelFormatRGBA16Float;
|
|
// case Format::RGB16UI: return MTLPixelFormatRGBA16Uint;
|
|
// case Format::RGB16I: return MTLPixelFormatRGBA16Sint;
|
|
// case Format::RGB32F: return MTLPixelFormatRGBA32Float;
|
|
// case Format::RGB32UI: return MTLPixelFormatRGBA32Uint;
|
|
// case Format::RGB32I: return MTLPixelFormatRGBA32Sint;
|
|
// case Format::SRGB8: return MTLPixelFormatRGBA8Unorm_sRGB;
|
|
|
|
case Format::RGBA8: return MTLPixelFormatRGBA8Unorm;
|
|
case Format::RGBA8SN: return MTLPixelFormatRGBA8Snorm;
|
|
case Format::RGBA8UI: return MTLPixelFormatRGBA8Uint;
|
|
case Format::RGBA8I: return MTLPixelFormatRGBA8Sint;
|
|
case Format::SRGB8_A8: return MTLPixelFormatRGBA8Unorm_sRGB;
|
|
case Format::RGBA16F: return MTLPixelFormatRGBA16Float;
|
|
case Format::RGBA16UI: return MTLPixelFormatRGBA16Uint;
|
|
case Format::RGBA16I: return MTLPixelFormatRGBA16Sint;
|
|
case Format::RGBA32F: return MTLPixelFormatRGBA32Float;
|
|
case Format::RGBA32UI: return MTLPixelFormatRGBA32Uint;
|
|
case Format::RGBA32I: return MTLPixelFormatRGBA32Sint;
|
|
case Format::BGRA8:
|
|
return MTLPixelFormatBGRA8Unorm;
|
|
|
|
// Should convert.
|
|
// case Format::R5G6B5: return MTLPixelFormatB5G6R5Unorm;
|
|
// case Format::RGB5A1: return MTLPixelFormatBGR5A1Unorm;
|
|
// case Format::RGBA4: return MTLPixelFormatABGR4Unorm;
|
|
// case Format::RGB10A2: return MTLPixelFormatBGR10A2Unorm;
|
|
case Format::RGB9E5: return MTLPixelFormatRGB9E5Float;
|
|
case Format::RGB10A2UI: return MTLPixelFormatRGB10A2Uint;
|
|
case Format::R11G11B10F: return MTLPixelFormatRG11B10Float;
|
|
case Format::DEPTH: return MTLPixelFormatDepth32Float;
|
|
#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
// FIXME: works fine on imac, but invalid pixel format on intel macbook.
|
|
//case Format::DEPTH_STENCIL: return MTLPixelFormatDepth24Unorm_Stencil8;
|
|
case Format::DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8;
|
|
case Format::BC1:
|
|
case Format::BC1_ALPHA: return MTLPixelFormatBC1_RGBA;
|
|
case Format::BC1_SRGB_ALPHA: return MTLPixelFormatBC1_RGBA_sRGB;
|
|
case Format::BC2: return MTLPixelFormatBC2_RGBA;
|
|
case Format::BC2_SRGB: return MTLPixelFormatBC2_RGBA_sRGB;
|
|
case Format::BC3: return MTLPixelFormatBC3_RGBA;
|
|
case Format::BC3_SRGB: return MTLPixelFormatBC3_RGBA_sRGB;
|
|
case Format::BC4: return MTLPixelFormatBC4_RUnorm;
|
|
case Format::BC4_SNORM: return MTLPixelFormatBC4_RSnorm;
|
|
#else
|
|
case Format::DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8;
|
|
case Format::ASTC_RGBA_4X4: return MTLPixelFormatASTC_4x4_LDR;
|
|
case Format::ASTC_RGBA_5X4: return MTLPixelFormatASTC_5x4_LDR;
|
|
case Format::ASTC_RGBA_5X5: return MTLPixelFormatASTC_5x5_LDR;
|
|
case Format::ASTC_RGBA_6X5: return MTLPixelFormatASTC_6x5_LDR;
|
|
case Format::ASTC_RGBA_6X6: return MTLPixelFormatASTC_6x6_LDR;
|
|
case Format::ASTC_RGBA_8X5: return MTLPixelFormatASTC_8x5_LDR;
|
|
case Format::ASTC_RGBA_8X6: return MTLPixelFormatASTC_8x6_LDR;
|
|
case Format::ASTC_RGBA_8X8: return MTLPixelFormatASTC_8x8_LDR;
|
|
case Format::ASTC_RGBA_10X5: return MTLPixelFormatASTC_10x5_LDR;
|
|
case Format::ASTC_RGBA_10X6: return MTLPixelFormatASTC_10x6_LDR;
|
|
case Format::ASTC_RGBA_10X8: return MTLPixelFormatASTC_10x8_LDR;
|
|
case Format::ASTC_RGBA_10X10: return MTLPixelFormatASTC_10x10_LDR;
|
|
case Format::ASTC_RGBA_12X10: return MTLPixelFormatASTC_12x10_LDR;
|
|
case Format::ASTC_RGBA_12X12: return MTLPixelFormatASTC_12x12_LDR;
|
|
|
|
case Format::ASTC_SRGBA_4X4: return MTLPixelFormatASTC_4x4_sRGB;
|
|
case Format::ASTC_SRGBA_5X4: return MTLPixelFormatASTC_5x4_sRGB;
|
|
case Format::ASTC_SRGBA_5X5: return MTLPixelFormatASTC_5x5_sRGB;
|
|
case Format::ASTC_SRGBA_6X5: return MTLPixelFormatASTC_6x5_sRGB;
|
|
case Format::ASTC_SRGBA_6X6: return MTLPixelFormatASTC_6x6_sRGB;
|
|
case Format::ASTC_SRGBA_8X5: return MTLPixelFormatASTC_8x5_sRGB;
|
|
case Format::ASTC_SRGBA_8X6: return MTLPixelFormatASTC_8x6_sRGB;
|
|
case Format::ASTC_SRGBA_8X8: return MTLPixelFormatASTC_8x8_sRGB;
|
|
case Format::ASTC_SRGBA_10X5: return MTLPixelFormatASTC_10x5_sRGB;
|
|
case Format::ASTC_SRGBA_10X6: return MTLPixelFormatASTC_10x6_sRGB;
|
|
case Format::ASTC_SRGBA_10X8: return MTLPixelFormatASTC_10x8_sRGB;
|
|
case Format::ASTC_SRGBA_10X10: return MTLPixelFormatASTC_10x10_sRGB;
|
|
case Format::ASTC_SRGBA_12X10: return MTLPixelFormatASTC_12x10_sRGB;
|
|
case Format::ASTC_SRGBA_12X12: return MTLPixelFormatASTC_12x12_sRGB;
|
|
|
|
case Format::ETC2_RGB8: return MTLPixelFormatETC2_RGB8;
|
|
case Format::ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB;
|
|
case Format::ETC2_RGB8_A1: return MTLPixelFormatETC2_RGB8A1;
|
|
case Format::ETC2_SRGB8_A1: return MTLPixelFormatETC2_RGB8A1_sRGB;
|
|
case Format::ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8;
|
|
case Format::ETC2_SRGB8_A8: return MTLPixelFormatEAC_RGBA8_sRGB;
|
|
|
|
case Format::EAC_R11: return MTLPixelFormatEAC_R11Unorm;
|
|
case Format::EAC_R11SN: return MTLPixelFormatEAC_R11Snorm;
|
|
case Format::EAC_RG11: return MTLPixelFormatEAC_RG11Unorm;
|
|
case Format::EAC_RG11SN: return MTLPixelFormatEAC_RG11Snorm;
|
|
|
|
case Format::PVRTC_RGB2: return MTLPixelFormatPVRTC_RGB_2BPP;
|
|
case Format::PVRTC_RGBA2: return MTLPixelFormatPVRTC_RGBA_2BPP;
|
|
case Format::PVRTC_RGB4: return MTLPixelFormatPVRTC_RGB_4BPP;
|
|
case Format::PVRTC_RGBA4: return MTLPixelFormatPVRTC_RGBA_4BPP;
|
|
#endif
|
|
default: break;
|
|
}
|
|
CC_LOG_ERROR("Invalid pixel format %u", format);
|
|
return MTLPixelFormatInvalid;
|
|
}
|
|
|
|
MTLColorWriteMask mu::toMTLColorWriteMask(ColorMask mask) {
|
|
switch (mask) {
|
|
case ColorMask::R: return MTLColorWriteMaskRed;
|
|
case ColorMask::G: return MTLColorWriteMaskGreen;
|
|
case ColorMask::B: return MTLColorWriteMaskBlue;
|
|
case ColorMask::A: return MTLColorWriteMaskAlpha;
|
|
case ColorMask::ALL: return MTLColorWriteMaskAll;
|
|
default: return MTLColorWriteMaskNone;
|
|
}
|
|
}
|
|
|
|
MTLBlendFactor mu::toMTLBlendFactor(BlendFactor factor) {
|
|
switch (factor) {
|
|
case BlendFactor::ZERO: return MTLBlendFactorZero;
|
|
case BlendFactor::ONE: return MTLBlendFactorOne;
|
|
case BlendFactor::SRC_ALPHA: return MTLBlendFactorSourceAlpha;
|
|
case BlendFactor::DST_ALPHA: return MTLBlendFactorDestinationAlpha;
|
|
case BlendFactor::ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
|
|
case BlendFactor::ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
|
|
case BlendFactor::SRC_COLOR: return MTLBlendFactorSourceColor;
|
|
case BlendFactor::DST_COLOR: return MTLBlendFactorDestinationColor;
|
|
case BlendFactor::ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
|
|
case BlendFactor::ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
|
|
case BlendFactor::SRC_ALPHA_SATURATE: return MTLBlendFactorSourceAlphaSaturated;
|
|
default: {
|
|
CC_LOG_ERROR("Unsupported blend factor %u", (uint32_t)factor);
|
|
return MTLBlendFactorZero;
|
|
}
|
|
}
|
|
}
|
|
|
|
MTLBlendOperation mu::toMTLBlendOperation(BlendOp op) {
|
|
switch (op) {
|
|
case BlendOp::ADD: return MTLBlendOperationAdd;
|
|
case BlendOp::SUB: return MTLBlendOperationSubtract;
|
|
case BlendOp::REV_SUB: return MTLBlendOperationReverseSubtract;
|
|
case BlendOp::MIN: return MTLBlendOperationMin;
|
|
case BlendOp::MAX: return MTLBlendOperationMax;
|
|
}
|
|
}
|
|
|
|
MTLCullMode mu::toMTLCullMode(CullMode mode) {
|
|
switch (mode) {
|
|
case CullMode::FRONT: return MTLCullModeFront;
|
|
case CullMode::BACK: return MTLCullModeBack;
|
|
case CullMode::NONE: return MTLCullModeNone;
|
|
}
|
|
}
|
|
|
|
MTLWinding mu::toMTLWinding(bool isFrontFaceCCW) {
|
|
if (isFrontFaceCCW)
|
|
return MTLWindingCounterClockwise;
|
|
else
|
|
return MTLWindingClockwise;
|
|
}
|
|
|
|
MTLViewport mu::toMTLViewport(const Viewport &viewport) {
|
|
MTLViewport mtlViewport;
|
|
mtlViewport.originX = viewport.left;
|
|
mtlViewport.originY = viewport.top;
|
|
mtlViewport.width = viewport.width;
|
|
mtlViewport.height = viewport.height;
|
|
mtlViewport.znear = viewport.minDepth;
|
|
mtlViewport.zfar = viewport.maxDepth;
|
|
|
|
return mtlViewport;
|
|
}
|
|
|
|
MTLScissorRect mu::toMTLScissorRect(const Rect &rect) {
|
|
MTLScissorRect scissorRect;
|
|
scissorRect.x = static_cast<NSUInteger>(rect.x);
|
|
scissorRect.y = static_cast<NSUInteger>(rect.y);
|
|
scissorRect.width = rect.width;
|
|
scissorRect.height = rect.height;
|
|
|
|
return scissorRect;
|
|
}
|
|
|
|
MTLTriangleFillMode mu::toMTLTriangleFillMode(PolygonMode mode) {
|
|
switch (mode) {
|
|
case PolygonMode::FILL: return MTLTriangleFillModeFill;
|
|
case PolygonMode::LINE: return MTLTriangleFillModeLines;
|
|
case PolygonMode::POINT: {
|
|
CC_LOG_WARNING("Metal doesn't support PolygonMode::POINT, translate to PolygonMode::LINE.");
|
|
return MTLTriangleFillModeLines;
|
|
}
|
|
}
|
|
}
|
|
|
|
MTLDepthClipMode mu::toMTLDepthClipMode(bool isClip) {
|
|
if (isClip)
|
|
return MTLDepthClipModeClip;
|
|
else
|
|
return MTLDepthClipModeClamp;
|
|
}
|
|
|
|
MTLCompareFunction mu::toMTLCompareFunction(ComparisonFunc func) {
|
|
switch (func) {
|
|
case ComparisonFunc::NEVER: return MTLCompareFunctionNever;
|
|
case ComparisonFunc::LESS: return MTLCompareFunctionLess;
|
|
case ComparisonFunc::EQUAL: return MTLCompareFunctionEqual;
|
|
case ComparisonFunc::LESS_EQUAL: return MTLCompareFunctionLessEqual;
|
|
case ComparisonFunc::GREATER: return MTLCompareFunctionGreater;
|
|
case ComparisonFunc::NOT_EQUAL: return MTLCompareFunctionNotEqual;
|
|
case ComparisonFunc::GREATER_EQUAL: return MTLCompareFunctionGreaterEqual;
|
|
case ComparisonFunc::ALWAYS: return MTLCompareFunctionAlways;
|
|
default: return MTLCompareFunctionNever;
|
|
}
|
|
}
|
|
|
|
MTLStencilOperation mu::toMTLStencilOperation(StencilOp op) {
|
|
switch (op) {
|
|
case StencilOp::ZERO: return MTLStencilOperationZero;
|
|
case StencilOp::KEEP: return MTLStencilOperationKeep;
|
|
case StencilOp::REPLACE: return MTLStencilOperationReplace;
|
|
case StencilOp::INCR: return MTLStencilOperationIncrementClamp;
|
|
case StencilOp::DECR: return MTLStencilOperationDecrementClamp;
|
|
case StencilOp::INVERT: return MTLStencilOperationInvert;
|
|
case StencilOp::INCR_WRAP: return MTLStencilOperationIncrementWrap;
|
|
case StencilOp::DECR_WRAP: return MTLStencilOperationDecrementWrap;
|
|
}
|
|
}
|
|
|
|
MTLPrimitiveType mu::toMTLPrimitiveType(PrimitiveMode mode) {
|
|
switch (mode) {
|
|
case PrimitiveMode::POINT_LIST: return MTLPrimitiveTypePoint;
|
|
case PrimitiveMode::LINE_LIST: return MTLPrimitiveTypeLine;
|
|
case PrimitiveMode::LINE_STRIP: return MTLPrimitiveTypeLineStrip;
|
|
case PrimitiveMode::TRIANGLE_LIST: return MTLPrimitiveTypeTriangle;
|
|
case PrimitiveMode::TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip;
|
|
|
|
case PrimitiveMode::LINE_LOOP: {
|
|
CC_LOG_ERROR("Metal doesn't support PrimitiveMode::LINE_LOOP. Translate to PrimitiveMode::LINE_LIST.");
|
|
return MTLPrimitiveTypeLine;
|
|
}
|
|
default: {
|
|
//TODO: how to support these mode?
|
|
CC_ABORT();
|
|
return MTLPrimitiveTypeTriangle;
|
|
}
|
|
}
|
|
}
|
|
|
|
MTLTextureUsage mu::toMTLTextureUsage(TextureUsage usage) {
|
|
if (usage == TextureUsage::NONE)
|
|
return MTLTextureUsageUnknown;
|
|
|
|
MTLTextureUsage ret = MTLTextureUsageUnknown;
|
|
if (hasFlag(usage, TextureUsage::TRANSFER_SRC))
|
|
ret |= MTLTextureUsageShaderRead;
|
|
if (hasFlag(usage, TextureUsage::TRANSFER_DST))
|
|
ret |= MTLTextureUsageShaderWrite;
|
|
if (hasFlag(usage, TextureUsage::SAMPLED) || hasFlag(usage, TextureUsageBit::INPUT_ATTACHMENT))
|
|
ret |= MTLTextureUsageShaderRead;
|
|
if (hasFlag(usage, TextureUsage::STORAGE))
|
|
ret |= MTLTextureUsageShaderWrite;
|
|
if (hasFlag(usage, TextureUsage::COLOR_ATTACHMENT) ||
|
|
hasFlag(usage, TextureUsage::DEPTH_STENCIL_ATTACHMENT)) {
|
|
ret |= MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
MTLTextureType mu::toMTLTextureType(TextureType type) {
|
|
switch (type) {
|
|
case TextureType::TEX1D: return MTLTextureType1D;
|
|
case TextureType::TEX2D: return MTLTextureType2D;
|
|
case TextureType::TEX3D: return MTLTextureType3D;
|
|
case TextureType::CUBE: return MTLTextureTypeCube;
|
|
case TextureType::TEX1D_ARRAY: return MTLTextureType1DArray;
|
|
case TextureType::TEX2D_ARRAY: return MTLTextureType2DArray;
|
|
}
|
|
}
|
|
|
|
NSUInteger mu::toMTLSampleCount(SampleCount count) {
|
|
return static_cast<NSUInteger>(count);
|
|
}
|
|
|
|
MTLSamplerAddressMode mu::toMTLSamplerAddressMode(Address mode) {
|
|
switch (mode) {
|
|
case Address::WRAP: return MTLSamplerAddressModeRepeat;
|
|
case Address::MIRROR: return MTLSamplerAddressModeMirrorRepeat;
|
|
case Address::CLAMP: return MTLSamplerAddressModeClampToEdge;
|
|
case Address::BORDER: {
|
|
#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
return MTLSamplerAddressModeClampToBorderColor;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return MTLSamplerAddressModeClampToEdge;
|
|
}
|
|
|
|
int mu::toMTLSamplerBorderColor(const Color &color) {
|
|
#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
float diff = color.x - 0.5f;
|
|
if (math::isEqualF(color.w, 0.f))
|
|
return MTLSamplerBorderColorTransparentBlack;
|
|
else if (math::isEqualF(diff, 0.f))
|
|
return MTLSamplerBorderColorOpaqueBlack;
|
|
else
|
|
return MTLSamplerBorderColorOpaqueWhite;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
MTLSamplerMinMagFilter mu::toMTLSamplerMinMagFilter(Filter filter) {
|
|
switch (filter) {
|
|
case Filter::LINEAR:
|
|
case Filter::ANISOTROPIC:
|
|
return MTLSamplerMinMagFilterLinear;
|
|
default:
|
|
return MTLSamplerMinMagFilterNearest;
|
|
}
|
|
}
|
|
|
|
MTLSamplerMipFilter mu::toMTLSamplerMipFilter(Filter filter) {
|
|
switch (filter) {
|
|
case Filter::NONE: return MTLSamplerMipFilterNotMipmapped;
|
|
case Filter::LINEAR:
|
|
case Filter::ANISOTROPIC:
|
|
return MTLSamplerMipFilterLinear;
|
|
case Filter::POINT: return MTLSamplerMipFilterNearest;
|
|
}
|
|
}
|
|
|
|
bool mu::isImageBlockSupported() {
|
|
//implicit imageblocks
|
|
if (!mu::isFramebufferFetchSupported()) {
|
|
return false;
|
|
}
|
|
#if (CC_PLATFORM == CC_PLATFORM_IOS) //|| TARGET_CPU_ARM64
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool mu::isFramebufferFetchSupported() {
|
|
return true;
|
|
}
|
|
|
|
ccstd::string mu::spirv2MSL(const uint32_t *ir, size_t word_count,
|
|
ShaderStageFlagBit shaderType,
|
|
CCMTLGPUShader *gpuShader,
|
|
RenderPass* renderPass,
|
|
uint32_t subpassIndex) {
|
|
CCMTLDevice *device = CCMTLDevice::getInstance();
|
|
spirv_cross::CompilerMSL msl(ir, word_count);
|
|
|
|
// The SPIR-V is now parsed, and we can perform reflection on it.
|
|
auto executionModel = msl.get_execution_model();
|
|
auto active = msl.get_active_interface_variables();
|
|
spirv_cross::ShaderResources resources = msl.get_shader_resources(active);
|
|
msl.set_enabled_interface_variables(std::move(active));
|
|
|
|
// Set some options.
|
|
spirv_cross::CompilerMSL::Options options;
|
|
options.enable_decoration_binding = true;
|
|
#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
options.platform = spirv_cross::CompilerMSL::Options::Platform::macOS;
|
|
#elif (CC_PLATFORM == CC_PLATFORM_IOS)
|
|
options.platform = spirv_cross::CompilerMSL::Options::Platform::iOS;
|
|
#endif
|
|
options.emulate_subgroups = true;
|
|
options.pad_fragment_output_components = true;
|
|
// fully support
|
|
options.set_msl_version(2, 0, 0);
|
|
if (isFramebufferFetchSupported()) {
|
|
options.use_framebuffer_fetch_subpasses = true;
|
|
#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
options.set_msl_version(2, 3, 0);
|
|
#endif
|
|
}
|
|
msl.set_msl_options(options);
|
|
|
|
// TODO: bindings from shader just kind of validation, cannot be directly input
|
|
// Get all uniform buffers in the shader.
|
|
uint32_t maxBufferBindingIndex = device->getMaximumBufferBindingIndex();
|
|
for (const auto &ubo : resources.uniform_buffers) {
|
|
auto set = msl.get_decoration(ubo.id, spv::DecorationDescriptorSet);
|
|
auto binding = msl.get_decoration(ubo.id, spv::DecorationBinding);
|
|
auto size = msl.get_declared_struct_size(msl.get_type(ubo.base_type_id));
|
|
|
|
if (binding >= maxBufferBindingIndex) {
|
|
CC_LOG_ERROR("Implementation limits: %s binding at %d, should not use more than %d entries in the buffer argument table", ubo.name.c_str(), binding, maxBufferBindingIndex);
|
|
}
|
|
|
|
uint32_t fakeHash = set * 128 + binding;
|
|
spirv_cross::MSLResourceBinding newBinding;
|
|
newBinding.stage = executionModel;
|
|
auto mappedBinding = gpuShader->bufferIndex;
|
|
newBinding.desc_set = set;
|
|
newBinding.binding = binding;
|
|
if (gpuShader->resourceBinding.find(fakeHash) == gpuShader->resourceBinding.end()) {
|
|
newBinding.msl_buffer = mappedBinding;
|
|
gpuShader->blocks[fakeHash] = {ubo.name, set, binding, mappedBinding, shaderType, size};
|
|
gpuShader->resourceBinding[fakeHash] = {mappedBinding, 0, 0};
|
|
++gpuShader->bufferIndex;
|
|
} else {
|
|
gpuShader->blocks[fakeHash].stages |= shaderType;
|
|
gpuShader->resourceBinding[fakeHash].bufferBinding = gpuShader->blocks[fakeHash].mappedBinding;
|
|
newBinding.msl_sampler = gpuShader->resourceBinding[fakeHash].samplerBinding;
|
|
newBinding.msl_texture = gpuShader->resourceBinding[fakeHash].textureBinding;
|
|
newBinding.msl_buffer = gpuShader->resourceBinding[fakeHash].bufferBinding;
|
|
}
|
|
msl.add_msl_resource_binding(newBinding);
|
|
}
|
|
|
|
for (const auto &ubo : resources.storage_buffers) {
|
|
auto set = msl.get_decoration(ubo.id, spv::DecorationDescriptorSet);
|
|
auto binding = msl.get_decoration(ubo.id, spv::DecorationBinding);
|
|
auto size = msl.get_declared_struct_size(msl.get_type(ubo.base_type_id));
|
|
|
|
if (binding >= maxBufferBindingIndex) {
|
|
CC_LOG_ERROR("Implementation limits: %s binding at %d, should not use more than %d entries in the buffer argument table", ubo.name.c_str(), binding, maxBufferBindingIndex);
|
|
}
|
|
|
|
uint32_t fakeHash = set * 128 + binding;
|
|
spirv_cross::MSLResourceBinding newBinding;
|
|
newBinding.stage = executionModel;
|
|
auto mappedBinding = gpuShader->bufferIndex;
|
|
newBinding.desc_set = set;
|
|
newBinding.binding = binding;
|
|
newBinding.msl_buffer = mappedBinding;
|
|
if (gpuShader->resourceBinding.find(fakeHash) == gpuShader->resourceBinding.end()) {
|
|
gpuShader->blocks[fakeHash] = {ubo.name, set, binding, mappedBinding, shaderType, size};
|
|
gpuShader->resourceBinding[fakeHash] = {mappedBinding, 0, 0};
|
|
++gpuShader->bufferIndex;
|
|
} else {
|
|
auto mappedBinding = gpuShader->blocks[fakeHash].mappedBinding;
|
|
gpuShader->blocks[fakeHash].stages |= shaderType;
|
|
gpuShader->resourceBinding[fakeHash].bufferBinding = mappedBinding;
|
|
newBinding.msl_sampler = gpuShader->resourceBinding[fakeHash].samplerBinding;
|
|
newBinding.msl_texture = gpuShader->resourceBinding[fakeHash].textureBinding;
|
|
newBinding.msl_buffer = gpuShader->resourceBinding[fakeHash].bufferBinding;
|
|
}
|
|
msl.add_msl_resource_binding(newBinding);
|
|
}
|
|
|
|
//TODO: coulsonwang, need to set sampler binding explicitly
|
|
if (resources.sampled_images.size() > device->getMaximumSamplerUnits()) {
|
|
CC_LOG_ERROR("Implementation limits: Should not use more than %d entries in the sampler state argument table", device->getMaximumSamplerUnits());
|
|
return "";
|
|
}
|
|
|
|
// avoid conflict index with input attachments.
|
|
const uint8_t rtOffsets = executionModel == spv::ExecutionModelFragment ? resources.subpass_inputs.size() : 0;
|
|
for (const auto &sampler : resources.sampled_images) {
|
|
auto set = msl.get_decoration(sampler.id, spv::DecorationDescriptorSet);
|
|
auto binding = msl.get_decoration(sampler.id, spv::DecorationBinding);
|
|
int size = 1;
|
|
const spirv_cross::SPIRType &type = msl.get_type(sampler.type_id);
|
|
if (type.array_size_literal[0]) {
|
|
size = type.array[0];
|
|
}
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
auto mappedBinding = gpuShader->samplerIndex + rtOffsets;
|
|
uint32_t fakeHash = set * 128 + binding;
|
|
spirv_cross::MSLResourceBinding newBinding;
|
|
newBinding.stage = executionModel;
|
|
newBinding.desc_set = set;
|
|
newBinding.binding = binding + i;
|
|
newBinding.msl_texture = mappedBinding;
|
|
newBinding.msl_sampler = gpuShader->samplerIndex;
|
|
|
|
if(gpuShader->resourceBinding.find(fakeHash) == gpuShader->resourceBinding.end()) {
|
|
gpuShader->resourceBinding[fakeHash] = {0, mappedBinding, mappedBinding};
|
|
gpuShader->samplers[mappedBinding] = {sampler.name, set, binding, newBinding.msl_texture, newBinding.msl_sampler, shaderType};
|
|
++gpuShader->samplerIndex;
|
|
} else {
|
|
gpuShader->resourceBinding[fakeHash].samplerBinding = mappedBinding;
|
|
gpuShader->resourceBinding[fakeHash].textureBinding = mappedBinding;
|
|
gpuShader->samplers[mappedBinding].stages |= shaderType;
|
|
newBinding.msl_buffer = gpuShader->resourceBinding[fakeHash].bufferBinding;
|
|
newBinding.msl_texture = gpuShader->resourceBinding[fakeHash].textureBinding;
|
|
newBinding.msl_sampler = gpuShader->resourceBinding[fakeHash].samplerBinding;
|
|
}
|
|
msl.add_msl_resource_binding(newBinding);
|
|
}
|
|
}
|
|
|
|
if (executionModel == spv::ExecutionModelFragment) {
|
|
auto* ccRenderPass = static_cast<CCMTLRenderPass*>(renderPass);
|
|
const auto& readBuffer = ccRenderPass ? ccRenderPass->getReadBuffer(subpassIndex) : ccstd::vector<uint32_t>{};
|
|
if (!resources.subpass_inputs.empty()) {
|
|
// gpuShader->inputs.resize(resources.subpass_inputs.size());
|
|
for (size_t i = 0; i < resources.subpass_inputs.size(); i++) {
|
|
const auto &attachment = resources.subpass_inputs[i];
|
|
auto inputIndex = msl.get_decoration(attachment.id, spv::DecorationInputAttachmentIndex);
|
|
auto loc = inputIndex >= readBuffer.size() ? inputIndex : readBuffer[inputIndex];
|
|
// depth stencil input not support in metal
|
|
CC_ASSERT(loc != renderPass->getColorAttachments().size());
|
|
auto& input = gpuShader->inputs.emplace_back();
|
|
input.name = attachment.name;
|
|
msl.set_decoration(attachment.id, spv::DecorationInputAttachmentIndex, loc);
|
|
}
|
|
}
|
|
|
|
gpuShader->outputs.resize(resources.stage_outputs.size());
|
|
const auto& drawBuffer = renderPass ? ccRenderPass->getDrawBuffer(subpassIndex) : ccstd::vector<uint32_t>{};
|
|
for (size_t i = 0; i < resources.stage_outputs.size(); i++) {
|
|
const auto &stageOutput = resources.stage_outputs[i];
|
|
auto set = msl.get_decoration(stageOutput.id, spv::DecorationDescriptorSet);
|
|
auto id = msl.get_decoration(stageOutput.id, spv::DecorationLocation);
|
|
auto loc = id >= drawBuffer.size() ? id : drawBuffer[id];
|
|
msl.set_decoration(stageOutput.id, spv::DecorationLocation, loc);
|
|
|
|
gpuShader->outputs[i].name = stageOutput.name;
|
|
gpuShader->outputs[i].set = set;
|
|
gpuShader->outputs[i].binding = loc;
|
|
}
|
|
} else if (executionModel == spv::ExecutionModelGLCompute) {
|
|
spirv_cross::SpecializationConstant x, y, z;
|
|
auto workGroupID = msl.get_work_group_size_specialization_constants(x, y, z);
|
|
const auto& workGroupSizeSpv = msl.get_constant(workGroupID);
|
|
const auto& workGroupSize = workGroupSizeSpv.vector().r;
|
|
gpuShader->workGroupSize[0] = workGroupSize[0].u32;
|
|
gpuShader->workGroupSize[1] = workGroupSize[1].u32;
|
|
gpuShader->workGroupSize[2] = workGroupSize[2].u32;
|
|
}
|
|
|
|
// Compile to MSL, ready to give to metal driver.
|
|
ccstd::string output = msl.compile();
|
|
if (!output.size()) {
|
|
CC_LOG_ERROR("Compile to MSL failed.");
|
|
CC_LOG_ERROR("%s", output.c_str());
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
const uint8_t *mu::convertRGB8ToRGBA8(const uint8_t *source, uint32_t length) {
|
|
uint32_t finalLength = length * 4;
|
|
uint8_t *out = (uint8_t *)CC_MALLOC(finalLength);
|
|
if (!out) {
|
|
CC_LOG_WARNING("Failed to alloc memory in convertRGB8ToRGBA8().");
|
|
return source;
|
|
}
|
|
|
|
const uint8_t *src = source;
|
|
uint8_t *dst = out;
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = 255;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
const uint8_t *mu::convertRGB32FToRGBA32F(const uint8_t *source, uint32_t length) {
|
|
uint32_t finalLength = length * sizeof(float) * 4;
|
|
uint8_t *out = (uint8_t *)CC_MALLOC(finalLength);
|
|
if (!out) {
|
|
CC_LOG_WARNING("Failed to alloc memory in convertRGB32FToRGBA32F().");
|
|
return source;
|
|
}
|
|
|
|
const float *src = reinterpret_cast<const float *>(source);
|
|
float *dst = reinterpret_cast<float *>(out);
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = *src++;
|
|
*dst++ = 1.0f;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
NSUInteger mu::highestSupportedFeatureSet(id<MTLDevice> device) {
|
|
NSUInteger maxKnownFeatureSet;
|
|
NSUInteger defaultFeatureSet;
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
defaultFeatureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
|
|
if (@available(iOS 12.0, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_iOS_GPUFamily4_v2;
|
|
} else if (@available(iOS 11.0, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_iOS_GPUFamily4_v1;
|
|
} else if (@available(iOS 10.0, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_iOS_GPUFamily3_v2;
|
|
} else if (@available(iOS 9.0, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
|
|
} else {
|
|
maxKnownFeatureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
|
|
}
|
|
#else
|
|
defaultFeatureSet = MTLFeatureSet_macOS_GPUFamily1_v1;
|
|
if (@available(macOS 10.14, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_macOS_GPUFamily2_v1;
|
|
} else if (@available(macOS 10.13, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_macOS_GPUFamily1_v3;
|
|
} else if (@available(macOS 10.12, *)) {
|
|
maxKnownFeatureSet = MTLFeatureSet_macOS_GPUFamily1_v2;
|
|
} else {
|
|
maxKnownFeatureSet = MTLFeatureSet_macOS_GPUFamily1_v1;
|
|
}
|
|
#endif
|
|
for (int featureSet = static_cast<int>(maxKnownFeatureSet); featureSet >= 0; --featureSet) {
|
|
if ([device supportsFeatureSet:MTLFeatureSet(featureSet)]) {
|
|
return static_cast<NSUInteger>(featureSet);
|
|
}
|
|
}
|
|
return defaultFeatureSet;
|
|
}
|
|
|
|
uint32_t mu::getGPUFamily(MTLFeatureSet featureSet) {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
return static_cast<uint32_t>(getIOSGPUFamily(featureSet));
|
|
#else
|
|
return static_cast<uint32_t>(getMacGPUFamily(featureSet));
|
|
#endif
|
|
}
|
|
|
|
uint32_t mu::getMaxVertexAttributes(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 31;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxUniformBufferBindings(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return 31;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 14;
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t mu::getMaxEntriesInBufferArgumentTable(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 31;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxEntriesInTextureArgumentTable(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
return 31;
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
return 96;
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 128;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxEntriesInSamplerStateArgumentTable(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 16;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxTexture2DWidthHeight(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
return 8192;
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 16384;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxCubeMapTextureWidthHeight(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
return 8192;
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 16384;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxThreadsPerGroup(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
return 512;
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 1024;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMaxColorRenderTarget(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
return 4;
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getMinBufferOffsetAlignment(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
#ifdef TARGET_OS_SIMULATOR
|
|
return 256;
|
|
#else
|
|
return 4; //4 Bytes
|
|
#endif
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return 256; //256 Bytes
|
|
}
|
|
}
|
|
|
|
bool mu::isPVRTCSuppported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return true;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool mu::isEAC_ETCCSuppported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return true;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool mu::isASTCSuppported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
return false;
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return true;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool mu::isBCSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return false;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isColorBufferFloatSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isColorBufferHalfFloatSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isLinearTextureSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isUISamplerSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return false;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isRGB10A2UIStorageSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
return false;
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isDDepthStencilFilterSupported(uint32_t family) {
|
|
switch (static_cast<GPUFamily>(family)) {
|
|
case GPUFamily::Apple1:
|
|
case GPUFamily::Apple2:
|
|
case GPUFamily::Apple3:
|
|
case GPUFamily::Apple4:
|
|
case GPUFamily::Apple5:
|
|
case GPUFamily::Apple6:
|
|
return false;
|
|
case GPUFamily::Mac1:
|
|
case GPUFamily::Mac2:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool mu::isIndirectCommandBufferSupported(MTLFeatureSet featureSet) {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
if (@available(iOS 12.0, *)) {
|
|
return featureSet >= MTLFeatureSet_iOS_GPUFamily3_v4;
|
|
}
|
|
#else
|
|
if (@available(macOS 10.14, *)) {
|
|
return featureSet >= MTLFeatureSet_macOS_GPUFamily2_v1;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
bool mu::isDepthStencilFormatSupported(id<MTLDevice> device, Format format, uint32_t family) {
|
|
return true;
|
|
// GPUFamily gpuFamily = static_cast<GPUFamily>(family);
|
|
// switch (format) {
|
|
// case Format::D16:
|
|
// switch (gpuFamily) {
|
|
// case GPUFamily::Apple1:
|
|
// case GPUFamily::Apple2:
|
|
// case GPUFamily::Apple3:
|
|
// case GPUFamily::Apple4:
|
|
// case GPUFamily::Apple5:
|
|
// case GPUFamily::Apple6:
|
|
// case GPUFamily::Mac1:
|
|
// case GPUFamily::Mac2:
|
|
// return true;
|
|
// }
|
|
// case Format::D32F:
|
|
// case Format::D32F_S8:
|
|
// switch (gpuFamily) {
|
|
// case GPUFamily::Apple1:
|
|
// case GPUFamily::Apple2:
|
|
// case GPUFamily::Apple3:
|
|
// case GPUFamily::Apple4:
|
|
// case GPUFamily::Apple5:
|
|
// case GPUFamily::Apple6:
|
|
//#ifdef TARGET_OS_SIMULATOR
|
|
// return true;
|
|
//#else
|
|
// return false;
|
|
//#endif
|
|
// case GPUFamily::Mac1:
|
|
// case GPUFamily::Mac2:
|
|
// return true;
|
|
// }
|
|
// case Format::D24S8:
|
|
//#if (CC_PLATFORM == CC_PLATFORM_MACOS)
|
|
// return [device isDepth24Stencil8PixelFormatSupported];
|
|
//#else
|
|
// return false;
|
|
//#endif
|
|
// default:
|
|
// return false;
|
|
// }
|
|
}
|
|
|
|
bool mu::isIndirectDrawSupported(uint32_t family) {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
return static_cast<GPUFamily>(family) < GPUFamily::Apple3 ? false : true; //is only supported on MTLFeatureSet_iOS_GPUFamily3_v1 and later'
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
MTLPixelFormat mu::getSupportedDepthStencilFormat(id<MTLDevice> device, uint32_t family, uint32_t &depthBits) {
|
|
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
|
return MTLPixelFormatDepth24Unorm_Stencil8;
|
|
#else
|
|
return MTLPixelFormatDepth32Float_Stencil8;
|
|
#endif
|
|
}
|
|
|
|
ccstd::string mu::featureSetToString(MTLFeatureSet featureSet) {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
return getIOSFeatureSetToString(featureSet);
|
|
#else
|
|
return getMacFeatureSetToString(featureSet);
|
|
#endif
|
|
}
|
|
|
|
const uint8_t *const mu::convertData(const uint8_t *source, uint32_t length, Format type) {
|
|
switch (type) {
|
|
case Format::RGB8: return mu::convertRGB8ToRGBA8(source, length);
|
|
case Format::RGB32F: return mu::convertRGB32FToRGBA32F(source, length);
|
|
default: return source;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getBlockSize(Format format) {
|
|
switch (format) {
|
|
case Format::ASTC_RGBA_4X4:
|
|
case Format::ASTC_SRGBA_4X4:
|
|
case Format::ASTC_RGBA_5X4:
|
|
case Format::ASTC_SRGBA_5X4:
|
|
case Format::ASTC_RGBA_5X5:
|
|
case Format::ASTC_SRGBA_5X5:
|
|
case Format::ASTC_RGBA_6X5:
|
|
case Format::ASTC_SRGBA_6X5:
|
|
case Format::ASTC_RGBA_6X6:
|
|
case Format::ASTC_SRGBA_6X6:
|
|
case Format::ASTC_RGBA_8X5:
|
|
case Format::ASTC_SRGBA_8X5:
|
|
case Format::ASTC_RGBA_8X6:
|
|
case Format::ASTC_SRGBA_8X6:
|
|
case Format::ASTC_RGBA_8X8:
|
|
case Format::ASTC_SRGBA_8X8:
|
|
case Format::ASTC_RGBA_10X5:
|
|
case Format::ASTC_SRGBA_10X5:
|
|
case Format::ASTC_RGBA_10X6:
|
|
case Format::ASTC_SRGBA_10X6:
|
|
case Format::ASTC_RGBA_10X8:
|
|
case Format::ASTC_SRGBA_10X8:
|
|
case Format::ASTC_RGBA_10X10:
|
|
case Format::ASTC_SRGBA_10X10:
|
|
case Format::ASTC_RGBA_12X10:
|
|
case Format::ASTC_SRGBA_12X10:
|
|
case Format::ASTC_RGBA_12X12:
|
|
case Format::ASTC_SRGBA_12X12:
|
|
return 16u;
|
|
case Format::PVRTC_RGB2:
|
|
case Format::PVRTC_RGBA2:
|
|
return 32u; // blockWidth = 8, blockHeight = 4, bitsPerPixel = 2;
|
|
case Format::PVRTC_RGB4:
|
|
case Format::PVRTC_RGBA4:
|
|
return 16u; // blockWidth = 4, blockHeight = 4, bitsPerPixel = 4;
|
|
case Format::ETC2_RGB8:
|
|
case Format::ETC2_SRGB8:
|
|
case Format::ETC2_RGB8_A1:
|
|
case Format::ETC2_SRGB8_A1:
|
|
case Format::EAC_R11:
|
|
case Format::EAC_R11SN:
|
|
return 8u; // blockWidth = 4, blockHeight = 4
|
|
case Format::ETC2_RGBA8:
|
|
case Format::ETC2_SRGB8_A8:
|
|
case Format::EAC_RG11:
|
|
case Format::EAC_RG11SN: // blockWidth = 4, blockHeight = 4;
|
|
return 16u;
|
|
default:
|
|
return GFX_FORMAT_INFOS[static_cast<uint32_t>(format)].size;
|
|
}
|
|
}
|
|
|
|
uint32_t mu::getBytesPerRow(Format format, uint32_t width) {
|
|
uint32_t blockSize = getBlockSize(format);
|
|
uint32_t widthInBlock = 1u;
|
|
switch (format) {
|
|
case Format::ASTC_RGBA_4X4:
|
|
case Format::ASTC_SRGBA_4X4:
|
|
widthInBlock = (width + 3) / 4;
|
|
break;
|
|
case Format::ASTC_RGBA_5X4:
|
|
case Format::ASTC_SRGBA_5X4:
|
|
case Format::ASTC_RGBA_5X5:
|
|
case Format::ASTC_SRGBA_5X5:
|
|
widthInBlock = (width + 4) / 5;
|
|
break;
|
|
case Format::ASTC_RGBA_6X5:
|
|
case Format::ASTC_SRGBA_6X5:
|
|
case Format::ASTC_RGBA_6X6:
|
|
case Format::ASTC_SRGBA_6X6:
|
|
widthInBlock = (width + 5) / 6;
|
|
break;
|
|
case Format::ASTC_RGBA_8X5:
|
|
case Format::ASTC_SRGBA_8X5:
|
|
case Format::ASTC_RGBA_8X6:
|
|
case Format::ASTC_SRGBA_8X6:
|
|
case Format::ASTC_RGBA_8X8:
|
|
case Format::ASTC_SRGBA_8X8:
|
|
widthInBlock = (width + 7) / 8;
|
|
break;
|
|
case Format::ASTC_RGBA_10X5:
|
|
case Format::ASTC_SRGBA_10X5:
|
|
case Format::ASTC_RGBA_10X6:
|
|
case Format::ASTC_SRGBA_10X6:
|
|
case Format::ASTC_RGBA_10X8:
|
|
case Format::ASTC_SRGBA_10X8:
|
|
case Format::ASTC_RGBA_10X10:
|
|
case Format::ASTC_SRGBA_10X10:
|
|
widthInBlock = (width + 9) / 10;
|
|
break;
|
|
case Format::ASTC_RGBA_12X10:
|
|
case Format::ASTC_SRGBA_12X10:
|
|
case Format::ASTC_RGBA_12X12:
|
|
case Format::ASTC_SRGBA_12X12:
|
|
widthInBlock = (width + 11) / 12;
|
|
break;
|
|
case Format::PVRTC_RGB2:
|
|
case Format::PVRTC_RGBA2:
|
|
widthInBlock = width / 2;
|
|
break;
|
|
case Format::PVRTC_RGB4:
|
|
case Format::PVRTC_RGBA4:
|
|
widthInBlock = width / 4;
|
|
break;
|
|
case Format::ETC2_RGB8:
|
|
case Format::ETC2_SRGB8:
|
|
case Format::ETC2_RGB8_A1:
|
|
case Format::ETC2_SRGB8_A1:
|
|
case Format::EAC_R11:
|
|
case Format::EAC_R11SN:
|
|
case Format::EAC_RG11:
|
|
case Format::EAC_RG11SN:
|
|
case Format::ETC2_RGBA8:
|
|
case Format::ETC2_SRGB8_A8:
|
|
widthInBlock = width / 4;
|
|
break;
|
|
default:
|
|
widthInBlock = width;
|
|
break;
|
|
}
|
|
return widthInBlock * blockSize;
|
|
}
|
|
|
|
bool mu::pixelFormatIsColorRenderable(Format format) {
|
|
MTLPixelFormat pixelFormat = toMTLPixelFormat(format);
|
|
BOOL isCompressedFormat = false;
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
isCompressedFormat = (pixelFormat >= MTLPixelFormatASTC_4x4_sRGB && pixelFormat <= MTLPixelFormatASTC_12x12_LDR) ||
|
|
(pixelFormat >= MTLPixelFormatPVRTC_RGB_2BPP && pixelFormat <= MTLPixelFormatPVRTC_RGBA_4BPP_sRGB) ||
|
|
(pixelFormat >= MTLPixelFormatEAC_R11Unorm && pixelFormat <= MTLPixelFormatETC2_RGB8A1_sRGB);
|
|
#else
|
|
isCompressedFormat = (pixelFormat >= MTLPixelFormatBC1_RGBA && pixelFormat <= MTLPixelFormatBC3_RGBA_sRGB);
|
|
#endif
|
|
BOOL is422Format = (pixelFormat == MTLPixelFormatGBGR422 || pixelFormat == MTLPixelFormatBGRG422);
|
|
|
|
return !isCompressedFormat && !is422Format && !(pixelFormat == MTLPixelFormatInvalid);
|
|
}
|
|
|
|
//CompareFunction of MTLSamplerDescriptor is only supported on MTLFeatureSet_iOS_GPUFamily3_v1 and later
|
|
bool mu::isSamplerDescriptorCompareFunctionSupported(uint32_t family) {
|
|
#if CC_PLATFORM == CC_PLATFORM_IOS
|
|
return (static_cast<GPUFamily>(family) < GPUFamily::Apple3) ? false : true;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void mu::clearRenderArea(CCMTLDevice *device, id<MTLRenderCommandEncoder> renderEncoder, RenderPass *renderPass, const Rect &renderArea, const Color *colors, float /*depth*/, uint32_t /*stencil*/) {
|
|
const auto gpuPSO = getClearRenderPassPipelineState(device, renderPass);
|
|
const auto mtlRenderPass = static_cast<CCMTLRenderPass *>(renderPass);
|
|
uint32_t slot = 0u;
|
|
|
|
const auto &renderTargetSizes = mtlRenderPass->getRenderTargetSizes();
|
|
float renderTargetWidth = renderTargetSizes[slot].x;
|
|
float renderTargetHeight = renderTargetSizes[slot].y;
|
|
float halfWidth = renderTargetWidth * 0.5f;
|
|
float halfHeight = renderTargetHeight * 0.5f;
|
|
float rcpWidth = 1.0f / halfWidth;
|
|
float rcpHeight = 1.0f / halfHeight;
|
|
float width = renderArea.x + renderArea.width;
|
|
float height = renderArea.height + renderArea.y;
|
|
Vec2 leftTop{(renderArea.x - halfWidth) * rcpWidth, (halfHeight - renderArea.y) * rcpHeight};
|
|
Vec2 rightTop{(width - halfWidth) * rcpWidth, (halfHeight - renderArea.y) * rcpHeight};
|
|
Vec2 rightBottom{(width - halfWidth) * rcpWidth, (halfHeight - height) * rcpHeight};
|
|
Vec2 leftBottom{(renderArea.x - halfWidth) * rcpWidth, (halfHeight - height) * rcpHeight};
|
|
Vec2 vertexes[] = {leftTop, leftBottom, rightBottom, leftTop, rightBottom, rightTop};
|
|
|
|
const auto &colorAttachments = renderPass->getColorAttachments();
|
|
const auto &depthStencilAttachment = renderPass->getDepthStencilAttachment();
|
|
[renderEncoder setViewport:(MTLViewport){0, 0, renderTargetWidth, renderTargetHeight, 0, 1}];
|
|
MTLScissorRect scissorArea = {static_cast<NSUInteger>(renderArea.x), static_cast<NSUInteger>(renderArea.y), static_cast<NSUInteger>(renderArea.width), static_cast<NSUInteger>(renderArea.height)};
|
|
#if defined(CC_DEBUG) && (CC_DEBUG > 0)
|
|
scissorArea.width = MIN(scissorArea.width, renderTargetWidth - scissorArea.x);
|
|
scissorArea.height = MIN(scissorArea.height, renderTargetHeight - scissorArea.y);
|
|
#endif
|
|
[renderEncoder setScissorRect:scissorArea];
|
|
[renderEncoder setRenderPipelineState:gpuPSO->mtlRenderPipelineState];
|
|
if (gpuPSO->mtlDepthStencilState) {
|
|
[renderEncoder setStencilFrontReferenceValue:gpuPSO->stencilRefFront
|
|
backReferenceValue:gpuPSO->stencilRefBack];
|
|
[renderEncoder setDepthStencilState:gpuPSO->mtlDepthStencilState];
|
|
}
|
|
|
|
[renderEncoder setVertexBytes:vertexes
|
|
length:sizeof(vertexes)
|
|
atIndex:30];
|
|
|
|
[renderEncoder setFragmentBytes:&colors[slot]
|
|
length:sizeof(colors[slot])
|
|
atIndex:0];
|
|
|
|
uint32_t count = sizeof(vertexes) / sizeof(Vec2);
|
|
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
|
|
vertexStart:0
|
|
vertexCount:count];
|
|
}
|
|
|
|
void mu::clearUtilResource() {
|
|
if (!renderPassMap.empty()) {
|
|
for (auto &pass : renderPassMap) {
|
|
//TODO: create and destroy not in the same level
|
|
pass.second->destroy();
|
|
delete pass.second;
|
|
}
|
|
renderPassMap.clear();
|
|
}
|
|
if (!pipelineMap.empty()) {
|
|
for (auto &pipeline : pipelineMap) {
|
|
pipeline.second->destroy();
|
|
delete pipeline.second;
|
|
}
|
|
pipelineMap.clear();
|
|
}
|
|
}
|
|
|
|
} // namespace gfx
|
|
} // namespace cc
|
|
|