feat(nescapture): open the capture layer

A Vulkan implicit layer that captures frames from inside the workload's own
process and encodes them on the GPU they were drawn on. Fourth and last of this
batch, imported as a tree from `nestrilabs/nescapture` on the same terms.

Filed under `apps/` rather than `crates/` despite building a cdylib. The rule
here is what a thing *is*, not what it compiles to: this is a finished artefact
that gets installed into an image beside its layer manifest, not a library
another crate in this tree depends on. `crates/` is for the latter, and putting
this there would make the distinction useless the first time someone looked.

Wired to the workspace, `nesprotocol` by path. Its description named the
transport component; that reads better as what it actually is — where the frames
go — so it says that instead.

Whole workspace builds and tests: 21 across four members.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wanjohi
2026-08-26 18:04:02 +03:00
co-authored by Claude Opus 5
parent 06b844b961
commit 6164e0c636
22 changed files with 5958 additions and 22 deletions
+121
View File
@@ -0,0 +1,121 @@
// ─────────────────────────────────────────────────────────────────────────────
// swapchain.rs — Phase 4: swapchain tracking
//
// Captures format, extent AND color space from vkCreateSwapchainKHR so the
// encoder can automatically determine the correct color space pipeline
// (SDR/BT.709 vs HDR10/BT.2020-PQ vs FP16) without any manual configuration.
// ─────────────────────────────────────────────────────────────────────────────
use crate::dispatch_key;
use crate::state::DEVICE_STATE;
use ash::vk::{self, Handle};
use std::os::raw::c_void;
use std::sync::atomic::Ordering;
#[unsafe(no_mangle)]
pub unsafe extern "system" fn vkCreateSwapchainKHR(
device: vk::Device,
p_create_info: *const vk::SwapchainCreateInfoKHR,
p_allocator: *const vk::AllocationCallbacks,
p_swapchain: *mut vk::SwapchainKHR,
) -> vk::Result {
let key = unsafe { dispatch_key(device.as_raw() as *const c_void) };
let ds = match DEVICE_STATE.get(&key) {
Some(s) => s.clone(),
None => return vk::Result::ERROR_DEVICE_LOST,
};
let create_fn = match ds.fp.create_swapchain_khr {
Some(f) => f,
None => return vk::Result::ERROR_EXTENSION_NOT_PRESENT,
};
let ci = unsafe { &*p_create_info };
// Add TRANSFER_SRC so we can blit from swapchain images.
let mut modified_ci = *ci;
modified_ci.image_usage = ci.image_usage | vk::ImageUsageFlags::TRANSFER_SRC;
let result = unsafe { create_fn(device, &modified_ci, p_allocator, p_swapchain) };
// If the driver rejects TRANSFER_SRC (e.g. composited window), try without.
let result = if result != vk::Result::SUCCESS {
unsafe { create_fn(device, ci, p_allocator, p_swapchain) }
} else {
result
};
if result != vk::Result::SUCCESS {
return result;
}
*ds.swapchain.lock().unwrap() = Some(unsafe { *p_swapchain });
*ds.swapchain_format.lock().unwrap() = ci.image_format;
*ds.swapchain_extent.lock().unwrap() = ci.image_extent;
ds.swapchain_colorspace
.store(ci.image_color_space.as_raw() as u32, Ordering::Relaxed);
log::debug!(
"swapchain created — format={:?} colorspace={:?} extent={}x{}",
ci.image_format,
ci.image_color_space,
ci.image_extent.width,
ci.image_extent.height,
);
vk::Result::SUCCESS
}
#[unsafe(no_mangle)]
pub unsafe extern "system" fn vkDestroySwapchainKHR(
device: vk::Device,
swapchain: vk::SwapchainKHR,
p_allocator: *const vk::AllocationCallbacks,
) {
let key = unsafe { dispatch_key(device.as_raw() as *const c_void) };
if let Some(ds) = DEVICE_STATE.get(&key) {
*ds.swapchain.lock().unwrap() = None;
*ds.swapchain_images.lock().unwrap() = Vec::new();
if let Some(destroy_fn) = ds.fp.destroy_swapchain_khr {
unsafe { destroy_fn(device, swapchain, p_allocator) };
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "system" fn vkGetSwapchainImagesKHR(
device: vk::Device,
swapchain: vk::SwapchainKHR,
p_swapchain_image_count: *mut u32,
p_swapchain_images: *mut vk::Image,
) -> vk::Result {
let key = unsafe { dispatch_key(device.as_raw() as *const c_void) };
let ds = match DEVICE_STATE.get(&key) {
Some(s) => s.clone(),
None => return vk::Result::ERROR_DEVICE_LOST,
};
let get_fn = match ds.fp.get_swapchain_images_khr {
Some(f) => f,
None => return vk::Result::ERROR_EXTENSION_NOT_PRESENT,
};
let result = unsafe {
get_fn(
device,
swapchain,
p_swapchain_image_count,
p_swapchain_images,
)
};
if result != vk::Result::SUCCESS {
return result;
}
if !p_swapchain_images.is_null() {
let count = unsafe { *p_swapchain_image_count as usize };
let images = unsafe { std::slice::from_raw_parts(p_swapchain_images, count) };
*ds.swapchain_images.lock().unwrap() = images.to_vec();
log::debug!("swapchain images — {} images", count);
}
vk::Result::SUCCESS
}