#import "modules/std.sx";
#import "modules/gpu/types.sx";

// GPU is the rendering-API abstraction. Concrete backends live as siblings
// of this file: `metal.sx` (iOS, eventually macOS), `vulkan.sx` (Linux/
// Android, plus macOS via MoltenVK), `webgpu.sx` (wasm). The SDL-backed
// GL renderer used by the desktop+wasm path stays as-is until those
// backends land.

GPU :: protocol {
    // Bind the GPU to a backend-specific render target (e.g. a
    // CAMetalLayer on iOS). pixel_w/pixel_h are the drawable's pixel
    // dimensions; call resize when they change.
    init :: (target: *void, pixel_w: s32, pixel_h: s32) -> bool;
    shutdown :: ();

    resize :: (pixel_w: s32, pixel_h: s32);

    begin_frame :: (clear: ClearColor) -> bool;

    // target_time is the host clock time at which the drawable should be
    // presented (units match the platform's CADisplayLink.targetTimestamp
    // on Apple). Metal forwards it to presentDrawable:atTime: to cap the
    // pipeline at one frame so the inset slide lands on the same vsync as
    // UIKit's keyboard view. GL backends ignore it.
    end_frame :: (target_time: f64);

    create_shader  :: (vsrc: string, fsrc: string) -> ShaderHandle;
    create_buffer  :: (size_bytes: s64) -> BufferHandle;
    update_buffer  :: (buf: BufferHandle, data: *void, size_bytes: s64);
    // Sub-buffer write at a byte offset. Required for Metal where re-using
    // the same buffer slice across multiple draws in a single command
    // encoder is a race: the GPU executes draws asynchronously and reads
    // shared-storage buffer contents at execution time, so the LAST writer
    // wins if every flush targets offset 0. Renderers that issue more than
    // one draw per frame must advance their write offset between flushes.
    update_buffer_at :: (buf: BufferHandle, data: *void, size_bytes: s64, byte_offset: s64);
    create_texture :: (w: s32, h: s32, format: TextureFormat, pixels: *void) -> TextureHandle;
    update_texture_region :: (tex: TextureHandle, x: s32, y: s32, w: s32, h: s32, pixels: *void);

    // Release a GPU resource. Implementations release the backing object and
    // null the slot so the handle becomes inert. Calling with handle 0 or
    // an already-destroyed handle is a no-op. Handles are not re-used; the
    // backing List entry stays at its index with a null sentinel.
    destroy_shader  :: (sh: ShaderHandle);
    destroy_buffer  :: (buf: BufferHandle);
    destroy_texture :: (tex: TextureHandle);

    set_shader           :: (sh: ShaderHandle);
    set_vertex_buffer    :: (buf: BufferHandle);
    set_texture          :: (slot: u32, tex: TextureHandle);
    set_vertex_constants :: (slot: u32, data: *void, size_bytes: s64);
    set_scissor          :: (x: s32, y: s32, w: s32, h: s32);
    disable_scissor      :: ();

    draw_triangles :: (vertex_offset: s32, vertex_count: s32);
}
