diff --git a/zigrtiow/src/main.zig b/zigrtiow/src/main.zig index 275393a..8b56961 100644 --- a/zigrtiow/src/main.zig +++ b/zigrtiow/src/main.zig @@ -8,6 +8,8 @@ const hittable = @import("./hittable.zig"); const hittable_list = @import("./hittable_list.zig"); const material = @import("./material.zig"); +const Thread = std.Thread; +const Queue = std.atomic.Queue; const Camera = camera.Camera; const Color = vec.Color; const Hittable = hittable.Hittable; @@ -38,6 +40,43 @@ fn ray_color(r: Ray, world: Hittable, depth: isize) Color { return Color.init(1, 1, 1).scale(1 - t).add(Color.init(0.5, 0.7, 1.0).scale(t)); } +const Task = struct { + cam: Camera, + samples_per_pixel: usize, + max_depth: isize, + row_idx: usize, + image_height: isize, + image_width: isize, + row_pixels: []Color, + world: Hittable, +}; + +fn render_row(q: *Queue(Task)) void { + var prng = std.rand.DefaultPrng.init(0); + const rand = prng.random(); + while (true) { + if (q.get()) |node| { + const task = node.data; + info("Rendering row: {}", .{task.row_idx}); + const j = task.row_idx; + var i: usize = 0; + while (i < task.image_width) : (i += 1) { + var pixel_color = Color.init(0, 0, 0); + var s: isize = 0; + while (s < task.samples_per_pixel) : (s += 1) { + const u = (@intToFloat(f32, i) + rand.float(f32)) / @intToFloat(f32, task.image_width - 1); + const v = (@intToFloat(f32, j) + rand.float(f32)) / @intToFloat(f32, task.image_height - 1); + const r = task.cam.get_ray(u, v); + pixel_color = pixel_color.add(ray_color(r, task.world, task.max_depth)); + } + task.row_pixels[i] = pixel_color; + } + } else { + return; + } + } +} + pub fn main() anyerror!void { // Image const aspect_ratio: f32 = 16.0 / 9.0; @@ -64,27 +103,56 @@ pub fn main() anyerror!void { const cam = Camera.init(); // Render + const Node = Queue(Task).Node; + const allocator = std.heap.page_allocator; + const cpus = try Thread.getCpuCount(); + var pixels: [image_width * image_height]Color = undefined; + var q = Queue(Task).init(); + var threads: std.ArrayList(std.Thread) = std.ArrayList(std.Thread).init(allocator); + var row: usize = 0; + while (row < image_height) : (row += 1) { + const node = try allocator.create(Node); + const start: usize = row * image_width; + const end: usize = (row + 1) * image_width; + node.* = .{ + .prev = undefined, + .next = undefined, + .data = Task{ + .cam = cam, + .samples_per_pixel = samples_per_pixel, + .max_depth = max_depth, + .row_idx = row, + .image_width = image_width, + .image_height = image_height, + .row_pixels = pixels[start..end], + .world = world, + }, + }; + q.put(node); + } + + var t: usize = 0; + while (t < cpus) : (t += 1) { + try threads.append(try Thread.spawn(.{}, render_row, .{&q})); + } + + // Wait for all threads to finish. + for (threads.items) |thread| { + Thread.join(thread); + } + const stdout = std.io.getStdOut(); info("Writing image {d}x{d}", .{ image_width, image_height }); try stdout.writer().print("P3\n{d} {d}\n255\n", .{ image_width, image_height }); - var prng = std.rand.DefaultPrng.init(0); - const rand = prng.random(); - var j: isize = image_height - 1; - while (j >= 0) : (j -= 1) { - info("Scanlines remaining: {d}", .{j}); - - var i: isize = 0; + var j: usize = 0; + while (j < image_height) : (j += 1) { + var i: usize = 0; while (i < image_width) : (i += 1) { - var pixel_color = Color.init(0, 0, 0); - var s: isize = 0; - while (s < samples_per_pixel) : (s += 1) { - const u = (@intToFloat(f32, i) + rand.float(f32)) / @intToFloat(f32, image_width - 1); - const v = (@intToFloat(f32, j) + rand.float(f32)) / @intToFloat(f32, image_height - 1); - const r = cam.get_ray(u, v); - pixel_color = pixel_color.add(ray_color(r, world, max_depth)); - } + // Flip image upside down. + const j_idx = @as(usize, image_height) - j - 1; + const pixel_color = pixels[i + j_idx * image_width]; try write_color(pixel_color, samples_per_pixel); } }