this repo has no description
1const std = @import("std"); 2 3const spall = @import("spall"); 4const zigimg = @import("zigimg"); 5const color = zigimg.color; 6 7const IntervalUsize = @import("a").interval.IntervalUsize; 8 9pub const Camera = @import("camera.zig"); 10pub const hittable = @import("hittable.zig"); 11pub const material = @import("material.zig"); 12pub const tracer = @import("tracer.zig"); 13pub const util = @import("util.zig"); 14 15const log = std.log.scoped(.rayray); 16 17pub const TaskTracker = struct { 18 marked_as_done: bool = false, 19 done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), 20}; 21 22pub const Raytracer = struct { 23 const Self = @This(); 24 25 allocator: std.mem.Allocator, 26 thread_pool: *std.Thread.Pool, 27 28 camera: Camera, 29 world: hittable.HittableList, 30 31 pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList, camera_opts: Camera.Options) !Self { 32 var thread_pool = try allocator.create(std.Thread.Pool); 33 try thread_pool.init(.{ .allocator = allocator }); 34 35 return .{ 36 .allocator = allocator, 37 .thread_pool = thread_pool, 38 .camera = try Camera.init(allocator, camera_opts), 39 .world = world, 40 }; 41 } 42 43 pub fn deinit(self: *Self) void { 44 self.camera.deinit(); 45 self.world.deinit(); 46 47 self.thread_pool.deinit(); 48 self.allocator.destroy(self.thread_pool); 49 } 50 51 pub fn render(self: *Self) !zigimg.Image { 52 const s = spall.trace(@src(), "Render", .{}); 53 defer s.end(); 54 55 const chunk_height: usize = 25; 56 const chunk_width: usize = 25; 57 58 var rows: usize = @divTrunc(self.camera.image_height, chunk_height); 59 if (self.camera.image_height % rows != 0) { 60 rows += 1; 61 } 62 63 var cols: usize = @divTrunc(self.camera.image_width, chunk_width); 64 if (self.camera.image_width % cols != 0) { 65 cols += 1; 66 } 67 68 const num_chunks = cols * rows; 69 70 log.debug("rows: {}, cols: {}, chunk_height: {}, chunk_width: {}, num_chunks: {}, num_threads: {}", .{ 71 rows, 72 cols, 73 chunk_height, 74 chunk_width, 75 num_chunks, 76 self.thread_pool.threads.len, 77 }); 78 79 const tasks = try self.allocator.alloc(TaskTracker, num_chunks); 80 defer self.allocator.free(tasks); 81 82 for (tasks, 0..) |*t, id| { 83 const row: usize = @divTrunc(id, cols) * chunk_height; 84 const col: usize = (id - cols * @divTrunc(id, cols)) * chunk_width; 85 86 const c_height = IntervalUsize{ .min = row, .max = row + chunk_height }; 87 const c_width = IntervalUsize{ .min = col, .max = col + chunk_width + 1 }; 88 89 const ctx = tracer.Context{ 90 .cam = &self.camera, 91 .world = &self.world, 92 .height = c_height, 93 .width = c_width, 94 }; 95 96 // log.debug("Spawning chunk: {}, row start: {}, col start: {}", .{ id, row, col }); 97 98 try self.thread_pool.spawn( 99 renderThread, 100 .{ ctx, t, id }, 101 ); 102 } 103 104 const stderr = std.io.getStdErr(); 105 106 var progress = std.Progress{ 107 .terminal = stderr, 108 .supports_ansi_escape_codes = true, 109 }; 110 111 var node = progress.start("Rendered Chunks", num_chunks); 112 node.setCompletedItems(0); 113 node.context.refresh(); 114 115 while (true) { 116 var done = true; 117 118 for (tasks) |*t| { 119 const task_done = t.done.load(.acquire); 120 121 if (task_done and !t.marked_as_done) { 122 t.marked_as_done = true; 123 node.completeOne(); 124 try self.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); 125 } else if (!task_done) { 126 done = false; 127 } 128 } 129 130 if (done or !self.thread_pool.is_running) break; 131 } 132 133 node.end(); 134 135 return self.camera.image; 136 } 137}; 138 139pub fn renderThread(ctx: tracer.Context, task: *TaskTracker, id: usize) void { 140 _ = id; 141 tracer.trace(ctx); 142 task.done.store(true, .release); 143}