From bd655828e38973c05d943fff6e9a3a8d4c3866f5 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Tue, 7 May 2024 17:49:24 -0700 Subject: [PATCH] all: use new mach.Entity.Mod query API Signed-off-by: Stephen Gutekanst --- examples/custom-renderer/App.zig | 32 ++++++------ examples/custom-renderer/Renderer.zig | 17 +++---- examples/glyphs/App.zig | 22 ++++----- examples/piano/App.zig | 14 +++--- examples/play-opus/App.zig | 12 ++--- examples/sprite/App.zig | 22 +++------ examples/text/App.zig | 39 +++++++-------- src/Audio.zig | 22 ++++----- src/Core.zig | 37 ++++++-------- src/gfx/Sprite.zig | 62 +++++++++++------------- src/gfx/SpritePipeline.zig | 70 ++++++++++++--------------- src/gfx/Text.zig | 63 ++++++++++++------------ src/gfx/TextPipeline.zig | 66 +++++++++++-------------- 13 files changed, 213 insertions(+), 265 deletions(-) diff --git a/examples/custom-renderer/App.zig b/examples/custom-renderer/App.zig index 81b7f79e..426c6508 100644 --- a/examples/custom-renderer/App.zig +++ b/examples/custom-renderer/App.zig @@ -157,27 +157,29 @@ fn tick( // Query all the entities that have the .follower tag indicating they should follow the player. // TODO(important): better querying API - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .app = &.{.follower} }, - } }); - while (archetypes_iter.next()) |archetype| { - // Iterate the ID and position of each entity - const ids = archetype.slice(.entities, .id); - const positions = archetype.slice(.renderer, .position); - for (ids, positions) |id, position| { + + // Iterate the ID and position of each follower entity + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .followers = Mod.read(.follower), + .positions = Renderer.Mod.read(.position), + }); + while (q.next()) |v| { + for (v.ids, v.positions) |id, position| { // Nested query to find all the other follower entities that we should move away from. // We will avoid all other follower entities if we're too close to them. // This is not very efficient, but it works! const close_dist = 1.0 / 15.0; var avoidance = Vec3.splat(0); var avoidance_div: f32 = 1.0; - var archetypes_iter_2 = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .app = &.{.follower} }, - } }); - while (archetypes_iter_2.next()) |archetype_2| { - const other_ids = archetype_2.slice(.entities, .id); - const other_positions = archetype_2.slice(.renderer, .position); - for (other_ids, other_positions) |other_id, other_position| { + + var q2 = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .followers = Mod.read(.follower), + .positions = Renderer.Mod.read(.position), + }); + while (q2.next()) |v2| { + for (v2.ids, v2.positions) |other_id, other_position| { if (id == other_id) continue; if (position.dist(&other_position) < close_dist) { avoidance = avoidance.sub(&position.dir(&other_position, 0.0000001)); diff --git a/examples/custom-renderer/Renderer.zig b/examples/custom-renderer/Renderer.zig index bdfe6579..33fe81ee 100644 --- a/examples/custom-renderer/Renderer.zig +++ b/examples/custom-renderer/Renderer.zig @@ -124,6 +124,7 @@ fn deinit( } fn renderFrame( + entities: *mach.Entities.Mod, core: *mach.Core.Mod, renderer: *Mod, ) !void { @@ -138,17 +139,13 @@ fn renderFrame( defer encoder.release(); // Update uniform buffer - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .renderer = &.{ .position, .scale } }, - } }); var num_entities: usize = 0; - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const positions = archetype.slice(.renderer, .position); - const scales = archetype.slice(.renderer, .scale); - for (ids, positions, scales) |id, position, scale| { - _ = id; - + var q = try entities.query(.{ + .positions = Mod.read(.position), + .scales = Mod.read(.scale), + }); + while (q.next()) |v| { + for (v.positions, v.scales) |position, scale| { const ubo = UniformBufferObject{ .offset = position, .scale = scale, diff --git a/examples/glyphs/App.zig b/examples/glyphs/App.zig index b6459ed2..2c4df07c 100644 --- a/examples/glyphs/App.zig +++ b/examples/glyphs/App.zig @@ -165,18 +165,17 @@ fn tick( const delta_time = game.state().timer.lap(); // Animate entities - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite = &.{.transform} }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const transforms = archetype.slice(.mach_gfx_sprite, .transform); - for (ids, transforms) |id, *old_transform| { - var location = old_transform.translation(); + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .transforms = gfx.Sprite.Mod.write(.transform), + }); + while (q.next()) |v| { + for (v.ids, v.transforms) |id, *entity_transform| { + var location = entity_transform.translation(); // TODO: formatting // TODO(Core) if (location.x() < -@as(f32, @floatFromInt(mach.core.size().width)) / 1.5 or location.x() > @as(f32, @floatFromInt(mach.core.size().width)) / 1.5 or location.y() < -@as(f32, @floatFromInt(mach.core.size().height)) / 1.5 or location.y() > @as(f32, @floatFromInt(mach.core.size().height)) / 1.5) { - try core.__entities.remove(id); + try entities.remove(id); game.state().sprites -= 1; continue; } @@ -186,10 +185,7 @@ fn tick( transform = transform.mul(&Mat4x4.translate(location)); transform = transform.mul(&Mat4x4.rotateZ(2 * math.pi * game.state().time)); transform = transform.mul(&Mat4x4.scale(Vec3.splat(@max(math.cos(game.state().time / 2.0), 0.2)))); - - // TODO: .set() API is substantially slower due to internals - // try sprite.set(id, .transform, transform); - old_transform.* = transform; + entity_transform.* = transform; } } diff --git a/examples/piano/App.zig b/examples/piano/App.zig index d4a9649e..a00a60c8 100644 --- a/examples/piano/App.zig +++ b/examples/piano/App.zig @@ -70,14 +70,12 @@ fn audioStateChange( app: *Mod, ) !void { // Find audio entities that are no longer playing - var archetypes_iter = audio.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_audio = &.{.playing} }, - } }); - while (archetypes_iter.next()) |archetype| { - for ( - archetype.slice(.entities, .id), - archetype.slice(.mach_audio, .playing), - ) |id, playing| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .playings = mach.Audio.Mod.read(.playing), + }); + while (q.next()) |v| { + for (v.ids, v.playings) |id, playing| { if (playing) continue; if (app.get(id, .play_after)) |frequency| { diff --git a/examples/play-opus/App.zig b/examples/play-opus/App.zig index 07756ff9..ec699702 100644 --- a/examples/play-opus/App.zig +++ b/examples/play-opus/App.zig @@ -85,12 +85,12 @@ fn audioStateChange( app: *Mod, ) !void { // Find audio entities that are no longer playing - var archetypes_iter = audio.__entities.queryDeprecated(.{ .all = &.{.{ .mach_audio = &.{.playing} }} }); - while (archetypes_iter.next()) |archetype| { - for ( - archetype.slice(.entities, .id), - archetype.slice(.mach_audio, .playing), - ) |id, playing| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .playings = mach.Audio.Mod.read(.playing), + }); + while (q.next()) |v| { + for (v.ids, v.playings) |id, playing| { if (playing) continue; if (app.get(id, .is_bgm)) |_| { diff --git a/examples/sprite/App.zig b/examples/sprite/App.zig index 74713052..61571373 100644 --- a/examples/sprite/App.zig +++ b/examples/sprite/App.zig @@ -158,26 +158,20 @@ fn tick( const delta_time = game.state().timer.lap(); // Rotate entities - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite = &.{.transform} }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const transforms = archetype.slice(.mach_gfx_sprite, .transform); - for (ids, transforms) |id, *old_transform| { - _ = id; - const location = old_transform.*.translation(); - // var transform = old_transform.mul(&Mat4x4.translate(-location)); + var q = try entities.query(.{ + .transforms = gfx.Sprite.Mod.write(.transform), + }); + while (q.next()) |v| { + for (v.transforms) |*entity_transform| { + const location = entity_transform.*.translation(); + // var transform = entity_transform.mul(&Mat4x4.translate(-location)); // transform = mat.rotateZ(0.3 * delta_time).mul(&transform); // transform = transform.mul(&Mat4x4.translate(location)); var transform = Mat4x4.ident; transform = transform.mul(&Mat4x4.translate(location)); transform = transform.mul(&Mat4x4.rotateZ(2 * math.pi * game.state().time)); transform = transform.mul(&Mat4x4.scaleScalar(@min(math.cos(game.state().time / 2.0), 0.5))); - - // TODO: .set() API is substantially slower due to internals - // try sprite.set(id, .transform, transform); - old_transform.* = transform; + entity_transform.* = transform; } } diff --git a/examples/text/App.zig b/examples/text/App.zig index 25c1e22c..626259ba 100644 --- a/examples/text/App.zig +++ b/examples/text/App.zig @@ -201,15 +201,12 @@ fn tick( const delta_time = game.state().timer.lap(); // Rotate entities - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text = &.{.transform} }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const transforms = archetype.slice(.mach_gfx_text, .transform); - for (ids, transforms) |id, *old_transform| { - _ = id; - const location = old_transform.*.translation(); + var q = try entities.query(.{ + .transforms = gfx.Text.Mod.write(.transform), + }); + while (q.next()) |v| { + for (v.transforms) |*entity_transform| { + const location = entity_transform.*.translation(); // var transform = old_transform.mul(&Mat4x4.translate(-location)); // transform = mat.rotateZ(0.3 * delta_time).mul(&transform); // transform = transform.mul(&Mat4x4.translate(location)); @@ -217,10 +214,7 @@ fn tick( transform = transform.mul(&Mat4x4.translate(location)); transform = transform.mul(&Mat4x4.rotateZ(2 * math.pi * game.state().time)); transform = transform.mul(&Mat4x4.scaleScalar(@min(math.cos(game.state().time / 2.0), 0.5))); - - // TODO: .set() API is substantially slower due to internals - // try text.set(id, .transform, transform); - old_transform.* = transform; + entity_transform.* = transform; } } @@ -268,7 +262,11 @@ fn tick( game.state().time += delta_time; } -fn endFrame(game: *Mod, text: *gfx.Text.Mod, core: *mach.Core.Mod) !void { +fn endFrame( + entities: *mach.Entities.Mod, + game: *Mod, + core: *mach.Core.Mod, +) !void { // Finish render pass game.state().frame_render_pass.end(); const label = @tagName(name) ++ ".tick"; @@ -285,14 +283,11 @@ fn endFrame(game: *Mod, text: *gfx.Text.Mod, core: *mach.Core.Mod) !void { // Gather some text rendering stats var num_texts: u32 = 0; var num_glyphs: usize = 0; - var archetypes_iter = text.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const builts = archetype.slice(.mach_gfx_text, .built); - for (builts) |built| { + var q = try entities.query(.{ + .built_pipelines = gfx.Text.Mod.read(.built), + }); + while (q.next()) |v| { + for (v.built_pipelines) |built| { num_texts += 1; num_glyphs += built.glyphs.items.len; } diff --git a/src/Audio.zig b/src/Audio.zig index 1e114364..8cc46825 100644 --- a/src/Audio.zig +++ b/src/Audio.zig @@ -97,7 +97,7 @@ fn deinit(audio: *Mod) void { /// instead. At the same time, we don't want to play too far ahead as that would cause latency /// between e.g. user interactions and audio actually playing - so in practice the amount we play /// ahead is rather small and imperceivable to most humans. -fn audioTick(audio: *Mod) !void { +fn audioTick(entities: *mach.Entities.Mod, audio: *Mod) !void { const allocator = audio.state().allocator; const player = &audio.state().player; const player_channels: u8 = @intCast(player.channels().len); @@ -134,17 +134,15 @@ fn audioTick(audio: *Mod) !void { @memset(mixing_buffer.items, 0); var did_state_change = false; - var archetypes_iter = audio.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_audio = &.{ .samples, .channels, .playing, .index } }, - } }); - while (archetypes_iter.next()) |archetype| { - for ( - archetype.slice(.entities, .id), - archetype.slice(.mach_audio, .samples), - archetype.slice(.mach_audio, .channels), - archetype.slice(.mach_audio, .playing), - archetype.slice(.mach_audio, .index), - ) |id, samples, channels, playing, index| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .samples_slices = Mod.read(.samples), + .channels = Mod.read(.channels), + .playings = Mod.read(.playing), + .indexes = Mod.read(.index), + }); + while (q.next()) |v| { + for (v.ids, v.samples_slices, v.channels, v.playings, v.indexes) |id, samples, channels, playing, index| { if (!playing) continue; const channels_diff = player_channels - channels + 1; diff --git a/src/Core.zig b/src/Core.zig index d0c6c993..228ed902 100644 --- a/src/Core.zig +++ b/src/Core.zig @@ -131,21 +131,16 @@ fn init(entities: *mach.Entities.Mod, core: *Mod) !void { mach.core.mods.send(.app, .init, .{}); } -fn update(core: *Mod) !void { - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_core = &.{ - .title, - } }, - } }); - +fn update(entities: *mach.Entities.Mod) !void { var num_windows: usize = 0; - while (archetypes_iter.next()) |archetype| { - for ( - archetype.slice(.entities, .id), - archetype.slice(.mach_core, .title), - ) |window_id, title| { - num_windows += 1; + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .titles = Mod.read(.title), + }); + while (q.next()) |v| { + for (v.ids, v.titles) |window_id, title| { _ = window_id; + num_windows += 1; try mach.core.printTitle("{s}", .{title}); } } @@ -169,17 +164,17 @@ fn presentFrame(core: *Mod) !void { } } -fn deinit(core: *Mod) void { +fn deinit(entities: *mach.Entities.Mod, core: *Mod) !void { core.state().queue.release(); mach.core.deinit(); - var archetypes_iter = core.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_core = &.{ - .title, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - for (archetype.slice(.mach_core, .title)) |title| core.state().allocator.free(title); + var q = try entities.query(.{ + .titles = Mod.read(.title), + }); + while (q.next()) |v| { + for (v.titles) |title| { + core.state().allocator.free(title); + } } _ = gpa.deinit(); diff --git a/src/gfx/Sprite.zig b/src/gfx/Sprite.zig index 9aeeef90..af303e85 100644 --- a/src/gfx/Sprite.zig +++ b/src/gfx/Sprite.zig @@ -42,56 +42,50 @@ pub const events = .{ .update = .{ .handler = update }, }; -fn update(core: *mach.Core.Mod, sprite: *Mod, sprite_pipeline: *gfx.SpritePipeline.Mod) !void { - var archetypes_iter = sprite_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const built_pipelines = archetype.slice(.mach_gfx_sprite_pipeline, .built); - for (ids, built_pipelines) |pipeline_id, *built| { - try updatePipeline(core, sprite, sprite_pipeline, pipeline_id, built); +fn update( + entities: *mach.Entities.Mod, + core: *mach.Core.Mod, + sprite_pipeline: *gfx.SpritePipeline.Mod, +) !void { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .built_pipelines = gfx.SpritePipeline.Mod.read(.built), + }); + while (q.next()) |v| { + for (v.ids, v.built_pipelines) |pipeline_id, built| { + try updatePipeline(entities, core, sprite_pipeline, pipeline_id, &built); } } } fn updatePipeline( + entities: *mach.Entities.Mod, core: *mach.Core.Mod, - sprite: *Mod, sprite_pipeline: *gfx.SpritePipeline.Mod, pipeline_id: mach.EntityID, - built: *gfx.SpritePipeline.BuiltPipeline, + built: *const gfx.SpritePipeline.BuiltPipeline, ) !void { const device = core.state().device; const label = @tagName(name) ++ ".updatePipeline"; const encoder = device.createCommandEncoder(&.{ .label = label }); defer encoder.release(); - var archetypes_iter = sprite.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite = &.{ - .uv_transform, - .transform, - .size, - .pipeline, - } }, - } }); var num_sprites: u32 = 0; var i: usize = 0; - while (archetypes_iter.next()) |archetype| { - const transforms = archetype.slice(.mach_gfx_sprite, .transform); - const uv_transforms = archetype.slice(.mach_gfx_sprite, .uv_transform); - const sizes = archetype.slice(.mach_gfx_sprite, .size); - const pipelines = archetype.slice(.mach_gfx_sprite, .pipeline); - - // TODO: currently we cannot query all sprites which have a _single_ pipeline component - // value and get back contiguous memory for all of them. This is because all sprites with - // possibly different pipeline component values are stored as the same archetype. If we - // introduce a new concept of tagging-by-value to our entity storage then we can enforce - // that all entities with the same pipeline value are stored in contiguous memory, and - // skip this copy. - for (transforms, uv_transforms, sizes, pipelines) |transform, uv_transform, size, sprite_pipeline_id| { + var q = try entities.query(.{ + .transforms = Mod.read(.transform), + .uv_transforms = Mod.read(.uv_transform), + .sizes = Mod.read(.size), + .pipelines = Mod.read(.pipeline), + }); + while (q.next()) |v| { + for (v.transforms, v.uv_transforms, v.sizes, v.pipelines) |transform, uv_transform, size, sprite_pipeline_id| { + // TODO: currently we cannot query all sprites which have a _single_ pipeline component + // value and get back contiguous memory for all of them. This is because all sprites with + // possibly different pipeline component values are stored as the same archetype. If we + // introduce a new concept of tagging-by-value to our entity storage then we can enforce + // that all entities with the same pipeline value are stored in contiguous memory, and + // skip this copy. if (sprite_pipeline_id == pipeline_id) { gfx.SpritePipeline.cp_transforms[i] = transform; gfx.SpritePipeline.cp_uv_transforms[i] = uv_transform; diff --git a/src/gfx/SpritePipeline.zig b/src/gfx/SpritePipeline.zig index 0a762d22..ade4482b 100644 --- a/src/gfx/SpritePipeline.zig +++ b/src/gfx/SpritePipeline.zig @@ -104,7 +104,7 @@ pub const BuiltPipeline = struct { uv_transforms: *gpu.Buffer, sizes: *gpu.Buffer, - pub fn deinit(p: *BuiltPipeline) void { + pub fn deinit(p: *const BuiltPipeline) void { p.render.release(); p.texture_sampler.release(); p.texture.release(); @@ -123,31 +123,27 @@ fn init(sprite_pipeline: *Mod) void { sprite_pipeline.init(.{}); } -fn deinit(sprite_pipeline: *Mod) void { - var archetypes_iter = sprite_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - for (archetype.slice(.mach_gfx_sprite_pipeline, .built)) |*p| p.deinit(); +fn deinit(entities: *mach.Entities.Mod) !void { + var q = try entities.query(.{ + .built_pipelines = Mod.read(.built), + }); + while (q.next()) |v| { + for (v.built_pipelines) |built| { + built.deinit(); + } } } -fn update(core: *mach.Core.Mod, sprite_pipeline: *Mod) !void { +fn update(entities: *mach.Entities.Mod, core: *mach.Core.Mod, sprite_pipeline: *Mod) !void { // Destroy all sprite render pipelines. We will rebuild them all. - deinit(sprite_pipeline); + try deinit(entities); - var archetypes_iter = sprite_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite_pipeline = &.{ - .texture, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const textures = archetype.slice(.mach_gfx_sprite_pipeline, .texture); - - for (ids, textures) |pipeline_id, texture| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .textures = Mod.read(.texture), + }); + while (q.next()) |v| { + for (v.ids, v.textures) |pipeline_id, texture| { try buildPipeline(core, sprite_pipeline, pipeline_id, texture); } } @@ -319,19 +315,16 @@ fn buildPipeline( try sprite_pipeline.set(pipeline_id, .num_sprites, 0); } -fn preRender(sprite_pipeline: *Mod, core: *mach.Core.Mod) void { +fn preRender(entities: *mach.Entities.Mod, core: *mach.Core.Mod) !void { const label = @tagName(name) ++ ".preRender"; const encoder = core.state().device.createCommandEncoder(&.{ .label = label }); defer encoder.release(); - var archetypes_iter = sprite_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const built_pipelines = archetype.slice(.mach_gfx_sprite_pipeline, .built); - for (built_pipelines) |built| { + var q = try entities.query(.{ + .built_pipelines = Mod.read(.built), + }); + while (q.next()) |v| { + for (v.built_pipelines) |built| { // Create the projection matrix // TODO(sprite): move this out of the hot codepath const proj = math.Mat4x4.projection2D(.{ @@ -362,20 +355,17 @@ fn preRender(sprite_pipeline: *Mod, core: *mach.Core.Mod) void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); } -fn render(sprite_pipeline: *Mod) !void { +fn render(entities: *mach.Entities.Mod, sprite_pipeline: *Mod) !void { const render_pass = if (sprite_pipeline.state().render_pass) |rp| rp else std.debug.panic("sprite_pipeline.state().render_pass must be specified", .{}); sprite_pipeline.state().render_pass = null; // TODO(sprite): need a way to specify order of rendering with multiple pipelines - var archetypes_iter = sprite_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_sprite_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const built_pipelines = archetype.slice(.mach_gfx_sprite_pipeline, .built); - for (ids, built_pipelines) |pipeline_id, built| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .built_pipelines = Mod.read(.built), + }); + while (q.next()) |v| { + for (v.ids, v.built_pipelines) |pipeline_id, built| { // Draw the sprite batch const total_vertices = sprite_pipeline.get(pipeline_id, .num_sprites).? * 6; render_pass.setPipeline(built.render); diff --git a/src/gfx/Text.zig b/src/gfx/Text.zig index 5b0dc9b2..1f7978aa 100644 --- a/src/gfx/Text.zig +++ b/src/gfx/Text.zig @@ -58,24 +58,27 @@ const BuiltText = struct { glyphs: std.ArrayListUnmanaged(gfx.TextPipeline.Glyph), }; -fn update(core: *mach.Core.Mod, text: *Mod, text_pipeline: *gfx.TextPipeline.Mod) !void { - var archetypes_iter = text_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const built_pipelines = archetype.slice(.mach_gfx_text_pipeline, .built); - for (ids, built_pipelines) |pipeline_id, *built| { - try updatePipeline(core, text, text_pipeline, pipeline_id, built); +fn update( + entities: *mach.Entities.Mod, + text: *Mod, + core: *mach.Core.Mod, + text_pipeline: *gfx.TextPipeline.Mod, +) !void { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .built_pipelines = gfx.TextPipeline.Mod.write(.built), + }); + while (q.next()) |v| { + for (v.ids, v.built_pipelines) |pipeline_id, *built| { + try updatePipeline(entities, text, core, text_pipeline, pipeline_id, built); } } } fn updatePipeline( - core: *mach.Core.Mod, + entities: *mach.Entities.Mod, text: *Mod, + core: *mach.Core.Mod, text_pipeline: *gfx.TextPipeline.Mod, pipeline_id: mach.EntityID, built: *gfx.TextPipeline.BuiltPipeline, @@ -97,28 +100,22 @@ fn updatePipeline( var texture_update = false; var num_texts: u32 = 0; var removes = try std.ArrayListUnmanaged(mach.EntityID).initCapacity(allocator, 8); - var archetypes_iter = text.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text = &.{ - .transform, - .text, - .style, - .pipeline, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const transforms = archetype.slice(.mach_gfx_text, .transform); - const segment_slices = archetype.slice(.mach_gfx_text, .text); - const style_slices = archetype.slice(.mach_gfx_text, .style); - const pipelines = archetype.slice(.mach_gfx_text, .pipeline); - // TODO: currently we cannot query all texts which have a _single_ pipeline component - // value and get back contiguous memory for all of them. This is because all texts with - // possibly different pipeline component values are stored as the same archetype. If we - // introduce a new concept of tagging-by-value to our entity storage then we can enforce - // that all entities with the same pipeline value are stored in contiguous memory, and - // skip this copy. - for (ids, transforms, segment_slices, style_slices, pipelines) |id, transform, segments, styles, text_pipeline_id| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .transforms = Mod.read(.transform), + .segment_slices = Mod.read(.text), + .style_slices = Mod.read(.style), + .pipelines = Mod.read(.pipeline), + }); + while (q.next()) |v| { + for (v.ids, v.transforms, v.segment_slices, v.style_slices, v.pipelines) |id, transform, segments, styles, text_pipeline_id| { + // TODO: currently we cannot query all texts which have a _single_ pipeline component + // value and get back contiguous memory for all of them. This is because all texts with + // possibly different pipeline component values are stored as the same archetype. If we + // introduce a new concept of tagging-by-value to our entity storage then we can enforce + // that all entities with the same pipeline value are stored in contiguous memory, and + // skip this copy. if (text_pipeline_id != pipeline_id) continue; gfx.TextPipeline.cp_transforms[num_texts] = transform; diff --git a/src/gfx/TextPipeline.zig b/src/gfx/TextPipeline.zig index d0c086d9..54124713 100644 --- a/src/gfx/TextPipeline.zig +++ b/src/gfx/TextPipeline.zig @@ -144,33 +144,31 @@ pub const BuiltPipeline = struct { } }; -fn deinit(text_pipeline: *Mod) void { - var archetypes_iter = text_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - for (archetype.slice(.mach_gfx_text_pipeline, .built)) |*p| p.deinit(text_pipeline.state().allocator); +fn deinit(entities: *mach.Entities.Mod, text_pipeline: *Mod) !void { + var q = try entities.query(.{ + .built_pipelines = Mod.write(.built), + }); + while (q.next()) |v| { + for (v.built_pipelines) |*built| { + built.deinit(text_pipeline.state().allocator); + } } } -fn update(core: *mach.Core.Mod, text_pipeline: *Mod) !void { +fn update(entities: *mach.Entities.Mod, core: *mach.Core.Mod, text_pipeline: *Mod) !void { text_pipeline.init(.{ .allocator = gpa.allocator(), }); // Destroy all text render pipelines. We will rebuild them all. - deinit(text_pipeline); + try deinit(entities, text_pipeline); - var archetypes_iter = text_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text_pipeline = &.{ - .is_pipeline, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - for (ids) |pipeline_id| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .is_pipelines = Mod.read(.is_pipeline), + }); + while (q.next()) |v| { + for (v.ids) |pipeline_id| { try buildPipeline(core, text_pipeline, pipeline_id); } } @@ -348,19 +346,16 @@ fn buildPipeline( try text_pipeline.set(pipeline_id, .num_glyphs, 0); } -fn preRender(text_pipeline: *Mod, core: *mach.Core.Mod) void { +fn preRender(entities: *mach.Entities.Mod, core: *mach.Core.Mod) !void { const label = @tagName(name) ++ ".preRender"; const encoder = core.state().device.createCommandEncoder(&.{ .label = label }); defer encoder.release(); - var archetypes_iter = text_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const built_pipelines = archetype.slice(.mach_gfx_text_pipeline, .built); - for (built_pipelines) |built| { + var q = try entities.query(.{ + .built_pipelines = Mod.read(.built), + }); + while (q.next()) |v| { + for (v.built_pipelines) |built| { // Create the projection matrix // TODO(text): move this out of the hot codepath const proj = math.Mat4x4.projection2D(.{ @@ -391,20 +386,17 @@ fn preRender(text_pipeline: *Mod, core: *mach.Core.Mod) void { core.state().queue.submit(&[_]*gpu.CommandBuffer{command}); } -fn render(text_pipeline: *Mod) !void { +fn render(entities: *mach.Entities.Mod, text_pipeline: *Mod) !void { const render_pass = if (text_pipeline.state().render_pass) |rp| rp else std.debug.panic("text_pipeline.state().render_pass must be specified", .{}); text_pipeline.state().render_pass = null; // TODO(text): need a way to specify order of rendering with multiple pipelines - var archetypes_iter = text_pipeline.__entities.queryDeprecated(.{ .all = &.{ - .{ .mach_gfx_text_pipeline = &.{ - .built, - } }, - } }); - while (archetypes_iter.next()) |archetype| { - const ids = archetype.slice(.entities, .id); - const built_pipelines = archetype.slice(.mach_gfx_text_pipeline, .built); - for (ids, built_pipelines) |pipeline_id, built| { + var q = try entities.query(.{ + .ids = mach.Entities.Mod.read(.id), + .built_pipelines = Mod.read(.built), + }); + while (q.next()) |v| { + for (v.ids, v.built_pipelines) |pipeline_id, built| { // Draw the text batch const total_vertices = text_pipeline.get(pipeline_id, .num_glyphs).? * 6; render_pass.setPipeline(built.render);