hal/dx12: adapter initialization

This commit is contained in:
Dzmitry Malyshau 2021-07-05 02:17:09 -04:00 committed by Dzmitry Malyshau
parent 4b4e393eec
commit 3a2bbfd563
10 changed files with 1080 additions and 12 deletions

12
Cargo.lock generated
View File

@ -444,6 +444,17 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "d3d12"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "091ed1b25fe47c7ff129fc440c23650b6114f36aa00bc7212cc8041879294428"
dependencies = [
"bitflags",
"libloading 0.7.0",
"winapi 0.3.9",
]
[[package]]
name = "darling"
version = "0.10.2"
@ -1984,6 +1995,7 @@ dependencies = [
"bitflags",
"block",
"core-graphics-types",
"d3d12",
"env_logger",
"foreign-types",
"fxhash",

View File

@ -16,6 +16,7 @@ default = []
metal = ["naga/msl-out", "block", "foreign-types"]
vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it", "renderdoc-sys"]
gles = ["naga/glsl-out", "glow", "egl", "libloading"]
dx12 = ["native", "winapi/d3d12", "winapi/d3d12shader", "winapi/d3d12sdklayers", "winapi/dxgi1_6"]
[dependencies]
bitflags = "1.0"
@ -39,6 +40,9 @@ inplace_it = { version ="0.3.3", optional = true }
renderdoc-sys = { version = "0.7.1", optional = true }
# backend: Gles
glow = { git = "https://github.com/grovesNL/glow", rev = "0864897a28bbdd43f89f4fd8fdd4ed781b719f8a", optional = true }
# backend: Dx12
native = { package = "d3d12", version = "0.4", features = ["libloading"], optional = true }
#winapi = { version = "0.3", features = ["basetsd","d3dcommon","d3dcompiler","dxgi1_2","dxgi1_3","dxgi1_4","dxgi1_5","dxgi1_6","dxgidebug","dxgiformat","dxgitype","handleapi","minwindef","synchapi","unknwnbase","winbase","winerror","winnt"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], optional = true }

View File

@ -0,0 +1,319 @@
use super::{conv, HResultPair as _};
use std::{mem, sync::Arc};
use winapi::{
shared::{dxgi, dxgi1_2, winerror},
um::d3d12,
};
impl Drop for super::Adapter {
fn drop(&mut self) {
unsafe {
self.raw.destroy();
}
}
}
impl super::Adapter {
#[allow(trivial_casts)]
pub(super) fn expose(
adapter: native::WeakPtr<dxgi1_2::IDXGIAdapter2>,
library: &Arc<native::D3D12Lib>,
) -> Option<crate::ExposedAdapter<super::Api>> {
// Create the device so that we can get the capabilities.
let device = match library.create_device(adapter, native::FeatureLevel::L11_0) {
Ok(pair) => match pair.check() {
Ok(device) => device,
Err(err) => {
log::warn!("Device creation failed: {}", err);
return None;
}
},
Err(err) => {
log::warn!("Device creation function is not found: {:?}", err);
return None;
}
};
// We have found a possible adapter.
// Acquire the device information.
let mut desc: dxgi1_2::DXGI_ADAPTER_DESC2 = unsafe { mem::zeroed() };
unsafe {
adapter.GetDesc2(&mut desc);
}
let device_name = {
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
let len = desc.Description.iter().take_while(|&&c| c != 0).count();
let name = OsString::from_wide(&desc.Description[..len]);
name.to_string_lossy().into_owned()
};
let mut features_architecture: d3d12::D3D12_FEATURE_DATA_ARCHITECTURE =
unsafe { mem::zeroed() };
assert_eq!(0, unsafe {
device.CheckFeatureSupport(
d3d12::D3D12_FEATURE_ARCHITECTURE,
&mut features_architecture as *mut _ as *mut _,
mem::size_of::<d3d12::D3D12_FEATURE_DATA_ARCHITECTURE>() as _,
)
});
let mut workarounds = super::Workarounds::default();
let info = wgt::AdapterInfo {
backend: wgt::Backend::Dx12,
name: device_name,
vendor: desc.VendorId as usize,
device: desc.DeviceId as usize,
device_type: if (desc.Flags & dxgi::DXGI_ADAPTER_FLAG_SOFTWARE) != 0 {
workarounds.avoid_cpu_descriptor_overwrites = true;
wgt::DeviceType::VirtualGpu
} else if features_architecture.CacheCoherentUMA != 0 {
wgt::DeviceType::IntegratedGpu
} else {
wgt::DeviceType::DiscreteGpu
},
};
let mut options: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS = unsafe { mem::zeroed() };
assert_eq!(0, unsafe {
device.CheckFeatureSupport(
d3d12::D3D12_FEATURE_D3D12_OPTIONS,
&mut options as *mut _ as *mut _,
mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS>() as _,
)
});
let _depth_bounds_test_supported = {
let mut features2: d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2 = unsafe { mem::zeroed() };
let hr = unsafe {
device.CheckFeatureSupport(
d3d12::D3D12_FEATURE_D3D12_OPTIONS2,
&mut features2 as *mut _ as *mut _,
mem::size_of::<d3d12::D3D12_FEATURE_DATA_D3D12_OPTIONS2>() as _,
)
};
hr == 0 && features2.DepthBoundsTestSupported != 0
};
let private_caps = super::PrivateCapabilities {
heterogeneous_resource_heaps: options.ResourceHeapTier
!= d3d12::D3D12_RESOURCE_HEAP_TIER_1,
memory_architecture: if features_architecture.UMA != 0 {
super::MemoryArchitecture::Unified {
cache_coherent: features_architecture.CacheCoherentUMA != 0,
}
} else {
super::MemoryArchitecture::NonUnified
},
};
// Theoretically vram limited, but in practice 2^20 is the limit
let tier3_practical_descriptor_limit = 1 << 20;
let (full_heap_count, _uav_count) = match options.ResourceBindingTier {
d3d12::D3D12_RESOURCE_BINDING_TIER_1 => (
d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1,
8, // conservative, is 64 on feature level 11.1
),
d3d12::D3D12_RESOURCE_BINDING_TIER_2 => (
d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_2,
64,
),
d3d12::D3D12_RESOURCE_BINDING_TIER_3 => (
tier3_practical_descriptor_limit,
tier3_practical_descriptor_limit,
),
other => {
log::warn!("Unknown resource binding tier {}", other);
(
d3d12::D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1,
8,
)
}
};
let mut features = wgt::Features::empty()
| wgt::Features::DEPTH_CLAMPING
//TODO: Naga part
//| wgt::Features::TEXTURE_BINDING_ARRAY
//| wgt::Features::BUFFER_BINDING_ARRAY
//| wgt::Features::STORAGE_RESOURCE_BINDING_ARRAY
//| wgt::Features::UNSIZED_BINDING_ARRAY
| wgt::Features::MULTI_DRAW_INDIRECT
| wgt::Features::MULTI_DRAW_INDIRECT_COUNT
| wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER
| wgt::Features::NON_FILL_POLYGON_MODE
|wgt::Features::VERTEX_WRITABLE_STORAGE;
features.set(
wgt::Features::CONSERVATIVE_RASTERIZATION,
options.ConservativeRasterizationTier
!= d3d12::D3D12_CONSERVATIVE_RASTERIZATION_TIER_NOT_SUPPORTED,
);
let base = wgt::Limits::default();
Some(crate::ExposedAdapter {
adapter: super::Adapter {
raw: adapter,
device,
library: Arc::clone(library),
private_caps,
workarounds,
},
info,
features,
capabilities: crate::Capabilities {
limits: wgt::Limits {
max_texture_dimension_1d: d3d12::D3D12_REQ_TEXTURE1D_U_DIMENSION,
max_texture_dimension_2d: d3d12::D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION
.min(d3d12::D3D12_REQ_TEXTURECUBE_DIMENSION),
max_texture_dimension_3d: d3d12::D3D12_REQ_TEXTURE3D_U_V_OR_W_DIMENSION,
max_texture_array_layers: d3d12::D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION,
max_bind_groups: crate::MAX_BIND_GROUPS as u32,
// dynamic offsets take a root constant, so we expose the minimum here
max_dynamic_uniform_buffers_per_pipeline_layout: base
.max_dynamic_uniform_buffers_per_pipeline_layout,
max_dynamic_storage_buffers_per_pipeline_layout: base
.max_dynamic_storage_buffers_per_pipeline_layout,
max_sampled_textures_per_shader_stage: match options.ResourceBindingTier {
d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 128,
d3d12::D3D12_RESOURCE_BINDING_TIER_2
| d3d12::D3D12_RESOURCE_BINDING_TIER_3
| _ => full_heap_count,
},
max_samplers_per_shader_stage: match options.ResourceBindingTier {
d3d12::D3D12_RESOURCE_BINDING_TIER_1 => 16,
d3d12::D3D12_RESOURCE_BINDING_TIER_2
| d3d12::D3D12_RESOURCE_BINDING_TIER_3
| _ => d3d12::D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE,
},
// these both account towards `uav_count`, but we can't express the limit as as sum
max_storage_buffers_per_shader_stage: base.max_storage_buffers_per_shader_stage,
max_storage_textures_per_shader_stage: base
.max_storage_textures_per_shader_stage,
max_uniform_buffers_per_shader_stage: full_heap_count,
max_uniform_buffer_binding_size: d3d12::D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT
* 16,
max_storage_buffer_binding_size: !0,
max_vertex_buffers: d3d12::D3D12_VS_INPUT_REGISTER_COUNT
.min(crate::MAX_VERTEX_BUFFERS as u32),
max_vertex_attributes: d3d12::D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT,
max_vertex_buffer_array_stride: d3d12::D3D12_SO_BUFFER_MAX_STRIDE_IN_BYTES,
max_push_constant_size: 0,
},
alignments: crate::Alignments {
buffer_copy_offset: wgt::BufferSize::new(
d3d12::D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT as u64,
)
.unwrap(),
buffer_copy_pitch: wgt::BufferSize::new(
d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT as u64,
)
.unwrap(),
uniform_buffer_offset: wgt::BufferSize::new(
d3d12::D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT as u64,
)
.unwrap(),
storage_buffer_offset: wgt::BufferSize::new(4).unwrap(), //TODO?
},
downlevel: wgt::DownlevelCapabilities::default(),
},
})
}
}
impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
_features: wgt::Features,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let queue = self
.device
.create_command_queue(
native::CmdListType::Direct,
native::Priority::Normal,
native::CommandQueueFlags::empty(),
0,
)
.check()
.map_err(|err| {
log::warn!("Queue creation failed: {}", err);
crate::DeviceError::OutOfMemory
})?;
Ok(crate::OpenDevice {
device: super::Device { raw: self.device },
queue: super::Queue { raw: queue },
})
}
#[allow(trivial_casts)]
unsafe fn texture_format_capabilities(
&self,
format: wgt::TextureFormat,
) -> crate::TextureFormatCapabilities {
use crate::TextureFormatCapabilities as Tfc;
let info = format.describe();
let is_compressed = info.block_dimensions != (1, 1);
let raw_format = conv::map_texture_format(format);
let mut data = d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT {
Format: raw_format,
Support1: mem::zeroed(),
Support2: mem::zeroed(),
};
assert_eq!(
winerror::S_OK,
self.device.CheckFeatureSupport(
d3d12::D3D12_FEATURE_FORMAT_SUPPORT,
&mut data as *mut _ as *mut _,
mem::size_of::<d3d12::D3D12_FEATURE_DATA_FORMAT_SUPPORT>() as _,
)
);
let mut caps = Tfc::COPY_SRC | Tfc::COPY_DST;
let can_image = 0
!= data.Support1
& (d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE1D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE2D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURE3D
| d3d12::D3D12_FORMAT_SUPPORT1_TEXTURECUBE);
caps.set(Tfc::SAMPLED, can_image);
caps.set(
Tfc::SAMPLED_LINEAR,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE != 0,
);
caps.set(
Tfc::COLOR_ATTACHMENT,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_RENDER_TARGET != 0,
);
caps.set(
Tfc::COLOR_ATTACHMENT_BLEND,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_BLENDABLE != 0,
);
caps.set(
Tfc::DEPTH_STENCIL_ATTACHMENT,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL != 0,
);
caps.set(
Tfc::STORAGE,
data.Support1 & d3d12::D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW != 0,
);
caps.set(
Tfc::STORAGE_READ_WRITE,
data.Support2 & d3d12::D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD != 0,
);
caps
}
unsafe fn surface_capabilities(
&self,
surface: &super::Surface,
) -> Option<crate::SurfaceCapabilities> {
None
}
}

