rayt-web / app.py
goedev's picture
add more info about performance comparison.
7f5ff84
Raw History Blame Contribute Delete
5.28 kB
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()