Skip to documentation
SLOP

tiny.gui.paint.executor

Reference tiny.gui paint executor

Defined in paint.

API (49)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallstest sourcelib.gui.src.paint.executortest: paint Executor admitted prepare...test sourcelib.gui.src.paint.executortest: paint Executor eagerly acquires...test sourcelib.gui.src.paint.executortest: paint Executor failed storage r...test sourcelib.gui.src.paint.executortest: paint Executor initial storage ...test sourcelib.gui.src.paint.executortest: paint Executor replaces storage...+30 moreprivate sourcelib.gui.src.paint.executor.ExecutorreleaseBufferspaint.Executordeinit
Static calls · unresolved targets: 0 · external targets: 5.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.executor.ExecutorimageProcessorpaint.ExecutorexpandImageShadowsAlloc
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.gui.src.paint.executortest: paint Executor admitted prepare...test sourcelib.gui.src.paint.executortest: paint Executor eagerly acquires...test sourcelib.gui.src.paint.executortest: paint Executor failed storage r...test sourcelib.gui.src.paint.executortest: paint Executor initial storage ...test sourcelib.gui.src.paint.executortest: paint Executor rejects initial ...+31 moreprivate sourcelib.gui.src.paint.executor.Buffersinitprivate sourcelib.gui.src.paint.executordefaultFormatprivate sourcelib.gui.src.paint.executorloadDeviceScanKernelsprivate sourcelib.gui.src.paint.executorloadGraphKernelpaint.executorstorageModeForArtifactFormattiny.smggraphdeinitpaint.Executorinit
Static calls · unresolved targets: 0 · external targets: 9.
Called byCallspaint.ExecutorprepareCommandsPackedLaunchWithImageS...private sourcelib.gui.src.paint.executor.ExecutorrenderPackedtest sourcelib.gui.src.paint.executortest: paint Executor admitted prepare...test sourcelib.gui.src.paint.executortest: paint Executor failed storage r...test sourcelib.gui.src.paint.executortest: paint Executor replaces storage...+11 moreprivate sourcelib.gui.src.paint.executor.ExecutorprepareCommandsFormattedLaunchpaint.ExecutorprepareCommandsPackedLaunch
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor prepares image-s...private sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowspaint.ExecutorprepareCommandsPackedLaunchpaint.ExecutorprepareCommandsPackedLaunchWithImageS...
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor writes bgra surf...test sourcelib.gui.src.paint.executortest: paint executor writes prepared ...test sourcelib.gui.src.paint.executortest: paint executor writes smaller s...private sourcelib.gui.src.paint.executor.ExecutorprepareCommandsFormattedLaunchprivate sourcelib.gui.src.paint.executorsurfacePaintTargetpaint.ExecutorprepareCommandsSurfaceLaunch
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandsSurfaceFrameWithImageSh...test sourcelib.gui.src.paint.executortest: paint executor prepares image-s...private sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowsprivate sourcelib.gui.src.paint.executor.ExecutorprepareCommandsFormattedLaunchprivate sourcelib.gui.src.paint.executorsurfacePaintTargetpaint.ExecutorprepareCommandsSurfaceLaunchWithImage...
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor describes prepar...test sourcelib.gui.src.paint.executortest: paint executor preserves prepar...test sourcelib.gui.src.paint.executortest: paint executor preserves prepar...test sourcelib.gui.src.paint.executortest: paint executor rejects prepared...test sourcelib.gui.src.paint.executortest: paint executor rejects stale pr...private sourcelib.gui.src.paint.executor.ExecutorvalidatePreparedLaunchprivate sourcelib.gui.src.paint.executorartifactPayloadBytespaint.ExecutorpreparedLaunchInfo
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedtest sourcelib.gui.src.paint.executortest: paint executor preserves prepar...test sourcelib.gui.src.paint.executortest: paint executor reads prepared p...test sourcelib.gui.src.paint.executortest: paint executor rejects prepared...test sourcelib.gui.src.paint.executortest: paint executor rejects stale pr...test sourcelib.gui.src.paint.executortest: paint executor reuses resident ...private sourcelib.gui.src.paint.executor.ExecutorvalidatePreparedLaunchpaint.ExecutorreadPreparedPackedPixels
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor converts readbac...paint.ExecutorrenderCommandsWithImagespaint.ExecutorrenderCommands
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor grows target buf...test sourcelib.gui.src.paint.executortest: paint executor launches packed ...test sourcelib.gui.src.paint.executortest: paint executor reads full reuse...test sourcelib.gui.src.paint.executortest: paint executor renders packed c...test sourcelib.gui.src.paint.executortest: paint executor reuses device CS...paint.ExecutorrenderCommandsPackedWithImagespaint.ExecutorrenderCommandsPacked
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowspaint.ExecutorrenderCommandsPackedRegionWithImagespaint.ExecutorrenderCommandsPackedRegionWithImageSh...
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandsPackedRegionWithImageSh...test sourcelib.gui.src.paint.executortest: paint executor launches packed ...tiny.accychoir.record.targetvalidateprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedprivate sourcelib.gui.src.paint.executorcopyPackedRegionprivate sourcelib.gui.src.paint.executorpixelCountpaint.ExecutorrenderCommandsPackedRegionWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor renders packed c...test sourcelib.gui.src.paint.executortest: paint executor reuses image-sha...test sourcelib.gui.src.paint.executortest: paint executor reuses image-sha...private sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowspaint.ExecutorrenderCommandsPackedWithImagespaint.ExecutorrenderCommandsPackedWithImageShadows
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandsPackedpaint.ExecutorrenderCommandsPackedWithImageShadowstest sourcelib.gui.src.paint.executortest: paint executor renders packed c...test sourcelib.gui.src.paint.executortest: paint executor uploads image me...tiny.accychoir.record.targetvalidateprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedprivate sourcelib.gui.src.paint.executorpixelCountpaint.ExecutorrenderCommandsPackedWithImages
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowspaint.ExecutorrenderCommandsRegionWithImagespaint.ExecutorrenderCommandsRegionWithImageShadows
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandsRegionWithImageShadowstiny.accychoir.record.targetvalidateprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedprivate sourcelib.gui.src.paint.executorcopyRgba8Regionprivate sourcelib.gui.src.paint.executorpixelCountpaint.ExecutorrenderCommandsRegionWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callerspaint.ExecutorrenderCommandsSurfaceFrameWithImagespaint.ExecutorrenderCommandsSurfaceFrame
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor writes image sha...paint.ExecutorprepareCommandsSurfaceLaunchWithImage...paint.ExecutorsubmitPreparedLaunchQueuedpaint.ExecutorwritePreparedSurfaceFramepaint.ExecutorrenderCommandsSurfaceFrameWithImageSh...
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallspaint.ExecutorrenderCommandsSurfaceFrametest sourcelib.gui.src.paint.executortest: paint executor writes packed co...private sourcelib.gui.src.paint.executor.ExecutorprepareCommandsFormattedLaunchpaint.ExecutorsubmitPreparedLaunchQueuedpaint.ExecutorwritePreparedSurfaceFrameprivate sourcelib.gui.src.paint.executorsurfaceFramePaintTargetpaint.ExecutorrenderCommandsSurfaceFrameWithImages
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.gui.src.paint.executor.ExecutorcachedImageShadowspaint.ExecutorrenderCommandsWithImagespaint.ExecutorrenderCommandsWithImageShadows
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandspaint.ExecutorrenderCommandsWithImageShadowstiny.accychoir.record.targetvalidateprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedprivate sourcelib.gui.src.paint.executorpixelCountpaint.ExecutorrenderCommandsWithImages
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callstest sourcelib.gui.src.paint.executortest: paint Executor eagerly acquires...test sourcelib.gui.src.paint.executortest: paint Executor failed storage r...test sourcelib.gui.src.paint.executortest: paint Executor initial storage ...test sourcelib.gui.src.paint.executortest: paint Executor replaces storage...paint.ExecutorstorageStatus
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.gui.src.paint.executor.ExecutorrenderPackedtest sourcelib.gui.src.paint.executortest: paint executor exposes prepared...test sourcelib.gui.src.paint.executortest: paint executor preserves prepar...test sourcelib.gui.src.paint.executortest: paint executor reads prepared p...test sourcelib.gui.src.paint.executortest: paint executor reuses resident ...paint.ExecutorsubmitPreparedLaunchQueuedpaint.ExecutorsubmitPreparedLaunch
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.ExecutorrenderCommandsSurfaceFrameWithImageSh...paint.ExecutorrenderCommandsSurfaceFrameWithImagespaint.ExecutorsubmitPreparedLaunchtest sourcelib.gui.src.paint.executortest: paint executor exposes prepared...test sourcelib.gui.src.paint.executortest: paint executor prepares image-s...+5 morepaint.ExecutorsubmitPreparedLaunchQueuedWithEventspaint.ExecutorsubmitPreparedLaunchQueued
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallspaint.ExecutorsubmitPreparedLaunchQueuedtest sourcelib.gui.src.paint.executortest: paint executor writes prepared ...private sourcelib.gui.src.paint.executor.ExecutorpreparedLaunchRequestpaint.ExecutorsubmitPreparedLaunchQueuedWithEvents
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.gui.src.paint.executortest: paint executor rejects prepared...test sourcelib.gui.src.paint.executortest: paint executor rejects stale pr...test sourcelib.gui.src.paint.executortest: paint executor times prepared l...private sourcelib.gui.src.paint.executor.ExecutorvalidatePreparedLaunchpaint.ExecutorsubmitPreparedLaunchTimed
Static calls · unresolved targets: 0 · external targets: 8.
Called byCallspaint.ExecutorrenderCommandsSurfaceFrameWithImageSh...paint.ExecutorrenderCommandsSurfaceFrameWithImagestest sourcelib.gui.src.paint.executortest: paint executor prepares image-s...test sourcelib.gui.src.paint.executortest: paint executor rejects prepared...test sourcelib.gui.src.paint.executortest: paint executor rejects prepared...+3 morepaint.ExecutorwritePreparedSurfaceFrameWithEventspaint.ExecutorwritePreparedSurfaceFrame
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallspaint.ExecutorwritePreparedSurfaceFrametest sourcelib.gui.src.paint.executortest: paint executor writes prepared ...private sourcelib.gui.src.paint.executor.ExecutorvalidatePreparedLaunchprivate sourcelib.gui.src.paint.executorsurfaceFramePaintTargetprivate sourcelib.gui.src.paint.executorvalidatePreparedSurfaceTargetpaint.ExecutorwritePreparedSurfaceFrameWithEvents
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallspaint.PaintExecutorHostStorageinitprivate sourcelib.gui.src.paint.executorsumCheckedpaint.ExecutorCapacityderive
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.gui.src.paint.executor.Buffersinittest sourcelib.gui.src.paint.executortest: paint Executor host storage rej...paint.PaintExecutorHostStorageactivate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.gui.src.paint.executor.Buffersdeinitprivate sourcelib.gui.src.paint.executor.Buffersinittest sourcelib.gui.src.paint.executortest: paint Executor host storage rej...paint.PaintExecutorHostStoragedeinit
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.gui.src.paint.executor.Buffersinittest sourcelib.gui.src.paint.executortest: paint Executor host storage rej...paint.ExecutorCapacityderivepaint.PaintExecutorHostStorageinit
Static calls · unresolved targets: 1 · external targets: 1.
Called byCallsNo direct callspaint.Executorinitpaint.executorstorageModeForArtifactFormat
Static calls · unresolved targets: 0 · external targets: 1.

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

