///|
/// Benchmarks for the 2D primitive builders. These are the per-quad path the
/// scene graph uses: `@scene.render_rect` calls `append_rect_fill` once per
/// rect, so a frame of a 2D game pays this N times.

///|
fn bench_primitive_frame() -> RenderFrame2D {
  RenderFrame2D::new(
    dst=@gfx.new_image_handle(1, 320, 240),
    shader=@gfx.new_shader_handle(2, "primitives_bench"),
    screen_w=320,
    screen_h=240,
  )
}

///|
test "bench append_rect_fill 1000" (b : @bench.T) {
  let frame = bench_primitive_frame()
  let color = color_from_hex(0x4A4A6A)
  b.bench(name="primitives/append_rect_fill_1000", fn() {
    let cmds : Array[@gfx.DrawTrianglesCommand] = []
    for i in 0..<1000 {
      let x = (i % 40).to_double() * 8.0
      let y = (i / 40).to_double() * 8.0
      append_rect_fill(
        cmds,
        frame,
        RectFill2D::new(x~, y~, w=6.0, h=6.0, color~),
      )
    }
    b.keep(cmds.length())
  })
}

///|
/// Floor for the bench above: everything `append_rect_fill` does except the
/// geometry and the command. Subtract to price the builder itself.
test "bench append_rect_fill floor 1000" (b : @bench.T) {
  let color = color_from_hex(0x4A4A6A)
  b.bench(name="primitives/append_rect_fill_floor_1000", fn() {
    let cmds : Array[@gfx.DrawTrianglesCommand] = []
    let mut acc = 0.0
    for i in 0..<1000 {
      let x = (i % 40).to_double() * 8.0
      let y = (i / 40).to_double() * 8.0
      let rect = RectFill2D::new(x~, y~, w=6.0, h=6.0, color~)
      acc = acc + rect.x + rect.y
    }
    b.keep(cmds.length() + acc.to_int())
  })
}

///|
test "bench append_rect_fill_geometry 1000" (b : @bench.T) {
  let frame = bench_primitive_frame()
  let color = color_from_hex(0x4A4A6A)
  b.bench(name="primitives/append_rect_fill_geometry_1000", fn() {
    let vertices : Array[Double] = []
    let indices : Array[Int] = []
    for i in 0..<1000 {
      let x = (i % 40).to_double() * 8.0
      let y = (i / 40).to_double() * 8.0
      append_rect_fill_geometry(
        vertices,
        indices,
        frame,
        RectFill2D::new(x~, y~, w=6.0, h=6.0, color~),
      )
    }
    b.keep(vertices.length() + indices.length())
  })
}

///|
test "bench append_dot_text 200 glyphs" (b : @bench.T) {
  let frame = bench_primitive_frame()
  let color = color_from_hex(0xFFFFFF)
  let chars : Array[Int] = []
  for i in 0..<20 {
    chars.push(i % 10)
  }
  b.bench(name="primitives/append_dot_text_10x20", fn() {
    let cmds : Array[@gfx.DrawTrianglesCommand] = []
    for i in 0..<10 {
      append_dot_text(
        cmds,
        frame,
        DotText2D::new(
          chars~,
          cx=4.0,
          cy=i.to_double() * 10.0,
          color~,
          scale=1.0,
        ),
      )
    }
    b.keep(cmds.length())
  })
}

///|
/// The same call as `scene/append_dot_text_direct_12` in
/// `engine/scene`: 12 runs of "SCORE 12345" (102 lit dots each) at
/// the same size and position.
///
/// The pair is a reproducer, not a measurement. Under `moon bench -p` the two
/// disagree by about 2.5x; under `moon bench` over the workspace they agree.
/// This one is the side that moves, so its number is only meaningful next to
/// the other -- see `docs/performance/dot-text-batching.md`. The gate reports
/// it as NOISY (its own spread across runs is 3.12x, wider than the 1.5x
/// threshold), which is correct: it cannot be gated.
test "bench append_dot_text scene content 12" (b : @bench.T) {
  let frame = bench_primitive_frame()
  let color = color_from_hex(0xFFFFFF)
  // "SCORE 12345" as the scene renderer's `string_to_char_codes` maps it:
  // letters stay ASCII, digits become their value.
  let chars = [83, 67, 79, 82, 69, 32, 1, 2, 3, 4, 5]
  b.bench(name="primitives/append_dot_text_scene_content_12", fn() {
    let cmds : Array[@gfx.DrawTrianglesCommand] = []
    for i in 0..<12 {
      append_dot_text(
        cmds,
        frame,
        DotText2D::new(
          chars~,
          cx=8.0,
          cy=20.0 + i.to_double() * 12.0,
          color~,
          scale=1.0,
        ),
      )
    }
    b.keep(cmds.length())
  })
}