From 46c6d49db895bb284b5b27c35d5e0520163066ec Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 28 Nov 2024 10:52:12 -0600 Subject: [PATCH 1/7] obj: Make field tracking use a single bitset --- src/module.zig | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/module.zig b/src/module.zig index e81ce41367..0cf66e54b0 100644 --- a/src/module.zig +++ b/src/module.zig @@ -69,7 +69,7 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { graph: *Graph, /// A bitset per-field used to track field changes. Only used if options.track_fields == true. - updated: ?std.ArrayListUnmanaged(std.bit_set.DynamicBitSetUnmanaged) = if (options.track_fields) .{} else null, + updated: ?std.bit_set.DynamicBitSetUnmanaged = if (options.track_fields) .{} else null, }, pub const IsMachObjects = void; @@ -187,9 +187,9 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { try dead.resize(allocator, data.capacity, true); try generation.ensureUnusedCapacity(allocator, 1); + // If we are tracking fields, we need to resize the bitset to hold another object's fields if (objs.internal.updated) |*updated_fields| { - try updated_fields.ensureUnusedCapacity(allocator, 1); - updated_fields.appendAssumeCapacity(try std.bit_set.DynamicBitSetUnmanaged.initEmpty(allocator, @typeInfo(T).@"struct".fields.len)); + try updated_fields.resize(allocator, data.capacity * @typeInfo(T).@"struct".fields.len, false); } const index = data.len; @@ -225,7 +225,11 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { const unpacked = objs.validateAndUnpack(id, "setAll"); data.set(unpacked.index, value); - if (options.track_fields) objs.internal.updated.items[unpacked.index].setAll(); + if (objs.internal.updated) |*updated_fields| { + const updated_start = unpacked.index * @typeInfo(T).@"struct".fields.len; + const updated_end = updated_start + @typeInfo(T).@"struct".fields.len; + updated_fields.setRangeValue(.{ .start = @intCast(updated_start), .end = @intCast(updated_end) }, true); + } } /// Sets a single field of the given object to the given value. @@ -257,8 +261,8 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { if (options.track_fields) if (std.meta.fieldIndex(T, @tagName(field_name))) |field_index| - if (objs.internal.updated) |updated_fields| - updated_fields.items[unpacked.index].set(field_index); + if (objs.internal.updated) |*updated_fields| + updated_fields.set(unpacked.index * @typeInfo(T).@"struct".fields.len + field_index); } /// Get a single field. @@ -324,9 +328,10 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { const unpacked = objs.validateAndUnpack(id, "updated"); if (std.meta.fieldIndex(T, @tagName(field_name))) |field_index| { if (objs.internal.updated) |*updated_fields| { - const value = updated_fields.items[unpacked.index].isSet(field_index); - updated_fields.items[unpacked.index].unset(field_index); - return value; + const updated_index = unpacked.index * @typeInfo(T).@"struct".fields.len + field_index; + const updated_value = updated_fields.isSet(updated_index); + updated_fields.unset(updated_index); + return updated_value; } } } From fc72b255c528f915937227594cce6f43a4cb12fe Mon Sep 17 00:00:00 2001 From: foxnne Date: Thu, 28 Nov 2024 11:01:58 -0600 Subject: [PATCH 2/7] obj: module: fix comment --- src/module.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module.zig b/src/module.zig index 0cf66e54b0..b676a3c7f3 100644 --- a/src/module.zig +++ b/src/module.zig @@ -68,7 +68,7 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { /// Global pointer to object relations graph graph: *Graph, - /// A bitset per-field used to track field changes. Only used if options.track_fields == true. + /// A bitset used to track per-field changes. Only used if options.track_fields == true. updated: ?std.bit_set.DynamicBitSetUnmanaged = if (options.track_fields) .{} else null, }, From 63fa3fc089e74027f7118e92ef17179fc2504017 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 30 Nov 2024 09:23:01 -0600 Subject: [PATCH 3/7] obj: Move `Platform` state and `InitOptions` fields into `core.windows`, initial push, only triangle example working on macos currently --- examples/core-triangle/App.zig | 191 +++++++------ src/Core.zig | 481 ++++++++++++++++++--------------- src/core/Darwin.zig | 361 +++++++++++++++---------- src/core/Null.zig | 6 +- src/core/Windows.zig | 40 +-- src/module.zig | 3 +- 6 files changed, 626 insertions(+), 456 deletions(-) diff --git a/examples/core-triangle/App.zig b/examples/core-triangle/App.zig index 8bcba25923..9f2fadc98f 100644 --- a/examples/core-triangle/App.zig +++ b/examples/core-triangle/App.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const mach = @import("mach"); const gpu = mach.gpu; @@ -24,43 +25,49 @@ pub fn init( core.on_tick = app_mod.id.tick; core.on_exit = app_mod.id.deinit; - // Create our shader module - const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); - defer shader_module.release(); - - // Blend state describes how rendered colors get blended - const blend = gpu.BlendState{}; - - // Color target describes e.g. the pixel format of the window we are rendering to. - const color_target = gpu.ColorTargetState{ - .format = core.windows.get(core.main_window, .framebuffer_format).?, - .blend = &blend, - }; - - // Fragment state describes which shader and entrypoint to use for rendering fragments. - const fragment = gpu.FragmentState.init(.{ - .module = shader_module, - .entry_point = "frag_main", - .targets = &.{color_target}, - }); - - // Create our render pipeline that will ultimately get pixels onto the screen. - const label = @tagName(mach_module) ++ ".init"; - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .label = label, - .fragment = &fragment, - .vertex = gpu.VertexState{ - .module = shader_module, - .entry_point = "vertex_main", - }, - }; - const pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - - // Store our render pipeline in our module's state, so we can access it later on. - app.* = .{ - .title_timer = try mach.time.Timer.start(), - .pipeline = pipeline, - }; + if (core.windows.getAll(core.main_window)) |cw| { + const core_window = cw; + if (core_window.device) |device| { + + // Create our shader module + const shader_module = device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); + defer shader_module.release(); + + // Blend state describes how rendered colors get blended + const blend = gpu.BlendState{}; + + // Color target describes e.g. the pixel format of the window we are rendering to. + const color_target = gpu.ColorTargetState{ + .format = core.windows.get(core.main_window, .framebuffer_format).?, + .blend = &blend, + }; + + // Fragment state describes which shader and entrypoint to use for rendering fragments. + const fragment = gpu.FragmentState.init(.{ + .module = shader_module, + .entry_point = "frag_main", + .targets = &.{color_target}, + }); + + // Create our render pipeline that will ultimately get pixels onto the screen. + const label = @tagName(mach_module) ++ ".init"; + const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ + .label = label, + .fragment = &fragment, + .vertex = gpu.VertexState{ + .module = shader_module, + .entry_point = "vertex_main", + }, + }; + const pipeline = device.createRenderPipeline(&pipeline_descriptor); + + // Store our render pipeline in our module's state, so we can access it later on. + app.* = .{ + .title_timer = try mach.time.Timer.start(), + .pipeline = pipeline, + }; + } + } // TODO(object): window-title // try updateWindowTitle(core); @@ -69,53 +76,83 @@ pub fn init( pub fn tick(app: *App, core: *mach.Core) void { while (core.nextEvent()) |event| { switch (event) { + .key_press => |ev| { + switch (ev.key) { + .right => { + var w = core.windows.getAll(core.main_window).?; + w.width = w.width + 10; + core.windows.setAll(core.main_window, w); + }, + .left => { + var w = core.windows.getAll(core.main_window).?; + w.width = w.width - 10; + core.windows.setAll(core.main_window, w); + }, + .up => { + if (core.windows.get(core.main_window, .height)) |height| core.windows.set(core.main_window, .height, height + 10); + }, + .down => { + if (core.windows.get(core.main_window, .height)) |height| core.windows.set(core.main_window, .height, height - 10); + }, + else => {}, + } + }, .close => core.exit(), else => {}, } } - // Grab the back buffer of the swapchain - // TODO(Core) - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; - defer back_buffer_view.release(); - - // Create a command encoder - const label = @tagName(mach_module) ++ ".tick"; - const encoder = core.device.createCommandEncoder(&.{ .label = label }); - defer encoder.release(); - - // Begin render pass - const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; - const color_attachments = [_]gpu.RenderPassColorAttachment{.{ - .view = back_buffer_view, - .clear_value = sky_blue_background, - .load_op = .clear, - .store_op = .store, - }}; - const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ - .label = label, - .color_attachments = &color_attachments, - })); - defer render_pass.release(); - - // Draw - render_pass.setPipeline(app.pipeline); - render_pass.draw(3, 1, 0, 0); - - // Finish render pass - render_pass.end(); - - // Submit our commands to the queue - var command = encoder.finish(&.{ .label = label }); - defer command.release(); - core.queue.submit(&[_]*gpu.CommandBuffer{command}); + if (core.windows.getAll(core.main_window)) |mw| { + var main_window = mw; + + // Window is ready when device is not null + if (main_window.device) |device| { + + // Grab the back buffer of the swapchain + // TODO(Core) + const back_buffer_view = main_window.swap_chain.getCurrentTextureView().?; + defer back_buffer_view.release(); + + // Create a command encoder + const label = @tagName(mach_module) ++ ".tick"; + + const encoder = device.createCommandEncoder(&.{ .label = label }); + defer encoder.release(); + + // Begin render pass + const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; + const color_attachments = [_]gpu.RenderPassColorAttachment{.{ + .view = back_buffer_view, + .clear_value = sky_blue_background, + .load_op = .clear, + .store_op = .store, + }}; + const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ + .label = label, + .color_attachments = &color_attachments, + })); + defer render_pass.release(); + + // Draw + render_pass.setPipeline(app.pipeline); + render_pass.draw(3, 1, 0, 0); + + // Finish render pass + render_pass.end(); + + // Submit our commands to the queue + var command = encoder.finish(&.{ .label = label }); + defer command.release(); + main_window.queue.submit(&[_]*gpu.CommandBuffer{command}); + } + } // update the window title every second - if (app.title_timer.read() >= 1.0) { - app.title_timer.reset(); - // TODO(object): window-title - // try updateWindowTitle(core); - } + // if (app.title_timer.read() >= 1.0) { + // app.title_timer.reset(); + // // TODO(object): window-title + // // try updateWindowTitle(core); + // } } pub fn deinit(app: *App) void { diff --git a/src/Core.zig b/src/Core.zig index 6b83c9f543..7a094cbcbd 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -30,7 +30,7 @@ pub var non_blocking = false; pub const mach_module = .mach_core; -pub const mach_systems = .{ .main, .init, .tick, .presentFrame, .processWindowUpdates, .deinit }; +pub const mach_systems = .{ .main, .init, .tick, .presentFrame, .deinit }; // Set track_fields to true so that when these field values change, we know about it // and can update the platform windows. @@ -40,25 +40,61 @@ windows: mach.Objects( /// Window title string // TODO: document how to set this using a format string // TODO: allocation/free strategy - title: []const u8, + title: []const u8 = "Mach Window", /// Texture format of the framebuffer (read-only) - framebuffer_format: gpu.Texture.Format, + framebuffer_format: gpu.Texture.Format = .bgra8_unorm, /// Width of the framebuffer in texels (read-only) - framebuffer_width: u32, + framebuffer_width: u32 = 1920 / 2, /// Height of the framebuffer in texels (read-only) - framebuffer_height: u32, + framebuffer_height: u32 = 1080 / 2, + + /// Vertical sync mode, prevents screen tearing. + vsync_mode: VSyncMode = .none, + + display_mode: DisplayMode = .windowed, + + /// Cursor + cursor_mode: CursorMode = .normal, + cursor_shape: CursorShape = .arrow, + + /// Outer border + border: bool = true, /// Width of the window in virtual pixels (read-only) - width: u32, + width: u32 = 1920 / 2, /// Height of the window in virtual pixels (read-only) - height: u32, + height: u32 = 1080 / 2, + + /// Target frames per second + refresh_rate: u32 = 0, + + // GPU + // When device is not null, the rest of the fields have been + // initialized. + device: ?*gpu.Device = null, + instance: *gpu.Instance = undefined, + adapter: *gpu.Adapter = undefined, + queue: *gpu.Queue = undefined, + swap_chain: *gpu.SwapChain = undefined, + swap_chain_descriptor: gpu.SwapChain.Descriptor = undefined, + swap_chain_update: std.Thread.ResetEvent = .{}, + surface: *gpu.Surface = undefined, + surface_descriptor: gpu.Surface.Descriptor = undefined, + + // After window initialization, (when device is not null) + // changing these will have no effect + power_preference: gpu.PowerPreference = .undefined, + required_features: ?[]const gpu.FeatureName = null, + required_limits: ?gpu.Limits = null, + swap_chain_usage: gpu.Texture.UsageFlags = .{ + .render_attachment = true, + }, - /// Whether the window is fullscreen (read-only) - fullscreen: bool, + native: Platform.Native = .{}, }, ), @@ -80,19 +116,16 @@ state: enum { } = .running, // TODO: handle window titles better -title: [256:0]u8 = undefined, +//title: [256:0]u8 = undefined, frame: mach.time.Frequency, input: mach.time.Frequency, -swap_chain_update: std.Thread.ResetEvent = .{}, // GPU -instance: *gpu.Instance, -adapter: *gpu.Adapter, -device: *gpu.Device, -queue: *gpu.Queue, -surface: *gpu.Surface, -swap_chain: *gpu.SwapChain, -descriptor: gpu.SwapChain.Descriptor, +// instance: *gpu.Instance, +// adapter: *gpu.Adapter, +// device: *gpu.Device, +// queue: *gpu.Queue, +// surface: *gpu.Surface, // Internal module state allocator: std.mem.Allocator, @@ -102,31 +135,19 @@ input_state: InputState, oom: std.Thread.ResetEvent = .{}, pub fn init(core: *Core) !void { - // TODO: this needs to be removed. - const options: InitOptions = .{ - .allocator = std.heap.c_allocator, - }; - const allocator = options.allocator; + const allocator = std.heap.c_allocator; // TODO: fix all leaks and use options.allocator try mach.sysgpu.Impl.init(allocator, .{}); - const main_window = try core.windows.new(.{ - .title = options.title, // TODO - .framebuffer_format = undefined, // TODO: null? - .framebuffer_width = undefined, // TODO: null? - .framebuffer_height = undefined, // TODO: null? - .width = 1920 / 2, - .height = 1080 / 2, - .fullscreen = false, - }); - - // Copy window title into owned buffer. - var title: [256:0]u8 = undefined; - if (options.title.len < title.len) { - @memcpy(title[0..options.title.len], options.title); - title[options.title.len] = 0; - } + const main_window = try core.windows.new(.{}); + + // TODO: Copy window title into owned buffer. + // var title: [256:0]u8 = undefined; + // if (options.title.len < title.len) { + // @memcpy(title[0..options.title.len], options.title); + // title[options.title.len] = 0; + // } var events = EventQueue.init(allocator); try events.ensureTotalCapacity(8192); @@ -143,114 +164,114 @@ pub fn init(core: *Core) !void { .input_state = .{}, .platform = platform, - - // TODO: these should not be state, they should be components. - .title = title, - .frame = undefined, - .input = undefined, - .instance = undefined, - .adapter = undefined, - .device = undefined, - .queue = undefined, - .surface = undefined, - .swap_chain = undefined, - .descriptor = undefined, + .input = .{ .target = 0 }, + .frame = .{ .target = 1 }, }; - try Platform.init(&core.platform, core, options); + //try initWindow(core, main_window); - core.instance = gpu.createInstance(null) orelse { - log.err("failed to create GPU instance", .{}); - std.process.exit(1); - }; - core.surface = core.instance.createSurface(&core.platform.surface_descriptor); - - var response: RequestAdapterResponse = undefined; - core.instance.requestAdapter(&gpu.RequestAdapterOptions{ - .compatible_surface = core.surface, - .power_preference = options.power_preference, - .force_fallback_adapter = .false, - }, &response, requestAdapterCallback); - if (response.status != .success) { - log.err("failed to create GPU adapter: {?s}", .{response.message}); - log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{}); - std.process.exit(1); - } + // Tick the platform so that the platform can grab the newly created window + // and perform initialization + try Platform.tick(core); - // Print which adapter we are going to use. - var props = std.mem.zeroes(gpu.Adapter.Properties); - response.adapter.?.getProperties(&props); - if (props.backend_type == .null) { - log.err("no backend found for {s} adapter", .{props.adapter_type.name()}); - std.process.exit(1); - } - log.info("found {s} backend on {s} adapter: {s}, {s}\n", .{ - props.backend_type.name(), - props.adapter_type.name(), - props.name, - props.driver_description, - }); - - core.adapter = response.adapter.?; - - // Create a device with default limits/features. - core.device = response.adapter.?.createDevice(&.{ - .required_features_count = if (options.required_features) |v| @as(u32, @intCast(v.len)) else 0, - .required_features = if (options.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null, - .required_limits = if (options.required_limits) |limits| @as(?*const gpu.RequiredLimits, &gpu.RequiredLimits{ - .limits = limits, - }) else null, - .device_lost_callback = &deviceLostCallback, - .device_lost_userdata = null, - }) orelse { - log.err("failed to create GPU device\n", .{}); - std.process.exit(1); - }; - core.device.setUncapturedErrorCallback({}, printUnhandledErrorCallback); - core.queue = core.device.getQueue(); - - core.descriptor = gpu.SwapChain.Descriptor{ - .label = "main swap chain", - .usage = options.swap_chain_usage, - .format = .bgra8_unorm, - .width = @intCast(core.platform.size.width), - .height = @intCast(core.platform.size.height), - .present_mode = switch (core.platform.vsync_mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }, - }; - core.swap_chain = core.device.createSwapChain(core.surface, &core.descriptor); - - core.windows.setRaw(core.main_window, .framebuffer_format, core.descriptor.format); - core.windows.setRaw(core.main_window, .framebuffer_width, core.descriptor.width); - core.windows.setRaw(core.main_window, .framebuffer_height, core.descriptor.height); - // TODO(important): update this information upon framebuffer resize events - // var w = core.windows.get(core.main_window).?; - // w.framebuffer_format = core.descriptor.format; - // w.framebuffer_width = core.descriptor.width; - // w.framebuffer_height = core.descriptor.height; - // w.width = core.platform.size.width; - // w.height = core.platform.size.height; - // core.windows.setAll(core.main_window, w); - - core.frame = .{ .target = 0 }; - core.input = .{ .target = 1 }; try core.frame.start(); try core.input.start(); } -pub fn tick(core: *Core, core_mod: mach.Mod(Core)) void { +pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void { + if (core.windows.getAll(window_id)) |cw| { + var core_window = cw; + + core_window.instance = gpu.createInstance(null) orelse { + log.err("failed to create GPU instance", .{}); + std.process.exit(1); + }; + core_window.surface = core_window.instance.createSurface(&core_window.surface_descriptor); + + var response: RequestAdapterResponse = undefined; + core_window.instance.requestAdapter(&gpu.RequestAdapterOptions{ + .compatible_surface = core_window.surface, + .power_preference = core_window.power_preference, + .force_fallback_adapter = .false, + }, &response, requestAdapterCallback); + if (response.status != .success) { + log.err("failed to create GPU adapter: {?s}", .{response.message}); + log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{}); + std.process.exit(1); + } + + // Print which adapter we are going to use. + var props = std.mem.zeroes(gpu.Adapter.Properties); + response.adapter.?.getProperties(&props); + if (props.backend_type == .null) { + log.err("no backend found for {s} adapter", .{props.adapter_type.name()}); + std.process.exit(1); + } + log.info("found {s} backend on {s} adapter: {s}, {s}\n", .{ + props.backend_type.name(), + props.adapter_type.name(), + props.name, + props.driver_description, + }); + + core_window.adapter = response.adapter.?; + + // Create a device with default limits/features. + core_window.device = response.adapter.?.createDevice(&.{ + .required_features_count = if (core_window.required_features) |v| @as(u32, @intCast(v.len)) else 0, + .required_features = if (core_window.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null, + .required_limits = if (core_window.required_limits) |limits| @as(?*const gpu.RequiredLimits, &gpu.RequiredLimits{ + .limits = limits, + }) else null, + .device_lost_callback = &deviceLostCallback, + .device_lost_userdata = null, + }) orelse { + log.err("failed to create GPU device\n", .{}); + std.process.exit(1); + }; + core_window.device.?.setUncapturedErrorCallback({}, printUnhandledErrorCallback); + core_window.queue = core_window.device.?.getQueue(); + + core_window.swap_chain_descriptor = gpu.SwapChain.Descriptor{ + .label = "main swap chain", + .usage = core_window.swap_chain_usage, + .format = .bgra8_unorm, + .width = core_window.width, + .height = core_window.height, + .present_mode = switch (core_window.vsync_mode) { + .none => .immediate, + .double => .fifo, + .triple => .mailbox, + }, + }; + core_window.swap_chain = core_window.device.?.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); + core_window.framebuffer_format = core_window.swap_chain_descriptor.format; + core_window.framebuffer_width = core_window.swap_chain_descriptor.width; + core_window.framebuffer_height = core_window.swap_chain_descriptor.height; + + core.windows.setAllRaw(window_id, core_window); + } +} + +pub fn tick(core: *Core, core_mod: mach.Mod(Core)) !void { + // Allow each platform to react to any changes + // Do we want to call this prior to calling `on_tick`? + // If we dont, and the user calls `updated()` on a window field + // it would set the field back to false, and our platform wouldnt catch + // the update. If we call if before we call the user tick, theres no opportunity. + try Platform.tick(core); + core_mod.run(core.on_tick.?); core_mod.call(.presentFrame); - core_mod.call(.processWindowUpdates); + //core_mod.call(.processWindowUpdates); } pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void { if (core.on_tick == null) @panic("core.on_tick callback must be set"); if (core.on_exit == null) @panic("core.on_exit callback must be set"); + try Platform.tick(core); + core_mod.run(core.on_tick.?); core_mod.call(.presentFrame); @@ -287,50 +308,51 @@ pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void { } fn platform_update_callback(core: *Core, core_mod: mach.Mod(Core)) !bool { + // Allow each platform to react to any changes + // Do we want to call this prior to calling `on_tick`? + // If we dont, and the user calls `updated()` on a window field + // it would set the field back to false, and our platform wouldnt catch + // the update. If we call if before we call the user tick, theres no opportunity. + try Platform.tick(core); + core_mod.run(core.on_tick.?); core_mod.call(.presentFrame); - core_mod.call(.processWindowUpdates); + //core_mod.call(.processWindowUpdates); return core.state != .exited; } -pub fn processWindowUpdates(core: *Core) void { - if (core.windows.updated(core.main_window, .width) or core.windows.updated(core.main_window, .height)) { - const window = core.windows.getAll(core.main_window); - - if (window) |main_window| { - core.platform.setSize(.{ - .width = main_window.width, - .height = main_window.height, - }); - } - } -} +// pub fn processWindowUpdates(core: *Core) void { +// while (core.windows.slice().next()) |window_id| { +// if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) { +// const window = core.windows.getAll(window_id); + +// if (window) |w| { +// core.platform.setSize(.{ +// .width = w.width, +// .height = w.height, +// }); +// } +// } +// } +// } pub fn deinit(core: *Core) !void { core.state = .exited; - // TODO(object)(window-title) - // var q = try entities.query(.{ - // .titles = Mod.read(.title), - // }); - // while (q.next()) |v| { - // for (v.titles) |title| { - // state.allocator.free(title); - // } - // } - - // GPU backend must be released BEFORE platform deinit, otherwise we may enter a race - // where the GPU might try to present to the window server. - core.swap_chain.release(); - core.queue.release(); - core.device.release(); - core.surface.release(); - core.adapter.release(); - core.instance.release(); - - // Deinit the platform - core.platform.deinit(); + var windows = core.windows.slice(); + while (windows.next()) |window_id| { + if (core.windows.getAll(window_id)) |cw| { + var core_window = cw; + core_window.swap_chain.release(); + core_window.queue.release(); + if (core_window.device) |device| device.release(); + core_window.surface.release(); + core_window.adapter.release(); + core_window.instance.release(); + core_window.device = null; + } + } core.events.deinit(); } @@ -587,6 +609,7 @@ pub fn mousePosition(core: *@This()) Position { // } pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { + // TODO(object)(window-title) // // Update windows title // var num_windows: usize = 0; @@ -602,30 +625,43 @@ pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { // } // if (num_windows > 1) @panic("mach: Core currently only supports a single window"); - _ = try core.platform.update(); - mach.sysgpu.Impl.deviceTick(core.device); - core.swap_chain.present(); + //_ = try core.platform.update(); - // Update swapchain for the next frame - if (core.swap_chain_update.isSet()) blk: { - core.swap_chain_update.reset(); + var windows = core.windows.slice(); + while (windows.next()) |window_id| { + if (core.windows.getAll(window_id)) |cw| { + var core_window = cw; - switch (core.platform.vsync_mode) { - .triple => core.frame.target = 2 * core.platform.refresh_rate, - else => core.frame.target = 0, - } + if (core_window.device) |device| mach.sysgpu.Impl.deviceTick(device); - if (core.platform.size.width == 0 or core.platform.size.height == 0) break :blk; + core_window.swap_chain.present(); - core.descriptor.present_mode = switch (core.platform.vsync_mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }; - core.descriptor.width = @intCast(core.platform.size.width); - core.descriptor.height = @intCast(core.platform.size.height); - core.swap_chain.release(); - core.swap_chain = core.device.createSwapChain(core.surface, &core.descriptor); + // Update swapchain for the next frame + if (core_window.swap_chain_update.isSet()) blk: { + core_window.swap_chain_update.reset(); + + switch (core_window.vsync_mode) { + .triple => core.frame.target = 2 * core_window.refresh_rate, + else => core.frame.target = 0, + } + + if (core_window.width == 0 or core_window.height == 0) break :blk; + + core_window.swap_chain_descriptor.present_mode = switch (core_window.vsync_mode) { + .none => .immediate, + .double => .fifo, + .triple => .mailbox, + }; + + if (core_window.device) |device| { + core_window.swap_chain_descriptor.width = core_window.width; + core_window.swap_chain_descriptor.height = core_window.height; + core_window.swap_chain.release(); + + core_window.swap_chain = device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); + } + } + } } // TODO(important): update this information in response to resize events rather than @@ -747,21 +783,21 @@ const Platform = switch (build_options.core_platform) { // TODO(object): this struct should not exist // TODO: this should not be here, it is exposed because the platform implementations need it. -pub const InitOptions = struct { - allocator: std.mem.Allocator, - is_app: bool = false, - headless: bool = false, - display_mode: DisplayMode = .windowed, - border: bool = true, - title: [:0]const u8 = "Mach core", - size: Size = .{ .width = 1920 / 2, .height = 1080 / 2 }, - power_preference: gpu.PowerPreference = .undefined, - required_features: ?[]const gpu.FeatureName = null, - required_limits: ?gpu.Limits = null, - swap_chain_usage: gpu.Texture.UsageFlags = .{ - .render_attachment = true, - }, -}; +// pub const InitOptions = struct { +// allocator: std.mem.Allocator, +// is_app: bool = false, +// headless: bool = false, +// display_mode: DisplayMode = .windowed, +// border: bool = true, +// title: [:0]const u8 = "Mach core", +// size: Size = .{ .width = 1920 / 2, .height = 1080 / 2 }, +// power_preference: gpu.PowerPreference = .undefined, +// required_features: ?[]const gpu.FeatureName = null, +// required_limits: ?gpu.Limits = null, +// swap_chain_usage: gpu.Texture.UsageFlags = .{ +// .render_attachment = true, +// }, +// }; pub const InputState = struct { const KeyBitSet = std.StaticBitSet(@as(u8, @intFromEnum(Key.max)) + 1); @@ -1070,37 +1106,37 @@ const RequestAdapterResponse = struct { }; // Verifies that a platform implementation exposes the expected function declarations. -comptime { - // Core - assertHasField(Platform, "surface_descriptor"); - assertHasField(Platform, "refresh_rate"); +// comptime { +// // Core +// assertHasField(Platform, "surface_descriptor"); +// assertHasField(Platform, "refresh_rate"); - assertHasDecl(Platform, "init"); - assertHasDecl(Platform, "deinit"); +// assertHasDecl(Platform, "init"); +// assertHasDecl(Platform, "deinit"); - assertHasDecl(Platform, "setTitle"); +// assertHasDecl(Platform, "setTitle"); - assertHasDecl(Platform, "setDisplayMode"); - assertHasField(Platform, "display_mode"); +// assertHasDecl(Platform, "setDisplayMode"); +// assertHasField(Platform, "display_mode"); - assertHasDecl(Platform, "setBorder"); - assertHasField(Platform, "border"); +// assertHasDecl(Platform, "setBorder"); +// assertHasField(Platform, "border"); - assertHasDecl(Platform, "setHeadless"); - assertHasField(Platform, "headless"); +// assertHasDecl(Platform, "setHeadless"); +// assertHasField(Platform, "headless"); - assertHasDecl(Platform, "setVSync"); - assertHasField(Platform, "vsync_mode"); +// assertHasDecl(Platform, "setVSync"); +// assertHasField(Platform, "vsync_mode"); - assertHasDecl(Platform, "setSize"); - assertHasField(Platform, "size"); +// assertHasDecl(Platform, "setSize"); +// assertHasField(Platform, "size"); - assertHasDecl(Platform, "setCursorMode"); - assertHasField(Platform, "cursor_mode"); +// assertHasDecl(Platform, "setCursorMode"); +// assertHasField(Platform, "cursor_mode"); - assertHasDecl(Platform, "setCursorShape"); - assertHasField(Platform, "cursor_shape"); -} +// assertHasDecl(Platform, "setCursorShape"); +// assertHasField(Platform, "cursor_shape"); +// } fn assertHasDecl(comptime T: anytype, comptime decl_name: []const u8) void { if (!@hasDecl(T, decl_name)) @compileError(@typeName(T) ++ " missing declaration: " ++ decl_name); @@ -1112,7 +1148,6 @@ fn assertHasField(comptime T: anytype, comptime field_name: []const u8) void { test { _ = Platform; - @import("std").testing.refAllDeclsRecursive(InitOptions); @import("std").testing.refAllDeclsRecursive(VSyncMode); @import("std").testing.refAllDeclsRecursive(Size); @import("std").testing.refAllDeclsRecursive(Position); diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index ed9c9c26f7..2a9be9e7bc 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -21,26 +21,9 @@ const log = std.log.scoped(.mach); pub const Darwin = @This(); -// -------------------------- -// Module state -// -------------------------- -allocator: std.mem.Allocator, -core: *Core, - -// Core platform interface -surface_descriptor: gpu.Surface.Descriptor, -title: [:0]const u8, -display_mode: DisplayMode, -vsync_mode: VSyncMode, -cursor_mode: CursorMode, -cursor_shape: CursorShape, -border: bool, -headless: bool, -refresh_rate: u32, -size: Size, - -// Internals -window: ?*objc.app_kit.Window, +pub const Native = struct { + window: ?*objc.app_kit.Window = null, +}; pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@TypeOf(on_each_update_fn))) noreturn { const Args = @TypeOf(args_tuple); @@ -76,193 +59,299 @@ pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@ // TODO: support UIKit. } -pub fn init( - darwin: *Darwin, +pub fn tick(core: *Core) !void { + var windows = core.windows.slice(); + while (windows.next()) |window_id| { + if (core.windows.getAll(window_id)) |cw| { + const core_window = cw; + const native: Native = core_window.native; + + if (core.windows.get(window_id, .device).? == null) { + // Window has not been set up yet, we need to init the window + //try initWindow(core, window_id); + //try core.initWindow(window_id); + + try initWindow(core, window_id); + try core.initWindow(window_id); + } + if (native.window) |native_window| { + // Handle resizing the window when the user changes width or height + if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) { + var frame = native_window.frame(); + frame.size.height = @floatFromInt(core.windows.get(window_id, .width).?); + frame.size.width = @floatFromInt(core.windows.get(window_id, .height).?); + native_window.setFrame_display_animate(frame, true, true); + } + } + } + } +} + +fn initWindow( core: *Core, - options: InitOptions, + window_id: mach.ObjectID, ) !void { - var surface_descriptor = gpu.Surface.Descriptor{}; - - // TODO: support UIKit. - var window_opt: ?*objc.app_kit.Window = null; - if (!options.headless) { + if (core.windows.getAll(window_id)) |cw| { + var core_window = cw; // If the application is not headless, we need to make the application a genuine UI application // by setting the activation policy, this moves the process to foreground + // TODO: Only call this on the first window creation _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); - const metal_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromMetalLayer); + const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer); const layer = objc.quartz_core.MetalLayer.new(); defer layer.release(); layer.setDisplaySyncEnabled(true); metal_descriptor.* = .{ .layer = layer, }; - surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; + core_window.surface_descriptor = .{}; + core_window.surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; const screen = objc.app_kit.Screen.mainScreen(); const rect = objc.core_graphics.Rect{ .origin = .{ .x = 0, .y = 0 }, - .size = .{ .width = @floatFromInt(options.size.width), .height = @floatFromInt(options.size.height) }, + .size = .{ .width = @floatFromInt(core_window.width), .height = @floatFromInt(core_window.height) }, }; const window_style = - (if (options.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | - (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | - (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | - (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | - (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); + (if (core_window.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); - window_opt = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( + const native_window_opt: ?*objc.app_kit.Window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( rect, window_style, objc.app_kit.BackingStoreBuffered, false, screen, ); - if (window_opt) |window| { - window.setReleasedWhenClosed(false); + if (native_window_opt) |native_window| { + core_window.native = .{ .window = native_window }; + + native_window.setReleasedWhenClosed(false); var view = objc.mach.View.allocInit(); view.setLayer(@ptrCast(layer)); { - var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, darwin, null, null); + var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, core, null, null); view.setBlock_keyDown(keyDown.asBlock().copy()); - var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, darwin, null, null); + var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, core, null, null); view.setBlock_keyUp(keyUp.asBlock().copy()); } - window.setContentView(@ptrCast(view)); - window.center(); - window.setIsVisible(true); - window.makeKeyAndOrderFront(null); + native_window.setContentView(@ptrCast(view)); + native_window.center(); + native_window.setIsVisible(true); + native_window.makeKeyAndOrderFront(null); const delegate = objc.mach.WindowDelegate.allocInit(); - defer window.setDelegate(@ptrCast(delegate)); + defer native_window.setDelegate(@ptrCast(delegate)); { // Set WindowDelegate blocks - var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, darwin, null, null); + var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, core, null, null); delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); - var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, darwin, null, null); + var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, core, null, null); delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); } - } else std.debug.panic("mach: window failed to initialize", .{}); - } - - darwin.* = .{ - .allocator = options.allocator, - .core = core, - .title = options.title, - .display_mode = options.display_mode, - .vsync_mode = .none, - .cursor_mode = .normal, - .cursor_shape = .arrow, - .border = options.border, - .headless = options.headless, - .refresh_rate = 60, // TODO: set to something meaningful - .size = options.size, - .surface_descriptor = surface_descriptor, - .window = window_opt, - }; -} - -pub fn deinit(darwin: *Darwin) void { - if (darwin.window) |w| @as(*objc.foundation.ObjectProtocol, @ptrCast(w)).release(); - return; -} -pub fn update(darwin: *Darwin) !void { - if (darwin.window) |window| window.update(); -} - -pub fn setTitle(darwin: *Darwin, title: [:0]const u8) void { - if (darwin.window) |window| { - var string = objc.app_kit.String.allocInit(); - defer string.release(); - string = string.initWithUTF8String(title.ptr); - window.setTitle(string); - } -} - -pub fn setDisplayMode(_: *Darwin, _: DisplayMode) void { - return; -} - -pub fn setBorder(_: *Darwin, _: bool) void { - return; -} - -pub fn setHeadless(_: *Darwin, _: bool) void { - return; -} - -pub fn setVSync(_: *Darwin, _: VSyncMode) void { - return; -} - -pub fn setSize(darwin: *Darwin, size: Size) void { - if (darwin.window) |window| { - var frame = window.frame(); - frame.size.height = @floatFromInt(size.height); - frame.size.width = @floatFromInt(size.width); - window.setFrame_display_animate(frame, true, true); + core.windows.setAllRaw(window_id, core_window); + } else std.debug.panic("mach: window failed to initialize", .{}); } } -pub fn setCursorMode(_: *Darwin, _: CursorMode) void { - return; -} - -pub fn setCursorShape(_: *Darwin, _: CursorShape) void { - return; -} +// pub fn init( +// darwin: *Darwin, +// core: *Core, +// //options: InitOptions, +// ) !void { +// // var surface_descriptor = gpu.Surface.Descriptor{}; + +// // // TODO: support UIKit. +// // var window_opt: ?*objc.app_kit.Window = null; +// // if (!options.headless) { +// // // If the application is not headless, we need to make the application a genuine UI application +// // // by setting the activation policy, this moves the process to foreground +// // _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); + +// // const metal_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromMetalLayer); +// // const layer = objc.quartz_core.MetalLayer.new(); +// // defer layer.release(); +// // layer.setDisplaySyncEnabled(true); +// // metal_descriptor.* = .{ +// // .layer = layer, +// // }; +// // surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; + +// // const screen = objc.app_kit.Screen.mainScreen(); +// // const rect = objc.core_graphics.Rect{ +// // .origin = .{ .x = 0, .y = 0 }, +// // .size = .{ .width = @floatFromInt(options.size.width), .height = @floatFromInt(options.size.height) }, +// // }; + +// // const window_style = +// // (if (options.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | +// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | +// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | +// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | +// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); + +// // window_opt = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( +// // rect, +// // window_style, +// // objc.app_kit.BackingStoreBuffered, +// // false, +// // screen, +// // ); +// // if (window_opt) |window| { +// // window.setReleasedWhenClosed(false); + +// // var view = objc.mach.View.allocInit(); +// // view.setLayer(@ptrCast(layer)); + +// // { +// // var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, darwin, null, null); +// // view.setBlock_keyDown(keyDown.asBlock().copy()); + +// // var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, darwin, null, null); +// // view.setBlock_keyUp(keyUp.asBlock().copy()); +// // } +// // window.setContentView(@ptrCast(view)); +// // window.center(); +// // window.setIsVisible(true); +// // window.makeKeyAndOrderFront(null); + +// // const delegate = objc.mach.WindowDelegate.allocInit(); +// // defer window.setDelegate(@ptrCast(delegate)); +// // { // Set WindowDelegate blocks +// // var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, darwin, null, null); +// // delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); + +// // var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, darwin, null, null); +// // delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); +// // } +// // } else std.debug.panic("mach: window failed to initialize", .{}); +// // } + +// darwin.* = .{ +// .allocator = core.allocator, +// .core = core, +// // .title = options.title, +// // .display_mode = options.display_mode, +// // .vsync_mode = .none, +// // .cursor_mode = .normal, +// // .cursor_shape = .arrow, +// // .border = options.border, +// // .headless = options.headless, +// // .refresh_rate = 60, // TODO: set to something meaningful +// // .size = options.size, +// // .surface_descriptor = surface_descriptor, +// .window = window_opt, +// }; +// } + +// pub fn deinit(darwin: *Darwin) void { +// if (darwin.window) |w| @as(*objc.foundation.ObjectProtocol, @ptrCast(w)).release(); +// return; +// } + +// pub fn update(darwin: *Darwin) !void { +// if (darwin.window) |window| window.update(); +// } + +// pub fn setTitle(darwin: *Darwin, title: [:0]const u8) void { +// if (darwin.window) |window| { +// var string = objc.app_kit.String.allocInit(); +// defer string.release(); +// string = string.initWithUTF8String(title.ptr); +// window.setTitle(string); +// } +// } + +// pub fn setDisplayMode(_: *Darwin, _: DisplayMode) void { +// return; +// } + +// pub fn setBorder(_: *Darwin, _: bool) void { +// return; +// } + +// pub fn setHeadless(_: *Darwin, _: bool) void { +// return; +// } + +// pub fn setVSync(_: *Darwin, _: VSyncMode) void { +// return; +// } + +// pub fn setSize(darwin: *Darwin, window_id: mach.ObjectID, size: Size) void { +// _ = darwin; // autofix +// _ = window_id; // autofix +// _ = size; // autofix +// // if (darwin.core.windows.getAll(window_id)) |window| { + +// // } +// } + +// pub fn setCursorMode(_: *Darwin, _: CursorMode) void { +// return; +// } + +// pub fn setCursorShape(_: *Darwin, _: CursorShape) void { +// return; +// } const WindowDelegateCallbacks = struct { - pub fn windowWillResize_toSize(block: *objc.foundation.BlockLiteral(*Darwin), size: objc.app_kit.Size) callconv(.C) void { - const darwin: *Darwin = block.context; + pub fn windowWillResize_toSize(block: *objc.foundation.BlockLiteral(*Core), size: objc.app_kit.Size) callconv(.C) void { + const core: *Core = block.context; + _ = core; // autofix const s: Size = .{ .width = @intFromFloat(size.width), .height = @intFromFloat(size.height) }; + _ = s; // autofix - // TODO: Eventually we need to be able to tie a window here with the window Objects in core, and treat the windows - // as a list, rather than a single main window - darwin.size = .{ - .height = s.width, - .width = s.height, - }; - darwin.core.swap_chain_update.set(); + // // TODO: Eventually we need to be able to tie a window here with the window Objects in core, and treat the windows + // // as a list, rather than a single main window + // core.size = .{ + // .height = s.width, + // .width = s.height, + // }; + // core.swap_chain_update.set(); - darwin.core.windows.setRaw(darwin.core.main_window, .width, s.width); - darwin.core.windows.setRaw(darwin.core.main_window, .height, s.height); + // darwin.core.windows.setRaw(darwin.core.main_window, .width, s.width); + // darwin.core.windows.setRaw(darwin.core.main_window, .height, s.height); - darwin.core.pushEvent(.{ .framebuffer_resize = .{ .width = s.width, .height = s.height } }); + // darwin.core.pushEvent(.{ .framebuffer_resize = .{ .width = s.width, .height = s.height } }); } - pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Darwin)) callconv(.C) bool { - const darwin: *Darwin = block.context; - darwin.core.pushEvent(.close); + pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Core)) callconv(.C) bool { + const core: *Core = block.context; + core.pushEvent(.close); return false; } }; const ViewCallbacks = struct { - pub fn keyDown(block: *objc.foundation.BlockLiteral(*Darwin), event: *objc.app_kit.Event) callconv(.C) void { - const darwin: *Darwin = block.context; + pub fn keyDown(block: *objc.foundation.BlockLiteral(*Core), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context; if (event.isARepeat()) { - darwin.core.pushEvent(.{ .key_repeat = .{ + core.pushEvent(.{ .key_repeat = .{ .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); } else { - darwin.core.pushEvent(.{ .key_press = .{ + core.pushEvent(.{ .key_press = .{ .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); } } - pub fn keyUp(block: *objc.foundation.BlockLiteral(*Darwin), event: *objc.app_kit.Event) callconv(.C) void { - const darwin: *Darwin = block.context; + pub fn keyUp(block: *objc.foundation.BlockLiteral(*Core), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context; - darwin.core.pushEvent(.{ .key_release = .{ + core.pushEvent(.{ .key_release = .{ .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); diff --git a/src/core/Null.zig b/src/core/Null.zig index 37767c71ff..a222f2e3e4 100644 --- a/src/core/Null.zig +++ b/src/core/Null.zig @@ -5,7 +5,7 @@ const std = @import("std"); const mach = @import("../main.zig"); const Core = @import("../Core.zig"); const gpu = mach.gpu; -const InitOptions = Core.InitOptions; +//const InitOptions = Core.InitOptions; const Event = Core.Event; const KeyEvent = Core.KeyEvent; const MouseButtonEvent = Core.MouseButtonEvent; @@ -40,10 +40,10 @@ surface_descriptor: gpu.Surface.Descriptor, pub fn init( nul: *Null, core: *Core, - options: InitOptions, + //options: InitOptions, ) !void { _ = nul; - _ = options; + //_ = options; _ = core; return; } diff --git a/src/core/Windows.zig b/src/core/Windows.zig index d955a129df..3f971890b4 100644 --- a/src/core/Windows.zig +++ b/src/core/Windows.zig @@ -21,6 +21,14 @@ const KeyMods = Core.KeyMods; const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); const Win32 = @This(); +pub const Native = struct { + window: w.HWND, + surrogate: u16 = 0, + dinput: *w.IDirectInput8W, + saved_window_rect: w.RECT, + surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, +}; + // -------------------------- // Module state // -------------------------- @@ -28,22 +36,22 @@ allocator: std.mem.Allocator, core: *Core, // Core platform interface -surface_descriptor: gpu.Surface.Descriptor, -display_mode: DisplayMode, -vsync_mode: VSyncMode, -cursor_mode: CursorMode, -cursor_shape: CursorShape, -border: bool, -headless: bool, -size: Size, - -// Internals -window: w.HWND, -refresh_rate: u32, -surrogate: u16 = 0, -dinput: *w.IDirectInput8W, -saved_window_rect: w.RECT, -surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, +// surface_descriptor: gpu.Surface.Descriptor, +// display_mode: DisplayMode, +// vsync_mode: VSyncMode, +// cursor_mode: CursorMode, +// cursor_shape: CursorShape, +// border: bool, +// headless: bool, +// size: Size, + +// // Internals +// window: w.HWND, +// refresh_rate: u32, +// surrogate: u16 = 0, +// dinput: *w.IDirectInput8W, +// saved_window_rect: w.RECT, +// surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, // ------------------------------ // Platform interface diff --git a/src/module.zig b/src/module.zig index b676a3c7f3..bf3ed965ec 100644 --- a/src/module.zig +++ b/src/module.zig @@ -85,7 +85,7 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { pub const Slice = struct { index: Index, - objs: *Objects(T), + objs: *Objects(options, T), /// Same as Objects(T).set but doesn't employ safety checks pub fn set(objs: *@This(), id: ObjectID, value: T) void { @@ -127,6 +127,7 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { defer iter.index += 1; if (!dead.isSet(iter.index)) return @bitCast(PackedID{ + .type_id = iter.objs.internal.type_id, .generation = generation.items[iter.index], .index = iter.index, }); From e63aa65038b33fb43cba91a83b71e3963504a36c Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 30 Nov 2024 11:02:19 -0600 Subject: [PATCH 4/7] obj: `get` and `getValue` (renamed `getAll`) now do not return optionals, comment revisions, `device` is no longer optional, `native` is optional --- examples/core-triangle/App.zig | 183 +++++++++++++-------------- src/Core.zig | 221 ++++++++++++++++----------------- src/core/Darwin.zig | 153 +++++++++++------------ src/module.zig | 14 +-- 4 files changed, 274 insertions(+), 297 deletions(-) diff --git a/examples/core-triangle/App.zig b/examples/core-triangle/App.zig index 9f2fadc98f..0a280dbce3 100644 --- a/examples/core-triangle/App.zig +++ b/examples/core-triangle/App.zig @@ -25,74 +25,73 @@ pub fn init( core.on_tick = app_mod.id.tick; core.on_exit = app_mod.id.deinit; - if (core.windows.getAll(core.main_window)) |cw| { - const core_window = cw; - if (core_window.device) |device| { - - // Create our shader module - const shader_module = device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); - defer shader_module.release(); - - // Blend state describes how rendered colors get blended - const blend = gpu.BlendState{}; - - // Color target describes e.g. the pixel format of the window we are rendering to. - const color_target = gpu.ColorTargetState{ - .format = core.windows.get(core.main_window, .framebuffer_format).?, - .blend = &blend, - }; - - // Fragment state describes which shader and entrypoint to use for rendering fragments. - const fragment = gpu.FragmentState.init(.{ + const main_window = core.windows.getValue(core.main_window); + if (main_window.native != null) { + // if window.native is not null, the window is initialized + + // Create our shader module + const shader_module = main_window.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); + defer shader_module.release(); + + // Blend state describes how rendered colors get blended + const blend = gpu.BlendState{}; + + // Color target describes e.g. the pixel format of the window we are rendering to. + const color_target = gpu.ColorTargetState{ + .format = core.windows.get(core.main_window, .framebuffer_format), + .blend = &blend, + }; + + // Fragment state describes which shader and entrypoint to use for rendering fragments. + const fragment = gpu.FragmentState.init(.{ + .module = shader_module, + .entry_point = "frag_main", + .targets = &.{color_target}, + }); + + // Create our render pipeline that will ultimately get pixels onto the screen. + const label = @tagName(mach_module) ++ ".init"; + const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ + .label = label, + .fragment = &fragment, + .vertex = gpu.VertexState{ .module = shader_module, - .entry_point = "frag_main", - .targets = &.{color_target}, - }); - - // Create our render pipeline that will ultimately get pixels onto the screen. - const label = @tagName(mach_module) ++ ".init"; - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .label = label, - .fragment = &fragment, - .vertex = gpu.VertexState{ - .module = shader_module, - .entry_point = "vertex_main", - }, - }; - const pipeline = device.createRenderPipeline(&pipeline_descriptor); - - // Store our render pipeline in our module's state, so we can access it later on. - app.* = .{ - .title_timer = try mach.time.Timer.start(), - .pipeline = pipeline, - }; - } + .entry_point = "vertex_main", + }, + }; + const pipeline = main_window.device.createRenderPipeline(&pipeline_descriptor); + + // Store our render pipeline in our module's state, so we can access it later on. + app.* = .{ + .title_timer = try mach.time.Timer.start(), + .pipeline = pipeline, + }; } - - // TODO(object): window-title - // try updateWindowTitle(core); } +// TODO(object): window-title +// try updateWindowTitle(core); + pub fn tick(app: *App, core: *mach.Core) void { while (core.nextEvent()) |event| { switch (event) { .key_press => |ev| { switch (ev.key) { .right => { - var w = core.windows.getAll(core.main_window).?; + var w = core.windows.getValue(core.main_window); w.width = w.width + 10; - core.windows.setAll(core.main_window, w); + core.windows.setValue(core.main_window, w); }, .left => { - var w = core.windows.getAll(core.main_window).?; + var w = core.windows.getValue(core.main_window); w.width = w.width - 10; - core.windows.setAll(core.main_window, w); + core.windows.setValue(core.main_window, w); }, .up => { - if (core.windows.get(core.main_window, .height)) |height| core.windows.set(core.main_window, .height, height + 10); + core.windows.set(core.main_window, .height, core.windows.get(core.main_window, .height) + 10); }, .down => { - if (core.windows.get(core.main_window, .height)) |height| core.windows.set(core.main_window, .height, height - 10); + core.windows.set(core.main_window, .height, core.windows.get(core.main_window, .height) - 10); }, else => {}, } @@ -102,50 +101,46 @@ pub fn tick(app: *App, core: *mach.Core) void { } } - if (core.windows.getAll(core.main_window)) |mw| { - var main_window = mw; - - // Window is ready when device is not null - if (main_window.device) |device| { - - // Grab the back buffer of the swapchain - // TODO(Core) - const back_buffer_view = main_window.swap_chain.getCurrentTextureView().?; - defer back_buffer_view.release(); - - // Create a command encoder - const label = @tagName(mach_module) ++ ".tick"; - - const encoder = device.createCommandEncoder(&.{ .label = label }); - defer encoder.release(); - - // Begin render pass - const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; - const color_attachments = [_]gpu.RenderPassColorAttachment{.{ - .view = back_buffer_view, - .clear_value = sky_blue_background, - .load_op = .clear, - .store_op = .store, - }}; - const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ - .label = label, - .color_attachments = &color_attachments, - })); - defer render_pass.release(); - - // Draw - render_pass.setPipeline(app.pipeline); - render_pass.draw(3, 1, 0, 0); - - // Finish render pass - render_pass.end(); - - // Submit our commands to the queue - var command = encoder.finish(&.{ .label = label }); - defer command.release(); - main_window.queue.submit(&[_]*gpu.CommandBuffer{command}); - } - } + var main_window = core.windows.getValue(core.main_window); + + // Window is ready when device is not null + + // Grab the back buffer of the swapchain + // TODO(Core) + const back_buffer_view = main_window.swap_chain.getCurrentTextureView().?; + defer back_buffer_view.release(); + + // Create a command encoder + const label = @tagName(mach_module) ++ ".tick"; + + const encoder = main_window.device.createCommandEncoder(&.{ .label = label }); + defer encoder.release(); + + // Begin render pass + const sky_blue_background = gpu.Color{ .r = 0.776, .g = 0.988, .b = 1, .a = 1 }; + const color_attachments = [_]gpu.RenderPassColorAttachment{.{ + .view = back_buffer_view, + .clear_value = sky_blue_background, + .load_op = .clear, + .store_op = .store, + }}; + const render_pass = encoder.beginRenderPass(&gpu.RenderPassDescriptor.init(.{ + .label = label, + .color_attachments = &color_attachments, + })); + defer render_pass.release(); + + // Draw + render_pass.setPipeline(app.pipeline); + render_pass.draw(3, 1, 0, 0); + + // Finish render pass + render_pass.end(); + + // Submit our commands to the queue + var command = encoder.finish(&.{ .label = label }); + defer command.release(); + main_window.queue.submit(&[_]*gpu.CommandBuffer{command}); // update the window title every second // if (app.title_timer.read() >= 1.0) { diff --git a/src/Core.zig b/src/Core.zig index 7a094cbcbd..a0c384abc0 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -75,7 +75,7 @@ windows: mach.Objects( // GPU // When device is not null, the rest of the fields have been // initialized. - device: ?*gpu.Device = null, + device: *gpu.Device = undefined, instance: *gpu.Instance = undefined, adapter: *gpu.Adapter = undefined, queue: *gpu.Queue = undefined, @@ -94,7 +94,7 @@ windows: mach.Objects( .render_attachment = true, }, - native: Platform.Native = .{}, + native: ?Platform.Native = null, }, ), @@ -179,86 +179,83 @@ pub fn init(core: *Core) !void { } pub fn initWindow(core: *Core, window_id: mach.ObjectID) !void { - if (core.windows.getAll(window_id)) |cw| { - var core_window = cw; - - core_window.instance = gpu.createInstance(null) orelse { - log.err("failed to create GPU instance", .{}); - std.process.exit(1); - }; - core_window.surface = core_window.instance.createSurface(&core_window.surface_descriptor); - - var response: RequestAdapterResponse = undefined; - core_window.instance.requestAdapter(&gpu.RequestAdapterOptions{ - .compatible_surface = core_window.surface, - .power_preference = core_window.power_preference, - .force_fallback_adapter = .false, - }, &response, requestAdapterCallback); - if (response.status != .success) { - log.err("failed to create GPU adapter: {?s}", .{response.message}); - log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{}); - std.process.exit(1); - } + var core_window = core.windows.getValue(window_id); - // Print which adapter we are going to use. - var props = std.mem.zeroes(gpu.Adapter.Properties); - response.adapter.?.getProperties(&props); - if (props.backend_type == .null) { - log.err("no backend found for {s} adapter", .{props.adapter_type.name()}); - std.process.exit(1); - } - log.info("found {s} backend on {s} adapter: {s}, {s}\n", .{ - props.backend_type.name(), - props.adapter_type.name(), - props.name, - props.driver_description, - }); - - core_window.adapter = response.adapter.?; - - // Create a device with default limits/features. - core_window.device = response.adapter.?.createDevice(&.{ - .required_features_count = if (core_window.required_features) |v| @as(u32, @intCast(v.len)) else 0, - .required_features = if (core_window.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null, - .required_limits = if (core_window.required_limits) |limits| @as(?*const gpu.RequiredLimits, &gpu.RequiredLimits{ - .limits = limits, - }) else null, - .device_lost_callback = &deviceLostCallback, - .device_lost_userdata = null, - }) orelse { - log.err("failed to create GPU device\n", .{}); - std.process.exit(1); - }; - core_window.device.?.setUncapturedErrorCallback({}, printUnhandledErrorCallback); - core_window.queue = core_window.device.?.getQueue(); - - core_window.swap_chain_descriptor = gpu.SwapChain.Descriptor{ - .label = "main swap chain", - .usage = core_window.swap_chain_usage, - .format = .bgra8_unorm, - .width = core_window.width, - .height = core_window.height, - .present_mode = switch (core_window.vsync_mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }, - }; - core_window.swap_chain = core_window.device.?.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - core_window.framebuffer_format = core_window.swap_chain_descriptor.format; - core_window.framebuffer_width = core_window.swap_chain_descriptor.width; - core_window.framebuffer_height = core_window.swap_chain_descriptor.height; - - core.windows.setAllRaw(window_id, core_window); + core_window.instance = gpu.createInstance(null) orelse { + log.err("failed to create GPU instance", .{}); + std.process.exit(1); + }; + core_window.surface = core_window.instance.createSurface(&core_window.surface_descriptor); + + var response: RequestAdapterResponse = undefined; + core_window.instance.requestAdapter(&gpu.RequestAdapterOptions{ + .compatible_surface = core_window.surface, + .power_preference = core_window.power_preference, + .force_fallback_adapter = .false, + }, &response, requestAdapterCallback); + if (response.status != .success) { + log.err("failed to create GPU adapter: {?s}", .{response.message}); + log.info("-> maybe try MACH_GPU_BACKEND=opengl ?", .{}); + std.process.exit(1); + } + + // Print which adapter we are going to use. + var props = std.mem.zeroes(gpu.Adapter.Properties); + response.adapter.?.getProperties(&props); + if (props.backend_type == .null) { + log.err("no backend found for {s} adapter", .{props.adapter_type.name()}); + std.process.exit(1); } + log.info("found {s} backend on {s} adapter: {s}, {s}\n", .{ + props.backend_type.name(), + props.adapter_type.name(), + props.name, + props.driver_description, + }); + + core_window.adapter = response.adapter.?; + + // Create a device with default limits/features. + core_window.device = response.adapter.?.createDevice(&.{ + .required_features_count = if (core_window.required_features) |v| @as(u32, @intCast(v.len)) else 0, + .required_features = if (core_window.required_features) |v| @as(?[*]const gpu.FeatureName, v.ptr) else null, + .required_limits = if (core_window.required_limits) |limits| @as(?*const gpu.RequiredLimits, &gpu.RequiredLimits{ + .limits = limits, + }) else null, + .device_lost_callback = &deviceLostCallback, + .device_lost_userdata = null, + }) orelse { + log.err("failed to create GPU device\n", .{}); + std.process.exit(1); + }; + core_window.device.setUncapturedErrorCallback({}, printUnhandledErrorCallback); + core_window.queue = core_window.device.getQueue(); + + core_window.swap_chain_descriptor = gpu.SwapChain.Descriptor{ + .label = "main swap chain", + .usage = core_window.swap_chain_usage, + .format = .bgra8_unorm, + .width = core_window.width, + .height = core_window.height, + .present_mode = switch (core_window.vsync_mode) { + .none => .immediate, + .double => .fifo, + .triple => .mailbox, + }, + }; + core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); + core_window.framebuffer_format = core_window.swap_chain_descriptor.format; + core_window.framebuffer_width = core_window.swap_chain_descriptor.width; + core_window.framebuffer_height = core_window.swap_chain_descriptor.height; + + core.windows.setValueRaw(window_id, core_window); } pub fn tick(core: *Core, core_mod: mach.Mod(Core)) !void { - // Allow each platform to react to any changes - // Do we want to call this prior to calling `on_tick`? - // If we dont, and the user calls `updated()` on a window field - // it would set the field back to false, and our platform wouldnt catch - // the update. If we call if before we call the user tick, theres no opportunity. + // TODO(core)(slimsag): consider execution order of mach.Core (e.g. creating a new window + // during application execution, rendering to multiple windows, etc.) and how + // that relates to Platform.tick being responsible for both handling window updates + // (like title/size changes) and window creation, plus multi-threaded rendering. try Platform.tick(core); core_mod.run(core.on_tick.?); @@ -308,11 +305,10 @@ pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void { } fn platform_update_callback(core: *Core, core_mod: mach.Mod(Core)) !bool { - // Allow each platform to react to any changes - // Do we want to call this prior to calling `on_tick`? - // If we dont, and the user calls `updated()` on a window field - // it would set the field back to false, and our platform wouldnt catch - // the update. If we call if before we call the user tick, theres no opportunity. + // TODO(core)(slimsag): consider execution order of mach.Core (e.g. creating a new window + // during application execution, rendering to multiple windows, etc.) and how + // that relates to Platform.tick being responsible for both handling window updates + // (like title/size changes) and window creation, plus multi-threaded rendering. try Platform.tick(core); core_mod.run(core.on_tick.?); @@ -342,16 +338,13 @@ pub fn deinit(core: *Core) !void { var windows = core.windows.slice(); while (windows.next()) |window_id| { - if (core.windows.getAll(window_id)) |cw| { - var core_window = cw; - core_window.swap_chain.release(); - core_window.queue.release(); - if (core_window.device) |device| device.release(); - core_window.surface.release(); - core_window.adapter.release(); - core_window.instance.release(); - core_window.device = null; - } + var core_window = core.windows.getValue(window_id); + core_window.swap_chain.release(); + core_window.queue.release(); + core_window.device.release(); + core_window.surface.release(); + core_window.adapter.release(); + core_window.instance.release(); } core.events.deinit(); @@ -629,38 +622,34 @@ pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { var windows = core.windows.slice(); while (windows.next()) |window_id| { - if (core.windows.getAll(window_id)) |cw| { - var core_window = cw; + var core_window = core.windows.getValue(window_id); - if (core_window.device) |device| mach.sysgpu.Impl.deviceTick(device); + mach.sysgpu.Impl.deviceTick(core_window.device); - core_window.swap_chain.present(); + core_window.swap_chain.present(); - // Update swapchain for the next frame - if (core_window.swap_chain_update.isSet()) blk: { - core_window.swap_chain_update.reset(); + // Update swapchain for the next frame + if (core_window.swap_chain_update.isSet()) blk: { + core_window.swap_chain_update.reset(); - switch (core_window.vsync_mode) { - .triple => core.frame.target = 2 * core_window.refresh_rate, - else => core.frame.target = 0, - } + switch (core_window.vsync_mode) { + .triple => core.frame.target = 2 * core_window.refresh_rate, + else => core.frame.target = 0, + } - if (core_window.width == 0 or core_window.height == 0) break :blk; + if (core_window.width == 0 or core_window.height == 0) break :blk; - core_window.swap_chain_descriptor.present_mode = switch (core_window.vsync_mode) { - .none => .immediate, - .double => .fifo, - .triple => .mailbox, - }; + core_window.swap_chain_descriptor.present_mode = switch (core_window.vsync_mode) { + .none => .immediate, + .double => .fifo, + .triple => .mailbox, + }; - if (core_window.device) |device| { - core_window.swap_chain_descriptor.width = core_window.width; - core_window.swap_chain_descriptor.height = core_window.height; - core_window.swap_chain.release(); + core_window.swap_chain_descriptor.width = core_window.width; + core_window.swap_chain_descriptor.height = core_window.height; + core_window.swap_chain.release(); - core_window.swap_chain = device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); - } - } + core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor); } } diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index 2a9be9e7bc..f1fb41c1e3 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -62,27 +62,20 @@ pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@ pub fn tick(core: *Core) !void { var windows = core.windows.slice(); while (windows.next()) |window_id| { - if (core.windows.getAll(window_id)) |cw| { - const core_window = cw; - const native: Native = core_window.native; + const native_opt: ?Native = core.windows.get(window_id, .native); - if (core.windows.get(window_id, .device).? == null) { - // Window has not been set up yet, we need to init the window - //try initWindow(core, window_id); - //try core.initWindow(window_id); - - try initWindow(core, window_id); - try core.initWindow(window_id); - } + if (native_opt) |native| { if (native.window) |native_window| { // Handle resizing the window when the user changes width or height if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) { var frame = native_window.frame(); - frame.size.height = @floatFromInt(core.windows.get(window_id, .width).?); - frame.size.width = @floatFromInt(core.windows.get(window_id, .height).?); + frame.size.height = @floatFromInt(core.windows.get(window_id, .width)); + frame.size.width = @floatFromInt(core.windows.get(window_id, .height)); native_window.setFrame_display_animate(frame, true, true); } } + } else { + try initWindow(core, window_id); } } } @@ -91,76 +84,76 @@ fn initWindow( core: *Core, window_id: mach.ObjectID, ) !void { - if (core.windows.getAll(window_id)) |cw| { - var core_window = cw; - // If the application is not headless, we need to make the application a genuine UI application - // by setting the activation policy, this moves the process to foreground - // TODO: Only call this on the first window creation - _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); - - const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer); - const layer = objc.quartz_core.MetalLayer.new(); - defer layer.release(); - layer.setDisplaySyncEnabled(true); - metal_descriptor.* = .{ - .layer = layer, - }; - core_window.surface_descriptor = .{}; - core_window.surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; + var core_window = core.windows.getValue(window_id); + // If the application is not headless, we need to make the application a genuine UI application + // by setting the activation policy, this moves the process to foreground + // TODO: Only call this on the first window creation + _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); + + const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer); + const layer = objc.quartz_core.MetalLayer.new(); + defer layer.release(); + layer.setDisplaySyncEnabled(true); + metal_descriptor.* = .{ + .layer = layer, + }; + core_window.surface_descriptor = .{}; + core_window.surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; - const screen = objc.app_kit.Screen.mainScreen(); - const rect = objc.core_graphics.Rect{ - .origin = .{ .x = 0, .y = 0 }, - .size = .{ .width = @floatFromInt(core_window.width), .height = @floatFromInt(core_window.height) }, - }; + const screen = objc.app_kit.Screen.mainScreen(); + const rect = objc.core_graphics.Rect{ + .origin = .{ .x = 0, .y = 0 }, + .size = .{ .width = @floatFromInt(core_window.width), .height = @floatFromInt(core_window.height) }, + }; - const window_style = - (if (core_window.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | - (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); - - const native_window_opt: ?*objc.app_kit.Window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( - rect, - window_style, - objc.app_kit.BackingStoreBuffered, - false, - screen, - ); - if (native_window_opt) |native_window| { - core_window.native = .{ .window = native_window }; - - native_window.setReleasedWhenClosed(false); - - var view = objc.mach.View.allocInit(); - view.setLayer(@ptrCast(layer)); - - { - var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, core, null, null); - view.setBlock_keyDown(keyDown.asBlock().copy()); - - var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, core, null, null); - view.setBlock_keyUp(keyUp.asBlock().copy()); - } - native_window.setContentView(@ptrCast(view)); - native_window.center(); - native_window.setIsVisible(true); - native_window.makeKeyAndOrderFront(null); - - const delegate = objc.mach.WindowDelegate.allocInit(); - defer native_window.setDelegate(@ptrCast(delegate)); - { // Set WindowDelegate blocks - var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, core, null, null); - delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); - - var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, core, null, null); - delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); - } + const window_style = + (if (core_window.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | + (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); + + const native_window_opt: ?*objc.app_kit.Window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( + rect, + window_style, + objc.app_kit.BackingStoreBuffered, + false, + screen, + ); + if (native_window_opt) |native_window| { + native_window.setReleasedWhenClosed(false); + + var view = objc.mach.View.allocInit(); + view.setLayer(@ptrCast(layer)); + + { + var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, core, null, null); + view.setBlock_keyDown(keyDown.asBlock().copy()); + + var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, core, null, null); + view.setBlock_keyUp(keyUp.asBlock().copy()); + } + native_window.setContentView(@ptrCast(view)); + native_window.center(); + native_window.setIsVisible(true); + native_window.makeKeyAndOrderFront(null); + + const delegate = objc.mach.WindowDelegate.allocInit(); + defer native_window.setDelegate(@ptrCast(delegate)); + { // Set WindowDelegate blocks + var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, core, null, null); + delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); + + var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, core, null, null); + delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); + } - core.windows.setAllRaw(window_id, core_window); - } else std.debug.panic("mach: window failed to initialize", .{}); - } + // Set core_window.native, which we use to check if a window is initialized + // Then call core.initWindow to finish initializing the window + core_window.native = .{ .window = native_window }; + core.windows.setValueRaw(window_id, core_window); + try core.initWindow(window_id); + } else std.debug.panic("mach: window failed to initialize", .{}); } // pub fn init( diff --git a/src/module.zig b/src/module.zig index bf3ed965ec..1cb63fa5f9 100644 --- a/src/module.zig +++ b/src/module.zig @@ -209,10 +209,10 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { /// /// Unlike setAll(), this method does not respect any mach.Objects tracking /// options, so changes made to an object through this method will not be tracked. - pub fn setAllRaw(objs: *@This(), id: ObjectID, value: T) void { + pub fn setValueRaw(objs: *@This(), id: ObjectID, value: T) void { const data = &objs.internal.data; - const unpacked = objs.validateAndUnpack(id, "setAllRaw"); + const unpacked = objs.validateAndUnpack(id, "setValueRaw"); data.set(unpacked.index, value); } @@ -220,10 +220,10 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { /// /// Unlike setAllRaw, this method respects mach.Objects tracking /// and changes made to an object through this method will be tracked. - pub fn setAll(objs: *@This(), id: ObjectID, value: T) void { + pub fn setValue(objs: *@This(), id: ObjectID, value: T) void { const data = &objs.internal.data; - const unpacked = objs.validateAndUnpack(id, "setAll"); + const unpacked = objs.validateAndUnpack(id, "setValue"); data.set(unpacked.index, value); if (objs.internal.updated) |*updated_fields| { @@ -267,7 +267,7 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { } /// Get a single field. - pub fn get(objs: *@This(), id: ObjectID, comptime field_name: std.meta.FieldEnum(T)) ?std.meta.FieldType(T, field_name) { + pub fn get(objs: *@This(), id: ObjectID, comptime field_name: std.meta.FieldEnum(T)) std.meta.FieldType(T, field_name) { const data = &objs.internal.data; const unpacked = objs.validateAndUnpack(id, "get"); @@ -276,10 +276,10 @@ pub fn Objects(options: ObjectsOptions, comptime T: type) type { } /// Get all fields. - pub fn getAll(objs: *@This(), id: ObjectID) ?T { + pub fn getValue(objs: *@This(), id: ObjectID) T { const data = &objs.internal.data; - const unpacked = objs.validateAndUnpack(id, "getAll"); + const unpacked = objs.validateAndUnpack(id, "getValue"); return data.get(unpacked.index); } From 33e9494ff20c361f08658cb913be68e9d134a31c Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 30 Nov 2024 14:28:39 -0600 Subject: [PATCH 5/7] core: Lots of cleanup of unnecessary comments --- src/Core.zig | 141 +-------------------------------------------------- 1 file changed, 1 insertion(+), 140 deletions(-) diff --git a/src/Core.zig b/src/Core.zig index a0c384abc0..1fe5dec19b 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -115,21 +115,11 @@ state: enum { exited, } = .running, -// TODO: handle window titles better -//title: [256:0]u8 = undefined, frame: mach.time.Frequency, input: mach.time.Frequency, -// GPU -// instance: *gpu.Instance, -// adapter: *gpu.Adapter, -// device: *gpu.Device, -// queue: *gpu.Queue, -// surface: *gpu.Surface, - // Internal module state allocator: std.mem.Allocator, -platform: Platform, events: EventQueue, input_state: InputState, oom: std.Thread.ResetEvent = .{}, @@ -142,18 +132,9 @@ pub fn init(core: *Core) !void { const main_window = try core.windows.new(.{}); - // TODO: Copy window title into owned buffer. - // var title: [256:0]u8 = undefined; - // if (options.title.len < title.len) { - // @memcpy(title[0..options.title.len], options.title); - // title[options.title.len] = 0; - // } - var events = EventQueue.init(allocator); try events.ensureTotalCapacity(8192); - // TODO: remove undefined initialization (disgusting!) - const platform: Platform = undefined; core.* = .{ // Note: since core.windows is initialized for us already, we just copy the pointer. .windows = core.windows, @@ -163,15 +144,13 @@ pub fn init(core: *Core) !void { .events = events, .input_state = .{}, - .platform = platform, .input = .{ .target = 0 }, .frame = .{ .target = 1 }, }; - //try initWindow(core, main_window); - // Tick the platform so that the platform can grab the newly created window // and perform initialization + // TODO: consider removing `main_window` and then this wont be necessary try Platform.tick(core); try core.frame.start(); @@ -260,7 +239,6 @@ pub fn tick(core: *Core, core_mod: mach.Mod(Core)) !void { core_mod.run(core.on_tick.?); core_mod.call(.presentFrame); - //core_mod.call(.processWindowUpdates); } pub fn main(core: *Core, core_mod: mach.Mod(Core)) !void { @@ -318,21 +296,6 @@ fn platform_update_callback(core: *Core, core_mod: mach.Mod(Core)) !bool { return core.state != .exited; } -// pub fn processWindowUpdates(core: *Core) void { -// while (core.windows.slice().next()) |window_id| { -// if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) { -// const window = core.windows.getAll(window_id); - -// if (window) |w| { -// core.platform.setSize(.{ -// .width = w.width, -// .height = w.height, -// }); -// } -// } -// } -// } - pub fn deinit(core: *Core) !void { core.state = .exited; @@ -391,50 +354,6 @@ pub fn outOfMemory(core: *@This()) bool { return true; } -// TODO(object) -// /// Sets the window title. The string must be owned by Core, and will not be copied or freed. It is -// /// advised to use the `core.title` buffer for this purpose, e.g.: -// /// -// /// ``` -// /// const title = try std.fmt.bufPrintZ(&core.title, "Hello, world!", .{}); -// /// core.setTitle(title); -// /// ``` -// pub inline fn setTitle(core: *@This(), value: [:0]const u8) void { -// return core.platform.setTitle(value); -// } - -// TODO(object) -// /// Set the window mode -// pub inline fn setDisplayMode(core: *@This(), mode: DisplayMode) void { -// return core.platform.setDisplayMode(mode); -// } - -// TODO(object) -// /// Returns the window mode -// pub inline fn displayMode(core: *@This()) DisplayMode { -// return core.platform.display_mode; -// } - -// TODO(object) -// pub inline fn setBorder(core: *@This(), value: bool) void { -// return core.platform.setBorder(value); -// } - -// TODO(object) -// pub inline fn border(core: *@This()) bool { -// return core.platform.border; -// } - -// TODO(object) -// pub inline fn setHeadless(core: *@This(), value: bool) void { -// return core.platform.setHeadless(value); -// } - -// TODO(object) -// pub inline fn headless(core: *@This()) bool { -// return core.platform.headless; -// } - pub fn keyPressed(core: *@This(), key: Key) bool { return core.input_state.isKeyPressed(key); } @@ -602,24 +521,6 @@ pub fn mousePosition(core: *@This()) Position { // } pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { - - // TODO(object)(window-title) - // // Update windows title - // var num_windows: usize = 0; - // var q = try entities.query(.{ - // .ids = mach.Entities.Mod.read(.id), - // .titles = Mod.read(.title), - // }); - // while (q.next()) |v| { - // for (v.ids, v.titles) |_, title| { - // num_windows += 1; - // state.platform.setTitle(title); - // } - // } - // if (num_windows > 1) @panic("mach: Core currently only supports a single window"); - - //_ = try core.platform.update(); - var windows = core.windows.slice(); while (windows.next()) |window_id| { var core_window = core.windows.getValue(window_id); @@ -678,28 +579,6 @@ pub fn presentFrame(core: *Core, core_mod: mach.Mod(Core)) !void { } } -// TODO(object)(window-title) -// /// Prints into the window title buffer using a format string and arguments. e.g. -// /// -// /// ``` -// /// try core.state().printTitle(core_mod, core_mod.state().main_window, "Hello, {s}!", .{"Mach"}); -// /// ``` -// pub fn printTitle( -// core: *@This(), -// window_id: mach.EntityID, -// comptime fmt: []const u8, -// args: anytype, -// ) !void { -// _ = window_id; -// // Allocate and assign a new window title slice. -// const slice = try std.fmt.allocPrintZ(core.allocator, fmt, args); -// defer core.allocator.free(slice); -// core.setTitle(slice); - -// // TODO: This function does not have access to *core.Mod to update -// // try core.Mod.set(window_id, .title, slice); -// } - pub fn exit(core: *Core) void { core.state = .exiting; } @@ -770,24 +649,6 @@ const Platform = switch (build_options.core_platform) { .null => @import("core/Null.zig"), }; -// TODO(object): this struct should not exist -// TODO: this should not be here, it is exposed because the platform implementations need it. -// pub const InitOptions = struct { -// allocator: std.mem.Allocator, -// is_app: bool = false, -// headless: bool = false, -// display_mode: DisplayMode = .windowed, -// border: bool = true, -// title: [:0]const u8 = "Mach core", -// size: Size = .{ .width = 1920 / 2, .height = 1080 / 2 }, -// power_preference: gpu.PowerPreference = .undefined, -// required_features: ?[]const gpu.FeatureName = null, -// required_limits: ?gpu.Limits = null, -// swap_chain_usage: gpu.Texture.UsageFlags = .{ -// .render_attachment = true, -// }, -// }; - pub const InputState = struct { const KeyBitSet = std.StaticBitSet(@as(u8, @intFromEnum(Key.max)) + 1); const MouseButtonSet = std.StaticBitSet(@as(u4, @intFromEnum(MouseButton.max)) + 1); From 59826b4c33a51560bd1ed599a03ef76db90f84aa Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 30 Nov 2024 15:19:37 -0600 Subject: [PATCH 6/7] core: `Event`s now all contain `window_id`, darwin/windows: event functions now send window id --- src/Core.zig | 24 +++- src/core/Darwin.zig | 225 ++++++++---------------------- src/core/Windows.zig | 316 ++++++++++++++++++++++++++++++------------- 3 files changed, 296 insertions(+), 269 deletions(-) diff --git a/src/Core.zig b/src/Core.zig index 1fe5dec19b..7ccea9def8 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -679,34 +679,50 @@ pub const Event = union(enum) { key_repeat: KeyEvent, key_release: KeyEvent, char_input: struct { + window_id: mach.ObjectID, codepoint: u21, }, mouse_motion: struct { + window_id: mach.ObjectID, pos: Position, }, mouse_press: MouseButtonEvent, mouse_release: MouseButtonEvent, mouse_scroll: struct { + window_id: mach.ObjectID, xoffset: f32, yoffset: f32, }, - framebuffer_resize: Size, - focus_gained, - focus_lost, - close, + window_resize: ResizeEvent, + focus_gained: struct { + window_id: mach.ObjectID, + }, + focus_lost: struct { + window_id: mach.ObjectID, + }, + close: struct { + window_id: mach.ObjectID, + }, }; pub const KeyEvent = struct { + window_id: mach.ObjectID, key: Key, mods: KeyMods, }; pub const MouseButtonEvent = struct { + window_id: mach.ObjectID, button: MouseButton, pos: Position, mods: KeyMods, }; +pub const ResizeEvent = struct { + window_id: mach.ObjectID, + size: Size, +}; + pub const MouseButton = enum { left, right, diff --git a/src/core/Darwin.zig b/src/core/Darwin.zig index f1fb41c1e3..eee8e40a72 100644 --- a/src/core/Darwin.zig +++ b/src/core/Darwin.zig @@ -2,7 +2,6 @@ const std = @import("std"); const mach = @import("../main.zig"); const Core = @import("../Core.zig"); const gpu = mach.gpu; -const InitOptions = Core.InitOptions; const Event = Core.Event; const KeyEvent = Core.KeyEvent; const MouseButtonEvent = Core.MouseButtonEvent; @@ -25,6 +24,11 @@ pub const Native = struct { window: ?*objc.app_kit.Window = null, }; +pub const Context = struct { + core: *Core, + window_id: mach.ObjectID, +}; + pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@TypeOf(on_each_update_fn))) noreturn { const Args = @TypeOf(args_tuple); const args_bytes = std.mem.asBytes(&args_tuple); @@ -126,11 +130,22 @@ fn initWindow( var view = objc.mach.View.allocInit(); view.setLayer(@ptrCast(layer)); + var context: Context = .{ .core = core, .window_id = window_id }; { - var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, core, null, null); + var keyDown = objc.foundation.stackBlockLiteral( + ViewCallbacks.keyDown, + &context, + null, + null, + ); view.setBlock_keyDown(keyDown.asBlock().copy()); - var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, core, null, null); + var keyUp = objc.foundation.stackBlockLiteral( + ViewCallbacks.keyUp, + &context, + null, + null, + ); view.setBlock_keyUp(keyUp.asBlock().copy()); } native_window.setContentView(@ptrCast(view)); @@ -141,10 +156,21 @@ fn initWindow( const delegate = objc.mach.WindowDelegate.allocInit(); defer native_window.setDelegate(@ptrCast(delegate)); { // Set WindowDelegate blocks - var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, core, null, null); + + var windowWillResize_toSize = objc.foundation.stackBlockLiteral( + WindowDelegateCallbacks.windowWillResize_toSize, + &context, + null, + null, + ); delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); - var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, core, null, null); + var windowShouldClose = objc.foundation.stackBlockLiteral( + WindowDelegateCallbacks.windowShouldClose, + &context, + null, + null, + ); delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); } @@ -156,195 +182,58 @@ fn initWindow( } else std.debug.panic("mach: window failed to initialize", .{}); } -// pub fn init( -// darwin: *Darwin, -// core: *Core, -// //options: InitOptions, -// ) !void { -// // var surface_descriptor = gpu.Surface.Descriptor{}; - -// // // TODO: support UIKit. -// // var window_opt: ?*objc.app_kit.Window = null; -// // if (!options.headless) { -// // // If the application is not headless, we need to make the application a genuine UI application -// // // by setting the activation policy, this moves the process to foreground -// // _ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular); - -// // const metal_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromMetalLayer); -// // const layer = objc.quartz_core.MetalLayer.new(); -// // defer layer.release(); -// // layer.setDisplaySyncEnabled(true); -// // metal_descriptor.* = .{ -// // .layer = layer, -// // }; -// // surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor }; - -// // const screen = objc.app_kit.Screen.mainScreen(); -// // const rect = objc.core_graphics.Rect{ -// // .origin = .{ .x = 0, .y = 0 }, -// // .size = .{ .width = @floatFromInt(options.size.width), .height = @floatFromInt(options.size.height) }, -// // }; - -// // const window_style = -// // (if (options.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) | -// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) | -// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) | -// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) | -// // (if (options.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0); - -// // window_opt = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen( -// // rect, -// // window_style, -// // objc.app_kit.BackingStoreBuffered, -// // false, -// // screen, -// // ); -// // if (window_opt) |window| { -// // window.setReleasedWhenClosed(false); - -// // var view = objc.mach.View.allocInit(); -// // view.setLayer(@ptrCast(layer)); - -// // { -// // var keyDown = objc.foundation.stackBlockLiteral(ViewCallbacks.keyDown, darwin, null, null); -// // view.setBlock_keyDown(keyDown.asBlock().copy()); - -// // var keyUp = objc.foundation.stackBlockLiteral(ViewCallbacks.keyUp, darwin, null, null); -// // view.setBlock_keyUp(keyUp.asBlock().copy()); -// // } -// // window.setContentView(@ptrCast(view)); -// // window.center(); -// // window.setIsVisible(true); -// // window.makeKeyAndOrderFront(null); - -// // const delegate = objc.mach.WindowDelegate.allocInit(); -// // defer window.setDelegate(@ptrCast(delegate)); -// // { // Set WindowDelegate blocks -// // var windowWillResize_toSize = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowWillResize_toSize, darwin, null, null); -// // delegate.setBlock_windowWillResize_toSize(windowWillResize_toSize.asBlock().copy()); - -// // var windowShouldClose = objc.foundation.stackBlockLiteral(WindowDelegateCallbacks.windowShouldClose, darwin, null, null); -// // delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy()); -// // } -// // } else std.debug.panic("mach: window failed to initialize", .{}); -// // } - -// darwin.* = .{ -// .allocator = core.allocator, -// .core = core, -// // .title = options.title, -// // .display_mode = options.display_mode, -// // .vsync_mode = .none, -// // .cursor_mode = .normal, -// // .cursor_shape = .arrow, -// // .border = options.border, -// // .headless = options.headless, -// // .refresh_rate = 60, // TODO: set to something meaningful -// // .size = options.size, -// // .surface_descriptor = surface_descriptor, -// .window = window_opt, -// }; -// } - -// pub fn deinit(darwin: *Darwin) void { -// if (darwin.window) |w| @as(*objc.foundation.ObjectProtocol, @ptrCast(w)).release(); -// return; -// } - -// pub fn update(darwin: *Darwin) !void { -// if (darwin.window) |window| window.update(); -// } - -// pub fn setTitle(darwin: *Darwin, title: [:0]const u8) void { -// if (darwin.window) |window| { -// var string = objc.app_kit.String.allocInit(); -// defer string.release(); -// string = string.initWithUTF8String(title.ptr); -// window.setTitle(string); -// } -// } - -// pub fn setDisplayMode(_: *Darwin, _: DisplayMode) void { -// return; -// } - -// pub fn setBorder(_: *Darwin, _: bool) void { -// return; -// } - -// pub fn setHeadless(_: *Darwin, _: bool) void { -// return; -// } - -// pub fn setVSync(_: *Darwin, _: VSyncMode) void { -// return; -// } - -// pub fn setSize(darwin: *Darwin, window_id: mach.ObjectID, size: Size) void { -// _ = darwin; // autofix -// _ = window_id; // autofix -// _ = size; // autofix -// // if (darwin.core.windows.getAll(window_id)) |window| { - -// // } -// } - -// pub fn setCursorMode(_: *Darwin, _: CursorMode) void { -// return; -// } - -// pub fn setCursorShape(_: *Darwin, _: CursorShape) void { -// return; -// } - const WindowDelegateCallbacks = struct { - pub fn windowWillResize_toSize(block: *objc.foundation.BlockLiteral(*Core), size: objc.app_kit.Size) callconv(.C) void { - const core: *Core = block.context; - _ = core; // autofix + pub fn windowWillResize_toSize(block: *objc.foundation.BlockLiteral(*Context), size: objc.app_kit.Size) callconv(.C) void { + const core: *Core = block.context.core; const s: Size = .{ .width = @intFromFloat(size.width), .height = @intFromFloat(size.height) }; - _ = s; // autofix - - // // TODO: Eventually we need to be able to tie a window here with the window Objects in core, and treat the windows - // // as a list, rather than a single main window - // core.size = .{ - // .height = s.width, - // .width = s.height, - // }; - // core.swap_chain_update.set(); - // darwin.core.windows.setRaw(darwin.core.main_window, .width, s.width); - // darwin.core.windows.setRaw(darwin.core.main_window, .height, s.height); + var window = core.windows.getValue(block.context.window_id); + window.width = s.width; + window.height = s.height; + window.swap_chain_update.set(); + core.windows.setValueRaw(block.context.window_id, window); - // darwin.core.pushEvent(.{ .framebuffer_resize = .{ .width = s.width, .height = s.height } }); + core.pushEvent(.{ .window_resize = .{ + .window_id = block.context.window_id, + .size = s, + } }); } - pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Core)) callconv(.C) bool { - const core: *Core = block.context; - core.pushEvent(.close); + pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Context)) callconv(.C) bool { + const core: *Core = block.context.core; + core.pushEvent(.{ .close = .{ .window_id = block.context.window_id } }); + + // TODO: This should just attempt to close the window, not the entire program, unless + // this is the only window. return false; } }; const ViewCallbacks = struct { - pub fn keyDown(block: *objc.foundation.BlockLiteral(*Core), event: *objc.app_kit.Event) callconv(.C) void { - const core: *Core = block.context; + pub fn keyDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; if (event.isARepeat()) { core.pushEvent(.{ .key_repeat = .{ + .window_id = window_id, .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); } else { core.pushEvent(.{ .key_press = .{ + .window_id = window_id, .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); } } - pub fn keyUp(block: *objc.foundation.BlockLiteral(*Core), event: *objc.app_kit.Event) callconv(.C) void { - const core: *Core = block.context; + pub fn keyUp(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void { + const core: *Core = block.context.core; + const window_id = block.context.window_id; core.pushEvent(.{ .key_release = .{ + .window_id = window_id, .key = machKeyFromKeycode(event.keyCode()), .mods = machModifierFromModifierFlag(event.modifierFlags()), } }); diff --git a/src/core/Windows.zig b/src/core/Windows.zig index 3f971890b4..d6e2aa452c 100644 --- a/src/core/Windows.zig +++ b/src/core/Windows.zig @@ -4,7 +4,6 @@ const mach = @import("../main.zig"); const Core = @import("../Core.zig"); const gpu = mach.gpu; -const InitOptions = Core.InitOptions; const Event = Core.Event; const KeyEvent = Core.KeyEvent; const MouseButtonEvent = Core.MouseButtonEvent; @@ -22,18 +21,23 @@ const EventQueue = std.fifo.LinearFifo(Event, .Dynamic); const Win32 = @This(); pub const Native = struct { - window: w.HWND, + window: w.HWND = undefined, surrogate: u16 = 0, - dinput: *w.IDirectInput8W, - saved_window_rect: w.RECT, - surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND, + dinput: *w.IDirectInput8W = undefined, + saved_window_rect: w.RECT = undefined, + surface_descriptor_from_hwnd: gpu.Surface.DescriptorFromWindowsHWND = undefined, +}; + +pub const Context = struct { + core: *Core, + window_id: mach.ObjectID, }; // -------------------------- // Module state // -------------------------- -allocator: std.mem.Allocator, -core: *Core, +// allocator: std.mem.Allocator, +// core: *Core, // Core platform interface // surface_descriptor: gpu.Surface.Descriptor, @@ -56,15 +60,107 @@ core: *Core, // ------------------------------ // Platform interface // ------------------------------ -pub fn init( - self: *Win32, +// pub fn init( +// self: *Win32, +// core: *Core, +// //options: InitOptions, +// ) !void { +// // self.allocator = options.allocator; +// // self.core = core; +// // self.size = options.size; +// // self.saved_window_rect = .{ .top = 0, .left = 0, .right = 0, .bottom = 0 }; + +// var native: Native = .{}; + +// const hInstance = w.GetModuleHandleW(null); +// const class_name = w.L("mach"); +// const class = std.mem.zeroInit(w.WNDCLASSW, .{ +// .style = w.CS_OWNDC, +// .lpfnWndProc = wndProc, +// .hInstance = hInstance, +// .hIcon = w.LoadIconW(null, @as([*:0]align(1) const u16, @ptrFromInt(@as(u32, w.IDI_APPLICATION)))), +// .hCursor = w.LoadCursorW(null, @as([*:0]align(1) const u16, @ptrFromInt(@as(u32, w.IDC_ARROW)))), +// .lpszClassName = class_name, +// }); +// if (w.RegisterClassW(&class) == 0) return error.Unexpected; + +// const title = try std.unicode.utf8ToUtf16LeAllocZ(self.allocator, options.title); +// defer self.allocator.free(title); + +// var request_window_width: i32 = @bitCast(self.size.width); +// var request_window_height: i32 = @bitCast(self.size.height); + +// const window_ex_style: w.WINDOW_EX_STYLE = .{ .APPWINDOW = 1 }; +// const window_style: w.WINDOW_STYLE = if (options.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; // w.WINDOW_STYLE{.POPUP = 1}; +// // TODO (win32): should border == false mean borderless display_mode? + +// var rect: w.RECT = .{ .left = 0, .top = 0, .right = request_window_width, .bottom = request_window_height }; + +// if (w.TRUE == w.AdjustWindowRectEx(&rect, window_style, w.FALSE, window_ex_style)) { +// request_window_width = rect.right - rect.left; +// request_window_height = rect.bottom - rect.top; +// } + +// const window = w.CreateWindowExW( +// window_ex_style, +// class_name, +// title, +// window_style, +// w.CW_USEDEFAULT, +// w.CW_USEDEFAULT, +// request_window_width, +// request_window_height, +// null, +// null, +// hInstance, +// null, +// ) orelse return error.Unexpected; + +// native.window = window; + +// var dinput: ?*w.IDirectInput8W = undefined; +// const ptr: ?*?*anyopaque = @ptrCast(&dinput); +// if (w.DirectInput8Create(hInstance, w.DIRECTINPUT_VERSION, w.IID_IDirectInput8W, ptr, null) != w.DI_OK) { +// return error.Unexpected; +// } +// native.dinput = dinput.?; + +// native.surface_descriptor_from_hwnd = .{ +// .hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?, +// .hwnd = window, +// }; + +// core_window.surface_descriptor = .{ .next_in_chain = .{ +// .from_windows_hwnd = &self.surface_descriptor_from_hwnd, +// } }; +// self.border = options.border; +// self.headless = options.headless; +// self.refresh_rate = 60; // TODO (win32) get monitor refresh rate +// self.vsync_mode = .triple; + +// _ = w.SetWindowLongPtrW(window, w.GWLP_USERDATA, @bitCast(@intFromPtr(self))); +// if (!options.headless) { +// setDisplayMode(self, options.display_mode); +// } + +// self.size = getClientRect(self); +// _ = w.GetWindowRect(self.window, &self.saved_window_rect); +// } + +// pub fn deinit(self: *Win32) void { +// _ = self.dinput.IUnknown_Release(); +// } + +pub fn tick(core: *Core) !void { + _ = core; // autofix + +} + +fn initWindow( core: *Core, - options: InitOptions, + window_id: mach.ObjectID, ) !void { - self.allocator = options.allocator; - self.core = core; - self.size = options.size; - self.saved_window_rect = .{ .top = 0, .left = 0, .right = 0, .bottom = 0 }; + const core_window = core.windows.getValue(window_id); const hInstance = w.GetModuleHandleW(null); const class_name = w.L("mach"); @@ -78,15 +174,14 @@ pub fn init( }); if (w.RegisterClassW(&class) == 0) return error.Unexpected; - const title = try std.unicode.utf8ToUtf16LeAllocZ(self.allocator, options.title); - defer self.allocator.free(title); + const title = try std.unicode.utf8ToUtf16LeAllocZ(core.allocator, core_window.title); + defer core.allocator.free(title); - var request_window_width: i32 = @bitCast(self.size.width); - var request_window_height: i32 = @bitCast(self.size.height); + var request_window_width: i32 = @bitCast(core_window.width); + var request_window_height: i32 = @bitCast(core_window.height); const window_ex_style: w.WINDOW_EX_STYLE = .{ .APPWINDOW = 1 }; - const window_style: w.WINDOW_STYLE = if (options.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; // w.WINDOW_STYLE{.POPUP = 1}; - // TODO (win32): should border == false mean borderless display_mode? + const window_style: w.WINDOW_STYLE = if (core_window.border) w.WS_OVERLAPPEDWINDOW else w.WS_POPUPWINDOW; // w.WINDOW_STYLE{.POPUP = 1}; var rect: w.RECT = .{ .left = 0, .top = 0, .right = request_window_width, .bottom = request_window_height }; @@ -95,7 +190,7 @@ pub fn init( request_window_height = rect.bottom - rect.top; } - const window = w.CreateWindowExW( + const native_window = w.CreateWindowExW( window_ex_style, class_name, title, @@ -110,50 +205,44 @@ pub fn init( null, ) orelse return error.Unexpected; - self.window = window; + var native: Native = .{}; var dinput: ?*w.IDirectInput8W = undefined; const ptr: ?*?*anyopaque = @ptrCast(&dinput); if (w.DirectInput8Create(hInstance, w.DIRECTINPUT_VERSION, w.IID_IDirectInput8W, ptr, null) != w.DI_OK) { return error.Unexpected; } - self.dinput = dinput.?; + native.dinput = dinput.?; - self.surface_descriptor_from_hwnd = .{ + native.surface_descriptor_from_hwnd = .{ .hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?, - .hwnd = window, + .hwnd = native_window, }; - self.surface_descriptor = .{ .next_in_chain = .{ - .from_windows_hwnd = &self.surface_descriptor_from_hwnd, + core_window.surface_descriptor = .{ .next_in_chain = .{ + .from_windows_hwnd = &native.surface_descriptor_from_hwnd, } }; - self.border = options.border; - self.headless = options.headless; - self.refresh_rate = 60; // TODO (win32) get monitor refresh rate - self.vsync_mode = .triple; - - _ = w.SetWindowLongPtrW(window, w.GWLP_USERDATA, @bitCast(@intFromPtr(self))); - if (!options.headless) { - setDisplayMode(self, options.display_mode); - } - self.size = getClientRect(self); - _ = w.GetWindowRect(self.window, &self.saved_window_rect); -} + const context: Context = .{ .core = core, .window_id = window_id }; -pub fn deinit(self: *Win32) void { - _ = self.dinput.IUnknown_Release(); -} + _ = w.SetWindowLongPtrW(native_window, w.GWLP_USERDATA, @bitCast(@intFromPtr(&context))); -pub fn update(self: *Win32) !void { - _ = self; - var msg: w.MSG = undefined; - while (w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE) != 0) { - _ = w.TranslateMessage(&msg); - _ = w.DispatchMessageW(&msg); - } + const size = getClientRect(core, window_id); + core_window.width = size.width; + core_window.height = size.height; + + _ = w.GetWindowRect(native.window, &native.saved_window_rect); } +// pub fn update(self: *Win32) !void { +// _ = self; +// var msg: w.MSG = undefined; +// while (w.PeekMessageW(&msg, null, 0, 0, w.PM_REMOVE) != 0) { +// _ = w.TranslateMessage(&msg); +// _ = w.DispatchMessageW(&msg); +// } +// } + pub fn setTitle(self: *Win32, title: [:0]const u8) void { const wtitle = std.unicode.utf8ToUtf16LeAllocZ(self.allocator, title) catch { self.state.oom.set(); @@ -268,21 +357,30 @@ pub fn nativeWindowWin32(self: *Win32) w.HWND { // ----------------------------- // Internal functions // ----------------------------- -fn getClientRect(self: *Win32) Size { - var rect: w.RECT = undefined; - _ = w.GetClientRect(self.window, &rect); +fn getClientRect(core: *Core, window_id: mach.ObjectID) Size { + const window = core.windows.getValue(window_id); + + if (window.native) |native| { + var rect: w.RECT = undefined; + _ = w.GetClientRect(native.window, &rect); - const width: u32 = @intCast(rect.right - rect.left); - const height: u32 = @intCast(rect.bottom - rect.top); + const width: u32 = @intCast(rect.right - rect.left); + const height: u32 = @intCast(rect.bottom - rect.top); - return .{ .width = width, .height = height }; + return .{ .width = width, .height = height }; + } + + return .{ .width = 0, .height = 0 }; } -fn restoreWindowPosition(self: *Win32) void { - if (self.saved_window_rect.right - self.saved_window_rect.left == 0) { - _ = w.ShowWindow(self.window, w.SW_RESTORE); - } else { - _ = w.SetWindowPos(self.window, null, self.saved_window_rect.left, self.saved_window_rect.top, self.saved_window_rect.right - self.saved_window_rect.left, self.saved_window_rect.bottom - self.saved_window_rect.top, w.SWP_SHOWWINDOW); +fn restoreWindowPosition(core: *Core, window_id: mach.ObjectID) void { + const window = core.windows.getValue(window_id); + if (window.native) |native| { + if (native.saved_window_rect.right - native.saved_window_rect.left == 0) { + _ = w.ShowWindow(native.window, w.SW_RESTORE); + } else { + _ = w.SetWindowPos(native.window, null, native.saved_window_rect.left, native.saved_window_rect.top, native.saved_window_rect.right - native.saved_window_rect.left, native.saved_window_rect.bottom - native.saved_window_rect.top, w.SWP_SHOWWINDOW); + } } } @@ -299,28 +397,38 @@ fn getKeyboardModifiers() mach.Core.KeyMods { } fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT { - const self = blk: { + const context = blk: { const userdata: usize = @bitCast(w.GetWindowLongPtrW(wnd, w.GWLP_USERDATA)); - const ptr: ?*Win32 = @ptrFromInt(userdata); + const ptr: ?*Context = @ptrFromInt(userdata); break :blk ptr orelse return w.DefWindowProcW(wnd, msg, wParam, lParam); }; + const core = context.core; + const window_id = context.window_id; + + const window = core.windows.getValue(window_id); + defer core.windows.setValueRaw(window_id, window); + switch (msg) { w.WM_CLOSE => { - self.core.pushEvent(.close); + core.pushEvent(.close); return 0; }, w.WM_SIZE => { const width: u32 = @as(u32, @intCast(lParam & 0xFFFF)); const height: u32 = @as(u32, @intCast((lParam >> 16) & 0xFFFF)); - self.size = .{ .width = width, .height = height }; + + window.width = width; + window.height = height; + + core.pushEvent(.{ .window_resize = .{ .window_id = window_id, .size = .{ .width = width, .height = height } } }); // TODO (win32): only send resize event when sizing is done. // the main mach loops does not run while resizing. // Which means if events are pushed here they will // queue up until resize is done. - self.core.swap_chain_update.set(); + window.swap_chain_update.set(); return 0; }, @@ -329,7 +437,7 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w if (vkey == w.VK_PROCESSKEY) return 0; if (msg == w.WM_SYSKEYDOWN and vkey == w.VK_F4) { - self.core.pushEvent(.close); + core.pushEvent(.close); return 0; } @@ -340,12 +448,14 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w // right alt sends left control first var next: w.MSG = undefined; const time = w.GetMessageTime(); - if (w.PeekMessageW(&next, self.window, 0, 0, w.PM_NOREMOVE) != 0 and - next.time == time and - (next.message == msg or (msg == w.WM_SYSKEYDOWN and next.message == w.WM_KEYUP)) and - ((next.lParam >> 16) & 0x1FF) == 0x138) - { - return 0; + if (window.native) |native| { + if (w.PeekMessageW(&next, native.window, 0, 0, w.PM_NOREMOVE) != 0 and + next.time == time and + (next.message == msg or (msg == w.WM_SYSKEYDOWN and next.message == w.WM_KEYUP)) and + ((next.lParam >> 16) & 0x1FF) == 0x138) + { + return 0; + } } } @@ -353,21 +463,24 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const key = keyFromScancode(scancode); if (msg == w.WM_KEYDOWN or msg == w.WM_SYSKEYDOWN) { if (flags & w.KF_REPEAT == 0) - self.core.pushEvent(.{ + core.pushEvent(.{ .key_press = .{ + .window_id = window_id, .key = key, .mods = mods, }, }) else - self.core.pushEvent(.{ + core.pushEvent(.{ .key_repeat = .{ + .window_id = window_id, .key = key, .mods = mods, }, }); - } else self.core.pushEvent(.{ + } else core.pushEvent(.{ .key_release = .{ + .window_id = window_id, .key = key, .mods = mods, }, @@ -376,24 +489,29 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w return 0; }, w.WM_CHAR => { - const char: u16 = @truncate(wParam); - var chars: []const u16 = undefined; - if (self.surrogate != 0) { - chars = &.{ self.surrogate, char }; - self.surrogate = 0; - } else if (std.unicode.utf16IsHighSurrogate(char)) { - self.surrogate = char; + if (window.native) |native| { + const char: u16 = @truncate(wParam); + var chars: []const u16 = undefined; + if (native.surrogate != 0) { + chars = &.{ native.surrogate, char }; + native.surrogate = 0; + } else if (std.unicode.utf16IsHighSurrogate(char)) { + native.surrogate = char; + return 0; + } else { + chars = &.{char}; + } + var iter = std.unicode.Utf16LeIterator.init(chars); + if (iter.nextCodepoint()) |codepoint| { + core.pushEvent(.{ .char_input = .{ + .window_id = window_id, + .codepoint = codepoint.?, + } }); + } else |err| { + err catch {}; + } return 0; - } else { - chars = &.{char}; - } - var iter = std.unicode.Utf16LeIterator.init(chars); - if (iter.nextCodepoint()) |codepoint| { - self.core.pushEvent(.{ .char_input = .{ .codepoint = codepoint.? } }); - } else |err| { - err catch {}; } - return 0; }, w.WM_LBUTTONDOWN, w.WM_LBUTTONUP, @@ -420,15 +538,17 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w w.WM_MBUTTONDOWN, w.WM_RBUTTONDOWN, w.WM_XBUTTONDOWN, - => self.core.pushEvent(.{ + => core.pushEvent(.{ .mouse_press = .{ + .window_id = window_id, .button = button, .mods = mods, .pos = .{ .x = x, .y = y }, }, }), - else => self.core.pushEvent(.{ + else => core.pushEvent(.{ .mouse_release = .{ + .window_id = window_id, .button = button, .mods = mods, .pos = .{ .x = x, .y = y }, @@ -441,8 +561,9 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w w.WM_MOUSEMOVE => { const x: f64 = @floatFromInt(@as(i16, @truncate(lParam & 0xFFFF))); const y: f64 = @floatFromInt(@as(i16, @truncate((lParam >> 16) & 0xFFFF))); - self.core.pushEvent(.{ + core.pushEvent(.{ .mouse_motion = .{ + .window_id = window_id, .pos = .{ .x = x, .y = y, @@ -456,8 +577,9 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w const wheel_high_word: u16 = @truncate((wParam >> 16) & 0xffff); const delta_y: f32 = @as(f32, @floatFromInt(@as(i16, @bitCast(wheel_high_word)))) / WHEEL_DELTA; - self.core.pushEvent(.{ + core.pushEvent(.{ .mouse_scroll = .{ + .window_id = window_id, .xoffset = 0, .yoffset = delta_y, }, @@ -465,11 +587,11 @@ fn wndProc(wnd: w.HWND, msg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w return 0; }, w.WM_SETFOCUS => { - self.core.pushEvent(.{ .focus_gained = {} }); + core.pushEvent(.{ .focus_gained = .{ .window_id = window_id } }); return 0; }, w.WM_KILLFOCUS => { - self.core.pushEvent(.{ .focus_lost = {} }); + core.pushEvent(.{ .focus_lost = .{ .window_id = window_id } }); return 0; }, else => return w.DefWindowProcW(wnd, msg, wParam, lParam), From ffed03bd2fe7b4855a8a3121de71bd571db1fc59 Mon Sep 17 00:00:00 2001 From: foxnne Date: Sat, 30 Nov 2024 15:32:08 -0600 Subject: [PATCH 7/7] core: comments, examples: fix `core-custom-entrypoint` --- examples/core-custom-entrypoint/App.zig | 16 ++++++++++------ src/Core.zig | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/core-custom-entrypoint/App.zig b/examples/core-custom-entrypoint/App.zig index 4de8495694..8784c3d057 100644 --- a/examples/core-custom-entrypoint/App.zig +++ b/examples/core-custom-entrypoint/App.zig @@ -28,8 +28,10 @@ pub fn init( core.on_tick = app_mod.id.tick; core.on_exit = app_mod.id.deinit; + const main_window = core.windows.getValue(core.main_window); + // Create our shader module - const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); + const shader_module = main_window.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); defer shader_module.release(); // Blend state describes how rendered colors get blended @@ -37,7 +39,7 @@ pub fn init( // Color target describes e.g. the pixel format of the window we are rendering to. const color_target = gpu.ColorTargetState{ - .format = core.windows.get(core.main_window, .framebuffer_format).?, + .format = main_window.framebuffer_format, .blend = &blend, }; @@ -58,7 +60,7 @@ pub fn init( .entry_point = "vertex_main", }, }; - const pipeline = core.device.createRenderPipeline(&pipeline_descriptor); + const pipeline = main_window.device.createRenderPipeline(&pipeline_descriptor); // Store our render pipeline in our module's state, so we can access it later on. app.* = .{ @@ -78,14 +80,16 @@ pub fn tick(core: *mach.Core, app: *App) !void { } } + const main_window = core.windows.getValue(core.main_window); + // Grab the back buffer of the swapchain // TODO(Core) - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; + const back_buffer_view = main_window.swap_chain.getCurrentTextureView().?; defer back_buffer_view.release(); // Create a command encoder const label = @tagName(mach_module) ++ ".tick"; - const encoder = core.device.createCommandEncoder(&.{ .label = label }); + const encoder = main_window.device.createCommandEncoder(&.{ .label = label }); defer encoder.release(); // Begin render pass @@ -112,7 +116,7 @@ pub fn tick(core: *mach.Core, app: *App) !void { // Submit our commands to the queue var command = encoder.finish(&.{ .label = label }); defer command.release(); - core.queue.submit(&[_]*gpu.CommandBuffer{command}); + main_window.queue.submit(&[_]*gpu.CommandBuffer{command}); // update the window title every second if (app.title_timer.read() >= 1.0) { diff --git a/src/Core.zig b/src/Core.zig index 7ccea9def8..05f5430a3f 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -46,9 +46,11 @@ windows: mach.Objects( framebuffer_format: gpu.Texture.Format = .bgra8_unorm, /// Width of the framebuffer in texels (read-only) + /// Will be updated to reflect the actual framebuffer dimensions after window creation. framebuffer_width: u32 = 1920 / 2, /// Height of the framebuffer in texels (read-only) + /// Will be updated to reflect the actual framebuffer dimensions after window creation. framebuffer_height: u32 = 1080 / 2, /// Vertical sync mode, prevents screen tearing.