use crate::EncodeError;
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_NV12;
use windows::Win32::Graphics::Dxgi::Common::DXGI_RATIONAL;
use windows::Win32::Media::MediaFoundation::{
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS,
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT, D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_0, D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, D3D12_VIDEO_ENCODER_CODEC_HEVC,
D3D12_VIDEO_ENCODER_DESC, D3D12_VIDEO_ENCODER_FLAG_NONE,
D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, D3D12_VIDEO_ENCODER_HEAP_DESC,
D3D12_VIDEO_ENCODER_HEAP_FLAG_NONE, D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
D3D12_VIDEO_ENCODER_LEVEL_SETTING, D3D12_VIDEO_ENCODER_LEVEL_SETTING_0,
D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC, D3D12_VIDEO_ENCODER_LEVELS_HEVC,
D3D12_VIDEO_ENCODER_LEVELS_HEVC_61,
D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM,
D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC, D3D12_VIDEO_ENCODER_PROFILE_DESC,
D3D12_VIDEO_ENCODER_PROFILE_DESC_0, D3D12_VIDEO_ENCODER_PROFILE_HEVC,
D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN, D3D12_VIDEO_ENCODER_RATE_CONTROL,
D3D12_VIDEO_ENCODER_RATE_CONTROL_CONFIGURATION_PARAMS,
D3D12_VIDEO_ENCODER_RATE_CONTROL_CONFIGURATION_PARAMS_0, D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP,
D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE, D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP,
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE, D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_0,
D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC,
D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK, D3D12_VIDEO_ENCODER_SUPPORT_FLAGS,
D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH, D3D12_VIDEO_ENCODER_VALIDATION_FLAGS, ID3D12VideoDevice3,
ID3D12VideoEncoder, ID3D12VideoEncoderHeap,
};
use windows::core::BOOL;
use super::setup::{
check_codec_support as check_codec_support_generic,
check_output_resolution as check_output_resolution_generic,
};
use super::util::data_size;
pub(super) const MIN_CB_SIZE_PIXELS: u32 = 8;
pub(super) fn check_codec_support(video_device: &ID3D12VideoDevice3) -> Result<(), EncodeError> {
check_codec_support_generic(video_device, D3D12_VIDEO_ENCODER_CODEC_HEVC)
}
pub(super) fn check_output_resolution(
video_device: &ID3D12VideoDevice3,
resolution: D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC,
) -> Result<(), EncodeError> {
check_output_resolution_generic(video_device, D3D12_VIDEO_ENCODER_CODEC_HEVC, resolution)
}
fn profile_desc_main(
profile_hevc: &mut D3D12_VIDEO_ENCODER_PROFILE_HEVC,
) -> D3D12_VIDEO_ENCODER_PROFILE_DESC {
D3D12_VIDEO_ENCODER_PROFILE_DESC {
DataSize: data_size::<D3D12_VIDEO_ENCODER_PROFILE_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_PROFILE_DESC_0 {
pHEVCProfile: profile_hevc,
},
}
}
pub(super) fn check_resource_requirements(
video_device: &ID3D12VideoDevice3,
resolution: D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC,
) -> Result<D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS, EncodeError> {
let mut profile_hevc = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
let mut req = D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS {
NodeIndex: 0,
Codec: D3D12_VIDEO_ENCODER_CODEC_HEVC,
Profile: profile_desc_main(&mut profile_hevc),
InputFormat: DXGI_FORMAT_NV12,
PictureTargetResolution: resolution,
IsSupported: BOOL::default(),
CompressedBitstreamBufferAccessAlignment: 0,
EncoderMetadataBufferAccessAlignment: 0,
MaxEncoderOutputMetadataBufferSize: 0,
};
unsafe {
video_device
.CheckFeatureSupport(
D3D12_FEATURE_VIDEO_ENCODER_RESOURCE_REQUIREMENTS,
std::ptr::from_mut(&mut req).cast(),
data_size::<D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS>(),
)
.map_err(|_| EncodeError::Unsupported)?;
}
if !req.IsSupported.as_bool() {
return Err(EncodeError::Unsupported);
}
Ok(req)
}
pub(super) const fn default_codec_config_hevc() -> D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC {
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC {
ConfigurationFlags:
D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_FLAG_USE_ASYMETRIC_MOTION_PARTITION,
MinLumaCodingUnitSize: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8,
MaxLumaCodingUnitSize: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32,
MinLumaTransformUnitSize: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4,
MaxLumaTransformUnitSize: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32,
max_transform_hierarchy_depth_inter: 3,
max_transform_hierarchy_depth_intra: 3,
}
}
pub(super) fn check_encoder_support(
video_device: &ID3D12VideoDevice3,
resolution: D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC,
mut gop: D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC,
rc_cqp: D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP,
frame_rate: (u32, u32),
) -> Result<D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC, EncodeError> {
let mut codec_conf_hevc = default_codec_config_hevc();
let mut resolution_limits =
D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOLUTION_SUPPORT_LIMITS::default();
let mut suggested_profile_hevc = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
let mut suggested_level_hevc = D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC {
Level: D3D12_VIDEO_ENCODER_LEVELS_HEVC_61,
Tier: D3D12_VIDEO_ENCODER_TIER_HEVC_HIGH,
};
let mut support = D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT {
NodeIndex: 0,
Codec: D3D12_VIDEO_ENCODER_CODEC_HEVC,
InputFormat: DXGI_FORMAT_NV12,
CodecConfiguration: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION {
DataSize: data_size::<D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_0 {
pHEVCConfig: &raw mut codec_conf_hevc,
},
},
CodecGopSequence: D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE {
DataSize: data_size::<D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_SEQUENCE_GOP_STRUCTURE_0 {
pHEVCGroupOfPictures: &raw mut gop,
},
},
RateControl: D3D12_VIDEO_ENCODER_RATE_CONTROL {
Mode: D3D12_VIDEO_ENCODER_RATE_CONTROL_MODE_CQP,
Flags: D3D12_VIDEO_ENCODER_RATE_CONTROL_FLAG_NONE,
ConfigParams: D3D12_VIDEO_ENCODER_RATE_CONTROL_CONFIGURATION_PARAMS {
DataSize: data_size::<D3D12_VIDEO_ENCODER_RATE_CONTROL_CQP>(),
Anonymous: D3D12_VIDEO_ENCODER_RATE_CONTROL_CONFIGURATION_PARAMS_0 {
pConfiguration_CQP: &raw const rc_cqp,
},
},
TargetFrameRate: DXGI_RATIONAL {
Numerator: frame_rate.0,
Denominator: frame_rate.1,
},
},
IntraRefresh: D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE,
SubregionFrameEncoding: D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME,
ResolutionsListCount: 1,
pResolutionList: &raw const resolution,
MaxReferenceFramesInDPB: 0,
ValidationFlags: D3D12_VIDEO_ENCODER_VALIDATION_FLAGS::default(),
SupportFlags: D3D12_VIDEO_ENCODER_SUPPORT_FLAGS::default(),
SuggestedProfile: D3D12_VIDEO_ENCODER_PROFILE_DESC {
DataSize: data_size::<D3D12_VIDEO_ENCODER_PROFILE_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_PROFILE_DESC_0 {
pHEVCProfile: &raw mut suggested_profile_hevc,
},
},
SuggestedLevel: D3D12_VIDEO_ENCODER_LEVEL_SETTING {
DataSize: data_size::<D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_LEVEL_SETTING_0 {
pHEVCLevelSetting: &raw mut suggested_level_hevc,
},
},
pResolutionDependentSupport: &raw mut resolution_limits,
};
unsafe {
video_device
.CheckFeatureSupport(
D3D12_FEATURE_VIDEO_ENCODER_SUPPORT,
std::ptr::from_mut(&mut support).cast(),
data_size::<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT>(),
)
.map_err(|_| EncodeError::Unsupported)?;
}
let _ = suggested_profile_hevc; if !support
.SupportFlags
.contains(D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK)
{
return Err(EncodeError::Unsupported);
}
Ok(suggested_level_hevc)
}
pub(super) fn create_encoder(
video_device: &ID3D12VideoDevice3,
resolution: D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC,
level: D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC,
) -> Result<(ID3D12VideoEncoder, ID3D12VideoEncoderHeap), EncodeError> {
let mut profile_hevc = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN;
let mut codec_conf_hevc = default_codec_config_hevc();
let encoder_desc = D3D12_VIDEO_ENCODER_DESC {
NodeMask: 0,
Flags: D3D12_VIDEO_ENCODER_FLAG_NONE,
EncodeCodec: D3D12_VIDEO_ENCODER_CODEC_HEVC,
EncodeProfile: profile_desc_main(&mut profile_hevc),
InputFormat: DXGI_FORMAT_NV12,
CodecConfiguration: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION {
DataSize: data_size::<D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_0 {
pHEVCConfig: &raw mut codec_conf_hevc,
},
},
MaxMotionEstimationPrecision: D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM,
};
let encoder: ID3D12VideoEncoder =
unsafe { video_device.CreateVideoEncoder(&raw const encoder_desc) }
.map_err(|_| EncodeError::Backend)?;
let mut level = level;
let heap_desc = D3D12_VIDEO_ENCODER_HEAP_DESC {
NodeMask: 0,
Flags: D3D12_VIDEO_ENCODER_HEAP_FLAG_NONE,
EncodeCodec: D3D12_VIDEO_ENCODER_CODEC_HEVC,
EncodeProfile: profile_desc_main(&mut profile_hevc),
EncodeLevel: D3D12_VIDEO_ENCODER_LEVEL_SETTING {
DataSize: data_size::<D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC>(),
Anonymous: D3D12_VIDEO_ENCODER_LEVEL_SETTING_0 {
pHEVCLevelSetting: &raw mut level,
},
},
ResolutionsListCount: 1,
pResolutionList: &raw const resolution,
};
let encoder_heap: ID3D12VideoEncoderHeap =
unsafe { video_device.CreateVideoEncoderHeap(&raw const heap_desc) }
.map_err(|_| EncodeError::Backend)?;
Ok((encoder, encoder_heap))
}
pub(super) const fn level_hevc_to_general_level_idc(level: D3D12_VIDEO_ENCODER_LEVELS_HEVC) -> u8 {
match level.0 {
0 => 30, 1 => 60, 2 => 63, 3 => 90, 4 => 93, 5 => 120, 6 => 123, 7 => 150, 8 => 153, 9 => 156, 10 => 180, 11 => 183, _ => 186, }
}