Marvel's Spider-Man 2

Characters stay in a T-pose and the symbiote breaks on M1-M4 Macs: D3DMetal reads and writes typed buffers only to element 2^28.

What you see

Characters stay in a T-pose; the symbiote shows up as spikes, a flat black sheet, or not at all; brief black blocks flash in the opening sandstorm stage

Why it happens

On M1-M4 Macs, D3DMetal returns 0 for typed buffer reads past element 2^28 and drops typed writes past it. The black blocks come from a missing barrier after the volumetric fog copy

What the fix changes

macprotips': the wide R32 views made raw, and four shaders rebuilt raw from your own exe. Ours: no DLL override, and a UAV barrier after CS_VolumetricFogCopy

The code

macprotips': the wide R32 views past element 2^28 are created raw, which D3DMetal reads in full.

fixes/sm2-tpose-fix.c · hk_CreateShaderResourceView, lines 565–587
c
static void STDMETHODCALLTYPE hk_CreateShaderResourceView(ID3D12Device *device, ID3D12Resource *resource,
        const D3D12_SHADER_RESOURCE_VIEW_DESC *desc, D3D12_CPU_DESCRIPTOR_HANDLE handle)
{
    void (STDMETHODCALLTYPE *orig)(ID3D12Device *, ID3D12Resource *, const D3D12_SHADER_RESOURCE_VIEW_DESC *,
            D3D12_CPU_DESCRIPTOR_HANDLE) = orig_fn(device, DEV_IDX(CreateShaderResourceView));

    if (desc && desc->ViewDimension == D3D12_SRV_DIMENSION_BUFFER && desc->Buffer.NumElements > WIDE_VIEW
            && !(desc->Buffer.Flags & D3D12_BUFFER_SRV_FLAG_RAW)
            && (desc->Format == DXGI_FORMAT_R32_UINT || desc->Format == DXGI_FORMAT_R32_SINT
                || desc->Format == DXGI_FORMAT_R32_FLOAT))
    {
        D3D12_SHADER_RESOURCE_VIEW_DESC raw = *desc;

        raw.Format = DXGI_FORMAT_R32_TYPELESS;
        raw.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW;
        raw.Buffer.StructureByteStride = 0;
        if (InterlockedIncrement(&g_views) <= 4)
            log_msg("Wide typed view (%u elements) created as a raw view", desc->Buffer.NumElements);
        orig(device, resource, &raw, handle);
        return;
    }
    orig(device, resource, desc, handle);
}

Ours: a UAV barrier after the volumetric fog copy, which removed the black blocks in the sandstorm stage.

fixes/sm2-tpose-fix.c · hk_Dispatch, lines 411–426
c
static void STDMETHODCALLTYPE hk_Dispatch(ID3D12GraphicsCommandList *list, UINT x, UINT y, UINT z)
{
    void (STDMETHODCALLTYPE *orig)(ID3D12GraphicsCommandList *, UINT, UINT, UINT) = orig_fn(list, LIST_IDX(Dispatch));

    orig(list, x, y, z);
    if (t_bound.list == list && t_bound.fog)
    {
        D3D12_RESOURCE_BARRIER barrier;

        memset(&barrier, 0, sizeof(barrier));
        barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
        barrier.UAV.pResource = NULL;   /* every UAV access before, against every one after */
        list->lpVtbl->ResourceBarrier(list, 1, &barrier);
        InterlockedIncrement(&g_fog_barriers);
    }
}

Thanks to

  • macprotipsThe diagnosis, the raw views and the four rewritten shaders (public domain). MacGameFix adds a carrier that needs no DLL override and a barrier after the volumetric fog copy.

Details

Needs
D3DMetal, D3D12
Installer
install-sm2-fix.sh
Executable
Spider-Man2.exe
Built from
sm2-tpose-fix.c, sm2fix_core.c, sm2-shadertool.c, sm2-shaderfix.c
Verified on
Preview 27, through NotProton; M5 Macs do not have the limit

The full story, with the measurements and the wrong turns