use crate::*;
use crate::d3d9::*;
use bytemuck::Zeroable;
use winapi::shared::d3d9::{Direct3DCreate9, IDirect3D9};
use winapi::shared::d3d9types::{D3DDISPLAYMODE, D3DPRESENT_PARAMETERS};
use winapi::shared::windef::{HMONITOR, HWND};
use winapi::um::unknwnbase::IUnknown;
use std::ptr::null_mut;
type AdapterIndex = u32;
type ModeIndex = u32;
#[derive(Clone)] #[repr(transparent)]
pub struct Direct3D(pub(crate) mcom::Rc<IDirect3D9>);
unsafe impl AsSafe<IUnknown > for Direct3D { fn as_safe(&self) -> &IUnknown { &**self.0 } }
unsafe impl AsSafe<IDirect3D9 > for Direct3D { fn as_safe(&self) -> &IDirect3D9 { &*self.0 } }
pub trait IDirect3D9Ext : AsSafe<IDirect3D9> + Sized {
unsafe fn create(sdk_version: SdkVersion) -> Result<Self, MethodError> where Self : From<mcom::Rc<IDirect3D9>> {
let d3d9 = Direct3DCreate9(sdk_version.into());
mcom::Rc::from_raw_opt(d3d9).ok_or(MethodError("Direct3DCreate9", THINERR::NONSPECIFIC)).map(Self::from)
}
fn check_depth_stencil_match(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, adapter_format: impl Into<Format>, render_target_format: impl Into<Format>, depth_stencil_format: impl Into<Format>) -> Result<(), MethodError> {
let hr = unsafe { self.as_winapi().CheckDepthStencilMatch(adapter, device_type.into().into(), adapter_format.into().into(), render_target_format.into().into(), depth_stencil_format.into().into()) };
MethodError::check("IDirect3D9::CheckDepthStencilMatch", hr)?;
Ok(())
}
fn check_device_format(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, adapter_format: impl Into<Format>, usage: u32, rtype: impl Into<ResourceType>, check_format: impl Into<Format>) -> Result<(), MethodError> {
let hr = unsafe { self.as_winapi().CheckDeviceFormat(adapter, device_type.into().into(), adapter_format.into().into(), usage, rtype.into().into(), check_format.into().into()) };
MethodError::check("IDirect3D9::CheckDeviceFormat", hr)?;
Ok(())
}
fn check_device_format_conversion(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, source_format: impl Into<Format>, target_format: impl Into<Format>) -> Result<(), MethodError> {
let hr = unsafe { self.as_winapi().CheckDeviceFormatConversion(adapter, device_type.into().into(), source_format.into().into(), target_format.into().into()) };
MethodError::check("IDirect3D9::CheckDeviceFormatConversion", hr)?;
Ok(())
}
fn check_device_multi_sample_type(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, surface_format: impl Into<Format>, windowed: bool, multi_sample_type: impl Into<MultiSample>) -> Result<u32, MethodError> {
let mut quality_levels = 0;
let hr = unsafe { self.as_winapi().CheckDeviceMultiSampleType(adapter, device_type.into().into(), surface_format.into().into(), windowed.into(), multi_sample_type.into().into(), &mut quality_levels) };
MethodError::check("IDirect3D9::CheckDeviceMultiSampleType", hr)?;
Ok(quality_levels)
}
fn check_device_type(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, adapter_format: impl Into<Format>, back_buffer_format: impl Into<Format>, windowed: bool) -> Result<(), MethodError> {
let hr = unsafe { self.as_winapi().CheckDeviceType(adapter, device_type.into().into(), adapter_format.into().into(), back_buffer_format.into().into(), windowed.into()) };
MethodError::check("IDirect3D9::CheckDeviceType", hr)?;
Ok(())
}
unsafe fn create_device(&self, adapter: AdapterIndex, device_type: impl Into<DevType>, focus_window: HWND, behavior_flags: impl Into<Create>, present_parameters: &mut D3DPRESENT_PARAMETERS) -> Result<Device, MethodError> {
let mut device = null_mut();
let hr = self.as_winapi().CreateDevice(adapter, device_type.into().into(), focus_window, behavior_flags.into().into(), present_parameters, &mut device);
MethodError::check("IDirect3D9::CreateDevice", hr)?;
Ok(Device::from_raw(device))
}
fn enum_adapter_modes(&self, adapter: AdapterIndex, format: impl Into<Format>, mode: ModeIndex) -> Result<D3DDISPLAYMODE, MethodError> {
let mut dm = unsafe { std::mem::zeroed::<D3DDISPLAYMODE>() };
let hr = unsafe { self.as_winapi().EnumAdapterModes(adapter, format.into().into(), mode, &mut dm) };
MethodError::check("IDirect3D9::EnumAdapterModes", hr)?;
Ok(dm)
}
fn get_adapter_count(&self) -> AdapterIndex {
unsafe { self.as_winapi().GetAdapterCount() } }
fn get_adapter_display_mode(&self, adapter: AdapterIndex) -> Result<D3DDISPLAYMODE, MethodError> {
let mut dm = unsafe { std::mem::zeroed::<D3DDISPLAYMODE>() };
let hr = unsafe { self.as_winapi().GetAdapterDisplayMode(adapter, &mut dm) };
MethodError::check("IDirect3D9::GetAdapterDisplayMode", hr)?;
Ok(dm)
}
fn get_adapter_identifier(&self, adapter: AdapterIndex, flags: u32) -> Result<AdapterIdentifier, MethodError> {
let mut ident = AdapterIdentifier::default();
let hr = unsafe { self.as_winapi().GetAdapterIdentifier(adapter, flags, &mut *ident) };
MethodError::check("IDirect3D9::GetAdapterIdentifier", hr)?;
Ok(ident)
}
fn get_adapter_mode_count(&self, adapter: AdapterIndex, format: impl Into<Format>) -> ModeIndex {
unsafe { self.as_winapi().GetAdapterModeCount(adapter, format.into().into()) }
}
fn get_adapter_monitor(&self, adapter: AdapterIndex) -> Result<HMONITOR, MethodError> {
let hm = unsafe { self.as_winapi().GetAdapterMonitor(adapter) }; if hm.is_null() { Err(MethodError("IDirect3D9::GetAdapterMonitor", THINERR::NONSPECIFIC)) } else { Ok(hm) }
}
fn get_device_caps(&self, adapter: AdapterIndex, device_type: DevType) -> Result<Caps, MethodError> {
let mut caps = Caps::zeroed();
let hr = unsafe { self.as_winapi().GetDeviceCaps(adapter, device_type.into(), &mut *caps) }; MethodError::check("IDirect3D9::GetDeviceCaps", hr)?;
Ok(caps)
}
}
impl<T: AsSafe<IDirect3D9> + Sized> IDirect3D9Ext for T {}
#[cfg(test)] mod tests {
use dev::d3d9::*;
use std::ptr::null_mut;
#[test] fn create() {
use winapi::shared::d3d9::D3D_SDK_VERSION;
unsafe {
Direct3D::create(SdkVersion::default()).unwrap();
Direct3D::create(SdkVersion::default().with_debug_disabled()).unwrap();
Direct3D::create(SdkVersion::default().with_debug_enabled()).unwrap();
Direct3D::create(SdkVersion::DEFAULT.with_debug_disabled()).unwrap();
Direct3D::create(SdkVersion::DEFAULT.with_debug_enabled()).unwrap();
Direct3D::create(SdkVersion::DEFAULT9B.with_debug_disabled()).unwrap();
Direct3D::create(SdkVersion::DEFAULT9B.with_debug_enabled()).unwrap();
Direct3D::create(SdkVersion::from(D3D_SDK_VERSION).with_debug_disabled()).unwrap();
Direct3D::create(SdkVersion::from(D3D_SDK_VERSION).with_debug_enabled()).unwrap();
assert_eq!(THINERR::NONSPECIFIC, Direct3D::create(SdkVersion::from(0) ).err());
assert_eq!(THINERR::NONSPECIFIC, Direct3D::create(SdkVersion::from(0).with_debug_disabled() ).err());
assert_eq!(THINERR::NONSPECIFIC, Direct3D::create(SdkVersion::from(0).with_debug_enabled() ).err());
}
}
#[test] fn check_depth_stencil_match() {
let d3d = d3d_test();
assert!( d3d.check_depth_stencil_match(0, DevType::HAL, Format::X8R8G8B8, Format::A8R8G8B8, Format::D24S8 ).is_ok()); assert_eq!(D3DERR::INVALIDCALL, d3d.check_depth_stencil_match(9001, DevType::HAL, Format::X8R8G8B8, Format::A8R8G8B8, Format::D24S8 )); assert_eq!(D3DERR::INVALIDCALL, d3d.check_depth_stencil_match(0, Invalid, Format::X8R8G8B8, Format::A8R8G8B8, Format::D24S8 )); assert_eq!(D3DERR::INVALIDCALL, d3d.check_depth_stencil_match(0, DevType::HAL, Format::UNKNOWN, Format::A8R8G8B8, Format::D24S8 )); assert_eq!(D3DERR::INVALIDCALL, d3d.check_depth_stencil_match(0, DevType::HAL, Format::X8R8G8B8, Format::UNKNOWN, Format::D24S8 )); assert_eq!(D3DERR::INVALIDCALL, d3d.check_depth_stencil_match(0, DevType::HAL, Format::X8R8G8B8, Format::A8R8G8B8, Format::UNKNOWN)); }
#[test] fn check_device_format() {
let d3d = d3d_test();
assert!( d3d.check_device_format(0, DevType::HAL, Format::X8R8G8B8, 0, ResourceType::Texture, Format::A8R8G8B8).is_ok()); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(9001, DevType::HAL, Format::X8R8G8B8, 0, ResourceType::Texture, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(0, Invalid, Format::X8R8G8B8, 0, ResourceType::Texture, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(0, DevType::HAL, Format::UNKNOWN, 0, ResourceType::Texture, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(0, DevType::HAL, Format::X8R8G8B8, !0, ResourceType::Texture, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(0, DevType::HAL, Format::X8R8G8B8, 0, Invalid, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format(0, DevType::HAL, Format::X8R8G8B8, 0, ResourceType::Texture, Format::UNKNOWN )); }
#[test] fn check_device_format_conversion() {
let d3d = d3d_test();
assert!( d3d.check_device_format_conversion(0, DevType::HAL, Format::A8R8G8B8, Format::A8R8G8B8).is_ok()); assert!( d3d.check_device_format_conversion(0, DevType::HAL, Format::D24S8, Format::D24S8 ).is_ok()); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_format_conversion(9001, DevType::HAL, Format::A8R8G8B8, Format::A8R8G8B8)); assert_eq!(D3DERR::INVALIDDEVICE, d3d.check_device_format_conversion(0, Invalid, Format::A8R8G8B8, Format::A8R8G8B8)); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_format_conversion(0, DevType::HAL, Invalid, Format::A8R8G8B8)); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_format_conversion(0, DevType::HAL, Format::A8R8G8B8, Invalid )); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_format_conversion(0, DevType::HAL, Format::D24S8, Format::A8R8G8B8)); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_format_conversion(0, DevType::HAL, Format::A8R8G8B8, Format::D24S8 )); }
#[test] fn check_device_multi_sample_type() {
let d3d = d3d_test();
assert!( d3d.check_device_multi_sample_type(0, DevType::HAL, Format::A8R8G8B8, true, MultiSample::None ).is_ok()); assert!( d3d.check_device_multi_sample_type(0, DevType::HAL, Format::A8R8G8B8, true, MultiSample::X2 ).is_ok()); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_multi_sample_type(9001, DevType::HAL, Format::A8R8G8B8, true, MultiSample::X2 )); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_multi_sample_type(0, Invalid, Format::A8R8G8B8, true, MultiSample::X2 )); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_multi_sample_type(0, DevType::HAL, Invalid, true, MultiSample::X2 )); let _ = d3d.check_device_multi_sample_type(0, DevType::HAL, Invalid, true, MultiSample::None ); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_multi_sample_type(0, DevType::HAL, Format::A8R8G8B8, true, Invalid )); }
#[test] fn check_device_type() {
let d3d = d3d_test();
assert!( d3d.check_device_type(0, DevType::HAL, Format::X8R8G8B8, Format::A8R8G8B8, true).is_ok()); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_type(9001, DevType::HAL, Format::X8R8G8B8, Format::A8R8G8B8, true)); assert_eq!(D3DERR::INVALIDCALL, d3d.check_device_type(0, Invalid, Format::X8R8G8B8, Format::A8R8G8B8, true)); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_type(0, DevType::HAL, Invalid, Format::A8R8G8B8, true)); assert_eq!(D3DERR::NOTAVAILABLE, d3d.check_device_type(0, DevType::HAL, Format::X8R8G8B8, Invalid, true)); }
#[test] fn enum_adapter_modes() {
let d3d = d3d_test();
let adapters = d3d.get_adapter_count();
for adapter in 0..adapters {
eprintln!("checking adapter {} of {}", adapter+1, adapters);
for fmt in [
Format::UNKNOWN, Format::R8G8B8, Format::A8R8G8B8, Format::X8R8G8B8, Format::A8B8G8R8, Format::X8B8G8R8,
Format::from_unchecked(1), Format::from_unchecked(10000), Format::from_unchecked(!0),
].iter().copied() {
let modes = d3d.get_adapter_mode_count(adapter, fmt);
for mode in 0..modes {
d3d.enum_adapter_modes(adapter, fmt, mode).unwrap_or_else(|err| panic!("enum_adapter_modes({}, {:?}, {}) failed: {}", adapter, fmt, mode, err));
}
for mode in modes..modes+100 {
assert_eq!(D3DERR::INVALIDCALL, d3d.enum_adapter_modes(adapter, fmt, mode).map(|_| ()));
}
}
}
for adapter in [adapters+0, adapters+100, adapters+100000, adapters+10000000].iter().copied() {
eprintln!("checking invalid adapter {} of {}", adapter+1, adapters);
assert_eq!(D3DERR::INVALIDCALL, d3d.enum_adapter_modes(adapter, Format::X8R8G8B8, 0).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.enum_adapter_modes(adapter, Format::X8R8G8B8, 100).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.enum_adapter_modes(adapter, Format::X8R8G8B8, 10000).map(|_| ()));
}
}
#[test] fn get_adapter_count() {
let d3d = d3d_test();
assert!(d3d.get_adapter_count() > 0);
}
#[test] fn get_adapter_display_mode() {
let d3d = d3d_test();
let adapters = d3d.get_adapter_count();
for adapter in 0..adapters {
d3d.get_adapter_display_mode(adapter).unwrap_or_else(|err| panic!("unable to query display mode of adapter {} of {}: {}", adapter+1, adapters, err));
}
for adapter in adapters..(100+adapters) {
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_display_mode(adapter).err());
}
}
#[test] fn get_adapter_identifier() {
let d3d = d3d_test();
const D3DENUM_WHQL_LEVEL : u32 = 2;
let valid = 0;
d3d.get_adapter_identifier(valid, 0 ).unwrap();
d3d.get_adapter_identifier(valid, D3DENUM_WHQL_LEVEL).unwrap();
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_identifier(valid, D3DENUM_WHQL_LEVEL+1).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_identifier(valid, D3DENUM_WHQL_LEVEL+1000).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_identifier(valid, D3DENUM_WHQL_LEVEL+1000000).map(|_| ()));
let invalid = 9001;
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_identifier(invalid, 0 ).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_adapter_identifier(invalid, D3DENUM_WHQL_LEVEL ).map(|_| ()));
}
#[test] fn get_adapter_mode_count() {
let d3d = d3d_test();
let adapters = d3d.get_adapter_count();
for valid in 0..adapters {
for (fmt, expect) in [
(Format::UNKNOWN, Some(false)),
(Format::from_unchecked(1), Some(false)),
(Format::R8G8B8, None),
(Format::A8R8G8B8, None),
(Format::X8R8G8B8, Some(true)),
(Format::A8B8G8R8, None),
(Format::X8B8G8R8, None),
(Format::from_unchecked(10000), Some(false)),
(Format::from_unchecked(!0), Some(false)),
].iter().copied() {
let modes = d3d.get_adapter_mode_count(valid, fmt);
assert!(expect.unwrap_or(true) || modes == 0, "adapter {} of {}: {:?} has {} modes, none expected", valid+1, adapters, fmt, modes);
assert!(!expect.unwrap_or(false) || modes > 0, "adapter {} of {}: {:?} has {} modes, some expected", valid+1, adapters, fmt, modes);
}
}
for invalid in [adapters+0, adapters+100, adapters+100000, adapters+10000000].iter().copied() {
for fmt in [Format::UNKNOWN, Format::X8R8G8B8].iter() {
assert_eq!(0, d3d.get_adapter_mode_count(invalid, Format::UNKNOWN ), "adapter {} of {} has modes for format {:?} despite being out-of-bounds", invalid+1, adapters, fmt);
}
}
}
#[test] fn get_adapter_monitor() {
let d3d = d3d_test();
let adapters = d3d.get_adapter_count();
for valid in 0..adapters { assert!(!d3d.get_adapter_monitor( valid).unwrap_or(null_mut()).is_null(), "adapter {} of {} has a null HMONITOR", valid+1, adapters); }
for invalid in adapters..adapters+100 { assert!( d3d.get_adapter_monitor(invalid).is_err(), "adapter {} of {} returned an HMONITOR despite being out-of-bounds!", invalid+1, adapters); }
}
#[test] fn get_device_caps() {
let d3d = d3d_test();
let adapter = 0; d3d.get_device_caps(adapter, DevType::HAL).unwrap();
d3d.get_device_caps(adapter, DevType::Ref).unwrap();
d3d.get_device_caps(adapter, DevType::NullRef).unwrap();
assert_eq!(D3DERR::INVALIDDEVICE, d3d.get_device_caps(adapter, DevType::from_unchecked(100)).map(|_| ()));
assert_eq!(D3DERR::INVALIDDEVICE, d3d.get_device_caps(adapter, DevType::from_unchecked(10000)).map(|_| ()));
assert_eq!(D3DERR::INVALIDDEVICE, d3d.get_device_caps(adapter, DevType::from_unchecked(1000000)).map(|_| ()));
assert_eq!(D3DERR::INVALIDDEVICE, d3d.get_device_caps(adapter, DevType::from_unchecked(100000000)).map(|_| ()));
let adapter = 9001; assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::HAL).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::Ref).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::NullRef).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::from_unchecked(100)).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::from_unchecked(10000)).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::from_unchecked(1000000)).map(|_| ()));
assert_eq!(D3DERR::INVALIDCALL, d3d.get_device_caps(adapter, DevType::from_unchecked(100000000)).map(|_| ()));
}
}