use super::com::hr;
use super::device::device;
use crate::d3d11_layout::image_d3d11_layout;
use crate::{DType, Error, PixelFormat, PixelLayout, Result};
use std::ffi::c_void;
use std::os::windows::io::RawHandle;
use windows::core::Interface;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Graphics::Direct3D11::{ID3D11Texture2D, D3D11_TEXTURE2D_DESC};
fn geometry_of(
tex: &ID3D11Texture2D,
format: PixelFormat,
shape: Option<&[usize]>,
) -> Result<(usize, usize)> {
let mut desc = D3D11_TEXTURE2D_DESC::default();
unsafe { tex.GetDesc(&mut desc) };
let (tex_w, tex_h) = (desc.Width as usize, desc.Height as usize);
let dxgi = desc.Format.0 as u32;
if format.layout() == PixelLayout::SemiPlanar {
semi_planar_geometry(format, shape, tex_w, tex_h, dxgi)
} else {
packed_or_planar_geometry(format, tex_w, tex_h, dxgi)
}
}
fn semi_planar_width(format: PixelFormat, shape: Option<&[usize]>, tex_w: usize) -> Result<usize> {
let shape = shape.ok_or_else(|| {
Error::InvalidArgument(format!(
"d3d11 texture geometry: semi-planar textures carry a padded width; pass the tensor \
shape so the {format:?} image width is known"
))
})?;
if shape.len() != 2 {
return Err(Error::InvalidShape(format!(
"d3d11 texture geometry: a {format:?} shape is rank 2 -- [combined height, width] or \
[height, width] -- not {shape:?}"
)));
}
let width = shape[1];
let even = width.checked_next_multiple_of(2).ok_or_else(|| {
Error::InvalidShape(format!(
"d3d11 texture geometry: a {format:?} shape carries a width of {width}, which has no \
even row"
))
})?;
if width == 0 || even > tex_w {
return Err(Error::InvalidShape(format!(
"d3d11 texture geometry: a {format:?} image {width} wide needs a texture row of at \
least {even} texels, but this texture is {tex_w} wide"
)));
}
Ok(width)
}
fn semi_planar_geometry(
format: PixelFormat,
shape: Option<&[usize]>,
tex_w: usize,
tex_h: usize,
dxgi: u32,
) -> Result<(usize, usize)> {
let width = semi_planar_width(format, shape, tex_w)?;
let height = format.image_height_from_combined(tex_h).ok_or_else(|| {
Error::InvalidArgument(format!(
"d3d11 texture geometry: {tex_h} texture rows are not a {format:?} combined plane \
height, so this texture does not hold a {format:?} image"
))
})?;
let l = image_d3d11_layout(format, DType::U8, width, height).ok_or_else(|| {
Error::InvalidArgument(format!(
"d3d11 texture geometry: {format:?} has no D3D11 layout at {width}x{height}"
))
})?;
if l.dxgi_format != dxgi {
return Err(Error::InvalidArgument(format!(
"d3d11 texture geometry: a {tex_w}x{tex_h} texture in DXGI format {dxgi} does not \
hold a {format:?} image: {format:?} is allocated in DXGI format {} at that grid \
(see edgefirst_tensor::d3d11_layout)",
l.dxgi_format
)));
}
Ok((width, height))
}
fn packed_or_planar_geometry(
format: PixelFormat,
tex_w: usize,
tex_h: usize,
dxgi: u32,
) -> Result<(usize, usize)> {
let heights: Vec<usize> = match format.layout() {
PixelLayout::Planar => vec![tex_h, tex_h / format.channels().max(1)],
_ => vec![tex_h],
};
let widths = [tex_w, tex_w * 4 / 3, tex_w * 4];
let mut expected: Vec<u32> = Vec::new();
for &dtype in DType::all() {
for &h in &heights {
for &w in &widths {
let Some(l) = image_d3d11_layout(format, dtype, w, h) else {
continue;
};
if l.texture_width != tex_w || l.texture_height != tex_h {
continue;
}
if l.dxgi_format == dxgi {
return Ok((w, h));
}
if !expected.contains(&l.dxgi_format) {
expected.push(l.dxgi_format);
}
}
}
}
let detail = if expected.is_empty() {
format!("{format:?} has no D3D11 layout at a {tex_w}x{tex_h} texture grid")
} else {
format!("{format:?} is allocated in DXGI format one of {expected:?} at that grid")
};
Err(Error::InvalidArgument(format!(
"d3d11 texture geometry: a {tex_w}x{tex_h} texture in DXGI format {dxgi} does not \
hold a {format:?} image: {detail} (see edgefirst_tensor::d3d11_layout)"
)))
}
pub unsafe fn d3d11_texture_geometry(
texture: *mut c_void,
format: PixelFormat,
shape: Option<&[usize]>,
) -> Result<(usize, usize)> {
let tex = unsafe { ID3D11Texture2D::from_raw_borrowed(&texture) }
.cloned()
.ok_or_else(|| Error::InvalidArgument("d3d11_texture_geometry: null texture".to_owned()))?;
geometry_of(&tex, format, shape)
}
pub unsafe fn d3d11_shared_handle_geometry(
handle: RawHandle,
format: PixelFormat,
shape: Option<&[usize]>,
) -> Result<(usize, usize)> {
let d = device()?;
let dev1 = d.dev1().ok_or_else(|| {
Error::NotImplemented("ID3D11Device1 required to open shared handles".to_owned())
})?;
let tex: ID3D11Texture2D = hr("ID3D11Device1::OpenSharedResource1", unsafe {
dev1.OpenSharedResource1(HANDLE(handle))
})?;
geometry_of(&tex, format, shape)
}
#[cfg(all(test, feature = "static"))]
mod tests {
use super::*;
use crate::d3d11::texture::D3d11TextureTensor;
use crate::CpuAccess;
use std::os::windows::io::AsRawHandle;
#[test]
fn geometry_helpers_report_the_image_dimensions() {
for (fmt, w, h) in [
(PixelFormat::Rgba, 64usize, 32usize),
(PixelFormat::Nv12, 640, 481),
] {
let shape = fmt.allocation_shape(w, h).unwrap();
let t = D3d11TextureTensor::<u8>::new_image(
w,
h,
fmt,
DType::U8,
&shape,
None,
CpuAccess::None,
)
.unwrap();
let by_ptr =
unsafe { d3d11_texture_geometry(t.texture_ptr(), fmt, Some(&shape)) }.unwrap();
assert_eq!(by_ptr, (w, h), "{fmt:?} through the pointer");
let handle = t.shared_handle().unwrap();
let by_handle =
unsafe { d3d11_shared_handle_geometry(handle.as_raw_handle(), fmt, Some(&shape)) }
.unwrap();
assert_eq!(by_handle, (w, h), "{fmt:?} through the shared handle");
}
}
#[test]
fn semi_planar_geometry_takes_the_width_from_the_shape() {
let fmt = PixelFormat::Nv12;
let (w, h) = (640usize, 481usize);
let allocation = fmt.allocation_shape(w, h).unwrap();
let addressing = fmt.addressing_shape(w, h).unwrap();
let t = D3d11TextureTensor::<u8>::new_image(
w,
h,
fmt,
DType::U8,
&allocation,
None,
CpuAccess::None,
)
.unwrap();
let handle = t.shared_handle().unwrap();
for shape in [&allocation, &addressing] {
let by_ptr =
unsafe { d3d11_texture_geometry(t.texture_ptr(), fmt, Some(shape)) }.unwrap();
assert_eq!(by_ptr, (w, h), "{shape:?} through the pointer");
let by_handle =
unsafe { d3d11_shared_handle_geometry(handle.as_raw_handle(), fmt, Some(shape)) }
.unwrap();
assert_eq!(by_handle, (w, h), "{shape:?} through the shared handle");
}
}
#[test]
fn semi_planar_geometry_without_a_shape_is_refused() {
let fmt = PixelFormat::Nv12;
let shape = fmt.allocation_shape(640, 481).unwrap();
let t = D3d11TextureTensor::<u8>::new_image(
640,
481,
fmt,
DType::U8,
&shape,
None,
CpuAccess::None,
)
.unwrap();
let err = unsafe { d3d11_texture_geometry(t.texture_ptr(), fmt, None) }.unwrap_err();
assert!(
matches!(err, Error::InvalidArgument(_)) && err.to_string().contains("tensor shape"),
"{err}"
);
let handle = t.shared_handle().unwrap();
let err =
unsafe { d3d11_shared_handle_geometry(handle.as_raw_handle(), fmt, None) }.unwrap_err();
assert!(
matches!(err, Error::InvalidArgument(_)) && err.to_string().contains("tensor shape"),
"{err}"
);
}
#[test]
fn semi_planar_geometry_refuses_a_width_wider_than_the_texture() {
let fmt = PixelFormat::Nv12;
let shape = fmt.allocation_shape(640, 481).unwrap();
let t = D3d11TextureTensor::<u8>::new_image(
640,
481,
fmt,
DType::U8,
&shape,
None,
CpuAccess::None,
)
.unwrap();
let too_wide = fmt.allocation_shape(641, 481).unwrap();
let err =
unsafe { d3d11_texture_geometry(t.texture_ptr(), fmt, Some(&too_wide)) }.unwrap_err();
assert!(matches!(err, Error::InvalidShape(_)), "{err}");
}
#[test]
fn geometry_helpers_refuse_a_texture_of_another_format() {
let shape = PixelFormat::Grey.allocation_shape(640, 481).unwrap();
let t = D3d11TextureTensor::<u8>::new_image(
640,
481,
PixelFormat::Grey,
DType::U8,
&shape,
None,
CpuAccess::None,
)
.unwrap();
let err =
unsafe { d3d11_texture_geometry(t.texture_ptr(), PixelFormat::Nv12, Some(&shape)) }
.unwrap_err();
assert!(
matches!(err, Error::InvalidShape(_)) && err.to_string().contains("Nv12"),
"{err}"
);
let handle = t.shared_handle().unwrap();
let err = unsafe {
d3d11_shared_handle_geometry(handle.as_raw_handle(), PixelFormat::Nv12, Some(&shape))
}
.unwrap_err();
assert!(matches!(err, Error::InvalidShape(_)), "{err}");
let shape = PixelFormat::Rgba.allocation_shape(64, 32).unwrap();
let rgba = D3d11TextureTensor::<u8>::new_image(
64,
32,
PixelFormat::Rgba,
DType::U8,
&shape,
None,
CpuAccess::None,
)
.unwrap();
let err =
unsafe { d3d11_texture_geometry(rgba.texture_ptr(), PixelFormat::PlanarRgb, None) }
.unwrap_err();
assert!(
err.to_string()
.contains("no D3D11 layout at a 64x32 texture grid"),
"{err}"
);
}
}