///|
extern "js" fn webgpu_pop_clip_rect() -> Unit =
  #| () => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (rt) {
  #|     rt.popClip2d();
  #|   }
  #| }

///|
extern "js" fn webgpu_reset_clip_stack() -> Unit =
  #| () => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (rt) {
  #|     rt.resetClip2d();
  #|   }
  #| }

///|
extern "js" fn webgpu_clear_text_textures() -> Unit =
  #| () => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return;
  #|   for (const record of rt.textCache.values()) {
  #|     try { record.texture?.destroy(); } catch (_err) {}
  #|   }
  #|   rt.textCache.clear();
  #| }

///|
extern "js" fn webgpu_push_color_vertices_2d(
  vertices : Array[Double],
  pipeline_code : Int,
) -> Unit =
  #| (vertices, pipelineCode) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (rt) {
  #|     rt.pushColor2d(new Float32Array(vertices), pipelineCode);
  #|   }
  #| }

///|
extern "js" fn webgpu_image_dimensions(path : String) -> Array[Double] =
  #| (path) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return [0, 0, 0];
  #|   const rec = rt.ensureImage(path);
  #|   if (!rec || rec.state !== 'ready') return [0, 0, 0];
  #|   return [1, rec.width, rec.height];
  #| }

///|
extern "js" fn webgpu_push_texture_vertices_2d(
  path : String,
  sampler_code : Int,
  vertices : Array[Double],
  pipeline_code : Int,
) -> Unit =
  #| (path, samplerCode, vertices, pipelineCode) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return;
  #|   const rec = rt.ensureImage(path);
  #|   if (!rec || rec.state !== 'ready') return;
  #|   rt.pushTex2d(samplerCode, rec, new Float32Array(vertices), pipelineCode);
  #| }

///|
extern "js" fn webgpu_text_texture_dimensions(
  text : String,
  family : String,
  size : Double,
  color_r : Double,
  color_g : Double,
  color_b : Double,
  color_a : Double,
) -> Array[Double] =
  #| (text, family, size, r, g, bcol, alpha) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return [0, 0, 0];
  #|   const rec = rt.ensureTextTexture(text, family, size, r, g, bcol, alpha);
  #|   if (!rec) return [0, 0, 0];
  #|   return [1, rec.width, rec.height];
  #| }

///|
extern "js" fn webgpu_push_text_vertices_2d(
  text : String,
  family : String,
  size : Double,
  color_r : Double,
  color_g : Double,
  color_b : Double,
  color_a : Double,
  vertices : Array[Double],
  pipeline_code : Int,
) -> Unit =
  #| (text, family, size, r, g, bcol, alpha, vertices, pipelineCode) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return;
  #|   const rec = rt.ensureTextTexture(text, family, size, r, g, bcol, alpha);
  #|   if (!rec) return;
  #|   rt.pushTex2d(0, rec, new Float32Array(vertices), pipelineCode);
  #| }

///|
extern "js" fn webgpu_upload_text_texture(
  key : String,
  width : Int,
  height : Int,
  pixels : Array[Int],
) -> Bool =
  #| (key, width, height, pixels) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return false;
  #|   try {
  #|     return !!rt.uploadTextTexture?.(key, width, height, pixels);
  #|   } catch (_err) {
  #|     return false;
  #|   }
  #| }

///|
extern "js" fn webgpu_push_cached_text_vertices_2d(
  key : String,
  vertices : Array[Double],
  pipeline_code : Int,
) -> Unit =
  #| (key, vertices, pipelineCode) => {
  #|   const rt = globalThis.__selene_webgpu_runtime;
  #|   if (!rt) return;
  #|   const rec = rt.textCache.get(key);
  #|   if (!rec) return;
  #|   rt.pushTex2d(0, rec, new Float32Array(vertices), pipelineCode);
  #| }