ludic/runtime/native/vk.ludic
Orkuncakilkaya 750d13779d feat(render3d): mesh-shader grass
The chunked grass path draws each chunk as one mesh-shader dispatch when the
setting asks and the card has VK_EXT_mesh_shader: work group y is a tile, x a
batch of 16 of its blades, and a blade the placement, density, frustum, water
or slope tests reject emits nothing - the instanced path still runs its eight
vertices to a degenerate position. grass.mesh generates the same blades as
grass.vert (grass_blade_mesh(4)'s rows, the same hashes, sway and lighting
normal), capped at the instanced path's 65535 a tile.

Vulkan: VK_EXT_mesh_shader with meshShader, and maintenance4 (glslang's mesh
stages declare LocalSizeId); vkCmdDrawMeshTasksEXT looked up per device, as the
Streamline interposer exports none; a *.mesh program's pipeline takes the mesh
stage and no vertex input, its bindings the mesh stage bit. gpu_has_mesh,
gpu_draw_mesh_tasks; r3d_mesh_grass and R3D_MESH_GRASS / R3D_NO_MESH.
bin/ludic-dev rebuilt: the committed binary predated the shader tool's mesh
support and compiled grass.mesh as a vertex stage.

PC (RTX 3070 Ti): the camp matches the chunked path; validation only the
no-window present-id message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 20:20:42 +03:00

48 lines
3.7 KiB
Text

# ============================================================================
# vk.ludic — Vk.*: Vulkan for Ludic.
#
# Every command of Vulkan 1.0-1.4 and of the extensions the render3d Vulkan renderer
# is built around is bound in vk_api.ludic (generated by `ludic-dev vkgen` from the
# Vulkan registry), with every constant and every struct's size and field offsets.
# Vk.<snake_name>(...) calls one: Vk.create_instance(info, null, out). Structs are
# plain memory filled by field name:
# let ci = bytes(VkInstanceCreateInfo_sizeof)
# Vk.zero(ci, VkInstanceCreateInfo_sizeof)
# Vk.put_i32(ci, VkInstanceCreateInfo_sType, VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO)
#
# Numbers: a C float is float bits in an int; uint64_t, VkDeviceSize and
# non-dispatchable handles are `long`. A negative long has to come from a `long`
# variable - an int literal passed straight to a long parameter is zero-extended.
#
# The loader is opened at run time by Vk.open() (vk_win.ll / vk_mac.ll), never
# linked: 0 means this machine has no Vulkan, and the program carries on without it.
# ============================================================================
import "vk_api.ludic"
# Streamline (NVIDIA DLSS, Ray Reconstruction, Frame Generation, Reflex), Windows only: Vk.sl_prefer(1)
# before Vk.open() loads sl.interposer.dll as the Vulkan loader when it is beside the program, and
# Vk.sl_active() says whether it was. Each Vk.sl_* is the sl* export of the same name and returns
# its sl::Result (0 is eOk); -1 means there is no Streamline (macOS, or the DLL missing). The
# structs are plain memory, laid out as the SDK headers (include/sl_*.h, SDK 2.14.1) declare them.
extern function vk_sl_prefer(on: int) = "lvk_sl_prefer"
extern function vk_sl_active() -> int = "lvk_sl_active"
extern function vk_sl_init(pref: pointer, sdk_version: long) -> int = "lsl_slInit"
extern function vk_sl_shutdown() -> int = "lsl_slShutdown"
extern function vk_sl_is_feature_supported(feature: int, adapter_info: pointer) -> int = "lsl_slIsFeatureSupported"
extern function vk_sl_is_feature_loaded(feature: int, loaded: pointer) -> int = "lsl_slIsFeatureLoaded"
extern function vk_sl_get_feature_requirements(feature: int, requirements: pointer) -> int = "lsl_slGetFeatureRequirements"
extern function vk_sl_get_feature_function(feature: int, name: pointer, function_out: pointer) -> int = "lsl_slGetFeatureFunction"
extern function vk_sl_get_new_frame_token(token_out: pointer, frame_index: pointer) -> int = "lsl_slGetNewFrameToken"
extern function vk_sl_set_tag_for_frame(frame: pointer, viewport: pointer, tags: pointer, num_tags: int, cmd_buffer: pointer) -> int = "lsl_slSetTagForFrame"
extern function vk_sl_set_constants(constants: pointer, frame: pointer, viewport: pointer) -> int = "lsl_slSetConstants"
extern function vk_sl_evaluate_feature(feature: int, frame: pointer, inputs: pointer, num_inputs: int, cmd_buffer: pointer) -> int = "lsl_slEvaluateFeature"
extern function vk_sl_free_resources(feature: int, viewport: pointer) -> int = "lsl_slFreeResources"
# a feature function obtained with Vk.sl_get_feature_function, called with its arguments
extern function vk_sl_call_p(fn: pointer, a: pointer) -> int = "lsl_call_p"
extern function vk_sl_call_pp(fn: pointer, a: pointer, b: pointer) -> int = "lsl_call_pp"
extern function vk_sl_call_ppp(fn: pointer, a: pointer, b: pointer, c: pointer) -> int = "lsl_call_ppp"
extern function vk_sl_call_ip(fn: pointer, a: int, b: pointer) -> int = "lsl_call_ip"
# a command-buffer command with three counts through a pointer (vkCmdDrawMeshTasksEXT, which the
# Streamline interposer does not export; looked up with vkGetDeviceProcAddr)
extern function vk_sl_call_piii(fn: pointer, a: pointer, b: int, c: int, d: int) = "lsl_call_piii"