Skip to content

Instantly share code, notes, and snippets.

@roman01la
Created May 15, 2024 11:09
Show Gist options
  • Save roman01la/1097c98bfd79822953a94c7f3ffaa4e5 to your computer and use it in GitHub Desktop.
Save roman01la/1097c98bfd79822953a94c7f3ffaa4e5 to your computer and use it in GitHub Desktop.
Running Blender on multiple GPUs on Modal.com
  1. Put example.blend file near blend.py
  2. Run modal run blend.py
from pathlib import Path
from typing import Dict
import modal
import modal.gpu
app = modal.App("blender")
rendering_image = (
modal.Image.debian_slim(python_version="3.11")
.apt_install("xorg", "libxkbcommon0", "ffmpeg")
.pip_install("bpy==4.1.0")
)
def configure_rendering(ctx):
# configure the rendering process
ctx.scene.render.engine = "CYCLES"
ctx.scene.render.resolution_x = 1920
ctx.scene.render.resolution_y = 1080
ctx.scene.render.resolution_percentage = 100
ctx.scene.render.image_settings.file_format = 'PNG'
ctx.scene.cycles.samples = 4096
cycles = ctx.preferences.addons["cycles"]
cycles.preferences.compute_device_type = "CUDA"
ctx.scene.cycles.device = "GPU"
# reload the devices to update the configuration
cycles.preferences.get_devices()
for device in cycles.preferences.devices:
device.use = True
# report rendering devices -- a nice snippet for debugging and ensuring the accelerators are being used
for dev in cycles.preferences.devices:
print(
f"ID:{dev['id']} Name:{dev['name']} Type:{dev['type']} Use:{dev['use']}"
)
@app.function(
gpu=modal.gpu.L4(count=8),
image=rendering_image,
)
def render(blend_file: bytes, frame_number: int = 0) -> bytes:
"""Renders the n-th frame of a Blender file as a PNG."""
import bpy
input_path = "/tmp/input.blend"
output_path = f"/tmp/output-{frame_number}.png"
# Blender requires input as a file.
Path(input_path).write_bytes(blend_file)
bpy.ops.wm.open_mainfile(filepath=input_path)
bpy.context.scene.frame_set(frame_number)
bpy.context.scene.render.filepath = output_path
configure_rendering(bpy.context)
bpy.ops.render.render(write_still=True)
# Blender renders image outputs to a file as well.
return Path(output_path).read_bytes()
@app.function(image=rendering_image)
def run(blend_bytes: bytes) -> bytes:
output_directory = Path("/tmp") / "render"
output_directory.mkdir(parents=True, exist_ok=True)
args = [
(blend_bytes, frame) for frame in range(1, 1, 1)
]
images = list(render.starmap(args))
return images[0]
@app.local_entrypoint()
def main():
output_directory = Path("/tmp") / "render"
output_directory.mkdir(parents=True, exist_ok=True)
input_path = Path(__file__).parent / "example.blend"
blend_bytes = input_path.read_bytes()
video_bytes = run.remote(blend_bytes)
video_path = Path(__file__).parent / "image.png"
video_path.write_bytes(video_bytes)
print("DONE!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment