use sim_lib_compute_auto::{ComputeDeviceIdentity, ComputeEvidenceKind, ComputePhysicalEvidence};
use wgpu::{Backends, Features, Limits};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestedWgpuProfile {
pub limits: WgpuLimitEvidence,
pub timestamp_query: bool,
pub shader_f16: bool,
}
impl RequestedWgpuProfile {
pub fn from_parts(limits: Limits, features: Features) -> Self {
Self {
limits: WgpuLimitEvidence::from_limits(&limits),
timestamp_query: features.contains(Features::TIMESTAMP_QUERY),
shader_f16: features.contains(Features::SHADER_F16),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WgpuLimitEvidence {
pub max_buffer_size: u64,
pub max_storage_buffer_binding_size: u64,
pub max_uniform_buffer_binding_size: u64,
pub min_storage_buffer_offset_alignment: u32,
pub min_uniform_buffer_offset_alignment: u32,
pub max_compute_workgroups_per_dimension: u32,
pub max_compute_invocations_per_workgroup: u32,
pub max_compute_workgroup_size_x: u32,
pub max_compute_workgroup_size_y: u32,
pub max_compute_workgroup_size_z: u32,
}
impl WgpuLimitEvidence {
pub fn from_limits(limits: &Limits) -> Self {
Self {
max_buffer_size: limits.max_buffer_size,
max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size,
max_uniform_buffer_binding_size: limits.max_uniform_buffer_binding_size,
min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment,
min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment,
max_compute_workgroups_per_dimension: limits.max_compute_workgroups_per_dimension,
max_compute_invocations_per_workgroup: limits.max_compute_invocations_per_workgroup,
max_compute_workgroup_size_x: limits.max_compute_workgroup_size_x,
max_compute_workgroup_size_y: limits.max_compute_workgroup_size_y,
max_compute_workgroup_size_z: limits.max_compute_workgroup_size_z,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WgpuCapabilityEvidence {
pub timestamp_query: bool,
pub shader_f16: bool,
pub mappable_primary_buffers: bool,
}
impl WgpuCapabilityEvidence {
pub fn from_features(features: Features) -> Self {
Self {
timestamp_query: features.contains(Features::TIMESTAMP_QUERY),
shader_f16: features.contains(Features::SHADER_F16),
mappable_primary_buffers: features.contains(Features::MAPPABLE_PRIMARY_BUFFERS),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WgpuAdapterEvidence {
pub ordinal: usize,
pub name: String,
pub backend: String,
pub adapter_type: String,
pub vendor: u32,
pub device: u32,
pub requested: RequestedWgpuProfile,
pub granted_limits: WgpuLimitEvidence,
pub granted_features: WgpuCapabilityEvidence,
}
impl WgpuAdapterEvidence {
pub fn sort_key(&self) -> (&str, &str, &str, u32, u32) {
(
self.backend.as_str(),
self.adapter_type.as_str(),
self.name.as_str(),
self.vendor,
self.device,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransferEvidence {
pub bytes: u64,
pub transfer_ok: bool,
pub mapping_ok: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AllocationAttempt {
pub bytes: u64,
pub success: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProbeEvidence {
pub transfer: TransferEvidence,
pub allocation_attempts: Vec<AllocationAttempt>,
}
impl ProbeEvidence {
pub fn successful(&self) -> bool {
self.transfer.transfer_ok
&& self.transfer.mapping_ok
&& self
.allocation_attempts
.iter()
.any(|attempt| attempt.success && attempt.bytes > 0)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WgpuAdapterProbe {
pub evidence_kind: ComputeEvidenceKind,
pub claimed_identity: Option<ComputeDeviceIdentity>,
pub observed_identity: Option<ComputeDeviceIdentity>,
pub adapter: WgpuAdapterEvidence,
pub probe: ProbeEvidence,
}
pub struct WgpuAdapterRuntime {
pub(crate) probe: WgpuAdapterProbe,
pub(crate) device: wgpu::Device,
pub(crate) queue: wgpu::Queue,
}
impl WgpuAdapterRuntime {
pub fn new(probe: WgpuAdapterProbe, device: wgpu::Device, queue: wgpu::Queue) -> Self {
Self {
probe,
device,
queue,
}
}
pub fn probe(&self) -> &WgpuAdapterProbe {
&self.probe
}
}
pub trait WgpuProbePort {
fn probe_wgpu(
&self,
policy: &ProbePolicy,
) -> Result<Vec<WgpuAdapterRuntime>, WgpuDiscoveryError>;
}
impl ComputePhysicalEvidence for WgpuAdapterProbe {
fn evidence_kind(&self) -> ComputeEvidenceKind {
self.evidence_kind
}
fn claimed_identity(&self) -> Option<&ComputeDeviceIdentity> {
self.claimed_identity.as_ref()
}
fn observed_identity(&self) -> Option<&ComputeDeviceIdentity> {
self.observed_identity.as_ref()
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct WgpuDiscovery {
pub adapters: Vec<WgpuAdapterProbe>,
pub diagnostics: Vec<String>,
}
impl WgpuDiscovery {
pub fn from_probes(probes: Vec<WgpuAdapterProbe>, mut diagnostics: Vec<String>) -> Self {
let mut adapters = Vec::new();
for probe in probes {
if probe.probe.successful() {
adapters.push(probe);
} else {
diagnostics.push(format!(
"wgpu adapter {} did not pass required probes",
probe.adapter.name
));
}
}
adapters.sort_by(|left, right| left.adapter.sort_key().cmp(&right.adapter.sort_key()));
for (ordinal, probe) in adapters.iter_mut().enumerate() {
probe.adapter.ordinal = ordinal;
}
Self {
adapters,
diagnostics,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProbePolicy {
pub backends: Backends,
pub transfer_bytes: u64,
pub max_allocation_probe_bytes: u64,
}
impl Default for ProbePolicy {
fn default() -> Self {
Self {
backends: Backends::all(),
transfer_bytes: 16,
max_allocation_probe_bytes: 16 * 1024 * 1024,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WgpuDiscoveryError {
message: String,
}
impl WgpuDiscoveryError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl std::fmt::Display for WgpuDiscoveryError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.message)
}
}
impl std::error::Error for WgpuDiscoveryError {}