1use std::{borrow::Cow, collections::HashMap, rc::Rc, sync::mpsc};
4
5use glium::{
6 backend::{Context, Facade},
7 framebuffer::SimpleFrameBuffer,
8 program,
9 texture::{ClientFormat, MipmapsOption, RawImage2d, SrgbTexture2d, UncompressedFloatFormat},
10 uniform,
11 uniforms::UniformBuffer,
12 vertex::{AttributeType, VertexBufferAny},
13 Blend, DrawParameters, Program, Surface, Texture2d,
14};
15
16use crate::{
17 bitmap::{BitmapFormat, OwnedBitmap},
18 gpu_driver::ShaderType,
19};
20
21use super::{GpuCommand, GpuDriver, IndexBuffer, RenderBuffer, VertexBuffer, VertexBufferFormat};
22pub use either_texture::{EitherSampler, EitherTexture};
23
24mod either_texture;
25
26type StaticVertexFormatBinding =
27 Cow<'static, [(Cow<'static, str>, usize, i32, AttributeType, bool)]>;
28
29lazy_static::lazy_static! {
30 static ref BINDING_2F4UB2F2F28F: StaticVertexFormatBinding = Cow::Owned(vec![
31 (
32 Cow::Borrowed("in_Position"),
33 0,
34 -1,
35 glium::vertex::AttributeType::F32F32,
36 false,
37 ),
38 (
39 Cow::Borrowed("in_Color"),
40 2 * ::std::mem::size_of::<f32>(),
41 -1,
42 glium::vertex::AttributeType::U8U8U8U8,
43 true,
44 ),
45 (
46 Cow::Borrowed("in_TexCoord"),
47 2 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
48 -1,
49 glium::vertex::AttributeType::F32F32,
50 false,
51 ),
52 (
53 Cow::Borrowed("in_ObjCoord"),
54 4 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
55 -1,
56 glium::vertex::AttributeType::F32F32,
57 false,
58 ),
59 (
60 Cow::Borrowed("in_Data0"),
61 6 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
62 -1,
63 glium::vertex::AttributeType::F32F32F32F32,
64 false,
65 ),
66 (
67 Cow::Borrowed("in_Data1"),
68 10 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
69 -1,
70 glium::vertex::AttributeType::F32F32F32F32,
71 false,
72 ),
73 (
74 Cow::Borrowed("in_Data2"),
75 14 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
76 -1,
77 glium::vertex::AttributeType::F32F32F32F32,
78 false,
79 ),
80 (
81 Cow::Borrowed("in_Data3"),
82 18 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
83 -1,
84 glium::vertex::AttributeType::F32F32F32F32,
85 false,
86 ),
87 (
88 Cow::Borrowed("in_Data4"),
89 22 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
90 -1,
91 glium::vertex::AttributeType::F32F32F32F32,
92 false,
93 ),
94 (
95 Cow::Borrowed("in_Data5"),
96 26 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
97 -1,
98 glium::vertex::AttributeType::F32F32F32F32,
99 false,
100 ),
101 (
102 Cow::Borrowed("in_Data6"),
103 30 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
104 -1,
105 glium::vertex::AttributeType::F32F32F32F32,
106 false,
107 ),
108 ]);
109
110 static ref BINDING_2F4UB2F: StaticVertexFormatBinding = Cow::Owned(vec![
111 (
112 Cow::Borrowed("in_Position"),
113 0,
114 -1,
115 glium::vertex::AttributeType::F32F32,
116 false,
117 ),
118 (
119 Cow::Borrowed("in_Color"),
120 2 * ::std::mem::size_of::<f32>(),
121 -1,
122 glium::vertex::AttributeType::U8U8U8U8,
123 true,
124 ),
125 (
126 Cow::Borrowed("in_TexCoord"),
127 2 * ::std::mem::size_of::<f32>() + 4 * ::std::mem::size_of::<u8>(),
128 -1,
129 glium::vertex::AttributeType::F32F32,
130 false,
131 ),
132 ]);
133
134}
135
136#[derive(Debug, thiserror::Error)]
138pub enum GliumGpuDriverError {
139 #[error("Failed to create `glium` textures")]
140 TextureCreationError(#[from] glium::texture::TextureCreationError),
141 #[error("Failed to shader program in `glium`")]
142 ProgramCreationError(#[from] glium::program::ProgramChooserCreationError),
143 #[error("Failed to create `glium` index buffer")]
144 IndexBufferCreationError(#[from] glium::index::BufferCreationError),
145 #[error("Failed to create `glium` vertex buffer")]
146 VertexBufferCreationError(#[from] glium::vertex::BufferCreationError),
147 #[error("Failed to create `glium` buffer")]
148 BufferCreationError(#[from] glium::buffer::BufferCreationError),
149 #[error("Failed to create `glium` framebuffer")]
150 FrameBufferCreationError(#[from] glium::framebuffer::ValidationError),
151 #[error("Failed to draw")]
152 DrawError(#[from] glium::DrawError),
153 #[error(
154 "The index offset ({draw_index_offset}) and size ({draw_index_size}) used in draw is out of range from the selected index buffer (size = {index_buffer_size})"
155 )]
156 DrawIndexOutOfRange {
157 index_buffer_size: usize,
158 draw_index_offset: u32,
159 draw_index_size: u32,
160 },
161}
162
163pub fn create_gpu_driver<F>(
185 facade: &F,
186) -> Result<(GliumGpuDriverSender, GliumGpuDriverReceiver), GliumGpuDriverError>
187where
188 F: Facade + ?Sized,
189{
190 let (sender, receiver) = mpsc::channel();
191 Ok((
192 GliumGpuDriverSender {
193 next_texture_id: 0,
194 next_render_buffer_id: 0,
195 next_geometry_id: 0,
196 sender,
197 },
198 GliumGpuDriverReceiver::new(receiver, facade.get_context())?,
199 ))
200}
201
202enum GliumGpuCommand {
203 CreateTexture(u32, OwnedBitmap),
204 UpdateTexture(u32, OwnedBitmap),
205 DestroyTexture(u32),
206 CreateRenderBuffer(u32, RenderBuffer),
207 DestroyRenderBuffer(u32),
208 CreateGeometry(u32, GliumDriverVertexBuffer, IndexBuffer),
209 UpdateGeometry(u32, GliumDriverVertexBuffer, IndexBuffer),
210 DestroyGeometry(u32),
211 UpdateCommandList(Vec<GpuCommand>),
212}
213
214enum GliumDriverVertexBuffer {
216 Format2f4ub2f(Vec<ul_sys::ULVertex_2f_4ub_2f>),
217 Format2f4ub2f2f28f(Vec<ul_sys::ULVertex_2f_4ub_2f_2f_28f>),
218}
219
220impl GliumDriverVertexBuffer {
221 fn into_glium_vertex_buffer<F>(
222 self,
223 context: &F,
224 ) -> Result<VertexBufferAny, GliumGpuDriverError>
225 where
226 F: Facade + ?Sized,
227 {
228 match self {
229 GliumDriverVertexBuffer::Format2f4ub2f(buf) => {
230 let element_size = std::mem::size_of::<ul_sys::ULVertex_2f_4ub_2f>();
231
232 Ok(unsafe {
235 glium::VertexBuffer::new_raw(context, &buf, &BINDING_2F4UB2F, element_size)
236 }?
237 .into())
238 }
239 GliumDriverVertexBuffer::Format2f4ub2f2f28f(buf) => {
240 let element_size = std::mem::size_of::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>();
241
242 Ok(unsafe {
245 glium::VertexBuffer::new_raw(context, &buf, &BINDING_2F4UB2F2F28F, element_size)
246 }?
247 .into())
248 }
249 }
250 }
251}
252
253pub struct GliumGpuDriverSender {
261 next_texture_id: u32,
262 next_render_buffer_id: u32,
263 next_geometry_id: u32,
264 sender: mpsc::Sender<GliumGpuCommand>,
265}
266
267impl GpuDriver for GliumGpuDriverSender {
268 fn begin_synchronize(&mut self) {
269 }
271
272 fn end_synchronize(&mut self) {
273 }
275
276 fn next_texture_id(&mut self) -> u32 {
277 self.next_texture_id += 1;
278 self.next_texture_id
279 }
280
281 fn create_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap) {
282 self.sender
283 .send(GliumGpuCommand::CreateTexture(texture_id, bitmap))
284 .unwrap();
285 }
286
287 fn update_texture(&mut self, texture_id: u32, bitmap: OwnedBitmap) {
288 self.sender
289 .send(GliumGpuCommand::UpdateTexture(texture_id, bitmap))
290 .unwrap();
291 }
292
293 fn destroy_texture(&mut self, texture_id: u32) {
294 self.sender
295 .send(GliumGpuCommand::DestroyTexture(texture_id))
296 .unwrap();
297 }
298
299 fn next_render_buffer_id(&mut self) -> u32 {
300 self.next_render_buffer_id += 1;
301 self.next_render_buffer_id
302 }
303
304 fn create_render_buffer(&mut self, render_buffer_id: u32, render_buffer: RenderBuffer) {
305 self.sender
306 .send(GliumGpuCommand::CreateRenderBuffer(
307 render_buffer_id,
308 render_buffer,
309 ))
310 .unwrap();
311 }
312
313 fn destroy_render_buffer(&mut self, render_buffer_id: u32) {
314 self.sender
315 .send(GliumGpuCommand::DestroyRenderBuffer(render_buffer_id))
316 .unwrap();
317 }
318
319 fn next_geometry_id(&mut self) -> u32 {
320 self.next_geometry_id += 1;
321 self.next_geometry_id
322 }
323
324 fn create_geometry(
325 &mut self,
326 geometry_id: u32,
327 vertex_buffer: VertexBuffer,
328 index_buffer: IndexBuffer,
329 ) {
330 let glium_vertex_buffer = match vertex_buffer.format {
331 VertexBufferFormat::Format_2f_4ub_2f => {
332 let (head, body, tail) = unsafe {
336 vertex_buffer
337 .buffer
338 .as_slice()
339 .align_to::<ul_sys::ULVertex_2f_4ub_2f>()
340 };
341 assert!(head.is_empty());
342 assert!(tail.is_empty());
343
344 GliumDriverVertexBuffer::Format2f4ub2f(body.to_vec())
345 }
346 VertexBufferFormat::Format_2f_4ub_2f_2f_28f => {
347 let (head, body, tail) = unsafe {
351 vertex_buffer
352 .buffer
353 .as_slice()
354 .align_to::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>()
355 };
356 assert!(head.is_empty());
357 assert!(tail.is_empty());
358
359 GliumDriverVertexBuffer::Format2f4ub2f2f28f(body.to_vec())
360 }
361 };
362
363 self.sender
364 .send(GliumGpuCommand::CreateGeometry(
365 geometry_id,
366 glium_vertex_buffer,
367 index_buffer,
368 ))
369 .unwrap();
370 }
371
372 fn update_geometry(
373 &mut self,
374 geometry_id: u32,
375 vertex_buffer: VertexBuffer,
376 index_buffer: IndexBuffer,
377 ) {
378 let glium_vertex_buffer = match vertex_buffer.format {
379 VertexBufferFormat::Format_2f_4ub_2f => {
380 let (head, body, tail) = unsafe {
384 vertex_buffer
385 .buffer
386 .as_slice()
387 .align_to::<ul_sys::ULVertex_2f_4ub_2f>()
388 };
389 assert!(head.is_empty());
390 assert!(tail.is_empty());
391
392 GliumDriverVertexBuffer::Format2f4ub2f(body.to_vec())
393 }
394 VertexBufferFormat::Format_2f_4ub_2f_2f_28f => {
395 let (head, body, tail) = unsafe {
399 vertex_buffer
400 .buffer
401 .as_slice()
402 .align_to::<ul_sys::ULVertex_2f_4ub_2f_2f_28f>()
403 };
404 assert!(head.is_empty());
405 assert!(tail.is_empty());
406
407 GliumDriverVertexBuffer::Format2f4ub2f2f28f(body.to_vec())
408 }
409 };
410
411 self.sender
412 .send(GliumGpuCommand::UpdateGeometry(
413 geometry_id,
414 glium_vertex_buffer,
415 index_buffer,
416 ))
417 .unwrap();
418 }
419
420 fn destroy_geometry(&mut self, geometry_id: u32) {
421 self.sender
422 .send(GliumGpuCommand::DestroyGeometry(geometry_id))
423 .unwrap();
424 }
425
426 fn update_command_list(&mut self, command_list: Vec<GpuCommand>) {
427 self.sender
428 .send(GliumGpuCommand::UpdateCommandList(command_list))
429 .unwrap();
430 }
431}
432
433struct GluimContextWrapper {
434 context: Rc<Context>,
435}
436
437impl Facade for GluimContextWrapper {
438 fn get_context(&self) -> &Rc<Context> {
439 &self.context
440 }
441}
442
443pub struct GliumGpuDriverReceiver {
452 receiver: mpsc::Receiver<GliumGpuCommand>,
454 context: GluimContextWrapper,
456
457 empty_texture: EitherTexture,
460 texture_map: HashMap<u32, (EitherTexture, Option<u32>)>,
462 render_buffer_map: HashMap<u32, RenderBuffer>,
465 geometry_map: HashMap<u32, (VertexBufferAny, glium::IndexBuffer<u32>)>,
467
468 path_program: Program,
470 fill_program: Program,
472}
473
474impl GliumGpuDriverReceiver {
475 fn new(
476 receiver: mpsc::Receiver<GliumGpuCommand>,
477 context: &Rc<Context>,
478 ) -> Result<Self, GliumGpuDriverError> {
479 let context = GluimContextWrapper {
480 context: context.clone(),
481 };
482 let empty_texture = EitherTexture::Regular2d(Texture2d::empty(&context, 1, 1)?);
483
484 let texture_map = HashMap::new();
485 let render_buffer_map = HashMap::new();
486 let geometry_map = HashMap::new();
487
488 let path_program = program!(&context,
489 150 => {
490 vertex: include_str!("./shaders/v2f_c4f_t2f_vert.glsl"),
491 fragment: include_str!("./shaders/path_frag.glsl")
492 })?;
493 let fill_program = program!(&context,
494 150 => {
495 vertex: include_str!("./shaders/v2f_c4f_t2f_t2f_d28f_vert.glsl"),
496 fragment: include_str!("./shaders/fill_frag.glsl")
497 })?;
498
499 Ok(GliumGpuDriverReceiver {
500 receiver,
501 context,
502 empty_texture,
503 texture_map,
504 render_buffer_map,
505 geometry_map,
506
507 path_program,
508 fill_program,
509 })
510 }
511
512 fn create_texture(&self, bitmap: &OwnedBitmap) -> Result<EitherTexture, GliumGpuDriverError> {
514 if bitmap.is_empty() {
515 Texture2d::empty(&self.context, bitmap.width(), bitmap.height())
516 .map_err(|e| e.into())
517 .map(EitherTexture::Regular2d)
518 } else {
519 let bitmap_pixels = bitmap.pixels().unwrap();
521
522 match bitmap.format() {
523 BitmapFormat::A8Unorm => {
524 let img = RawImage2d {
525 data: Cow::Borrowed(bitmap_pixels),
526 width: bitmap.width(),
527 height: bitmap.height(),
528 format: ClientFormat::U8,
529 };
530
531 Texture2d::with_format(
532 &self.context,
533 img,
534 UncompressedFloatFormat::U8,
535 MipmapsOption::NoMipmap,
536 )
537 .map_err(|e| e.into())
538 .map(EitherTexture::Regular2d)
539 }
540 BitmapFormat::Bgra8UnormSrgb => {
541 let expected_row_bytes = bitmap.width() * 4;
547 let data = if bitmap.row_bytes() != expected_row_bytes {
548 let mut new_data = Vec::with_capacity(
549 bitmap.height() as usize * expected_row_bytes as usize,
550 );
551 for row in bitmap_pixels.chunks(bitmap.row_bytes() as usize) {
552 new_data.extend_from_slice(&row[..expected_row_bytes as usize]);
553 }
554 Cow::Owned(new_data)
555 } else {
556 Cow::Borrowed(bitmap_pixels)
557 };
558
559 let img = RawImage2d {
560 data,
561 width: bitmap.width(),
562 height: bitmap.height(),
563 format: ClientFormat::U8U8U8U8,
564 };
565
566 SrgbTexture2d::with_format(
567 &self.context,
568 img,
569 glium::texture::SrgbFormat::U8U8U8U8,
570 MipmapsOption::NoMipmap,
571 )
572 .map_err(|e| e.into())
573 .map(EitherTexture::Srgb2d)
574 }
575 }
576 }
577 }
578}
579
580impl GliumGpuDriverReceiver {
581 pub fn get_texture(&self, id: &u32) -> Option<&EitherTexture> {
590 self.texture_map.get(id).map(|(t, _)| t)
591 }
592
593 pub fn render(&mut self) -> Result<(), GliumGpuDriverError> {
600 while let Ok(cmd) = self.receiver.try_recv() {
601 match cmd {
602 GliumGpuCommand::CreateTexture(id, bitmap) => {
603 let t = self.create_texture(&bitmap)?;
604 self.texture_map.insert(id, (t, None));
605 }
606 GliumGpuCommand::UpdateTexture(id, bitmap) => {
607 assert!(self.texture_map.contains_key(&id));
608
609 let t = self.create_texture(&bitmap)?;
610
611 let entry = self.texture_map.get_mut(&id).unwrap();
612 entry.0 = t;
613 }
614 GliumGpuCommand::DestroyTexture(id) => {
615 assert!(self.texture_map.contains_key(&id));
616 self.texture_map.remove(&id);
617 }
618 GliumGpuCommand::CreateRenderBuffer(id, render_buffer) => {
619 let entry = self.texture_map.get_mut(&render_buffer.texture_id).unwrap();
620 entry.1 = Some(id);
621 assert!(entry.0.width() == render_buffer.width);
623 assert!(entry.0.height() == render_buffer.height);
624
625 self.render_buffer_map.insert(id, render_buffer);
626 }
627 GliumGpuCommand::DestroyRenderBuffer(id) => {
628 assert!(self.render_buffer_map.contains_key(&id));
629 let render_buffer = self.render_buffer_map.remove(&id).unwrap();
630 if let Some(entry) = self.texture_map.get_mut(&render_buffer.texture_id) {
631 entry.1 = None;
632 }
633 }
634 GliumGpuCommand::CreateGeometry(id, vert, index) => {
635 let index_buffer = glium::IndexBuffer::new(
636 &self.context,
637 glium::index::PrimitiveType::TrianglesList,
638 &index.buffer,
639 )?;
640
641 self.geometry_map.insert(
642 id,
643 (vert.into_glium_vertex_buffer(&self.context)?, index_buffer),
644 );
645 }
646 GliumGpuCommand::UpdateGeometry(id, vert, index) => {
647 assert!(self.geometry_map.contains_key(&id));
648
649 let index_buffer = glium::IndexBuffer::new(
650 &self.context,
651 glium::index::PrimitiveType::TrianglesList,
652 &index.buffer,
653 )?;
654
655 *self.geometry_map.get_mut(&id).unwrap() =
656 (vert.into_glium_vertex_buffer(&self.context)?, index_buffer);
657 }
658 GliumGpuCommand::DestroyGeometry(id) => {
659 assert!(self.geometry_map.contains_key(&id));
660 self.geometry_map.remove(&id);
661 }
662 GliumGpuCommand::UpdateCommandList(cmd_list) => {
663 for cmd in cmd_list {
664 match cmd {
665 GpuCommand::ClearRenderBuffer {
666 render_buffer_id: id,
667 } => {
668 assert!(self.render_buffer_map.contains_key(&id));
669 let render_buffer = self.render_buffer_map.get(&id).unwrap();
670
671 assert!(!render_buffer.has_stencil_buffer);
673 assert!(!render_buffer.has_depth_buffer);
674
675 let t = self.texture_map.get(&render_buffer.texture_id).unwrap();
676
677 let mut frame_buffer = SimpleFrameBuffer::new(&self.context, &t.0)?;
678
679 frame_buffer.clear(
680 None,
681 Some((0.0, 0.0, 0.0, 0.0)),
682 false,
683 None,
684 None,
685 );
686 }
687 GpuCommand::DrawGeometry {
688 gpu_state,
689 geometry_id,
690 indices_count,
691 indices_offset,
692 } => {
693 assert!(self.geometry_map.contains_key(&geometry_id));
694 let (vertex_buffer, index_buffer) =
695 self.geometry_map.get(&geometry_id).unwrap();
696
697 assert!(self
698 .render_buffer_map
699 .contains_key(&gpu_state.render_buffer_id));
700 let render_buffer = self
701 .render_buffer_map
702 .get(&gpu_state.render_buffer_id)
703 .unwrap();
704
705 assert!(!render_buffer.has_stencil_buffer);
707 assert!(!render_buffer.has_depth_buffer);
708
709 let index_buffer_slice = index_buffer
710 .slice(
711 indices_offset as usize
712 ..(indices_offset as usize + indices_count as usize),
713 )
714 .ok_or(GliumGpuDriverError::DrawIndexOutOfRange {
715 index_buffer_size: index_buffer.len(),
716 draw_index_offset: indices_offset,
717 draw_index_size: indices_count,
718 })?;
719
720 let (t, _) =
721 self.texture_map.get(&render_buffer.texture_id).unwrap();
722
723 let mut frame_buffer = SimpleFrameBuffer::new(&self.context, t)?;
724
725 let used_program = match gpu_state.shader_type {
726 ShaderType::Fill => &self.fill_program,
727 ShaderType::FillPath => &self.path_program,
728 };
729
730 let scalar_data =
731 UniformBuffer::new(&self.context, gpu_state.uniform_scalar)?;
732 let vector_data =
733 UniformBuffer::new(&self.context, gpu_state.uniform_vector)?;
734 let clip_data = UniformBuffer::new(&self.context, gpu_state.clip)?;
735
736 let orth_projection_matrix = [
739 [2.0 / gpu_state.viewport_width as f32, 0.0, 0.0, 0.0],
740 [0.0, 2.0 / gpu_state.viewport_height as f32, 0.0, 0.0],
741 [0.0, 0.0, -0.000002, 0.0],
742 [-1.0, -1.0, 0.818183, 1.0],
743 ];
744 let mut transformation = [
746 [0., 0., 0., 0.],
747 [0., 0., 0., 0.],
748 [0., 0., 0., 0.],
749 [0., 0., 0., 0.],
750 ];
751
752 #[allow(clippy::needless_range_loop)]
754 for i in 0..4 {
755 for j in 0..4 {
756 for k in 0..4 {
757 transformation[i][j] += gpu_state.transform[i * 4 + k]
758 * orth_projection_matrix[k][j];
759 }
760 }
761 }
762
763 let texture1 = if let Some(id) = gpu_state.texture_1_id {
766 let (t, _) = self.texture_map.get(&id).unwrap();
767 t
768 } else {
769 &self.empty_texture
770 };
771 let texture2 = if let Some(id) = gpu_state.texture_2_id {
772 let (t, _) = self.texture_map.get(&id).unwrap();
773 t
774 } else {
775 &self.empty_texture
776 };
777 let texture3 = if let Some(id) = gpu_state.texture_3_id {
778 let (t, _) = self.texture_map.get(&id).unwrap();
779 t
780 } else {
781 &self.empty_texture
782 };
783
784 let uniforms = uniform! {
785 State: [0.0, gpu_state.viewport_width as f32, gpu_state.viewport_height as f32, 1.0],
787 Transform: transformation,
788 Scalar: &scalar_data,
789 Vector: &vector_data,
790 ClipSize: gpu_state.clip_size,
791 Clip: &clip_data,
792 Texture1: texture1.sampled(),
793 Texture2: texture2.sampled(),
794 Texture3: texture3.sampled(),
795 };
796
797 let params = DrawParameters {
798 viewport: Some(glium::Rect {
799 left: 0,
800 bottom: 0,
801 width: gpu_state.viewport_width,
802 height: gpu_state.viewport_height,
803 }),
804 scissor: if gpu_state.enable_scissor {
805 Some(glium::Rect {
806 left: gpu_state.scissor_rect.left as u32,
807 bottom: gpu_state.scissor_rect.top as u32,
808 width: (gpu_state.scissor_rect.right
809 - gpu_state.scissor_rect.left)
810 as u32,
811 height: (gpu_state.scissor_rect.bottom
812 - gpu_state.scissor_rect.top)
813 as u32,
814 })
815 } else {
816 None
817 },
818 blend: if gpu_state.enable_blend {
819 Blend::alpha_blending()
820 } else {
821 Blend::default()
822 },
823 ..DrawParameters::default()
824 };
825
826 frame_buffer.draw(
827 vertex_buffer,
828 index_buffer_slice,
829 used_program,
830 &uniforms,
831 ¶ms,
832 )?;
833 }
834 }
835 }
836 }
837 }
838 }
839
840 Ok(())
841 }
842}