hui_glium/
lib.rs

1use std::rc::Rc;
2use glam::Vec2;
3use glium::{
4  backend::{Context, Facade}, implement_vertex, index::PrimitiveType, texture::{RawImage2d, Texture2d}, uniform, uniforms::{MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction}, Api, Blend, DrawParameters, IndexBuffer, Program, Surface, VertexBuffer
5};
6use hui::{
7  draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
8};
9
10const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert");
11const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag");
12
13const VERTEX_SHADER_150: &str = include_str!("../shaders/vertex.150.vert");
14const FRAGMENT_SHADER_150: &str = include_str!("../shaders/fragment.150.frag");
15
16#[derive(Clone, Copy)]
17#[repr(C)]
18struct Vertex {
19  position: [f32; 2],
20  color: [f32; 4],
21  uv: [f32; 2],
22}
23
24impl From<UiVertex> for Vertex {
25  fn from(v: UiVertex) -> Self {
26    Self {
27      position: v.position.to_array(),
28      color: v.color.to_array(),
29      uv: v.uv.to_array(),
30    }
31  }
32}
33
34implement_vertex!(Vertex, position, color, uv);
35
36struct BufferPair {
37  pub vertex_buffer: glium::VertexBuffer<Vertex>,
38  pub index_buffer: glium::IndexBuffer<u32>,
39  pub vertex_count: usize,
40  pub index_count: usize,
41}
42
43impl BufferPair {
44  pub fn new<F: Facade>(facade: &F) -> Self {
45    log::debug!("init ui buffers (empty)...");
46    Self {
47      vertex_buffer: VertexBuffer::empty_dynamic(facade, 1024).unwrap(),
48      index_buffer: IndexBuffer::empty_dynamic(facade, PrimitiveType::TrianglesList, 1024).unwrap(),
49      vertex_count: 0,
50      index_count: 0,
51    }
52  }
53
54  pub fn new_with_data<F: Facade>(facade: &F, vtx: &[Vertex], idx: &[u32]) -> Self {
55    log::debug!("init ui buffers (data)...");
56    Self {
57      vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(),
58      index_buffer: IndexBuffer::dynamic(facade, PrimitiveType::TrianglesList, idx).unwrap(),
59      vertex_count: vtx.len(),
60      index_count: idx.len(),
61    }
62  }
63
64  pub fn ensure_buffer_size(&mut self, need_vtx: usize, need_idx: usize) {
65    let current_vtx_size = self.vertex_buffer.get_size() / std::mem::size_of::<Vertex>();
66    let current_idx_size = self.index_buffer.get_size() / std::mem::size_of::<u32>();
67    //log::debug!("current vtx size: {}, current idx size: {}", current_vtx_size, current_idx_size);
68    if current_vtx_size >= need_vtx && current_idx_size >= need_idx {
69      return
70    }
71    let new_vtx_size = (need_vtx + 1).next_power_of_two();
72    let new_idx_size = (need_idx + 1).next_power_of_two();
73    log::debug!("resizing buffers: vtx {} -> {}, idx {} -> {}", current_vtx_size, new_vtx_size, current_idx_size, new_idx_size);
74    if current_vtx_size != new_vtx_size {
75      self.vertex_buffer = VertexBuffer::empty_dynamic(
76        self.vertex_buffer.get_context(),
77        new_vtx_size
78      ).unwrap();
79    }
80    if current_idx_size != new_idx_size {
81      self.index_buffer = IndexBuffer::empty_dynamic(
82        self.index_buffer.get_context(),
83        PrimitiveType::TrianglesList,
84        new_idx_size
85      ).unwrap();
86    }
87  }
88
89  pub fn write_data(&mut self, vtx: &[Vertex], idx: &[u32]) {
90    //log::trace!("uploading {} vertices and {} indices", vtx.len(), idx.len());
91
92    self.vertex_count = vtx.len();
93    self.index_count = idx.len();
94
95    if self.vertex_count == 0 || self.index_count == 0 {
96      self.vertex_buffer.invalidate();
97      self.index_buffer.invalidate();
98      return
99    }
100
101    self.ensure_buffer_size(self.vertex_count, self.index_count);
102
103    self.vertex_buffer.slice_mut(0..self.vertex_count).unwrap().write(vtx);
104    self.index_buffer.slice_mut(0..self.index_count).unwrap().write(idx);
105  }
106
107  pub fn is_empty(&self) -> bool {
108    self.vertex_count == 0 || self.index_count == 0
109  }
110}
111
112pub struct GliumUiRenderer {
113  context: Rc<Context>,
114  program: glium::Program,
115  ui_texture: Option<Texture2d>,
116  buffer_pair: Option<BufferPair>,
117}
118
119impl GliumUiRenderer {
120  pub fn new<F: Facade>(facade: &F) -> Self {
121    log::info!("initializing hui-glium");
122    Self {
123      program: match facade.get_context().get_supported_glsl_version().0 {
124        Api::Gl => Program::from_source(facade, VERTEX_SHADER_150, FRAGMENT_SHADER_150, None).unwrap(),
125        Api::GlEs => Program::from_source(facade, VERTEX_SHADER_GLES3, FRAGMENT_SHADER_GLES3, None).unwrap(),
126      },
127      context: Rc::clone(facade.get_context()),
128      ui_texture: None,
129      buffer_pair: None,
130    }
131  }
132
133  fn update_buffers(&mut self, call: &UiDrawCall) {
134    log::trace!("updating ui buffers (tris: {})", call.indices.len() / 3);
135    let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..];
136    let data_idx = &call.indices[..];
137    if let Some(buffer) = &mut self.buffer_pair {
138      buffer.write_data(data_vtx, data_idx);
139    } else if !call.indices.is_empty() {
140      self.buffer_pair = Some(BufferPair::new_with_data(&self.context, data_vtx, data_idx));
141    }
142  }
143
144  fn update_texture_atlas(&mut self, atlas: &TextureAtlasMeta) {
145    log::trace!("updating ui atlas texture");
146    self.ui_texture = Some(Texture2d::new(
147      &self.context,
148      RawImage2d::from_raw_rgba(
149        atlas.data.to_owned(),
150        (atlas.size.x, atlas.size.y)
151      )
152    ).unwrap());
153  }
154
155  pub fn update(&mut self, instance: &UiInstance) {
156    if self.ui_texture.is_none() || instance.atlas().modified {
157      self.update_texture_atlas(&instance.atlas());
158    }
159    if self.buffer_pair.is_none() || instance.draw_call().0 {
160      self.update_buffers(instance.draw_call().1);
161    }
162  }
163
164  pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
165    let params = DrawParameters {
166      blend: Blend::alpha_blending(),
167      ..Default::default()
168    };
169
170    if let Some(buffer) = &self.buffer_pair {
171      if buffer.is_empty() {
172        return
173      }
174
175      let vtx_buffer = buffer.vertex_buffer.slice(0..buffer.vertex_count).unwrap();
176      let idx_buffer = buffer.index_buffer.slice(0..buffer.index_count).unwrap();
177
178      frame.draw(
179        vtx_buffer,
180        idx_buffer,
181        &self.program,
182        &uniform! {
183          resolution: resolution.to_array(),
184          tex: Sampler(self.ui_texture.as_ref().unwrap(), SamplerBehavior {
185            max_anisotropy: 1,
186            magnify_filter: MagnifySamplerFilter::Nearest,
187            minify_filter: MinifySamplerFilter::Linear,
188            wrap_function: (SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp),
189            ..Default::default()
190          }),
191        },
192        &params,
193      ).unwrap();
194    }
195  }
196}