Skip to main content

agpu/
resource.rs

1//! GPU resource wrappers — first-class agpu types for every GPU object.
2//!
3//! Each wrapper holds the underlying wgpu object plus metadata for agent
4//! discoverability (label, id, resource kind).  Users interact with these
5//! types instead of raw wgpu handles.  The `.raw()` escape hatch exposes
6//! the inner wgpu type for advanced use.
7
8use crate::ontology::*;
9use crate::types::*;
10use std::sync::atomic::{AtomicU64, Ordering};
11
12/// Global resource ID counter.
13static NEXT_RESOURCE_ID: AtomicU64 = AtomicU64::new(1);
14
15fn next_id() -> u64 {
16    NEXT_RESOURCE_ID.fetch_add(1, Ordering::Relaxed)
17}
18
19// ── GpuBuffer ───────────────────────────────────────────────────────
20
21/// A GPU buffer — vertex, index, uniform, storage, or staging memory.
22pub struct GpuBuffer {
23    inner: wgpu::Buffer,
24    id: u64,
25    label: Option<String>,
26    size: u64,
27    usage: BufferUsages,
28}
29
30impl GpuBuffer {
31    pub(crate) fn from_raw(inner: wgpu::Buffer, desc: &GpuBufferDescriptor) -> Self {
32        Self {
33            inner,
34            id: next_id(),
35            label: desc.label.clone(),
36            size: desc.size,
37            usage: desc.usage,
38        }
39    }
40
41    /// Unique resource identifier.
42    pub fn id(&self) -> u64 {
43        self.id
44    }
45    /// Optional label.
46    pub fn label(&self) -> Option<&str> {
47        self.label.as_deref()
48    }
49    /// Buffer size in bytes.
50    pub fn size(&self) -> u64 {
51        self.size
52    }
53    /// Buffer usage flags.
54    pub fn usage(&self) -> BufferUsages {
55        self.usage
56    }
57    /// Access the underlying wgpu buffer.
58    pub fn raw(&self) -> &wgpu::Buffer {
59        &self.inner
60    }
61
62    /// Get a slice of the entire buffer.
63    pub fn slice(
64        &self,
65        bounds: impl std::ops::RangeBounds<BufferAddress>,
66    ) -> wgpu::BufferSlice<'_> {
67        self.inner.slice(bounds)
68    }
69
70    /// Helper: get the entire buffer as a binding resource.
71    pub fn as_entire_binding(&self) -> wgpu::BindingResource<'_> {
72        self.inner.as_entire_binding()
73    }
74}
75
76// ── GpuTexture ──────────────────────────────────────────────────────
77
78/// A GPU texture — 2D, 3D, or cube-map image data on the GPU.
79pub struct GpuTexture {
80    inner: wgpu::Texture,
81    id: u64,
82    label: Option<String>,
83    size: Extent3d,
84    format: TextureFormat,
85    dimension: TextureDimension,
86    mip_level_count: u32,
87    sample_count: u32,
88    usage: TextureUsages,
89}
90
91impl GpuTexture {
92    pub(crate) fn from_raw(inner: wgpu::Texture, desc: &GpuTextureDescriptor) -> Self {
93        Self {
94            inner,
95            id: next_id(),
96            label: desc.label.clone(),
97            size: desc.size,
98            format: desc.format,
99            dimension: desc.dimension,
100            mip_level_count: desc.mip_level_count,
101            sample_count: desc.sample_count,
102            usage: desc.usage,
103        }
104    }
105
106    pub fn id(&self) -> u64 {
107        self.id
108    }
109    pub fn label(&self) -> Option<&str> {
110        self.label.as_deref()
111    }
112    pub fn size(&self) -> Extent3d {
113        self.size
114    }
115    pub fn format(&self) -> TextureFormat {
116        self.format
117    }
118    pub fn dimension(&self) -> TextureDimension {
119        self.dimension
120    }
121    pub fn mip_level_count(&self) -> u32 {
122        self.mip_level_count
123    }
124    pub fn sample_count(&self) -> u32 {
125        self.sample_count
126    }
127    pub fn usage(&self) -> TextureUsages {
128        self.usage
129    }
130    pub fn raw(&self) -> &wgpu::Texture {
131        &self.inner
132    }
133
134    /// Create a texture view with default settings.
135    pub fn create_view_default(&self) -> GpuTextureView {
136        let view = self
137            .inner
138            .create_view(&wgpu::TextureViewDescriptor::default());
139        GpuTextureView {
140            inner: view,
141            id: next_id(),
142            label: self.label.clone().map(|l| format!("{l}_view")),
143            format: self.format,
144        }
145    }
146
147    /// Create a texture view with custom settings.
148    pub fn create_view(&self, desc: &wgpu::TextureViewDescriptor<'_>) -> GpuTextureView {
149        let view = self.inner.create_view(desc);
150        GpuTextureView {
151            inner: view,
152            id: next_id(),
153            label: desc.label.map(String::from),
154            format: desc.format.unwrap_or(self.format),
155        }
156    }
157}
158
159impl Discoverable for GpuTexture {
160    fn schema(&self) -> WidgetSchema {
161        let mut s = WidgetSchema::new(
162            "GpuTexture",
163            "GPU texture — image data stored on the GPU for sampling or rendering",
164            SemanticRole::System,
165        );
166        s.tags = vec!["gpu".into(), "texture".into(), "image".into()];
167        s
168    }
169
170    fn capabilities(&self) -> Vec<AgentCapability> {
171        vec![AgentCapability::Custom("gpu_texture".into())]
172    }
173
174    fn actions(&self) -> Vec<AgentAction> {
175        vec![AgentAction::simple(
176            "get_info",
177            "Query texture dimensions, format, and usage",
178            false,
179        )]
180    }
181
182    fn semantic_role(&self) -> SemanticRole {
183        SemanticRole::System
184    }
185
186    fn agent_state(&self) -> serde_json::Value {
187        serde_json::json!({
188            "id": self.id,
189            "label": self.label,
190            "width": self.size.width,
191            "height": self.size.height,
192            "depth_or_layers": self.size.depth_or_array_layers,
193            "format": format!("{:?}", self.format),
194            "dimension": format!("{:?}", self.dimension),
195            "mip_levels": self.mip_level_count,
196            "sample_count": self.sample_count,
197            "usage": format!("{:?}", self.usage),
198        })
199    }
200
201    fn execute_action(
202        &mut self,
203        action: &str,
204        _params: &serde_json::Value,
205    ) -> Result<serde_json::Value, String> {
206        match action {
207            "get_info" => Ok(self.agent_state()),
208            _ => Err(format!("Unknown action: {action}")),
209        }
210    }
211
212    fn agent_id(&self) -> Option<&str> {
213        None
214    }
215}
216
217// ── GpuTextureView ──────────────────────────────────────────────────
218
219/// A view into a GPU texture.
220pub struct GpuTextureView {
221    inner: wgpu::TextureView,
222    id: u64,
223    label: Option<String>,
224    format: TextureFormat,
225}
226
227impl GpuTextureView {
228    pub(crate) fn from_surface(inner: wgpu::TextureView, format: TextureFormat) -> Self {
229        Self {
230            inner,
231            id: next_id(),
232            label: Some("surface_view".into()),
233            format,
234        }
235    }
236
237    pub fn id(&self) -> u64 {
238        self.id
239    }
240    pub fn label(&self) -> Option<&str> {
241        self.label.as_deref()
242    }
243    pub fn format(&self) -> TextureFormat {
244        self.format
245    }
246    pub fn raw(&self) -> &wgpu::TextureView {
247        &self.inner
248    }
249}
250
251// ── GpuSampler ──────────────────────────────────────────────────────
252
253/// A GPU sampler — controls how textures are filtered and addressed.
254pub struct GpuSampler {
255    inner: wgpu::Sampler,
256    id: u64,
257    label: Option<String>,
258}
259
260impl GpuSampler {
261    pub(crate) fn from_raw(inner: wgpu::Sampler, label: Option<String>) -> Self {
262        Self {
263            inner,
264            id: next_id(),
265            label,
266        }
267    }
268
269    pub fn id(&self) -> u64 {
270        self.id
271    }
272    pub fn label(&self) -> Option<&str> {
273        self.label.as_deref()
274    }
275    pub fn raw(&self) -> &wgpu::Sampler {
276        &self.inner
277    }
278}
279
280// ── GpuShaderModule ─────────────────────────────────────────────────
281
282/// A compiled GPU shader module (WGSL or SPIR-V).
283pub struct GpuShaderModule {
284    inner: wgpu::ShaderModule,
285    id: u64,
286    label: Option<String>,
287    source_kind: ShaderSourceKind,
288}
289
290/// How the shader was provided.
291#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
292pub enum ShaderSourceKind {
293    Wgsl,
294    SpirV,
295    Naga,
296}
297
298impl GpuShaderModule {
299    pub(crate) fn from_raw(
300        inner: wgpu::ShaderModule,
301        label: Option<String>,
302        source_kind: ShaderSourceKind,
303    ) -> Self {
304        Self {
305            inner,
306            id: next_id(),
307            label,
308            source_kind,
309        }
310    }
311
312    pub fn id(&self) -> u64 {
313        self.id
314    }
315    pub fn label(&self) -> Option<&str> {
316        self.label.as_deref()
317    }
318    pub fn source_kind(&self) -> ShaderSourceKind {
319        self.source_kind
320    }
321    pub fn raw(&self) -> &wgpu::ShaderModule {
322        &self.inner
323    }
324}
325
326// ── GpuBindGroupLayout ──────────────────────────────────────────────
327
328/// A bind group layout describing the shape of a bind group.
329pub struct GpuBindGroupLayout {
330    inner: wgpu::BindGroupLayout,
331    id: u64,
332    label: Option<String>,
333}
334
335impl GpuBindGroupLayout {
336    pub(crate) fn from_raw(inner: wgpu::BindGroupLayout, label: Option<String>) -> Self {
337        Self {
338            inner,
339            id: next_id(),
340            label,
341        }
342    }
343
344    pub fn id(&self) -> u64 {
345        self.id
346    }
347    pub fn label(&self) -> Option<&str> {
348        self.label.as_deref()
349    }
350    pub fn raw(&self) -> &wgpu::BindGroupLayout {
351        &self.inner
352    }
353}
354
355// ── GpuBindGroup ────────────────────────────────────────────────────
356
357/// A bind group — a set of resources bound to a shader.
358pub struct GpuBindGroup {
359    inner: wgpu::BindGroup,
360    id: u64,
361    label: Option<String>,
362}
363
364impl GpuBindGroup {
365    pub(crate) fn from_raw(inner: wgpu::BindGroup, label: Option<String>) -> Self {
366        Self {
367            inner,
368            id: next_id(),
369            label,
370        }
371    }
372
373    pub fn id(&self) -> u64 {
374        self.id
375    }
376    pub fn label(&self) -> Option<&str> {
377        self.label.as_deref()
378    }
379    pub fn raw(&self) -> &wgpu::BindGroup {
380        &self.inner
381    }
382}
383
384// ── GpuPipelineLayout ───────────────────────────────────────────────
385
386/// A pipeline layout describing bind group layouts and push constants.
387pub struct GpuPipelineLayout {
388    inner: wgpu::PipelineLayout,
389    id: u64,
390    label: Option<String>,
391}
392
393impl GpuPipelineLayout {
394    pub(crate) fn from_raw(inner: wgpu::PipelineLayout, label: Option<String>) -> Self {
395        Self {
396            inner,
397            id: next_id(),
398            label,
399        }
400    }
401
402    pub fn id(&self) -> u64 {
403        self.id
404    }
405    pub fn label(&self) -> Option<&str> {
406        self.label.as_deref()
407    }
408    pub fn raw(&self) -> &wgpu::PipelineLayout {
409        &self.inner
410    }
411}
412
413// ── GpuRenderPipeline ───────────────────────────────────────────────
414
415/// A GPU render pipeline — vertex + fragment shaders, primitive state,
416/// blend mode, and depth/stencil configuration.
417pub struct GpuRenderPipeline {
418    inner: wgpu::RenderPipeline,
419    id: u64,
420    label: Option<String>,
421}
422
423impl GpuRenderPipeline {
424    pub(crate) fn from_raw(inner: wgpu::RenderPipeline, label: Option<String>) -> Self {
425        Self {
426            inner,
427            id: next_id(),
428            label,
429        }
430    }
431
432    pub fn id(&self) -> u64 {
433        self.id
434    }
435    pub fn label(&self) -> Option<&str> {
436        self.label.as_deref()
437    }
438    pub fn raw(&self) -> &wgpu::RenderPipeline {
439        &self.inner
440    }
441}
442
443impl Discoverable for GpuRenderPipeline {
444    fn schema(&self) -> WidgetSchema {
445        let mut s = WidgetSchema::new(
446            "GpuRenderPipeline",
447            "GPU render pipeline — compiled vertex/fragment shader program",
448            SemanticRole::System,
449        );
450        s.tags = vec![
451            "gpu".into(),
452            "pipeline".into(),
453            "render".into(),
454            "shader".into(),
455        ];
456        s
457    }
458
459    fn capabilities(&self) -> Vec<AgentCapability> {
460        vec![AgentCapability::Custom("gpu_render_pipeline".into())]
461    }
462
463    fn actions(&self) -> Vec<AgentAction> {
464        vec![AgentAction::simple(
465            "get_info",
466            "Query pipeline metadata",
467            false,
468        )]
469    }
470
471    fn semantic_role(&self) -> SemanticRole {
472        SemanticRole::System
473    }
474
475    fn agent_state(&self) -> serde_json::Value {
476        serde_json::json!({
477            "id": self.id,
478            "label": self.label,
479            "kind": "RenderPipeline",
480        })
481    }
482
483    fn execute_action(
484        &mut self,
485        action: &str,
486        _params: &serde_json::Value,
487    ) -> Result<serde_json::Value, String> {
488        match action {
489            "get_info" => Ok(self.agent_state()),
490            _ => Err(format!("Unknown action: {action}")),
491        }
492    }
493
494    fn agent_id(&self) -> Option<&str> {
495        None
496    }
497}
498
499// ── GpuComputePipeline ──────────────────────────────────────────────
500
501/// A GPU compute pipeline — a compiled compute shader program.
502pub struct GpuComputePipeline {
503    inner: wgpu::ComputePipeline,
504    id: u64,
505    label: Option<String>,
506}
507
508impl GpuComputePipeline {
509    pub(crate) fn from_raw(inner: wgpu::ComputePipeline, label: Option<String>) -> Self {
510        Self {
511            inner,
512            id: next_id(),
513            label,
514        }
515    }
516
517    pub fn id(&self) -> u64 {
518        self.id
519    }
520    pub fn label(&self) -> Option<&str> {
521        self.label.as_deref()
522    }
523    pub fn raw(&self) -> &wgpu::ComputePipeline {
524        &self.inner
525    }
526}
527
528impl Discoverable for GpuComputePipeline {
529    fn schema(&self) -> WidgetSchema {
530        let mut s = WidgetSchema::new(
531            "GpuComputePipeline",
532            "GPU compute pipeline — compiled compute shader program for GPGPU workloads",
533            SemanticRole::System,
534        );
535        s.tags = vec![
536            "gpu".into(),
537            "pipeline".into(),
538            "compute".into(),
539            "shader".into(),
540        ];
541        s
542    }
543
544    fn capabilities(&self) -> Vec<AgentCapability> {
545        vec![AgentCapability::Custom("gpu_compute_pipeline".into())]
546    }
547
548    fn actions(&self) -> Vec<AgentAction> {
549        vec![AgentAction::simple(
550            "get_info",
551            "Query compute pipeline metadata",
552            false,
553        )]
554    }
555
556    fn semantic_role(&self) -> SemanticRole {
557        SemanticRole::System
558    }
559
560    fn agent_state(&self) -> serde_json::Value {
561        serde_json::json!({
562            "id": self.id,
563            "label": self.label,
564            "kind": "ComputePipeline",
565        })
566    }
567
568    fn execute_action(
569        &mut self,
570        action: &str,
571        _params: &serde_json::Value,
572    ) -> Result<serde_json::Value, String> {
573        match action {
574            "get_info" => Ok(self.agent_state()),
575            _ => Err(format!("Unknown action: {action}")),
576        }
577    }
578
579    fn agent_id(&self) -> Option<&str> {
580        None
581    }
582}
583
584// ── GpuQuerySet ─────────────────────────────────────────────────────
585
586/// A GPU query set — for occlusion queries or timestamp queries.
587pub struct GpuQuerySet {
588    inner: wgpu::QuerySet,
589    id: u64,
590    label: Option<String>,
591    count: u32,
592}
593
594impl GpuQuerySet {
595    pub(crate) fn from_raw(inner: wgpu::QuerySet, label: Option<String>, count: u32) -> Self {
596        Self {
597            inner,
598            id: next_id(),
599            label,
600            count,
601        }
602    }
603
604    pub fn id(&self) -> u64 {
605        self.id
606    }
607    pub fn label(&self) -> Option<&str> {
608        self.label.as_deref()
609    }
610    pub fn count(&self) -> u32 {
611        self.count
612    }
613    pub fn raw(&self) -> &wgpu::QuerySet {
614        &self.inner
615    }
616}