97
wgpu-hal/src/dx12/conv.rs Normal file
View File

@ -0,0 +1,97 @@
use winapi::shared::dxgiformat;
pub(super) fn map_texture_format(format: wgt::TextureFormat) -> dxgiformat::DXGI_FORMAT {
use wgt::TextureFormat as Tf;
use winapi::shared::dxgiformat::*;
match format {
Tf::R8Unorm => DXGI_FORMAT_R8_UNORM,
Tf::R8Snorm => DXGI_FORMAT_R8_SNORM,
Tf::R8Uint => DXGI_FORMAT_R8_UINT,
Tf::R8Sint => DXGI_FORMAT_R8_SINT,
Tf::R16Uint => DXGI_FORMAT_R16_UINT,
Tf::R16Sint => DXGI_FORMAT_R16_SINT,
Tf::R16Float => DXGI_FORMAT_R16_FLOAT,
Tf::Rg8Unorm => DXGI_FORMAT_R8G8_UNORM,
Tf::Rg8Snorm => DXGI_FORMAT_R8G8_SNORM,
Tf::Rg8Uint => DXGI_FORMAT_R8G8_UINT,
Tf::Rg8Sint => DXGI_FORMAT_R8G8_SINT,
Tf::R32Uint => DXGI_FORMAT_R32_UINT,
Tf::R32Sint => DXGI_FORMAT_R32_SINT,
Tf::R32Float => DXGI_FORMAT_R32_FLOAT,
Tf::Rg16Uint => DXGI_FORMAT_R16G16_UINT,
Tf::Rg16Sint => DXGI_FORMAT_R16G16_SINT,
Tf::Rg16Float => DXGI_FORMAT_R16G16_FLOAT,
Tf::Rgba8Unorm => DXGI_FORMAT_R8G8B8A8_UNORM,
Tf::Rgba8UnormSrgb => DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
Tf::Bgra8UnormSrgb => DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
Tf::Rgba8Snorm => DXGI_FORMAT_R8G8B8A8_SNORM,
Tf::Bgra8Unorm => DXGI_FORMAT_B8G8R8A8_UNORM,
Tf::Rgba8Uint => DXGI_FORMAT_R8G8B8A8_UINT,
Tf::Rgba8Sint => DXGI_FORMAT_R8G8B8A8_SINT,
Tf::Rgb10a2Unorm => DXGI_FORMAT_R10G10B10A2_UNORM,
Tf::Rg11b10Float => DXGI_FORMAT_R11G11B10_FLOAT,
Tf::Rg32Uint => DXGI_FORMAT_R32G32_UINT,
Tf::Rg32Sint => DXGI_FORMAT_R32G32_SINT,
Tf::Rg32Float => DXGI_FORMAT_R32G32_FLOAT,
Tf::Rgba16Uint => DXGI_FORMAT_R16G16B16A16_UINT,
Tf::Rgba16Sint => DXGI_FORMAT_R16G16B16A16_SINT,
Tf::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT,
Tf::Rgba32Uint => DXGI_FORMAT_R32G32B32A32_UINT,
Tf::Rgba32Sint => DXGI_FORMAT_R32G32B32A32_SINT,
Tf::Rgba32Float => DXGI_FORMAT_R32G32B32A32_FLOAT,
Tf::Depth32Float => DXGI_FORMAT_D32_FLOAT,
Tf::Depth24Plus => DXGI_FORMAT_D24_UNORM_S8_UINT,
Tf::Depth24PlusStencil8 => DXGI_FORMAT_D24_UNORM_S8_UINT,
Tf::Bc1RgbaUnorm => DXGI_FORMAT_BC1_UNORM,
Tf::Bc1RgbaUnormSrgb => DXGI_FORMAT_BC1_UNORM_SRGB,
Tf::Bc2RgbaUnorm => DXGI_FORMAT_BC2_UNORM,
Tf::Bc2RgbaUnormSrgb => DXGI_FORMAT_BC2_UNORM_SRGB,
Tf::Bc3RgbaUnorm => DXGI_FORMAT_BC3_UNORM,
Tf::Bc3RgbaUnormSrgb => DXGI_FORMAT_BC3_UNORM_SRGB,
Tf::Bc4RUnorm => DXGI_FORMAT_BC4_UNORM,
Tf::Bc4RSnorm => DXGI_FORMAT_BC4_SNORM,
Tf::Bc5RgUnorm => DXGI_FORMAT_BC5_UNORM,
Tf::Bc5RgSnorm => DXGI_FORMAT_BC5_SNORM,
Tf::Bc6hRgbUfloat => DXGI_FORMAT_BC6H_UF16,
Tf::Bc6hRgbSfloat => DXGI_FORMAT_BC6H_SF16,
Tf::Bc7RgbaUnorm => DXGI_FORMAT_BC7_UNORM,
Tf::Bc7RgbaUnormSrgb => DXGI_FORMAT_BC7_UNORM_SRGB,
Tf::Etc2RgbUnorm
| Tf::Etc2RgbUnormSrgb
| Tf::Etc2RgbA1Unorm
| Tf::Etc2RgbA1UnormSrgb
| Tf::EacRUnorm
| Tf::EacRSnorm
| Tf::EacRgUnorm
| Tf::EacRgSnorm
| Tf::Astc4x4RgbaUnorm
| Tf::Astc4x4RgbaUnormSrgb
| Tf::Astc5x4RgbaUnorm
| Tf::Astc5x4RgbaUnormSrgb
| Tf::Astc5x5RgbaUnorm
| Tf::Astc5x5RgbaUnormSrgb
| Tf::Astc6x5RgbaUnorm
| Tf::Astc6x5RgbaUnormSrgb
| Tf::Astc6x6RgbaUnorm
| Tf::Astc6x6RgbaUnormSrgb
| Tf::Astc8x5RgbaUnorm
| Tf::Astc8x5RgbaUnormSrgb
| Tf::Astc8x6RgbaUnorm
| Tf::Astc8x6RgbaUnormSrgb
| Tf::Astc10x5RgbaUnorm
| Tf::Astc10x5RgbaUnormSrgb
| Tf::Astc10x6RgbaUnorm
| Tf::Astc10x6RgbaUnormSrgb
| Tf::Astc8x8RgbaUnorm
| Tf::Astc8x8RgbaUnormSrgb
| Tf::Astc10x8RgbaUnorm
| Tf::Astc10x8RgbaUnormSrgb
| Tf::Astc10x10RgbaUnorm
| Tf::Astc10x10RgbaUnormSrgb
| Tf::Astc12x10RgbaUnorm
| Tf::Astc12x10RgbaUnormSrgb
| Tf::Astc12x12RgbaUnorm
| Tf::Astc12x12RgbaUnormSrgb => unreachable!(),
}
}

