From 972e879940cb5b13a0f8dc768397e6940a764af4 Mon Sep 17 00:00:00 2001 From: Altagos Date: Thu, 13 Jun 2024 11:38:56 +0200 Subject: [PATCH] + --- .gitignore | 4 +-- build.zig | 16 ++++----- build.zig.zon | 16 ++++----- libs/zmath/LICENSE | 22 ++++++++++++ libs/zmath/build.zig | 6 ++-- libs/zmath/src/util.zig | 10 +++--- libs/zmath/src/zmath.zig | 4 +-- src/BVH.zig | 61 ++++++++++++++++++++++++---------- src/main.zig | 24 ++++++------- src/rayray.zig | 58 +++++++++++++++++++++++--------- src/scences/in_one_weekend.zig | 17 ++++++---- src/tracer.zig | 1 + 12 files changed, 161 insertions(+), 78 deletions(-) create mode 100644 libs/zmath/LICENSE diff --git a/.gitignore b/.gitignore index aec55a6..06aa80e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -**/zig-cache -**/zig-out +*.zig-cache +*zig-out /out diff --git a/build.zig b/build.zig index 36818ea..2242035 100644 --- a/build.zig +++ b/build.zig @@ -4,34 +4,34 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const enable_spall = b.option(bool, "enable_spall", "Enable spall profiling") orelse false; - const spall = b.dependency("spall", .{ - .enable = enable_spall, - }); + // const enable_spall = b.option(bool, "enable_spall", "Enable spall profiling") orelse false; + // const spall = b.dependency("spall", .{ + // .enable = enable_spall, + // }); const strip = b.option(bool, "strip", "") orelse (optimize != .Debug); const rayray = b.addModule("rayray", .{ - .root_source_file = .{ .path = "src/rayray.zig" }, + .root_source_file = b.path("src/rayray.zig"), .target = target, .optimize = optimize, }); rayray.strip = strip; - rayray.addImport("spall", spall.module("spall")); + // rayray.addImport("spall", spall.module("spall")); addDeps(b, rayray); const exe = b.addExecutable(.{ .name = "rayray", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); exe.root_module.strip = strip; // addDeps(b, &exe.root_module); - exe.root_module.addImport("spall", spall.module("spall")); + // exe.root_module.addImport("spall", spall.module("spall")); exe.root_module.addImport("rayray", rayray); const alib = b.dependency("a", .{ diff --git a/build.zig.zon b/build.zig.zon index ff6e125..90ca48e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,16 +9,16 @@ // See `zig fetch --save ` for a command-line interface for adding dependencies. .a = .{ - .url = "git+https://git.sr.ht/~altagos/a#8c8f2cc405743d062f5b617e2f8ab85ce5dc0409", - .hash = "12203338074c5e0fed0696abfa1ec868da0d1bb22539e5fd4220b2585a8f3cf0c788", - }, - .spall = .{ - .url = "git+https://git.sr.ht/~altagos/zig-spall#7cae52aa2d1a519006e0c90fbd179cf7fcac0c83", - .hash = "1220753b2f9e7c4f3fb6cdc8fc275cc9073904ea7032a1f5596aa776a857de69f72c", + .url = "git+https://git.sr.ht/~altagos/a#78b378a924008a072ede6be850398299d233e55e", + .hash = "12209a07e35e58e69838fd66b68a13ee2b1fbf999e6242acdec0d7354be1324aef76", }, + // .spall = .{ + // .url = "git+https://git.sr.ht/~altagos/zig-spall#7cae52aa2d1a519006e0c90fbd179cf7fcac0c83", + // .hash = "1220753b2f9e7c4f3fb6cdc8fc275cc9073904ea7032a1f5596aa776a857de69f72c", + // }, .zigimg = .{ - .url = "git+https://github.com/MidlightStudio/zigimg#637974e2d31dcdbc33f1e9cc8ffb2e46abd2e215", - .hash = "122012026c3a65ff1d4acba3b3fe80785f7cee9c6b4cdaff7ed0fbf23b0a6c803989", + .url = "git+https://github.com/altagos/zigimg#a5a18768691c27cf3b312b67ca5084148a502e3e", + .hash = "1220e69e80741a9650a86e4369128698005f0049c6508b788e7fe1809480af62f4f8", }, }, .paths = .{ diff --git a/libs/zmath/LICENSE b/libs/zmath/LICENSE new file mode 100644 index 0000000..3db02da --- /dev/null +++ b/libs/zmath/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2021 Michal Ziulek +Copyright (c) 2024 zig-gamedev contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/zmath/build.zig b/libs/zmath/build.zig index 7506c3a..89f44b7 100644 --- a/libs/zmath/build.zig +++ b/libs/zmath/build.zig @@ -26,7 +26,7 @@ pub fn build(b: *std.Build) void { const options_module = options_step.createModule(); const zmath = b.addModule("root", .{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .imports = &.{ .{ .name = "zmath_options", .module = options_module }, }, @@ -36,7 +36,7 @@ pub fn build(b: *std.Build) void { const tests = b.addTest(.{ .name = "zmath-tests", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = options.optimize, }); @@ -50,7 +50,7 @@ pub fn build(b: *std.Build) void { const benchmarks = b.addExecutable(.{ .name = "zmath-benchmarks", - .root_source_file = .{ .path = "src/benchmark.zig" }, + .root_source_file = b.path("src/benchmark.zig"), .target = target, .optimize = options.optimize, }); diff --git a/libs/zmath/src/util.zig b/libs/zmath/src/util.zig index faa9a9f..d498e75 100644 --- a/libs/zmath/src/util.zig +++ b/libs/zmath/src/util.zig @@ -111,7 +111,7 @@ test "zmath.util.mat.z_vec" { var identity = zm.identity(); var z_vec = getAxisZ(identity); try expect(zm.approxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001)); - const rot_yaw = zm.rotationY(degToRad(f32, 90)); + const rot_yaw = zm.rotationY(degToRad(90)); identity = zm.mul(identity, rot_yaw); z_vec = getAxisZ(identity); try expect(zm.approxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001)); @@ -122,11 +122,11 @@ test "zmath.util.mat.y_vec" { var identity = zm.identity(); var y_vec = getAxisY(identity); try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01)); - const rot_yaw = zm.rotationY(degToRad(f32, 90)); + const rot_yaw = zm.rotationY(degToRad(90)); identity = zm.mul(identity, rot_yaw); y_vec = getAxisY(identity); try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01)); - const rot_pitch = zm.rotationX(degToRad(f32, 90)); + const rot_pitch = zm.rotationX(degToRad(90)); identity = zm.mul(identity, rot_pitch); y_vec = getAxisY(identity); try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01)); @@ -137,11 +137,11 @@ test "zmath.util.mat.right" { var identity = zm.identity(); var right = getAxisX(identity); try expect(zm.approxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01)); - const rot_yaw = zm.rotationY(degToRad(f32, 90)); + const rot_yaw = zm.rotationY(degToRad(90)); identity = zm.mul(identity, rot_yaw); right = getAxisX(identity); try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01)); - const rot_pitch = zm.rotationX(degToRad(f32, 90)); + const rot_pitch = zm.rotationX(degToRad(90)); identity = zm.mul(identity, rot_pitch); right = getAxisX(identity); try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01)); diff --git a/libs/zmath/src/zmath.zig b/libs/zmath/src/zmath.zig index 5b5dc31..34c57d2 100644 --- a/libs/zmath/src/zmath.zig +++ b/libs/zmath/src/zmath.zig @@ -3089,11 +3089,11 @@ pub fn quatToRollPitchYaw(q: Quat) [3]f32 { const singularity = p[0] * p[2] + sign * p[1] * p[3]; if (singularity > 0.499) { angles[0] = math.pi * 0.5; - angles[1] = 2.0 * math.atan2(f32, p[1], p[0]); + angles[1] = 2.0 * math.atan2(p[1], p[0]); angles[2] = 0.0; } else if (singularity < -0.499) { angles[0] = -math.pi * 0.5; - angles[1] = 2.0 * math.atan2(f32, p[1], p[0]); + angles[1] = 2.0 * math.atan2(p[1], p[0]); angles[2] = 0.0; } else { const sq = p * p; diff --git a/src/BVH.zig b/src/BVH.zig index ed38c68..a1b4012 100644 --- a/src/BVH.zig +++ b/src/BVH.zig @@ -35,7 +35,25 @@ const Ast = struct { } }; -const Leaf = Hittable; +const Leaf = struct { + objects: []Hittable, + bbox: AABB, + + pub inline fn hit(self: *Leaf, r: *Ray, ray_t: IntervalF32) ?HitRecord { + var rec: ?HitRecord = null; + var interval = ray_t; + for (self.objects) |obj| { + if (@constCast(&obj).hit(r, interval)) |res| { + interval = IntervalF32.init(ray_t.min, res.t); + rec = res; + } + } + return rec; + } +}; + +threadlocal var reached_depth: usize = 0; +threadlocal var max_objects: usize = 0; const Node = union(enum) { ast: Ast, @@ -44,22 +62,24 @@ const Node = union(enum) { pub fn init( self: *Node, allocator: std.mem.Allocator, - objects: []hittable.Hittable, + objects: []Hittable, + max_depth: usize, + depth: usize, ) !void { - if (objects.len == 1) { - self.* = .{ .leaf = objects[0] }; - return; - } + if (reached_depth < depth) reached_depth = depth; var ast_bbox = AABB{}; for (0..objects.len) |idx| { ast_bbox = AABB.initAB(&ast_bbox, &objects[idx].boundingBox()); } - const axis = ast_bbox.longestAxis(); + if (depth >= max_depth or objects.len <= 2) { + if (max_objects < objects.len) max_objects = objects.len; + self.* = .{ .leaf = .{ .objects = objects, .bbox = ast_bbox } }; + return; + } - var left = try allocator.create(Node); - var right = try allocator.create(Node); + const axis = ast_bbox.longestAxis(); if (axis == 0) { std.mem.sort(Hittable, objects, .{}, boxXCompare); @@ -69,9 +89,12 @@ const Node = union(enum) { std.mem.sort(Hittable, objects, .{}, boxZCompare); } + var left = try allocator.create(Node); + var right = try allocator.create(Node); + const mid = objects.len / 2; - try left.init(allocator, objects[0..mid]); - try right.init(allocator, objects[mid..]); + try left.init(allocator, objects[0..mid], max_depth, depth + 1); + try right.init(allocator, objects[mid..], max_depth, depth + 1); self.* = .{ .ast = .{ .left = left, @@ -92,14 +115,16 @@ const Node = union(enum) { allocator.destroy(right); } }, - else => {}, + .leaf => |*l| { + allocator.destroy(&l.objects); + }, } } pub inline fn bbox(self: *Node) AABB { switch (self.*) { .ast => |*a| return a.bbox, - .leaf => |l| return @constCast(&l).boundingBox(), + .leaf => |*l| return l.bbox, } } @@ -115,7 +140,7 @@ const Node = union(enum) { fn recomputeBbox(self: *Node) AABB { switch (self.*) { - .leaf => |*l| return l.boundingBox(), + .leaf => |*l| return l.bbox, .ast => |*a| { var left = AABB{}; var right = AABB{}; @@ -145,7 +170,7 @@ const Node = union(enum) { if (a.left) |left| left.print(depth + 1, 1); if (a.right) |right| right.print(depth + 1, 2); }, - .leaf => |*l| std.debug.print("Leaf = {s}\n", .{l.getName()}), + .leaf => |*l| std.debug.print("Leafs = {}\n", .{l}), } } @@ -164,14 +189,16 @@ allocator: std.mem.Allocator, root: *Node, bbox: AABB, -pub fn init(allocator: std.mem.Allocator, objects: hittable.HittableList) !BVH { +pub fn init(allocator: std.mem.Allocator, objects: hittable.HittableList, max_depth: usize) !BVH { defer @constCast(&objects).deinit(); std.log.info("Creating BVH Tree with {} objects", .{objects.list.items.len}); const root = try allocator.create(Node); - try root.init(allocator, objects.list.items); + try root.init(allocator, objects.list.items, max_depth, 0); const bbox = root.recomputeBbox(); + std.log.debug("Reached depth of: {}, max objects: {}", .{ reached_depth, max_objects }); + // root.print(0, 0); return .{ .allocator = allocator, diff --git a/src/main.zig b/src/main.zig index f440859..11efc3b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,7 @@ const std = @import("std"); const aa = @import("a"); -const spall = @import("spall"); +// const spall = @import("spall"); const rayray = @import("rayray"); const Hittable = rayray.hittable.Hittable; @@ -22,11 +22,11 @@ pub fn main() !void { defer arena.deinit(); const allocator = arena.allocator(); - try spall.init("./out/trace.spall"); - defer spall.deinit(); + // try spall.init("./out/trace.spall"); + // defer spall.deinit(); - spall.init_thread(); - defer spall.deinit_thread(); + // spall.init_thread(); + // defer spall.deinit_thread(); // Setting up the world var scence = try scences.inOneWeekend(allocator); @@ -34,14 +34,14 @@ pub fn main() !void { std.log.info("World created", .{}); - const s = spall.trace(@src(), "Raytracer", .{}); + // const s = spall.trace(@src(), "Raytracer", .{}); // Raytracing part var raytracer = try rayray.Raytracer.init(allocator, scence.world, .{ .aspect_ratio = 16.0 / 9.0, - .image_width = 4000, - .samples_per_pixel = 500, - .max_depth = 100, + .image_width = 400, + .samples_per_pixel = 50, + .max_depth = 50, .vfov = 20, .look_from = zm.f32x4(20, 6, 6, 0), @@ -60,11 +60,11 @@ pub fn main() !void { std.log.info("Image rendered ({}s)", .{rendering_time / std.time.ns_per_s}); - s.end(); + // s.end(); // Saving to file - const s_saving = spall.trace(@src(), "Write Image", .{}); - defer s_saving.end(); + // const s_saving = spall.trace(@src(), "Write Image", .{}); + // defer s_saving.end(); try img.writeToFilePath("./out/out.png", .{ .png = .{} }); std.log.info("Image saved to: ./out/out.png", .{}); diff --git a/src/rayray.zig b/src/rayray.zig index 0104ac6..3e73eab 100644 --- a/src/rayray.zig +++ b/src/rayray.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub const zmath = @import("zmath"); -const spall = @import("spall"); +// const spall = @import("spall"); const zigimg = @import("zigimg"); const color = zigimg.color; @@ -20,6 +20,7 @@ const log = std.log.scoped(.rayray); pub const TaskTracker = struct { marked_as_done: bool = false, done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), + thread_id: std.Thread.Id = 0, }; pub const Raytracer = struct { @@ -39,7 +40,7 @@ pub const Raytracer = struct { .allocator = allocator, .thread_pool = thread_pool, .camera = try Camera.init(allocator, camera_opts), - .world = try BVH.init(allocator, world), + .world = try BVH.init(allocator, world, 100), }; } @@ -52,8 +53,8 @@ pub const Raytracer = struct { } pub fn render(self: *Self) !zigimg.Image { - const s = spall.trace(@src(), "Render", .{}); - defer s.end(); + // const s = spall.trace(@src(), "Render", .{}); + // defer s.end(); const chunk_height: usize = 25; const chunk_width: usize = 25; @@ -102,18 +103,33 @@ pub const Raytracer = struct { ); } - const stderr = std.io.getStdErr(); + // const stderr = std.io.getStdErr(); - var progress = std.Progress{ - .terminal = stderr, - .supports_ansi_escape_codes = true, - }; - var node = progress.start("Rendered Chunks", num_chunks); - node.setCompletedItems(0); - node.context.refresh(); + // var progress = std.Progress{ + // .terminal = stderr, + // .supports_ansi_escape_codes = true, + // }; + // var node = progress.start("Rendered Chunks", num_chunks); + // node.setCompletedItems(0); + // node.context.refresh(); - var completed_chunks: u64 = 0; + const num_threads = try std.Thread.getCpuCount(); + var thread_to_idx = std.ArrayList(std.Thread.Id).init(self.allocator); + defer thread_to_idx.deinit(); + var root_node = std.Progress.start(.{ + .root_name = "Ray Tracer", + .estimated_total_items = num_threads, + }); + var nodes = std.ArrayList(std.Progress.Node).init(self.allocator); + defer nodes.deinit(); + + for (0..num_threads) |_| { + try nodes.append(root_node.start("Chunks Rendered", num_chunks / num_threads)); + } + + var completed_chunks: u64 = 0; + var i: usize = 0; while (true) { var done = true; @@ -122,7 +138,18 @@ pub const Raytracer = struct { if (task_done and !t.marked_as_done) { t.marked_as_done = true; - node.completeOne(); + + const idx = blk: { + for (thread_to_idx.items, 0..) |value, idx| { + if (value == t.thread_id) break :blk idx; + } + try thread_to_idx.append(t.thread_id); + const idx = i; + i += 1; + break :blk idx; + }; + nodes.items[idx].completeOne(); + completed_chunks += 1; if (completed_chunks % self.thread_pool.threads.len == 0) try self.camera.image.writeToFilePath("./out/out.png", .{ .png = .{} }); } else if (!task_done) { @@ -133,7 +160,7 @@ pub const Raytracer = struct { if (done or !self.thread_pool.is_running) break; } - node.end(); + // node.end(); return self.camera.image; } @@ -141,6 +168,7 @@ pub const Raytracer = struct { pub fn renderThread(ctx: tracer.Context, task: *TaskTracker, id: usize) void { defer task.done.store(true, .release); + task.thread_id = std.Thread.getCurrentId(); _ = id; tracer.trace(ctx); } diff --git a/src/scences/in_one_weekend.zig b/src/scences/in_one_weekend.zig index fcd9962..028252f 100644 --- a/src/scences/in_one_weekend.zig +++ b/src/scences/in_one_weekend.zig @@ -17,10 +17,13 @@ pub fn scene(allocator: std.mem.Allocator) !@This() { material_ground.* = Material.lambertian(zm.f32x4(0.5, 0.5, 0.5, 1.0)); try world.add(Hittable.sphere("Ground", Sphere{ .center = zm.f32x4(0, -1000, 0, 0), .radius = 1000, .mat = material_ground })); - var a: isize = -33; - while (a < 11) : (a += 1) { - var b: isize = -22; - while (b < 7) : (b += 1) { + const a_max = 50; + const b_max = 50; + + var a: isize = -a_max; + while (a < a_max) : (a += 1) { + var b: isize = -b_max; + while (b < b_max) : (b += 1) { const choose_mat = rayray.util.randomF32(); const center = zm.f32x4( @as(f32, @floatFromInt(a)) + 0.9 * rayray.util.randomF32(), @@ -55,16 +58,18 @@ pub fn scene(allocator: std.mem.Allocator) !@This() { const material1 = try allocator.create(Material); material1.* = Material.dielectric(1.5); - try world.add(Hittable.sphere("One: Dielectric", Sphere{ .center = zm.f32x4(0, 1, 0, 0), .radius = 1, .mat = material1 })); + // try world.add(Hittable.sphere("One: Dielectric", Sphere{ .center = zm.f32x4(0, 1, 0, 0), .radius = 1, .mat = material1 })); const material2 = try allocator.create(Material); material2.* = Material.lambertian(zm.f32x4(0.4, 0.2, 0.1, 1)); - try world.add(Hittable.sphere("Two: Lambertian", Sphere{ .center = zm.f32x4(-4, 1, 0, 0), .radius = 1, .mat = material2 })); + try world.add(Hittable.sphere("Two: Lambertian", Sphere{ .center = zm.f32x4(-4, 1, 0, 0), .radius = 1, .mat = material1 })); const material3 = try allocator.create(Material); material3.* = Material.metal(zm.f32x4(0.7, 0.6, 0.5, 1), 0); try world.add(Hittable.sphere("Three: Metal", Sphere{ .center = zm.f32x4(4, 1, 0, 0), .radius = 1, .mat = material3 })); + try world.add(Hittable.sphere("One: Dielectric", Sphere{ .center = zm.f32x4(0, 1, 0, 0), .radius = 1, .mat = material2 })); + return .{ .allocator = allocator, .world = world }; } diff --git a/src/tracer.zig b/src/tracer.zig index 00085b1..7b8bd7f 100644 --- a/src/tracer.zig +++ b/src/tracer.zig @@ -40,6 +40,7 @@ pub fn rayColor(r: *Ray, world: *BVH, depth: usize) zm.Vec { const unit_direction = zm.normalize3(r.dir); const a = 0.5 * (unit_direction[1] + 1.0); return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); + // return zm.f32x4s(1.0); } pub fn trace(ctx: Context) void { -- 2.51.2