Skip to main content

anvilkit_render/renderer/
pipeline.rs

1//! # 渲染管线管理
2//! 
3//! 提供渲染管线的创建、配置和管理功能。
4
5use wgpu::{
6    RenderPipeline, RenderPipelineDescriptor, VertexState, FragmentState,
7    PrimitiveState, MultisampleState, PipelineLayoutDescriptor,
8    ShaderModule, ShaderModuleDescriptor, ShaderSource,
9    ColorTargetState, BlendState, ColorWrites,
10    PrimitiveTopology, FrontFace, PolygonMode,
11    TextureFormat, Device,
12};
13use log::{info, debug};
14
15use crate::renderer::RenderDevice;
16use anvilkit_core::error::{AnvilKitError, Result};
17
18/// 渲染管线构建器
19/// 
20/// 提供流式 API 来配置和创建渲染管线。
21/// 
22/// # 设计理念
23/// 
24/// - **流式配置**: 使用构建器模式简化管线配置
25/// - **默认值**: 提供合理的默认配置参数
26/// - **类型安全**: 编译时检查配置的正确性
27/// - **灵活性**: 支持自定义着色器和状态配置
28/// 
29/// # 示例
30/// 
31/// ```rust,no_run
32/// use anvilkit_render::renderer::{RenderDevice, RenderPipelineBuilder};
33/// use wgpu::TextureFormat;
34/// 
35/// # async fn example(device: &RenderDevice) -> anvilkit_core::error::Result<()> {
36/// let pipeline = RenderPipelineBuilder::new()
37///     .with_vertex_shader("vertex_shader.wgsl")
38///     .with_fragment_shader("fragment_shader.wgsl")
39///     .with_format(TextureFormat::Bgra8UnormSrgb)
40///     .build(device)?;
41/// # Ok(())
42/// # }
43/// ```
44pub struct RenderPipelineBuilder {
45    /// 顶点着色器源码
46    vertex_shader: Option<String>,
47    /// 片段着色器源码
48    fragment_shader: Option<String>,
49    /// 渲染目标格式
50    format: Option<TextureFormat>,
51    /// 图元拓扑
52    topology: PrimitiveTopology,
53    /// 多重采样状态
54    multisample_count: u32,
55    /// 标签
56    label: Option<String>,
57    /// 顶点缓冲区布局
58    vertex_layouts: Vec<wgpu::VertexBufferLayout<'static>>,
59    /// 深度纹理格式(None = 不启用深度测试)
60    depth_format: Option<TextureFormat>,
61    /// Bind group 布局
62    bind_group_layouts: Vec<wgpu::BindGroupLayout>,
63}
64
65impl Default for RenderPipelineBuilder {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71impl RenderPipelineBuilder {
72    /// 创建新的渲染管线构建器
73    /// 
74    /// # 示例
75    /// 
76    /// ```rust
77    /// use anvilkit_render::renderer::RenderPipelineBuilder;
78    /// 
79    /// let builder = RenderPipelineBuilder::new();
80    /// ```
81    pub fn new() -> Self {
82        Self {
83            vertex_shader: None,
84            fragment_shader: None,
85            format: None,
86            topology: PrimitiveTopology::TriangleList,
87            multisample_count: 1,
88            label: None,
89            vertex_layouts: Vec::new(),
90            depth_format: None,
91            bind_group_layouts: Vec::new(),
92        }
93    }
94    
95    /// 设置顶点着色器
96    /// 
97    /// # 参数
98    /// 
99    /// - `source`: 着色器源码
100    /// 
101    /// # 示例
102    /// 
103    /// ```rust
104    /// use anvilkit_render::renderer::RenderPipelineBuilder;
105    /// 
106    /// let builder = RenderPipelineBuilder::new()
107    ///     .with_vertex_shader("vertex_shader.wgsl");
108    /// ```
109    pub fn with_vertex_shader<S: Into<String>>(mut self, source: S) -> Self {
110        self.vertex_shader = Some(source.into());
111        self
112    }
113    
114    /// 设置片段着色器
115    /// 
116    /// # 参数
117    /// 
118    /// - `source`: 着色器源码
119    /// 
120    /// # 示例
121    /// 
122    /// ```rust
123    /// use anvilkit_render::renderer::RenderPipelineBuilder;
124    /// 
125    /// let builder = RenderPipelineBuilder::new()
126    ///     .with_fragment_shader("fragment_shader.wgsl");
127    /// ```
128    pub fn with_fragment_shader<S: Into<String>>(mut self, source: S) -> Self {
129        self.fragment_shader = Some(source.into());
130        self
131    }
132    
133    /// 设置渲染目标格式
134    /// 
135    /// # 参数
136    /// 
137    /// - `format`: 纹理格式
138    /// 
139    /// # 示例
140    /// 
141    /// ```rust
142    /// use anvilkit_render::renderer::RenderPipelineBuilder;
143    /// use wgpu::TextureFormat;
144    /// 
145    /// let builder = RenderPipelineBuilder::new()
146    ///     .with_format(TextureFormat::Bgra8UnormSrgb);
147    /// ```
148    pub fn with_format(mut self, format: TextureFormat) -> Self {
149        self.format = Some(format);
150        self
151    }
152    
153    /// 设置图元拓扑
154    /// 
155    /// # 参数
156    /// 
157    /// - `topology`: 图元拓扑类型
158    /// 
159    /// # 示例
160    /// 
161    /// ```rust
162    /// use anvilkit_render::renderer::RenderPipelineBuilder;
163    /// use wgpu::PrimitiveTopology;
164    /// 
165    /// let builder = RenderPipelineBuilder::new()
166    ///     .with_topology(PrimitiveTopology::LineList);
167    /// ```
168    pub fn with_topology(mut self, topology: PrimitiveTopology) -> Self {
169        self.topology = topology;
170        self
171    }
172    
173    /// 设置多重采样数量
174    /// 
175    /// # 参数
176    /// 
177    /// - `count`: 采样数量
178    /// 
179    /// # 示例
180    /// 
181    /// ```rust
182    /// use anvilkit_render::renderer::RenderPipelineBuilder;
183    /// 
184    /// let builder = RenderPipelineBuilder::new()
185    ///     .with_multisample_count(4);
186    /// ```
187    pub fn with_multisample_count(mut self, count: u32) -> Self {
188        self.multisample_count = count;
189        self
190    }
191    
192    /// 设置标签
193    /// 
194    /// # 参数
195    /// 
196    /// - `label`: 管线标签
197    /// 
198    /// # 示例
199    /// 
200    /// ```rust
201    /// use anvilkit_render::renderer::RenderPipelineBuilder;
202    /// 
203    /// let builder = RenderPipelineBuilder::new()
204    ///     .with_label("My Render Pipeline");
205    /// ```
206    pub fn with_label<S: Into<String>>(mut self, label: S) -> Self {
207        self.label = Some(label.into());
208        self
209    }
210
211    /// 设置顶点缓冲区布局
212    ///
213    /// # 参数
214    ///
215    /// - `layouts`: 顶点缓冲区布局列表
216    ///
217    /// # 示例
218    ///
219    /// ```rust
220    /// use anvilkit_render::renderer::{RenderPipelineBuilder, buffer::ColorVertex, Vertex};
221    ///
222    /// let builder = RenderPipelineBuilder::new()
223    ///     .with_vertex_layouts(vec![ColorVertex::layout()]);
224    /// ```
225    pub fn with_vertex_layouts(mut self, layouts: Vec<wgpu::VertexBufferLayout<'static>>) -> Self {
226        self.vertex_layouts = layouts;
227        self
228    }
229
230    /// 设置深度纹理格式,启用深度测试
231    ///
232    /// # 参数
233    ///
234    /// - `format`: 深度纹理格式(如 `TextureFormat::Depth32Float`)
235    pub fn with_depth_format(mut self, format: TextureFormat) -> Self {
236        self.depth_format = Some(format);
237        self
238    }
239
240    /// 设置 Bind Group 布局
241    ///
242    /// # 参数
243    ///
244    /// - `layouts`: Bind Group 布局列表(用于 Uniform Buffer、纹理等)
245    pub fn with_bind_group_layouts(mut self, layouts: Vec<wgpu::BindGroupLayout>) -> Self {
246        self.bind_group_layouts = layouts;
247        self
248    }
249
250    /// 构建渲染管线
251    /// 
252    /// # 参数
253    /// 
254    /// - `device`: 渲染设备
255    /// 
256    /// # 返回
257    /// 
258    /// 成功时返回 BasicRenderPipeline,失败时返回错误
259    /// 
260    /// # 示例
261    /// 
262    /// ```rust,no_run
263    /// use anvilkit_render::renderer::{RenderDevice, RenderPipelineBuilder};
264    /// use wgpu::TextureFormat;
265    /// 
266    /// # async fn example(device: &RenderDevice) -> anvilkit_core::error::Result<()> {
267    /// let pipeline = RenderPipelineBuilder::new()
268    ///     .with_vertex_shader("vertex_shader.wgsl")
269    ///     .with_fragment_shader("fragment_shader.wgsl")
270    ///     .with_format(TextureFormat::Bgra8UnormSrgb)
271    ///     .build(device)?;
272    /// # Ok(())
273    /// # }
274    /// ```
275    /// 构建深度-only 渲染管线(无片段着色器,用于 shadow pass)
276    pub fn build_depth_only(self, device: &RenderDevice) -> Result<BasicRenderPipeline> {
277        let vertex_shader = self.vertex_shader
278            .ok_or_else(|| AnvilKitError::render("缺少顶点着色器".to_string()))?;
279
280        let depth_format = self.depth_format
281            .ok_or_else(|| AnvilKitError::render("深度-only 管线需要深度格式".to_string()))?;
282
283        let bind_group_layout_refs: Vec<&wgpu::BindGroupLayout> =
284            self.bind_group_layouts.iter().collect();
285
286        let wgpu_device = device.device();
287
288        let vs_module = BasicRenderPipeline::create_shader_module(
289            wgpu_device, &vertex_shader, Some("Shadow VS"),
290        )?;
291
292        let layout = wgpu_device.create_pipeline_layout(&PipelineLayoutDescriptor {
293            label: Some("Shadow Pipeline Layout"),
294            bind_group_layouts: &bind_group_layout_refs,
295            push_constant_ranges: &[],
296        });
297
298        let pipeline = wgpu_device.create_render_pipeline(&RenderPipelineDescriptor {
299            label: self.label.as_deref(),
300            layout: Some(&layout),
301            vertex: VertexState {
302                module: &vs_module,
303                entry_point: "vs_main",
304                buffers: &self.vertex_layouts,
305            },
306            primitive: PrimitiveState {
307                topology: self.topology,
308                strip_index_format: None,
309                front_face: FrontFace::Ccw,
310                cull_mode: None, // disabled for glTF compatibility
311                unclipped_depth: false,
312                polygon_mode: PolygonMode::Fill,
313                conservative: false,
314            },
315            depth_stencil: Some(wgpu::DepthStencilState {
316                format: depth_format,
317                depth_write_enabled: true,
318                depth_compare: wgpu::CompareFunction::Less,
319                stencil: wgpu::StencilState::default(),
320                bias: wgpu::DepthBiasState::default(),
321            }),
322            multisample: MultisampleState {
323                count: 1,
324                mask: !0,
325                alpha_to_coverage_enabled: false,
326            },
327            fragment: None, // depth-only, no fragment stage
328            multiview: None,
329        });
330
331        // Create a dummy fragment shader module for the struct (required field)
332        let dummy_fs = BasicRenderPipeline::create_shader_module(
333            wgpu_device,
334            "// dummy\n@fragment fn fs_main() -> @location(0) vec4<f32> { return vec4<f32>(0.0); }",
335            Some("Dummy FS"),
336        )?;
337
338        Ok(BasicRenderPipeline {
339            pipeline,
340            vertex_shader: vs_module,
341            fragment_shader: dummy_fs,
342        })
343    }
344
345    /// 构建带颜色输出的完整渲染管线
346    pub fn build(self, device: &RenderDevice) -> Result<BasicRenderPipeline> {
347        let vertex_shader = self.vertex_shader
348            .ok_or_else(|| AnvilKitError::render("缺少顶点着色器".to_string()))?;
349        
350        let fragment_shader = self.fragment_shader
351            .ok_or_else(|| AnvilKitError::render("缺少片段着色器".to_string()))?;
352        
353        let format = self.format
354            .ok_or_else(|| AnvilKitError::render("缺少渲染目标格式".to_string()))?;
355        
356        let bind_group_layout_refs: Vec<&wgpu::BindGroupLayout> =
357            self.bind_group_layouts.iter().collect();
358
359        BasicRenderPipeline::new(
360            device,
361            &vertex_shader,
362            &fragment_shader,
363            format,
364            self.topology,
365            self.multisample_count,
366            self.label.as_deref(),
367            &self.vertex_layouts,
368            self.depth_format,
369            &bind_group_layout_refs,
370        )
371    }
372}
373
374/// 基础渲染管线
375/// 
376/// 封装 wgpu 渲染管线,提供基础的渲染功能。
377/// 
378/// # 示例
379/// 
380/// ```rust,no_run
381/// use anvilkit_render::renderer::{RenderDevice, BasicRenderPipeline};
382/// use wgpu::{TextureFormat, PrimitiveTopology};
383///
384/// # async fn example(device: &RenderDevice) -> anvilkit_core::error::Result<()> {
385/// let pipeline = BasicRenderPipeline::new(
386///     device,
387///     "vertex_shader.wgsl",
388///     "fragment_shader.wgsl",
389///     TextureFormat::Bgra8UnormSrgb,
390///     PrimitiveTopology::TriangleList,
391///     1,
392///     Some("Basic Pipeline"),
393///     &[],
394///     None,
395///     &[],
396/// )?;
397/// # Ok(())
398/// # }
399/// ```
400pub struct BasicRenderPipeline {
401    /// wgpu 渲染管线
402    pipeline: RenderPipeline,
403    /// 顶点着色器模块
404    vertex_shader: ShaderModule,
405    /// 片段着色器模块
406    fragment_shader: ShaderModule,
407}
408
409impl BasicRenderPipeline {
410    /// 创建新的基础渲染管线
411    /// 
412    /// # 参数
413    /// 
414    /// - `device`: 渲染设备
415    /// - `vertex_source`: 顶点着色器源码
416    /// - `fragment_source`: 片段着色器源码
417    /// - `format`: 渲染目标格式
418    /// - `topology`: 图元拓扑
419    /// - `multisample_count`: 多重采样数量
420    /// - `label`: 可选的标签
421    /// 
422    /// # 返回
423    /// 
424    /// 成功时返回 BasicRenderPipeline,失败时返回错误
425    pub fn new(
426        device: &RenderDevice,
427        vertex_source: &str,
428        fragment_source: &str,
429        format: TextureFormat,
430        topology: PrimitiveTopology,
431        multisample_count: u32,
432        label: Option<&str>,
433        vertex_layouts: &[wgpu::VertexBufferLayout<'_>],
434        depth_format: Option<TextureFormat>,
435        bind_group_layouts: &[&wgpu::BindGroupLayout],
436    ) -> Result<Self> {
437        info!("创建基础渲染管线: {:?}", label);
438        
439        let wgpu_device = device.device();
440        
441        // 创建着色器模块
442        let vertex_shader = Self::create_shader_module(
443            wgpu_device,
444            vertex_source,
445            Some("Vertex Shader"),
446        )?;
447        
448        let fragment_shader = Self::create_shader_module(
449            wgpu_device,
450            fragment_source,
451            Some("Fragment Shader"),
452        )?;
453        
454        // 创建管线布局
455        let layout = wgpu_device.create_pipeline_layout(&PipelineLayoutDescriptor {
456            label: Some("Basic Pipeline Layout"),
457            bind_group_layouts,
458            push_constant_ranges: &[],
459        });
460        
461        // 创建渲染管线
462        let pipeline = wgpu_device.create_render_pipeline(&RenderPipelineDescriptor {
463            label,
464            layout: Some(&layout),
465            vertex: VertexState {
466                module: &vertex_shader,
467                entry_point: "vs_main",
468                buffers: vertex_layouts,
469            },
470            primitive: PrimitiveState {
471                topology,
472                strip_index_format: None,
473                front_face: FrontFace::Ccw,
474                cull_mode: None, // disabled for glTF compatibility
475                unclipped_depth: false,
476                polygon_mode: PolygonMode::Fill,
477                conservative: false,
478            },
479            depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
480                format,
481                depth_write_enabled: true,
482                depth_compare: wgpu::CompareFunction::Less,
483                stencil: wgpu::StencilState::default(),
484                bias: wgpu::DepthBiasState::default(),
485            }),
486            multisample: MultisampleState {
487                count: multisample_count,
488                mask: !0,
489                alpha_to_coverage_enabled: false,
490            },
491            fragment: Some(FragmentState {
492                module: &fragment_shader,
493                entry_point: "fs_main",
494                targets: &[Some(ColorTargetState {
495                    format,
496                    blend: Some(BlendState::REPLACE),
497                    write_mask: ColorWrites::ALL,
498                })],
499            }),
500            multiview: None,
501        });
502        
503        info!("基础渲染管线创建成功");
504        
505        Ok(Self {
506            pipeline,
507            vertex_shader,
508            fragment_shader,
509        })
510    }
511    
512    /// 创建着色器模块
513    /// 
514    /// # 参数
515    /// 
516    /// - `device`: GPU 设备
517    /// - `source`: 着色器源码
518    /// - `label`: 可选的标签
519    /// 
520    /// # 返回
521    /// 
522    /// 成功时返回 ShaderModule,失败时返回错误
523    fn create_shader_module(
524        device: &Device,
525        source: &str,
526        label: Option<&str>,
527    ) -> Result<ShaderModule> {
528        debug!("创建着色器模块: {:?}", label);
529        
530        let shader = device.create_shader_module(ShaderModuleDescriptor {
531            label,
532            source: ShaderSource::Wgsl(source.into()),
533        });
534        
535        Ok(shader)
536    }
537    
538    /// 获取渲染管线
539    /// 
540    /// # 返回
541    /// 
542    /// 返回 wgpu 渲染管线的引用
543    /// 
544    /// # 示例
545    /// 
546    /// ```rust,no_run
547    /// # use anvilkit_render::renderer::BasicRenderPipeline;
548    /// # async fn example(pipeline: &BasicRenderPipeline) {
549    /// let wgpu_pipeline = pipeline.pipeline();
550    /// // 使用管线进行渲染
551    /// # }
552    /// ```
553    pub fn pipeline(&self) -> &RenderPipeline {
554        &self.pipeline
555    }
556
557    /// 消费 BasicRenderPipeline 并返回内部的 wgpu RenderPipeline
558    pub fn into_pipeline(self) -> RenderPipeline {
559        self.pipeline
560    }
561    
562    /// 获取顶点着色器
563    /// 
564    /// # 返回
565    /// 
566    /// 返回顶点着色器模块的引用
567    /// 
568    /// # 示例
569    /// 
570    /// ```rust,no_run
571    /// # use anvilkit_render::renderer::BasicRenderPipeline;
572    /// # async fn example(pipeline: &BasicRenderPipeline) {
573    /// let vertex_shader = pipeline.vertex_shader();
574    /// # }
575    /// ```
576    pub fn vertex_shader(&self) -> &ShaderModule {
577        &self.vertex_shader
578    }
579    
580    /// 获取片段着色器
581    /// 
582    /// # 返回
583    /// 
584    /// 返回片段着色器模块的引用
585    /// 
586    /// # 示例
587    /// 
588    /// ```rust,no_run
589    /// # use anvilkit_render::renderer::BasicRenderPipeline;
590    /// # async fn example(pipeline: &BasicRenderPipeline) {
591    /// let fragment_shader = pipeline.fragment_shader();
592    /// # }
593    /// ```
594    pub fn fragment_shader(&self) -> &ShaderModule {
595        &self.fragment_shader
596    }
597}
598
599#[cfg(test)]
600mod tests {
601    use super::*;
602    use wgpu::{TextureFormat, PrimitiveTopology};
603    
604    #[test]
605    fn test_pipeline_builder_creation() {
606        let builder = RenderPipelineBuilder::new()
607            .with_vertex_shader("vertex.wgsl")
608            .with_fragment_shader("fragment.wgsl")
609            .with_format(TextureFormat::Bgra8UnormSrgb)
610            .with_topology(PrimitiveTopology::LineList)
611            .with_multisample_count(4)
612            .with_label("Test Pipeline");
613        
614        assert_eq!(builder.vertex_shader.as_ref().unwrap(), "vertex.wgsl");
615        assert_eq!(builder.fragment_shader.as_ref().unwrap(), "fragment.wgsl");
616        assert_eq!(builder.format.unwrap(), TextureFormat::Bgra8UnormSrgb);
617        assert_eq!(builder.topology, PrimitiveTopology::LineList);
618        assert_eq!(builder.multisample_count, 4);
619        assert_eq!(builder.label.as_ref().unwrap(), "Test Pipeline");
620    }
621    
622    #[test]
623    fn test_pipeline_builder_defaults() {
624        let builder = RenderPipelineBuilder::new();
625
626        assert!(builder.vertex_shader.is_none());
627        assert!(builder.fragment_shader.is_none());
628        assert!(builder.format.is_none());
629        assert_eq!(builder.topology, PrimitiveTopology::TriangleList);
630        assert_eq!(builder.multisample_count, 1);
631        assert!(builder.label.is_none());
632    }
633
634    #[test]
635    fn test_pipeline_builder_with_label() {
636        let builder = RenderPipelineBuilder::new()
637            .with_label("Test Pipeline");
638        assert_eq!(builder.label.as_deref(), Some("Test Pipeline"));
639    }
640
641    #[test]
642    fn test_pipeline_builder_with_format() {
643        let builder = RenderPipelineBuilder::new()
644            .with_format(TextureFormat::Bgra8UnormSrgb);
645        assert_eq!(builder.format, Some(TextureFormat::Bgra8UnormSrgb));
646    }
647
648    #[test]
649    fn test_pipeline_builder_with_topology() {
650        let builder = RenderPipelineBuilder::new()
651            .with_topology(PrimitiveTopology::LineList);
652        assert_eq!(builder.topology, PrimitiveTopology::LineList);
653    }
654
655    #[test]
656    fn test_pipeline_builder_with_multisample() {
657        let builder = RenderPipelineBuilder::new()
658            .with_multisample_count(4);
659        assert_eq!(builder.multisample_count, 4);
660    }
661
662    #[test]
663    fn test_pipeline_builder_chaining() {
664        let builder = RenderPipelineBuilder::new()
665            .with_label("Chained")
666            .with_format(TextureFormat::Rgba8Unorm)
667            .with_topology(PrimitiveTopology::TriangleStrip)
668            .with_multisample_count(2);
669
670        assert_eq!(builder.label.as_deref(), Some("Chained"));
671        assert_eq!(builder.format, Some(TextureFormat::Rgba8Unorm));
672        assert_eq!(builder.topology, PrimitiveTopology::TriangleStrip);
673        assert_eq!(builder.multisample_count, 2);
674    }
675}