Skip to content

Windows/Blackwell (SM 12.0) compatibility: multiple failures preventing shape generation and texturing #157

Description

@dbollwerk

I want to be upfront: I'm a 3D Previs Artist, not a software engineer. I've been working through these issues with Claude "vibe coding" I've done my best to document the root causes accurately, but I may have missed nuance in places. If this has any value I'd be happy to provide full diffs of everything we changed if that's useful.

Environment:

OS: Windows 11
GPU: RTX 5090 (Blackwell, SM 12.0 / CC 12.0)
Python: 3.13.12 (embedded)
PyTorch: 2.12.0.dev20260226+cu130
CUDA: 13.0
spconv: latest
flash_attn: not installed (MSVC/CUTLASS incompatible with SM 12.0)
ComfyUI: 0.15.1

Summary:
On Windows with a Blackwell GPU, several incompatibilities prevent the pipeline from running at all. After patching, shape generation works correctly. Texturing runs but output quality is degraded due to the missing grid_sample_3d fallback (see below). This post documents every issue we found and the workarounds applied.

Issue 1: spconv bfloat16 incompatibility

File: trellis2/modules/sparse/conv/conv_spconv.py
Problem: The pipeline runs in bfloat16 on modern GPUs, but spconv's native C++ kernels don't support bfloat16. This causes a KeyError: torch.bfloat16 crash before any generation happens.
Fix applied: Wrapped forward and inverse_forward with a cast:
python

def sparse_conv3d_forward(self, x):
original_dtype = x.feats.dtype
if original_dtype == torch.bfloat16:
x = x.replace(x.feats.to(torch.float16))
out = # ... original forward ...
if original_dtype == torch.bfloat16:
out = out.replace(out.feats.to(original_dtype))
return out

Issue 2: flash_attn mock causes silent garbage attention output

File: trellis2/modules/sparse/attention/full_attn.py
Problem: flash_attn is unavailable on Windows/Blackwell. It gets mocked in sys.modules at startup. However, something in the pipeline sets config.ATTN = 'flash_attn' at runtime after module load (source never identified — may be in pipeline init or model loading). When the flash_attn branch executes, it calls the mock which returns a MagicMock object. The replace() guard in basic.py catches this and returns the original tensor unchanged — meaning qkv [N, 3, H, C] is used as the attention output instead of the attended [N, H, C]. Shape generation produces garbage silently.
Fix applied: Redirected the flash_attn branch to sdpa:
python

elif config.ATTN == 'flash_attn':
out = _sdpa_varlen(q, k, v, q_seqlen, kv_seqlen)

Open question: What is setting config.ATTN = 'flash_attn' at runtime? The default in config.py is already set to 'sdpa' but something overrides it.

Issue 3: Attention and conv backend config defaults

Files: trellis2/modules/sparse/config.py, trellis2/modules/attention/config.py
Problem: 'sdpa' was not in the valid values list for ATTN, causing validation errors. Default values needed to be set explicitly.
Fix applied:

sparse/config.py: set CONV = 'spconv', ATTN = 'sdpa', added 'sdpa' to valid env values
attention/config.py: set BACKEND = 'sdpa'

Issue 4: basic.py SparseTensor crashes when feats is a dict

File: trellis2/modules/sparse/basic.py
Problem: ComfyUI strips custom class wrappers when passing data between nodes. When a SparseTensor passes through the ComfyUI graph, spconv data can arrive as a plain Python dict instead of a C++ spconv object. Accessing .features on a dict crashes.
Fix applied:

feats property: added guard to return _features if it exists and is not None
replace() method: added if not isinstance(feats, torch.Tensor): return self guard
replace() spconv branch: sets both new_data._features = feats and new_data.features = feats to handle both accessor patterns

Issue 5: flex_gemm mock causes silent texture failure (the big one)

Files: trellis2/pipelines/trellis2_image_to_3d.py, nodes.py, trellis2/representations/mesh/base.py
Problem: flex_gemm is unavailable on Windows. It gets mocked in sys.modules at startup. The import guard in each file is:
python

try:
from flex_gemm.ops.grid_sample import grid_sample_3d
HAS_FLEX_GEMM_GRID = True
except Exception:
grid_sample_3d = None
HAS_FLEX_GEMM_GRID = False

Because flex_gemm is already in sys.modules as a MagicMock, the import succeeds — grid_sample_3d becomes a MagicMock, not None. The except branch never fires. Every subsequent if grid_sample_3d is not None: check passes, the mock is called, returns garbage silently, and the texture is near-black with no error.
Fix applied: Added mock detection at import in all three files:
python

try:
from flex_gemm.ops.grid_sample import grid_sample_3d
import inspect
if not inspect.isfunction(grid_sample_3d) and not inspect.isbuiltin(grid_sample_3d):
raise ImportError("grid_sample_3d is mocked")
HAS_FLEX_GEMM_GRID = True
except Exception:
grid_sample_3d = None
HAS_FLEX_GEMM_GRID = False

Current status of fallback: With grid_sample_3d correctly set to None, the texturing pipeline reaches the fallback path. We have been experimenting with sparse trilinear interpolation replacements but have not matched the quality of the original. We're currently seeing ~8-18% hit rate when sampling mesh surface positions against the sparse voxel grid, resulting in degraded texture coverage.

Question:
Is there an official fallback planned for platforms where flex_gemm is unavailable?
What coordinate system does grid_sample_3d expect for the grid parameter? Is it normalized [-1,1] or voxel-space [0, N]?
Is pbr_voxel a surface shell (voxels only at the mesh surface) or a filled volume? We're confused about why ~90% of mesh surface query points fall in empty voxels.
Would a pure F.grid_sample on a downsampled dense grid be a viable fallback, or is the sparse format essential for memory reasons?

Issue 6: Blackwell SM 12.0 compute capability spoofing

File: Custom blackwell_fix.py injected at startup
Problem: Several CUDA kernels fail or are unavailable on SM 12.0 (Blackwell). spconv uses static Ampere kernels that work correctly when CC is spoofed to (8, 6).
Fix applied: Early in init.py, spoof compute capability before any CUDA kernel compilation:
python

import torch.cuda
torch.cuda.get_device_capability = lambda device=None: (8, 6)

Also set SPCONV_DISABLE_JIT=1 and CUMM_DISABLE_JIT=1 environment variables to prevent NVRTC JIT compilation failures.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions