package render

// Generated by Gododin. Do not edit.
import "core:mem"
import "core:sync"

import raw "../../raw"
import godot "../godot"

BOUND_API_SCHEMA_VERSION :: 4
MAX_UNIFORM_BINDINGS :: 8

Call_Role :: enum u8 {
	Invalid,
	Main,
	Render,
}

Call_Error :: enum u8 {
	None,
	Stale_Generation,
	Wrong_Role,
	Missing_API,
	Invalid_Handle,
	Invalid_Owner,
	Wrong_Device,
	Wrong_RID_Kind,
	Out_Of_Range,
	Invalid_State,
	Godot_Error,
	Allocation_Failed,
	Synchronization_Required,
}

Device_Kind :: enum u8 {
	Invalid,
	Global,
	Local,
}

RID_Kind :: enum u8 {
	Invalid,
	Shader,
	Storage_Buffer,
	Uniform_Set,
	Compute_Pipeline,
	Texture_Borrow,
}

Uniform_Type :: enum i64 {
	Image          = 3,
	Storage_Buffer = 8,
}

Rendering_Server_Borrow :: distinct raw.GDExtensionObjectPtr
Rendering_Device_Object :: distinct raw.GDExtensionObjectPtr
Render_Data_Borrow :: distinct raw.GDExtensionObjectPtr
Render_Scene_Buffers_Borrow :: distinct raw.GDExtensionObjectPtr
Render_Scene_Buffers_RD_Borrow :: distinct raw.GDExtensionObjectPtr
RD_Shader_SPIRV_Borrow :: distinct raw.GDExtensionObjectPtr
RD_Shader_Source_Borrow :: distinct raw.GDExtensionObjectPtr

Owned_Ref :: struct {
	value: raw.GDExtensionObjectPtr,
	live:  bool,
}

Owned_Local_Device :: struct {
	object:                 raw.GDExtensionObjectPtr,
	identity:               uintptr,
	live:                   bool,
	outstanding_rids:       u32,
	work_recorded:          bool,
	submitted:              bool,
	synchronized:           bool,
}

Device_Borrow :: struct {
	object:      raw.GDExtensionObjectPtr,
	kind:        Device_Kind,
	identity:    uintptr,
	creator:     Call_Role,
	local_owner: ^Owned_Local_Device,
}

Owned_RID :: struct {
	value:        raw.RID,
	kind:         RID_Kind,
	device_kind:  Device_Kind,
	device:       uintptr,
	producer_id:  u64,
	creator_role: Call_Role,
	local_owner:  ^Owned_Local_Device,
	live:         bool,
}

Borrowed_RID :: struct {
	value:        raw.RID,
	kind:         RID_Kind,
	device_kind:  Device_Kind,
	device:       uintptr,
	producer_id:  u64,
	creator_role: Call_Role,
	local_owner:  ^Owned_Local_Device,
}

Compute_List :: struct {
	value:        i64,
	device_kind:  Device_Kind,
	device:       uintptr,
	creator_role: Call_Role,
	local_owner:  ^Owned_Local_Device,
	open:         bool,
}

Packed_Byte_Array :: struct {
	value: raw.PackedByteArray,
	live:  bool,
}

Packed_Float32_Array :: struct {
	value: raw.PackedFloat32Array,
	live:  bool,
}

Vector2i :: struct {
	x: i32,
	y: i32,
}

Uniform_Binding :: struct {
	type:    Uniform_Type,
	binding: u32,
	rid:     Borrowed_RID,
}

Bound_API :: struct {
	schema_version: u32,
	size:           uintptr,
	generation:     u64,
	published:      bool,

	// The complete generated class-method projection is the only operation
	// surface. This authored package retains the helper interfaces needed by its
	// multi-call owners and reviewed provenance narrowings.
	typed:                          godot.Bound_Interface,
	object_destroy:                raw.raw_godot_interface_object_destroy,
	packed_byte_array_index:       raw.raw_godot_interface_packed_byte_array_operator_index,
	packed_byte_array_index_const: raw.raw_godot_interface_packed_byte_array_operator_index_const,
	string_new_utf8:               raw.raw_godot_interface_string_new_with_utf8_chars_and_len2,
	variant_destroy:               raw.raw_godot_interface_variant_destroy,
	variant_from_object:           raw.GDExtensionVariantFromTypeConstructorFunc,

	array_constructor:             raw.GDExtensionPtrConstructor,
	array_destructor:              raw.GDExtensionPtrDestructor,
	array_append:                  raw.GDExtensionPtrBuiltInMethod,
	packed_byte_array_constructor: raw.GDExtensionPtrConstructor,
	packed_byte_array_destructor:  raw.GDExtensionPtrDestructor,
	packed_byte_array_resize:      raw.GDExtensionPtrBuiltInMethod,
	packed_byte_array_size:        raw.GDExtensionPtrBuiltInMethod,
	packed_float32_array_index:       raw.raw_godot_interface_packed_float32_array_operator_index,
	packed_float32_array_index_const: raw.raw_godot_interface_packed_float32_array_operator_index_const,
	packed_float32_array_constructor: raw.GDExtensionPtrConstructor,
	packed_float32_array_destructor:  raw.GDExtensionPtrDestructor,
	packed_float32_array_resize:      raw.GDExtensionPtrBuiltInMethod,
	packed_float32_array_size:        raw.GDExtensionPtrBuiltInMethod,
	rid_get_id:                    raw.GDExtensionPtrBuiltInMethod,
	rid_is_valid:                  raw.GDExtensionPtrBuiltInMethod,
	string_constructor:            raw.GDExtensionPtrConstructor,
	string_destructor:             raw.GDExtensionPtrDestructor,
}

Call :: struct {
	api:        ^Bound_API,
	generation: u64,
	role:       Call_Role,
}

bound_api_is_complete :: proc(api: ^Bound_API) -> bool {
	if api == nil || api.schema_version != BOUND_API_SCHEMA_VERSION ||
	   api.size != uintptr(size_of(Bound_API)) || api.generation == 0 || !api.published ||
	   api.typed.generation != api.generation || api.typed.published != api.published ||
	   !godot.bound_interface_is_complete(&api.typed) || api.object_destroy == nil ||
	   api.packed_byte_array_index == nil ||
	   api.packed_byte_array_index_const == nil || api.string_new_utf8 == nil ||
	   api.variant_destroy == nil ||
	   api.variant_from_object == nil || api.array_constructor == nil ||
	   api.array_destructor == nil || api.array_append == nil ||
	   api.packed_byte_array_constructor == nil || api.packed_byte_array_destructor == nil ||
	   api.packed_byte_array_resize == nil || api.packed_byte_array_size == nil ||
	   api.packed_float32_array_index == nil || api.packed_float32_array_index_const == nil ||
	   api.packed_float32_array_constructor == nil || api.packed_float32_array_destructor == nil ||
	   api.packed_float32_array_resize == nil || api.packed_float32_array_size == nil ||
	   api.rid_get_id == nil || api.rid_is_valid == nil ||
	   api.string_constructor == nil || api.string_destructor == nil {
		return false
	}
	return true
}

main_call :: proc(api: ^Bound_API) -> Call {
	return {api=api, generation=api.generation if api != nil else 0, role=.Main}
}

render_call :: proc(api: ^Bound_API) -> Call {
	return {api=api, generation=api.generation if api != nil else 0, role=.Render}
}

validate_call :: proc(call: Call) -> Call_Error {
	if call.api == nil || !call.api.published ||
	   call.api.schema_version != BOUND_API_SCHEMA_VERSION ||
	   call.api.size != uintptr(size_of(Bound_API)) ||
	   call.generation == 0 || call.generation != call.api.generation {
		return .Stale_Generation
	}
	if call.role != .Main && call.role != .Render do return .Wrong_Role
	return .None
}

generated_role :: proc(role: Call_Role) -> godot.Call_Role {
	switch role {
	case .Main:   return .Main
	case .Render: return .Render
	case .Invalid: return .Invalid
	}
	return .Invalid
}

generated_call :: proc(call: Call) -> godot.Call {
	return {api=&call.api.typed if call.api != nil else nil, generation=call.generation, role=generated_role(call.role)}
}

generated_error :: proc(error: godot.Call_Error) -> Call_Error {
	switch error {
	case .None:               return .None
	case .Stale_Generation:   return .Stale_Generation
	case .Wrong_Role:         return .Wrong_Role
	case .Missing_Interface:  return .Missing_API
	case .Unbound_Operation:  return .Missing_API
	case .Invalid_Handle:     return .Invalid_Handle
	case .Invalid_Owner:      return .Invalid_Owner
	case .Too_Many_Arguments: return .Out_Of_Range
	case .Godot_Call_Failed:  return .Godot_Error
	}
	return .Godot_Error
}

const_type_pointer :: proc(value: rawptr) -> raw.GDExtensionConstTypePtr {
	return raw.GDExtensionConstTypePtr(value)
}

type_pointer :: proc(value: rawptr) -> raw.GDExtensionTypePtr {
	return raw.GDExtensionTypePtr(value)
}

next_producer_id :: proc(api: ^Bound_API) -> u64 {
	return sync.atomic_add(&api.typed.producer_sequence, u64(1)) + 1
}

// rid_valid is the engine's own RID.is_valid(): a non-zero id check, not a
// liveness probe. A resource the scene has freed keeps its non-zero RID, so
// scene-side invalidation is not observable here — the server itself rejects
// writes to such a RID with its own diagnostic, and rebinding through close
// plus open is the recovery. Scene RID liveness stays a caller constraint,
// like the staged store's retention constraints.
rid_valid :: proc(api: ^Bound_API, value: raw.RID) -> bool {
	if api == nil do return false
	result := raw.GDExtensionBool(0)
	local := value
	api.rid_is_valid(type_pointer(rawptr(&local)), nil, type_pointer(rawptr(&result)), 0)
	return result != 0
}

rid_identity :: proc(api: ^Bound_API, value: Borrowed_RID) -> (u64, Call_Error) {
	if api == nil || value.kind == .Invalid || !rid_valid(api, value.value) do return 0, .Invalid_Handle
	local := value.value
	result: i64
	api.rid_get_id(type_pointer(rawptr(&local)), nil, type_pointer(rawptr(&result)), 0)
	if result <= 0 do return 0, .Invalid_Handle
	return u64(result), .None
}

device_valid :: proc(value: Device_Borrow) -> bool {
	if rawptr(value.object) == nil || value.identity != uintptr(rawptr(value.object)) ||
	   (value.creator != .Main && value.creator != .Render) {
		return false
	}
	if value.kind == .Global do return value.creator == .Render && value.local_owner == nil
	if value.kind == .Local do return value.creator == .Main && value.local_owner != nil &&
		value.local_owner.live && value.local_owner.object == value.object && value.local_owner.identity == value.identity
	return false
}

device_call_valid :: proc(call: Call, device: Device_Borrow) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if !device_valid(device) do return .Invalid_Handle
	if call.role != device.creator do return .Wrong_Role
	return .None
}

owned_rid_borrow :: proc(owner: ^Owned_RID) -> (Borrowed_RID, bool) {
	if owner == nil || !owner.live do return {}, false
	return {
		value=owner.value,
		kind=owner.kind,
		device_kind=owner.device_kind,
		device=owner.device,
		producer_id=owner.producer_id,
		creator_role=owner.creator_role,
		local_owner=owner.local_owner,
	}, true
}

rid_matches_device :: proc(api: ^Bound_API, value: Borrowed_RID, device: Device_Borrow) -> bool {
	return value.kind != .Invalid && value.device_kind == device.kind && value.device == device.identity &&
	       value.creator_role == device.creator && value.local_owner == device.local_owner && rid_valid(api, value.value)
}

new_owned_rid :: proc(
	call: Call,
	device: Device_Borrow,
	kind: RID_Kind,
	generated: godot.Resource_Handle,
) -> (Owned_RID, Call_Error) {
	if !generated.live || generated.producer != device.object ||
	   generated.producer_role != generated_role(call.role) ||
	   !rid_valid(call.api, generated.value) {
		return {}, .Invalid_Handle
	}
	if device.kind == .Local {
		if device.local_owner.outstanding_rids == max(u32) do return {}, .Out_Of_Range
		device.local_owner.outstanding_rids += 1
	}
	return {
		value=generated.value,
		kind=kind,
		device_kind=device.kind,
		device=device.identity,
		producer_id=generated.producer_sequence,
		creator_role=call.role,
		local_owner=device.local_owner,
		live=true,
	}, .None
}

rendering_server :: proc(call: Call) -> (Rendering_Server_Borrow, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	server, generated_failure := godot.singleton_rendering_server(generated_call(call))
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if rawptr(server.ptr) == nil do return {}, .Invalid_Handle
	return Rendering_Server_Borrow(server.ptr), .None
}

rendering_server_get_global_device :: proc(
	call: Call,
	server: Rendering_Server_Borrow,
) -> (Device_Borrow, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	if call.role != .Render do return {}, .Wrong_Role
	if rawptr(server) == nil do return {}, .Invalid_Handle
	server_handle: godot.RenderingServer
	server_handle.ptr = raw.GDExtensionObjectPtr(server)
	result, generated_failure := godot.rendering_server_get_rendering_device(generated_call(call), &server_handle)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if rawptr(result.ptr) == nil do return {}, .Invalid_Handle
	return {object=result.ptr, kind=.Global, identity=uintptr(rawptr(result.ptr)), creator=.Render}, .None
}

rendering_server_create_local_device :: proc(
	call: Call,
	server: Rendering_Server_Borrow,
) -> (Owned_Local_Device, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	if call.role != .Main do return {}, .Wrong_Role
	if rawptr(server) == nil do return {}, .Invalid_Handle
	server_handle: godot.RenderingServer
	server_handle.ptr = raw.GDExtensionObjectPtr(server)
	result, generated_failure := godot.rendering_server_create_local_rendering_device(generated_call(call), &server_handle)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if rawptr(result.ptr) == nil do return {}, .Invalid_Handle
	return {object=result.ptr, identity=uintptr(rawptr(result.ptr)), live=true}, .None
}

owned_local_device_borrow :: proc(owner: ^Owned_Local_Device) -> (Device_Borrow, bool) {
	if owner == nil || !owner.live || owner.object == nil || owner.identity != uintptr(rawptr(owner.object)) do return {}, false
	return {object=owner.object, kind=.Local, identity=owner.identity, creator=.Main, local_owner=owner}, true
}

owned_local_device_destroy :: proc(call: Call, owner: ^Owned_Local_Device) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if call.role != .Main do return .Wrong_Role
	if owner == nil || !owner.live || owner.object == nil do return .Invalid_Owner
	if owner.outstanding_rids != 0 do return .Invalid_State
	if owner.work_recorded && !owner.synchronized do return .Synchronization_Required
	call.api.object_destroy(owner.object)
	owner^ = {}
	return .None
}

ref_counted_release :: proc(call: Call, owner: ^Owned_Ref) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if owner == nil || !owner.live || owner.value == nil do return .Invalid_Owner
	object := owner.value
	handle: godot.RefCounted
	handle.ptr = object
	result, generated_failure := godot.ref_counted_unreference(generated_call(call), &handle)
	if generated_failure != .None do return generated_error(generated_failure)
	owner^ = {}
	if result do call.api.object_destroy(object)
	return .None
}

render_data_get_scene_buffers :: proc(
	call: Call,
	render_data: Render_Data_Borrow,
) -> (Owned_Ref, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	if call.role != .Render do return {}, .Wrong_Role
	if rawptr(render_data) == nil do return {}, .Invalid_Handle
	handle: godot.RenderData
	handle.ptr = raw.GDExtensionObjectPtr(render_data)
	result, generated_failure := godot.render_data_get_render_scene_buffers(generated_call(call), &handle)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if !result.live || rawptr(result.handle.ptr) == nil do return {}, .Invalid_Handle
	return {value=result.handle.ptr, live=true}, .None
}

owned_ref_borrow_render_scene_buffers :: proc(owner: ^Owned_Ref) -> (Render_Scene_Buffers_Borrow, bool) {
	if owner == nil || !owner.live || owner.value == nil do return {}, false
	return Render_Scene_Buffers_Borrow(owner.value), true
}

// Exact Forward+ source guarantees RenderData's owned RenderSceneBuffers
// reference points to RenderSceneBuffersRD. The borrow remains bounded by owner.
render_scene_buffers_rd_from_forward_plus :: proc(
	value: Render_Scene_Buffers_Borrow,
) -> (Render_Scene_Buffers_RD_Borrow, bool) {
	if rawptr(value) == nil do return {}, false
	return Render_Scene_Buffers_RD_Borrow(value), true
}

render_scene_buffers_get_color_layer :: proc(
	call: Call,
	device: Device_Borrow,
	buffers: Render_Scene_Buffers_RD_Borrow,
	layer: u32,
	msaa: bool,
) -> (Borrowed_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if device.kind != .Global || rawptr(buffers) == nil do return {}, .Wrong_Device
	handle: godot.RenderSceneBuffersRD
	handle.ptr = raw.GDExtensionObjectPtr(buffers)
	result, generated_failure := godot.render_scene_buffers_rd_get_color_layer(generated_call(call), &handle, layer, msaa)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if !result.live || result.producer != handle.ptr ||
	   result.producer_role != generated_role(call.role) || !rid_valid(call.api, result.value) {
		return {}, .Invalid_Handle
	}
	return {
		value=result.value,
		kind=.Texture_Borrow,
		device_kind=.Global,
		device=device.identity,
		producer_id=result.producer_sequence,
		creator_role=.Render,
	}, .None
}

render_scene_buffers_get_internal_size :: proc(
	call: Call,
	buffers: Render_Scene_Buffers_RD_Borrow,
) -> (Vector2i, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	if call.role != .Render do return {}, .Wrong_Role
	if rawptr(buffers) == nil do return {}, .Invalid_Handle
	handle: godot.RenderSceneBuffersRD
	handle.ptr = raw.GDExtensionObjectPtr(buffers)
	result, generated_failure := godot.render_scene_buffers_rd_get_internal_size(generated_call(call), &handle)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return transmute(Vector2i)result, .None
}

render_scene_buffers_get_view_count :: proc(
	call: Call,
	buffers: Render_Scene_Buffers_RD_Borrow,
) -> (u32, Call_Error) {
	if error := validate_call(call); error != .None do return 0, error
	if call.role != .Render do return 0, .Wrong_Role
	if rawptr(buffers) == nil do return 0, .Invalid_Handle
	handle: godot.RenderSceneBuffersRD
	handle.ptr = raw.GDExtensionObjectPtr(buffers)
	result, generated_failure := godot.render_scene_buffers_rd_get_view_count(generated_call(call), &handle)
	if generated_failure != .None do return 0, generated_error(generated_failure)
	if result == 0 do return 0, .Out_Of_Range
	return result, .None
}

packed_byte_array_from_bytes :: proc(api: ^Bound_API, bytes: []u8) -> (Packed_Byte_Array, Call_Error) {
	if api == nil || len(bytes) > int(max(u32)) do return {}, .Out_Of_Range
	result := Packed_Byte_Array{live=true}
	api.packed_byte_array_constructor(
		raw.GDExtensionUninitializedTypePtr(rawptr(&result.value)),
		nil,
	)
	if len(bytes) == 0 do return result, .None
	size := i64(len(bytes))
	arguments := [1]raw.GDExtensionConstTypePtr{const_type_pointer(rawptr(&size))}
	resize_result: i64
	api.packed_byte_array_resize(
		type_pointer(rawptr(&result.value)),
		&arguments[0],
		type_pointer(rawptr(&resize_result)),
		1,
	)
	if resize_result != 0 {
		api.packed_byte_array_destructor(type_pointer(rawptr(&result.value)))
		return {}, .Allocation_Failed
	}
	// One acquisition, one copy. The mutable accessor detaches copy-on-write
	// before it returns a pointer, so the write path may use it; the bound comes
	// from the engine's own size rather than from a null return, because a bulk
	// move never probes an out-of-range index.
	base, bound, bound_error := packed_byte_array_write_base(api, &result)
	if bound_error != .None || bound != len(bytes) {
		api.packed_byte_array_destructor(type_pointer(rawptr(&result.value)))
		return {}, .Invalid_State if bound_error == .None else bound_error
	}
	mem.copy_non_overlapping(rawptr(base), raw_data(bytes), len(bytes))
	return result, .None
}

// packed_byte_array_length reads the engine's own size for a packed array. A
// bulk move needs an established bound: the per-element loops this replaced
// derived one from the operator index returning null out of range, which a
// pointer-and-length move does not do.
packed_byte_array_length :: proc(api: ^Bound_API, owner: ^Packed_Byte_Array) -> (int, Call_Error) {
	if api == nil do return 0, .Missing_API
	if owner == nil || !owner.live do return 0, .Invalid_Owner
	result: i64
	api.packed_byte_array_size(
		type_pointer(rawptr(&owner.value)),
		nil,
		type_pointer(rawptr(&result)),
		0,
	)
	if result < 0 || result > i64(max(int)) do return 0, .Out_Of_Range
	return int(result), .None
}

// packed_byte_array_write_base acquires the writable backing store once. The
// mutable accessor detaches copy-on-write, so acquisition itself is safe; the
// hazard this facade guards is post-acquisition aliasing, handled structurally
// by Packed_Byte_Staging.
packed_byte_array_write_base :: proc(api: ^Bound_API, owner: ^Packed_Byte_Array) -> (^u8, int, Call_Error) {
	bound, bound_error := packed_byte_array_length(api, owner)
	if bound_error != .None do return nil, 0, bound_error
	if bound == 0 do return nil, 0, .None
	base := api.packed_byte_array_index(type_pointer(rawptr(&owner.value)), 0)
	if base == nil do return nil, 0, .Invalid_State
	return base, bound, .None
}

// packed_byte_array_read_base acquires the read-only backing store once. It must
// use the const accessor: the mutable one detaches copy-on-write and would force
// exactly the copy this path exists to avoid.
packed_byte_array_read_base :: proc(api: ^Bound_API, owner: ^Packed_Byte_Array) -> (^u8, int, Call_Error) {
	bound, bound_error := packed_byte_array_length(api, owner)
	if bound_error != .None do return nil, 0, bound_error
	if bound == 0 do return nil, 0, .None
	base := api.packed_byte_array_index_const(const_type_pointer(rawptr(&owner.value)), 0)
	if base == nil do return nil, 0, .Invalid_State
	return base, bound, .None
}

// Packed_Byte_Staging retains one packed array for the process lifetime of a
// transfer so a pure Odin subsystem writes into the engine's own backing store
// and the engine performs the only copy. It removes the per-frame allocation and
// the per-element foreign call from the bulk path.
//
// Sole ownership is structural, not a uniqueness test. GDExtension exposes no
// reference count or uniqueness query for a copy-on-write packed array, so an
// invariant written as "invalidate on losing uniqueness" would silently do
// nothing. Instead: the owner never copies the array value, never hands it
// anywhere the engine can retain, re-acquires the base pointer every frame and
// treats a changed pointer as detachment, and invalidates on a changed size.
// Acquisition itself is safe because the mutable accessor detaches first; the
// hazard is entirely post-acquisition aliasing.
//
// The owner declares its thread roles. It is retained in image state, never in a
// Render callback, which gets no retained Host state.
Packed_Byte_Staging :: struct {
	array:          Packed_Byte_Array,
	base:           ^u8,
	length:         int,
	publisher_role: Call_Role,
	consumer_role:  Call_Role,
	generation:     u64,
	published:      u64,
	consumed:       u64,
	live:           bool,
}

packed_byte_staging_create :: proc(
	api: ^Bound_API,
	length: int,
	publisher_role: Call_Role,
	consumer_role: Call_Role,
) -> (Packed_Byte_Staging, Call_Error) {
	if !bound_api_is_complete(api) do return {}, .Missing_API
	if length <= 0 || length > int(max(u32)) do return {}, .Out_Of_Range
	if publisher_role == .Invalid || consumer_role == .Invalid do return {}, .Wrong_Role
	array, array_error := packed_byte_array_zeroed(api, length)
	if array_error != .None do return {}, array_error
	result := Packed_Byte_Staging{
		array=array,
		length=length,
		publisher_role=publisher_role,
		consumer_role=consumer_role,
		generation=api.generation,
		live=true,
	}
	base, bound, base_error := packed_byte_array_write_base(api, &result.array)
	if base_error != .None || bound != length {
		packed_byte_array_destroy(api, &result.array)
		return {}, .Invalid_State if base_error == .None else base_error
	}
	result.base = base
	return result, .None
}

// packed_byte_staging_write returns the writable backing store to the declared
// publisher role. It re-acquires the base pointer and re-reads the size on every
// call, so a detached or resized array fails here rather than corrupting a frame.
packed_byte_staging_write :: proc(
	api: ^Bound_API,
	staging: ^Packed_Byte_Staging,
	role: Call_Role,
) -> ([]u8, Call_Error) {
	if api == nil do return nil, .Missing_API
	if staging == nil || !staging.live || !staging.array.live do return nil, .Invalid_Owner
	if role != staging.publisher_role do return nil, .Wrong_Role
	if api.generation != staging.generation do return nil, .Stale_Generation
	base, bound, base_error := packed_byte_array_write_base(api, &staging.array)
	if base_error != .None {
		staging.live = false
		return nil, base_error
	}
	if base != staging.base || bound != staging.length {
		staging.live = false
		return nil, .Invalid_State
	}
	return mem.slice_ptr(base, bound), .None
}

// packed_byte_staging_publish orders the publisher's writes before the consumer
// observes them. Main publishes, Render consumes, and the release/acquire pair
// is what makes the shared backing store a protocol rather than an
// unsynchronized cross-role alias.
packed_byte_staging_publish :: proc(staging: ^Packed_Byte_Staging, role: Call_Role) -> (u64, Call_Error) {
	if staging == nil || !staging.live do return 0, .Invalid_Owner
	if role != staging.publisher_role do return 0, .Wrong_Role
	next := staging.published + 1
	sync.atomic_store_explicit(&staging.published, next, .Release)
	return next, .None
}

packed_byte_staging_pending :: proc(staging: ^Packed_Byte_Staging, role: Call_Role) -> (u64, bool) {
	if staging == nil || !staging.live do return 0, false
	if role != staging.consumer_role do return 0, false
	published := sync.atomic_load_explicit(&staging.published, .Acquire)
	return published, published != staging.consumed
}

packed_byte_staging_destroy :: proc(api: ^Bound_API, staging: ^Packed_Byte_Staging) -> Call_Error {
	if api == nil do return .Missing_API
	if staging == nil || !staging.live && !staging.array.live do return .Invalid_Owner
	error := Call_Error.None
	if staging.array.live do error = packed_byte_array_destroy(api, &staging.array)
	staging^ = {}
	return error
}

// packed_byte_array_zeroed builds an engine-owned array of the requested length
// without a caller-side source buffer, so a staging owner never allocates a
// second copy just to initialize.
packed_byte_array_zeroed :: proc(api: ^Bound_API, length: int) -> (Packed_Byte_Array, Call_Error) {
	if api == nil || length < 0 || length > int(max(u32)) do return {}, .Out_Of_Range
	result := Packed_Byte_Array{live=true}
	api.packed_byte_array_constructor(
		raw.GDExtensionUninitializedTypePtr(rawptr(&result.value)),
		nil,
	)
	if length == 0 do return result, .None
	size := i64(length)
	arguments := [1]raw.GDExtensionConstTypePtr{const_type_pointer(rawptr(&size))}
	resize_result: i64
	api.packed_byte_array_resize(
		type_pointer(rawptr(&result.value)),
		&arguments[0],
		type_pointer(rawptr(&resize_result)),
		1,
	)
	if resize_result != 0 {
		api.packed_byte_array_destructor(type_pointer(rawptr(&result.value)))
		return {}, .Allocation_Failed
	}
	return result, .None
}

packed_byte_array_destroy :: proc(api: ^Bound_API, owner: ^Packed_Byte_Array) -> Call_Error {
	if api == nil do return .Missing_API
	if owner == nil || !owner.live do return .Invalid_Owner
	api.packed_byte_array_destructor(type_pointer(rawptr(&owner.value)))
	owner^ = {}
	return .None
}

packed_byte_array_read_exact :: proc(api: ^Bound_API, owner: ^Packed_Byte_Array, output: []u8) -> Call_Error {
	if api == nil do return .Missing_API
	if owner == nil || !owner.live do return .Invalid_Owner
	if len(output) == 0 do return .None
	base, bound, bound_error := packed_byte_array_read_base(api, owner)
	if bound_error != .None do return bound_error
	if base == nil || bound < len(output) do return .Out_Of_Range
	mem.copy_non_overlapping(raw_data(output), rawptr(base), len(output))
	return .None
}

// The packed-float32 kit mirrors the packed-byte kit primitive for primitive.
// A PackedFloat32Array's engine size counts f32 elements, so every length below
// is an element count; the engine stores four bytes per element. The MultiMesh
// TRANSFORM_3D buffer is exactly this element domain — twelve floats per
// instance — which is why the staging owner is typed rather than a byte view.
//
// packed_float32_array_zeroed builds an engine-owned array of the requested
// element count without a caller-side source buffer, so a staging owner never
// allocates a second copy just to initialize.
packed_float32_array_zeroed :: proc(api: ^Bound_API, length: int) -> (Packed_Float32_Array, Call_Error) {
	if api == nil || length < 0 || length > int(max(u32)) do return {}, .Out_Of_Range
	result := Packed_Float32_Array{live=true}
	api.packed_float32_array_constructor(
		raw.GDExtensionUninitializedTypePtr(rawptr(&result.value)),
		nil,
	)
	if length == 0 do return result, .None
	size := i64(length)
	arguments := [1]raw.GDExtensionConstTypePtr{const_type_pointer(rawptr(&size))}
	resize_result: i64
	api.packed_float32_array_resize(
		type_pointer(rawptr(&result.value)),
		&arguments[0],
		type_pointer(rawptr(&resize_result)),
		1,
	)
	if resize_result != 0 {
		api.packed_float32_array_destructor(type_pointer(rawptr(&result.value)))
		return {}, .Allocation_Failed
	}
	return result, .None
}

packed_float32_array_destroy :: proc(api: ^Bound_API, owner: ^Packed_Float32_Array) -> Call_Error {
	if api == nil do return .Missing_API
	if owner == nil || !owner.live do return .Invalid_Owner
	api.packed_float32_array_destructor(type_pointer(rawptr(&owner.value)))
	owner^ = {}
	return .None
}

packed_float32_array_length :: proc(api: ^Bound_API, owner: ^Packed_Float32_Array) -> (int, Call_Error) {
	if api == nil do return 0, .Missing_API
	if owner == nil || !owner.live do return 0, .Invalid_Owner
	result: i64
	api.packed_float32_array_size(
		type_pointer(rawptr(&owner.value)),
		nil,
		type_pointer(rawptr(&result)),
		0,
	)
	if result < 0 || result > i64(max(int)) do return 0, .Out_Of_Range
	return int(result), .None
}

packed_float32_array_write_base :: proc(api: ^Bound_API, owner: ^Packed_Float32_Array) -> (^f32, int, Call_Error) {
	bound, bound_error := packed_float32_array_length(api, owner)
	if bound_error != .None do return nil, 0, bound_error
	if bound == 0 do return nil, 0, .None
	base := api.packed_float32_array_index(type_pointer(rawptr(&owner.value)), 0)
	if base == nil do return nil, 0, .Invalid_State
	return base, bound, .None
}

packed_float32_array_read_base :: proc(api: ^Bound_API, owner: ^Packed_Float32_Array) -> (^f32, int, Call_Error) {
	bound, bound_error := packed_float32_array_length(api, owner)
	if bound_error != .None do return nil, 0, bound_error
	if bound == 0 do return nil, 0, .None
	base := api.packed_float32_array_index_const(const_type_pointer(rawptr(&owner.value)), 0)
	if base == nil do return nil, 0, .Invalid_State
	return base, bound, .None
}

packed_float32_array_read_exact :: proc(api: ^Bound_API, owner: ^Packed_Float32_Array, output: []f32) -> Call_Error {
	if api == nil do return .Missing_API
	if owner == nil || !owner.live do return .Invalid_Owner
	if len(output) == 0 do return .None
	base, bound, bound_error := packed_float32_array_read_base(api, owner)
	if bound_error != .None do return bound_error
	if base == nil || bound < len(output) do return .Out_Of_Range
	mem.copy_non_overlapping(raw_data(output), rawptr(base), len(output)*size_of(f32))
	return .None
}

// Packed_Float32_Staging is the float twin of Packed_Byte_Staging: it retains
// one engine-owned PackedFloat32Array so a pure Odin subsystem writes transforms
// into the engine's own backing store. Reuse requires a consumer that has finished
// reading and does not retain the array's storage. Returning from
// RenderingServer.multimesh_set_buffer alone does not establish this: the server
// can queue the call, physics interpolation can retain the submitted array, and
// renderer-side CPU caches can retain it after a direct upload.
//
// The qualified retained-write path uses direct render-thread submission,
// disabled physics interpolation, and a non-retaining renderer path. Its
// transform-only examples avoid per-instance getters/setters that create a CPU
// cache. These are caller constraints, not guarantees inferred by this owner.
// Its publish/pending sequence orders publication; it neither drains a server
// queue nor waits for GPU completion. Sole ownership and detachment detection
// follow the byte owner's structural rules; `length` counts f32 elements, and
// the write view is []f32 so no producer re-derives a byte stride from a float count.
Packed_Float32_Staging :: struct {
	array:          Packed_Float32_Array,
	base:           ^f32,
	length:         int,
	publisher_role: Call_Role,
	consumer_role:  Call_Role,
	generation:     u64,
	published:      u64,
	consumed:       u64,
	live:           bool,
}

packed_float32_staging_create :: proc(
	api: ^Bound_API,
	length: int,
	publisher_role: Call_Role,
	consumer_role: Call_Role,
) -> (Packed_Float32_Staging, Call_Error) {
	if !bound_api_is_complete(api) do return {}, .Missing_API
	if length <= 0 || length > int(max(u32)) do return {}, .Out_Of_Range
	if publisher_role == .Invalid || consumer_role == .Invalid do return {}, .Wrong_Role
	array, array_error := packed_float32_array_zeroed(api, length)
	if array_error != .None do return {}, array_error
	result := Packed_Float32_Staging{
		array=array,
		length=length,
		publisher_role=publisher_role,
		consumer_role=consumer_role,
		generation=api.generation,
		live=true,
	}
	base, bound, base_error := packed_float32_array_write_base(api, &result.array)
	if base_error != .None || bound != length {
		packed_float32_array_destroy(api, &result.array)
		return {}, .Invalid_State if base_error == .None else base_error
	}
	result.base = base
	return result, .None
}

// packed_float32_staging_write returns the writable element view to the declared
// publisher role. It re-acquires the base pointer and re-reads the size on every
// call, so a detached or resized array fails here rather than corrupting a frame.
packed_float32_staging_write :: proc(
	api: ^Bound_API,
	staging: ^Packed_Float32_Staging,
	role: Call_Role,
) -> ([]f32, Call_Error) {
	if api == nil do return nil, .Missing_API
	if staging == nil || !staging.live || !staging.array.live do return nil, .Invalid_Owner
	if role != staging.publisher_role do return nil, .Wrong_Role
	if api.generation != staging.generation do return nil, .Stale_Generation
	base, bound, base_error := packed_float32_array_write_base(api, &staging.array)
	if base_error != .None {
		staging.live = false
		return nil, base_error
	}
	if base != staging.base || bound != staging.length {
		staging.live = false
		return nil, .Invalid_State
	}
	return mem.slice_ptr(base, bound), .None
}

packed_float32_staging_publish :: proc(staging: ^Packed_Float32_Staging, role: Call_Role) -> (u64, Call_Error) {
	if staging == nil || !staging.live do return 0, .Invalid_Owner
	if role != staging.publisher_role do return 0, .Wrong_Role
	next := staging.published + 1
	sync.atomic_store_explicit(&staging.published, next, .Release)
	return next, .None
}

packed_float32_staging_pending :: proc(staging: ^Packed_Float32_Staging, role: Call_Role) -> (u64, bool) {
	if staging == nil || !staging.live do return 0, false
	if role != staging.consumer_role do return 0, false
	published := sync.atomic_load_explicit(&staging.published, .Acquire)
	return published, published != staging.consumed
}

packed_float32_staging_destroy :: proc(api: ^Bound_API, staging: ^Packed_Float32_Staging) -> Call_Error {
	if api == nil do return .Missing_API
	if staging == nil || !staging.live && !staging.array.live do return .Invalid_Owner
	error := Call_Error.None
	if staging.array.live do error = packed_float32_array_destroy(api, &staging.array)
	staging^ = {}
	return error
}

// MultiMesh_Layout is the buffer layout one stream submits, restricted to the
// combinations the current consumers use: TRANSFORM_2D contributes eight
// floats per instance and TRANSFORM_3D twelve, and colors add four more.
// Custom data is refused outright — no current consumer uses it, and a layout
// nothing exercises has no measured retention contract.
MultiMesh_Layout :: struct {
	transform_3d: bool,
	colors:       bool,
}

multimesh_layout_stride :: proc(layout: MultiMesh_Layout) -> int {
	stride := 12 if layout.transform_3d else 8
	if layout.colors do stride += 4
	return stride
}

// MultiMesh_Stream is one scene-owned MultiMesh RID's per-frame submission
// pipeline — the whole acquire/allocate/write/publish/submit/replace/retire
// sequence in one owner. The RID stays a scene-owned borrow: every server
// allocation here targets it, and nothing here ever frees server storage, so
// releasing the MultiMesh resource remains the scene's own act. The staged
// array is the one thing this owner owns and destroys.
//
// open allocates the engine's server data at the declared capacity and one
// zeroed staged store of exactly capacity*stride floats. write hands the
// producer the staged view; the view is valid through the matching publish
// and no further — the staging owner's own contract. publish marks the
// frame. submit posts the staged content to the server: it refuses when no
// new publication is pending, refuses an unset or zero RID handle before any
// server call, and advances the submitted sequence. A resource the scene has
// freed keeps a non-zero RID that this check cannot distinguish — the
// engine's own server diagnostic names the dead RID and the call no-ops, and
// the recovery is close plus a fresh open against the replacement resource. resize
// runs create-first choreography: the replacement staged store is created,
// the same RID is re-allocated at the new capacity, the old pair becomes the
// pending retirement, and only a pending retirement that refuses to die fails
// the resize — the new capacity is already live at that point and the
// refusing owner stays reachable in `retiring` for every later retry. close
// retires the pending pair, destroys the active store, and touches nothing
// else.
//
// The staged store's contract is Packed_Float32_Staging's, unchanged: reuse
// requires a consumer that has finished reading and retains nothing, and
// returning from multimesh_set_buffer does not establish that. The qualified
// retained-write path needs direct render-thread submission, disabled physics
// interpolation, and a non-retaining renderer path; per-instance
// getters/setters create a CPU cache that invalidates reuse. These remain
// caller constraints this owner does not verify. The sequence counters order
// publication and submission; they neither drain a server queue nor wait for
// GPU completion.
MultiMesh_Stream :: struct {
	rid:       raw.RID,
	layout:    MultiMesh_Layout,
	stride:    int,
	capacity:  int,
	staging:   Packed_Float32_Staging,
	retiring:  Packed_Float32_Staging,
	submitted: u64,
	live:      bool,
}

// multimesh_stream_open binds one scene-owned RID at one capacity. A zero
// capacity or an engine-invalid RID is a refusal before any server call.
multimesh_stream_open :: proc(call: Call, rid: raw.RID, layout: MultiMesh_Layout, capacity: int) -> (MultiMesh_Stream, Call_Error) {
	if error := validate_call(call); error != .None do return {}, error
	if !rid_valid(call.api, rid) do return {}, .Invalid_Handle
	if capacity <= 0 || capacity > 1_000_000 do return {}, .Out_Of_Range
	server, server_error := godot.singleton_rendering_server(generated_call(call))
	if server_error != .None do return {}, generated_error(server_error)
	format := raw.raw_godot_class_renderingserver_enum_multimeshtransformformat_value_multimesh_transform_2d
	if layout.transform_3d do format = raw.raw_godot_class_renderingserver_enum_multimeshtransformformat_value_multimesh_transform_3d
	if allocate_error := godot.rendering_server_multimesh_allocate_data(
		generated_call(call),
		&server,
		rid,
		i32(capacity),
		format,
		layout.colors,
		false,
		false,
	); allocate_error != .None do return {}, generated_error(allocate_error)
	staging, staging_error := packed_float32_staging_create(call.api, capacity*multimesh_layout_stride(layout), call.role, call.role)
	if staging_error != .None do return {}, staging_error
	return MultiMesh_Stream{
		rid=rid,
		layout=layout,
		stride=multimesh_layout_stride(layout),
		capacity=capacity,
		staging=staging,
		live=true,
	}, .None
}

// multimesh_stream_owned reports whether the stream still owns anything: a
// live owner, an active staged store, or a pending retirement. Setup refuses
// to open over a stream that owns anything, and shutdown runs until this is
// false.
multimesh_stream_owned :: proc(stream: ^MultiMesh_Stream) -> bool {
	if stream == nil do return false
	return stream.live || stream.staging.live || stream.staging.array.live ||
		stream.retiring.live || stream.retiring.array.live
}

// multimesh_stream_write returns the staged view for the stream's publisher
// role. Detachment and resizing of the underlying engine array fail here,
// killing the stream's store exactly like the bare staging owner.
multimesh_stream_write :: proc(call: Call, stream: ^MultiMesh_Stream) -> ([]f32, Call_Error) {
	if stream == nil || !stream.live do return nil, .Invalid_Owner
	return packed_float32_staging_write(call.api, &stream.staging, call.role)
}

// multimesh_stream_publish marks one written frame. The returned sequence is
// the value the next submit will record.
multimesh_stream_publish :: proc(stream: ^MultiMesh_Stream, role: Call_Role) -> (u64, Call_Error) {
	if stream == nil || !stream.live do return 0, .Invalid_Owner
	return packed_float32_staging_publish(&stream.staging, role)
}

// multimesh_stream_submit posts the staged content to the scene's RID.
// Without a new publication since the last submit it refuses with
// Invalid_State — a repeated submit would republish stale bytes and hide a
// producer-side choreography bug. A zero or unset RID handle is a
// Wrong_Device refusal, named before the engine call; a RID whose resource
// the scene freed stays non-zero, is invisible to that check, and reaches
// the server, which rejects it with its own diagnostic while the staged
// store stays intact. Scene RID liveness is a caller constraint; close plus
// open against the replacement resource is the named recovery.
multimesh_stream_submit :: proc(call: Call, stream: ^MultiMesh_Stream) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if stream == nil || !stream.live do return .Invalid_Owner
	published := sync.atomic_load_explicit(&stream.staging.published, .Acquire)
	if published == stream.submitted do return .Invalid_State
	if !rid_valid(call.api, stream.rid) do return .Wrong_Device
	server, server_error := godot.singleton_rendering_server(generated_call(call))
	if server_error != .None do return generated_error(server_error)
	if submit_error := godot.rendering_server_multimesh_set_buffer(
		generated_call(call),
		&server,
		stream.rid,
		stream.staging.array.value,
	); submit_error != .None do return generated_error(submit_error)
	stream.submitted = published
	return .None
}

// multimesh_stream_resize moves one live stream to a new capacity on the
// same RID. The replacement store is created and the server re-allocated
// before anything is adopted, so every earlier failure leaves the previous
// active pair untouched. After adoption the pending pair is destroyed once;
// a refusing destruction fails the resize with the new capacity already live
// and the owner retained in `retiring` for the next retry.
multimesh_stream_resize :: proc(call: Call, stream: ^MultiMesh_Stream, capacity: int) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if stream == nil || !stream.live do return .Invalid_Owner
	if capacity <= 0 || capacity > 1_000_000 do return .Out_Of_Range
	if !rid_valid(call.api, stream.rid) do return .Wrong_Device
	if stream.retiring.live || stream.retiring.array.live {
		if destroy_error := multimesh_stream_retire_pending(call, stream); destroy_error != .None do return destroy_error
	}
	replacement, replacement_error := packed_float32_staging_create(call.api, capacity*stream.stride, stream.staging.publisher_role, stream.staging.consumer_role)
	if replacement_error != .None do return replacement_error
	server, server_error := godot.singleton_rendering_server(generated_call(call))
	if server_error != .None {
		packed_float32_staging_destroy(call.api, &replacement)
		return generated_error(server_error)
	}
	format := raw.raw_godot_class_renderingserver_enum_multimeshtransformformat_value_multimesh_transform_2d
	if stream.layout.transform_3d do format = raw.raw_godot_class_renderingserver_enum_multimeshtransformformat_value_multimesh_transform_3d
	if allocate_error := godot.rendering_server_multimesh_allocate_data(
		generated_call(call),
		&server,
		stream.rid,
		i32(capacity),
		format,
		stream.layout.colors,
		false,
		false,
	); allocate_error != .None {
		packed_float32_staging_destroy(call.api, &replacement)
		return generated_error(allocate_error)
	}
	stream.retiring = stream.staging
	stream.staging = replacement
	stream.capacity = capacity
	stream.submitted = 0
	return multimesh_stream_retire_pending(call, stream)
}

// multimesh_stream_retire_pending destroys the pending retirement once. An
// engine refusal keeps the owner reachable for the next attempt; a stream
// with nothing pending reports success.
multimesh_stream_retire_pending :: proc(call: Call, stream: ^MultiMesh_Stream) -> Call_Error {
	if stream == nil do return .Invalid_Owner
	if !stream.retiring.live && !stream.retiring.array.live do return .None
	return packed_float32_staging_destroy(call.api, &stream.retiring)
}

// multimesh_stream_close retires every staged store the stream owns and
// zeroes the owner. The scene's RID and its server storage are untouched.
// A refusal names which half failed and leaves the survivor reachable in the
// owner for a repeated close.
multimesh_stream_close :: proc(call: Call, stream: ^MultiMesh_Stream) -> Call_Error {
	if stream == nil do return .Invalid_Owner
	if !stream.live && !stream.staging.live && !stream.staging.array.live do return .Invalid_Owner
	result := Call_Error.None
	if pending_error := multimesh_stream_retire_pending(call, stream); pending_error != .None do result = pending_error
	if stream.staging.live || stream.staging.array.live {
		if active_error := packed_float32_staging_destroy(call.api, &stream.staging); active_error != .None && result == .None do result = active_error
	}
	if result == .None {
		stream^ = {}
	}
	return result
}

empty_array_create :: proc(api: ^Bound_API) -> raw.Array {
	result: raw.Array
	api.array_constructor(raw.GDExtensionUninitializedTypePtr(rawptr(&result)), nil)
	return result
}

empty_string_create :: proc(api: ^Bound_API) -> raw.String {
	result: raw.String
	api.string_constructor(raw.GDExtensionUninitializedTypePtr(rawptr(&result)), nil)
	return result
}

godot_string_from_utf8 :: proc(api: ^Bound_API, text: string) -> (raw.String, Call_Error) {
	if api == nil || len(text) == 0 || len(text) > int(max(i32)) do return {}, .Out_Of_Range
	result: raw.String
	status := api.string_new_utf8(
		raw.GDExtensionUninitializedStringPtr(rawptr(&result)),
		cast(^i8)raw_data(text),
		raw.GDExtensionInt(len(text)),
	)
	if status != 0 do return {}, .Godot_Error
	return result, .None
}

rd_shader_source_set_compute_source :: proc(
	call: Call,
	object: raw.GDExtensionObjectPtr,
	source: string,
) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if object == nil do return .Invalid_Handle
	text, text_error := godot_string_from_utf8(call.api, source)
	if text_error != .None do return text_error
	defer call.api.string_destructor(type_pointer(rawptr(&text)))
	stage := raw.raw_godot_class_renderingdevice_enum_shaderstage_value_shader_stage_compute
	handle: godot.RDShaderSource
	handle.ptr = object
	return generated_error(godot.rd_shader_source_set_stage_source(generated_call(call), &handle, stage, text))
}

rendering_device_compile_spirv :: proc(
	call: Call,
	device: Device_Borrow,
	source: RD_Shader_Source_Borrow,
) -> (Owned_Ref, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if rawptr(source) == nil do return {}, .Invalid_Handle
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	source_handle: godot.RDShaderSource
	source_handle.ptr = raw.GDExtensionObjectPtr(source)
	result, generated_failure := godot.rendering_device_shader_compile_spirv_from_source(
		generated_call(call),
		&device_handle,
		&source_handle,
		true,
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if !result.live || rawptr(result.handle.ptr) == nil do return {}, .Invalid_Handle
	return {value=result.handle.ptr, live=true}, .None
}

rendering_device_create_shader :: proc(
	call: Call,
	device: Device_Borrow,
	spirv: RD_Shader_SPIRV_Borrow,
) -> (Owned_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if rawptr(spirv) == nil do return {}, .Invalid_Handle
	name := empty_string_create(call.api)
	defer call.api.string_destructor(type_pointer(rawptr(&name)))
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	spirv_handle: godot.RDShaderSPIRV
	spirv_handle.ptr = raw.GDExtensionObjectPtr(spirv)
	result, generated_failure := godot.rendering_device_shader_create_from_spirv(
		generated_call(call),
		&device_handle,
		&spirv_handle,
		name,
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return new_owned_rid(call, device, .Shader, result)
}

rendering_device_create_shader_from_glsl :: proc(
	call: Call,
	device: Device_Borrow,
	source: string,
) -> (Owned_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	generated_owner, generated_failure := godot.construct_rd_shader_source(generated_call(call))
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if !generated_owner.live || rawptr(generated_owner.handle.ptr) == nil do return {}, .Allocation_Failed
	source_owner := Owned_Ref{value=generated_owner.handle.ptr, live=true}
	if error := rd_shader_source_set_compute_source(call, source_owner.value, source); error != .None {
		_ = ref_counted_release(call, &source_owner)
		return {}, error
	}
	spirv, compile_error := rendering_device_compile_spirv(call, device, RD_Shader_Source_Borrow(source_owner.value))
	if release_error := ref_counted_release(call, &source_owner); release_error != .None do return {}, release_error
	if compile_error != .None do return {}, compile_error
	shader, shader_error := rendering_device_create_shader(call, device, RD_Shader_SPIRV_Borrow(spirv.value))
	if release_error := ref_counted_release(call, &spirv); release_error != .None do return {}, release_error
	return shader, shader_error
}

rendering_device_create_storage_buffer :: proc(
	call: Call,
	device: Device_Borrow,
	size: u32,
	initial_data: []u8,
) -> (Owned_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if size == 0 || (len(initial_data) != 0 && len(initial_data) != int(size)) do return {}, .Out_Of_Range
	data, data_error := packed_byte_array_from_bytes(call.api, initial_data)
	if data_error != .None do return {}, data_error
	defer packed_byte_array_destroy(call.api, &data)
	usage := raw.RenderingDevice_StorageBufferUsage(0)
	creation := raw.RenderingDevice_BufferCreationBits(0)
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_storage_buffer_create(
		generated_call(call),
		&device_handle,
		size,
		data.value,
		usage,
		creation,
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return new_owned_rid(call, device, .Storage_Buffer, result)
}

// rendering_device_buffer_update_staged is the parity path: the engine performs
// the only copy. No packed array is constructed, resized, or destroyed here, no
// element crosses the boundary individually, and the retained array's backing
// store is the same memory the publisher wrote in place.
//
// The consumer role is checked against the owner's declared role, so a Render
// consumer cannot submit a Main-only owner and the reverse.
rendering_device_buffer_update_staged :: proc(
	call: Call,
	device: Device_Borrow,
	buffer: Borrowed_RID,
	offset: u32,
	staging: ^Packed_Byte_Staging,
) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if buffer.kind != .Storage_Buffer do return .Wrong_RID_Kind
	if !rid_matches_device(call.api, buffer, device) do return .Wrong_Device
	if staging == nil || !staging.live || !staging.array.live do return .Invalid_Owner
	if call.role != staging.consumer_role do return .Wrong_Role
	if call.api.generation != staging.generation do return .Stale_Generation
	// Re-establish the bound from the engine on the consuming side too: a resize
	// or detachment between publication and submission must fail rather than
	// submit a stale length.
	bound, bound_error := packed_byte_array_length(call.api, &staging.array)
	if bound_error != .None {
		staging.live = false
		return bound_error
	}
	if bound != staging.length {
		staging.live = false
		return .Invalid_State
	}
	if bound == 0 || bound > int(max(u32)) do return .Out_Of_Range
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_buffer_update(
		generated_call(call),
		&device_handle,
		buffer.value,
		offset,
		u32(bound),
		staging.array.value,
	)
	if generated_failure != .None do return generated_error(generated_failure)
	if result != raw.Error(0) do return .Godot_Error
	staging.consumed = sync.atomic_load_explicit(&staging.published, .Acquire)
	return .None
}

rendering_device_buffer_update :: proc(
	call: Call,
	device: Device_Borrow,
	buffer: Borrowed_RID,
	offset: u32,
	bytes: []u8,
) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if buffer.kind != .Storage_Buffer do return .Wrong_RID_Kind
	if !rid_matches_device(call.api, buffer, device) do return .Wrong_Device
	if len(bytes) == 0 || len(bytes) > int(max(u32)) do return .Out_Of_Range
	data, data_error := packed_byte_array_from_bytes(call.api, bytes)
	if data_error != .None do return data_error
	defer packed_byte_array_destroy(call.api, &data)
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_buffer_update(
		generated_call(call),
		&device_handle,
		buffer.value,
		offset,
		u32(len(bytes)),
		data.value,
	)
	if generated_failure != .None do return generated_error(generated_failure)
	if result != raw.Error(0) do return .Godot_Error
	return .None
}

rendering_device_buffer_get_data :: proc(
	call: Call,
	device: Device_Borrow,
	buffer: Borrowed_RID,
	offset, size: u32,
) -> (Packed_Byte_Array, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if device.kind != .Local do return {}, .Wrong_Device
	if buffer.kind != .Storage_Buffer do return {}, .Wrong_RID_Kind
	if !rid_matches_device(call.api, buffer, device) do return {}, .Wrong_Device
	if device.local_owner.work_recorded && !device.local_owner.synchronized do return {}, .Synchronization_Required
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_buffer_get_data(
		generated_call(call),
		&device_handle,
		buffer.value,
		offset,
		size,
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	// The generated owner's single engine reference moves into the facade's
	// owner; zeroing the generated owner keeps it from releasing the same
	// reference twice.
	moved := Packed_Byte_Array{value=result.value, live=true}
	result = {}
	return moved, .None
}

rendering_device_create_compute_pipeline :: proc(
	call: Call,
	device: Device_Borrow,
	shader: Borrowed_RID,
) -> (Owned_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if shader.kind != .Shader do return {}, .Wrong_RID_Kind
	if !rid_matches_device(call.api, shader, device) do return {}, .Wrong_Device
	specializations := empty_array_create(call.api)
	defer call.api.array_destructor(type_pointer(rawptr(&specializations)))
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_compute_pipeline_create(
		generated_call(call),
		&device_handle,
		shader.value,
		godot.Typed_Array(godot.RDPipelineSpecializationConstant){value=specializations},
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return new_owned_rid(call, device, .Compute_Pipeline, result)
}

object_postinitialize :: proc(call: Call, object: raw.GDExtensionObjectPtr) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if object == nil do return .Invalid_Handle
	handle := godot.Object{ptr=object}
	return generated_error(godot.object_notification(generated_call(call), &handle, 0, false))
}

rd_uniform_set_type :: proc(call: Call, object: raw.GDExtensionObjectPtr, type: Uniform_Type) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if object == nil || (type != .Image && type != .Storage_Buffer) do return .Invalid_Handle
	handle: godot.RDUniform
	handle.ptr = object
	return generated_error(godot.rd_uniform_set_uniform_type(generated_call(call), &handle, raw.RenderingDevice_UniformType(type)))
}

rd_uniform_set_binding :: proc(call: Call, object: raw.GDExtensionObjectPtr, binding: u32) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if object == nil do return .Invalid_Handle
	handle: godot.RDUniform
	handle.ptr = object
	return generated_error(godot.rd_uniform_set_binding(generated_call(call), &handle, i32(binding)))
}

rd_uniform_add_rid :: proc(
	call: Call,
	api_device: Device_Borrow,
	object: raw.GDExtensionObjectPtr,
	rid: Borrowed_RID,
) -> Call_Error {
	if error := device_call_valid(call, api_device); error != .None do return error
	if object == nil do return .Invalid_Handle
	if !rid_matches_device(call.api, rid, api_device) do return .Wrong_Device
	handle: godot.RDUniform
	handle.ptr = object
	return generated_error(godot.rd_uniform_add_id(generated_call(call), &handle, rid.value))
}

