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 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 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 ¶ms,
193 ).unwrap();
194 }
195 }
196}