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}