Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions src/encoder/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll};
use std::thread::JoinHandle;

use ash::vk;
use ash::vk::{self, Handle};
use futures_channel::oneshot;

use crate::encoder::resources::{
Expand Down Expand Up @@ -252,6 +252,22 @@ impl EncodePipeline {
let command_buffers = unsafe { device.allocate_command_buffers(&alloc_info) }
.map_err(|e| PixelForgeError::CommandBuffer(e.to_string()))?;

// Timestamp queries are only legal on a queue family with non-zero
// `timestampValidBits` (VUID-vkCmdWriteTimestamp-timestampValidBits-00829).
// RADV's dedicated video encode queue reports 0, so recording
// vkCmdWriteTimestamp there causes device loss. When unsupported we
// leave the per-slot pools null and the recording/readback helpers treat
// a null pool as "timestamps disabled".
let timestamps_supported = context.encode_timestamps_supported();
let timestamp_period = context.device_properties().limits.timestamp_period;
if !timestamps_supported {
tracing::info!(
"Video encode queue family {:?} reports timestampValidBits=0; \
GPU encode timing stats disabled",
context.video_encode_queue_family()
);
}

let mut slots = Vec::with_capacity(ENCODE_PIPELINE_DEPTH);
for &encode_command_buffer in &command_buffers {
let (input_image, input_image_memory, input_image_view) = create_image(
Expand Down Expand Up @@ -300,13 +316,10 @@ impl EncodePipeline {
let mut profile = *config.profile_info;
let query_pool = create_encode_feedback_query_pool(context, &mut profile)?;

let timestamp_query_pool = create_encode_timestamp_query_pool(context)?;
let timestamp_period = unsafe {
context
.instance()
.get_physical_device_properties(context.physical_device())
.limits
.timestamp_period
let timestamp_query_pool = if timestamps_supported {
create_encode_timestamp_query_pool(context)?
} else {
vk::QueryPool::null()
};

slots.push(EncodeSlot {
Expand Down Expand Up @@ -513,8 +526,12 @@ impl EncodePipeline {
}
slot.bitstream_buffer_ptr = std::ptr::null_mut();
}
if !slot.timestamp_query_pool.is_null() {
unsafe {
device.destroy_query_pool(slot.timestamp_query_pool, None);
}
}
unsafe {
device.destroy_query_pool(slot.timestamp_query_pool, None);
device.destroy_query_pool(slot.query_pool, None);
device.destroy_fence(slot.encode_fence, None);
device.destroy_buffer(slot.bitstream_buffer, None);
Expand Down
20 changes: 19 additions & 1 deletion src/encoder/resources.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::encoder::{BitDepth, PixelFormat};
use crate::error::{PixelForgeError, Result};
use crate::vulkan::VideoContext;
use ash::vk;
use ash::vk::TaggedStructure;
use ash::vk::{self, Handle};
use std::ptr;

/// Minimum bitstream buffer size.
Expand Down Expand Up @@ -1597,11 +1597,17 @@ pub(crate) fn get_encoded_session_params(
}

/// Resets the given query pool and writes starting timestamp command.
///
/// No-op when `query_pool` is null (i.e. the encode queue family does not
/// support timestamp queries).
pub(crate) fn reset_start_timestamp(
device: &ash::Device,
command_buffer: vk::CommandBuffer,
query_pool: vk::QueryPool,
) {
if query_pool.is_null() {
return;
}
unsafe {
device.cmd_reset_query_pool(command_buffer, query_pool, 0, 2);
device.cmd_write_timestamp(
Expand All @@ -1614,11 +1620,17 @@ pub(crate) fn reset_start_timestamp(
}

/// Writes ending timestamp command to the given query pool.
///
/// No-op when `query_pool` is null (i.e. the encode queue family does not
/// support timestamp queries).
pub(crate) fn end_timestamp(
device: &ash::Device,
command_buffer: vk::CommandBuffer,
query_pool: vk::QueryPool,
) {
if query_pool.is_null() {
return;
}
unsafe {
device.cmd_write_timestamp(
command_buffer,
Expand All @@ -1631,6 +1643,9 @@ pub(crate) fn end_timestamp(

/// Queries the given query pool for recorded timestamps, returning their difference.
///
/// Returns `None` when `query_pool` is null (i.e. the encode queue family does
/// not support timestamp queries).
///
/// # Safety
/// This must only be called if both `reset_start_timestamp` and `end_timestamp`
/// were previously written and executed for the given query pool.
Expand All @@ -1640,6 +1655,9 @@ pub(crate) unsafe fn query_timestamp_diff(
mut timestamps: [u64; 2],
timestamp_period: f32,
) -> Option<u64> {
if query_pool.is_null() {
return None;
}
let mut encode_time_ns: Option<u64> = None;
let result = unsafe {
device.get_query_pool_results(
Expand Down
14 changes: 14 additions & 0 deletions src/vulkan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct VideoContextInner {
physical_device: vk::PhysicalDevice,
device: ash::Device,
video_encode_queue_family: Option<u32>,
video_encode_timestamp_valid_bits: u32,
video_encode_queue: Option<vk::Queue>,
transfer_queue_family: u32,
transfer_queue: vk::Queue,
Expand Down Expand Up @@ -118,6 +119,14 @@ impl VideoContext {
self.inner.video_encode_queue
}

/// Whether the selected video encode queue family supports timestamp
/// queries, i.e. reports a non-zero `timestampValidBits`. RADV's dedicated
/// video encode queue reports 0, so `vkCmdWriteTimestamp` is illegal there
/// (VUID-vkCmdWriteTimestamp-timestampValidBits-00829).
pub(crate) fn encode_timestamps_supported(&self) -> bool {
self.inner.video_encode_timestamp_valid_bits > 0
}

/// Get the transfer queue family index.
pub fn transfer_queue_family(&self) -> u32 {
self.inner.transfer_queue_family
Expand Down Expand Up @@ -241,6 +250,7 @@ impl VideoContext {

let mut selected_device = None;
let mut video_encode_queue_family = None;
let mut video_encode_timestamp_valid_bits = 0u32;
let mut transfer_queue_family = u32::MAX;
let mut compute_queue_family = u32::MAX;
let mut supported_encode_codecs = Vec::new();
Expand All @@ -258,6 +268,7 @@ impl VideoContext {

// Find queue families.
let mut encode_queue = None;
let mut encode_ts_bits = 0u32;
let mut transfer_q = u32::MAX;
let mut compute_q = u32::MAX;

Expand All @@ -270,6 +281,7 @@ impl VideoContext {
// Check for video encode queue.
if props.queue_flags.contains(vk::QueueFlags::VIDEO_ENCODE_KHR) {
encode_queue = Some(idx as u32);
encode_ts_bits = props.timestamp_valid_bits;
debug!("Found video encode queue at family {}", idx);
}

Expand Down Expand Up @@ -347,6 +359,7 @@ impl VideoContext {
if has_video_support && encode_supported && has_compute_support {
selected_device = Some(physical_device);
video_encode_queue_family = encode_queue;
video_encode_timestamp_valid_bits = encode_ts_bits;
transfer_queue_family = if transfer_q != u32::MAX {
transfer_q
} else {
Expand Down Expand Up @@ -575,6 +588,7 @@ impl VideoContext {
physical_device,
device,
video_encode_queue_family,
video_encode_timestamp_valid_bits,
video_encode_queue,
transfer_queue_family,
transfer_queue,
Expand Down
Loading