create_rd_uniform :: proc(
	call: Call,
	device: Device_Borrow,
	binding: Uniform_Binding,
) -> (Owned_Ref, Call_Error) {
	if !rid_matches_device(call.api, binding.rid, device) do return {}, .Wrong_Device
	generated_owner, generated_failure := godot.construct_rd_uniform(generated_call(call))
	if generated_failure != .None do return {}, generated_error(generated_failure)
	if !generated_owner.live || rawptr(generated_owner.handle.ptr) == nil do return {}, .Allocation_Failed
	owner := Owned_Ref{value=generated_owner.handle.ptr, live=true}
	if error := rd_uniform_set_type(call, owner.value, binding.type); error != .None {
		_ = ref_counted_release(call, &owner)
		return {}, error
	}
	if error := rd_uniform_set_binding(call, owner.value, binding.binding); error != .None {
		_ = ref_counted_release(call, &owner)
		return {}, error
	}
	if error := rd_uniform_add_rid(call, device, owner.value, binding.rid); error != .None {
		_ = ref_counted_release(call, &owner)
		return {}, error
	}
	return owner, .None
}

rendering_device_create_uniform_set :: proc(
	call: Call,
	device: Device_Borrow,
	bindings: []Uniform_Binding,
	shader: Borrowed_RID,
	set_index: u32,
) -> (Owned_RID, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	if len(bindings) == 0 || len(bindings) > MAX_UNIFORM_BINDINGS do return {}, .Out_Of_Range
	if shader.kind != .Shader do return {}, .Wrong_RID_Kind
	if !rid_matches_device(call.api, shader, device) do return {}, .Wrong_Device
	for binding, index in bindings {
		if binding.type != .Image && binding.type != .Storage_Buffer do return {}, .Out_Of_Range
		if !rid_matches_device(call.api, binding.rid, device) do return {}, .Wrong_Device
		if binding.type == .Image && binding.rid.kind != .Texture_Borrow do return {}, .Wrong_RID_Kind
		if binding.type == .Storage_Buffer && binding.rid.kind != .Storage_Buffer do return {}, .Wrong_RID_Kind
		for previous in bindings[:index] do if previous.binding == binding.binding do return {}, .Invalid_State
	}

	array := empty_array_create(call.api)
	defer call.api.array_destructor(type_pointer(rawptr(&array)))
	for binding in bindings {
		uniform, uniform_error := create_rd_uniform(call, device, binding)
		if uniform_error != .None do return {}, uniform_error
		variant: raw.Variant
		uniform_object := uniform.value
		call.api.variant_from_object(
			raw.GDExtensionUninitializedVariantPtr(rawptr(&variant)),
			type_pointer(rawptr(&uniform_object)),
		)
		arguments := [1]raw.GDExtensionConstTypePtr{const_type_pointer(rawptr(&variant))}
		call.api.array_append(type_pointer(rawptr(&array)), &arguments[0], type_pointer(nil), 1)
		call.api.variant_destroy(raw.GDExtensionVariantPtr(rawptr(&variant)))
		if release_error := ref_counted_release(call, &uniform); release_error != .None do return {}, release_error
	}
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_uniform_set_create(
		generated_call(call),
		&device_handle,
		godot.Typed_Array(godot.RDUniform){value=array},
		shader.value,
		set_index,
	)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return new_owned_rid(call, device, .Uniform_Set, result)
}

rendering_device_compute_list_begin :: proc(call: Call, device: Device_Borrow) -> (Compute_List, Call_Error) {
	if error := device_call_valid(call, device); error != .None do return {}, error
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	result, generated_failure := godot.rendering_device_compute_list_begin(generated_call(call), &device_handle)
	if generated_failure != .None do return {}, generated_error(generated_failure)
	return {
		value=result,
		device_kind=device.kind,
		device=device.identity,
		creator_role=call.role,
		local_owner=device.local_owner,
		open=true,
	}, .None
}

compute_list_matches_device :: proc(list: ^Compute_List, device: Device_Borrow, call: Call) -> bool {
	return list != nil && list.open && list.device_kind == device.kind && list.device == device.identity &&
	       list.creator_role == call.role && list.local_owner == device.local_owner
}

compute_list_bind_pipeline :: proc(
	call: Call,
	device: Device_Borrow,
	list: ^Compute_List,
	pipeline: Borrowed_RID,
) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if !compute_list_matches_device(list, device, call) do return .Invalid_State
	if pipeline.kind != .Compute_Pipeline do return .Wrong_RID_Kind
	if !rid_matches_device(call.api, pipeline, device) do return .Wrong_Device
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	return generated_error(godot.rendering_device_compute_list_bind_compute_pipeline(
		generated_call(call),
		&device_handle,
		list.value,
		pipeline.value,
	))
}

compute_list_bind_uniform_set :: proc(
	call: Call,
	device: Device_Borrow,
	list: ^Compute_List,
	uniform_set: Borrowed_RID,
	set_index: u32,
) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if !compute_list_matches_device(list, device, call) do return .Invalid_State
	if uniform_set.kind != .Uniform_Set do return .Wrong_RID_Kind
	if !rid_matches_device(call.api, uniform_set, device) do return .Wrong_Device
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	return generated_error(godot.rendering_device_compute_list_bind_uniform_set(
		generated_call(call),
		&device_handle,
		list.value,
		uniform_set.value,
		set_index,
	))
}

compute_list_dispatch :: proc(
	call: Call,
	device: Device_Borrow,
	list: ^Compute_List,
	x, y, z: u32,
) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if !compute_list_matches_device(list, device, call) do return .Invalid_State
	if x == 0 || y == 0 || z == 0 do return .Out_Of_Range
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	return generated_error(godot.rendering_device_compute_list_dispatch(
		generated_call(call),
		&device_handle,
		list.value,
		x,
		y,
		z,
	))
}

compute_list_end :: proc(call: Call, device: Device_Borrow, list: ^Compute_List) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if !compute_list_matches_device(list, device, call) do return .Invalid_State
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	if generated_failure := godot.rendering_device_compute_list_end(generated_call(call), &device_handle); generated_failure != .None {
		return generated_error(generated_failure)
	}
	list^ = {}
	if device.kind == .Local {
		device.local_owner.work_recorded = true
		device.local_owner.submitted = false
		device.local_owner.synchronized = false
	}
	return .None
}

local_device_submit :: proc(call: Call, owner: ^Owned_Local_Device) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if call.role != .Main do return .Wrong_Role
	if owner == nil || !owner.live || owner.object == nil do return .Invalid_Owner
	if !owner.work_recorded || owner.submitted do return .Invalid_State
	device_handle: godot.RenderingDevice
	device_handle.ptr = owner.object
	if generated_failure := godot.rendering_device_submit(generated_call(call), &device_handle); generated_failure != .None {
		return generated_error(generated_failure)
	}
	owner.submitted = true
	owner.synchronized = false
	return .None
}

local_device_sync :: proc(call: Call, owner: ^Owned_Local_Device) -> Call_Error {
	if error := validate_call(call); error != .None do return error
	if call.role != .Main do return .Wrong_Role
	if owner == nil || !owner.live || owner.object == nil do return .Invalid_Owner
	if !owner.work_recorded || !owner.submitted || owner.synchronized do return .Invalid_State
	device_handle: godot.RenderingDevice
	device_handle.ptr = owner.object
	if generated_failure := godot.rendering_device_sync(generated_call(call), &device_handle); generated_failure != .None {
		return generated_error(generated_failure)
	}
	owner.synchronized = true
	return .None
}

owned_rid_free :: proc(call: Call, device: Device_Borrow, owner: ^Owned_RID) -> Call_Error {
	if error := device_call_valid(call, device); error != .None do return error
	if owner == nil || !owner.live do return .Invalid_Owner
	if owner.kind == .Texture_Borrow || owner.kind == .Invalid do return .Wrong_RID_Kind
	if owner.device_kind != device.kind || owner.device != device.identity ||
	   owner.creator_role != call.role || owner.local_owner != device.local_owner {
		return .Wrong_Device
	}
	if device.kind == .Local && device.local_owner.work_recorded && !device.local_owner.synchronized {
		return .Synchronization_Required
	}
	if device.kind == .Local && device.local_owner.outstanding_rids == 0 do return .Invalid_State
	if !rid_valid(call.api, owner.value) do return .Invalid_Handle
	device_handle: godot.RenderingDevice
	device_handle.ptr = device.object
	if generated_failure := godot.rendering_device_free_rid(generated_call(call), &device_handle, owner.value); generated_failure != .None {
		return generated_error(generated_failure)
	}
	if device.kind == .Local do device.local_owner.outstanding_rids -= 1
	owner^ = {}
	return .None
}