627
wgpu-hal/src/dx12/mod.rs Normal file
View File

@ -0,0 +1,627 @@
/*!
# DirectX12 API internals.
## Pipeline Layout
!*/
#![allow(unused_variables)]
mod adapter;
mod conv;
use std::{borrow::Cow, ops::Range, sync::Arc};
use winapi::{
shared::{dxgi, dxgi1_2, dxgi1_4, dxgi1_6, windef, winerror},
Interface as _,
};
#[derive(Clone)]
pub struct Api;
//TODO: remove these temporaries
pub struct Encoder;
#[derive(Debug)]
pub struct Resource;
type DeviceResult<T> = Result<T, crate::DeviceError>;
impl crate::Api for Api {
type Instance = Instance;
type Surface = Surface;
type Adapter = Adapter;
type Device = Device;
type Queue = Queue;
type CommandEncoder = Encoder;
type CommandBuffer = Resource;
type Buffer = Resource;
type Texture = Resource;
type SurfaceTexture = Resource;
type TextureView = Resource;
type Sampler = Resource;
type QuerySet = Resource;
type Fence = Resource;
type BindGroupLayout = Resource;
type BindGroup = Resource;
type PipelineLayout = Resource;
type ShaderModule = Resource;
type RenderPipeline = Resource;
type ComputePipeline = Resource;
}
trait HResult {
fn to_error(self) -> Option<Cow<'static, str>>;
}
impl HResult for i32 {
fn to_error(self) -> Option<Cow<'static, str>> {
if self >= 0 {
return None;
}
let description = match self {
winerror::E_UNEXPECTED => "unexpected",
winerror::E_NOTIMPL => "not implemented",
winerror::E_OUTOFMEMORY => "out of memory",
winerror::E_INVALIDARG => "invalid argument",
_ => return Some(Cow::Owned(format!("0x{:X}", self as u32))),
};
Some(Cow::Borrowed(description))
}
}
trait HResultPair {
type Object;
fn check(self) -> Result<Self::Object, Cow<'static, str>>;
}
impl<T> HResultPair for (T, i32) {
type Object = T;
fn check(self) -> Result<T, Cow<'static, str>> {
match self.1.to_error() {
None => Ok(self.0),
Some(err) => Err(err),
}
}
}
pub struct Instance {
factory: native::Factory4,
library: Arc<native::D3D12Lib>,
lib_dxgi: native::DxgiLib,
}
impl Drop for Instance {
fn drop(&mut self) {
unsafe {
self.factory.destroy();
}
}
}
unsafe impl Send for Instance {}
unsafe impl Sync for Instance {}
pub struct Surface {
factory: native::WeakPtr<dxgi1_4::IDXGIFactory4>,
wnd_handle: windef::HWND,
//presentation: Option<Presentation>,
}
unsafe impl Send for Surface {}
unsafe impl Sync for Surface {}
#[derive(Debug, Clone, Copy)]
enum MemoryArchitecture {
Unified { cache_coherent: bool },
NonUnified,
}
#[derive(Debug, Clone, Copy)]
struct PrivateCapabilities {
heterogeneous_resource_heaps: bool,
memory_architecture: MemoryArchitecture,
}
#[derive(Default)]
struct Workarounds {
// On WARP, temporary CPU descriptors are still used by the runtime
// after we call `CopyDescriptors`.
avoid_cpu_descriptor_overwrites: bool,
}
pub struct Adapter {
raw: native::WeakPtr<dxgi1_2::IDXGIAdapter2>,
device: native::Device,
library: Arc<native::D3D12Lib>,
private_caps: PrivateCapabilities,
workarounds: Workarounds,
}
unsafe impl Send for Adapter {}
unsafe impl Sync for Adapter {}
pub struct Device {
raw: native::Device,
}
unsafe impl Send for Device {}
unsafe impl Sync for Device {}
pub struct Queue {
raw: native::CommandQueue,
}
unsafe impl Send for Queue {}
unsafe impl Sync for Queue {}
impl crate::Instance<Api> for Instance {
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
let lib_main = native::D3D12Lib::new().map_err(|_| crate::InstanceError)?;
let lib_dxgi = native::DxgiLib::new().map_err(|_| crate::InstanceError)?;
let mut factory_flags = native::FactoryCreationFlags::empty();
if desc.flags.contains(crate::InstanceFlags::VALIDATION) {
// Enable debug layer
match lib_main.get_debug_interface() {
Ok(pair) => match pair.check() {
Ok(debug_controller) => {
debug_controller.enable_layer();
debug_controller.Release();
}
Err(err) => {
log::warn!("Unable to enable D3D12 debug interface: {}", err);
}
},
Err(err) => {
log::warn!("Debug interface function for D3D12 not found: {:?}", err);
}
}
// The `DXGI_CREATE_FACTORY_DEBUG` flag is only allowed to be passed to
// `CreateDXGIFactory2` if the debug interface is actually available. So
// we check for whether it exists first.
match lib_dxgi.get_debug_interface1() {
Ok(pair) => match pair.check() {
Ok(debug_controller) => {
debug_controller.destroy();
factory_flags |= native::FactoryCreationFlags::DEBUG;
}
Err(err) => {
log::warn!("Unable to enable DXGI debug interface: {}", err);
}
},
Err(err) => {
log::warn!("Debug interface function for DXGI not found: {:?}", err);
}
}
}
// Create DXGI factory
let factory = match lib_dxgi.create_factory2(factory_flags) {
Ok(pair) => match pair.check() {
Ok(factory) => factory,
Err(err) => {
log::warn!("Failed to create DXGI factory: {}", err);
return Err(crate::InstanceError);
}
},
Err(err) => {
log::warn!("Factory creation function for DXGI not found: {:?}", err);
return Err(crate::InstanceError);
}
};
Ok(Self {
factory,
library: Arc::new(lib_main),
lib_dxgi,
})
}
unsafe fn create_surface(
&self,
has_handle: &impl raw_window_handle::HasRawWindowHandle,
) -> Result<Surface, crate::InstanceError> {
match has_handle.raw_window_handle() {
raw_window_handle::RawWindowHandle::Windows(handle) => {
Ok(Surface {
factory: self.factory,
wnd_handle: handle.hwnd as *mut _,
//presentation: None,
})
}
_ => Err(crate::InstanceError),
}
}
unsafe fn destroy_surface(&self, _surface: Surface) {
// just drop
}
unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<Api>> {
// Try to use high performance order by default (returns None on Windows < 1803)
let factory6 = match self.factory.cast::<dxgi1_6::IDXGIFactory6>().check() {
Ok(f6) => {
// It's okay to decrement the refcount here because we
// have another reference to the factory already owned by `self`.
f6.destroy();
Some(f6)
}
Err(err) => {
log::info!("Failed to cast DXGI to 1.6: {}", err);
None
}
};
// Enumerate adapters
let mut adapters = Vec::new();
for cur_index in 0.. {
let raw = match factory6 {
Some(factory) => {
let mut adapter2 = native::WeakPtr::<dxgi1_2::IDXGIAdapter2>::null();
let hr = factory.EnumAdapterByGpuPreference(
cur_index,
dxgi1_6::DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE,
&dxgi1_2::IDXGIAdapter2::uuidof(),
adapter2.mut_void(),
);
if hr == winerror::DXGI_ERROR_NOT_FOUND {
break;
}
if let Some(err) = hr.to_error() {
log::error!("Failed enumerating adapters: {}", err);
break;
}
adapter2
}
None => {
let mut adapter1 = native::WeakPtr::<dxgi::IDXGIAdapter1>::null();
let hr = self
.factory
.EnumAdapters1(cur_index, adapter1.mut_void() as *mut *mut _);
if hr == winerror::DXGI_ERROR_NOT_FOUND {
break;
}
if let Some(err) = hr.to_error() {
log::error!("Failed enumerating adapters: {}", err);
break;
}
match adapter1.cast::<dxgi1_2::IDXGIAdapter2>().check() {
Ok(adapter2) => {
adapter1.destroy();
adapter2
}
Err(err) => {
log::error!("Failed casting to Adapter2: {}", err);
break;
}
}
}
};
adapters.extend(Adapter::expose(raw, &self.library));
}
adapters
}
}
impl crate::Surface<Api> for Surface {
unsafe fn configure(
&mut self,
device: &Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
Ok(())
}
unsafe fn unconfigure(&mut self, device: &Device) {}
unsafe fn acquire_texture(
&mut self,
timeout_ms: u32,
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
Ok(None)
}
unsafe fn discard_texture(&mut self, texture: Resource) {}
}
impl crate::Queue<Api> for Queue {
unsafe fn submit(
&mut self,
command_buffers: &[&Resource],
signal_fence: Option<(&mut Resource, crate::FenceValue)>,
) -> DeviceResult<()> {
Ok(())
}
unsafe fn present(
&mut self,
surface: &mut Surface,
texture: Resource,
) -> Result<(), crate::SurfaceError> {
Ok(())
}
}
impl crate::Device<Api> for Device {
unsafe fn exit(self) {}
unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_buffer(&self, buffer: Resource) {}
unsafe fn map_buffer(
&self,
buffer: &Resource,
range: crate::MemoryRange,
) -> DeviceResult<crate::BufferMapping> {
Err(crate::DeviceError::Lost)
}
unsafe fn unmap_buffer(&self, buffer: &Resource) -> DeviceResult<()> {
Ok(())
}
unsafe fn flush_mapped_ranges<I>(&self, buffer: &Resource, ranges: I) {}
unsafe fn invalidate_mapped_ranges<I>(&self, buffer: &Resource, ranges: I) {}
unsafe fn create_texture(&self, desc: &crate::TextureDescriptor) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_texture(&self, texture: Resource) {}
unsafe fn create_texture_view(
&self,
texture: &Resource,
desc: &crate::TextureViewDescriptor,
) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_texture_view(&self, view: Resource) {}
unsafe fn create_sampler(&self, desc: &crate::SamplerDescriptor) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_sampler(&self, sampler: Resource) {}
unsafe fn create_command_encoder(
&self,
desc: &crate::CommandEncoderDescriptor<Api>,
) -> DeviceResult<Encoder> {
Ok(Encoder)
}
unsafe fn destroy_command_encoder(&self, encoder: Encoder) {}
unsafe fn create_bind_group_layout(
&self,
desc: &crate::BindGroupLayoutDescriptor,
) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_bind_group_layout(&self, bg_layout: Resource) {}
unsafe fn create_pipeline_layout(
&self,
desc: &crate::PipelineLayoutDescriptor<Api>,
) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_pipeline_layout(&self, pipeline_layout: Resource) {}
unsafe fn create_bind_group(
&self,
desc: &crate::BindGroupDescriptor<Api>,
) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_bind_group(&self, group: Resource) {}
unsafe fn create_shader_module(
&self,
desc: &crate::ShaderModuleDescriptor,
shader: crate::ShaderInput,
) -> Result<Resource, crate::ShaderError> {
Ok(Resource)
}
unsafe fn destroy_shader_module(&self, module: Resource) {}
unsafe fn create_render_pipeline(
&self,
desc: &crate::RenderPipelineDescriptor<Api>,
) -> Result<Resource, crate::PipelineError> {
Ok(Resource)
}
unsafe fn destroy_render_pipeline(&self, pipeline: Resource) {}
unsafe fn create_compute_pipeline(
&self,
desc: &crate::ComputePipelineDescriptor<Api>,
) -> Result<Resource, crate::PipelineError> {
Ok(Resource)
}
unsafe fn destroy_compute_pipeline(&self, pipeline: Resource) {}
unsafe fn create_query_set(
&self,
desc: &wgt::QuerySetDescriptor<crate::Label>,
) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_query_set(&self, set: Resource) {}
unsafe fn create_fence(&self) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn destroy_fence(&self, fence: Resource) {}
unsafe fn get_fence_value(&self, fence: &Resource) -> DeviceResult<crate::FenceValue> {
Ok(0)
}
unsafe fn wait(
&self,
fence: &Resource,
value: crate::FenceValue,
timeout_ms: u32,
) -> DeviceResult<bool> {
Ok(true)
}
unsafe fn start_capture(&self) -> bool {
false
}
unsafe fn stop_capture(&self) {}
}
impl crate::CommandEncoder<Api> for Encoder {
unsafe fn begin_encoding(&mut self, label: crate::Label) -> DeviceResult<()> {
Ok(())
}
unsafe fn discard_encoding(&mut self) {}
unsafe fn end_encoding(&mut self) -> DeviceResult<Resource> {
Ok(Resource)
}
unsafe fn reset_all<I>(&mut self, command_buffers: I) {}
unsafe fn transition_buffers<'a, T>(&mut self, barriers: T)
where
T: Iterator<Item = crate::BufferBarrier<'a, Api>>,
{
}
unsafe fn transition_textures<'a, T>(&mut self, barriers: T)
where
T: Iterator<Item = crate::TextureBarrier<'a, Api>>,
{
}
unsafe fn fill_buffer(&mut self, buffer: &Resource, range: crate::MemoryRange, value: u8) {}
unsafe fn copy_buffer_to_buffer<T>(&mut self, src: &Resource, dst: &Resource, regions: T) {}
unsafe fn copy_texture_to_texture<T>(
&mut self,
src: &Resource,
src_usage: crate::TextureUses,
dst: &Resource,
regions: T,
) {
}
unsafe fn copy_buffer_to_texture<T>(&mut self, src: &Resource, dst: &Resource, regions: T) {}
unsafe fn copy_texture_to_buffer<T>(
&mut self,
src: &Resource,
src_usage: crate::TextureUses,
dst: &Resource,
regions: T,
) {
}
unsafe fn begin_query(&mut self, set: &Resource, index: u32) {}
unsafe fn end_query(&mut self, set: &Resource, index: u32) {}
unsafe fn write_timestamp(&mut self, set: &Resource, index: u32) {}
unsafe fn reset_queries(&mut self, set: &Resource, range: Range<u32>) {}
unsafe fn copy_query_results(
&mut self,
set: &Resource,
range: Range<u32>,
buffer: &Resource,
offset: wgt::BufferAddress,
stride: wgt::BufferSize,
) {
}
// render
unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor<Api>) {}
unsafe fn end_render_pass(&mut self) {}
unsafe fn set_bind_group(
&mut self,
layout: &Resource,
index: u32,
group: &Resource,
dynamic_offsets: &[wgt::DynamicOffset],
) {
}
unsafe fn set_push_constants(
&mut self,
layout: &Resource,
stages: wgt::ShaderStages,
offset: u32,
data: &[u32],
) {
}
unsafe fn insert_debug_marker(&mut self, label: &str) {}
unsafe fn begin_debug_marker(&mut self, group_label: &str) {}
unsafe fn end_debug_marker(&mut self) {}
unsafe fn set_render_pipeline(&mut self, pipeline: &Resource) {}
unsafe fn set_index_buffer<'a>(
&mut self,
binding: crate::BufferBinding<'a, Api>,
format: wgt::IndexFormat,
) {
}
unsafe fn set_vertex_buffer<'a>(&mut self, index: u32, binding: crate::BufferBinding<'a, Api>) {
}
unsafe fn set_viewport(&mut self, rect: &crate::Rect<f32>, depth_range: Range<f32>) {}
unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect<u32>) {}
unsafe fn set_stencil_reference(&mut self, value: u32) {}
unsafe fn set_blend_constants(&mut self, color: &wgt::Color) {}
unsafe fn draw(
&mut self,
start_vertex: u32,
vertex_count: u32,
start_instance: u32,
instance_count: u32,
) {
}
unsafe fn draw_indexed(
&mut self,
start_index: u32,
index_count: u32,
base_vertex: i32,
start_instance: u32,
instance_count: u32,
) {
}
unsafe fn draw_indirect(
&mut self,
buffer: &Resource,
offset: wgt::BufferAddress,
draw_count: u32,
) {
}
unsafe fn draw_indexed_indirect(
&mut self,
buffer: &Resource,
offset: wgt::BufferAddress,
draw_count: u32,
) {
}
unsafe fn draw_indirect_count(
&mut self,
buffer: &Resource,
offset: wgt::BufferAddress,
count_buffer: &Resource,
count_offset: wgt::BufferAddress,
max_count: u32,
) {
}
unsafe fn draw_indexed_indirect_count(
&mut self,
buffer: &Resource,
offset: wgt::BufferAddress,
count_buffer: &Resource,
count_offset: wgt::BufferAddress,
max_count: u32,
) {
}
// compute
unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor) {}
unsafe fn end_compute_pass(&mut self) {}
unsafe fn set_compute_pipeline(&mut self, pipeline: &Resource) {}
unsafe fn dispatch(&mut self, count: [u32; 3]) {}
unsafe fn dispatch_indirect(&mut self, buffer: &Resource, offset: wgt::BufferAddress) {}
}

