- Remove DistributedListSplitter and DistributedListCollector nodes - Remove /distributed/init_list_queue and /distributed/request_list_item endpoints - Clean up all references from prompt_transform, queue_orchestration, constants, and tests - Refactor and reorganize distributed modules for quality improvements
105 lines
3.9 KiB
Python
105 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import math, torch
|
|
from typing import Any
|
|
from ...utils.logging import log
|
|
from ...utils.image import blend_processed_batch_item, pil_to_tensor, tensor_to_pil
|
|
from ..mode_contexts import SingleGpuModeContext, TileOpsCollaborator
|
|
from ..processing_args import UpscaleCoreArgs
|
|
from ..tile_processing import TileBatchArgs, extract_and_process_tile_batch
|
|
|
|
|
|
class SingleGpuModeMixin:
|
|
def _build_single_gpu_mode_context(self) -> SingleGpuModeContext:
|
|
"""Build explicit collaborators for single-GPU mode execution."""
|
|
return SingleGpuModeContext(tile_ops=TileOpsCollaborator(self))
|
|
|
|
def process_single_gpu(
|
|
self,
|
|
upscaled_image: torch.Tensor,
|
|
core_args: UpscaleCoreArgs,
|
|
tile_width: int,
|
|
tile_height: int,
|
|
padding: int,
|
|
mask_blur: int,
|
|
force_uniform_tiles: bool,
|
|
mode_context: SingleGpuModeContext | None = None,
|
|
) -> tuple[torch.Tensor]:
|
|
"""Process all tiles on a single GPU (no distribution), batching per tile like USDU."""
|
|
context = mode_context or self._build_single_gpu_mode_context()
|
|
ops = context.tile_ops
|
|
# Round tile dimensions
|
|
tile_width = ops.round_to_multiple(tile_width)
|
|
tile_height = ops.round_to_multiple(tile_height)
|
|
|
|
# Get image dimensions and batch size
|
|
batch_size, height, width, _ = upscaled_image.shape
|
|
|
|
# Calculate all tiles
|
|
all_tiles = ops.calculate_tiles(width, height, tile_width, tile_height, force_uniform_tiles)
|
|
|
|
rows = math.ceil(height / tile_height)
|
|
cols = math.ceil(width / tile_width)
|
|
log(
|
|
f"USDU Dist: Single GPU | Canvas {width}x{height} | Tile {tile_width}x{tile_height} | Grid {rows}x{cols} ({len(all_tiles)} tiles/image) | Batch {batch_size}"
|
|
)
|
|
tile_batch_args = TileBatchArgs(
|
|
core=core_args,
|
|
tile_width=tile_width,
|
|
tile_height=tile_height,
|
|
padding=padding,
|
|
force_uniform_tiles=force_uniform_tiles,
|
|
width=width,
|
|
height=height,
|
|
)
|
|
|
|
# Prepare result images list
|
|
result_images = []
|
|
for b in range(batch_size):
|
|
image_pil = tensor_to_pil(upscaled_image[b:b+1], 0).convert('RGB')
|
|
result_images.append(image_pil.copy())
|
|
|
|
# Precompute tile masks once
|
|
tile_masks = []
|
|
for tx, ty in all_tiles:
|
|
tile_masks.append(ops.create_tile_mask(width, height, tx, ty, tile_width, tile_height, mask_blur))
|
|
|
|
# Process tiles batched across images
|
|
for tile_idx, (tx, ty) in enumerate(all_tiles):
|
|
# Progressive state parity: extract each tile from the current updated image batch.
|
|
source_batch = torch.cat([pil_to_tensor(img) for img in result_images], dim=0)
|
|
if upscaled_image.is_cuda:
|
|
source_batch = source_batch.cuda()
|
|
|
|
processed_batch, x1, y1, ew, eh = extract_and_process_tile_batch(
|
|
node=ops,
|
|
upscaled_image=source_batch,
|
|
tx=tx,
|
|
ty=ty,
|
|
args=tile_batch_args,
|
|
)
|
|
|
|
# Blend results back into each image using cached mask
|
|
tile_mask = tile_masks[tile_idx]
|
|
for b in range(batch_size):
|
|
blend_processed_batch_item(
|
|
result_images,
|
|
processed_batch,
|
|
b,
|
|
ops.blend_tile,
|
|
x1,
|
|
y1,
|
|
ew,
|
|
eh,
|
|
tile_mask,
|
|
padding,
|
|
)
|
|
|
|
# Convert back to tensor
|
|
result_tensors = [pil_to_tensor(img) for img in result_images]
|
|
result_tensor = torch.cat(result_tensors, dim=0)
|
|
if upscaled_image.is_cuda:
|
|
result_tensor = result_tensor.cuda()
|
|
|
|
return (result_tensor,)
|