this repo has no description
at 99607ee3976658e1d00a96ec22bd6d7fc0b7e4b6 178 lines 5.7 kB view raw
1const std = @import("std"); 2const build_options = @import("build-options"); 3 4pub const zmath = @import("zmath"); 5 6const zigimg = @import("zigimg"); 7const color = zigimg.color; 8 9pub const BVH = @import("BVH.zig"); 10pub const Camera = @import("Camera.zig"); 11pub const hittable = @import("hittable.zig"); 12pub const interval = @import("interval.zig"); 13const IntervalUsize = interval.IntervalUsize; 14pub const material = @import("material.zig"); 15pub const tracer = @import("tracer.zig"); 16pub const util = @import("util.zig"); 17 18const log = std.log.scoped(.rayray); 19 20pub const TaskTracker = struct { 21 marked_as_done: bool = false, 22 done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), 23 thread_id: std.Thread.Id = 0, 24}; 25 26pub const Raytracer = struct { 27 const Self = @This(); 28 29 allocator: std.mem.Allocator, 30 thread_pool: *std.Thread.Pool, 31 32 camera: Camera, 33 world: BVH, 34 35 pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList, camera_opts: Camera.Options) !Self { 36 var thread_pool = try allocator.create(std.Thread.Pool); 37 try thread_pool.init(.{ .allocator = allocator }); 38 39 return .{ 40 .allocator = allocator, 41 .thread_pool = thread_pool, 42 .camera = try Camera.init(allocator, camera_opts), 43 .world = try BVH.init(allocator, world, build_options.max_depth), 44 }; 45 } 46 47 pub fn deinit(self: *Self) void { 48 self.camera.deinit(); 49 self.world.deinit(); 50 51 self.thread_pool.deinit(); 52 self.allocator.destroy(self.thread_pool); 53 } 54 55 pub fn render(self: *Self) !zigimg.Image { 56 const chunk_height: usize = 25; 57 const chunk_width: usize = 25; 58 59 var rows: usize = @divTrunc(self.camera.image_height, chunk_height); 60 if (self.camera.image_height % rows != 0) { 61 rows += 1; 62 } 63 64 var cols: usize = @divTrunc(self.camera.image_width, chunk_width); 65 if (self.camera.image_width % cols != 0) { 66 cols += 1; 67 } 68 69 const num_chunks = cols * rows; 70 71 const num_threads = blk: { 72 const count = try std.Thread.getCpuCount(); 73 if (count > 1) { 74 break :blk count; 75 } else break :blk 1; 76 }; 77 78 log.debug("rows: {}, cols: {}, chunk_height: {}, chunk_width: {}, num_chunks: {}, num_threads: {}", .{ 79 rows, 80 cols, 81 chunk_height, 82 chunk_width, 83 num_chunks, 84 num_threads, 85 }); 86 87 const tasks = try self.allocator.alloc(TaskTracker, num_chunks); 88 defer self.allocator.free(tasks); 89 90 for (tasks, 0..) |*t, id| { 91 const row: usize = @divTrunc(id, cols) * chunk_height; 92 const col: usize = (id - cols * @divTrunc(id, cols)) * chunk_width; 93 94 const c_height = IntervalUsize{ .min = row, .max = row + chunk_height }; 95 const c_width = IntervalUsize{ .min = col, .max = col + chunk_width + 1 }; 96 97 const ctx = tracer.Context{ 98 .cam = &self.camera, 99 .world = &self.world, 100 .height = c_height, 101 .width = c_width, 102 }; 103 104 try self.thread_pool.spawn( 105 renderThread, 106 .{ ctx, t, id }, 107 ); 108 } 109 110 // const stderr = std.io.getStdErr(); 111 112 // var progress = std.Progress{ 113 // .terminal = stderr, 114 // .supports_ansi_escape_codes = true, 115 // }; 116 // var node = progress.start("Rendered Chunks", num_chunks); 117 // node.setCompletedItems(0); 118 // node.context.refresh(); 119 120 var thread_to_idx = std.ArrayList(std.Thread.Id).init(self.allocator); 121 defer thread_to_idx.deinit(); 122 123 var root_node = std.Progress.start(.{ 124 .root_name = "Ray Tracer", 125 .estimated_total_items = num_chunks, 126 }); 127 var nodes = std.ArrayList(std.Progress.Node).init(self.allocator); 128 defer nodes.deinit(); 129 130 for (0..num_threads) |_| { 131 try nodes.append(root_node.start("Chunks Rendered", num_chunks / num_threads)); 132 } 133 134 var completed_chunks: u64 = 0; 135 var i: usize = 0; 136 while (true) { 137 var done = true; 138 139 for (tasks) |*t| { 140 const task_done = t.done.load(.acquire); 141 142 if (task_done and !t.marked_as_done) { 143 t.marked_as_done = true; 144 145 const idx = blk: { 146 for (thread_to_idx.items, 0..) |value, idx| { 147 if (value == t.thread_id) break :blk idx; 148 } 149 try thread_to_idx.append(t.thread_id); 150 const idx = i; 151 i += 1; 152 break :blk idx; 153 }; 154 nodes.items[idx].completeOne(); 155 156 completed_chunks += 1; 157 root_node.setCompletedItems(completed_chunks); 158 // if (completed_chunks % self.thread_pool.threads.len == 0) try self.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); 159 } else if (!task_done) { 160 done = false; 161 } 162 } 163 164 if (done or !self.thread_pool.is_running) break; 165 } 166 167 // node.end(); 168 169 return self.camera.image; 170 } 171}; 172 173pub fn renderThread(ctx: tracer.Context, task: *TaskTracker, id: usize) void { 174 defer task.done.store(true, .release); 175 task.thread_id = std.Thread.getCurrentId(); 176 _ = id; 177 tracer.trace(ctx); 178}