- Put
example.blend
file nearblend.py
- Run
modal run blend.py
Created
May 15, 2024 11:09
-
-
Save roman01la/1097c98bfd79822953a94c7f3ffaa4e5 to your computer and use it in GitHub Desktop.
Running Blender on multiple GPUs on Modal.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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