#include "hecl/Backend/Metal.hpp" #if BOO_HAS_METAL #include "hecl/Runtime.hpp" #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); } std::string blockCall; for (size_t i=0 ; i tex%u [[ texture(%u) ]]", i, i); } 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(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), 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"); 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.writeString(vertSource); w.writeString(fragSource); return dataOut; } boo::IShaderPipeline* 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::string vertSource = r.readString(); std::string fragSource = r.readString(); if (r.hasError()) return nullptr; boo::IShaderPipeline* ret = static_cast(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), 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> sources; sources.reserve(extensionSlots.size()); for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) { sources.emplace_back(m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), tag.getSkinSlotCount(), tag.getTexMtxCount(), slot.texCount, slot.texs), m_backend.makeFrag(slot.blockCount, slot.blockNames, tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, slot.lighting, slot.post, slot.texCount, slot.texs)); cachedSz += sources.back().first.size() + 1; cachedSz += sources.back().second.size() + 1; 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; } boo::IShaderPipeline* ret = static_cast(ctx). newShaderPipeline(sources.back().first.c_str(), sources.back().second.c_str(), 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.frontfaceCull ? boo::CullMode::Frontface : (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)); if (!ret) Log.report(logvisor::Fatal, "unable to build shader"); 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& src : sources) { w.writeString(src.first); w.writeString(src.second); } 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::string vertSource = r.readString(); std::string fragSource = r.readString(); 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; } boo::IShaderPipeline* ret = static_cast(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), 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.frontfaceCull ? boo::CullMode::Frontface : (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None)); if (!ret) Log.report(logvisor::Fatal, "unable to build shader"); returnFunc(ret); } return true; } }; IShaderBackendFactory* _NewMetalBackendFactory() { return new struct MetalBackendFactory(); } } } #endif