#include "hecl/Backend/Metal.hpp" #if BOO_HAS_METAL #include #include #include static logvisor::Module Log("hecl::Backend::Metal"); namespace hecl { namespace Backend { std::string Metal::EmitTexGenSource2(TexGenSrc src, int uvIdx) const { switch (src) { case TexGenSrc::Position: return "v.posIn.xy\n"; case TexGenSrc::Normal: return "v.normIn.xy\n"; case TexGenSrc::UV: return hecl::Format("v.uvIn%u", uvIdx); default: break; } return std::string(); } std::string Metal::EmitTexGenSource4(TexGenSrc src, int uvIdx) const { switch (src) { case TexGenSrc::Position: return "float4(v.posIn, 1.0)\n"; case TexGenSrc::Normal: return "float4(v.normIn, 1.0)\n"; case TexGenSrc::UV: return hecl::Format("float4(v.uvIn%u, 0.0, 1.0)", uvIdx); default: break; } return std::string(); } std::string Metal::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const { std::string retval = "struct VertData\n" "{\n" " float3 posIn [[ attribute(0) ]];\n" " float3 normIn [[ attribute(1) ]];\n"; unsigned idx = 2; if (col) { for (unsigned i=0 ; i tex%u [[ texture(%u) ]]", i, i); if (reflectionType == ReflectionType::Indirect) texMapDecl += hecl::Format(",\ntexture2d reflectionIndTex [[ texture(%u) ]]\n" ",\ntexture2d reflectionTex [[ texture(%u) ]]\n", m_texMapEnd, m_texMapEnd+1); else if (reflectionType == ReflectionType::Simple) texMapDecl += hecl::Format(",\ntexture2d reflectionTex [[ texture(%u) ]]\n", m_texMapEnd); std::string blockCall; for (size_t i=0 ; i tex%u [[ texture(%u) ]]", i, i); if (reflectionType == ReflectionType::Indirect) texMapDecl += hecl::Format(",\ntexture2d reflectionIndTex [[ texture(%u) ]]\n" ",\ntexture2d reflectionTex [[ texture(%u) ]]\n", m_texMapEnd, m_texMapEnd+1); else if (reflectionType == ReflectionType::Simple) texMapDecl += hecl::Format(",\ntexture2d reflectionTex [[ texture(%u) ]]\n", m_texMapEnd); std::string extTexCall; for (int i=0 ; i tex%u [[ texture(%u) ]]", extTex.mapIdx, extTex.mapIdx); extTexBits |= 1 << extTex.mapIdx; } std::string blockCall; for (size_t i=0 ; i& objOut) { if (!m_rtHint) Log.report(logvisor::Fatal, "ShaderCacheManager::setRenderTargetHint must be called before making metal shaders"); m_backend.reset(ir, diag); size_t cachedSz = 2; std::string vertSource = m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), tag.getSkinSlotCount(), tag.getTexMtxCount(), 0, nullptr, tag.getReflectionType()); std::string fragSource = m_backend.makeFrag(0, nullptr, tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, tag.getReflectionType()); std::vector vertBlob; std::vector fragBlob; objOut = static_cast(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), &vertBlob, &fragBlob, tag.newVertexFormat(ctx), m_rtHint, boo::BlendFactor(m_backend.m_blendSrc), boo::BlendFactor(m_backend.m_blendDst), tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, true, tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); if (!objOut) Log.report(logvisor::Fatal, "unable to build shader"); cachedSz += vertBlob.size() + 4; cachedSz += fragBlob.size() + 4; ShaderCachedData dataOut(tag, cachedSz); athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); w.writeUByte(atUint8(m_backend.m_blendSrc)); w.writeUByte(atUint8(m_backend.m_blendDst)); w.writeUint32Big(vertBlob.size()); w.writeUBytes(vertBlob.data(), vertBlob.size()); w.writeUint32Big(fragBlob.size()); w.writeUBytes(fragBlob.data(), fragBlob.size()); return dataOut; } boo::ObjToken buildShaderFromCache(const ShaderCachedData& data, boo::IGraphicsDataFactory::Context& ctx) { if (!m_rtHint) Log.report(logvisor::Fatal, "ShaderCacheManager::setRenderTargetHint must be called before making metal shaders"); const ShaderTag& tag = data.m_tag; athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); boo::BlendFactor blendSrc = boo::BlendFactor(r.readUByte()); boo::BlendFactor blendDst = boo::BlendFactor(r.readUByte()); std::vector vertBlob; std::vector fragBlob; atUint32 vertLen = r.readUint32Big(); if (vertLen) { vertBlob.resize(vertLen); r.readUBytesToBuf(&vertBlob[0], vertLen); } atUint32 fragLen = r.readUint32Big(); if (fragLen) { fragBlob.resize(fragLen); r.readUBytesToBuf(&fragBlob[0], fragLen); } if (r.hasError()) return nullptr; auto ret = static_cast(ctx). newShaderPipeline(nullptr, nullptr, &vertBlob, &fragBlob, tag.newVertexFormat(ctx), m_rtHint, blendSrc, blendDst, tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, true, tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); if (!ret) Log.report(logvisor::Fatal, "unable to build shader"); return ret; } ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, const hecl::Frontend::IR& ir, hecl::Frontend::Diagnostics& diag, const std::vector& extensionSlots, boo::IGraphicsDataFactory::Context& ctx, FReturnExtensionShader returnFunc) { if (!m_rtHint) Log.report(logvisor::Fatal, "ShaderCacheManager::setRenderTargetHint must be called before making metal shaders"); m_backend.reset(ir, diag); size_t cachedSz = 2; std::vector, std::vector>> blobs; blobs.reserve(extensionSlots.size()); for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) { std::string vertSource = m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), tag.getSkinSlotCount(), tag.getTexMtxCount(), slot.texCount, slot.texs, slot.noReflection ? Backend::ReflectionType::None : tag.getReflectionType()); std::string fragSource = m_backend.makeFrag(slot.blockCount, slot.blockNames, tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, slot.noReflection ? Backend::ReflectionType::None : tag.getReflectionType(), slot.lighting, slot.post, slot.texCount, slot.texs); boo::ZTest zTest; switch (slot.depthTest) { case hecl::Backend::ZTest::Original: default: zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; break; case hecl::Backend::ZTest::None: zTest = boo::ZTest::None; break; case hecl::Backend::ZTest::LEqual: zTest = boo::ZTest::LEqual; break; case hecl::Backend::ZTest::Greater: zTest = boo::ZTest::Greater; break; case hecl::Backend::ZTest::Equal: zTest = boo::ZTest::Equal; break; } blobs.emplace_back(); auto ret = static_cast(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), &blobs.back().first, &blobs.back().second, tag.newVertexFormat(ctx), m_rtHint, boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendSrc : slot.srcFactor), boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendDst : slot.dstFactor), tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), !slot.noColorWrite, !slot.noAlphaWrite, (slot.cullMode == hecl::Backend::CullMode::Original) ? (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : boo::CullMode(slot.cullMode)); if (!ret) Log.report(logvisor::Fatal, "unable to build shader"); cachedSz += blobs.back().first.size() + 4; cachedSz += blobs.back().second.size() + 4; returnFunc(ret); } ShaderCachedData dataOut(tag, cachedSz); athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); w.writeUByte(atUint8(m_backend.m_blendSrc)); w.writeUByte(atUint8(m_backend.m_blendDst)); for (auto& blob : blobs) { w.writeUint32Big(blob.first.size()); w.writeUBytes(blob.first.data(), blob.first.size()); w.writeUint32Big(blob.second.size()); w.writeUBytes(blob.second.data(), blob.second.size()); } return dataOut; } bool buildExtendedShaderFromCache(const ShaderCachedData& data, const std::vector& extensionSlots, boo::IGraphicsDataFactory::Context& ctx, FReturnExtensionShader returnFunc) { if (!m_rtHint) Log.report(logvisor::Fatal, "ShaderCacheManager::setRenderTargetHint must be called before making metal shaders"); const ShaderTag& tag = data.m_tag; athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); hecl::Backend::BlendFactor blendSrc = hecl::Backend::BlendFactor(r.readUByte()); hecl::Backend::BlendFactor blendDst = hecl::Backend::BlendFactor(r.readUByte()); if (r.hasError()) return false; for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) { std::vector vertBlob; std::vector fragBlob; atUint32 vertLen = r.readUint32Big(); if (vertLen) { vertBlob.resize(vertLen); r.readUBytesToBuf(&vertBlob[0], vertLen); } atUint32 fragLen = r.readUint32Big(); if (fragLen) { fragBlob.resize(fragLen); r.readUBytesToBuf(&fragBlob[0], fragLen); } if (r.hasError()) return false; boo::ZTest zTest; switch (slot.depthTest) { case hecl::Backend::ZTest::Original: default: zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; break; case hecl::Backend::ZTest::None: zTest = boo::ZTest::None; break; case hecl::Backend::ZTest::LEqual: zTest = boo::ZTest::LEqual; break; case hecl::Backend::ZTest::Greater: zTest = boo::ZTest::Greater; break; case hecl::Backend::ZTest::Equal: zTest = boo::ZTest::Equal; break; } auto ret = static_cast(ctx). newShaderPipeline(nullptr, nullptr, &vertBlob, &fragBlob, tag.newVertexFormat(ctx), m_rtHint, boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? blendSrc : slot.srcFactor), boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? blendDst : slot.dstFactor), tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), !slot.noColorWrite, !slot.noAlphaWrite, (slot.cullMode == hecl::Backend::CullMode::Original) ? (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : boo::CullMode(slot.cullMode)); if (!ret) Log.report(logvisor::Fatal, "unable to build shader"); returnFunc(ret); } return true; } }; IShaderBackendFactory* _NewMetalBackendFactory() { return new struct MetalBackendFactory(); } } } #endif