shader-slang / slang

Making it easier to work with shaders
MIT License
1.82k stars 165 forks source link

Unable to compile simple specialization containing a sampler and texture #4040

Closed Dynamitos closed 2 months ago

Dynamitos commented 3 months ago

I am trying to compile the following example using the C++ API:

interface IMaterial
{
    float4 calc(float2 texCoords);
};

struct Mat : IMaterial
{
    Texture2D tex;
    SamplerState sampler;
    float4 calc(float2 texCoords)
    {
        return tex.Sample(sampler, texCoords);
    }
};

ParameterBlock<IMaterial> pMat;

[shader("pixel")]
float4 fragMain(float2 texCoords) : SV_Target
{
    return pMat.calc(texCoords);
}

The C++ code used to reproduce the error is as follows:

#define CHECK_RESULT(x) {SlangResult r = x; if(r != 0) {throw std::runtime_error(fmt::format("Error: {0}", r));}}
#define CHECK_DIAGNOSTICS() {if(diagnostics) {std::cout << (const char*)diagnostics->getBufferPointer() << std::endl; assert(false);}}

void test()
{

    thread_local Slang::ComPtr<slang::IGlobalSession> globalSession;
    if (!globalSession)
    {
        slang::createGlobalSession(globalSession.writeRef());
    }
    slang::SessionDesc sessionDesc;
    sessionDesc.flags = 0;
    std::array<slang::CompilerOptionEntry, 2> option;
    option[0].name = slang::CompilerOptionName::DumpIntermediates;
    option[0].value = slang::CompilerOptionValue();
    option[0].value.kind = slang::CompilerOptionValueKind::Int;
    option[0].value.intValue0 = 1;
    option[1].name = slang::CompilerOptionName::EmitSpirvViaGLSL;
    option[1].value = slang::CompilerOptionValue();
    option[1].value.kind = slang::CompilerOptionValueKind::Int;
    option[1].value.intValue0 = 1;
    sessionDesc.compilerOptionEntries = option.data();
    sessionDesc.compilerOptionEntryCount = option.size();
    sessionDesc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;

    slang::TargetDesc targetDesc;
    targetDesc.profile = globalSession->findProfile("sm_6_6");
    targetDesc.format = SLANG_SPIRV;
    sessionDesc.targetCount = 1;
    sessionDesc.targets = &targetDesc;

    Slang::ComPtr<slang::ISession> session;
    CHECK_RESULT(globalSession->createSession(sessionDesc, session.writeRef()));
    Slang::ComPtr<slang::IBlob> diagnostics;
    Array<slang::IComponentType*> modules;
    Slang::ComPtr<slang::IEntryPoint> entrypoint;
    slang::IModule* mainModule = session->loadModule("repro", diagnostics.writeRef());

    CHECK_DIAGNOSTICS();

    mainModule->findEntryPointByName("fragMain", entrypoint.writeRef());
    modules.add(entrypoint);

    Slang::ComPtr<slang::IComponentType> moduleComposition;
    session->createCompositeComponentType(modules.data(), modules.size(), moduleComposition.writeRef(), diagnostics.writeRef());

    CHECK_DIAGNOSTICS();

    Slang::ComPtr<slang::IComponentType> linkedProgram;
    moduleComposition->link(linkedProgram.writeRef(), diagnostics.writeRef());

    CHECK_DIAGNOSTICS();

    slang::ProgramLayout* reflection = linkedProgram->getLayout(0, diagnostics.writeRef());

    CHECK_DIAGNOSTICS();

    std::vector<slang::SpecializationArg> specialization;
    specialization.push_back(slang::SpecializationArg::fromType(reflection->findTypeByName("Mat")));

    Slang::ComPtr<slang::IComponentType> specializedComponent;
    linkedProgram->specialize(specialization.data(), specialization.size(), specializedComponent.writeRef(), diagnostics.writeRef());
    CHECK_DIAGNOSTICS();

    Slang::ComPtr<slang::IBlob> kernelBlob;
    specializedComponent->getEntryPointCode(
        0,
        0,
        kernelBlob.writeRef(),
        diagnostics.writeRef()
    );
    CHECK_DIAGNOSTICS();
}

This error occured with the latest tagged release version v2024.1.10. What I could gather from the debugger, the problem is that the texture and sampler are passed to the function via a BitCast to Uint64, which it is unable to properly generate into code.

csyonghe commented 3 months ago

Everything inside a type that implements an interface and used to specialize a shader in a way that affects shader parameter layout must be ordinary data types, i.e. they must not contain opaque resource types. This is a limitation of the language and we don't have plans to support it due to the complexity it introduces to binding models and reflection API. You need to implement some sort of bindless mechanism and stick to pure data types with different material implementations.