Skip to documentation
SLOP

tiny.gui.paint.compositor

Reference tiny.gui paint compositor

Defined in paint.

API (17)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callspaintcompositor
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callerstiny.sdfiiEnginedeinitpaint.Compositordeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.AccyEngineinitVulkanpaint.CompositorinitCpupaint.Compositorinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callspaint.Compositorinittest sourcelib.gui.src.paint.compositortest: paint compositor CPU path match...test sourcelib.gui.src.paint.compositortest: paint compositor CPU region pre...test sourcelib.gui.src.paint.compositortest: paint compositor CPU status rep...paint.CompositorinitCpu
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.compositortest: paint compositor launches Accy ...test sourcelib.gui.src.paint.compositortest: paint compositor records render...private sourcelib.gui.src.paint.compositor.AccyEngineinitWithHandlepaint.CompositorinitWithHandle
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callspaint.Compositorstatuspaint.Compositorkind
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommands
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommandsPacked
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommandsPackedRegionWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommandsPackedWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommandsRegionWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.compositor.CompositornoteRenderFallbackpaint.CompositorrenderCommandsWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callerspaint.Compositorkindpaint.Compositorstatus
Static calls · unresolved targets: 0 · external targets: 1.

Source: lib/gui/src/paint/compositor.zig

