#![allow(unsafe_code)]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap,
reason = "Vulkan FFI: every count/size here is driver-reported and small — casts mirror \
session_command_hevc.rs's identical allow."
)]
#![allow(
clippy::redundant_pub_crate,
reason = "workspace `unreachable_pub` policy (Cargo.toml) wants `pub(crate)` here; \
clippy::pedantic's redundant_pub_crate disagrees for private modules — the \
two lints are mutually exclusive for this shape, workspace policy wins"
)]
#![allow(
clippy::too_many_lines,
reason = "linear per-frame command-recording sequence (barriers -> begin video coding -> \
decode -> end video coding -> submit), mirrors session_command_hevc.rs's own \
record_and_submit shape"
)]
use vulkanalia::vk;
use vulkanalia::vk::{
DeviceV1_0, DeviceV1_3, HasBuilder, KhrVideoDecodeQueueExtensionDeviceCommands,
KhrVideoQueueExtensionDeviceCommands,
};
use crate::vulkan::av1_params::{Av1FrameHeader, Av1PictureInfoOptionals};
use crate::vulkan::session::{DecodeDevice, VulkanDecodeError};
use crate::vulkan::session_command::{SessionResources, color_range_for_layer, submit_and_wait};
fn reset_session_once(
decode_device: &DecodeDevice<'_>,
resources: &mut SessionResources,
command_buffer: vk::CommandBuffer,
) -> Result<(), VulkanDecodeError> {
if resources.session_reset {
return Ok(());
}
let device = decode_device.device;
unsafe { device.reset_command_buffer(command_buffer, vk::CommandBufferResetFlags::empty()) }
.map_err(|result| VulkanDecodeError::VkCall {
call: "vkResetCommandBuffer",
result,
})?;
let begin_info =
vk::CommandBufferBeginInfo::builder().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe { device.begin_command_buffer(command_buffer, &begin_info) }.map_err(|result| {
VulkanDecodeError::VkCall {
call: "vkBeginCommandBuffer",
result,
}
})?;
let begin_coding = vk::VideoBeginCodingInfoKHR::builder()
.video_session(resources.session)
.video_session_parameters(resources.session_parameters);
unsafe { device.cmd_begin_video_coding_khr(command_buffer, &begin_coding) };
let control_info =
vk::VideoCodingControlInfoKHR::builder().flags(vk::VideoCodingControlFlagsKHR::RESET);
unsafe { device.cmd_control_video_coding_khr(command_buffer, &control_info) };
let end_info = vk::VideoEndCodingInfoKHR::builder();
unsafe { device.cmd_end_video_coding_khr(command_buffer, &end_info) };
unsafe { device.end_command_buffer(command_buffer) }.map_err(|result| {
VulkanDecodeError::VkCall {
call: "vkEndCommandBuffer",
result,
}
})?;
submit_and_wait(decode_device, resources, command_buffer)?;
resources.session_reset = true;
Ok(())
}
pub(crate) struct RecordParamsAv1<'a> {
pub(crate) command_buffer: vk::CommandBuffer,
pub(crate) coded_extent: vk::Extent2D,
pub(crate) bitstream_len: vk::DeviceSize,
pub(crate) dst_slot_index: u32,
pub(crate) tile_offset: u32,
pub(crate) tile_size: u32,
pub(crate) frame_header: &'a Av1FrameHeader,
pub(crate) optionals: &'a Av1PictureInfoOptionals,
}
pub(crate) fn record_and_submit_av1(
decode_device: &DecodeDevice<'_>,
resources: &mut SessionResources,
params: &RecordParamsAv1<'_>,
) -> Result<(), VulkanDecodeError> {
let device = decode_device.device;
let command_buffer = params.command_buffer;
reset_session_once(decode_device, resources, command_buffer)?;
unsafe { device.reset_command_buffer(command_buffer, vk::CommandBufferResetFlags::empty()) }
.map_err(|result| VulkanDecodeError::VkCall {
call: "vkResetCommandBuffer",
result,
})?;
let begin_info =
vk::CommandBufferBeginInfo::builder().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
unsafe { device.begin_command_buffer(command_buffer, &begin_info) }.map_err(|result| {
VulkanDecodeError::VkCall {
call: "vkBeginCommandBuffer",
result,
}
})?;
let all_commands = vk::PipelineStageFlags2::ALL_COMMANDS;
let memory_rw = vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE;
let bitstream_barrier = vk::BufferMemoryBarrier2::builder()
.src_stage_mask(vk::PipelineStageFlags2::HOST)
.src_access_mask(vk::AccessFlags2::HOST_WRITE)
.dst_stage_mask(all_commands)
.dst_access_mask(memory_rw)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.buffer(resources.bitstream_buffer)
.offset(0)
.size(params.bitstream_len);
let dst_range = color_range_for_layer(params.dst_slot_index);
let to_decode_dst = vk::ImageMemoryBarrier2::builder()
.src_stage_mask(all_commands)
.src_access_mask(memory_rw)
.dst_stage_mask(vk::PipelineStageFlags2::VIDEO_DECODE_KHR)
.dst_access_mask(vk::AccessFlags2::VIDEO_DECODE_WRITE_KHR)
.old_layout(vk::ImageLayout::VIDEO_DECODE_DPB_KHR)
.new_layout(vk::ImageLayout::VIDEO_DECODE_DST_KHR)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.image(resources.dpb_image)
.subresource_range(dst_range);
let dep_info = vk::DependencyInfo::builder()
.buffer_memory_barriers(std::slice::from_ref(&bitstream_barrier))
.image_memory_barriers(std::slice::from_ref(&to_decode_dst));
unsafe { device.cmd_pipeline_barrier2(command_buffer, &dep_info) };
let dst_resource = vk::VideoPictureResourceInfoKHR::builder()
.coded_offset(vk::Offset2D { x: 0, y: 0 })
.coded_extent(params.coded_extent)
.base_array_layer(params.dst_slot_index)
.image_view_binding(resources.dpb_image_view)
.build();
let dst_reference_info = params.frame_header.to_std_reference_info();
let mut dst_dpb_slot_info = vk::VideoDecodeAV1DpbSlotInfoKHR::builder()
.std_reference_info(&dst_reference_info)
.build();
let setup_slot = vk::VideoReferenceSlotInfoKHR::builder()
.slot_index(params.dst_slot_index as i32)
.picture_resource(&dst_resource)
.push_next(&mut dst_dpb_slot_info)
.build();
let mut begin_setup_slot = setup_slot;
begin_setup_slot.slot_index = -1;
let begin_slots = [begin_setup_slot];
let begin_coding = vk::VideoBeginCodingInfoKHR::builder()
.video_session(resources.session)
.video_session_parameters(resources.session_parameters)
.reference_slots(&begin_slots);
unsafe { device.cmd_begin_video_coding_khr(command_buffer, &begin_coding) };
let std_picture_info = params.frame_header.to_std_picture_info(params.optionals);
let reference_name_slot_indices = [-1i32; 7];
let tile_offsets = [params.tile_offset];
let tile_sizes = [params.tile_size];
let mut av1_picture_info = vk::VideoDecodeAV1PictureInfoKHR::builder()
.std_picture_info(&std_picture_info)
.reference_name_slot_indices(reference_name_slot_indices)
.frame_header_offset(0)
.tile_offsets(&tile_offsets)
.tile_sizes(&tile_sizes)
.build();
let no_references: [vk::VideoReferenceSlotInfoKHR; 0] = [];
let decode_info = vk::VideoDecodeInfoKHR::builder()
.src_buffer(resources.bitstream_buffer)
.src_buffer_offset(0)
.src_buffer_range(params.bitstream_len)
.dst_picture_resource(dst_resource)
.setup_reference_slot(&setup_slot)
.reference_slots(&no_references)
.push_next(&mut av1_picture_info);
unsafe { device.cmd_decode_video_khr(command_buffer, &decode_info) };
let end_info = vk::VideoEndCodingInfoKHR::builder();
unsafe { device.cmd_end_video_coding_khr(command_buffer, &end_info) };
let from_decode_dst = vk::ImageMemoryBarrier2::builder()
.src_stage_mask(vk::PipelineStageFlags2::VIDEO_DECODE_KHR)
.src_access_mask(vk::AccessFlags2::VIDEO_DECODE_WRITE_KHR)
.dst_stage_mask(all_commands)
.dst_access_mask(memory_rw)
.old_layout(vk::ImageLayout::VIDEO_DECODE_DST_KHR)
.new_layout(vk::ImageLayout::VIDEO_DECODE_DPB_KHR)
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
.image(resources.dpb_image)
.subresource_range(dst_range);
let dep_info_back =
vk::DependencyInfo::builder().image_memory_barriers(std::slice::from_ref(&from_decode_dst));
unsafe { device.cmd_pipeline_barrier2(command_buffer, &dep_info_back) };
unsafe { device.end_command_buffer(command_buffer) }.map_err(|result| {
VulkanDecodeError::VkCall {
call: "vkEndCommandBuffer",
result,
}
})?;
submit_and_wait(decode_device, resources, command_buffer)
}