#include "hecl/Backend/HLSL.hpp" #include "hecl/Runtime.hpp" #include #include #include static logvisor::Module Log("hecl::Backend::HLSL"); namespace hecl { namespace Backend { std::string HLSL::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 HLSL::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 HLSL::GenerateVertInStruct(unsigned col, unsigned uv, unsigned w) const { std::string retval = "struct VertData\n" "{\n" " float3 posIn : POSITION;\n" " float3 normIn : NORMAL;\n"; if (col) retval += hecl::Format(" float4 colIn[%u] : COLOR;\n", col); if (uv) retval += hecl::Format(" float2 uvIn[%u] : UV;\n", uv); if (w) retval += hecl::Format(" float4 weightIn[%u] : WEIGHT;\n", w); return retval + "};\n"; } std::string HLSL::GenerateVertToFragStruct(size_t extTexCount, bool reflectionCoords) const { std::string retval = "struct VertToFrag\n" "{\n" " float4 mvpPos : SV_Position;\n" " float4 mvPos : POSITION;\n" " float4 mvNorm : NORMAL;\n"; if (m_tcgs.size()) retval += hecl::Format(" float2 tcgs[%u] : UV;\n", unsigned(m_tcgs.size())); if (extTexCount) retval += hecl::Format(" float2 extTcgs[%u] : EXTUV;\n", unsigned(extTexCount)); if (reflectionCoords) retval += " float2 reflectTcgs[2] : REFLECTUV;\n" " float reflectAlpha;\n"; return retval + "};\n"; } std::string HLSL::GenerateVertUniformStruct(unsigned skinSlots, unsigned texMtxs, bool reflectionCoords) const { if (skinSlots == 0) skinSlots = 1; std::string retval = hecl::Format("cbuffer HECLVertUniform : register(b0)\n" "{\n" " float4x4 mv[%u];\n" " float4x4 mvInv[%u];\n" " float4x4 proj;\n" "};\n", skinSlots, skinSlots); if (texMtxs) retval += hecl::Format("struct TCGMtx\n" "{\n" " float4x4 mtx;\n" " float4x4 postMtx;\n" "};\n" "cbuffer HECLTCGMatrix : register(b1)\n" "{\n" " TCGMtx texMtxs[%u];\n" "};\n", texMtxs); if (reflectionCoords) retval += "cbuffer HECLReflectMtx : register(b3)\n" "{\n" " float4x4 indMtx;\n" " float4x4 reflectMtx;\n" " float reflectAlpha;\n" "};\n" "\n"; return retval; } std::string HLSL::GenerateAlphaTest() const { return " if (colorOut.a < 0.01)\n" " {\n" " discard;\n" " }\n"; } std::string HLSL::GenerateReflectionExpr(ReflectionType type) const { switch (type) { case ReflectionType::None: default: return "float3(0.0, 0.0, 0.0)"; case ReflectionType::Simple: return "reflectionTex.Sample(samp, vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha"; case ReflectionType::Indirect: return "reflectionTex.Sample(samp, (reflectionIndTex.Sample(samp, vtf.reflectTcgs[0]).rg - " "float2(0.5, 0.5)) * float2(0.5, 0.5) + vtf.reflectTcgs[1]).rgb * vtf.reflectAlpha"; } } void HLSL::reset(const IR& ir, Diagnostics& diag) { /* Common programmable interpretation */ ProgrammableCommon::reset(ir, diag, "HLSL"); } std::string HLSL::makeVert(unsigned col, unsigned uv, unsigned w, unsigned s, unsigned tm, size_t extTexCount, const TextureInfo* extTexs, ReflectionType reflectionType) const { std::string retval = GenerateVertInStruct(col, uv, w) + "\n" + GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" + GenerateVertUniformStruct(s, tm, reflectionType != ReflectionType::None) + "\n" + "VertToFrag main(in VertData v)\n" "{\n" " VertToFrag vtf;\n"; if (s) { /* skinned */ retval += " float4 posAccum = float4(0.0,0.0,0.0,0.0);\n" " float4 normAccum = float4(0.0,0.0,0.0,0.0);\n"; for (size_t i=0 ; i vertBlob; ComPtr fragBlob; ComPtr pipelineBlob; objOut = static_cast(ctx). newShaderPipeline(vertSource.c_str(), fragSource.c_str(), ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), tag.newVertexFormat(ctx), 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, false, tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); if (!objOut) Log.report(logvisor::Fatal, "unable to build shader"); atUint32 vertSz = 0; atUint32 fragSz = 0; atUint32 pipelineSz = 0; if (vertBlob) vertSz = vertBlob->GetBufferSize(); if (fragBlob) fragSz = fragBlob->GetBufferSize(); if (pipelineBlob) pipelineSz = pipelineBlob->GetBufferSize(); size_t cachedSz = 14 + vertSz + fragSz + pipelineSz; 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)); if (vertBlob) { w.writeUint32Big(vertSz); w.writeUBytes((atUint8*)vertBlob->GetBufferPointer(), vertSz); } else w.writeUint32Big(0); if (fragBlob) { w.writeUint32Big(fragSz); w.writeUBytes((atUint8*)fragBlob->GetBufferPointer(), fragSz); } else w.writeUint32Big(0); if (pipelineBlob) { w.writeUint32Big(pipelineSz); w.writeUBytes((atUint8*)pipelineBlob->GetBufferPointer(), pipelineSz); } else w.writeUint32Big(0); return dataOut; } boo::IShaderPipeline* buildShaderFromCache(const ShaderCachedData& data, boo::IGraphicsDataFactory::Context& ctx) { 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()); if (r.hasError()) return nullptr; atUint32 vertSz = r.readUint32Big(); ComPtr vertBlob; if (vertSz) { D3DCreateBlobPROC(vertSz, &vertBlob); r.readUBytesToBuf(vertBlob->GetBufferPointer(), vertSz); } atUint32 fragSz = r.readUint32Big(); ComPtr fragBlob; if (fragSz) { D3DCreateBlobPROC(fragSz, &fragBlob); r.readUBytesToBuf(fragBlob->GetBufferPointer(), fragSz); } atUint32 pipelineSz = r.readUint32Big(); ComPtr pipelineBlob; if (pipelineSz) { D3DCreateBlobPROC(pipelineSz, &pipelineBlob); r.readUBytesToBuf(pipelineBlob->GetBufferPointer(), pipelineSz); } if (r.hasError()) return nullptr; boo::IShaderPipeline* ret = static_cast(ctx). newShaderPipeline(nullptr, nullptr, ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), tag.newVertexFormat(ctx), blendSrc, blendDst, tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, false, 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) { m_backend.reset(ir, diag); struct Blobs { ComPtr vert; ComPtr frag; ComPtr pipeline; }; std::vector pipeBlobs; pipeBlobs.reserve(extensionSlots.size()); size_t cachedSz = 2 + 12 * 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, tag.getReflectionType()); std::string fragSource = m_backend.makeFrag(tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, tag.getReflectionType(), slot.lighting, slot.post, slot.texCount, slot.texs); pipeBlobs.emplace_back(); Blobs& thisPipeBlobs = pipeBlobs.back(); 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(), ReferenceComPtr(thisPipeBlobs.vert), ReferenceComPtr(thisPipeBlobs.frag), ReferenceComPtr(thisPipeBlobs.pipeline), tag.newVertexFormat(ctx), 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"); if (thisPipeBlobs.vert) cachedSz += thisPipeBlobs.vert->GetBufferSize(); if (thisPipeBlobs.frag) cachedSz += thisPipeBlobs.frag->GetBufferSize(); if (thisPipeBlobs.pipeline) cachedSz += thisPipeBlobs.pipeline->GetBufferSize(); 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 (const Blobs& blobs : pipeBlobs) { if (blobs.vert) { w.writeUint32Big(blobs.vert->GetBufferSize()); w.writeUBytes((atUint8*)blobs.vert->GetBufferPointer(), blobs.vert->GetBufferSize()); } else w.writeUint32Big(0); if (blobs.frag) { w.writeUint32Big(blobs.frag->GetBufferSize()); w.writeUBytes((atUint8*)blobs.frag->GetBufferPointer(), blobs.frag->GetBufferSize()); } else w.writeUint32Big(0); if (blobs.pipeline) { w.writeUint32Big(blobs.pipeline->GetBufferSize()); w.writeUBytes((atUint8*)blobs.pipeline->GetBufferPointer(), blobs.pipeline->GetBufferSize()); } else w.writeUint32Big(0); } return dataOut; } bool buildExtendedShaderFromCache(const ShaderCachedData& data, const std::vector& extensionSlots, boo::IGraphicsDataFactory::Context& ctx, FReturnExtensionShader returnFunc) { 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) { atUint32 vertSz = r.readUint32Big(); ComPtr vertBlob; if (vertSz) { D3DCreateBlobPROC(vertSz, &vertBlob); r.readUBytesToBuf(vertBlob->GetBufferPointer(), vertSz); } atUint32 fragSz = r.readUint32Big(); ComPtr fragBlob; if (fragSz) { D3DCreateBlobPROC(fragSz, &fragBlob); r.readUBytesToBuf(fragBlob->GetBufferPointer(), fragSz); } atUint32 pipelineSz = r.readUint32Big(); ComPtr pipelineBlob; if (pipelineSz) { D3DCreateBlobPROC(pipelineSz, &pipelineBlob); r.readUBytesToBuf(pipelineBlob->GetBufferPointer(), pipelineSz); } 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(nullptr, nullptr, ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), tag.newVertexFormat(ctx), 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* _NewHLSLBackendFactory() { return new struct HLSLBackendFactory(); } } }