mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 09:15:19 +03:00
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>
732 lines
24 KiB
Rust
732 lines
24 KiB
Rust
// ─────────────────────────────────────────────────────────────────────────────
|
|
// commands.rs — Phase 2: command buffer hooks
|
|
//
|
|
// vkCmdBindPipeline → update CbState.active_vert_hash/active_frag_hash
|
|
// vkCmdBeginRenderPass → resolve framebuffer → views → image, populate CbState
|
|
// vkCmdEndRenderPass → clear CbState attachment fields
|
|
// vkCmdBeginRenderingKHR → resolve views → image from VkRenderingInfoKHR
|
|
// vkCmdEndRenderingKHR → clear CbState attachment fields
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
use crate::capture;
|
|
use crate::discovery;
|
|
use crate::state::{CB_STATE, CMD_BUF_TO_DEVICE_KEY, DEVICE_STATE};
|
|
use ash::vk::{self, Handle};
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// vkCmdBindPipeline — track which pipeline (and thus which shader hashes)
|
|
// is currently bound for draw calls.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdBindPipeline(
|
|
command_buffer: vk::CommandBuffer,
|
|
pipeline_bind_point: vk::PipelineBindPoint,
|
|
pipeline: vk::Pipeline,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
(ds.fp.cmd_bind_pipeline)(command_buffer, pipeline_bind_point, pipeline);
|
|
}
|
|
|
|
if pipeline_bind_point == vk::PipelineBindPoint::GRAPHICS {
|
|
let hashes = ds
|
|
.pipeline_registry
|
|
.get(&pipeline.as_raw())
|
|
.map(|r| r.clone());
|
|
if let Some(hashes) = hashes {
|
|
if let Some(mut state) = CB_STATE.get_mut(&cb_key) {
|
|
state.active_vert_hash = hashes.vert_hash;
|
|
state.active_frag_hash = hashes.frag_hash;
|
|
} else {
|
|
CB_STATE.insert(
|
|
cb_key,
|
|
crate::state::CbState {
|
|
device_key,
|
|
active_vert_hash: hashes.vert_hash,
|
|
active_frag_hash: hashes.frag_hash,
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
|
|
// Phase 3: Check against loaded shader hash config
|
|
if let Some(ref shader_set) = ds.shader_hashes {
|
|
let is_hud = shader_set.is_hud_shader(hashes.vert_hash, hashes.frag_hash);
|
|
let is_skip = shader_set.is_skip_shader(hashes.frag_hash);
|
|
|
|
if is_hud {
|
|
log::debug!(
|
|
"HUD pipeline detected → vert={} frag={}",
|
|
hashes
|
|
.vert_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
hashes
|
|
.frag_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
);
|
|
// Device-level HUD detection (shared across all command buffers)
|
|
if !ds
|
|
.hud_detected_frame
|
|
.load(std::sync::atomic::Ordering::Relaxed)
|
|
{
|
|
ds.hud_detected_frame
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
ds.pending_capture_frame
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
if is_skip {
|
|
log::debug!(
|
|
"Skip pipeline detected → frag={}",
|
|
hashes
|
|
.frag_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
);
|
|
}
|
|
}
|
|
|
|
/*log::trace!(
|
|
"pipeline bound → vert={} frag={}",
|
|
hashes
|
|
.vert_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
hashes
|
|
.frag_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
);*/
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// vkCmdBeginRenderPass — resolve the current color attachment image.
|
|
//
|
|
// The framebuffer contains VkImageViews. We look up each view in the
|
|
// view_to_image map to get the VkImage, and use view_format for the format.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdBeginRenderPass(
|
|
command_buffer: vk::CommandBuffer,
|
|
p_render_pass_begin: *const vk::RenderPassBeginInfo,
|
|
contents: vk::SubpassContents,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
let rpb = unsafe { &*p_render_pass_begin };
|
|
let framebuffer = rpb.framebuffer;
|
|
|
|
let (color_image, format, extent) = if let Some(views) =
|
|
ds.framebuffer_to_views.get(&framebuffer.as_raw())
|
|
{
|
|
let view_keys: Vec<u64> = views.iter().copied().collect();
|
|
let extent = ds
|
|
.framebuffer_extent
|
|
.get(&framebuffer.as_raw())
|
|
.map(|r| *r)
|
|
.unwrap_or(vk::Extent2D {
|
|
width: rpb.render_area.extent.width,
|
|
height: rpb.render_area.extent.height,
|
|
});
|
|
|
|
if !view_keys.is_empty() {
|
|
let first_view = view_keys[0];
|
|
let image = ds
|
|
.view_to_image
|
|
.get(&first_view)
|
|
.map(|r| vk::Image::from_raw(*r));
|
|
let fmt = ds.view_format.get(&first_view).map(|r| *r);
|
|
log::debug!(
|
|
"vkCmdBeginRenderPass → fb={:#010x} view={:#010x} image={:?} format={} extent={}x{}",
|
|
framebuffer.as_raw(),
|
|
first_view,
|
|
image.map(|i| i.as_raw()),
|
|
fmt.map(|f| f.as_raw()).unwrap_or(0),
|
|
extent.width,
|
|
extent.height,
|
|
);
|
|
(image, fmt, Some(extent))
|
|
} else {
|
|
log::debug!(
|
|
"vkCmdBeginRenderPass → fb={:#010x} no views",
|
|
framebuffer.as_raw(),
|
|
);
|
|
(None, None, Some(extent))
|
|
}
|
|
} else {
|
|
log::debug!(
|
|
"vkCmdBeginRenderPass → fb={:#010x} NOT FOUND in framebuffer_to_views",
|
|
framebuffer.as_raw(),
|
|
);
|
|
(None, None, None)
|
|
};
|
|
|
|
unsafe {
|
|
(ds.fp.cmd_begin_render_pass)(command_buffer, p_render_pass_begin, contents);
|
|
}
|
|
|
|
if let Some(mut state) = CB_STATE.get_mut(&cb_key) {
|
|
state.current_color_image = color_image;
|
|
state.current_image_format = format;
|
|
state.current_image_extent = extent;
|
|
} else {
|
|
CB_STATE.insert(
|
|
cb_key,
|
|
crate::state::CbState {
|
|
device_key,
|
|
current_color_image: color_image,
|
|
current_image_format: format,
|
|
current_image_extent: extent,
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
|
|
if let (Some(img), Some(ext)) = (color_image, extent) {
|
|
log::debug!(
|
|
"render pass begin → color attachment image {:#010x} extent {}x{}",
|
|
img.as_raw(),
|
|
ext.width,
|
|
ext.height,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdEndRenderPass(command_buffer: vk::CommandBuffer) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
// Phase 4: track the largest extent we've seen (main framebuffer)
|
|
let current_extent = CB_STATE
|
|
.get(&cb_key)
|
|
.map(|r| r.current_image_extent)
|
|
.unwrap_or(None);
|
|
if let Some(ext) = current_extent {
|
|
let mut largest = ds.largest_extent.lock().unwrap();
|
|
if ext.width * ext.height > largest.width * largest.height {
|
|
*largest = ext;
|
|
}
|
|
}
|
|
|
|
// Phase 4: check if we need to inject a HUDless capture copy (device-level)
|
|
let needs_hudless_capture = ds
|
|
.pending_capture_frame
|
|
.load(std::sync::atomic::Ordering::Relaxed)
|
|
&& !ds
|
|
.capture_injected_frame
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
|
|
unsafe {
|
|
(ds.fp.cmd_end_render_pass)(command_buffer);
|
|
}
|
|
|
|
// Inject HUDless capture if HUD was detected this frame
|
|
if needs_hudless_capture {
|
|
log::info!("injecting HUDless capture copy for cb {:#010x}", cb_key);
|
|
unsafe {
|
|
capture::inject_hudless_copy(command_buffer, device_key);
|
|
}
|
|
ds.pending_capture_frame
|
|
.store(false, std::sync::atomic::Ordering::Relaxed);
|
|
ds.capture_injected_frame
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
}
|
|
|
|
let skipped = ds
|
|
.skipped_draws_frame
|
|
.swap(0, std::sync::atomic::Ordering::Relaxed);
|
|
if skipped > 0 {
|
|
log::info!("render pass end → {} draws skipped", skipped);
|
|
}
|
|
|
|
if let Some(mut state) = CB_STATE.get_mut(&cb_key) {
|
|
state.current_color_image = None;
|
|
state.current_image_format = None;
|
|
state.current_image_extent = None;
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// vkCmdBeginRenderingKHR — dynamic rendering path (DXVK 1.10+).
|
|
//
|
|
// Unlike the framebuffer path, VkRenderingInfoKHR gives VkImageViews directly
|
|
// in VkRenderingAttachmentInfoKHR. No framebuffer object is involved.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdBeginRenderingKHR(
|
|
command_buffer: vk::CommandBuffer,
|
|
p_rendering_info: *const vk::RenderingInfo,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
let ri = unsafe { &*p_rendering_info };
|
|
let extent = ri.render_area.extent;
|
|
|
|
let (color_image, format) =
|
|
if ri.color_attachment_count > 0 && !ri.p_color_attachments.is_null() {
|
|
let first = unsafe { &*ri.p_color_attachments };
|
|
if first.image_view != vk::ImageView::null() {
|
|
let image = ds
|
|
.view_to_image
|
|
.get(&first.image_view.as_raw())
|
|
.map(|r| vk::Image::from_raw(*r));
|
|
let fmt = ds.view_format.get(&first.image_view.as_raw()).map(|r| *r);
|
|
(image, fmt)
|
|
} else {
|
|
(None, None)
|
|
}
|
|
} else {
|
|
(None, None)
|
|
};
|
|
|
|
unsafe {
|
|
(ds.fp.cmd_begin_rendering_khr.unwrap())(command_buffer, p_rendering_info);
|
|
}
|
|
|
|
if let Some(mut state) = CB_STATE.get_mut(&cb_key) {
|
|
state.current_color_image = color_image;
|
|
state.current_image_format = format;
|
|
state.current_image_extent = Some(extent);
|
|
} else {
|
|
CB_STATE.insert(
|
|
cb_key,
|
|
crate::state::CbState {
|
|
device_key,
|
|
current_color_image: color_image,
|
|
current_image_format: format,
|
|
current_image_extent: Some(extent),
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
|
|
log::info!(
|
|
"vkCmdBeginRenderingKHR → color_image={:?} format={:?} extent={}x{}",
|
|
color_image.map(|i| i.as_raw()),
|
|
format.map(|f| f.as_raw()),
|
|
extent.width,
|
|
extent.height,
|
|
);
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdEndRenderingKHR(command_buffer: vk::CommandBuffer) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
// Phase 4: check if we need to inject a HUDless capture copy (device-level)
|
|
let needs_hudless_capture = ds
|
|
.pending_capture_frame
|
|
.load(std::sync::atomic::Ordering::Relaxed)
|
|
&& !ds
|
|
.capture_injected_frame
|
|
.load(std::sync::atomic::Ordering::Relaxed);
|
|
|
|
unsafe {
|
|
(ds.fp.cmd_end_rendering_khr.unwrap())(command_buffer);
|
|
}
|
|
|
|
// Inject HUDless capture if HUD was detected this frame
|
|
if needs_hudless_capture {
|
|
log::info!("injecting HUDless capture copy for cb {:#010x}", cb_key);
|
|
unsafe {
|
|
capture::inject_hudless_copy(command_buffer, device_key);
|
|
}
|
|
ds.pending_capture_frame
|
|
.store(false, std::sync::atomic::Ordering::Relaxed);
|
|
ds.capture_injected_frame
|
|
.store(true, std::sync::atomic::Ordering::Relaxed);
|
|
}
|
|
|
|
let skipped = ds
|
|
.skipped_draws_frame
|
|
.swap(0, std::sync::atomic::Ordering::Relaxed);
|
|
if skipped > 0 {
|
|
log::info!("rendering end → {} draws skipped", skipped);
|
|
}
|
|
|
|
if let Some(mut state) = CB_STATE.get_mut(&cb_key) {
|
|
state.current_color_image = None;
|
|
state.current_image_format = None;
|
|
state.current_image_extent = None;
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Phase 6: vkCmdDraw* hooks — skip HUD/skip shaders, log for discovery
|
|
//
|
|
// When a config is loaded and the active pipeline matches a HUD or skip shader,
|
|
// the draw call is NOT forwarded to the driver. This prevents the HUD from
|
|
// being rendered to the color attachment. The nescapture capture at
|
|
// vkCmdEndRenderPass then copies the clean (HUD-free) attachment.
|
|
//
|
|
// In discovery mode (HUDLESS_DISCOVER=1), draws are never skipped.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
/// Check if the currently bound pipeline should be suppressed.
|
|
/// Returns (should_skip, vert_hash, frag_hash) so the caller doesn't need
|
|
/// to re-acquire the CB_STATE lock.
|
|
unsafe fn should_skip_draw(
|
|
cb_key: u64,
|
|
ds: &std::sync::Arc<crate::state::DeviceState>,
|
|
) -> (bool, Option<u64>, Option<u64>) {
|
|
if discovery::is_discovery_mode() {
|
|
return (false, None, None);
|
|
}
|
|
let shader_set = match &ds.shader_hashes {
|
|
Some(s) => s,
|
|
None => return (false, None, None),
|
|
};
|
|
let state = match CB_STATE.get(&cb_key) {
|
|
Some(s) => s,
|
|
None => return (false, None, None),
|
|
};
|
|
let vh = state.active_vert_hash;
|
|
let fh = state.active_frag_hash;
|
|
let skip = shader_set.is_hud_shader(vh, fh) || shader_set.is_skip_shader(fh);
|
|
(skip, vh, fh)
|
|
}
|
|
|
|
/// Record that a draw was skipped (for diagnostics).
|
|
unsafe fn record_skipped_draw(
|
|
ds: &crate::state::DeviceState,
|
|
vert_hash: Option<u64>,
|
|
frag_hash: Option<u64>,
|
|
) {
|
|
ds.skipped_draws_frame
|
|
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
log::info!(
|
|
"SKIPPING draw → vert={} frag={}",
|
|
vert_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
frag_hash
|
|
.map(|h| format!("{:#018x}", h))
|
|
.unwrap_or_else(|| "none".to_string()),
|
|
);
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDraw(
|
|
command_buffer: vk::CommandBuffer,
|
|
vertex_count: u32,
|
|
instance_count: u32,
|
|
first_vertex: u32,
|
|
first_instance: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, vertex_count);
|
|
return;
|
|
}
|
|
|
|
(ds.fp.cmd_draw)(
|
|
command_buffer,
|
|
vertex_count,
|
|
instance_count,
|
|
first_vertex,
|
|
first_instance,
|
|
);
|
|
|
|
discovery::record_draw(command_buffer, vertex_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndexed(
|
|
command_buffer: vk::CommandBuffer,
|
|
index_count: u32,
|
|
instance_count: u32,
|
|
first_index: u32,
|
|
vertex_offset: i32,
|
|
first_instance: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, index_count);
|
|
return;
|
|
}
|
|
|
|
(ds.fp.cmd_draw_indexed)(
|
|
command_buffer,
|
|
index_count,
|
|
instance_count,
|
|
first_index,
|
|
vertex_offset,
|
|
first_instance,
|
|
);
|
|
|
|
discovery::record_draw(command_buffer, index_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndirect(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, draw_count);
|
|
return;
|
|
}
|
|
|
|
(ds.fp.cmd_draw_indirect)(command_buffer, buffer, offset, draw_count, stride);
|
|
|
|
discovery::record_draw(command_buffer, draw_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndexedIndirect(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, draw_count);
|
|
return;
|
|
}
|
|
|
|
(ds.fp.cmd_draw_indexed_indirect)(command_buffer, buffer, offset, draw_count, stride);
|
|
|
|
discovery::record_draw(command_buffer, draw_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndirectCount(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
count_buffer: vk::Buffer,
|
|
count_buffer_offset: vk::DeviceSize,
|
|
max_draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, max_draw_count);
|
|
return;
|
|
}
|
|
|
|
if let Some(f) = ds.fp.cmd_draw_indirect_count {
|
|
f(
|
|
command_buffer,
|
|
buffer,
|
|
offset,
|
|
count_buffer,
|
|
count_buffer_offset,
|
|
max_draw_count,
|
|
stride,
|
|
);
|
|
}
|
|
|
|
discovery::record_draw(command_buffer, max_draw_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndexedIndirectCount(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
count_buffer: vk::Buffer,
|
|
count_buffer_offset: vk::DeviceSize,
|
|
max_draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
let cb_key = command_buffer.as_raw();
|
|
let device_key = match CMD_BUF_TO_DEVICE_KEY.get(&cb_key) {
|
|
Some(r) => *r,
|
|
None => return,
|
|
};
|
|
let ds = match DEVICE_STATE.get(&device_key) {
|
|
Some(s) => s.clone(),
|
|
None => return,
|
|
};
|
|
|
|
unsafe {
|
|
let (skip, vh, fh) = should_skip_draw(cb_key, &ds);
|
|
if skip {
|
|
record_skipped_draw(&ds, vh, fh);
|
|
discovery::record_draw(command_buffer, max_draw_count);
|
|
return;
|
|
}
|
|
|
|
if let Some(f) = ds.fp.cmd_draw_indexed_indirect_count {
|
|
f(
|
|
command_buffer,
|
|
buffer,
|
|
offset,
|
|
count_buffer,
|
|
count_buffer_offset,
|
|
max_draw_count,
|
|
stride,
|
|
);
|
|
}
|
|
|
|
discovery::record_draw(command_buffer, max_draw_count);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndirectCountKHR(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
count_buffer: vk::Buffer,
|
|
count_buffer_offset: vk::DeviceSize,
|
|
max_draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
unsafe {
|
|
vkCmdDrawIndirectCount(
|
|
command_buffer,
|
|
buffer,
|
|
offset,
|
|
count_buffer,
|
|
count_buffer_offset,
|
|
max_draw_count,
|
|
stride,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "system" fn vkCmdDrawIndexedIndirectCountKHR(
|
|
command_buffer: vk::CommandBuffer,
|
|
buffer: vk::Buffer,
|
|
offset: vk::DeviceSize,
|
|
count_buffer: vk::Buffer,
|
|
count_buffer_offset: vk::DeviceSize,
|
|
max_draw_count: u32,
|
|
stride: u32,
|
|
) {
|
|
unsafe {
|
|
vkCmdDrawIndexedIndirectCount(
|
|
command_buffer,
|
|
buffer,
|
|
offset,
|
|
count_buffer,
|
|
count_buffer_offset,
|
|
max_draw_count,
|
|
stride,
|
|
);
|
|
}
|
|
}
|