View File

@ -204,7 +204,9 @@ impl super::Adapter {
vertex_shader_storage_blocks.min(fragment_shader_storage_blocks)
};
let mut features = wgt::Features::empty() | wgt::Features::TEXTURE_COMPRESSION_ETC2;
let mut features = wgt::Features::empty()
| wgt::Features::TEXTURE_COMPRESSION_ETC2
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
features.set(
wgt::Features::DEPTH_CLAMPING,
extensions.contains("GL_EXT_depth_clamp"),

View File

@ -70,8 +70,8 @@ impl super::AdapterShared {
| Tf::Bc4RSnorm
| Tf::Bc5RgUnorm
| Tf::Bc5RgSnorm
| Tf::Bc6hRgbSfloat
| Tf::Bc6hRgbUfloat
| Tf::Bc6hRgbSfloat
| Tf::Bc7RgbaUnorm
| Tf::Bc7RgbaUnormSrgb => unimplemented!(),
Tf::Etc2RgbUnorm => (glow::COMPRESSED_RGB8_ETC2, glow::RGB, 0),

View File

@ -44,6 +44,8 @@
#[cfg(all(feature = "metal", not(any(target_os = "macos", target_os = "ios"))))]
compile_error!("Metal backend enabled on non-Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should.");
#[cfg(all(feature = "dx12", windows))]
mod dx12;
mod empty;
#[cfg(feature = "gles")]
mod gles;
@ -54,6 +56,8 @@ mod vulkan;
pub mod util;
pub mod api {
#[cfg(feature = "dx12")]
pub use super::dx12::Api as Dx12;
pub use super::empty::Api as Empty;
#[cfg(feature = "gles")]
pub use super::gles::Api as Gles;

View File

@ -199,8 +199,8 @@ impl crate::Adapter<super::Api> for super::Adapter {
| Tf::Bc4RSnorm
| Tf::Bc5RgUnorm
| Tf::Bc5RgSnorm
| Tf::Bc6hRgbSfloat
| Tf::Bc6hRgbUfloat
| Tf::Bc6hRgbSfloat
| Tf::Bc7RgbaUnorm
| Tf::Bc7RgbaUnormSrgb => {
if pc.format_bc {
@ -889,6 +889,7 @@ impl super::PrivateCapabilities {
.flags
.set(wgt::DownlevelFlags::ANISOTROPIC_FILTERING, true);
let base = wgt::Limits::default();
crate::Capabilities {
limits: wgt::Limits {
max_texture_dimension_1d: self.max_texture_size as u32,
@ -896,18 +897,20 @@ impl super::PrivateCapabilities {
max_texture_dimension_3d: self.max_texture_3d_size as u32,
max_texture_array_layers: self.max_texture_layers as u32,
max_bind_groups: 8,
max_dynamic_uniform_buffers_per_pipeline_layout: 8,
max_dynamic_storage_buffers_per_pipeline_layout: 4,
max_sampled_textures_per_shader_stage: 16,
max_dynamic_uniform_buffers_per_pipeline_layout: base
.max_dynamic_uniform_buffers_per_pipeline_layout,
max_dynamic_storage_buffers_per_pipeline_layout: base
.max_dynamic_storage_buffers_per_pipeline_layout,
max_sampled_textures_per_shader_stage: base.max_sampled_textures_per_shader_stage,
max_samplers_per_shader_stage: self.max_samplers_per_stage,
max_storage_buffers_per_shader_stage: 8,
max_storage_textures_per_shader_stage: 8,
max_storage_buffers_per_shader_stage: base.max_storage_buffers_per_shader_stage,
max_storage_textures_per_shader_stage: base.max_storage_textures_per_shader_stage,
max_uniform_buffers_per_shader_stage: 12,
max_uniform_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32,
max_storage_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32,
max_vertex_buffers: 8,
max_vertex_attributes: 16,
max_vertex_buffer_array_stride: 2048,
max_vertex_buffers: base.max_vertex_buffers,
max_vertex_attributes: base.max_vertex_attributes,
max_vertex_buffer_array_stride: base.max_vertex_buffer_array_stride,
max_push_constant_size: 0x1000,
},
alignments: crate::Alignments {

View File

@ -66,8 +66,8 @@ impl super::PrivateCapabilities {
Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK,
Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK,
Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK,
Tf::Bc6hRgbSfloat => F::BC6H_SFLOAT_BLOCK,
Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK,
Tf::Bc6hRgbSfloat => F::BC6H_SFLOAT_BLOCK,
Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK,
Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK,
Tf::Etc2RgbUnorm => F::ETC2_R8G8B8_UNORM_BLOCK,