Skip to main content

WgpuComputeContext

Struct WgpuComputeContext 

Source
pub struct WgpuComputeContext {
    pub device: Device,
    pub queue: Queue,
    pub adapter: AdapterIdentity,
    pub constraints: AdapterConstraints,
    pub profile: HardwareProfile,
    pub allocator: QualiaSlabAllocator,
    pub slab: Buffer,
    pub out_slab: Buffer,
    pub weight_slab: Buffer,
    pub timestamp_supported: bool,
    pub timestamp_period_ns: f32,
    /* private fields */
}

Fields§

§device: Device§queue: Queue§adapter: AdapterIdentity§constraints: AdapterConstraints§profile: HardwareProfile

Rich topology description for profile-hardware and cache keying.

§allocator: QualiaSlabAllocator§slab: Buffer

Backs read-only storage and uniform views (both non-exclusive usages, so they may share one buffer).

§out_slab: Buffer

Backs read-write storage outputs. wgpu treats read-write storage as an exclusive usage, so it cannot share a buffer with the read-only inputs in the same dispatch.

§weight_slab: Buffer

Backs the persistent weight region (BindingUsage::StorageReadResident): big, upload-once matrices (a decode layer’s projection / FFN weights) that are referenced by offset across many runs instead of being re-uploaded each call. Separate buffer from the transient ring so Self::clear_transient_allocations never recycles it.

§timestamp_supported: bool§timestamp_period_ns: f32

Implementations§

Source§

impl WgpuComputeContext

Source

pub fn new(capacity_bytes: usize) -> Result<Self, ForgeError>

Source

pub fn new_for_coopmat(capacity_bytes: usize) -> Result<Self, ForgeError>

Like [new] but tries the Vulkan backend first to find a cooperative-matrix- capable adapter. On Windows/DX12, EXPERIMENTAL_COOPERATIVE_MATRIX is not advertised, but the same NVIDIA GPU exposes VK_KHR_cooperative_matrix via the Vulkan driver. This constructor:

  1. Creates a Vulkan-only instance (unless QUALIA_WGPU_BACKEND overrides).
  2. Enumerates adapters, looking for one with EXPERIMENTAL_COOPERATIVE_MATRIX.
  3. If found, builds the context on that adapter (un-gating coopmat).
  4. If not found, falls back to [new] (which uses the default backend).

This is the primary un-gating path for the HLSL WaveMatrix / WGSL coopmat tensor-core emitters on NVIDIA hardware where DX12 doesn’t expose coopmat.

Source

pub fn from_device( device: Device, queue: Queue, caps: &GpuAdapterCaps, capacity_bytes: usize, ) -> Result<Self, ForgeError>

Build a forge context on an already-existing wgpu::Device + Queue (e.g. the process-wide crate::gpu_context::shared_gpu) instead of requesting a second adapter and device the way Self::new does. wgpu Device/Queue are cheap Arc clones, so the forge then runs on the same device that owns the resident LLM weights + KV cache — the device-unification keystone for running decode on the forge (LLM-on-forge plan, Phase 1a).

Adapter identity / constraints / hardware profile are reconstructed from the live device.limits() + device.features() plus the caller’s GpuAdapterCaps snapshot, because the original wgpu::Adapter is consumed at shared-gpu init and not retained.

Honest boundary: this inherits the host device’s negotiated features and limits verbatim. In particular, if the shared device was created without the ray-tracing acceleration-structure limits raised (as shared_gpu currently does), RT-core Neighbor cannot create BLAS/TLAS on this context even when supports_rt_cores is true — from_device does not silently widen the host device. The decode path (matmul/elementwise/reduce) needs none of that.

Source

pub fn allocate_and_write( &mut self, data: &[u8], binding: u32, group: u32, usage: BindingUsage, ) -> Result<BufferView, ForgeError>

Allocate a transient slab sub-range and upload data into it.

§Topology note (honest scope, plan §2)

