mirror of
https://github.com/nestriness/nestri.git
synced 2026-10-01 23:22:24 +03:00
feat: nescapture capture improvements and drive mounts (#337)
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
DatCaptainHorse
Claude Opus 5
parent
ebc0242b49
commit
6811c93d51
@@ -61,6 +61,14 @@ pub unsafe extern "system" fn vkCreateSwapchainKHR(
|
||||
// outgoing swapchain, so they are set aside rather than reused.
|
||||
crate::capture::retire_all_present_semaphores(&ds);
|
||||
|
||||
// Those fresh images also make every recorded blit invalid: a recording
|
||||
// names its source image by handle, and the handles behind these indices
|
||||
// now belong to a destroyed swapchain. The extent check in
|
||||
// `capture_present_frame` catches a resize on its own, but a swapchain
|
||||
// recreated at the same size — which is the common case, on a format or
|
||||
// present-mode change — looks identical to it.
|
||||
crate::capture::invalidate_recorded_blits(&ds);
|
||||
|
||||
*ds.swapchain.lock().unwrap() = Some(unsafe { *p_swapchain });
|
||||
*ds.swapchain_format.lock().unwrap() = ci.image_format;
|
||||
*ds.swapchain_extent.lock().unwrap() = ci.image_extent;
|
||||
@@ -92,12 +100,26 @@ pub unsafe extern "system" fn vkCreateSwapchainKHR(
|
||||
ds.swapchain_colorspace
|
||||
.store(ci.image_color_space.as_raw() as u32, Ordering::Relaxed);
|
||||
|
||||
log::debug!(
|
||||
"swapchain created — format={:?} colorspace={:?} extent={}x{}",
|
||||
// `info`, not `debug`, and the present mode is why. It decides whether the
|
||||
// compositor paces this game at all: a FIFO swapchain waits on
|
||||
// `wl_surface.frame`, so the game's rate is the compositor's callback
|
||||
// cadence no matter what the gate here is set to, while MAILBOX and
|
||||
// IMMEDIATE ignore those callbacks entirely and leave the pacing to this
|
||||
// layer. The two cases need opposite fixes and nothing else in a log
|
||||
// distinguishes them — "the game runs at 60" reads identically either way.
|
||||
//
|
||||
// An application's in-game V-Sync setting is not the answer either: DXVK
|
||||
// and VKD3D choose the Vulkan present mode themselves, and what they pick
|
||||
// from a given setting is theirs to decide.
|
||||
log::info!(
|
||||
"swapchain created — format={:?} colorspace={:?} extent={}x{} present_mode={:?} \
|
||||
min_images={}",
|
||||
ci.image_format,
|
||||
ci.image_color_space,
|
||||
ci.image_extent.width,
|
||||
ci.image_extent.height,
|
||||
ci.present_mode,
|
||||
ci.min_image_count,
|
||||
);
|
||||
|
||||
vk::Result::SUCCESS
|
||||
@@ -158,3 +180,78 @@ pub unsafe extern "system" fn vkGetSwapchainImagesKHR(
|
||||
|
||||
vk::Result::SUCCESS
|
||||
}
|
||||
|
||||
/// Time the game's wait for a swapchain image.
|
||||
///
|
||||
/// Hooked for the measurement alone — the image index, the semaphore and the
|
||||
/// fence are the application's business and nothing here touches them.
|
||||
///
|
||||
/// It is the one part of a frame the present hook cannot see. `gap` runs from
|
||||
/// one present returning to the next arriving and the acquire sits inside it,
|
||||
/// so a game blocked waiting for the compositor to release a buffer and a game
|
||||
/// busy rendering are the same number. Under a FIFO swapchain that wait is the
|
||||
/// compositor's frame pacing, which is a different problem in a different
|
||||
/// process from anything this layer can fix.
|
||||
/// The device state for an acquire, with the present hook's fallback.
|
||||
///
|
||||
/// These hooks exist only to time the call, but hooking replaces the
|
||||
/// application's function pointer — so there is no passing through if the
|
||||
/// lookup misses, and returning an error would break a game for the sake of a
|
||||
/// measurement. The last resort is the same one `vkQueuePresentKHR` uses: with
|
||||
/// one device in the process, the only entry is the right one.
|
||||
fn device_for(device: vk::Device) -> Option<std::sync::Arc<crate::state::DeviceState>> {
|
||||
let key = unsafe { dispatch_key(device.as_raw() as *const c_void) };
|
||||
DEVICE_STATE
|
||||
.get(&key)
|
||||
.map(|s| s.clone())
|
||||
.or_else(|| DEVICE_STATE.iter().next().map(|e| e.value().clone()))
|
||||
}
|
||||
|
||||
fn record_acquire(ds: &crate::state::DeviceState, waited: std::time::Duration) {
|
||||
if let Ok(enc) = ds.encoder.lock()
|
||||
&& let Some(ref h) = *enc
|
||||
{
|
||||
h.timing.acquire.record(waited);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn vkAcquireNextImageKHR(
|
||||
device: vk::Device,
|
||||
swapchain: vk::SwapchainKHR,
|
||||
timeout: u64,
|
||||
semaphore: vk::Semaphore,
|
||||
fence: vk::Fence,
|
||||
p_image_index: *mut u32,
|
||||
) -> vk::Result {
|
||||
let Some(ds) = device_for(device) else {
|
||||
return vk::Result::ERROR_DEVICE_LOST;
|
||||
};
|
||||
let Some(acquire) = ds.fp.acquire_next_image_khr else {
|
||||
return vk::Result::ERROR_EXTENSION_NOT_PRESENT;
|
||||
};
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
let result = unsafe { acquire(device, swapchain, timeout, semaphore, fence, p_image_index) };
|
||||
record_acquire(&ds, started.elapsed());
|
||||
result
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn vkAcquireNextImage2KHR(
|
||||
device: vk::Device,
|
||||
p_acquire_info: *const vk::AcquireNextImageInfoKHR,
|
||||
p_image_index: *mut u32,
|
||||
) -> vk::Result {
|
||||
let Some(ds) = device_for(device) else {
|
||||
return vk::Result::ERROR_DEVICE_LOST;
|
||||
};
|
||||
let Some(acquire) = ds.fp.acquire_next_image2_khr else {
|
||||
return vk::Result::ERROR_EXTENSION_NOT_PRESENT;
|
||||
};
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
let result = unsafe { acquire(device, p_acquire_info, p_image_index) };
|
||||
record_acquire(&ds, started.elapsed());
|
||||
result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user