feat(render3d): gpu_vk_draw.ludic - Vulkan programs, pipelines, uniform blocks and passes

The drawing half of the Vulkan backend, compiled into render3d and not yet reached from it:

- gvk_program: a program handle as its manifest variant - two SPIR-V modules, a descriptor set
  layout (the vertex block at 0, the fragment block at 1, the samplers at their manifest bindings)
  and a pipeline layout. Nothing is compiled at run time.
- gvk_pipeline: one pipeline per program, recorded vertex layout, render state and pass formats,
  built the first time that combination draws. Attributes the shader does not read are left out;
  the front face is clockwise, since neither API flips y between clip space and its target rows.
- gvk_uniform / gvk_u_set / gvk_bind_texture: loose uniforms written into each stage's block at
  the manifest's offsets and array strides; gvk_draw_set copies the blocks into a per-frame ring
  at the device's alignment and fills a descriptor set from a per-frame pool, with a white 1x1
  texture for a sampler nothing was bound to.
- The frame: one command buffer; a framebuffer bind ends the pass and the next begins at its first
  clear or draw (a clear that comes first is the load op); attachments move to attachment layouts
  for the pass and back to SHADER_READ_ONLY after it, a cascade drawn through a view of its layer.
  gvk_present submits and waits; gvk_screenshot reads the screen image back bottom row first.

r3d.ludic now imports gpu_manifest.ludic too. Textures remember their size.

OpenGL frames byte-identical at the five viewpoints; 59 self-tests pass; VKRES OK and VKDEVICE OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 12:11:03 +03:00
parent 4fc4768083
commit 451bc9063c
3 changed files with 893 additions and 0 deletions

View file

@ -50,6 +50,8 @@ var gvk_tex_mem: []long = null
var gvk_tex_levels: []int = null
var gvk_tex_layers: []int = null
var gvk_tex_vkfmt: []int = null
var gvk_tex_dims_w: []int = null
var gvk_tex_dims_h: []int = null
var gvk_unpack_swap: bool = false # GL_UNPACK_SWAP_BYTES: 16-bit PNG samples arrive big-endian
function gvk_tex_new() -> int {
@ -57,12 +59,15 @@ function gvk_tex_new() -> int {
if gvk_tex_image == null {
gvk_tex_image = new []long; gvk_tex_view = new []long; gvk_tex_mem = new []long
gvk_tex_levels = new []int; gvk_tex_layers = new []int; gvk_tex_vkfmt = new []int
gvk_tex_dims_w = new []int; gvk_tex_dims_h = new []int
push(gvk_tex_dims_w, 0); push(gvk_tex_dims_h, 0)
# handle 0 is "no texture", as it is on OpenGL
push(gvk_tex_image, zero); push(gvk_tex_view, zero); push(gvk_tex_mem, zero)
push(gvk_tex_levels, 0); push(gvk_tex_layers, 0); push(gvk_tex_vkfmt, 0)
}
push(gvk_tex_image, zero); push(gvk_tex_view, zero); push(gvk_tex_mem, zero)
push(gvk_tex_levels, 0); push(gvk_tex_layers, 0); push(gvk_tex_vkfmt, 0)
push(gvk_tex_dims_w, 0); push(gvk_tex_dims_h, 0)
return len(gvk_tex_image) - 1
}
@ -185,6 +190,7 @@ function gvk_tex_storage(tex: int, array: bool, ifmt: int, w: int, h: int, layer
if r != VK_SUCCESS { return gvk_fail("vkCreateImageView", r) }
gvk_tex_image[tex] = image; gvk_tex_view[tex] = gvk_handle(out); gvk_tex_mem[tex] = mem
gvk_tex_levels[tex] = levels; gvk_tex_layers[tex] = layers; gvk_tex_vkfmt[tex] = vkfmt
gvk_tex_dims_w[tex] = w; gvk_tex_dims_h[tex] = h
# a target starts cleared-to-nothing but readable: every level in the layout samplers expect
let cb = gvk_once_begin()
gvk_barrier(cb, image, depth, 0, levels, layers, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)