removed monkey patch from stable3dgen/trellis mentioned in https://github.com/MrForExample/ComfyUI-3D-Pack/issues/555#issuecomment-3694387910
226 lines
6.5 KiB
Python
226 lines
6.5 KiB
Python
# trellis/backend_config.py
|
|
from typing import *
|
|
import os
|
|
import logging
|
|
import importlib
|
|
|
|
# Global variables
|
|
BACKEND = 'spconv' # Default sparse backend
|
|
DEBUG = False # Debug mode flag
|
|
ATTN = 'xformers' # Default attention backend
|
|
SPCONV_ALGO = 'implicit_gemm' # Default algorithm
|
|
|
|
def get_spconv_algo() -> str:
|
|
"""Get current spconv algorithm."""
|
|
global SPCONV_ALGO
|
|
return SPCONV_ALGO
|
|
|
|
def set_spconv_algo(algo: Literal['implicit_gemm', 'native', 'auto']) -> bool:
|
|
"""Set spconv algorithm with validation."""
|
|
global SPCONV_ALGO
|
|
|
|
if algo not in ['implicit_gemm', 'native', 'auto']:
|
|
logger.warning(f"Invalid spconv algorithm: {algo}. Must be 'implicit_gemm', 'native', or 'auto'")
|
|
return False
|
|
|
|
SPCONV_ALGO = algo
|
|
os.environ['SPCONV_ALGO'] = algo
|
|
logger.info(f"Set spconv algorithm to: {algo}")
|
|
return True
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def _try_import_xformers() -> bool:
|
|
try:
|
|
import xformers.ops
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def _try_import_flash_attn() -> bool:
|
|
try:
|
|
import flash_attn
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def _try_import_sageattention() -> bool:
|
|
try:
|
|
import torch.nn.functional as F
|
|
from sageattention import sageattn
|
|
#F.scaled_dot_product_attention = sageattn
|
|
#import sageattention
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def _try_import_spconv() -> bool:
|
|
try:
|
|
import spconv
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def _try_import_torchsparse() -> bool:
|
|
try:
|
|
import torchsparse
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def get_available_backends() -> Dict[str, bool]:
|
|
"""Return dict of available attention backends and their status"""
|
|
return {
|
|
'xformers': _try_import_xformers(),
|
|
'flash_attn': _try_import_flash_attn(),
|
|
'sage': _try_import_sageattention(),
|
|
'naive': True,
|
|
'sdpa': True # Always available with PyTorch >= 2.0
|
|
}
|
|
|
|
def get_available_sparse_backends() -> Dict[str, bool]:
|
|
"""Return dict of available sparse backends and their status"""
|
|
return {
|
|
'spconv': _try_import_spconv(),
|
|
'torchsparse': _try_import_torchsparse()
|
|
}
|
|
|
|
def get_attention_backend() -> str:
|
|
"""Get current attention backend"""
|
|
global ATTN
|
|
return ATTN
|
|
|
|
def get_sparse_backend() -> str:
|
|
"""Get current sparse backend"""
|
|
global BACKEND
|
|
return BACKEND
|
|
|
|
def get_debug_mode() -> bool:
|
|
"""Get current debug mode status"""
|
|
global DEBUG
|
|
return DEBUG
|
|
|
|
def __from_env():
|
|
"""Initialize settings from environment variables"""
|
|
global BACKEND
|
|
global DEBUG
|
|
global ATTN
|
|
|
|
env_sparse_backend = os.environ.get('SPARSE_BACKEND')
|
|
env_sparse_debug = os.environ.get('SPARSE_DEBUG')
|
|
env_sparse_attn = os.environ.get('SPARSE_ATTN_BACKEND')
|
|
|
|
if env_sparse_backend is not None and env_sparse_backend in ['spconv', 'torchsparse']:
|
|
BACKEND = env_sparse_backend
|
|
if env_sparse_debug is not None:
|
|
DEBUG = env_sparse_debug == '1'
|
|
if env_sparse_attn is not None and env_sparse_attn in ['xformers', 'flash_attn', 'sage', 'sdpa', 'naive']:
|
|
ATTN = env_sparse_attn
|
|
os.environ['SPARSE_ATTN_BACKEND'] = env_sparse_attn
|
|
os.environ['ATTN_BACKEND'] = env_sparse_attn
|
|
|
|
logger.info(f"[SPARSE] Backend: {BACKEND}, Attention: {ATTN}")
|
|
|
|
def set_backend(backend: Literal['spconv', 'torchsparse']) -> bool:
|
|
"""Set sparse backend with validation"""
|
|
global BACKEND
|
|
|
|
backend = backend.lower().strip()
|
|
logger.info(f"Setting sparse backend to: {backend}")
|
|
|
|
if backend == 'spconv':
|
|
try:
|
|
import spconv
|
|
BACKEND = 'spconv'
|
|
os.environ['SPARSE_BACKEND'] = 'spconv'
|
|
return True
|
|
except ImportError:
|
|
logger.warning("spconv not available")
|
|
return False
|
|
|
|
elif backend == 'torchsparse':
|
|
try:
|
|
import torchsparse
|
|
BACKEND = 'torchsparse'
|
|
os.environ['SPARSE_BACKEND'] = 'torchsparse'
|
|
return True
|
|
except ImportError:
|
|
logger.warning("torchsparse not available")
|
|
return False
|
|
|
|
return False
|
|
|
|
def set_sparse_backend(backend: Literal['spconv', 'torchsparse'], algo: str = None) -> bool:
|
|
"""Alias for set_backend for backwards compatibility
|
|
|
|
Parameters:
|
|
backend: The sparse backend to use
|
|
algo: The algorithm to use (only relevant for spconv backend)
|
|
"""
|
|
# Call set_backend first
|
|
result = set_backend(backend)
|
|
|
|
# If algorithm is provided and backend was set successfully
|
|
if algo is not None and result:
|
|
set_spconv_algo(algo)
|
|
|
|
return result
|
|
|
|
def set_debug(debug: bool):
|
|
"""Set debug mode"""
|
|
global DEBUG
|
|
DEBUG = debug
|
|
if debug:
|
|
os.environ['SPARSE_DEBUG'] = '1'
|
|
else:
|
|
os.environ['SPARSE_DEBUG'] = '0'
|
|
|
|
def set_attn(attn: Literal['xformers', 'flash_attn', 'sage', 'sdpa', 'naive']) -> bool:
|
|
"""Set attention backend with validation"""
|
|
global ATTN
|
|
|
|
attn = attn.lower().strip()
|
|
logger.info(f"Setting attention backend to: {attn}")
|
|
|
|
if attn == 'xformers' and _try_import_xformers():
|
|
ATTN = 'xformers'
|
|
os.environ['SPARSE_ATTN_BACKEND'] = 'xformers'
|
|
os.environ['ATTN_BACKEND'] = 'xformers'
|
|
return True
|
|
|
|
elif attn == 'flash_attn' and _try_import_flash_attn():
|
|
ATTN = 'flash_attn'
|
|
os.environ['SPARSE_ATTN_BACKEND'] = 'flash_attn'
|
|
os.environ['ATTN_BACKEND'] = 'flash_attn'
|
|
return True
|
|
|
|
elif attn == 'sage' and _try_import_sageattention():
|
|
ATTN = 'sage'
|
|
os.environ['SPARSE_ATTN_BACKEND'] = 'sage'
|
|
os.environ['ATTN_BACKEND'] = 'sage'
|
|
return True
|
|
|
|
elif attn == 'sdpa':
|
|
ATTN = 'sdpa'
|
|
os.environ['SPARSE_ATTN_BACKEND'] = 'sdpa'
|
|
os.environ['ATTN_BACKEND'] = 'sdpa'
|
|
return True
|
|
|
|
elif attn == 'naive':
|
|
ATTN = 'naive'
|
|
os.environ['SPARSE_ATTN_BACKEND'] = 'naive'
|
|
os.environ['ATTN_BACKEND'] = 'naive'
|
|
return True
|
|
|
|
|
|
logger.warning(f"Attention backend {attn} not available")
|
|
return False
|
|
|
|
# Add alias for backwards compatibility
|
|
def set_attention_backend(backend: Literal['xformers', 'flash_attn', 'sage', 'sdpa']) -> bool:
|
|
"""Alias for set_attn for backwards compatibility"""
|
|
return set_attn(backend)
|
|
|
|
# Initialize from environment variables on module import
|
|
__from_env()
|