rayt-web / app.py
goedev's picture
add more info about performance comparison.
7f5ff84
import gradio as gr
import io
import sys
import pandas as pd
from PIL import Image
from rayt_rust._core import Camera, Point3, Vec3
from rayt.gpu_utils import is_cuda_available
from rayt.scene import random_scene
from rayt.cuda_renderer import render_with_cuda
from rayt.numba_renderer import render_with_numba
from rayt.rust_renderer import render_with_rust
from numba import cuda
IS_CUDA_AVAILABE, _ = is_cuda_available()
def render_image(image_width, samples_per_pixel, aspect_ratio_str, max_depth, engine):
"""
Renders an image using the rayt library and returns it.
"""
# --- Capture stdout ---
old_stdout = sys.stdout
sys.stdout = captured_output = io.StringIO()
# --- Run the rendering logic ---
aspect_ratio = eval(aspect_ratio_str)
image_height = int(image_width / aspect_ratio)
world = random_scene()
camera = Camera(
lookfrom=Point3(13, 2, 3),
lookat=Point3(0, 0, 0),
vup=Vec3(0, 1, 0),
vfov=20.0,
aspect_ratio=aspect_ratio,
aperture=0.1,
focus_dist=10.0,
)
render_funcs = {
"rust": render_with_rust,
"numba": render_with_numba,
"cuda": render_with_cuda,
}
if engine == "cuda" and not IS_CUDA_AVAILABE:
gr.Warning(
"CUDA renderer is not available in this server. Falling back to numba."
)
engine = "numba"
render_func = render_funcs[engine]
render_func(world, camera, image_width, image_height, samples_per_pixel, max_depth)
# --- Restore stdout and get the PPM data ---
sys.stdout = old_stdout
ppm_data = captured_output.getvalue()
# --- Convert PPM to Image ---
# The PPM data is in a string, so we need to wrap it in a file-like object
ppm_file = io.BytesIO(ppm_data.encode())
image = Image.open(ppm_file)
return image
# --- Create the Gradio Interface ---
with gr.Blocks() as iface:
gr.Markdown("# Ray Tracing in One Weekend")
with gr.Row():
with gr.Column():
gr.Markdown(
"A Python implementation of the book by Peter Shirley, rendered with Numba."
)
if IS_CUDA_AVAILABE:
device = cuda.get_current_device()
cc = device.compute_capability
cuda_info_dataframe = pd.DataFrame(
{
"CUDA support": ["AVAILABLE"],
"Found GPU": [device.name.decode("utf-8")],
"Compute Capability": [f"{cc[0]}.{cc[1]}"],
}
)
else:
cuda_info_dataframe = pd.DataFrame(
{
"CUDA support": ["NOT AVAILABLE"],
"Found GPU": ["-"],
"Compute Capability": ["-"],
}
)
gr.DataFrame(cuda_info_dataframe)
gr.Markdown(
"If you have an NVIDIA GPU on your local machine, you can clone the project and "
"run it locally. You can also deploy it to a server with CUDA support for faster "
"rendering."
)
gr.Image(
label="Sample Output",
value="image.webp",
show_download_button=False,
show_fullscreen_button=False,
show_label=True,
)
with gr.Column():
gr.Markdown("## PERFORMANCE")
gr.Markdown("RAYT tested on this machine:")
test_info_dataframe = pd.DataFrame(
{
"CPU": ["AMD Radeon 9 9900X"],
"GPU": ["NVIDIA RTX 5080"],
"Image Width": ["600"],
"Samples Per Pixel": ["100"],
}
)
gr.DataFrame(test_info_dataframe)
gr.Image(
label="Performance",
value="performance.png",
show_download_button=False,
show_fullscreen_button=False,
show_label=True,
)
with gr.Row():
with gr.Column():
gr.Markdown("## INPUT")
image_width = gr.Slider(
label="Image Width", minimum=100, maximum=1200, value=300, step=10
)
samples_per_pixel = gr.Slider(
label="Samples Per Pixel", minimum=1, maximum=200, value=20, step=1
)
max_depth = gr.Slider(
label="Max Ray Depth", minimum=1, maximum=100, value=50, step=1
)
aspect_ratio = gr.Dropdown(
label="Aspect Ratio", choices=["16/9", "3/2", "1/1"], value="16/9"
)
engine = gr.Dropdown(
label="Engine", choices=["rust", "numba", "cuda"], value="rust"
)
render_button = gr.Button("Render Image")
with gr.Column():
gr.Markdown("## OUTPUT")
output_image = gr.Image(label="Rendered Image", type="pil")
render_button.click(
fn=render_image,
inputs=[image_width, samples_per_pixel, aspect_ratio, max_depth, engine],
outputs=output_image,
)
if __name__ == "__main__":
iface.launch()