This upload uses queue.write_buffer uniformly on every topology (unified and discrete alike); readback in Self::read_buffer_f32 likewise uses copy_buffer_to_buffer uniformly. The MemoryTopology::{Unified, Discrete} classification on the allocator is recorded but not yet acted upon here: the plan-§2 differentiated paths (zero-copy persistent-mapped slabs for unified memory; a pinned staging ring with async copy_buffer for discrete PCIe) are NOT implemented. The current uniform path is correct on both topologies but unoptimised; the unified zero-copy benefit cannot be measured on this discrete-only host (RTX A2000), so it is left as documented future work rather than shipped unverified. See MemoryTopology for the full rationale.

Source

pub fn allocate_weight( &mut self, data: &[u8], binding: u32, group: u32, ) -> Result<BufferView, ForgeError>

Bump-allocate data into the persistent weight region (weight_slab) and upload it once, returning a BufferView tagged BindingUsage::StorageReadResident. Unlike Self::allocate_and_write (transient ring), this view survives Self::clear_transient_allocations, so a decode layer’s projection / FFN matrices are uploaded a single time and referenced by offset across every token’s run — eliminating the per-call weight re-upload. Offsets are 256-aligned for direct bind-group use.

Source

pub fn clear_weights(&mut self)

Reset the persistent weight region so it can be reused for a different model/layer set. Any BufferViews previously returned by Self::allocate_weight become stale — drop the corresponding handles and re-load. (Weights are write-once; no per-tensor free.)

Source

pub fn resident_weight_bytes(&self) -> usize

Bytes currently consumed in the persistent weight region (for tests / introspection).

Source

pub fn allocate_transient( &mut self, size_bytes: usize, binding: u32, group: u32, usage: BindingUsage, ) -> Result<BufferView, ForgeError>

Source

pub fn advance_read_head(&mut self, offset: usize)

Source

pub fn clear_transient_allocations(&mut self)

Source

pub fn build_triangle_scene( &self, vertices: &[f32], ) -> Result<(Blas, Tlas), ForgeError>

Builds a bottom-level (BLAS) + top-level (TLAS) acceleration structure for a triangle soup and returns both, ready to bind to a ray-query shader. vertices is a flat list of f32 triples (3 per vertex, 3 vertices per triangle), row-major. The BLAS geometry is marked OPAQUE (required — naga’s ray-query has no candidate/any-hit path, so non-opaque geometry yields no committed hits), and the single TLAS instance uses the identity transform. Both structures are built and the queue drained before returning. Requires the adapter to support (and the device to have enabled) EXPERIMENTAL_RAY_QUERY.

The returned Blas must be kept alive alongside the Tlas for the lifetime of any bind group referencing the TLAS (the TlasInstance borrows the BLAS).

Source

pub fn compile_pipeline( &self, source: &str, entry_point: &str, ) -> Result<ComputePipeline, ForgeError>

Compile a WGSL compute pipeline and return the owned wgpu::ComputePipeline (no borrow of self), wrapped in a validation error scope. This is the building block the multi-node graph executor uses to compile every node’s kernel up front before recording them into a single command encoder (Self::submit_graph). WgpuPipeline::compile delegates here.

Source

pub fn compile_pipeline_spirv( &self, spirv: &[u8], entry_point: &str, ) -> Result<ComputePipeline, ForgeError>

Like [compile_pipeline] but accepts pre-compiled SPIR-V bytes instead of WGSL source. This is the execution bridge for native shader profiles that compile to SPIR-V (notably HLSL via DXC –spirv): the forge emits HLSL, DXC produces a SPIR-V binary, and this method feeds it into the same wgpu pipeline (bind groups, slab, dispatch — all unchanged).

Source

pub fn compile_pipeline_cached( &self, source: &str, entry_point: &str, ) -> Result<ComputePipeline, ForgeError>

compile_pipeline with a process-lifetime cache keyed by entry\0source — the same (source, entry) returns the previously-built pipeline (a cheap Arc-clone) instead of recompiling. This is what makes a re-run of a fixed graph (e.g. one decode block per generated token, via a held [ForgeGraphExecutor]) pay shader compilation once, not per call. The graph executor records its nodes through this path; one-shot callers see a cold cache (built + dropped with the context).

Source

pub fn cached_pipeline_count(&self) -> usize

Number of distinct pipelines currently cached (for tests / introspection).

Source