zig
const std = @import("std");const gpu = @import("gpu");const choir_abi = @import("choir_abi");const accy = @import("accy");const alloc_phase = @import("alloc_phase");const accy_paint = @import("accy.zig");const command = @import("command.zig");const cpu = @import("cpu/root.zig");const gui = @import("../root.zig");const paint_image = @import("image.zig");const Allocator = std.mem.Allocator;const Color = gui.model.UiColor;const Command = command.Command;const ImageSet = command.ImageSet;const kernel = accy.kernel;const scan_library = kernel.library.scan;const host_loop_launch_shape_arg_count: usize = choir_abi.launch_shape_argument_count;const PaintTarget = struct {    width: u32,    height: u32,    output_format: accy_paint.OutputFormat,};const LoadedKernel = struct {    artifact: gpu.KernelArtifact,    loaded: gpu.LoadedArtifact,    fn deinit(self: *LoadedKernel, handle: gpu.BackendHandle) void {        handle.destroyObject(self.loaded.id);        self.artifact.deinit();        self.* = undefined;    }};const DeviceScanKernels = struct {    instance: scan_library.DeviceScan,    stages: scan_library.DeviceScanStages,    block_scan: LoadedKernel,    sums_scan: LoadedKernel,    add_base: LoadedKernel,    fn deinit(self: *DeviceScanKernels, handle: gpu.BackendHandle) void {        self.add_base.deinit(handle);        self.sums_scan.deinit(handle);        self.block_scan.deinit(handle);        self.* = undefined;    }};pub const PreparedPackedLaunch = struct {    pixels: gpu.BufferHandle,    pixel_count: usize,    target_width: u32,    target_height: u32,    region_x: u32,    region_y: u32,    region_width: u32,    region_height: u32,    output_format: accy_paint.OutputFormat,    generation: u64,    loaded_artifact_id: gpu.BackendObjectId,    bindings: [7]gpu.BufferBinding,    scalar_storage: [11 + host_loop_launch_shape_arg_count]choir_abi.ScalarArgument,    scalar_count: usize,    geometry: choir_abi.LaunchGeometry,    command_visits: usize,    device_csr_prepared: bool,    fn request(        self: *const PreparedPackedLaunch,        artifact: *gpu.KernelArtifact,        loaded: gpu.LoadedArtifact,    ) gpu.LaunchRequest {        return .{            .artifact = artifact,            .loaded_artifact = loaded,            .buffers = self.bindings[0..],            .scalar_arguments = self.scalar_storage[0..self.scalar_count],            .geometry = self.geometry,        };    }};pub const PreparedLaunchTiming = struct {    elapsed_ns: u64,    stream_id: gpu.BackendObjectId,};pub const PreparedLaunchInfo = struct {    entry_name: []const u8,    artifact_format: gpu.ArtifactFormat,    artifact_payload_bytes: usize,    pixel_count: usize,    geometry: choir_abi.LaunchGeometry,    device_csr_prepared: bool,};pub const StorageMode = enum {    host_bins,    device_csr,};pub fn storageModeForArtifactFormat(format: gpu.ArtifactFormat) StorageMode {    return if (gpu.artifactFormatUsesHostLoopLaunch(format)) .host_bins else .device_csr;}const ExecutorLimits = struct {    mode: StorageMode,    commands: usize = 0,    pixels: usize = 0,    images: usize = 0,    image_pixels: usize = 0,    tiles: usize = 0,    tile_pairs: usize = 0,    pub fn worstCase(        mode: StorageMode,        commands: usize,        pixels: usize,        images: usize,        image_pixels: usize,        tiles: usize,    ) error{CapacityOverflow}!Limits {        return .{            .mode = mode,            .commands = commands,            .pixels = pixels,            .images = images,            .image_pixels = image_pixels,            .tiles = tiles,            .tile_pairs = std.math.mul(usize, commands, tiles) catch return error.CapacityOverflow,        };    }    fn merged(self: Limits, demand: Limits) error{StorageModeMismatch}!Limits {        if (self.mode != demand.mode) return error.StorageModeMismatch;        return .{            .mode = self.mode,            .commands = @max(self.commands, demand.commands),            .pixels = @max(self.pixels, demand.pixels),            .images = @max(self.images, demand.images),            .image_pixels = @max(self.image_pixels, demand.image_pixels),            .tiles = @max(self.tiles, demand.tiles),            .tile_pairs = @max(self.tile_pairs, demand.tile_pairs),        };    }};pub const Limits = ExecutorLimits;const ExecutorCapacity = struct {    limits: Limits,    command_capacity: usize,    pixel_capacity: usize,    image_capacity: usize,    image_pixel_capacity: usize,    tile_range_capacity: usize,    tile_offset_capacity: usize,    tile_count_capacity: usize,    tile_index_capacity: usize,    float_count: usize,    word_count: usize,    image_metadata_count: usize,    host_range_count: usize,    host_offset_count: usize,    host_index_count: usize,    host_cursor_count: usize,    host_storage_bytes: usize,    device_storage_bytes: usize,    device_buffer_count: usize,    total_storage_bytes: usize,    pub fn derive(limits: Limits) error{CapacityOverflow}!Capacity {        const command_capacity = @max(limits.commands, 1);        const pixel_capacity = @max(limits.pixels, 1);        const image_capacity = @max(limits.images, 1);        const image_pixel_capacity = @max(limits.image_pixels, 1);        const tile_range_capacity = @max(            std.math.mul(usize, limits.commands, accy_paint.tile_range_lanes) catch return error.CapacityOverflow,            1,        );        const tile_offset_capacity = @max(            std.math.add(usize, limits.tiles, 1) catch return error.CapacityOverflow,            2,        );        const tile_count_capacity = @max(limits.tiles, 1);        const tile_index_capacity = @max(limits.tile_pairs, 1);        const float_count = std.math.mul(usize, command_capacity, accy_paint.float_lanes) catch return error.CapacityOverflow;        const word_count = std.math.mul(usize, command_capacity, accy_paint.word_lanes) catch return error.CapacityOverflow;        const image_metadata_count = std.math.mul(usize, image_capacity, accy_paint.image_lanes) catch return error.CapacityOverflow;        const host_range_count = if (limits.mode == .host_bins) tile_range_capacity else 0;        const host_offset_count = if (limits.mode == .host_bins) tile_offset_capacity else 0;        const host_index_count = if (limits.mode == .host_bins) tile_index_capacity else 0;        const host_cursor_count = if (limits.mode == .host_bins) tile_count_capacity else 0;        const host_element_count = try sumChecked(&.{            float_count,            word_count,            image_metadata_count,            image_pixel_capacity,            pixel_capacity,            tile_offset_capacity,            host_range_count,            host_offset_count,            host_index_count,            host_cursor_count,        });        const host_storage_bytes = std.math.mul(usize, host_element_count, @sizeOf(u32)) catch return error.CapacityOverflow;        const common_device_elements = try sumChecked(&.{            pixel_capacity,            float_count,            word_count,            image_metadata_count,            image_pixel_capacity,            tile_offset_capacity,            tile_index_capacity,        });        const device_csr_elements = if (limits.mode == .device_csr)            try sumChecked(&.{                tile_range_capacity,                tile_offset_capacity,                tile_count_capacity,                scan_library.device_scan_max_blocks,                scan_library.device_scan_max_blocks,            })        else            0;        const device_elements = std.math.add(usize, common_device_elements, device_csr_elements) catch return error.CapacityOverflow;        const device_storage_bytes = std.math.mul(usize, device_elements, @sizeOf(u32)) catch return error.CapacityOverflow;        return .{            .limits = limits,            .command_capacity = command_capacity,            .pixel_capacity = pixel_capacity,            .image_capacity = image_capacity,            .image_pixel_capacity = image_pixel_capacity,            .tile_range_capacity = tile_range_capacity,            .tile_offset_capacity = tile_offset_capacity,            .tile_count_capacity = tile_count_capacity,            .tile_index_capacity = tile_index_capacity,            .float_count = float_count,            .word_count = word_count,            .image_metadata_count = image_metadata_count,            .host_range_count = host_range_count,            .host_offset_count = host_offset_count,            .host_index_count = host_index_count,            .host_cursor_count = host_cursor_count,            .host_storage_bytes = host_storage_bytes,            .device_storage_bytes = device_storage_bytes,            .device_buffer_count = if (limits.mode == .device_csr) 12 else 7,            .total_storage_bytes = std.math.add(usize, host_storage_bytes, device_storage_bytes) catch return error.CapacityOverflow,        };    }    pub fn admits(self: Capacity, demand: Limits) bool {        return self.limits.mode == demand.mode and            self.limits.commands >= demand.commands and            self.limits.pixels >= demand.pixels and            self.limits.images >= demand.images and            self.limits.image_pixels >= demand.image_pixels and            self.limits.tiles >= demand.tiles and            self.limits.tile_pairs >= demand.tile_pairs;    }};pub const Capacity = ExecutorCapacity;pub const StorageStatus = struct {    limits: ?Limits,    capacity: ?Capacity,    replacements: usize,    image_processor: ?paint_image.StorageStatus,};fn sumChecked(values: []const usize) error{CapacityOverflow}!usize {    var total: usize = 0;    for (values) |value| {        total = std.math.add(usize, total, value) catch return error.CapacityOverflow;    }    return total;}pub const Options = struct {    artifact_format: ?gpu.ArtifactFormat = null,    threads: u32 = accy_paint.default_threads,    initial_storage: ?Limits = null,    image_storage: ?paint_image.Limits = null,};pub const Executor = struct {    allocator: Allocator,    handle: gpu.BackendHandle,    artifact: gpu.KernelArtifact,    loaded: gpu.LoadedArtifact,    tile_range: ?LoadedKernel = null,    tile_count: ?LoadedKernel = null,    tile_index: ?LoadedKernel = null,    tile_scan: ?DeviceScanKernels = null,    threads: u32,    buffers: ?Buffers = null,    image_processor: ?paint_image.Processor = null,    shadow_expansion: ?CachedShadowExpansion = null,    storage_replacements: usize = 0,    prepared_generation: u64 = 0,    pub fn init(allocator: Allocator, handle: gpu.BackendHandle, options: Options) !Executor {        const format = options.artifact_format orelse try defaultFormat(handle);        const threads = @max(options.threads, 1);        var graph = try accy_paint.buildGraph(allocator, threads);        defer graph.deinit();        var artifact = try kernel.createKernelArtifact(allocator, handle, &graph, .{            .artifact_format = format,            .authored_kernel_diagnostic_id = "gui/paint/executor/rgba8-packed",        });        errdefer artifact.deinit();        const loaded = try handle.loadArtifact(&artifact);        errdefer handle.destroyObject(loaded.id);        var tile_range: ?LoadedKernel = null;        errdefer if (tile_range) |*value| value.deinit(handle);        var tile_count: ?LoadedKernel = null;        errdefer if (tile_count) |*value| value.deinit(handle);        var tile_index: ?LoadedKernel = null;        errdefer if (tile_index) |*value| value.deinit(handle);        var tile_scan: ?DeviceScanKernels = null;        errdefer if (tile_scan) |*value| value.deinit(handle);        if (!gpu.artifactFormatUsesHostLoopLaunch(format)) {            tile_range = try loadGraphKernel(allocator, handle, format, threads, accy_paint.buildTileRangeGraph, "gui/paint/executor/tile-ranges");            tile_count = try loadGraphKernel(allocator, handle, format, threads, accy_paint.buildTileCountGraph, "gui/paint/executor/tile-counts");            tile_index = try loadGraphKernel(allocator, handle, format, threads, accy_paint.buildTileIndexGraph, "gui/paint/executor/tile-indices");            tile_scan = try loadDeviceScanKernels(allocator, handle, format);        }        var result = Executor{            .allocator = allocator,            .handle = handle,            .artifact = artifact,            .loaded = loaded,            .tile_range = tile_range,            .tile_count = tile_count,            .tile_index = tile_index,            .tile_scan = tile_scan,            .threads = threads,        };        if (options.initial_storage) |limits| {            if (limits.mode != storageModeForArtifactFormat(format)) return error.StorageModeMismatch;            result.buffers = try Buffers.init(allocator, handle, limits);        }        errdefer result.releaseBuffers();        if (options.image_storage) |limits| {            result.image_processor = try paint_image.Processor.init(allocator, handle, .{                .artifact_format = format,                .initial_storage = limits,            });        }        return result;    }    pub fn deinit(self: *Executor) void {        if (self.shadow_expansion) |*cached| cached.deinit(self.allocator);        if (self.image_processor) |*processor| processor.deinit();        self.releaseBuffers();        if (self.tile_scan) |*value| value.deinit(self.handle);        if (self.tile_index) |*value| value.deinit(self.handle);        if (self.tile_count) |*value| value.deinit(self.handle);        if (self.tile_range) |*value| value.deinit(self.handle);        self.handle.destroyObject(self.loaded.id);        self.artifact.deinit();        self.* = undefined;    }    pub fn storageStatus(self: *const Executor) StorageStatus {        return .{            .limits = if (self.buffers) |buffers| buffers.capacity.limits else null,            .capacity = if (self.buffers) |buffers| buffers.capacity else null,            .replacements = self.storage_replacements,            .image_processor = if (self.image_processor) |*processor| processor.storageStatus() else null,        };    }    pub fn renderCommands(self: *Executor, commands: []const Command, target: cpu.Target, clear: Color) !void {        try self.renderCommandsWithImages(commands, target, clear, .{});    }    pub fn renderCommandsWithImageShadows(        self: *Executor,        commands: []const Command,        target: cpu.Target,        clear: Color,        images: ImageSet,        shadow_options: paint_image.ShadowExpansionOptions,    ) !void {        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        try self.renderCommandsWithImages(expansion.commands, target, clear, expansion.imageSet());    }    pub fn renderCommandsWithImages(self: *Executor, commands: []const Command, target: cpu.Target, clear: Color, images: ImageSet) !void {        try target.validate();        const pixel_count = try pixelCount(target.width, target.height);        if (pixel_count == 0) return;        const pixels = try self.renderPacked(commands, target.width, target.height, clear, images, cpu.Region.full(target.width, target.height));        try cpu.rgba8FromPacked(target.rgba8, pixels);    }    pub fn renderCommandsRegionWithImageShadows(        self: *Executor,        commands: []const Command,        target: cpu.Target,        clear: Color,        images: ImageSet,        region: cpu.Region,        shadow_options: paint_image.ShadowExpansionOptions,    ) !void {        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        try self.renderCommandsRegionWithImages(expansion.commands, target, clear, expansion.imageSet(), region);    }    pub fn renderCommandsRegionWithImages(self: *Executor, commands: []const Command, target: cpu.Target, clear: Color, images: ImageSet, region: cpu.Region) !void {        try target.validate();        const pixel_count = try pixelCount(target.width, target.height);        if (pixel_count == 0) return;        const active_region = region.clamped(target.width, target.height);        if (active_region.pixelCount() == 0) return;        const pixels = try self.renderPacked(commands, target.width, target.height, clear, images, active_region);        try copyRgba8Region(target.rgba8, target.width, active_region, pixels);    }    pub fn renderCommandsPacked(self: *Executor, commands: []const Command, target: cpu.PackedTarget, clear: Color) !void {        try self.renderCommandsPackedWithImages(commands, target, clear, .{});    }    pub fn renderCommandsPackedWithImageShadows(        self: *Executor,        commands: []const Command,        target: cpu.PackedTarget,        clear: Color,        images: ImageSet,        shadow_options: paint_image.ShadowExpansionOptions,    ) !void {        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        try self.renderCommandsPackedWithImages(expansion.commands, target, clear, expansion.imageSet());    }    pub fn renderCommandsPackedWithImages(self: *Executor, commands: []const Command, target: cpu.PackedTarget, clear: Color, images: ImageSet) !void {        try target.validate();        const pixel_count = try pixelCount(target.width, target.height);        if (pixel_count == 0) return;        const pixels = try self.renderPacked(commands, target.width, target.height, clear, images, cpu.Region.full(target.width, target.height));        @memcpy(target.pixels[0..pixel_count], pixels);    }    pub fn renderCommandsSurfaceFrame(self: *Executor, commands: []const Command, surface_frame: gpu.SurfaceFrame, clear: Color) !void {        try self.renderCommandsSurfaceFrameWithImages(commands, surface_frame, clear, .{});    }    pub fn renderCommandsSurfaceFrameWithImageShadows(        self: *Executor,        commands: []const Command,        surface_frame: gpu.SurfaceFrame,        clear: Color,        images: ImageSet,        shadow_options: paint_image.ShadowExpansionOptions,    ) !void {        var prepared = (try self.prepareCommandsSurfaceLaunchWithImageShadows(commands, surface_frame.surface, clear, images, shadow_options)) orelse return;        try self.submitPreparedLaunchQueued(&prepared);        try self.writePreparedSurfaceFrame(surface_frame, &prepared);    }    pub fn renderCommandsSurfaceFrameWithImages(self: *Executor, commands: []const Command, surface_frame: gpu.SurfaceFrame, clear: Color, images: ImageSet) !void {        const target = try surfaceFramePaintTarget(surface_frame);        var prepared = (try self.prepareCommandsFormattedLaunch(commands, target.width, target.height, clear, images, cpu.Region.full(target.width, target.height), target.output_format)) orelse return;        try self.submitPreparedLaunchQueued(&prepared);        try self.writePreparedSurfaceFrame(surface_frame, &prepared);    }    pub fn renderCommandsPackedRegionWithImageShadows(        self: *Executor,        commands: []const Command,        target: cpu.PackedTarget,        clear: Color,        images: ImageSet,        region: cpu.Region,        shadow_options: paint_image.ShadowExpansionOptions,    ) !void {        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        try self.renderCommandsPackedRegionWithImages(expansion.commands, target, clear, expansion.imageSet(), region);    }    pub fn renderCommandsPackedRegionWithImages(self: *Executor, commands: []const Command, target: cpu.PackedTarget, clear: Color, images: ImageSet, region: cpu.Region) !void {        try target.validate();        const pixel_count = try pixelCount(target.width, target.height);        if (pixel_count == 0) return;        const active_region = region.clamped(target.width, target.height);        if (active_region.pixelCount() == 0) return;        const pixels = try self.renderPacked(commands, target.width, target.height, clear, images, active_region);        copyPackedRegion(target.pixels, target.width, active_region, pixels);    }    pub fn expandImageShadowsAlloc(        self: *Executor,        commands: []const Command,        images: ImageSet,        options: paint_image.ShadowExpansionOptions,    ) !paint_image.ShadowExpansion {        const processor = try self.imageProcessor();        return processor.expandShadowsAlloc(commands, images, options);    }    fn cachedImageShadows(        self: *Executor,        commands: []const Command,        images: ImageSet,        options: paint_image.ShadowExpansionOptions,    ) !*const paint_image.ShadowExpansion {        if (self.shadow_expansion) |*cached| {            if (try cached.matches(commands, images, options)) return &cached.expansion;            if (try cached.refreshReusable(commands, images, options)) return &cached.expansion;        }        const processor = try self.imageProcessor();        var next = try CachedShadowExpansion.init(self.allocator, processor, commands, images, options);        errdefer next.deinit(self.allocator);        if (self.shadow_expansion) |*cached| {            cached.deinit(self.allocator);            self.shadow_expansion = null;        }        self.shadow_expansion = next;        if (self.shadow_expansion) |*cached| return &cached.expansion;        unreachable;    }    fn imageProcessor(self: *Executor) !*paint_image.Processor {        if (self.image_processor) |*processor| return processor;        self.image_processor = try paint_image.Processor.init(self.allocator, self.handle, .{            .artifact_format = self.artifact.format,        });        if (self.image_processor) |*processor| return processor;        unreachable;    }    fn renderPacked(self: *Executor, commands: []const Command, width: u32, height: u32, clear: Color, images: ImageSet, region: cpu.Region) ![]const u32 {        var prepared = (try self.prepareCommandsPackedLaunch(commands, width, height, clear, images, region)) orelse return &.{};        try self.submitPreparedLaunch(&prepared);        return self.readPreparedPackedPixels(&prepared);    }    pub fn prepareCommandsPackedLaunch(self: *Executor, commands: []const Command, width: u32, height: u32, clear: Color, images: ImageSet, region: cpu.Region) !?PreparedPackedLaunch {        return self.prepareCommandsFormattedLaunch(commands, width, height, clear, images, region, .rgba);    }    pub fn prepareCommandsPackedLaunchWithImageShadows(        self: *Executor,        commands: []const Command,        width: u32,        height: u32,        clear: Color,        images: ImageSet,        region: cpu.Region,        shadow_options: paint_image.ShadowExpansionOptions,    ) !?PreparedPackedLaunch {        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        return self.prepareCommandsPackedLaunch(expansion.commands, width, height, clear, expansion.imageSet(), region);    }    pub fn prepareCommandsSurfaceLaunch(self: *Executor, commands: []const Command, surface: gpu.SurfaceHandle, clear: Color, images: ImageSet) !?PreparedPackedLaunch {        const target = try surfacePaintTarget(surface);        return self.prepareCommandsFormattedLaunch(commands, target.width, target.height, clear, images, cpu.Region.full(target.width, target.height), target.output_format);    }    pub fn prepareCommandsSurfaceLaunchWithImageShadows(        self: *Executor,        commands: []const Command,        surface: gpu.SurfaceHandle,        clear: Color,        images: ImageSet,        shadow_options: paint_image.ShadowExpansionOptions,    ) !?PreparedPackedLaunch {        const target = try surfacePaintTarget(surface);        const expansion = try self.cachedImageShadows(commands, images, shadow_options);        return self.prepareCommandsFormattedLaunch(expansion.commands, target.width, target.height, clear, expansion.imageSet(), cpu.Region.full(target.width, target.height), target.output_format);    }    fn preparedLaunchRequest(self: *Executor, prepared: *const PreparedPackedLaunch) !gpu.LaunchRequest {        try self.validatePreparedLaunch(prepared);        return prepared.request(&self.artifact, self.loaded);    }    pub fn submitPreparedLaunchQueuedWithEvents(        self: *Executor,        prepared: *const PreparedPackedLaunch,        wait_events: []const gpu.EventHandle,        signal_event: ?gpu.EventHandle,    ) !void {        var request = try self.preparedLaunchRequest(prepared);        request.wait_events = wait_events;        request.signal_event = signal_event;        try self.handle.launch(request);    }    pub fn submitPreparedLaunchQueued(self: *Executor, prepared: *const PreparedPackedLaunch) !void {        try self.submitPreparedLaunchQueuedWithEvents(prepared, &.{}, null);    }    pub fn submitPreparedLaunchTimed(self: *Executor, prepared: *const PreparedPackedLaunch) !PreparedLaunchTiming {        try self.validatePreparedLaunch(prepared);        const stream = try self.handle.createStream(.{});        defer self.handle.destroyObject(stream.id);        const start = try self.handle.createEvent(.{});        defer self.handle.destroyObject(start.id);        const end = try self.handle.createEvent(.{});        defer self.handle.destroyObject(end.id);        try self.handle.recordEvent(.{            .stream = stream,            .event = start,        });        var request = prepared.request(&self.artifact, self.loaded);        request.stream = stream;        try self.handle.launch(request);        try self.handle.recordEvent(.{            .stream = stream,            .event = end,        });        try self.handle.synchronize(.{            .scope = .event,            .event = end,        });        const elapsed_ns = try self.handle.elapsedEventNs(.{            .start = start,            .end = end,        });        return .{            .elapsed_ns = elapsed_ns,            .stream_id = stream.id,        };    }    pub fn preparedLaunchInfo(self: *const Executor, prepared: *const PreparedPackedLaunch) !PreparedLaunchInfo {        try self.validatePreparedLaunch(prepared);        return .{            .entry_name = self.artifact.entry_name,            .artifact_format = self.artifact.format,            .artifact_payload_bytes = artifactPayloadBytes(&self.artifact),            .pixel_count = prepared.pixel_count,            .geometry = prepared.geometry,            .device_csr_prepared = prepared.device_csr_prepared,        };    }    pub fn submitPreparedLaunch(self: *Executor, prepared: *const PreparedPackedLaunch) !void {        try self.submitPreparedLaunchQueued(prepared);        try self.handle.synchronize(.{ .scope = .device });    }    pub fn readPreparedPackedPixels(self: *Executor, prepared: *const PreparedPackedLaunch) ![]const u32 {        try self.validatePreparedLaunch(prepared);        const buffers = if (self.buffers) |*value| value else unreachable;        try self.handle.readBuffer(.{            .handle = prepared.pixels,            .bytes = std.mem.sliceAsBytes(buffers.readback[0..buffers.pixel_capacity]),        });        return buffers.readback[0..prepared.pixel_count];    }    pub fn writePreparedSurfaceFrameWithEvents(        self: *Executor,        surface_frame: gpu.SurfaceFrame,        prepared: *const PreparedPackedLaunch,        wait_events: []const gpu.EventHandle,        signal_event: ?gpu.EventHandle,    ) !void {        try self.validatePreparedLaunch(prepared);        const target = try surfaceFramePaintTarget(surface_frame);        try validatePreparedSurfaceTarget(prepared, target);        const operations = [_]gpu.SurfaceFrameWriteOp{.{ .copy_buffer = prepared.pixels }};        try self.handle.writeSurfaceFrame(.{            .surface = surface_frame.surface,            .frame = surface_frame,            .operations = operations[0..],            .wait_events = wait_events,            .signal_event = signal_event,        });    }    pub fn writePreparedSurfaceFrame(self: *Executor, surface_frame: gpu.SurfaceFrame, prepared: *const PreparedPackedLaunch) !void {        try self.writePreparedSurfaceFrameWithEvents(surface_frame, prepared, &.{}, null);    }    fn prepareCommandsFormattedLaunch(        self: *Executor,        commands: []const Command,        width: u32,        height: u32,        clear: Color,        images: ImageSet,        region: cpu.Region,        output_format: accy_paint.OutputFormat,    ) !?PreparedPackedLaunch {        const target_pixel_count = try pixelCount(width, height);        if (target_pixel_count == 0) return null;        const active_region = region.clamped(width, height);        const region_pixel_count = active_region.pixelCount();        if (region_pixel_count == 0) return null;        const command_count_u32 = std.math.cast(u32, commands.len) orelse return error.CommandCountTooLarge;        const image_count = std.math.cast(u32, images.images.len) orelse return error.ImageCountTooLarge;        const region_pixel_count_u32 = std.math.cast(u32, region_pixel_count) orelse return error.DimensionsTooLarge;        const image_pixels = try accy_paint.imagePixelCount(images);        const shape = accy_paint.binShape(width, height, active_region);        const tile_offset_count = std.math.add(usize, shape.tile_count, 1) catch return error.BufferTooLarge;        const tile_pair_capacity = std.math.mul(usize, commands.len, shape.tile_count) catch return error.BufferTooLarge;        _ = std.math.cast(u32, tile_pair_capacity) orelse return error.BufferTooLarge;        const use_device_csr = !gpu.artifactFormatUsesHostLoopLaunch(self.artifact.format);        const storage_mode: StorageMode = if (use_device_csr) .device_csr else .host_bins;        if (use_device_csr and tile_offset_count > scan_library.deviceScanMaxExtent(self.tile_scan.?.instance)) return error.UnsupportedDeviceScanInstance;        var host_bins: accy_paint.BinView = undefined;        var reuse_host_tile_csr = false;        var host_tile_index_count: usize = 0;        if (!use_device_csr) {            if (self.buffers) |*existing| {                const resident_demand = Limits{                    .mode = storage_mode,                    .commands = commands.len,                    .pixels = region_pixel_count,                    .images = images.images.len,                    .image_pixels = image_pixels,                    .tiles = shape.tile_count,                    .tile_pairs = existing.tile_csr.tile_index_count,                };                if (existing.commandsResident(commands) and                    existing.tile_csr.matches(commands.len, width, height, active_region, shape) and                    existing.capacity.admits(resident_demand))                {                    reuse_host_tile_csr = true;                    host_tile_index_count = existing.tile_csr.tile_index_count;                }            }            if (!reuse_host_tile_csr) {                host_tile_index_count = try accy_paint.binPairCount(commands, width, height, active_region);            }        }        const tile_index_count = if (use_device_csr) tile_pair_capacity else host_tile_index_count;        const demand = Limits{            .mode = storage_mode,            .commands = commands.len,            .pixels = region_pixel_count,            .images = images.images.len,            .image_pixels = image_pixels,            .tiles = shape.tile_count,            .tile_pairs = tile_index_count,        };        const capacity_changed = try self.ensureCapacity(demand);        if (reuse_host_tile_csr) std.debug.assert(!capacity_changed);        const buffers = if (self.buffers) |*value| value else unreachable;        if (!use_device_csr and !reuse_host_tile_csr) {            host_bins = try buffers.host_bins.binCommands(commands, width, height, active_region);            std.debug.assert(host_bins.indices.len == host_tile_index_count);        }        const upload_commands = if (reuse_host_tile_csr) false else buffers.prepareCommandUpload(commands);        const image_upload = try buffers.prepareImageUpload(images, image_pixels);        const reuse_tile_csr = !upload_commands and buffers.tile_csr.matches(commands.len, width, height, active_region, shape);        const resident_changed = capacity_changed or            upload_commands or            image_upload != .none or            !reuse_tile_csr;        const generation = if (resident_changed) generation: {            try self.ensurePreparedGenerationAvailable();            break :generation self.advancePreparedGeneration();        } else self.prepared_generation;        if (commands.len != 0 and upload_commands) {            try self.handle.writeBuffer(.{                .handle = buffers.floats,                .bytes = std.mem.sliceAsBytes(buffers.staging.floats[0 .. commands.len * accy_paint.float_lanes]),            });            try self.handle.writeBuffer(.{                .handle = buffers.words,                .bytes = std.mem.sliceAsBytes(buffers.staging.words[0 .. commands.len * accy_paint.word_lanes]),            });        }        if (image_upload == .metadata_and_pixels) {            try self.handle.writeBuffer(.{                .handle = buffers.image_metadata,                .bytes = std.mem.sliceAsBytes(buffers.images.metadata[0 .. images.images.len * accy_paint.image_lanes]),            });        }        if (image_upload != .none) {            try self.handle.writeBuffer(.{                .handle = buffers.image_pixels,                .bytes = std.mem.sliceAsBytes(buffers.images.pixels[0..@max(image_pixels, 1)]),            });            buffers.markImageUpload(images.images.len, image_pixels);        }        var device_csr_prepared = false;        var command_visits: usize = 0;        if (use_device_csr) {            if (!reuse_tile_csr) {                try self.prepareTileCsr(commands.len, command_count_u32, width, height, active_region, shape, tile_offset_count);                buffers.markTileCsr(commands.len, width, height, active_region, shape, tile_pair_capacity, 0);                device_csr_prepared = true;            }        } else if (!reuse_tile_csr) {            try self.handle.writeBuffer(.{                .handle = buffers.tile_offsets,                .bytes = std.mem.sliceAsBytes(host_bins.offsets),            });            if (host_bins.indices.len != 0) {                try self.handle.writeBuffer(.{                    .handle = buffers.tile_indices,                    .bytes = std.mem.sliceAsBytes(host_bins.indices),                });            }            command_visits = accy_paint.commandVisits(host_bins, active_region);            buffers.markTileCsr(commands.len, width, height, active_region, shape, host_bins.indices.len, command_visits);        } else if (!use_device_csr) {            command_visits = buffers.tile_csr.command_visits;        }        const bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.pixels, .read_write),            bufferBinding(buffers.floats, .read_only),            bufferBinding(buffers.words, .read_only),            bufferBinding(buffers.image_metadata, .read_only),            bufferBinding(buffers.image_pixels, .read_only),            bufferBinding(buffers.tile_offsets, .read_only),            bufferBinding(buffers.tile_indices, .read_only),        };        const base_scalars = [_]choir_abi.ScalarArgument{            .{ .u32 = image_count },            .{ .u32 = clear.r },            .{ .u32 = clear.g },            .{ .u32 = clear.b },            .{ .u32 = clear.a },            .{ .u32 = active_region.x },            .{ .u32 = active_region.y },            .{ .u32 = active_region.width },            .{ .u32 = region_pixel_count_u32 },            .{ .u32 = shape.tiles_x },            .{ .u32 = @backingInt(output_format) },        };        var scalar_storage: [base_scalars.len + host_loop_launch_shape_arg_count]choir_abi.ScalarArgument = undefined;        @memcpy(scalar_storage[0..base_scalars.len], base_scalars[0..]);        var scalar_count: usize = base_scalars.len;        const geometry = choir_abi.LaunchGeometry{            .grid = .{ accy_paint.gridFor(region_pixel_count, self.threads), 1, 1 },            .threadgroup = .{ self.threads, 1, 1 },        };        if (gpu.artifactFormatUsesHostLoopLaunch(self.artifact.format)) {            const launch_shape = try choir_abi.launchShape(region_pixel_count, geometry);            try launch_shape.scalarArguments(scalar_storage[scalar_count..]);            scalar_count += host_loop_launch_shape_arg_count;        }        return .{            .pixels = buffers.pixels,            .pixel_count = region_pixel_count,            .target_width = width,            .target_height = height,            .region_x = active_region.x,            .region_y = active_region.y,            .region_width = active_region.width,            .region_height = active_region.height,            .output_format = output_format,            .generation = generation,            .loaded_artifact_id = self.loaded.id,            .bindings = bindings,            .scalar_storage = scalar_storage,            .scalar_count = scalar_count,            .geometry = geometry,            .command_visits = command_visits,            .device_csr_prepared = device_csr_prepared,        };    }    fn prepareTileCsr(        self: *Executor,        command_count: usize,        command_count_u32: u32,        width: u32,        height: u32,        active_region: cpu.Region,        shape: accy_paint.BinShape,        tile_offset_count: usize,    ) !void {        const buffers = if (self.buffers) |*value| value else unreachable;        if (command_count == 0) {            try buffers.writeZeroes(self.handle, buffers.tile_offsets, tile_offset_count);            return;        }        try buffers.writeZeroes(self.handle, buffers.tile_counts.?, tile_offset_count);        const range_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.tile_ranges.?, .read_write),            bufferBinding(buffers.floats, .read_only),            bufferBinding(buffers.words, .read_only),        };        const range_scalars = [_]choir_abi.ScalarArgument{            .{ .u32 = command_count_u32 },            .{ .u32 = width },            .{ .u32 = height },            .{ .u32 = active_region.x },            .{ .u32 = active_region.y },            .{ .u32 = active_region.width },            .{ .u32 = active_region.height },            .{ .u32 = shape.tiles_x },            .{ .u32 = shape.tiles_y },        };        try self.launchLoadedKernel(&self.tile_range.?, range_bindings[0..], range_scalars[0..], .{            .grid = .{ accy_paint.gridFor(command_count, self.threads), 1, 1 },            .threadgroup = .{ self.threads, 1, 1 },        }, command_count);        const count_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.tile_counts.?, .read_write),            bufferBinding(buffers.tile_ranges.?, .read_only),        };        const count_scalars = [_]choir_abi.ScalarArgument{            .{ .u32 = command_count_u32 },            .{ .u32 = shape.tiles_x },            .{ .u32 = shape.tiles_y },        };        try self.launchLoadedKernel(&self.tile_count.?, count_bindings[0..], count_scalars[0..], .{            .grid = .{ accy_paint.gridFor(shape.tile_count, self.threads), 1, 1 },            .threadgroup = .{ self.threads, 1, 1 },        }, shape.tile_count);        try self.launchTileOffsetScan(buffers, tile_offset_count);        try buffers.writeZeroes(self.handle, buffers.tile_cursors.?, shape.tile_count);        const tile_pair_count = std.math.mul(usize, command_count, shape.tile_count) catch return error.BufferTooLarge;        const index_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.tile_indices, .read_write),            bufferBinding(buffers.tile_cursors.?, .read_write),            bufferBinding(buffers.tile_offsets, .read_only),            bufferBinding(buffers.tile_ranges.?, .read_only),            bufferBinding(buffers.words, .read_only),        };        const index_scalars = [_]choir_abi.ScalarArgument{            .{ .u32 = command_count_u32 },            .{ .u32 = shape.tiles_x },            .{ .u32 = shape.tiles_y },        };        try self.launchLoadedKernel(&self.tile_index.?, index_bindings[0..], index_scalars[0..], .{            .grid = .{ accy_paint.gridFor(tile_pair_count, self.threads), 1, 1 },            .threadgroup = .{ self.threads, 1, 1 },        }, tile_pair_count);    }    fn launchTileOffsetScan(self: *Executor, buffers: *Buffers, tile_offset_count: usize) !void {        const extent_i32 = std.math.cast(i32, tile_offset_count) orelse return error.BufferTooLarge;        const tile_scan = &self.tile_scan.?;        const block_count = std.math.cast(u32, scan_library.deviceScanBlockCount(tile_offset_count, tile_scan.instance.threads)) orelse return error.UnsupportedDeviceScanInstance;        const block_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.tile_offsets, .read_write),            bufferBinding(buffers.tile_counts.?, .read_only),            bufferBinding(buffers.scan_sums.?, .read_write),        };        const block_scalars = [_]choir_abi.ScalarArgument{            .{ .i32 = extent_i32 },        };        const block_geometry = choir_abi.LaunchGeometry{            .grid = .{ block_count, 1, 1 },            .threadgroup = .{ tile_scan.instance.threads, 1, 1 },        };        try self.launchLoadedKernel(&tile_scan.block_scan, block_bindings[0..], block_scalars[0..], block_geometry, try block_geometry.threadCount());        const sums_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.scan_bases.?, .read_write),            bufferBinding(buffers.scan_sums.?, .read_only),        };        const sums_scalars = [_]choir_abi.ScalarArgument{            .{ .i32 = @intCast(block_count) },        };        const sums_geometry = choir_abi.LaunchGeometry{            .grid = .{ 1, 1, 1 },            .threadgroup = .{ tile_scan.stages.sums_scan.threads, 1, 1 },        };        try self.launchLoadedKernel(&tile_scan.sums_scan, sums_bindings[0..], sums_scalars[0..], sums_geometry, try sums_geometry.threadCount());        const add_bindings = [_]gpu.BufferBinding{            bufferBinding(buffers.tile_offsets, .read_write),            bufferBinding(buffers.scan_bases.?, .read_only),        };        const add_scalars = [_]choir_abi.ScalarArgument{            .{ .i32 = extent_i32 },        };        const add_geometry = choir_abi.LaunchGeometry{            .grid = .{ block_count, 1, 1 },            .threadgroup = .{ tile_scan.instance.threads, 1, 1 },        };        try self.launchLoadedKernel(&tile_scan.add_base, add_bindings[0..], add_scalars[0..], add_geometry, try add_geometry.threadCount());    }    fn launchLoadedKernel(        self: *Executor,        loaded_kernel: *LoadedKernel,        bindings: []const gpu.BufferBinding,        scalars: []const choir_abi.ScalarArgument,        geometry: choir_abi.LaunchGeometry,        total_count: u64,    ) !void {        var scalar_storage: [16 + host_loop_launch_shape_arg_count]choir_abi.ScalarArgument = undefined;        if (scalars.len > 16) return error.LaunchArgumentMismatch;        @memcpy(scalar_storage[0..scalars.len], scalars);        var scalar_count = scalars.len;        if (gpu.artifactFormatUsesHostLoopLaunch(loaded_kernel.artifact.format)) {            const shape = try choir_abi.launchShape(total_count, geometry);            try shape.scalarArguments(scalar_storage[scalar_count..]);            scalar_count += host_loop_launch_shape_arg_count;        }        try self.handle.launch(.{            .artifact = &loaded_kernel.artifact,            .loaded_artifact = loaded_kernel.loaded,            .buffers = bindings,            .scalar_arguments = scalar_storage[0..scalar_count],            .geometry = geometry,        });    }    fn ensureCapacity(self: *Executor, demand: Limits) !bool {        if (self.buffers) |buffers| {            if (buffers.capacity.admits(demand)) return false;        }        const next_limits = if (self.buffers) |buffers|            try buffers.capacity.limits.merged(demand)        else            demand;        const next = try Buffers.init(self.allocator, self.handle, next_limits);        const replacing = self.buffers != null;        self.releaseBuffers();        self.buffers = next;        if (replacing) self.storage_replacements += 1;        return true;    }    fn ensurePreparedGenerationAvailable(self: *const Executor) !void {        if (self.prepared_generation == std.math.maxInt(u64)) return error.PreparedLaunchGenerationExhausted;    }    fn advancePreparedGeneration(self: *Executor) u64 {        std.debug.assert(self.prepared_generation != std.math.maxInt(u64));        self.prepared_generation += 1;        return self.prepared_generation;    }    fn validatePreparedLaunch(self: *const Executor, prepared: *const PreparedPackedLaunch) !void {        if (prepared.loaded_artifact_id != self.loaded.id) return error.InvalidPreparedLaunch;        if (prepared.generation != self.prepared_generation) return error.StalePreparedLaunch;    }    fn releaseBuffers(self: *Executor) void {        if (self.buffers) |*buffers| {            buffers.deinit(self.allocator, self.handle);            self.buffers = null;        }    }};pub const ExecutorHostStorage = struct {    pub const Limits = ExecutorLimits;    pub const Capacity = ExecutorCapacity;    pub const claim: alloc_phase.capacity.Declaration = .{        .source = .{            .id = "gui.paint_executor_host_storage",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{                    .{                        .id = "command_and_image_upload_staging_for_one_prepared_paint_epoch",                        .lifetime = .steady,                        .detail = "command and image upload staging for one prepared paint epoch",                    },                    .{                        .id = "packed_readback_zero_fill_and_host_tile_bin_scratch",                        .lifetime = .steady,                        .detail = "packed readback zero-fill and host tile-bin scratch",                    },                },                .excluded = &.{                    "common and mode-specific backend buffers and their foreign allocation",                    "compiled and loaded paint kernel artifacts",                    "image Processor and cached shadow expansion storage",                    "caller-owned commands images targets surfaces and prepared values",                    "transactional complete-epoch replacement by Executor",                },            },            .capacity = .{                .inputs = &.{                    alloc_phase.capacity.bindInput(ExecutorLimits, "commands", "commands"),                    alloc_phase.capacity.bindInput(ExecutorLimits, "image_pixels", "image_pixels"),                    alloc_phase.capacity.bindInput(ExecutorLimits, "images", "images"),                    alloc_phase.capacity.bindInput(ExecutorLimits, "pixels", "pixels"),                    alloc_phase.capacity.bindInput(ExecutorLimits, "tile_pairs", "tile_pairs"),                    alloc_phase.capacity.bindInput(ExecutorLimits, "tiles", "tiles"),                },                .type_selectors = &.{},                .nodes = &.{                    .{ .input = 0 },                    .{ .input = 1 },                    .{ .input = 2 },                    .{ .input = 3 },                    .{ .input = 4 },                    .{ .input = 5 },                    .{ .add = .{ .left = 0, .right = 1 } },                    .{ .add = .{ .left = 6, .right = 2 } },                    .{ .add = .{ .left = 7, .right = 3 } },                    .{ .add = .{ .left = 8, .right = 4 } },                    .{ .add = .{ .left = 9, .right = 5 } },                },                .assertions = &.{.{                    .scope = .closure_total,                    .measure = .retained,                    .relation = .upper_bound,                    .expression = 10,                }},            },            .overload = .{                .kind = .reject_before_seal,                .detail = "checked capacity derivation and allocation failure reject before host storage activation; Executor may acquire a separate larger epoch",            },            .risks = .{                .transitive = .{                    .status = .open,                    .detail = "packing binning and backend transfer helpers are exercised but lack a machine-checked call-graph closure certificate",                },                .foreign = .{                    .status = .open,                    .detail = "backend buffer acquisition is capacity-accounted by Executor but remains outside this host-storage claim",                },            },            .obligations = &.{                .{ .key = "gui_paint_executor_capacity_capacity_model", .role = .capacity_model },                .{ .key = "gui_paint_executor_capacity_overload", .role = .overload },                .{ .key = "gui_paint_executor_acquisition", .role = .custom },                .{ .key = "gui_paint_executor_host_oom", .role = .overload },                .{ .key = "gui_paint_executor_boundary", .role = .custom },                .{ .key = "gui_paint_executor_atomic", .role = .custom },                .{ .key = "gui_paint_executor_steady", .role = .custom },            },        },        .bindings = .{            .owner = @This(),            .seal = .{                .family = alloc_phase.capacity.selector(@This().activate),                .premise = .{                    .class = .checked_semantic_fact,                    .authority = .checker,                },            },            .teardown = .{                .family = alloc_phase.capacity.selector(@This().deinit),                .premise = .{                    .class = .checked_semantic_fact,                    .authority = .checker,                },            },        },    };    phase: alloc_phase.capacity.Phase,    capacity: ExecutorCapacity,    limits: ExecutorLimits,    bytes: []align(@alignOf(u32)) u8,    pub fn init(allocator: Allocator, limits: ExecutorLimits) !ExecutorHostStorage {        const capacity = try ExecutorCapacity.derive(limits);        const bytes = if (capacity.host_storage_bytes == 0)            @as([]align(@alignOf(u32)) u8, &.{})        else            try allocator.alignedAlloc(                u8,                .fromByteUnits(@alignOf(u32)),                capacity.host_storage_bytes,            );        return .{            .phase = .initialization,            .capacity = capacity,            .limits = limits,            .bytes = bytes,        };    }    pub fn activate(self: *ExecutorHostStorage) void {        std.debug.assert(self.phase == .initialization);        std.debug.assert(std.meta.eql(self.capacity.limits, self.limits));        std.debug.assert(self.bytes.len == self.capacity.host_storage_bytes);        self.phase = .steady;    }    pub fn deinit(self: *ExecutorHostStorage, allocator: Allocator) void {        std.debug.assert(self.phase != .teardown);        self.phase = .teardown;        allocator.free(self.bytes);        self.* = undefined;    }};comptime {    alloc_phase.capacity.requireAllocatorExactOwnerShape(ExecutorHostStorage);}const HostCursor = struct {    bytes: []align(@alignOf(u32)) u8,    offset: usize = 0,    fn take(self: *HostCursor, comptime T: type, count: usize) []T {        comptime std.debug.assert(@sizeOf(T) == @sizeOf(u32));        comptime std.debug.assert(@alignOf(T) <= @alignOf(u32));        const byte_count = count * @sizeOf(T);        std.debug.assert(self.offset + byte_count <= self.bytes.len);        const pointer: [*]T = @ptrCast(@alignCast(self.bytes.ptr + self.offset));        self.offset += byte_count;        return pointer[0..count];    }};const Buffers = struct {    capacity: Capacity,    command_capacity: usize,    pixel_capacity: usize,    image_capacity: usize,    image_pixel_capacity: usize,    tile_range_capacity: usize,    tile_offset_capacity: usize,    tile_count_capacity: usize,    tile_index_capacity: usize,    pixels: gpu.BufferHandle,    floats: gpu.BufferHandle,    words: gpu.BufferHandle,    image_metadata: gpu.BufferHandle,    image_pixels: gpu.BufferHandle,    tile_ranges: ?gpu.BufferHandle,    tile_counts: ?gpu.BufferHandle,    tile_offsets: gpu.BufferHandle,    tile_cursors: ?gpu.BufferHandle,    tile_indices: gpu.BufferHandle,    scan_sums: ?gpu.BufferHandle,    scan_bases: ?gpu.BufferHandle,    host: ExecutorHostStorage,    host_bins: accy_paint.BinScratch,    readback: []u32,    zeroes: []u32,    staging: accy_paint.PackedCommands,    images: accy_paint.PackedImages,    command_upload: CommandUploadState = .{},    tile_csr: TileCsrState = .{},    image_upload: ImageUploadState = .{},    fn init(        allocator: Allocator,        handle: gpu.BackendHandle,        limits: Limits,    ) !Buffers {        var host_storage = try ExecutorHostStorage.init(allocator, limits);        errdefer host_storage.deinit(allocator);        const capacity = host_storage.capacity;        var host = HostCursor{ .bytes = host_storage.bytes };        const floats_staging = host.take(f32, capacity.float_count);        const words_staging = host.take(u32, capacity.word_count);        const image_metadata_staging = host.take(u32, capacity.image_metadata_count);        const image_pixels_staging = host.take(u32, capacity.image_pixel_capacity);        const readback = host.take(u32, capacity.pixel_capacity);        const zeroes = host.take(u32, capacity.tile_offset_capacity);        const host_ranges = host.take(u32, capacity.host_range_count);        const host_offsets = host.take(u32, capacity.host_offset_count);        const host_indices = host.take(u32, capacity.host_index_count);        const host_cursors = host.take(u32, capacity.host_cursor_count);        std.debug.assert(host.offset == host_storage.bytes.len);        const pixels = try allocateDeviceBuffer(handle, u32, .u32, capacity.pixel_capacity);        errdefer handle.destroyObject(pixels.id);        const floats = try allocateDeviceBuffer(handle, f32, .f32, capacity.float_count);        errdefer handle.destroyObject(floats.id);        const words = try allocateDeviceBuffer(handle, u32, .u32, capacity.word_count);        errdefer handle.destroyObject(words.id);        const image_metadata = try allocateDeviceBuffer(handle, u32, .u32, capacity.image_metadata_count);        errdefer handle.destroyObject(image_metadata.id);        const image_pixels = try allocateDeviceBuffer(handle, u32, .u32, capacity.image_pixel_capacity);        errdefer handle.destroyObject(image_pixels.id);        const tile_offsets = try allocateDeviceBuffer(handle, u32, .u32, capacity.tile_offset_capacity);        errdefer handle.destroyObject(tile_offsets.id);        const tile_indices = try allocateDeviceBuffer(handle, u32, .u32, capacity.tile_index_capacity);        errdefer handle.destroyObject(tile_indices.id);        var tile_ranges: ?gpu.BufferHandle = null;        errdefer if (tile_ranges) |buffer| handle.destroyObject(buffer.id);        var tile_counts: ?gpu.BufferHandle = null;        errdefer if (tile_counts) |buffer| handle.destroyObject(buffer.id);        var tile_cursors: ?gpu.BufferHandle = null;        errdefer if (tile_cursors) |buffer| handle.destroyObject(buffer.id);        var scan_sums: ?gpu.BufferHandle = null;        errdefer if (scan_sums) |buffer| handle.destroyObject(buffer.id);        var scan_bases: ?gpu.BufferHandle = null;        errdefer if (scan_bases) |buffer| handle.destroyObject(buffer.id);        if (limits.mode == .device_csr) {            tile_ranges = try allocateDeviceBuffer(handle, u32, .u32, capacity.tile_range_capacity);            tile_counts = try allocateDeviceBuffer(handle, u32, .u32, capacity.tile_offset_capacity);            tile_cursors = try allocateDeviceBuffer(handle, u32, .u32, capacity.tile_count_capacity);            scan_sums = try allocateDeviceBuffer(handle, u32, .u32, scan_library.device_scan_max_blocks);            scan_bases = try allocateDeviceBuffer(handle, u32, .u32, scan_library.device_scan_max_blocks);        }        host_storage.activate();        return .{            .capacity = capacity,            .command_capacity = capacity.command_capacity,            .pixel_capacity = capacity.pixel_capacity,            .image_capacity = capacity.image_capacity,            .image_pixel_capacity = capacity.image_pixel_capacity,            .tile_range_capacity = capacity.tile_range_capacity,            .tile_offset_capacity = capacity.tile_offset_capacity,            .tile_count_capacity = capacity.tile_count_capacity,            .tile_index_capacity = capacity.tile_index_capacity,            .pixels = pixels,            .floats = floats,            .words = words,            .image_metadata = image_metadata,            .image_pixels = image_pixels,            .tile_ranges = tile_ranges,            .tile_counts = tile_counts,            .tile_offsets = tile_offsets,            .tile_cursors = tile_cursors,            .tile_indices = tile_indices,            .scan_sums = scan_sums,            .scan_bases = scan_bases,            .host = host_storage,            .host_bins = .{                .ranges = host_ranges,                .offsets = host_offsets,                .indices = host_indices,                .cursors = host_cursors,            },            .readback = readback,            .zeroes = zeroes,            .staging = .{ .floats = floats_staging, .words = words_staging },            .images = .{ .metadata = image_metadata_staging, .pixels = image_pixels_staging },        };    }    fn deinit(self: *Buffers, allocator: Allocator, handle: gpu.BackendHandle) void {        handle.destroyObject(self.pixels.id);        handle.destroyObject(self.floats.id);        handle.destroyObject(self.words.id);        handle.destroyObject(self.image_metadata.id);        handle.destroyObject(self.image_pixels.id);        if (self.tile_ranges) |buffer| handle.destroyObject(buffer.id);        if (self.tile_counts) |buffer| handle.destroyObject(buffer.id);        handle.destroyObject(self.tile_offsets.id);        if (self.tile_cursors) |buffer| handle.destroyObject(buffer.id);        handle.destroyObject(self.tile_indices.id);        if (self.scan_sums) |buffer| handle.destroyObject(buffer.id);        if (self.scan_bases) |buffer| handle.destroyObject(buffer.id);        self.host.deinit(allocator);        self.* = undefined;    }    fn writeZeroes(self: *Buffers, handle: gpu.BackendHandle, buffer: gpu.BufferHandle, count: usize) !void {        if (count == 0) return;        if (self.zeroes.len < count) return error.BufferTooSmall;        @memset(self.zeroes[0..count], 0);        try handle.writeBuffer(.{            .handle = buffer,            .bytes = std.mem.sliceAsBytes(self.zeroes[0..count]),        });    }    fn commandsResident(self: *const Buffers, commands: []const Command) bool {        if (!self.command_upload.valid or self.command_upload.command_count != commands.len) return false;        const float_count = std.math.mul(usize, commands.len, accy_paint.float_lanes) catch return false;        const word_count = std.math.mul(usize, commands.len, accy_paint.word_lanes) catch return false;        if (float_count > self.staging.floats.len or word_count > self.staging.words.len) return false;        return accy_paint.commandsEqualPacked(commands, self.staging.floats[0..float_count], self.staging.words[0..word_count]);    }    fn prepareCommandUpload(self: *Buffers, commands: []const Command) bool {        const floats = self.staging.floats[0 .. commands.len * accy_paint.float_lanes];        const words = self.staging.words[0 .. commands.len * accy_paint.word_lanes];        const upload = !self.command_upload.valid or            self.command_upload.command_count != commands.len or            !accy_paint.commandsEqualPacked(commands, floats, words);        if (upload) {            accy_paint.packCommands(commands, floats, words);            self.command_upload = .{                .valid = true,                .command_count = commands.len,            };            self.tile_csr.valid = false;        }        return upload;    }    fn markTileCsr(self: *Buffers, command_count: usize, width: u32, height: u32, active_region: cpu.Region, shape: accy_paint.BinShape, tile_index_count: usize, command_visits: usize) void {        self.tile_csr = .{            .valid = true,            .command_count = command_count,            .width = width,            .height = height,            .active_region = active_region,            .tiles_x = shape.tiles_x,            .tiles_y = shape.tiles_y,            .tile_count = shape.tile_count,            .tile_index_count = tile_index_count,            .command_visits = command_visits,        };    }    fn prepareImageUpload(self: *Buffers, images: ImageSet, image_pixels: usize) !ImageUploadPlan {        if (images.images.len == 0) return .none;        if (!self.image_upload.valid or            self.image_upload.image_count != images.images.len or            self.image_upload.pixel_count != image_pixels)        {            try accy_paint.packImages(images, self.images.metadata, self.images.pixels);            return .metadata_and_pixels;        }        if (try accy_paint.imagesEqualPacked(images, self.images.metadata, self.images.pixels)) return .none;        const metadata_matches = try accy_paint.imageMetadataEqualPacked(images, self.images.metadata);        try accy_paint.packImages(images, self.images.metadata, self.images.pixels);        return if (metadata_matches) .pixels else .metadata_and_pixels;    }    fn markImageUpload(self: *Buffers, image_count: usize, pixel_count: usize) void {        self.image_upload = .{            .valid = true,            .image_count = image_count,            .pixel_count = pixel_count,        };    }};const CommandUploadState = struct {    valid: bool = false,    command_count: usize = 0,};const TileCsrState = struct {    valid: bool = false,    command_count: usize = 0,    width: u32 = 0,    height: u32 = 0,    active_region: cpu.Region = .{},    tiles_x: u32 = 0,    tiles_y: u32 = 0,    tile_count: usize = 0,    tile_index_count: usize = 0,    command_visits: usize = 0,    fn matches(self: TileCsrState, command_count: usize, width: u32, height: u32, active_region: cpu.Region, shape: accy_paint.BinShape) bool {        return self.valid and            self.command_count == command_count and            self.width == width and            self.height == height and            self.active_region.x == active_region.x and            self.active_region.y == active_region.y and            self.active_region.width == active_region.width and            self.active_region.height == active_region.height and            self.tiles_x == shape.tiles_x and            self.tiles_y == shape.tiles_y and            self.tile_count == shape.tile_count;    }};const ImageUploadState = struct {    valid: bool = false,    image_count: usize = 0,    pixel_count: usize = 0,};const ImageUploadPlan = enum {    none,    pixels,    metadata_and_pixels,};const CachedShadowExpansion = struct {    source_commands: []Command,    source_images: []command.Image,    source_pixels: []u32,    options: paint_image.ShadowExpansionOptions,    expansion: paint_image.ShadowExpansion,    fn init(        allocator: Allocator,        processor: *paint_image.Processor,        commands: []const Command,        images: ImageSet,        options: paint_image.ShadowExpansionOptions,    ) !CachedShadowExpansion {        const source_commands = try allocator.dupe(Command, commands);        errdefer allocator.free(source_commands);        var owned_images = try copyImageSet(allocator, images);        errdefer owned_images.deinit(allocator);        var expansion = try processor.expandShadowsAlloc(commands, .{ .images = owned_images.images }, options);        errdefer expansion.deinit();        return .{            .source_commands = source_commands,            .source_images = owned_images.images,            .source_pixels = owned_images.pixels,            .options = options,            .expansion = expansion,        };    }    fn deinit(self: *CachedShadowExpansion, allocator: Allocator) void {        self.expansion.deinit();        allocator.free(self.source_commands);        allocator.free(self.source_images);        allocator.free(self.source_pixels);        self.* = undefined;    }    fn matches(self: *const CachedShadowExpansion, commands: []const Command, images: ImageSet, options: paint_image.ShadowExpansionOptions) !bool {        return shadowExpansionOptionsEqual(self.options, options) and            commandsEqual(self.source_commands, commands) and            try imageSetsEqual(self.source_images, images);    }    fn refreshReusable(self: *CachedShadowExpansion, commands: []const Command, images: ImageSet, options: paint_image.ShadowExpansionOptions) !bool {        if (!shadowExpansionOptionsEqual(self.options, options)) return false;        if (!(try imageSetsEqual(self.source_images, images))) return false;        if (!commandsReusableForShadowExpansion(self.source_commands, commands)) return false;        try self.expansion.refreshReusableCommands(commands);        @memcpy(self.source_commands, commands);        return true;    }};const OwnedImageSet = struct {    images: []command.Image,    pixels: []u32,    fn deinit(self: *OwnedImageSet, allocator: Allocator) void {        allocator.free(self.images);        allocator.free(self.pixels);        self.* = undefined;    }};fn copyImageSet(allocator: Allocator, images: ImageSet) !OwnedImageSet {    const image_entries = try allocator.alloc(command.Image, images.images.len);    errdefer allocator.free(image_entries);    const pixel_count = try accy_paint.imagePixelCount(images);    const image_pixels = try allocator.alloc(u32, pixel_count);    errdefer allocator.free(image_pixels);    var pixel_offset: usize = 0;    for (images.images, 0..) |image, index| {        try image.validate();        const count = image.pixelCount();        @memcpy(image_pixels[pixel_offset .. pixel_offset + count], image.pixels[0..count]);        image_entries[index] = .{            .width = image.width,            .height = image.height,            .pixels = image_pixels[pixel_offset .. pixel_offset + count],        };        pixel_offset += count;    }    return .{ .images = image_entries, .pixels = image_pixels };}fn shadowExpansionOptionsEqual(left: paint_image.ShadowExpansionOptions, right: paint_image.ShadowExpansionOptions) bool {    return optionalF32Equal(left.sigma, right.sigma);}fn optionalF32Equal(left: ?f32, right: ?f32) bool {    if (left) |left_value| {        if (right) |right_value| return left_value == right_value;        return false;    }    return right == null;}fn commandsEqual(left: []const Command, right: []const Command) bool {    if (left.len != right.len) return false;    for (left, right) |left_command, right_command| {        if (!commandEqual(left_command, right_command)) return false;    }    return true;}fn commandsReusableForShadowExpansion(left: []const Command, right: []const Command) bool {    if (left.len != right.len) return false;    for (left, right) |left_command, right_command| {        const left_shadow = left_command.kind == .shadow;        const right_shadow = right_command.kind == .shadow;        if (left_shadow != right_shadow) return false;        if (left_shadow) {            if (!shadowCommandReusableForRetint(left_command, right_command)) return false;        }    }    return true;}fn shadowCommandReusableForRetint(left: Command, right: Command) bool {    return left.kind == right.kind and        rectEqual(left.rect, right.rect) and        rectEqual(left.clip, right.clip) and        rectEqual(left.source, right.source) and        left.color.a == right.color.a and        colorEqual(left.color_end, right.color_end) and        pointEqual(left.gradient_start, right.gradient_start) and        pointEqual(left.gradient_end, right.gradient_end) and        left.radius == right.radius and        left.width == right.width and        left.image_index == right.image_index and        left.order == right.order;}fn commandEqual(left: Command, right: Command) bool {    return left.kind == right.kind and        rectEqual(left.rect, right.rect) and        rectEqual(left.clip, right.clip) and        rectEqual(left.source, right.source) and        colorEqual(left.color, right.color) and        colorEqual(left.color_end, right.color_end) and        pointEqual(left.gradient_start, right.gradient_start) and        pointEqual(left.gradient_end, right.gradient_end) and        left.radius == right.radius and        left.width == right.width and        left.image_index == right.image_index and        left.order == right.order;}fn rectEqual(left: gui.layout.Rect, right: gui.layout.Rect) bool {    return left.x == right.x and        left.y == right.y and        left.width == right.width and        left.height == right.height;}fn colorEqual(left: Color, right: Color) bool {    return left.r == right.r and left.g == right.g and left.b == right.b and left.a == right.a;}fn pointEqual(left: gui.model.UiPoint, right: gui.model.UiPoint) bool {    return left.x == right.x and left.y == right.y;}fn expectShadowPixelsTinted(pixels: []const u32, color: Color) !void {    try std.testing.expect(pixels.len != 0);    for (pixels) |pixel| {        const unpacked = cpu.pixel.unpackRgba(pixel);        try std.testing.expectEqual(color.r, unpacked.r);        try std.testing.expectEqual(color.g, unpacked.g);        try std.testing.expectEqual(color.b, unpacked.b);    }}fn imageSetsEqual(cached: []const command.Image, images: ImageSet) !bool {    if (cached.len != images.images.len) return false;    for (cached, images.images) |left, right| {        try right.validate();        if (left.width != right.width or left.height != right.height) return false;        const count = right.pixelCount();        if (!std.mem.eql(u32, left.pixels[0..count], right.pixels[0..count])) return false;    }    return true;}fn defaultFormat(handle: gpu.BackendHandle) !gpu.ArtifactFormat {    const kind = handle.backendKind() orelse return error.UnsupportedArtifactFormat;    return switch (kind) {        .cuda => .cuda_ptx,        .vulkan => .vulkan_spirv,        .metal => .metal_msl,        else => error.UnsupportedArtifactFormat,    };}fn loadGraphKernel(    allocator: Allocator,    handle: gpu.BackendHandle,    format: gpu.ArtifactFormat,    threads: u32,    comptime build: anytype,    diagnostic_id: []const u8,) !LoadedKernel {    var graph = try build(allocator, threads);    defer graph.deinit();    var artifact = try kernel.createKernelArtifact(allocator, handle, &graph, .{        .artifact_format = format,        .authored_kernel_diagnostic_id = diagnostic_id,    });    errdefer artifact.deinit();    const loaded = try handle.loadArtifact(&artifact);    errdefer handle.destroyObject(loaded.id);    return .{ .artifact = artifact, .loaded = loaded };}fn loadDeviceScanKernels(    allocator: Allocator,    handle: gpu.BackendHandle,    format: gpu.ArtifactFormat,) !DeviceScanKernels {    const instance = scan_library.DeviceScan{        .extent = @as(u64, scan_library.prefix_sum_max_threads) * scan_library.device_scan_max_blocks,        .dtype = .u32,        .threads = scan_library.prefix_sum_max_threads,        .mode = .inclusive,    };    const stages = try scan_library.deviceScanStages(instance);    var artifacts = try scan_library.createDeviceScanPipelineArtifacts(allocator, handle, instance, .{        .limits = kernel.Limits.standard,        .format = format,    });    defer artifacts.deinit();    const entries = artifacts.entries();    var block_scan = try loadKernelCallEntry(allocator, handle, entries[0], "gui/paint/executor/tile-offset-scan-block");    errdefer block_scan.deinit(handle);    var sums_scan = try loadKernelCallEntry(allocator, handle, entries[1], "gui/paint/executor/tile-offset-scan-sums");    errdefer sums_scan.deinit(handle);    const add_base = try loadKernelCallEntry(allocator, handle, entries[2], "gui/paint/executor/tile-offset-scan-add");    return .{        .instance = instance,        .stages = stages,        .block_scan = block_scan,        .sums_scan = sums_scan,        .add_base = add_base,    };}fn loadKernelCallEntry(    allocator: Allocator,    handle: gpu.BackendHandle,    entry: anytype,    diagnostic_id: []const u8,) !LoadedKernel {    var artifact = try kernel.createBackendArtifactFromKernelCallEntry(allocator, handle, entry, diagnostic_id);    errdefer artifact.deinit();    const loaded = try handle.loadArtifact(&artifact);    errdefer handle.destroyObject(loaded.id);    return .{ .artifact = artifact, .loaded = loaded };}fn pixelCount(width: u32, height: u32) !usize {    return std.math.mul(usize, @as(usize, width), @as(usize, height)) catch return error.DimensionsTooLarge;}fn allocateDeviceBuffer(handle: gpu.BackendHandle, comptime T: type, dtype: choir_abi.DType, count: usize) !gpu.BufferHandle {    const byte_size = std.math.mul(usize, count, @sizeOf(T)) catch return error.BufferTooLarge;    return handle.allocateBuffer(.{        .byte_size = byte_size,        .alignment = 256,        .dtype = dtype,        .element_count = std.math.cast(u64, count) orelse return error.BufferTooLarge,    });}fn surfacePaintTarget(surface: gpu.SurfaceHandle) !PaintTarget {    if (!surface.extent.valid()) return error.InvalidSurface;    return .{        .width = surface.extent.width,        .height = surface.extent.height,        .output_format = outputFormatForTexture(surface.format) orelse return error.InvalidSurface,    };}fn surfaceFramePaintTarget(frame: gpu.SurfaceFrame) !PaintTarget {    if (!frame.texture.extent.valid()) return error.InvalidSurfaceFrame;    if (frame.texture.extent.depth != 1) return error.InvalidSurfaceFrame;    return .{        .width = frame.texture.extent.width,        .height = frame.texture.extent.height,        .output_format = outputFormatForTexture(frame.texture.format) orelse return error.InvalidSurfaceFrame,    };}fn validatePreparedSurfaceTarget(prepared: *const PreparedPackedLaunch, target: PaintTarget) !void {    const target_pixel_count = try pixelCount(target.width, target.height);    if (prepared.target_width != target.width) return error.InvalidSurfaceFrame;    if (prepared.target_height != target.height) return error.InvalidSurfaceFrame;    if (prepared.output_format != target.output_format) return error.InvalidSurfaceFrame;    if (prepared.region_x != 0 or prepared.region_y != 0) return error.InvalidSurfaceFrame;    if (prepared.region_width != target.width or prepared.region_height != target.height) return error.InvalidSurfaceFrame;    if (prepared.pixel_count != target_pixel_count) return error.InvalidSurfaceFrame;}fn artifactPayloadBytes(artifact: *const gpu.KernelArtifact) usize {    return switch (artifact.payload) {        .none => 0,        .bytes => |bytes| bytes.len,        .words_u32 => |words| words.len * @sizeOf(u32),        .text => |text| text.len,        .external => 0,    };}/// The paint output a color texture holds; a depth texture holds none.fn outputFormatForTexture(format: gpu.TextureFormat) ?accy_paint.OutputFormat {    return switch (format) {        .rgba8_unorm, .rgba8_srgb => .rgba,        .bgra8_unorm, .bgra8_srgb => .bgra,        .depth32_float => null,    };}fn bufferBinding(buffer: gpu.BufferHandle, access: gpu.BufferAccess) gpu.BufferBinding {    return .{        .handle = buffer,        .access = access,        .ownership = buffer.ownership,        .byte_size = buffer.byte_size,    };}fn copyRgba8Region(dst: []u8, width: u32, region: cpu.Region, src: []const u32) !void {    const required_pixels = @as(usize, region.y + region.height) * width;    const required_bytes = std.math.mul(usize, required_pixels, 4) catch return error.BufferTooSmall;    if (dst.len < required_bytes) return error.BufferTooSmall;    var y: u32 = 0;    while (y < region.height) : (y += 1) {        var x: u32 = 0;        while (x < region.width) : (x += 1) {            const src_index = @as(usize, y) * region.width + x;            const dst_index = @as(usize, region.y + y) * width + region.x + x;            writePackedRgba8(dst, dst_index, src[src_index]);        }    }}fn copyPackedRegion(dst: []u32, width: u32, region: cpu.Region, src: []const u32) void {    var y: u32 = 0;    while (y < region.height) : (y += 1) {        const dst_start = @as(usize, region.y + y) * width + region.x;        const src_start = @as(usize, y) * region.width;        @memcpy(dst[dst_start .. dst_start + region.width], src[src_start .. src_start + region.width]);    }}fn writePackedRgba8(dst: []u8, index: usize, pixel: u32) void {    const base = index * 4;    dst[base] = @truncate(pixel);    dst[base + 1] = @truncate(pixel >> 8);    dst[base + 2] = @truncate(pixel >> 16);    dst[base + 3] = @truncate(pixel >> 24);}test "paint Executor capacity matches an independent host and device byte model" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_capacity_capacity_model"),            null,            null,            null,            null,            null,            null,        );    }    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_capacity_overload"),            null,            null,            null,            null,            null,            null,        );    }    const cases = [_]Limits{        .{ .mode = .host_bins },        .{ .mode = .host_bins, .commands = 7, .pixels = 4096, .images = 3, .image_pixels = 600, .tiles = 16, .tile_pairs = 41 },        .{ .mode = .device_csr, .commands = 11, .pixels = 2304, .images = 2, .image_pixels = 257, .tiles = 9, .tile_pairs = 99 },    };    for (cases) |limits| {        const capacity = try Capacity.derive(limits);        const commands = @max(@as(u128, limits.commands), 1);        const pixels = @max(@as(u128, limits.pixels), 1);        const images = @max(@as(u128, limits.images), 1);        const image_pixels = @max(@as(u128, limits.image_pixels), 1);        const tile_ranges = @max(@as(u128, limits.commands) * accy_paint.tile_range_lanes, 1);        const tile_offsets = @max(@as(u128, limits.tiles) + 1, 2);        const tiles = @max(@as(u128, limits.tiles), 1);        const tile_pairs = @max(@as(u128, limits.tile_pairs), 1);        const floats = commands * accy_paint.float_lanes;        const words = commands * accy_paint.word_lanes;        const metadata = images * accy_paint.image_lanes;        const host_bins = if (limits.mode == .host_bins) tile_ranges + tile_offsets + tile_pairs + tiles else 0;        const host_elements = floats + words + metadata + image_pixels + pixels + tile_offsets + host_bins;        const common_device = pixels + floats + words + metadata + image_pixels + tile_offsets + tile_pairs;        const device_csr = if (limits.mode == .device_csr)            tile_ranges + tile_offsets + tiles + 2 * scan_library.device_scan_max_blocks        else            0;        try std.testing.expectEqual(@as(usize, @intCast(host_elements * @sizeOf(u32))), capacity.host_storage_bytes);        try std.testing.expectEqual(@as(usize, @intCast((common_device + device_csr) * @sizeOf(u32))), capacity.device_storage_bytes);        try std.testing.expectEqual(            capacity.host_storage_bytes + capacity.device_storage_bytes,            capacity.total_storage_bytes,        );        try std.testing.expectEqual(@as(usize, if (limits.mode == .device_csr) 12 else 7), capacity.device_buffer_count);    }}test "paint Executor rejects overflowing storage limits" {    try std.testing.expectError(        error.CapacityOverflow,        Limits.worstCase(.host_bins, std.math.maxInt(usize), 1, 0, 0, 2),    );    try std.testing.expectError(        error.CapacityOverflow,        Capacity.derive(.{ .mode = .host_bins, .commands = std.math.maxInt(usize) }),    );}test "paint Executor rejects initial storage for another artifact mode" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    try std.testing.expectError(        error.StorageModeMismatch,        Executor.init(allocator, state.handle(), .{            .artifact_format = .vulkan_spirv,            .initial_storage = .{ .mode = .host_bins },        }),    );}test "paint Executor host storage rejects initialization OOM and seals on retry" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_host_oom"),            null,            null,            null,            null,            null,            null,        );    }    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    const limits = Limits{        .mode = .device_csr,        .commands = 1,        .pixels = 16,        .tiles = 1,        .tile_pairs = 1,    };    failing.fail_index = failing.alloc_index;    try std.testing.expectError(        error.OutOfMemory,        ExecutorHostStorage.init(failing.allocator(), limits),    );    try std.testing.expect(failing.has_induced_failure);    failing.fail_index = std.math.maxInt(usize);    var storage = try ExecutorHostStorage.init(failing.allocator(), limits);    defer storage.deinit(failing.allocator());    try std.testing.expectEqual(alloc_phase.capacity.Phase.initialization, storage.phase);    storage.activate();    try std.testing.expectEqual(alloc_phase.capacity.Phase.steady, storage.phase);    try std.testing.expectEqual(storage.capacity.host_storage_bytes, storage.bytes.len);}test "paint Executor initial storage exposes the exact chosen capacity" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_acquisition"),            null,            null,            null,            null,            null,            null,        );    }    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const limits = Limits{        .mode = .device_csr,        .commands = 3,        .pixels = 64,        .images = 2,        .image_pixels = 32,        .tiles = 4,        .tile_pairs = 12,    };    var executor = try Executor.init(allocator, state.handle(), .{ .initial_storage = limits });    defer executor.deinit();    const status = executor.storageStatus();    try std.testing.expectEqual(limits, status.limits.?);    try std.testing.expectEqual(try Capacity.derive(limits), status.capacity.?);    try std.testing.expectEqual(@as(usize, 0), status.replacements);    try std.testing.expectEqual(status.capacity.?.host_storage_bytes, executor.buffers.?.host.bytes.len);    try std.testing.expectEqual(status.capacity.?.device_buffer_count, state.buffer_allocate_count);}test "paint Executor eagerly acquires and reports configured image storage" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const limits = paint_image.Limits{        .dst_pixels = 64,        .src_pixels = 32,        .scratch_pixels = 64,        .weight_taps = 7,    };    var executor = try Executor.init(allocator, state.handle(), .{ .image_storage = limits });    defer executor.deinit();    const status = executor.storageStatus();    try std.testing.expectEqual(@as(?Limits, null), status.limits);    try std.testing.expectEqual(limits, status.image_processor.?.limits.?);    try std.testing.expectEqual(try paint_image.Capacity.derive(limits), status.image_processor.?.capacity.?);    try std.testing.expectEqual(status.image_processor.?.capacity.?.device_buffer_count, state.buffer_allocate_count);}test "paint Executor replaces storage at max plus one and retains its high water mark" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_boundary"),            null,            null,            null,            null,            null,            null,        );    }    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const limits = Limits{        .mode = .device_csr,        .commands = 1,        .pixels = 16,        .tiles = 1,        .tile_pairs = 1,    };    var executor = try Executor.init(allocator, state.handle(), .{ .initial_storage = limits });    defer executor.deinit();    const commands = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 5, .height = 5 },        .clip = .{ .x = 0, .y = 0, .width = 5, .height = 5 },        .color = .{ .a = 255 },    }};    _ = try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{}, .{}, cpu.Region.full(4, 4));    try std.testing.expectEqual(@as(usize, 0), executor.storageStatus().replacements);    _ = try executor.prepareCommandsPackedLaunch(commands[0..], 5, 5, .{}, .{}, cpu.Region.full(5, 5));    const grown = executor.storageStatus();    try std.testing.expectEqual(@as(usize, 1), grown.replacements);    try std.testing.expectEqual(@as(usize, 25), grown.limits.?.pixels);    _ = try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{}, .{}, cpu.Region.full(4, 4));    try std.testing.expectEqual(grown, executor.storageStatus());}test "paint Executor failed storage replacement preserves the prior epoch" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_atomic"),            null,            null,            null,            null,            null,            null,        );    }    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    var state = gpu.recording.BackendState{        .allocator = failing.allocator(),        .kind = .vulkan,        .format = .vulkan_spirv,    };    const limits = Limits{        .mode = .device_csr,        .commands = 1,        .pixels = 16,        .tiles = 1,        .tile_pairs = 1,    };    var executor = try Executor.init(failing.allocator(), state.handle(), .{ .initial_storage = limits });    defer executor.deinit();    const before = executor.storageStatus();    const pixels_id = executor.buffers.?.pixels.id;    const commands = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 17, .height = 17 },        .clip = .{ .x = 0, .y = 0, .width = 17, .height = 17 },        .color = .{ .a = 255 },    }};    failing.fail_index = failing.alloc_index;    try std.testing.expectError(        error.OutOfMemory,        executor.prepareCommandsPackedLaunch(commands[0..], 17, 17, .{}, .{}, cpu.Region.full(17, 17)),    );    try std.testing.expect(failing.has_induced_failure);    try std.testing.expectEqual(before, executor.storageStatus());    try std.testing.expectEqual(pixels_id, executor.buffers.?.pixels.id);    failing.fail_index = std.math.maxInt(usize);    _ = try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{}, .{}, cpu.Region.full(4, 4));}test "paint Executor admitted prepared launch makes no allocator calls" {    comptime {        @stardustClaim(            @import("alloc_phase").capacity.witness(ExecutorHostStorage, "gui_paint_executor_steady"),            null,            null,            null,            null,            null,            null,        );    }    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    var state = gpu.recording.BackendState{        .allocator = failing.allocator(),        .kind = .vulkan,        .format = .vulkan_spirv,    };    const limits = Limits{        .mode = .device_csr,        .commands = 1,        .pixels = 16,        .tiles = 1,        .tile_pairs = 1,    };    var executor = try Executor.init(failing.allocator(), state.handle(), .{ .initial_storage = limits });    defer executor.deinit();    const commands = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .color = .{ .a = 255 },    }};    failing.fail_index = failing.alloc_index;    failing.resize_fail_index = failing.resize_index;    _ = try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{}, .{}, cpu.Region.full(4, 4));    try std.testing.expect(!failing.has_induced_failure);}test "paint executor initializes native recording artifact" {    const allocator = std.testing.allocator;    inline for (.{ gpu.ArtifactFormat.cuda_ptx, .vulkan_spirv, .metal_msl }) |format| {        var state = gpu.recording.BackendState{            .allocator = allocator,            .kind = switch (format) {                .cuda_ptx => .cuda,                .vulkan_spirv => .vulkan,                .metal_msl => .metal,                else => unreachable,            },            .format = format,        };        var executor = try Executor.init(allocator, state.handle(), .{});        var cleanup = true;        defer if (cleanup) executor.deinit();        const loaded_id = executor.loaded.id;        try std.testing.expectEqual(format, executor.artifact.format);        try std.testing.expectEqual(@as(usize, 4), state.create_count);        try std.testing.expectEqual(@as(usize, 7), state.load_count);        executor.deinit();        cleanup = false;        try std.testing.expectEqual(@as(usize, 7), state.destroy_count);        try std.testing.expectEqual(loaded_id, state.last_destroyed_id.?);    }}test "paint executor initializes native CPU object artifact" {    const allocator = std.testing.allocator;    var state = gpu.cpu.State.init(allocator);    defer state.deinit();    var executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer executor.deinit();    try std.testing.expectEqual(gpu.ArtifactFormat.cpu_object, executor.artifact.format);}test "paint executor renders packed commands with native CPU object artifact" {    const allocator = std.testing.allocator;    var state = gpu.cpu.State.init(allocator);    defer state.deinit();    var executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer executor.deinit();    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 = 20, .g = 40, .b = 60, .a = 255 },    }};    var expected = @as([(4 * 4)]u32, @splat(0));    var actual = @as([(4 * 4)]u32, @splat(0));    const clear = Color{ .r = 1, .g = 2, .b = 3, .a = 255 };    try cpu.renderCommandsPacked(commands[0..], .{ .width = 4, .height = 4, .pixels = expected[0..] }, clear);    try executor.renderCommandsPacked(commands[0..], .{ .width = 4, .height = 4, .pixels = actual[0..] }, clear);    try std.testing.expectEqualSlices(u32, expected[0..], actual[0..]);}test "paint executor renders packed commands with image shadows through CPU object artifact" {    const allocator = std.testing.allocator;    var state = gpu.cpu.State.init(allocator);    defer state.deinit();    var actual_executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer actual_executor.deinit();    var expected_executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer expected_executor.deinit();    var processor = try paint_image.Processor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer processor.deinit();    const commands = [_]Command{        .{            .kind = .shadow,            .rect = .{ .x = 3, .y = 2, .width = 4, .height = 3 },            .clip = .{ .x = 0, .y = 0, .width = 12, .height = 10 },            .color = .{ .r = 90, .g = 10, .b = 20, .a = 180 },            .radius = 1,            .width = 1,            .order = 0,        },        .{            .kind = .fill,            .rect = .{ .x = 5, .y = 4, .width = 3, .height = 2 },            .clip = .{ .x = 0, .y = 0, .width = 12, .height = 10 },            .color = .{ .r = 20, .g = 120, .b = 60, .a = 220 },            .order = 1,        },    };    const clear = Color{ .r = 1, .g = 2, .b = 3, .a = 255 };    var actual = @as([(12 * 10)]u32, @splat(0));    var expected = @as([(12 * 10)]u32, @splat(0));    try actual_executor.renderCommandsPackedWithImageShadows(commands[0..], .{        .width = 12,        .height = 10,        .pixels = actual[0..],    }, clear, .{}, .{});    var expansion = try processor.expandShadowsAlloc(commands[0..], .{}, .{});    defer expansion.deinit();    try expected_executor.renderCommandsPackedWithImages(expansion.commands, .{        .width = 12,        .height = 10,        .pixels = expected[0..],    }, clear, expansion.imageSet());    try std.testing.expectEqualSlices(u32, expected[0..], actual[0..]);    try std.testing.expect(actual[3 * 12 + 4] != cpu.packRgba(clear));}test "paint executor launches packed commands through backend handle" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    var pixels = @as([(8 * 5)]u32, @splat(0xffff_ffff));    const commands = [_]Command{        .{            .kind = .fill,            .rect = .{ .x = 1, .y = 1, .width = 3, .height = 2 },            .clip = .{ .x = 0, .y = 0, .width = 8, .height = 5 },            .color = .{ .r = 11, .g = 22, .b = 33, .a = 44 },        },        .{            .kind = .stroke,            .rect = .{ .x = 2, .y = 1, .width = 4, .height = 3 },            .clip = .{ .x = 0, .y = 0, .width = 8, .height = 5 },            .color = .{ .r = 99, .g = 88, .b = 77, .a = 66 },            .radius = 2,            .width = 1,        },    };    try executor.renderCommandsPacked(commands[0..], .{ .width = 8, .height = 5, .pixels = pixels[0..] }, .{        .r = 1,        .g = 2,        .b = 3,        .a = 4,    });    try std.testing.expectEqual(@as(usize, 12), state.buffer_allocate_count);    try std.testing.expectEqual(@as(usize, 4), state.write_count);    try std.testing.expectEqual(@as(usize, 7), state.launch_count);    try std.testing.expectEqual(@as(usize, 1), state.sync_count);    try std.testing.expectEqual(@as(usize, 1), state.read_count);    try std.testing.expectEqual(@as(usize, 7), state.last_launch_buffer_count);    try std.testing.expectEqual(@as(usize, 11), state.last_launch_scalar_count);    try std.testing.expectEqual(gpu.BufferAccess.read_write, state.last_buffer_access[0]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[1]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[2]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[3]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[4]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[5]);    try std.testing.expectEqual(gpu.BufferAccess.read_only, state.last_buffer_access[6]);    try std.testing.expectEqual(@as(u32, 0), state.last_launch_scalar_u32_values[0]);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[1]);    try std.testing.expectEqual(@as(u32, 2), state.last_launch_scalar_u32_values[2]);    try std.testing.expectEqual(@as(u32, 3), state.last_launch_scalar_u32_values[3]);    try std.testing.expectEqual(@as(u32, 4), state.last_launch_scalar_u32_values[4]);    try std.testing.expectEqual(@as(u32, 0), state.last_launch_scalar_u32_values[5]);    try std.testing.expectEqual(@as(u32, 0), state.last_launch_scalar_u32_values[6]);    try std.testing.expectEqual(@as(u32, 8), state.last_launch_scalar_u32_values[7]);    try std.testing.expectEqual(@as(u32, 40), state.last_launch_scalar_u32_values[8]);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[9]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), state.last_launch_scalar_u32_values[10]);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_grid[0]);    try std.testing.expectEqual(@as(u32, accy_paint.default_threads), state.last_launch_threadgroup[0]);    try std.testing.expectEqual(@as(usize, pixels.len * @sizeOf(u32)), state.last_read_byte_count);    try std.testing.expectEqualSlices(u32, &@as([(8 * 5)]u32, @splat(0)), pixels[0..]);}test "paint executor exposes prepared packed launch after device CSR preparation" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var prepared = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(@as(usize, 4), state.write_count);    try std.testing.expectEqual(@as(usize, 6), state.launch_count);    try std.testing.expectEqual(@as(usize, 0), state.sync_count);    try std.testing.expectEqual(@as(usize, 0), state.read_count);    try std.testing.expectEqual(executor.buffers.?.tile_cursors.?.id, state.last_write_buffer_id.?);    try std.testing.expectEqual(executor.tile_index.?.loaded.id, state.last_launch_loaded_id.?);    try std.testing.expectEqual(@as(usize, 7), prepared.bindings.len);    try std.testing.expectEqual(@as(usize, 11), prepared.scalar_count);    try std.testing.expectEqual(@as(u32, 16), prepared.scalar_storage[8].u32);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), prepared.scalar_storage[10].u32);    try std.testing.expectEqual(@as(u32, 1), prepared.geometry.grid[0]);    try std.testing.expectEqual(@as(u32, accy_paint.default_threads), prepared.geometry.threadgroup[0]);    try std.testing.expectEqual(@as(usize, 0), prepared.command_visits);    try std.testing.expect(prepared.device_csr_prepared);    try executor.submitPreparedLaunchQueued(&prepared);    try std.testing.expectEqual(@as(usize, 7), state.launch_count);    try std.testing.expectEqual(@as(usize, 0), state.sync_count);    try std.testing.expectEqual(@as(usize, 0), state.read_count);    try std.testing.expectEqual(@as(usize, 7), state.last_launch_buffer_count);    try std.testing.expectEqual(@as(usize, 11), state.last_launch_scalar_count);    try executor.submitPreparedLaunch(&prepared);    try std.testing.expectEqual(@as(usize, 8), state.launch_count);    try std.testing.expectEqual(@as(usize, 1), state.sync_count);    try std.testing.expectEqual(@as(usize, 0), state.read_count);}test "paint executor preserves prepared launch generations across resident device prepares" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var first = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expect(first.device_csr_prepared);    const first_generation = first.generation;    const first_write_count = state.write_count;    const first_launch_count = state.launch_count;    const second = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(first_generation, second.generation);    try std.testing.expect(!second.device_csr_prepared);    try std.testing.expectEqual(first_write_count, state.write_count);    try std.testing.expectEqual(first_launch_count, state.launch_count);    const info = try executor.preparedLaunchInfo(&first);    try std.testing.expectEqual(@as(usize, 16), info.pixel_count);    try executor.submitPreparedLaunchQueued(&first);    try std.testing.expectEqual(first_launch_count + 1, state.launch_count);    const changed = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .color = .{ .r = 8, .g = 8, .b = 9, .a = 255 },    }};    const third = (try executor.prepareCommandsPackedLaunch(changed[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(first_generation + 1, third.generation);    try std.testing.expect(third.device_csr_prepared);    try std.testing.expect(state.write_count > first_write_count);    try std.testing.expectError(error.StalePreparedLaunch, executor.preparedLaunchInfo(&first));}test "paint executor preserves prepared launch generations across resident host-loop prepares" {    const allocator = std.testing.allocator;    var state = gpu.cpu.State.init(allocator);    defer state.deinit();    var executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer executor.deinit();    const commands = [_]Command{};    const first_clear = Color{ .r = 3, .g = 5, .b = 7, .a = 255 };    const second_clear = Color{ .r = 11, .g = 13, .b = 17, .a = 255 };    var first = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, first_clear, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expect(!first.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 0), first.command_visits);    const first_generation = first.generation;    var second = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, second_clear, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(first_generation, second.generation);    try std.testing.expect(!second.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 0), second.command_visits);    try executor.submitPreparedLaunch(&first);    const first_pixels = try executor.readPreparedPackedPixels(&first);    try std.testing.expectEqual(cpu.packRgba(first_clear), first_pixels[0]);    try executor.submitPreparedLaunch(&second);    const second_pixels = try executor.readPreparedPackedPixels(&second);    try std.testing.expectEqual(cpu.packRgba(second_clear), second_pixels[0]);    const changed = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 2, .height = 2 },        .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .color = .{ .r = 19, .g = 23, .b = 29, .a = 255 },    }};    const third = (try executor.prepareCommandsPackedLaunch(changed[0..], 4, 4, first_clear, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(first_generation + 1, third.generation);    try std.testing.expectError(error.StalePreparedLaunch, executor.preparedLaunchInfo(&first));}test "paint executor reuses resident host-loop tile bins without rebuilding scratch" {    const allocator = std.testing.allocator;    var state = gpu.cpu.State.init(allocator);    defer state.deinit();    var executor = try Executor.init(allocator, state.handle(), .{        .artifact_format = .cpu_object,    });    defer executor.deinit();    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 = 19, .g = 23, .b = 29, .a = 255 },    }};    const clear = Color{ .a = 0 };    const first = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, clear, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expect(first.command_visits > 0);    try std.testing.expect(executor.buffers.?.host_bins.ranges.len != 0);    const first_generation = first.generation;    const first_tile_index_count = executor.buffers.?.tile_csr.tile_index_count;    const first_command_visits = executor.buffers.?.tile_csr.command_visits;    const retained_host_bins = executor.buffers.?.host_bins;    executor.buffers.?.host_bins = .{};    const second = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, clear, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(first_generation, second.generation);    try std.testing.expectEqual(first_tile_index_count, executor.buffers.?.tile_csr.tile_index_count);    try std.testing.expectEqual(first_command_visits, second.command_visits);    try std.testing.expectEqual(@as(usize, 0), executor.buffers.?.host_bins.ranges.len);    executor.buffers.?.host_bins = retained_host_bins;    try executor.submitPreparedLaunch(&second);    const pixels = try executor.readPreparedPackedPixels(&second);    try std.testing.expectEqual(cpu.packRgba(commands[0].color), pixels[0]);}test "paint executor times prepared launches through retained owner" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,        .event_elapsed_ns = 123_456,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var prepared = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    const launch_count = state.launch_count;    const timing = try executor.submitPreparedLaunchTimed(&prepared);    try std.testing.expectEqual(@as(u64, 123_456), timing.elapsed_ns);    try std.testing.expectEqual(@as(usize, 1), state.created_stream_count);    try std.testing.expectEqual(@as(usize, 2), state.created_event_count);    try std.testing.expectEqual(@as(usize, 2), state.record_event_count);    try std.testing.expectEqual(@as(usize, 1), state.elapsed_event_count);    try std.testing.expectEqual(@as(usize, 1), state.sync_count);    try std.testing.expectEqual(launch_count + 1, state.launch_count);    try std.testing.expectEqual(timing.stream_id, state.last_launch_stream.?);    try std.testing.expectEqual(timing.stream_id, state.record_streams[0].?);    try std.testing.expectEqual(timing.stream_id, state.record_streams[1].?);    try std.testing.expectEqual(state.record_events[0].?, state.elapsed_start_events[0].?);    try std.testing.expectEqual(state.record_events[1].?, state.elapsed_end_events[0].?);}test "paint executor describes prepared launches through retained owner" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    const prepared = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    const info = try executor.preparedLaunchInfo(&prepared);    try std.testing.expect(info.entry_name.len != 0);    try std.testing.expectEqual(gpu.ArtifactFormat.vulkan_spirv, info.artifact_format);    try std.testing.expectEqual(artifactPayloadBytes(&executor.artifact), info.artifact_payload_bytes);    try std.testing.expectEqual(@as(usize, 16), info.pixel_count);    try std.testing.expectEqual(@as(u32, 1), info.geometry.grid[0]);    try std.testing.expectEqual(@as(u32, accy_paint.default_threads), info.geometry.threadgroup[0]);    try std.testing.expect(info.device_csr_prepared);}test "paint executor reads prepared packed launches through retained capacity" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const commands = [_]Command{};    var large = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try executor.submitPreparedLaunch(&large);    const large_pixels = try executor.readPreparedPackedPixels(&large);    try std.testing.expectEqual(@as(usize, 16), large_pixels.len);    try std.testing.expectEqual(@as(usize, 1), state.read_count);    try std.testing.expectEqual(@as(usize, 16 * @sizeOf(u32)), state.last_read_byte_count);    const retained_pixel_capacity = executor.buffers.?.pixel_capacity;    try std.testing.expectEqual(@as(usize, 16), retained_pixel_capacity);    var small = (try executor.prepareCommandsPackedLaunch(commands[0..], 2, 2, .{ .a = 0 }, .{}, cpu.Region.full(2, 2))).?;    try executor.submitPreparedLaunch(&small);    const small_pixels = try executor.readPreparedPackedPixels(&small);    try std.testing.expectEqual(@as(usize, 4), small_pixels.len);    try std.testing.expectEqual(@as(usize, 2), state.read_count);    try std.testing.expectEqual(@as(usize, 16 * @sizeOf(u32)), state.last_read_byte_count);    try std.testing.expectEqual(retained_pixel_capacity, executor.buffers.?.pixel_capacity);}test "paint executor rejects stale prepared launch generations" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var first = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(@as(u64, 1), first.generation);    const first_pixels_id = first.pixels.id;    var second = (try executor.prepareCommandsPackedLaunch(commands[0..], 8, 8, .{ .a = 0 }, .{}, cpu.Region.full(8, 8))).?;    try std.testing.expectEqual(@as(u64, 2), second.generation);    try std.testing.expect(first_pixels_id != second.pixels.id);    const launch_count = state.launch_count;    const read_count = state.read_count;    const surface_write_count = state.surface_write_count;    try std.testing.expectError(error.StalePreparedLaunch, executor.preparedLaunchInfo(&first));    try std.testing.expectError(error.StalePreparedLaunch, executor.submitPreparedLaunchTimed(&first));    try std.testing.expectError(error.StalePreparedLaunch, executor.submitPreparedLaunchQueued(&first));    try std.testing.expectError(error.StalePreparedLaunch, executor.readPreparedPackedPixels(&first));    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    try std.testing.expectError(error.StalePreparedLaunch, executor.writePreparedSurfaceFrame(frame, &first));    try std.testing.expectEqual(launch_count, state.launch_count);    try std.testing.expectEqual(read_count, state.read_count);    try std.testing.expectEqual(surface_write_count, state.surface_write_count);    try executor.submitPreparedLaunchQueued(&second);    try std.testing.expectEqual(launch_count + 1, state.launch_count);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try handle.destroySurface(surface);}test "paint executor rejects prepared launches from another executor" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var first_executor = try Executor.init(allocator, handle, .{});    defer first_executor.deinit();    var second_executor = try Executor.init(allocator, handle, .{});    defer second_executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var first_prepared = (try first_executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    var second_prepared = (try second_executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(@as(u64, 1), first_prepared.generation);    try std.testing.expectEqual(first_prepared.generation, second_prepared.generation);    try std.testing.expect(first_prepared.loaded_artifact_id != second_prepared.loaded_artifact_id);    const launch_count = state.launch_count;    const read_count = state.read_count;    const surface_write_count = state.surface_write_count;    try std.testing.expectError(error.InvalidPreparedLaunch, second_executor.preparedLaunchInfo(&first_prepared));    try std.testing.expectError(error.InvalidPreparedLaunch, second_executor.submitPreparedLaunchTimed(&first_prepared));    try std.testing.expectError(error.InvalidPreparedLaunch, second_executor.submitPreparedLaunchQueued(&first_prepared));    try std.testing.expectError(error.InvalidPreparedLaunch, second_executor.readPreparedPackedPixels(&first_prepared));    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    try std.testing.expectError(error.InvalidPreparedLaunch, second_executor.writePreparedSurfaceFrame(frame, &first_prepared));    try std.testing.expectEqual(launch_count, state.launch_count);    try std.testing.expectEqual(read_count, state.read_count);    try std.testing.expectEqual(surface_write_count, state.surface_write_count);    try second_executor.submitPreparedLaunchQueued(&second_prepared);    try std.testing.expectEqual(launch_count + 1, state.launch_count);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try handle.destroySurface(surface);}test "paint executor reuses prepared device CSR for unchanged commands" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    const changed_commands = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },        .color = .{ .r = 7, .g = 8, .b = 10, .a = 255 },    }};    const first = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    const first_writes = state.write_count;    const first_launches = state.launch_count;    try std.testing.expect(first.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 4), first_writes);    try std.testing.expectEqual(@as(usize, 6), first_launches);    const second = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expect(!second.device_csr_prepared);    try std.testing.expectEqual(first_writes, state.write_count);    try std.testing.expectEqual(first_launches, state.launch_count);    const changed = (try executor.prepareCommandsPackedLaunch(changed_commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expect(changed.device_csr_prepared);    try std.testing.expectEqual(first_writes + 4, state.write_count);    try std.testing.expectEqual(first_launches + 6, state.launch_count);    const region_changed = (try executor.prepareCommandsPackedLaunch(changed_commands[0..], 4, 4, .{ .a = 0 }, .{}, .{ .x = 1, .y = 1, .width = 2, .height = 2 })).?;    try std.testing.expect(region_changed.device_csr_prepared);    try std.testing.expectEqual(first_writes + 6, state.write_count);    try std.testing.expectEqual(first_launches + 12, state.launch_count);}test "paint executor writes packed commands into surface frame without readback" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    const image_pixels = [_]u32{cpu.packRgba(.{ .r = 30, .g = 40, .b = 50, .a = 255 })};    const images = ImageSet{ .images = &.{.{ .width = 1, .height = 1, .pixels = image_pixels[0..] }} };    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 = 7, .g = 8, .b = 9, .a = 255 },        },        .{            .kind = .image,            .rect = .{ .x = 1, .y = 1, .width = 1, .height = 1 },            .clip = .{ .x = 0, .y = 0, .width = 4, .height = 4 },            .source = .{ .x = 0, .y = 0, .width = 1, .height = 1 },            .color = .{ .r = 255, .g = 255, .b = 255, .a = 255 },            .image_index = 0,        },    };    try executor.renderCommandsSurfaceFrameWithImages(commands[0..], frame, .{ .r = 1, .g = 2, .b = 3, .a = 255 }, images);    const buffers = executor.buffers.?;    try std.testing.expectEqual(@as(usize, 7), state.launch_count);    try std.testing.expectEqual(@as(usize, 0), state.sync_count);    try std.testing.expectEqual(@as(usize, 0), state.read_count);    try std.testing.expectEqual(@as(usize, 1), state.surface_write_count);    try std.testing.expectEqual(frame.id, state.last_written_frame_id.?);    try std.testing.expectEqual(@as(usize, 1), state.last_surface_write_op_count);    try std.testing.expectEqual(buffers.pixels.id, state.last_surface_write_copy_buffer_id.?);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[0]);    try std.testing.expectEqual(@as(u32, 4), state.last_launch_scalar_u32_values[7]);    try std.testing.expectEqual(@as(u32, 16), state.last_launch_scalar_u32_values[8]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), state.last_launch_scalar_u32_values[10]);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try std.testing.expectEqual(@as(usize, 1), state.surface_present_count);    try handle.destroySurface(surface);}test "paint executor writes prepared surface frames with events" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    const launch_done = try handle.createEvent(.{});    const write_done = try handle.createEvent(.{});    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var prepared = (try executor.prepareCommandsSurfaceLaunch(commands[0..], surface, .{ .a = 0 }, .{})).?;    const prepare_launches = state.launch_count;    try executor.submitPreparedLaunchQueuedWithEvents(&prepared, &.{}, launch_done);    try std.testing.expectEqual(prepare_launches + 1, state.launch_count);    try std.testing.expectEqual(launch_done.id, state.last_launch_signal_event.?);    try std.testing.expect(try handle.queryEvent(.{ .event = launch_done }));    try std.testing.expect(!try handle.queryEvent(.{ .event = write_done }));    try executor.writePreparedSurfaceFrameWithEvents(frame, &prepared, &.{launch_done}, write_done);    try std.testing.expectEqual(@as(usize, 1), state.surface_write_count);    try std.testing.expectEqual(@as(usize, 1), state.last_surface_write_wait_count);    try std.testing.expectEqual(launch_done.id, state.last_surface_write_wait_events[0]);    try std.testing.expectEqual(write_done.id, state.last_surface_write_signal_event.?);    try std.testing.expect(try handle.queryEvent(.{ .event = write_done }));    try std.testing.expectEqual(@as(usize, 0), state.read_count);    try std.testing.expectEqual(@as(usize, 0), state.sync_count);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try handle.destroySurface(surface);}test "paint executor writes bgra surface frame with bgra output format" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .bgra8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    var prepared = (try executor.prepareCommandsSurfaceLaunch(commands[0..], surface, .{ .r = 1, .g = 2, .b = 3, .a = 255 }, .{})).?;    try executor.submitPreparedLaunchQueued(&prepared);    try executor.writePreparedSurfaceFrame(frame, &prepared);    const buffers = executor.buffers.?;    try std.testing.expectEqual(@as(usize, 16), prepared.pixel_count);    try std.testing.expectEqual(@as(u32, 4), prepared.target_width);    try std.testing.expectEqual(@as(u32, 4), prepared.target_height);    try std.testing.expectEqual(@as(u32, 0), prepared.region_x);    try std.testing.expectEqual(@as(u32, 0), prepared.region_y);    try std.testing.expectEqual(@as(u32, 4), prepared.region_width);    try std.testing.expectEqual(@as(u32, 4), prepared.region_height);    try std.testing.expectEqual(accy_paint.OutputFormat.bgra, prepared.output_format);    try std.testing.expectEqual(@as(usize, 7), state.launch_count);    try std.testing.expectEqual(@as(usize, 0), state.sync_count);    try std.testing.expectEqual(@as(usize, 0), state.read_count);    try std.testing.expectEqual(@as(usize, 1), state.surface_write_count);    try std.testing.expectEqual(frame.id, state.last_written_frame_id.?);    try std.testing.expectEqual(@as(usize, 1), state.last_surface_write_op_count);    try std.testing.expectEqual(buffers.pixels.id, state.last_surface_write_copy_buffer_id.?);    try std.testing.expectEqual(@as(usize, 11), state.last_launch_scalar_count);    try std.testing.expectEqual(@as(u32, 16), state.last_launch_scalar_u32_values[8]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.bgra)), state.last_launch_scalar_u32_values[10]);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try handle.destroySurface(surface);}test "paint executor writes smaller surface frames through retained pixel capacity" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    const large_surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const large_frame = try handle.acquireSurfaceFrame(.{ .surface = large_surface });    var large = (try executor.prepareCommandsSurfaceLaunch(commands[0..], large_surface, .{ .a = 0 }, .{})).?;    try executor.submitPreparedLaunchQueued(&large);    try executor.writePreparedSurfaceFrame(large_frame, &large);    try handle.presentSurfaceFrame(.{        .surface = large_surface,        .frame = large_frame,    });    try handle.destroySurface(large_surface);    const retained_pixel_capacity = executor.buffers.?.pixel_capacity;    const retained_pixels = executor.buffers.?.pixels;    try std.testing.expectEqual(@as(usize, 16), large.pixel_count);    try std.testing.expectEqual(@as(usize, 16), retained_pixel_capacity);    try std.testing.expectEqual(@as(usize, 1), state.surface_write_count);    try std.testing.expectEqual(retained_pixels.id, state.last_surface_write_copy_buffer_id.?);    const small_surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 2, .height = 2 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const small_frame = try handle.acquireSurfaceFrame(.{ .surface = small_surface });    var small = (try executor.prepareCommandsSurfaceLaunch(commands[0..], small_surface, .{ .a = 0 }, .{})).?;    try executor.submitPreparedLaunchQueued(&small);    try executor.writePreparedSurfaceFrame(small_frame, &small);    try std.testing.expectEqual(@as(usize, 4), small.pixel_count);    try std.testing.expectEqual(retained_pixel_capacity, executor.buffers.?.pixel_capacity);    try std.testing.expectEqual(retained_pixels.id, executor.buffers.?.pixels.id);    try std.testing.expectEqual(@as(usize, 2), state.surface_write_count);    try std.testing.expectEqual(small_frame.id, state.last_written_frame_id.?);    try std.testing.expectEqual(retained_pixels.id, state.last_surface_write_copy_buffer_id.?);    try handle.presentSurfaceFrame(.{        .surface = small_surface,        .frame = small_frame,    });    try handle.destroySurface(small_surface);}test "paint executor rejects prepared surface frame target mismatches" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    const bgra_surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .bgra8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const bgra_frame = try handle.acquireSurfaceFrame(.{ .surface = bgra_surface });    var rgba_prepared = (try executor.prepareCommandsPackedLaunch(commands[0..], 4, 4, .{ .a = 0 }, .{}, cpu.Region.full(4, 4))).?;    try std.testing.expectEqual(accy_paint.OutputFormat.rgba, rgba_prepared.output_format);    try std.testing.expectError(error.InvalidSurfaceFrame, executor.writePreparedSurfaceFrame(bgra_frame, &rgba_prepared));    try std.testing.expectEqual(@as(usize, 0), state.surface_write_count);    try handle.presentSurfaceFrame(.{        .surface = bgra_surface,        .frame = bgra_frame,    });    try handle.destroySurface(bgra_surface);    const rgba_surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 4, .height = 4 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const rgba_frame = try handle.acquireSurfaceFrame(.{ .surface = rgba_surface });    var small_prepared = (try executor.prepareCommandsPackedLaunch(commands[0..], 2, 2, .{ .a = 0 }, .{}, cpu.Region.full(2, 2))).?;    try std.testing.expectEqual(@as(u32, 2), small_prepared.target_width);    try std.testing.expectError(error.InvalidSurfaceFrame, executor.writePreparedSurfaceFrame(rgba_frame, &small_prepared));    try std.testing.expectEqual(@as(usize, 0), state.surface_write_count);    try handle.presentSurfaceFrame(.{        .surface = rgba_surface,        .frame = rgba_frame,    });    try handle.destroySurface(rgba_surface);}test "paint executor writes image shadows into surface frame without paint readback" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 10, .height = 10 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    const commands = [_]Command{.{        .kind = .shadow,        .rect = .{ .x = 2, .y = 2, .width = 4, .height = 3 },        .clip = .{ .x = 0, .y = 0, .width = 10, .height = 10 },        .color = .{ .r = 30, .g = 40, .b = 50, .a = 210 },        .radius = 1,        .width = 2,        .order = 4,    }};    const load_before = state.load_count;    const launch_before = state.launch_count;    const sync_before = state.sync_count;    const read_before = state.read_count;    try executor.renderCommandsSurfaceFrameWithImageShadows(commands[0..], frame, .{ .a = 0 }, .{}, .{});    const buffers = executor.buffers.?;    try std.testing.expectEqual(@as(usize, 2), state.load_count - load_before);    try std.testing.expectEqual(@as(usize, 9), state.launch_count - launch_before);    try std.testing.expectEqual(@as(usize, 1), state.sync_count - sync_before);    try std.testing.expectEqual(@as(usize, 1), state.read_count - read_before);    try std.testing.expectEqual(@as(usize, 1), state.surface_write_count);    try std.testing.expectEqual(frame.id, state.last_written_frame_id.?);    try std.testing.expectEqual(buffers.pixels.id, state.last_surface_write_copy_buffer_id.?);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[0]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), state.last_launch_scalar_u32_values[10]);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = frame,    });    try handle.destroySurface(surface);}test "paint executor prepares image-shadow surface launches with cached expansion" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    const handle = state.handle();    var executor = try Executor.init(allocator, handle, .{});    defer executor.deinit();    const surface = try handle.createSurface(.{        .platform = .{ .headless = .{} },        .extent = .{ .width = 10, .height = 10 },        .format = .rgba8_unorm,        .usage = .{ .present = true, .copy_dst = true },    });    const commands = [_]Command{.{        .kind = .shadow,        .rect = .{ .x = 2, .y = 2, .width = 4, .height = 3 },        .clip = .{ .x = 0, .y = 0, .width = 10, .height = 10 },        .color = .{ .r = 30, .g = 40, .b = 50, .a = 210 },        .radius = 1,        .width = 2,        .order = 4,    }};    const load_before = state.load_count;    const launch_before = state.launch_count;    const sync_before = state.sync_count;    const read_before = state.read_count;    var first = (try executor.prepareCommandsSurfaceLaunchWithImageShadows(commands[0..], surface, .{ .a = 0 }, .{}, .{})).?;    try std.testing.expect(first.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 100), first.pixel_count);    try std.testing.expectEqual(@as(usize, 2), state.load_count - load_before);    try std.testing.expectEqual(@as(usize, 8), state.launch_count - launch_before);    try std.testing.expectEqual(@as(usize, 1), state.sync_count - sync_before);    try std.testing.expectEqual(@as(usize, 1), state.read_count - read_before);    try executor.submitPreparedLaunchQueued(&first);    const first_frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    try executor.writePreparedSurfaceFrame(first_frame, &first);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = first_frame,    });    const load_after_first = state.load_count;    const launch_after_first = state.launch_count;    const sync_after_first = state.sync_count;    const read_after_first = state.read_count;    const write_after_first = state.write_count;    const surface_write_after_first = state.surface_write_count;    var second = (try executor.prepareCommandsSurfaceLaunchWithImageShadows(commands[0..], surface, .{ .a = 0 }, .{}, .{})).?;    try std.testing.expect(!second.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 100), second.pixel_count);    try std.testing.expectEqual(load_after_first, state.load_count);    try std.testing.expectEqual(launch_after_first, state.launch_count);    try std.testing.expectEqual(sync_after_first, state.sync_count);    try std.testing.expectEqual(read_after_first, state.read_count);    try std.testing.expectEqual(write_after_first, state.write_count);    try std.testing.expectEqual(surface_write_after_first, state.surface_write_count);    try executor.submitPreparedLaunchQueued(&second);    const second_frame = try handle.acquireSurfaceFrame(.{ .surface = surface });    try executor.writePreparedSurfaceFrame(second_frame, &second);    try handle.presentSurfaceFrame(.{        .surface = surface,        .frame = second_frame,    });    try std.testing.expectEqual(launch_after_first + 1, state.launch_count);    try std.testing.expectEqual(sync_after_first, state.sync_count);    try std.testing.expectEqual(read_after_first, state.read_count);    try std.testing.expectEqual(surface_write_after_first + 1, state.surface_write_count);    const packed_launch_before = state.launch_count;    const packed_write_before = state.write_count;    const packed_launch = (try executor.prepareCommandsPackedLaunchWithImageShadows(commands[0..], 10, 10, .{ .a = 0 }, .{}, cpu.Region.full(10, 10), .{})).?;    try std.testing.expect(!packed_launch.device_csr_prepared);    try std.testing.expectEqual(@as(usize, 100), packed_launch.pixel_count);    try std.testing.expectEqual(packed_launch_before, state.launch_count);    try std.testing.expectEqual(packed_write_before, state.write_count);    try handle.destroySurface(surface);}test "paint executor reuses image-shadow family kernels across renders" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const commands = [_]Command{.{        .kind = .shadow,        .rect = .{ .x = 2, .y = 2, .width = 4, .height = 3 },        .clip = .{ .x = 0, .y = 0, .width = 10, .height = 10 },        .color = .{ .r = 30, .g = 40, .b = 50, .a = 210 },        .radius = 1,        .width = 2,        .order = 4,    }};    var changed = commands;    changed[0].width = 3;    var pixels = @as([(10 * 10)]u32, @splat(0));    const load_before = state.load_count;    try executor.renderCommandsPackedWithImageShadows(commands[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    const first_load_count = state.load_count;    try std.testing.expectEqual(@as(usize, 2), first_load_count - load_before);    try executor.renderCommandsPackedWithImageShadows(commands[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    try std.testing.expectEqual(first_load_count, state.load_count);    try executor.renderCommandsPackedWithImageShadows(changed[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    try std.testing.expectEqual(first_load_count + 2, state.load_count);}test "paint executor reuses image-shadow expansions across renders" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const commands = [_]Command{.{        .kind = .shadow,        .rect = .{ .x = 2, .y = 2, .width = 4, .height = 3 },        .clip = .{ .x = 0, .y = 0, .width = 10, .height = 10 },        .color = .{ .r = 30, .g = 40, .b = 50, .a = 210 },        .radius = 1,        .width = 2,        .order = 4,    }};    var changed = commands;    changed[0].color.r = 31;    var changed_alpha = changed;    changed_alpha[0].color.a = 211;    var pixels = @as([(10 * 10)]u32, @splat(0));    try executor.renderCommandsPackedWithImageShadows(commands[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    const cached = &executor.shadow_expansion.?;    const cached_commands_ptr = cached.expansion.commands.ptr;    const cached_images_ptr = cached.expansion.image_entries.ptr;    const cached_shadows_ptr = cached.expansion.shadows.ptr;    const cached_shadow_pixels_ptr = cached.expansion.shadows[0].pixels.ptr;    const first_launch_count = state.launch_count;    const first_write_count = state.write_count;    const first_read_count = state.read_count;    try executor.renderCommandsPackedWithImageShadows(commands[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    try std.testing.expectEqual(first_launch_count + 1, state.launch_count);    try std.testing.expectEqual(first_write_count, state.write_count);    try std.testing.expectEqual(first_read_count + 1, state.read_count);    try executor.renderCommandsPackedWithImageShadows(changed[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    const changed_cached = &executor.shadow_expansion.?;    try std.testing.expectEqual(cached_commands_ptr, changed_cached.expansion.commands.ptr);    try std.testing.expectEqual(cached_images_ptr, changed_cached.expansion.image_entries.ptr);    try std.testing.expectEqual(cached_shadows_ptr, changed_cached.expansion.shadows.ptr);    try std.testing.expectEqual(cached_shadow_pixels_ptr, changed_cached.expansion.shadows[0].pixels.ptr);    try expectShadowPixelsTinted(changed_cached.expansion.shadows[0].pixels, changed[0].color);    try std.testing.expectEqual(first_launch_count + 2, state.launch_count);    try std.testing.expectEqual(first_write_count + 1, state.write_count);    try std.testing.expectEqual(first_read_count + 2, state.read_count);    try executor.renderCommandsPackedWithImageShadows(changed[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    try std.testing.expectEqual(cached_commands_ptr, executor.shadow_expansion.?.expansion.commands.ptr);    try std.testing.expectEqual(first_launch_count + 3, state.launch_count);    try std.testing.expectEqual(first_write_count + 1, state.write_count);    try std.testing.expectEqual(first_read_count + 3, state.read_count);    try executor.renderCommandsPackedWithImageShadows(changed_alpha[0..], .{        .width = 10,        .height = 10,        .pixels = pixels[0..],    }, .{ .a = 0 }, .{}, .{});    try std.testing.expect(cached_commands_ptr != executor.shadow_expansion.?.expansion.commands.ptr);}test "paint executor converts readback into rgba8 targets" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .cuda,        .format = .cuda_ptx,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    var rgba = @as([(4 * 4)]u8, @splat(0xff));    const commands = [_]Command{};    try executor.renderCommands(commands[0..], .{ .width = 2, .height = 2, .rgba8 = rgba[0..] }, .{        .r = 9,        .g = 8,        .b = 7,        .a = 6,    });    try std.testing.expectEqual(@as(usize, 1), state.write_count);    try std.testing.expectEqual(@as(usize, 1), state.launch_count);    try std.testing.expectEqual(@as(u32, 0), state.last_launch_scalar_u32_values[0]);    try std.testing.expectEqual(@as(u32, 9), state.last_launch_scalar_u32_values[1]);    try std.testing.expectEqual(@as(u32, 8), state.last_launch_scalar_u32_values[2]);    try std.testing.expectEqual(@as(u32, 7), state.last_launch_scalar_u32_values[3]);    try std.testing.expectEqual(@as(u32, 6), state.last_launch_scalar_u32_values[4]);    try std.testing.expectEqual(@as(u32, 0), state.last_launch_scalar_u32_values[5]);    try std.testing.expectEqual(@as(u32, 2), state.last_launch_scalar_u32_values[7]);    try std.testing.expectEqual(@as(u32, 4), state.last_launch_scalar_u32_values[8]);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[9]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), state.last_launch_scalar_u32_values[10]);    try std.testing.expectEqualSlices(u8, &@as([(4 * 4)]u8, @splat(0)), rgba[0..]);}test "paint executor reads full reused pixel buffers" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const commands = [_]Command{};    var large = @as([16]u32, @splat(0xffff_ffff));    var small = @as([4]u32, @splat(0xffff_ffff));    try executor.renderCommandsPacked(commands[0..], .{ .width = 4, .height = 4, .pixels = large[0..] }, .{ .a = 0 });    try std.testing.expectEqual(@as(usize, 16 * @sizeOf(u32)), state.last_read_byte_count);    try executor.renderCommandsPacked(commands[0..], .{ .width = 2, .height = 2, .pixels = small[0..] }, .{ .a = 0 });    try std.testing.expectEqual(@as(usize, 16 * @sizeOf(u32)), state.last_read_byte_count);    try std.testing.expectEqualSlices(u32, &@as([4]u32, @splat(0)), small[0..]);}test "paint executor uploads image metadata and pixels" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const image_pixels = [_]u32{        cpu.packRgba(.{ .r = 10, .g = 20, .b = 30, .a = 255 }),        cpu.packRgba(.{ .r = 40, .g = 50, .b = 60, .a = 255 }),        cpu.packRgba(.{ .r = 70, .g = 80, .b = 90, .a = 255 }),        cpu.packRgba(.{ .r = 100, .g = 110, .b = 120, .a = 255 }),    };    const images = ImageSet{ .images = &.{.{ .width = 2, .height = 2, .pixels = image_pixels[0..] }} };    const changed_image_pixels = [_]u32{        cpu.packRgba(.{ .r = 10, .g = 20, .b = 30, .a = 255 }),        cpu.packRgba(.{ .r = 40, .g = 50, .b = 61, .a = 255 }),        cpu.packRgba(.{ .r = 70, .g = 80, .b = 90, .a = 255 }),        cpu.packRgba(.{ .r = 100, .g = 110, .b = 120, .a = 255 }),    };    const changed_images = ImageSet{ .images = &.{.{ .width = 2, .height = 2, .pixels = changed_image_pixels[0..] }} };    const second_image_pixels = [_]u32{        cpu.packRgba(.{ .r = 1, .g = 2, .b = 3, .a = 255 }),        cpu.packRgba(.{ .r = 4, .g = 5, .b = 6, .a = 255 }),        cpu.packRgba(.{ .r = 7, .g = 8, .b = 9, .a = 255 }),        cpu.packRgba(.{ .r = 10, .g = 11, .b = 12, .a = 255 }),    };    const two_images = ImageSet{ .images = &.{        .{ .width = 2, .height = 2, .pixels = image_pixels[0..] },        .{ .width = 2, .height = 2, .pixels = second_image_pixels[0..] },    } };    const commands = [_]Command{.{        .kind = .image,        .rect = .{ .x = 0, .y = 0, .width = 2, .height = 2 },        .clip = .{ .x = 0, .y = 0, .width = 2, .height = 2 },        .source = .{ .x = 0, .y = 0, .width = 2, .height = 2 },        .color = .{ .r = 255, .g = 255, .b = 255, .a = 255 },        .image_index = 0,    }};    var pixels = @as([4]u32, @splat(0));    var larger_pixels = @as([16]u32, @splat(0));    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 2, .height = 2, .pixels = pixels[0..] }, .{ .a = 0 }, images);    try std.testing.expectEqual(@as(usize, 6), state.write_count);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[0]);    try std.testing.expectEqual(@as(usize, 1 * @sizeOf(u32)), state.last_write_byte_count);    const first_buffers = executor.buffers.?;    try std.testing.expect(first_buffers.image_upload.valid);    try std.testing.expect(try accy_paint.imagesEqualPacked(images, first_buffers.images.metadata, first_buffers.images.pixels));    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 2, .height = 2, .pixels = pixels[0..] }, .{ .a = 0 }, images);    try std.testing.expectEqual(@as(usize, 6), state.write_count);    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = larger_pixels[0..] }, .{ .a = 0 }, images);    try std.testing.expectEqual(@as(usize, 12), state.write_count);    const resized_buffers = executor.buffers.?;    try std.testing.expect(try accy_paint.imagesEqualPacked(images, resized_buffers.images.metadata, resized_buffers.images.pixels));    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = larger_pixels[0..] }, .{ .a = 0 }, changed_images);    try std.testing.expectEqual(@as(usize, 13), state.write_count);    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = larger_pixels[0..] }, .{ .a = 0 }, two_images);    try std.testing.expectEqual(@as(usize, 19), state.write_count);    try executor.renderCommandsPackedWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = larger_pixels[0..] }, .{ .a = 0 }, images);    try std.testing.expectEqual(@as(usize, 21), state.write_count);}test "paint executor launches packed damage region without seed pixels" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    var pixels = @as([(4 * 4)]u32, @splat(0x1122_3344));    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    try executor.renderCommandsPackedRegionWithImages(commands[0..], .{ .width = 4, .height = 4, .pixels = pixels[0..] }, .{ .a = 0 }, .{}, .{ .x = 1, .y = 1, .width = 2, .height = 2 });    try std.testing.expectEqual(@as(usize, 4), state.write_count);    try std.testing.expectEqual(@as(usize, 11), state.last_launch_scalar_count);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[5]);    try std.testing.expectEqual(@as(u32, 1), state.last_launch_scalar_u32_values[6]);    try std.testing.expectEqual(@as(u32, 2), state.last_launch_scalar_u32_values[7]);    try std.testing.expectEqual(@as(u32, 4), state.last_launch_scalar_u32_values[8]);    try std.testing.expectEqual(@as(u32, @backingInt(accy_paint.OutputFormat.rgba)), state.last_launch_scalar_u32_values[10]);    try std.testing.expectEqual(@as(usize, 4 * @sizeOf(u32)), state.last_read_byte_count);}test "paint executor reuses device CSR buffers across same shape renders" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .vulkan,        .format = .vulkan_spirv,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    var pixels = @as([16]u32, @splat(0));    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 = 7, .g = 8, .b = 9, .a = 255 },    }};    try executor.renderCommandsPacked(commands[0..], .{ .width = 4, .height = 4, .pixels = pixels[0..] }, .{ .a = 0 });    const buffer_allocate_count = state.buffer_allocate_count;    const first_buffers = executor.buffers.?;    const ranges_id = first_buffers.tile_ranges.?.id;    const counts_id = first_buffers.tile_counts.?.id;    const offsets_id = first_buffers.tile_offsets.id;    const cursors_id = first_buffers.tile_cursors.?.id;    const indices_id = first_buffers.tile_indices.id;    const sums_id = first_buffers.scan_sums.?.id;    const bases_id = first_buffers.scan_bases.?.id;    try executor.renderCommandsPacked(commands[0..], .{ .width = 4, .height = 4, .pixels = pixels[0..] }, .{ .a = 0 });    const second_buffers = executor.buffers.?;    try std.testing.expectEqual(buffer_allocate_count, state.buffer_allocate_count);    try std.testing.expectEqual(ranges_id, second_buffers.tile_ranges.?.id);    try std.testing.expectEqual(counts_id, second_buffers.tile_counts.?.id);    try std.testing.expectEqual(offsets_id, second_buffers.tile_offsets.id);    try std.testing.expectEqual(cursors_id, second_buffers.tile_cursors.?.id);    try std.testing.expectEqual(indices_id, second_buffers.tile_indices.id);    try std.testing.expectEqual(sums_id, second_buffers.scan_sums.?.id);    try std.testing.expectEqual(bases_id, second_buffers.scan_bases.?.id);}test "paint executor grows target buffers without rebuilding artifact" {    const allocator = std.testing.allocator;    var state = gpu.recording.BackendState{        .allocator = allocator,        .kind = .metal,        .format = .metal_msl,    };    var executor = try Executor.init(allocator, state.handle(), .{});    defer executor.deinit();    const load_count = state.load_count;    var small = @as([4]u32, @splat(0));    var large = @as([64]u32, @splat(0));    const commands = [_]Command{.{        .kind = .fill,        .rect = .{ .x = 0, .y = 0, .width = 8, .height = 8 },        .clip = .{ .x = 0, .y = 0, .width = 8, .height = 8 },        .color = .{ .r = 7, .g = 8, .b = 9, .a = 255 },    }};    try executor.renderCommandsPacked(commands[0..], .{ .width = 2, .height = 2, .pixels = small[0..] }, .{ .a = 0 });    try executor.renderCommandsPacked(commands[0..], .{ .width = 8, .height = 8, .pixels = large[0..] }, .{ .a = 0 });    try std.testing.expectEqual(@as(usize, 4), state.create_count);    try std.testing.expectEqual(load_count, state.load_count);    try std.testing.expectEqual(@as(usize, 24), state.buffer_allocate_count);    try std.testing.expect(state.destroy_count >= 11);}

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

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

Complete caller list for paint.Executor.deinit

35 direct callers.

Complete caller list for paint.Executor.init

36 direct callers.

Complete caller list for paint.Executor.prepareCommandsPackedLaunch

16 direct callers.

Complete caller list for paint.Executor.submitPreparedLaunchQueued

10 direct callers.

Complete caller list for paint.Executor.writePreparedSurfaceFrame

8 direct callers.

Audit

Definitions50
Public names99
Members80
Version26.7.0
Revisiondaab053ee433