zig
const std = @import("std");const gpu = @import("gpu");const command = @import("command.zig");const cpu = @import("cpu/root.zig");const executor = @import("executor.zig");const gui = @import("../root.zig");const paint_image = @import("image.zig");const Allocator = std.mem.Allocator;const BackendHandle = gpu.BackendHandle;const Color = gui.model.UiColor;const Command = command.Command;const Executor = executor.Executor;const ImageSet = command.ImageSet;const Region = cpu.Region;const Target = cpu.Target;const PackedTarget = cpu.PackedTarget;pub const CompositorOptions = struct {    prefer_accy: bool = true,    device_index: i32 = 0,    executor: executor.Options = .{},};pub const Kind = enum {    cpu,    accy,};pub const Fallback = struct {    init_error: ?anyerror = null,    render_error: ?anyerror = null,    render_fallbacks: usize = 0,};pub const Status = struct {    kind: Kind,    init_error: ?anyerror,    render_error: ?anyerror,    render_fallbacks: usize,    executor_storage: ?executor.StorageStatus,};pub const Compositor = struct {    engine: Engine = .cpu,    fallback: Fallback = .{},    pub fn init(allocator: Allocator, options: CompositorOptions) Compositor {        if (options.prefer_accy) {            if (AccyEngine.initVulkan(allocator, options.device_index, options.executor)) |engine| {                return .{ .engine = .{ .accy = engine } };            } else |err| {                return .{ .fallback = .{ .init_error = err } };            }        }        return initCpu();    }    pub fn initCpu() Compositor {        return .{};    }    pub fn initWithHandle(allocator: Allocator, handle: BackendHandle, options: executor.Options) !Compositor {        return .{ .engine = .{ .accy = try AccyEngine.initWithHandle(allocator, handle, options) } };    }    pub fn deinit(self: *Compositor) void {        switch (self.engine) {            .cpu => {},            .accy => |*engine| engine.deinit(),        }        self.* = .{};    }    pub fn kind(self: *const Compositor) Kind {        return switch (self.engine) {            .cpu => .cpu,            .accy => .accy,        };    }    pub fn status(self: *const Compositor) Status {        return .{            .kind = self.kind(),            .init_error = self.fallback.init_error,            .render_error = self.fallback.render_error,            .render_fallbacks = self.fallback.render_fallbacks,            .executor_storage = switch (self.engine) {                .cpu => null,                .accy => |*engine| engine.executor.storageStatus(),            },        };    }    fn noteRenderFallback(self: *Compositor, err: anyerror) void {        self.fallback.render_error = err;        self.fallback.render_fallbacks += 1;    }    pub fn renderCommands(self: *Compositor, commands: []const Command, target: Target, clear: Color) !void {        switch (self.engine) {            .cpu => try cpu.renderCommands(commands, target, clear),            .accy => |*engine| engine.executor.renderCommands(commands, target, clear) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommands(commands, target, clear);            },        }    }    pub fn renderCommandsWithImages(        self: *Compositor,        commands: []const Command,        target: Target,        clear: Color,        images: ImageSet,    ) !void {        switch (self.engine) {            .cpu => try cpu.renderCommandsWithImages(commands, target, clear, images),            .accy => |*engine| engine.executor.renderCommandsWithImages(commands, target, clear, images) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommandsWithImages(commands, target, clear, images);            },        }    }    pub fn renderCommandsRegionWithImages(        self: *Compositor,        commands: []const Command,        target: Target,        clear: Color,        images: ImageSet,        region: Region,    ) !void {        switch (self.engine) {            .cpu => try cpu.renderCommandsRegionWithImages(commands, target, clear, images, region),            .accy => |*engine| engine.executor.renderCommandsRegionWithImages(commands, target, clear, images, region) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommandsRegionWithImages(commands, target, clear, images, region);            },        }    }    pub fn renderCommandsPacked(self: *Compositor, commands: []const Command, target: PackedTarget, clear: Color) !void {        switch (self.engine) {            .cpu => try cpu.renderCommandsPacked(commands, target, clear),            .accy => |*engine| engine.executor.renderCommandsPacked(commands, target, clear) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommandsPacked(commands, target, clear);            },        }    }    pub fn renderCommandsPackedWithImages(        self: *Compositor,        commands: []const Command,        target: PackedTarget,        clear: Color,        images: ImageSet,    ) !void {        switch (self.engine) {            .cpu => try cpu.renderCommandsPackedWithImages(commands, target, clear, images),            .accy => |*engine| engine.executor.renderCommandsPackedWithImages(commands, target, clear, images) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommandsPackedWithImages(commands, target, clear, images);            },        }    }    pub fn renderCommandsPackedRegionWithImages(        self: *Compositor,        commands: []const Command,        target: PackedTarget,        clear: Color,        images: ImageSet,        region: Region,    ) !void {        switch (self.engine) {            .cpu => try cpu.renderCommandsPackedRegionWithImages(commands, target, clear, images, region),            .accy => |*engine| engine.executor.renderCommandsPackedRegionWithImages(commands, target, clear, images, region) catch |err| {                self.noteRenderFallback(err);                try cpu.renderCommandsPackedRegionWithImages(commands, target, clear, images, region);            },        }    }};const Engine = union(enum) {    cpu,    accy: AccyEngine,};const AccyEngine = struct {    allocator: Allocator,    executor: Executor,    vulkan_state: ?*gpu.vulkan.State = null,    fn initWithHandle(allocator: Allocator, handle: BackendHandle, options: executor.Options) !AccyEngine {        var paint_executor = try Executor.init(allocator, handle, options);        errdefer paint_executor.deinit();        return .{            .allocator = allocator,            .executor = paint_executor,        };    }    fn initVulkan(allocator: Allocator, device_index: i32, options: executor.Options) !AccyEngine {        const state = try allocator.create(gpu.vulkan.State);        errdefer allocator.destroy(state);        state.* = try gpu.vulkan.State.initDevice(allocator, device_index);        errdefer state.deinit();        var paint_executor = try Executor.init(allocator, state.handle(), options);        errdefer paint_executor.deinit();        return .{            .allocator = allocator,            .executor = paint_executor,            .vulkan_state = state,        };    }    fn deinit(self: *AccyEngine) void {        self.executor.deinit();        if (self.vulkan_state) |state| {            state.deinit();            self.allocator.destroy(state);        }        self.* = undefined;    }};test "paint compositor CPU path matches rgba8 renderer" {    const testing = std.testing;    var expected = @as([(8 * 4 * 4)]u8, @splat(0));    var actual = @as([(8 * 4 * 4)]u8, @splat(0));    const commands = [_]Command{        .{            .kind = .fill,            .rect = .{ .x = 1, .y = 1, .width = 4, .height = 2 },            .clip = .{ .x = 0, .y = 0, .width = 8, .height = 4 },            .color = .{ .r = 200, .g = 80, .b = 30, .a = 255 },        },    };    const clear = Color{ .r = 1, .g = 2, .b = 3, .a = 255 };    try cpu.renderCommandsWithImages(commands[0..], .{ .width = 8, .height = 4, .rgba8 = expected[0..] }, clear, .{});    var compositor = Compositor.initCpu();    defer compositor.deinit();    try compositor.renderCommandsWithImages(commands[0..], .{ .width = 8, .height = 4, .rgba8 = actual[0..] }, clear, .{});    try testing.expectEqualSlices(u8, expected[0..], actual[0..]);}test "paint compositor CPU region preserves packed pixels outside damage" {    const testing = std.testing;    var pixels = @as([16]u32, @splat(0xeeee_eeee));    const commands = [_]Command{        .{            .kind = .fill,            .rect = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .color = .{ .r = 10, .g = 20, .b = 30, .a = 255 },        },    };    var compositor = Compositor.initCpu();    defer compositor.deinit();    try compositor.renderCommandsPackedRegionWithImages(commands[0..], .{        .width = 4,        .height = 4,        .pixels = pixels[0..],    }, .{ .r = 1, .g = 2, .b = 3, .a = 255 }, .{}, .{        .x = 1,        .y = 1,        .width = 2,        .height = 2,    });    const painted = cpu.packRgba(.{ .r = 10, .g = 20, .b = 30, .a = 255 });    try testing.expectEqual(@as(u32, 0xeeee_eeee), pixels[0]);    try testing.expectEqual(painted, pixels[5]);    try testing.expectEqual(painted, pixels[6]);    try testing.expectEqual(@as(u32, 0xeeee_eeee), pixels[15]);}test "paint compositor CPU status reports no fallback" {    var compositor = Compositor.initCpu();    defer compositor.deinit();    const state = compositor.status();    try std.testing.expectEqual(Kind.cpu, state.kind);    try std.testing.expectEqual(@as(?anyerror, null), state.init_error);    try std.testing.expectEqual(@as(?anyerror, null), state.render_error);    try std.testing.expectEqual(@as(usize, 0), state.render_fallbacks);    try std.testing.expectEqual(@as(?executor.StorageStatus, null), state.executor_storage);}test "paint compositor records render fallback and still paints through CPU" {    const testing = std.testing;    var state = gpu.recording.BackendState{        .allocator = testing.allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var compositor = try Compositor.initWithHandle(testing.allocator, state.handle(), .{});    defer compositor.deinit();    state.fail_launch_after_count = state.launch_count;    var expected = @as([16]u32, @splat(0xeeee_eeee));    var actual = @as([16]u32, @splat(0xeeee_eeee));    const commands = [_]Command{        .{            .kind = .fill,            .rect = .{ .x = 1, .y = 1, .width = 2, .height = 2 },            .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .color = .{ .r = 10, .g = 20, .b = 30, .a = 255 },        },    };    const clear = Color{ .r = 1, .g = 2, .b = 3, .a = 255 };    try cpu.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = expected[0..] }, clear, .{});    try compositor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = actual[0..] }, clear, .{});    try testing.expectEqualSlices(u32, expected[0..], actual[0..]);    const after = compositor.status();    try testing.expectEqual(Kind.accy, after.kind);    try testing.expectEqual(@as(?anyerror, error.RuntimeUnavailable), after.render_error);    try testing.expectEqual(@as(usize, 1), after.render_fallbacks);    try compositor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = actual[0..] }, clear, .{});    try testing.expectEqual(@as(usize, 2), compositor.status().render_fallbacks);}test "paint compositor launches Accy executor through backend handle" {    const testing = std.testing;    var state = gpu.recording.BackendState{        .allocator = testing.allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const executor_limits = executor.Limits{        .mode = .device_csr,        .commands = 1,        .pixels = 4,        .tiles = 1,        .tile_pairs = 1,    };    const image_limits = paint_image.Limits{ .dst_pixels = 4, .src_pixels = 4 };    var compositor = try Compositor.initWithHandle(testing.allocator, state.handle(), .{        .initial_storage = executor_limits,        .image_storage = image_limits,    });    defer compositor.deinit();    const initial_status = compositor.status().executor_storage.?;    try testing.expectEqual(executor_limits, initial_status.limits.?);    try testing.expectEqual(image_limits, initial_status.image_processor.?.limits.?);    var pixels = @as([16]u32, @splat(0xffff_ffff));    const commands = [_]Command{        .{            .kind = .fill,            .rect = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .color = .{ .r = 255, .g = 0, .b = 0, .a = 255 },        },    };    try testing.expectEqual(Kind.accy, compositor.kind());    try compositor.renderCommandsPackedRegionWithImages(commands[0..], .{        .width = 4,        .height = 4,        .pixels = pixels[0..],    }, .{ .r = 0, .g = 0, .b = 0, .a = 255 }, .{}, .{        .x = 1,        .y = 1,        .width = 2,        .height = 2,    });    try testing.expectEqual(@as(usize, 7), state.launch_count);    try testing.expectEqual(@as(usize, 1), state.sync_count);    try testing.expectEqual(@as(usize, 1), state.read_count);    try testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[5]);    try testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[6]);    try testing.expectEqual(@as(u32, 2), state.last_launch_scalar_u32_values[7]);    try testing.expectEqual(@as(u32, 4), state.last_launch_scalar_u32_values[8]);    try testing.expectEqual(@as(u32, 0xffff_ffff), pixels[0]);    try testing.expectEqual(@as(u32, 0), pixels[5]);    try testing.expectEqual(@as(u32, 0), pixels[6]);    try testing.expectEqual(@as(u32, 0xffff_ffff), pixels[15]);}

Source: lib/gui/src/paint/root.zig:3

zig
pub const compositor = @import("compositor.zig");

Audit

Definitions18
Public names34
Members15
Version26.7.0
Revisiondaab053ee433