pub fn create_compute_bind_group( &self, pipeline: &ComputePipeline, buffers: &[BufferView], ) -> BindGroup

Build a bind group binding each BufferView at its binding slot, choosing the physical slab per the view’s usage (Self::slab_for). Shared by the per-node WgpuPipeline::dispatch path and the deferred-submit graph path.

Source

pub fn submit_graph(&self, passes: &[GraphPass]) -> Result<(), ForgeError>

Record all of a graph’s node dispatches — and the GPU→GPU hand-off copies between them — into ONE [wgpu::CommandEncoder] and submit it once, instead of one queue.submit() per node. This is the single-encoder deferred-submit fusion (plan §8.1 “Option B”): within one command buffer wgpu preserves command order and inserts the necessary buffer hazard barriers, so a producer’s compute pass, its copy_buffer_to_buffer hand-off, and the consumer’s dispatch are correctly ordered with no host round-trip and no per-node submit latency. The caller (the executor) has already encoded each node’s data dependencies in passes (insertion/topological order) and built each bind group, so this loop is pure recording. Blocks on device completion and surfaces any validation error.

Source

pub fn copy_view( &self, src: &BufferView, dst: &BufferView, ) -> Result<(), ForgeError>

Copy src’s bytes to dst on the device (GPU→GPU, no host readback), honouring each view’s slab. Used by the multi-node graph executor to move a node’s output out of the read_write slab into the read slab, so a downstream node can bind it as a read-only input without aliasing its own read_write output (wgpu forbids the same buffer being bound read-write and read-only within one dispatch). Submits on the shared queue, so it is ordered before any later dispatch that reads dst.

Source

pub fn read_buffer_f32(&self, view: &BufferView) -> Result<Vec<f32>, ForgeError>

Trait Implementations§

Source§

impl OracleContext for WgpuComputeContext

Source§

fn run_kernel( &mut self, kernel: &KernelSpec, schedule: &Schedule, buffers: &[BufferView], element_count: usize, warmups: usize, samples: usize, ) -> Result<Vec<u64>, ForgeError>

Emit the kernel’s WGSL, compile it, then run the warmup + timed-sample dispatch loop — byte-for-byte the loop the wgpu oracle evaluators ran inline (warmups untimed, then samples timed dispatches via QualiaCompute::dispatch).

Source§

fn allocate_and_write( &mut self, data: &[u8], binding: u32, group: u32, usage: BindingUsage, ) -> Result<BufferView, ForgeError>

Allocate a transient slab sub-range and upload data into it. usage selects the backing slab on wgpu (read-only/uniform vs read-write); the CUDA backend addresses one slab and ignores it.
Source§

fn allocate_transient( &mut self, size_bytes: usize, binding: u32, group: u32, usage: BindingUsage, ) -> Result<BufferView, ForgeError>

Allocate a transient (uninitialised) slab sub-range. usage is honoured by the wgpu backend and ignored by CUDA, as for Self::allocate_and_write.
Source§

fn read_buffer_f32(&self, view: &BufferView) -> Result<Vec<f32>, ForgeError>

Read a device buffer back as f32s.
Source§

fn clear_transient_allocations(&mut self)

Release every transient allocation (resets the ring’s read head to the write head). Only valid once all device work on those ranges has completed.
Source§

fn adapter(&self) -> &AdapterIdentity

The adapter identity (vendor/device/driver) backing this context.
Source§

fn constraints(&self) -> &AdapterConstraints

The adapter’s intrinsic capability/limit constraints.
Source§

fn timestamp_supported(&self) -> bool

Whether GPU-timestamp timing is available (wgpu only; CUDA times on the host wall clock, so this is always false there).

Auto Trait Implementations§

Blanket Implementations§

§

impl<S, A> Aggregate<Result<S, Error>> for A
where A: Aggregate<S>,

§

fn from_shares<T>(iter: T) -> Result<A, Error>
where T: IntoIterator<Item = Result<S, Error>>,

Aggregate shares in an MPC protocol.
Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast<T> for T

§

fn downcast(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> Upcast<T> for T

§

fn upcast(&self) -> Option<&T>

§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

§

impl<T> WasmNotSend for T
where T: Send,