this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chunk based rendering on a thread pool

Altagos 514a48c3 997ba3b3

+97 -51
+4 -4
build.zig.zon
··· 8 8 .url = "https://git.sr.ht/~altagos/a/archive/8c8f2cc405743d062f5b617e2f8ab85ce5dc0409.tar.gz", 9 9 .hash = "12203338074c5e0fed0696abfa1ec868da0d1bb22539e5fd4220b2585a8f3cf0c788", 10 10 }, 11 - .zigimg = .{ 12 - .url = "https://github.com/rockorager/zigimg/archive/19a49a7e44fb4b1c22341dfbd6566019de742055.tar.gz", 13 - .hash = "1220ebfa8587cfd644995fc08e218dbb3ebd7344fb8e129ff02bc5a6d52a2325370d", 14 - }, 15 11 .spall = .{ 16 12 .url = "https://git.sr.ht/~altagos/zig-spall/archive/7cae52aa2d1a519006e0c90fbd179cf7fcac0c83.tar.gz", 17 13 .hash = "1220753b2f9e7c4f3fb6cdc8fc275cc9073904ea7032a1f5596aa776a857de69f72c", 14 + }, 15 + .zigimg = .{ 16 + .url = "git+https://github.com/MidlightStudio/zigimg#29d34702995a7e84e097004e87ede905cdb674dd", 17 + .hash = "12206b669a4712ff9c06d4111a650f298d7597dd39f9d4853858d7f298c819353a65", 18 18 }, 19 19 20 20 // libs folder
+88 -44
src/rayray.zig
··· 4 4 const zigimg = @import("zigimg"); 5 5 const color = zigimg.color; 6 6 7 + const IntervalUsize = @import("a").interval.IntervalUsize; 8 + 7 9 pub const Camera = @import("camera.zig"); 8 10 pub const hittable = @import("hittable.zig"); 9 11 pub const material = @import("material.zig"); 10 12 pub const tracer = @import("tracer.zig"); 11 13 12 - const IntervalUsize = @import("a").interval.IntervalUsize; 13 - 14 14 const log = std.log.scoped(.rayray); 15 15 16 - const ThreadTracker = struct { 17 - thread: std.Thread, 18 - done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), 16 + pub const TaskTracker = struct { 19 17 marked_as_done: bool = false, 18 + done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), 20 19 }; 21 20 22 21 pub const Raytracer = struct { 23 22 const Self = @This(); 24 23 25 24 allocator: std.mem.Allocator, 25 + thread_pool: *std.Thread.Pool, 26 26 27 27 camera: Camera, 28 28 world: hittable.HittableList, 29 29 30 30 pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList, camera_opts: Camera.Options) !Self { 31 + var thread_pool = try allocator.create(std.Thread.Pool); 32 + try thread_pool.init(.{ .allocator = allocator }); 33 + 31 34 return .{ 32 35 .allocator = allocator, 36 + .thread_pool = thread_pool, 33 37 .camera = try Camera.init(allocator, camera_opts), 34 38 .world = world, 35 39 }; ··· 38 42 pub fn deinit(self: *Self) void { 39 43 self.camera.deinit(); 40 44 self.world.deinit(); 45 + 46 + self.thread_pool.deinit(); 47 + self.allocator.destroy(self.thread_pool); 41 48 } 42 49 43 50 // TODO: Render in cubes not in rows ··· 45 52 const s = spall.trace(@src(), "Render", .{}); 46 53 defer s.end(); 47 54 48 - const rows: usize = try std.Thread.getCpuCount() - 1; 49 - const row_height = @divTrunc(self.camera.image_height, rows); 50 - const num_threads = blk: { 51 - if (self.camera.image_height % rows == 0) { 52 - break :blk rows; 53 - } 54 - break :blk rows + 1; 55 - }; 55 + // const rows: usize = try std.Thread.getCpuCount() - 1; 56 + // const row_height = @divTrunc(self.camera.image_height, rows); 57 + // const num_threads = blk: { 58 + // if (self.camera.image_height % rows == 0) { 59 + // break :blk rows; 60 + // } 61 + // break :blk rows + 1; 62 + // }; 56 63 57 - log.debug("rows: {}, row_height: {}, num_threads: {}", .{ rows, row_height, num_threads }); 64 + const chunk_height: usize = 25; 65 + const chunk_width: usize = 25; 66 + 67 + var rows: usize = @divTrunc(self.camera.image_height, chunk_height); 68 + if (self.camera.image_height % rows != 0) { 69 + rows += 1; 70 + } 71 + 72 + var cols: usize = @divTrunc(self.camera.image_width, chunk_width); 73 + if (self.camera.image_width % cols != 0) { 74 + cols += 1; 75 + } 76 + 77 + const num_chunks = cols * rows; 78 + 79 + // log.debug("rows: {}, cols: {}, chunk_height: {}, chunk_width: {}, num_chunks: {}, num_threads: {}", .{ 80 + // rows, 81 + // cols, 82 + // chunk_height, 83 + // chunk_width, 84 + // num_chunks, 85 + // self.thread_pool.threads.len, 86 + // }); 58 87 59 - var threads = try self.allocator.alloc(ThreadTracker, num_threads); 60 - defer self.allocator.free(threads); 88 + var tasks = try self.allocator.alloc(TaskTracker, num_chunks); 89 + defer self.allocator.free(tasks); 61 90 62 - for (0..num_threads) |row| { 63 - const ctx = tracer.Context{ .cam = &self.camera, .world = &self.world }; 64 - const t = try std.Thread.spawn( 65 - .{}, 91 + for (tasks, 0..) |*t, id| { 92 + var row: usize = id / cols; 93 + var col: usize = id - cols * row; 94 + 95 + row *= chunk_height; 96 + col *= chunk_width; 97 + 98 + const c_height = IntervalUsize{ .min = row, .max = row + chunk_height }; 99 + const c_width = IntervalUsize{ .min = col, .max = col + chunk_width + 1 }; 100 + 101 + const ctx = tracer.Context{ 102 + .cam = &self.camera, 103 + .world = &self.world, 104 + .height = c_height, 105 + .width = c_width, 106 + }; 107 + 108 + log.debug("Spawning chunk: {}, row start: {}, col start: {}", .{ id, row, col }); 109 + 110 + try self.thread_pool.spawn( 66 111 renderThread, 67 - .{ 68 - ctx, 69 - &threads[row].done, 70 - row, 71 - row_height, 72 - }, 112 + .{ ctx, t, id }, 73 113 ); 74 - threads[row].thread = t; 75 114 } 76 115 77 116 const stderr = std.io.getStdErr(); ··· 80 119 .terminal = stderr, 81 120 .supports_ansi_escape_codes = true, 82 121 }; 83 - var node = progress.start("Rendered Chunks", num_threads); 122 + 123 + var node = progress.start("Rendered Chunks", num_chunks); 84 124 node.setCompletedItems(0); 85 125 node.context.refresh(); 86 126 87 127 while (true) { 88 128 var done = true; 89 129 90 - for (0..num_threads) |id| { 91 - const thead_done = threads[id].done.load(.Acquire); 92 - if (thead_done and !threads[id].marked_as_done) { 93 - threads[id].thread.join(); 94 - threads[id].marked_as_done = true; 130 + for (0..num_chunks) |id| { 131 + const task_done = tasks[id].done.load(.acquire); 95 132 133 + if (task_done and !tasks[id].marked_as_done) { 134 + // threads[id].thread.join(); 135 + tasks[id].marked_as_done = true; 96 136 node.completeOne(); 97 - } else if (!thead_done) { 137 + try self.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); 138 + node.context.refresh(); 139 + } else if (!task_done) { 98 140 done = false; 99 141 } 100 142 } 101 143 102 - if (done) break; 144 + if (done or !self.thread_pool.is_running) break; 103 145 } 104 146 105 - // node.end(); 147 + node.end(); 106 148 107 149 return self.camera.image; 108 150 } 109 151 }; 110 152 111 - pub fn renderThread(ctx: tracer.Context, done: *std.atomic.Value(bool), row: usize, row_height: usize) void { 153 + pub fn renderThread(ctx: tracer.Context, task: *TaskTracker, id: usize) void { 112 154 spall.init_thread(); 113 155 defer spall.deinit_thread(); 114 156 115 - const height = IntervalUsize{ .min = row_height * row, .max = row_height * row + row_height }; 116 - const width = IntervalUsize{ .min = 0, .max = ctx.cam.image_width }; 117 - 118 157 // log.debug("Started Render Thread {}", .{row}); 119 158 120 - const s = spall.trace(@src(), "Render Thread {}", .{row}); 121 - defer s.end(); 159 + // const s = spall.trace(@src(), "Render Thread {}", .{row}); 160 + // defer s.end(); 122 161 123 - tracer.trace(ctx, height, width); 162 + tracer.trace(ctx); 124 163 125 - done.store(true, .Release); 164 + { 165 + task.done.store(true, .release); 166 + } 167 + 168 + // log.info("Chunk {} rendered", .{id}); 169 + _ = id; 126 170 }
+5 -3
src/tracer.zig
··· 19 19 pub const Context = struct { 20 20 cam: *Camera, 21 21 world: *hittable.HittableList, 22 + height: IntervalUsize, 23 + width: IntervalUsize, 22 24 }; 23 25 24 26 pub fn rayColor(r: *Ray, world: *hittable.HittableList, depth: usize) zm.Vec { ··· 38 40 return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); 39 41 } 40 42 41 - pub fn trace(ctx: Context, height: IntervalUsize, width: IntervalUsize) void { 42 - var height_iter = height.iter(); 43 + pub fn trace(ctx: Context) void { 44 + var height_iter = ctx.height.iter(); 43 45 while (height_iter.nextInc()) |j| { 44 46 if (j >= ctx.cam.image_height) break; 45 47 46 - var width_iter = width.iter(); 48 + var width_iter = ctx.width.iter(); 47 49 while (width_iter.nextExc()) |i| { 48 50 var col = zm.f32x4(0.0, 0.0, 0.0, 1.0); 49 51 for (0..ctx.cam.samples_per_pixel) |_| {