Repository: shader-slang/slang
File: tools/render-test/render-test-main.cpp (C++)

Code before:
        // Descriptor handles in SPIRV/DX12 with bindless are lowered to uint2
        // stored in uniform data.
        //
        // IMPORTANT: We only check for uint2, NOT uint64!
        // - uint64 in uniform data = device address (buffer pointer)
        // - uint2 in uniform data = descriptor handle (index into bindless heap)
        //
        // Note: We DON'T check for Kind::Resource in uniform data because:
        // - On Metal/CUDA, regular resources appear as pointers in uniform data
        // - Those work fine with regular setBinding(), not descriptor handles

        auto typeLayout = cursor.getTypeLayout();

        // Check if this parameter is stored in uniform data (not descriptor table)
        auto uniformSize = typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
        auto descriptorSlotSize =
            typeLayout->getSize(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);

        if (uniformSize == 8 && descriptorSlotSize == 0)
        {
            auto type = typeLayout->getType();
            auto kind = type->getKind();

            // Check for uint2 (SPIRV/DX12 descriptor handle)
            // This is the ONLY pattern we detect as descriptor handles
            if (kind == slang::TypeReflection::Kind::Vector && type->getElementCount() == 2 &&
                type->getElementType()->getScalarType() ==
                    slang::TypeReflection::ScalarType::UInt32)
                return true;
        }

        return false;
    }

    static DescriptorHandleAccess getDescriptorHandleAccess(SlangResourceAccess resourceAccess)
    {
        // Map Slang resource access to descriptor handle access
        // Most write-capable resources map to ReadWrite
        switch (resourceAccess)
        {
        case SLANG_RESOURCE_ACCESS_READ:
            return DescriptorHandleAccess::Read;
        case SLANG_RESOURCE_ACCESS_READ_WRITE:
        case SLANG_RESOURCE_ACCESS_WRITE:
        case SLANG_RESOURCE_ACCESS_RASTER_ORDERED:
        case SLANG_RESOURCE_ACCESS_APPEND:
        case SLANG_RESOURCE_ACCESS_CONSUME:
        case SLANG_RESOURCE_ACCESS_FEEDBACK:
            return DescriptorHandleAccess::ReadWrite;
        default:
            return DescriptorHandleAccess::ReadWrite;

Review comment (suggestion):
This isn't part of this PR, but it seems that we really want to change our reflection API implementaiton so that `type->getKind()` returns a new enum case `TypeReflection::Kind::DescriptorHandle` for bindless handle types.

Code after:
        // Descriptor handles in SPIRV/DX12 with bindless are lowered to uint2 or uint64
        // stored in uniform data.
        //
        // Two representations:
        // - uint2: Standard SPIRV path (OpAccessChain into __slang_resource_heap)
        // - uint64: NVIDIA spvBindlessTextureNV path (OpConvertUToSampledImageNV)
        //
        // Note: uint64 can also be device addresses (buffer pointers).
        // We use try-and-fallback: if getDescriptorHandle() fails, we fall back
        // to the device address path (which checks uint64 again).
        //
        // We DON'T check for Kind::Resource in uniform data because:
        // - On Metal/CUDA, regular resources appear as pointers in uniform data
        // - Those work fine with regular setBinding(), not descriptor handles

        auto typeLayout = cursor.getTypeLayout();

        // Check if this parameter is stored in uniform data (not descriptor table)
        auto uniformSize = typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
        auto descriptorSlotSize =
            typeLayout->getSize(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);

        if (uniformSize == 8 && descriptorSlotSize == 0)
        {
            auto type = typeLayout->getType();
            auto kind = type->getKind();

            // Check for uint2 (standard SPIRV/DX12 descriptor handle)
            if (kind == slang::TypeReflection::Kind::Vector && type->getElementCount() == 2 &&
                type->getElementType()->getScalarType() ==
                    slang::TypeReflection::ScalarType::UInt32)
                return true;

            // Check for uint64 (NVIDIA spvBindlessTextureNV descriptor handle)
            // Note: uint64 can also be a device address. We distinguish by checking
            // if there's an element type - descriptor handles are pointers to resources.
            if (kind == slang::TypeReflection::Kind::Scalar &&
                type->getScalarType() == slang::TypeReflection::ScalarType::UInt64)
            {
                // Only treat as descriptor handle if it has an element type (resource)
                // Plain uint64 with no element type is a device address
                auto elementType = type->getElementType();
                if (elementType &&
                    (elementType->getKind() == slang::TypeReflection::Kind::Resource ||
                     elementType->getKind() == slang::TypeReflection::Kind::SamplerState))
                    return true;
            }
        }

        return false;
    }