Skip to main content

anvilkit_render/renderer/
buffer.rs

1//! # 顶点和索引缓冲区管理
2//!
3//! 提供类型安全的 GPU 缓冲区创建和顶点数据定义。
4
5use wgpu::{Buffer, BufferUsages, VertexBufferLayout, VertexStepMode, VertexAttribute, VertexFormat};
6use bytemuck::{Pod, Zeroable};
7
8use crate::renderer::RenderDevice;
9
10/// 顶点数据 trait
11///
12/// 实现此 trait 的类型可以安全地用作 GPU 顶点缓冲区数据。
13///
14/// # 示例
15///
16/// ```rust
17/// use anvilkit_render::renderer::buffer::{Vertex, ColorVertex};
18///
19/// let layout = ColorVertex::layout();
20/// assert_eq!(layout.array_stride, std::mem::size_of::<ColorVertex>() as u64);
21/// ```
22pub trait Vertex: Pod + Zeroable {
23    /// 返回此顶点类型的缓冲区布局描述
24    fn layout() -> VertexBufferLayout<'static>;
25}
26
27/// 带颜色的顶点
28///
29/// 包含 3D 位置和 RGB 颜色的基础顶点类型。
30///
31/// # 内存布局
32///
33/// | 偏移 | 属性 | 格式 |
34/// |------|------|------|
35/// | 0 | position | Float32x3 |
36/// | 12 | color | Float32x3 |
37///
38/// # 示例
39///
40/// ```rust
41/// use anvilkit_render::renderer::buffer::ColorVertex;
42///
43/// let vertex = ColorVertex {
44///     position: [0.0, 0.5, 0.0],
45///     color: [1.0, 0.0, 0.0],
46/// };
47/// assert_eq!(vertex.position[1], 0.5);
48/// ```
49#[repr(C)]
50#[derive(Copy, Clone, Debug, Pod, Zeroable)]
51pub struct ColorVertex {
52    /// 3D 位置 (x, y, z)
53    pub position: [f32; 3],
54    /// RGB 颜色 (r, g, b)
55    pub color: [f32; 3],
56}
57
58impl Vertex for ColorVertex {
59    fn layout() -> VertexBufferLayout<'static> {
60        const ATTRIBUTES: &[VertexAttribute] = &[
61            // position: location 0
62            VertexAttribute {
63                offset: 0,
64                shader_location: 0,
65                format: VertexFormat::Float32x3,
66            },
67            // color: location 1
68            VertexAttribute {
69                offset: std::mem::size_of::<[f32; 3]>() as u64,
70                shader_location: 1,
71                format: VertexFormat::Float32x3,
72            },
73        ];
74
75        VertexBufferLayout {
76            array_stride: std::mem::size_of::<ColorVertex>() as u64,
77            step_mode: VertexStepMode::Vertex,
78            attributes: ATTRIBUTES,
79        }
80    }
81}
82
83/// 网格顶点(位置 + 法线 + 纹理坐标)
84///
85/// 用于 glTF 网格渲染的标准顶点格式。
86///
87/// # 内存布局
88///
89/// | 偏移 | 属性 | 格式 |
90/// |------|------|------|
91/// | 0 | position | Float32x3 |
92/// | 12 | normal | Float32x3 |
93/// | 24 | texcoord | Float32x2 |
94///
95/// # 示例
96///
97/// ```rust
98/// use anvilkit_render::renderer::buffer::MeshVertex;
99///
100/// let vertex = MeshVertex {
101///     position: [0.0, 1.0, 0.0],
102///     normal: [0.0, 1.0, 0.0],
103///     texcoord: [0.5, 0.5],
104/// };
105/// assert_eq!(std::mem::size_of::<MeshVertex>(), 32);
106/// ```
107#[repr(C)]
108#[derive(Copy, Clone, Debug, Pod, Zeroable)]
109pub struct MeshVertex {
110    /// 3D 位置 (x, y, z)
111    pub position: [f32; 3],
112    /// 表面法线 (x, y, z)
113    pub normal: [f32; 3],
114    /// 纹理坐标 (u, v)
115    pub texcoord: [f32; 2],
116}
117
118impl Vertex for MeshVertex {
119    fn layout() -> VertexBufferLayout<'static> {
120        const ATTRIBUTES: &[VertexAttribute] = &[
121            VertexAttribute {
122                offset: 0,
123                shader_location: 0,
124                format: VertexFormat::Float32x3,
125            },
126            VertexAttribute {
127                offset: 12,
128                shader_location: 1,
129                format: VertexFormat::Float32x3,
130            },
131            VertexAttribute {
132                offset: 24,
133                shader_location: 2,
134                format: VertexFormat::Float32x2,
135            },
136        ];
137
138        VertexBufferLayout {
139            array_stride: std::mem::size_of::<MeshVertex>() as u64,
140            step_mode: VertexStepMode::Vertex,
141            attributes: ATTRIBUTES,
142        }
143    }
144}
145
146/// PBR 顶点(位置 + 法线 + 纹理坐标 + 切线)
147///
148/// 用于支持法线贴图的 PBR 渲染管线。
149///
150/// # 内存布局
151///
152/// | 偏移 | 属性 | 格式 |
153/// |------|------|------|
154/// | 0 | position | Float32x3 |
155/// | 12 | normal | Float32x3 |
156/// | 24 | texcoord | Float32x2 |
157/// | 32 | tangent | Float32x4 |
158///
159/// # 示例
160///
161/// ```rust
162/// use anvilkit_render::renderer::buffer::PbrVertex;
163///
164/// let vertex = PbrVertex {
165///     position: [0.0, 1.0, 0.0],
166///     normal: [0.0, 1.0, 0.0],
167///     texcoord: [0.5, 0.5],
168///     tangent: [1.0, 0.0, 0.0, 1.0],
169/// };
170/// assert_eq!(std::mem::size_of::<PbrVertex>(), 48);
171/// ```
172#[repr(C)]
173#[derive(Copy, Clone, Debug, Pod, Zeroable)]
174pub struct PbrVertex {
175    /// 3D 位置 (x, y, z)
176    pub position: [f32; 3],
177    /// 表面法线 (x, y, z)
178    pub normal: [f32; 3],
179    /// 纹理坐标 (u, v)
180    pub texcoord: [f32; 2],
181    /// 切线 (x, y, z, w) — w 是 bitangent sign
182    pub tangent: [f32; 4],
183}
184
185impl Vertex for PbrVertex {
186    fn layout() -> VertexBufferLayout<'static> {
187        const ATTRIBUTES: &[VertexAttribute] = &[
188            VertexAttribute {
189                offset: 0,
190                shader_location: 0,
191                format: VertexFormat::Float32x3,
192            },
193            VertexAttribute {
194                offset: 12,
195                shader_location: 1,
196                format: VertexFormat::Float32x3,
197            },
198            VertexAttribute {
199                offset: 24,
200                shader_location: 2,
201                format: VertexFormat::Float32x2,
202            },
203            VertexAttribute {
204                offset: 32,
205                shader_location: 3,
206                format: VertexFormat::Float32x4,
207            },
208        ];
209
210        VertexBufferLayout {
211            array_stride: std::mem::size_of::<PbrVertex>() as u64,
212            step_mode: VertexStepMode::Vertex,
213            attributes: ATTRIBUTES,
214        }
215    }
216}
217
218/// 带骨骼蒙皮的 PBR 顶点
219///
220/// 在 PbrVertex 基础上增加 joint_indices 和 joint_weights,
221/// 用于 GPU 端骨骼动画蒙皮。
222///
223/// # 内存布局
224///
225/// | 偏移 | 属性 | 格式 |
226/// |------|------|------|
227/// | 0 | position | Float32x3 |
228/// | 12 | normal | Float32x3 |
229/// | 24 | texcoord | Float32x2 |
230/// | 32 | tangent | Float32x4 |
231/// | 48 | joint_indices | Uint16x4 |
232/// | 56 | joint_weights | Float32x4 |
233#[repr(C)]
234#[derive(Copy, Clone, Debug, Pod, Zeroable)]
235pub struct SkinnedVertex {
236    pub position: [f32; 3],
237    pub normal: [f32; 3],
238    pub texcoord: [f32; 2],
239    pub tangent: [f32; 4],
240    pub joint_indices: [u16; 4],
241    pub joint_weights: [f32; 4],
242}
243
244impl Vertex for SkinnedVertex {
245    fn layout() -> VertexBufferLayout<'static> {
246        const ATTRIBUTES: &[VertexAttribute] = &[
247            VertexAttribute { offset: 0, shader_location: 0, format: VertexFormat::Float32x3 },
248            VertexAttribute { offset: 12, shader_location: 1, format: VertexFormat::Float32x3 },
249            VertexAttribute { offset: 24, shader_location: 2, format: VertexFormat::Float32x2 },
250            VertexAttribute { offset: 32, shader_location: 3, format: VertexFormat::Float32x4 },
251            VertexAttribute { offset: 48, shader_location: 4, format: VertexFormat::Uint16x4 },
252            VertexAttribute { offset: 56, shader_location: 5, format: VertexFormat::Float32x4 },
253        ];
254
255        VertexBufferLayout {
256            array_stride: std::mem::size_of::<SkinnedVertex>() as u64,
257            step_mode: VertexStepMode::Vertex,
258            attributes: ATTRIBUTES,
259        }
260    }
261}
262
263/// 创建顶点缓冲区
264///
265/// 将顶点数据上传到 GPU 内存。
266///
267/// # 参数
268///
269/// - `device`: 渲染设备
270/// - `label`: 缓冲区标签(用于调试)
271/// - `vertices`: 顶点数据切片
272///
273/// # 示例
274///
275/// ```rust,no_run
276/// use anvilkit_render::renderer::buffer::{ColorVertex, create_vertex_buffer};
277/// use anvilkit_render::renderer::RenderDevice;
278///
279/// # async fn example(device: &RenderDevice) {
280/// let vertices = [
281///     ColorVertex { position: [0.0, 0.5, 0.0], color: [1.0, 0.0, 0.0] },
282///     ColorVertex { position: [-0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0] },
283///     ColorVertex { position: [0.5, -0.5, 0.0], color: [0.0, 0.0, 1.0] },
284/// ];
285/// let buffer = create_vertex_buffer(device, "Triangle", &vertices);
286/// # }
287/// ```
288pub fn create_vertex_buffer<V: Vertex>(
289    device: &RenderDevice,
290    label: &str,
291    vertices: &[V],
292) -> Buffer {
293    use wgpu::util::{BufferInitDescriptor, DeviceExt};
294
295    device.device().create_buffer_init(&BufferInitDescriptor {
296        label: Some(label),
297        contents: bytemuck::cast_slice(vertices),
298        usage: BufferUsages::VERTEX,
299    })
300}
301
302/// 创建索引缓冲区
303///
304/// 将索引数据上传到 GPU 内存。
305///
306/// # 参数
307///
308/// - `device`: 渲染设备
309/// - `label`: 缓冲区标签
310/// - `indices`: 16 位索引数据切片
311///
312/// # 示例
313///
314/// ```rust,no_run
315/// use anvilkit_render::renderer::buffer::create_index_buffer;
316/// use anvilkit_render::renderer::RenderDevice;
317///
318/// # async fn example(device: &RenderDevice) {
319/// let indices: &[u16] = &[0, 1, 2];
320/// let buffer = create_index_buffer(device, "Triangle Indices", indices);
321/// # }
322/// ```
323pub fn create_index_buffer(
324    device: &RenderDevice,
325    label: &str,
326    indices: &[u16],
327) -> Buffer {
328    use wgpu::util::{BufferInitDescriptor, DeviceExt};
329
330    device.device().create_buffer_init(&BufferInitDescriptor {
331        label: Some(label),
332        contents: bytemuck::cast_slice(indices),
333        usage: BufferUsages::INDEX,
334    })
335}
336
337/// 创建 u32 索引缓冲区
338///
339/// 用于 glTF 模型等需要超过 65535 个顶点的网格。
340///
341/// # 示例
342///
343/// ```rust,no_run
344/// use anvilkit_render::renderer::buffer::create_index_buffer_u32;
345/// use anvilkit_render::renderer::RenderDevice;
346///
347/// # async fn example(device: &RenderDevice) {
348/// let indices: &[u32] = &[0, 1, 2, 2, 3, 0];
349/// let buffer = create_index_buffer_u32(device, "Mesh Indices", indices);
350/// # }
351/// ```
352pub fn create_index_buffer_u32(
353    device: &RenderDevice,
354    label: &str,
355    indices: &[u32],
356) -> Buffer {
357    use wgpu::util::{BufferInitDescriptor, DeviceExt};
358
359    device.device().create_buffer_init(&BufferInitDescriptor {
360        label: Some(label),
361        contents: bytemuck::cast_slice(indices),
362        usage: BufferUsages::INDEX,
363    })
364}
365
366/// AnvilKit 标准深度纹理格式
367pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
368
369/// AnvilKit HDR 渲染目标格式
370pub const HDR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
371
372/// 默认阴影贴图分辨率
373pub const SHADOW_MAP_SIZE: u32 = 2048;
374
375/// MSAA 采样数(设为 1 可禁用 MSAA)
376pub const MSAA_SAMPLE_COUNT: u32 = 4;
377
378/// 创建阴影深度贴图
379///
380/// 使用 Depth32Float 格式,可作为渲染附件(shadow pass)和纹理采样(main pass)。
381///
382/// # 示例
383///
384/// ```rust,no_run
385/// use anvilkit_render::renderer::buffer::create_shadow_map;
386/// use anvilkit_render::renderer::RenderDevice;
387///
388/// # async fn example(device: &RenderDevice) {
389/// let (shadow_tex, shadow_view) = create_shadow_map(device, 2048, "Shadow Map");
390/// # }
391/// ```
392pub fn create_shadow_map(
393    device: &RenderDevice,
394    size: u32,
395    label: &str,
396) -> (wgpu::Texture, wgpu::TextureView) {
397    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
398        label: Some(label),
399        size: wgpu::Extent3d {
400            width: size,
401            height: size,
402            depth_or_array_layers: 1,
403        },
404        mip_level_count: 1,
405        sample_count: 1,
406        dimension: wgpu::TextureDimension::D2,
407        format: DEPTH_FORMAT,
408        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
409        view_formats: &[],
410    });
411    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
412    (texture, view)
413}
414
415/// 创建阴影比较采样器
416///
417/// 使用 `LessEqual` 比较函数,用于 `textureSampleCompare()` 调用。
418///
419/// # 示例
420///
421/// ```rust,no_run
422/// use anvilkit_render::renderer::buffer::create_shadow_sampler;
423/// use anvilkit_render::renderer::RenderDevice;
424///
425/// # async fn example(device: &RenderDevice) {
426/// let sampler = create_shadow_sampler(device, "Shadow Sampler");
427/// # }
428/// ```
429pub fn create_shadow_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
430    device.device().create_sampler(&wgpu::SamplerDescriptor {
431        label: Some(label),
432        address_mode_u: wgpu::AddressMode::ClampToEdge,
433        address_mode_v: wgpu::AddressMode::ClampToEdge,
434        mag_filter: wgpu::FilterMode::Linear,
435        min_filter: wgpu::FilterMode::Linear,
436        compare: Some(wgpu::CompareFunction::LessEqual),
437        ..Default::default()
438    })
439}
440
441/// 创建 Uniform 缓冲区
442///
443/// 使用 `UNIFORM | COPY_DST` 用法创建,支持每帧通过 `queue.write_buffer()` 更新。
444///
445/// # 参数
446///
447/// - `device`: 渲染设备
448/// - `label`: 缓冲区标签
449/// - `contents`: 初始数据(字节切片)
450///
451/// # 示例
452///
453/// ```rust,no_run
454/// use anvilkit_render::renderer::buffer::create_uniform_buffer;
455/// use anvilkit_render::renderer::RenderDevice;
456///
457/// # async fn example(device: &RenderDevice) {
458/// let mvp_data = [0u8; 64]; // 4x4 f32 矩阵
459/// let buffer = create_uniform_buffer(device, "MVP Uniform", &mvp_data);
460/// # }
461/// ```
462pub fn create_uniform_buffer(
463    device: &RenderDevice,
464    label: &str,
465    contents: &[u8],
466) -> Buffer {
467    use wgpu::util::{BufferInitDescriptor, DeviceExt};
468
469    device.device().create_buffer_init(&BufferInitDescriptor {
470        label: Some(label),
471        contents,
472        usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
473    })
474}
475
476/// 创建深度纹理和视图
477///
478/// 在窗口大小变化时需要重新创建。
479///
480/// # 参数
481///
482/// - `device`: 渲染设备
483/// - `width`: 纹理宽度
484/// - `height`: 纹理高度
485/// - `label`: 纹理标签
486///
487/// # 返回
488///
489/// 返回 (Texture, TextureView) 元组
490///
491/// # 示例
492///
493/// ```rust,no_run
494/// use anvilkit_render::renderer::buffer::create_depth_texture;
495/// use anvilkit_render::renderer::RenderDevice;
496///
497/// # async fn example(device: &RenderDevice) {
498/// let (texture, view) = create_depth_texture(device, 800, 600, "Depth");
499/// # }
500/// ```
501pub fn create_depth_texture(
502    device: &RenderDevice,
503    width: u32,
504    height: u32,
505    label: &str,
506) -> (wgpu::Texture, wgpu::TextureView) {
507    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
508        label: Some(label),
509        size: wgpu::Extent3d {
510            width,
511            height,
512            depth_or_array_layers: 1,
513        },
514        mip_level_count: 1,
515        sample_count: 1,
516        dimension: wgpu::TextureDimension::D2,
517        format: DEPTH_FORMAT,
518        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
519        view_formats: &[],
520    });
521    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
522    (texture, view)
523}
524
525/// 创建 HDR 渲染目标纹理和视图
526///
527/// 使用 `Rgba16Float` 格式的离屏渲染目标,用于 HDR 渲染管线。
528/// 场景先渲染到 HDR RT,再通过后处理 pass 进行 tone mapping 输出到 swapchain。
529///
530/// # 参数
531///
532/// - `device`: 渲染设备
533/// - `width`: 纹理宽度
534/// - `height`: 纹理高度
535/// - `label`: 纹理标签
536///
537/// # 返回
538///
539/// 返回 (Texture, TextureView) 元组
540///
541/// # 示例
542///
543/// ```rust,no_run
544/// use anvilkit_render::renderer::buffer::create_hdr_render_target;
545/// use anvilkit_render::renderer::RenderDevice;
546///
547/// # async fn example(device: &RenderDevice) {
548/// let (hdr_texture, hdr_view) = create_hdr_render_target(device, 800, 600, "HDR RT");
549/// # }
550/// ```
551pub fn create_hdr_render_target(
552    device: &RenderDevice,
553    width: u32,
554    height: u32,
555    label: &str,
556) -> (wgpu::Texture, wgpu::TextureView) {
557    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
558        label: Some(label),
559        size: wgpu::Extent3d {
560            width,
561            height,
562            depth_or_array_layers: 1,
563        },
564        mip_level_count: 1,
565        sample_count: 1,
566        dimension: wgpu::TextureDimension::D2,
567        format: HDR_FORMAT,
568        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
569        view_formats: &[],
570    });
571    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
572    (texture, view)
573}
574
575/// 创建 MSAA 深度纹理(sample_count=MSAA_SAMPLE_COUNT)
576pub fn create_depth_texture_msaa(
577    device: &RenderDevice,
578    width: u32,
579    height: u32,
580    label: &str,
581) -> (wgpu::Texture, wgpu::TextureView) {
582    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
583        label: Some(label),
584        size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
585        mip_level_count: 1,
586        sample_count: MSAA_SAMPLE_COUNT,
587        dimension: wgpu::TextureDimension::D2,
588        format: DEPTH_FORMAT,
589        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
590        view_formats: &[],
591    });
592    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
593    (texture, view)
594}
595
596/// 创建 MSAA HDR 颜色纹理(sample_count=MSAA_SAMPLE_COUNT,仅 RENDER_ATTACHMENT)
597///
598/// 此纹理用作 MSAA 渲染附件,resolve 到单采样 HDR RT 后由 tonemap pass 采样。
599pub fn create_hdr_msaa_texture(
600    device: &RenderDevice,
601    width: u32,
602    height: u32,
603    label: &str,
604) -> (wgpu::Texture, wgpu::TextureView) {
605    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
606        label: Some(label),
607        size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
608        mip_level_count: 1,
609        sample_count: MSAA_SAMPLE_COUNT,
610        dimension: wgpu::TextureDimension::D2,
611        format: HDR_FORMAT,
612        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
613        view_formats: &[],
614    });
615    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
616    (texture, view)
617}
618
619/// 从 RGBA 数据创建 GPU 纹理和视图
620///
621/// # 参数
622///
623/// - `device`: 渲染设备
624/// - `queue`: GPU 命令队列(用于写入纹理数据)
625/// - `width`: 纹理宽度
626/// - `height`: 纹理高度
627/// - `data`: RGBA 像素数据(每像素 4 字节)
628/// - `label`: 纹理标签
629///
630/// # 示例
631///
632/// ```rust,no_run
633/// use anvilkit_render::renderer::buffer::create_texture;
634/// use anvilkit_render::renderer::RenderDevice;
635///
636/// # async fn example(device: &RenderDevice) {
637/// let rgba = vec![255u8; 4 * 4 * 4]; // 4x4 白色纹理
638/// let (texture, view) = create_texture(device, 4, 4, &rgba, "White Texture");
639/// # }
640/// ```
641pub fn create_texture(
642    device: &RenderDevice,
643    width: u32,
644    height: u32,
645    data: &[u8],
646    label: &str,
647) -> (wgpu::Texture, wgpu::TextureView) {
648    let size = wgpu::Extent3d {
649        width,
650        height,
651        depth_or_array_layers: 1,
652    };
653
654    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
655        label: Some(label),
656        size,
657        mip_level_count: 1,
658        sample_count: 1,
659        dimension: wgpu::TextureDimension::D2,
660        format: wgpu::TextureFormat::Rgba8UnormSrgb,
661        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
662        view_formats: &[],
663    });
664
665    device.queue().write_texture(
666        wgpu::ImageCopyTexture {
667            texture: &texture,
668            mip_level: 0,
669            origin: wgpu::Origin3d::ZERO,
670            aspect: wgpu::TextureAspect::All,
671        },
672        data,
673        wgpu::ImageDataLayout {
674            offset: 0,
675            bytes_per_row: Some(4 * width),
676            rows_per_image: Some(height),
677        },
678        size,
679    );
680
681    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
682    (texture, view)
683}
684
685/// 从 RGBA 数据创建线性空间 GPU 纹理和视图
686///
687/// 与 `create_texture` 相同,但使用 `Rgba8Unorm`(线性空间)而非 sRGB。
688/// 法线贴图必须用线性格式,否则 sRGB 解码会破坏法线方向。
689///
690/// # 示例
691///
692/// ```rust,no_run
693/// use anvilkit_render::renderer::buffer::create_texture_linear;
694/// use anvilkit_render::renderer::RenderDevice;
695///
696/// # async fn example(device: &RenderDevice) {
697/// let normal_data = vec![128u8, 128, 255, 255]; // flat normal (0.5, 0.5, 1.0)
698/// let (texture, view) = create_texture_linear(device, 1, 1, &normal_data, "Normal Map");
699/// # }
700/// ```
701pub fn create_texture_linear(
702    device: &RenderDevice,
703    width: u32,
704    height: u32,
705    data: &[u8],
706    label: &str,
707) -> (wgpu::Texture, wgpu::TextureView) {
708    let size = wgpu::Extent3d {
709        width,
710        height,
711        depth_or_array_layers: 1,
712    };
713
714    let texture = device.device().create_texture(&wgpu::TextureDescriptor {
715        label: Some(label),
716        size,
717        mip_level_count: 1,
718        sample_count: 1,
719        dimension: wgpu::TextureDimension::D2,
720        format: wgpu::TextureFormat::Rgba8Unorm,
721        usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
722        view_formats: &[],
723    });
724
725    device.queue().write_texture(
726        wgpu::ImageCopyTexture {
727            texture: &texture,
728            mip_level: 0,
729            origin: wgpu::Origin3d::ZERO,
730            aspect: wgpu::TextureAspect::All,
731        },
732        data,
733        wgpu::ImageDataLayout {
734            offset: 0,
735            bytes_per_row: Some(4 * width),
736            rows_per_image: Some(height),
737        },
738        size,
739    );
740
741    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
742    (texture, view)
743}
744
745/// 创建线性过滤纹理采样器
746///
747/// # 示例
748///
749/// ```rust,no_run
750/// use anvilkit_render::renderer::buffer::create_sampler;
751/// use anvilkit_render::renderer::RenderDevice;
752///
753/// # async fn example(device: &RenderDevice) {
754/// let sampler = create_sampler(device, "Linear Sampler");
755/// # }
756/// ```
757pub fn create_sampler(device: &RenderDevice, label: &str) -> wgpu::Sampler {
758    device.device().create_sampler(&wgpu::SamplerDescriptor {
759        label: Some(label),
760        address_mode_u: wgpu::AddressMode::Repeat,
761        address_mode_v: wgpu::AddressMode::Repeat,
762        address_mode_w: wgpu::AddressMode::Repeat,
763        mag_filter: wgpu::FilterMode::Linear,
764        min_filter: wgpu::FilterMode::Linear,
765        mipmap_filter: wgpu::FilterMode::Nearest,
766        ..Default::default()
767    })
768}
769
770#[cfg(test)]
771mod tests {
772    use super::*;
773
774    #[test]
775    fn test_color_vertex_size() {
776        assert_eq!(std::mem::size_of::<ColorVertex>(), 24); // 6 * f32
777    }
778
779    #[test]
780    fn test_color_vertex_layout() {
781        let layout = ColorVertex::layout();
782        assert_eq!(layout.array_stride, 24);
783        assert_eq!(layout.attributes.len(), 2);
784        assert_eq!(layout.attributes[0].offset, 0);
785        assert_eq!(layout.attributes[0].shader_location, 0);
786        assert_eq!(layout.attributes[1].offset, 12);
787        assert_eq!(layout.attributes[1].shader_location, 1);
788    }
789
790    #[test]
791    fn test_color_vertex_creation() {
792        let v = ColorVertex {
793            position: [1.0, 2.0, 3.0],
794            color: [0.5, 0.5, 0.5],
795        };
796        assert_eq!(v.position, [1.0, 2.0, 3.0]);
797        assert_eq!(v.color, [0.5, 0.5, 0.5]);
798    }
799
800    #[test]
801    fn test_color_vertex_bytemuck() {
802        let vertices = [
803            ColorVertex { position: [0.0, 0.5, 0.0], color: [1.0, 0.0, 0.0] },
804            ColorVertex { position: [-0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0] },
805        ];
806        let bytes: &[u8] = bytemuck::cast_slice(&vertices);
807        assert_eq!(bytes.len(), 48); // 2 vertices * 24 bytes each
808    }
809
810    #[test]
811    fn test_depth_format() {
812        assert_eq!(DEPTH_FORMAT, wgpu::TextureFormat::Depth32Float);
813    }
814
815    #[test]
816    fn test_mesh_vertex_size() {
817        assert_eq!(std::mem::size_of::<MeshVertex>(), 32); // 8 * f32
818    }
819
820    #[test]
821    fn test_mesh_vertex_layout() {
822        let layout = MeshVertex::layout();
823        assert_eq!(layout.array_stride, 32);
824        assert_eq!(layout.attributes.len(), 3);
825        assert_eq!(layout.attributes[0].offset, 0);   // position
826        assert_eq!(layout.attributes[1].offset, 12);  // normal
827        assert_eq!(layout.attributes[2].offset, 24);  // texcoord
828    }
829
830    #[test]
831    fn test_pbr_vertex_size() {
832        assert_eq!(std::mem::size_of::<PbrVertex>(), 48); // 12 * f32
833    }
834
835    #[test]
836    fn test_pbr_vertex_layout() {
837        let layout = PbrVertex::layout();
838        assert_eq!(layout.array_stride, 48);
839        assert_eq!(layout.attributes.len(), 4);
840        assert_eq!(layout.attributes[0].offset, 0);   // position
841        assert_eq!(layout.attributes[1].offset, 12);  // normal
842        assert_eq!(layout.attributes[2].offset, 24);  // texcoord
843        assert_eq!(layout.attributes[3].offset, 32);  // tangent
844    }
845}