nuklear_backend_wgpurs/
lib.rs

1#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] // TODO later
2
3use nuklear::{Buffer as NkBuffer, Context, ConvertConfig, DrawVertexLayoutAttribute, DrawVertexLayoutElements, DrawVertexLayoutFormat, Handle, Size, Vec2};
4use std::{
5    mem::{size_of, size_of_val, forget},
6    slice::from_raw_parts,
7    str::from_utf8,
8};
9use wgpu::*;
10
11pub const TEXTURE_FORMAT: TextureFormat = TextureFormat::Bgra8Unorm;
12
13#[allow(dead_code)]
14struct Vertex {
15    pos: [f32; 2], // "Position",
16    tex: [f32; 2], // "TexCoord",
17    col: [u8; 4],  // "Color",
18}
19#[allow(dead_code)]
20struct WgpuTexture {
21    texture: Texture,
22    sampler: Sampler,
23
24    pub bind_group: BindGroup,
25}
26
27type Ortho = [[f32; 4]; 4];
28
29impl WgpuTexture {
30    pub fn new(device: &mut Device, queue: &mut Queue, drawer: &Drawer, image: &[u8], width: u32, height: u32) -> Self {
31        let texture = device.create_texture(&wgpu::TextureDescriptor {
32            size: Extent3d { width: width, height: height, depth: 1 },
33            array_layer_count: 1,
34            dimension: TextureDimension::D2,
35            format: TEXTURE_FORMAT,
36            mip_level_count: 1,
37            sample_count: 1,
38            usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,
39        });
40        let sampler = device.create_sampler(&SamplerDescriptor {
41            address_mode_u: AddressMode::ClampToEdge,
42            address_mode_v: AddressMode::ClampToEdge,
43            address_mode_w: AddressMode::ClampToEdge,
44            mag_filter: FilterMode::Linear,
45            min_filter: FilterMode::Linear,
46            mipmap_filter: FilterMode::Linear,
47            lod_min_clamp: -100.0,
48            lod_max_clamp: 100.0,
49            compare_function: CompareFunction::Always,
50        });
51
52        let bytes = image.len();
53        let buffer = device.create_buffer_mapped(bytes, BufferUsage::COPY_SRC);
54        let buffer = buffer.fill_from_slice(image);
55
56        let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { todo: 0 });
57
58        let pixel_size = bytes as u32 / width / height;
59        encoder.copy_buffer_to_texture(
60            BufferCopyView {
61                buffer: &buffer,
62                offset: 0,
63                row_pitch: pixel_size * width,
64                image_height: height,
65            },
66            TextureCopyView {
67                texture: &texture,
68                mip_level: 0,
69                array_layer: 0,
70                origin: Origin3d { x: 0.0, y: 0.0, z: 0.0 },
71            },
72            Extent3d { width, height, depth: 1 },
73        );
74
75        queue.submit(&[encoder.finish()]);
76
77        WgpuTexture {
78            bind_group: device.create_bind_group(&BindGroupDescriptor {
79                layout: &drawer.tla,
80                bindings: &[
81                    wgpu::Binding {
82                        binding: 0,
83                        resource: BindingResource::TextureView(&texture.create_default_view()),
84                    },
85                    wgpu::Binding {
86                        binding: 1,
87                        resource: BindingResource::Sampler(&sampler),
88                    },
89                ],
90            }),
91            sampler,
92            texture,
93        }
94    }
95}
96
97pub struct Drawer {
98    cmd: NkBuffer,
99    pso: RenderPipeline,
100    tla: BindGroupLayout,
101    tex: Vec<WgpuTexture>,
102    ubf: Buffer,
103    ubg: BindGroup,
104    vsz: usize,
105    esz: usize,
106    vle: DrawVertexLayoutElements,
107
108    pub col: Option<Color>,
109}
110
111impl Drawer {
112    pub fn new(device: &mut Device, col: Color, texture_count: usize, vbo_size: usize, ebo_size: usize, command_buffer: NkBuffer) -> Drawer {
113        let vs = include_bytes!("../shaders/vs.fx");
114        let fs = include_bytes!("../shaders/ps.fx");
115
116        let vs = device.create_shader_module(compile_glsl(from_utf8(vs).unwrap(), glsl_to_spirv::ShaderType::Vertex).as_slice());
117        let fs = device.create_shader_module(compile_glsl(from_utf8(fs).unwrap(), glsl_to_spirv::ShaderType::Fragment).as_slice());
118
119        let ubf = device.create_buffer(&BufferDescriptor {
120            size: size_of::<Ortho>() as u64,
121            usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,
122        });
123        let ubg = BindGroupLayoutDescriptor {
124            bindings: &[BindGroupLayoutBinding {
125                binding: 0,
126                visibility: ShaderStage::VERTEX,
127                ty: wgpu::BindingType::UniformBuffer { dynamic: false },
128            }],
129        };
130
131        let tbg = BindGroupLayoutDescriptor {
132            bindings: &[
133                BindGroupLayoutBinding {
134                    binding: 0,
135                    visibility: ShaderStage::FRAGMENT,
136                    ty: wgpu::BindingType::SampledTexture {
137                        multisampled: false,
138                        dimension: wgpu::TextureViewDimension::D2,
139                    },
140                },
141                BindGroupLayoutBinding {
142                    binding: 1,
143                    visibility: ShaderStage::FRAGMENT,
144                    ty: BindingType::Sampler,
145                },
146            ],
147        };
148        let tla = device.create_bind_group_layout(&tbg);
149        let ula = device.create_bind_group_layout(&ubg);
150
151        Drawer {
152            cmd: command_buffer,
153            col: Some(col),
154            pso: device.create_render_pipeline(&RenderPipelineDescriptor {
155                layout: &device.create_pipeline_layout(&PipelineLayoutDescriptor { bind_group_layouts: &[&ula, &tla] }),
156                vertex_stage: ProgrammableStageDescriptor { module: &vs, entry_point: "main" },
157                fragment_stage: Some(ProgrammableStageDescriptor { module: &fs, entry_point: "main" }),
158                rasterization_state: Some(RasterizationStateDescriptor {
159                    front_face: FrontFace::Cw,
160                    cull_mode: CullMode::None,
161                    depth_bias: 0,
162                    depth_bias_slope_scale: 0.0,
163                    depth_bias_clamp: 0.0,
164                }),
165                primitive_topology: PrimitiveTopology::TriangleList,
166                color_states: &[ColorStateDescriptor {
167                    format: TEXTURE_FORMAT,
168                    color_blend: BlendDescriptor {
169                        src_factor: BlendFactor::SrcAlpha,
170                        dst_factor: BlendFactor::OneMinusSrcAlpha,
171                        operation: BlendOperation::Add,
172                    },
173                    alpha_blend: BlendDescriptor {
174                        src_factor: BlendFactor::OneMinusDstAlpha,
175                        dst_factor: BlendFactor::One,
176                        operation: BlendOperation::Add,
177                    },
178                    write_mask: ColorWrite::ALL,
179                }],
180                depth_stencil_state: None,
181                index_format: IndexFormat::Uint16,
182                vertex_buffers: &[VertexBufferDescriptor {
183                    stride: (size_of::<Vertex>()) as u64,
184                    step_mode: InputStepMode::Vertex,
185                    attributes: &[
186                        wgpu::VertexAttributeDescriptor {
187                            format: VertexFormat::Float2,
188                            shader_location: 0,
189                            offset: 0,
190                        },
191                        wgpu::VertexAttributeDescriptor {
192                            format: VertexFormat::Float2,
193                            shader_location: 1,
194                            offset: 8,
195                        },
196                        wgpu::VertexAttributeDescriptor {
197                            format: VertexFormat::Uint,
198                            shader_location: 2,
199                            offset: 16,
200                        },
201                    ],
202                }],
203                sample_count: 1,
204                sample_mask: !0,
205                alpha_to_coverage_enabled: false,
206            }),
207            tex: Vec::with_capacity(texture_count + 1),
208            vsz: vbo_size,
209            esz: ebo_size,
210            ubg: device.create_bind_group(&wgpu::BindGroupDescriptor {
211                layout: &ula,
212                bindings: &[Binding {
213                    binding: 0,
214                    resource: BindingResource::Buffer {
215                        buffer: &ubf,
216                        range: 0..(size_of::<Ortho>() as u64),
217                    },
218                }],
219            }),
220            ubf: ubf,
221            vle: DrawVertexLayoutElements::new(&[
222                (DrawVertexLayoutAttribute::Position, DrawVertexLayoutFormat::Float, 0),
223                (DrawVertexLayoutAttribute::TexCoord, DrawVertexLayoutFormat::Float, size_of::<f32>() as Size * 2),
224                (DrawVertexLayoutAttribute::Color, DrawVertexLayoutFormat::B8G8R8A8, size_of::<f32>() as Size * 4),
225                (DrawVertexLayoutAttribute::AttributeCount, DrawVertexLayoutFormat::Count, 0),
226            ]),
227            tla: tla,
228        }
229    }
230
231    pub fn add_texture(&mut self, device: &mut Device, queue: &mut Queue, image: &[u8], width: u32, height: u32) -> Handle {
232        self.tex.push(WgpuTexture::new(device, queue, self, image, width, height));
233        Handle::from_id(self.tex.len() as i32)
234    }
235
236    pub fn draw(&mut self, ctx: &mut Context, cfg: &mut ConvertConfig, encoder: &mut CommandEncoder, view: &TextureView, device: &mut Device, width: u32, height: u32, scale: Vec2) {
237        let ortho: Ortho = [
238            [2.0f32 / width as f32, 0.0f32, 0.0f32, 0.0f32],
239            [0.0f32, -2.0f32 / height as f32, 0.0f32, 0.0f32],
240            [0.0f32, 0.0f32, -1.0f32, 0.0f32],
241            [-1.0f32, 1.0f32, 0.0f32, 1.0f32],
242        ];
243        let ubf_size = size_of_val(&ortho);
244        cfg.set_vertex_layout(&self.vle);
245        cfg.set_vertex_size(size_of::<Vertex>());
246
247        let mut vbf = device.create_buffer_mapped(self.vsz, BufferUsage::VERTEX | BufferUsage::COPY_SRC);
248        let mut ebf = device.create_buffer_mapped(self.esz, BufferUsage::INDEX | BufferUsage::COPY_SRC);
249        let ubf = device.create_buffer_mapped(ubf_size, BufferUsage::UNIFORM | BufferUsage::COPY_SRC);
250        {
251            let mut vbuf = NkBuffer::with_fixed(&mut vbf.data);
252            let mut ebuf = NkBuffer::with_fixed(&mut ebf.data);
253
254            ctx.convert(&mut self.cmd, &mut vbuf, &mut ebuf, cfg);
255
256            let vbf = unsafe { std::slice::from_raw_parts_mut(vbf.data as *mut _ as *mut Vertex, vbf.data.len() / std::mem::size_of::<Vertex>()) };
257
258            for v in vbf.iter_mut() {
259                v.pos[1] = height as f32 - v.pos[1];
260            }
261        }
262        let vbf = vbf.finish();
263        let ebf = ebf.finish();
264        let ubf = ubf.fill_from_slice(as_typed_slice(&ortho));
265
266        encoder.copy_buffer_to_buffer(&ubf, 0, &self.ubf, 0, ubf_size as u64);
267
268        let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor {
269            color_attachments: &[RenderPassColorAttachmentDescriptor {
270                attachment: &view,
271                load_op: match self.col {
272                    Some(_) => wgpu::LoadOp::Clear,
273                    _ => wgpu::LoadOp::Load,
274                },
275                resolve_target: None,
276                store_op: StoreOp::Store,
277                clear_color: self.col.unwrap_or(Color { r: 1.0, g: 2.0, b: 3.0, a: 1.0 }),
278            }],
279            depth_stencil_attachment: None,
280        });
281        rpass.set_pipeline(&self.pso);
282
283        rpass.set_vertex_buffers(0, &[(&vbf, 0)]);
284        rpass.set_index_buffer(&ebf, 0);
285
286        rpass.set_bind_group(0, &self.ubg, &[]);
287
288        let mut start = 0;
289        let mut end;
290
291        for cmd in ctx.draw_command_iterator(&self.cmd) {
292            if cmd.elem_count() < 1 {
293                continue;
294            }
295
296            let id = cmd.texture().id().unwrap();
297            let res = self.find_res(id).unwrap();
298
299            end = start + cmd.elem_count();
300
301            rpass.set_bind_group(1, &res.bind_group, &[]);
302
303            rpass.set_scissor_rect((cmd.clip_rect().x * scale.x) as u32, (cmd.clip_rect().y * scale.y) as u32, (cmd.clip_rect().w * scale.x) as u32, (cmd.clip_rect().h * scale.y) as u32);
304
305            rpass.draw_indexed(start..end, 0 as i32, 0..1);
306
307            start = end;
308        }
309    }
310
311    fn find_res(&self, id: i32) -> Option<&WgpuTexture> {
312        if id > 0 && id as usize <= self.tex.len() {
313            self.tex.get((id - 1) as usize)
314        } else {
315            None
316        }
317    }
318}
319
320fn as_typed_slice<T>(data: &[T]) -> &[u8] {
321    unsafe { from_raw_parts(data.as_ptr() as *const u8, data.len() * size_of::<T>()) }
322}
323fn compile_glsl(code: &str, ty: glsl_to_spirv::ShaderType) -> Vec<u32> {
324    use std::io::Read;
325
326    let mut output = glsl_to_spirv::compile(code, ty).unwrap();
327    let mut spv = Vec::new();
328    output.read_to_end(&mut spv).unwrap();
329    let spv32: Vec<u32> = unsafe { Vec::from_raw_parts(spv.as_mut_ptr() as *mut _ as *mut u32, spv.len() / 4, spv.capacity() / 4) };
330    forget(spv);
331    spv32
332}