1use imgui::Context;
4use std::mem;
5
6use gl::*;
7use gl::types::*;
8
9pub struct ImguiRenderer {
10 program: GLuint,
11 locs: Locs,
12 vbo: GLuint,
13 ebo: GLuint,
14 font_texture: GLuint,
15}
16
17struct Locs {
18 texture: GLint,
19 proj_mtx: GLint,
20 position: GLuint,
21 uv: GLuint,
22 color: GLuint,
23}
24
25impl ImguiRenderer {
26 pub fn new<F>(
27 imgui: &mut Context,
28 load_fn: F,
29 ) -> Self
30 where
31 F: FnMut(&'static str) -> *const ::std::os::raw::c_void
32 {
33 let _gl = gl::load_with(load_fn);
34
35 unsafe {
36 #[cfg(target_os = "macos")]
37 let glsl_version = b"#version 150\n\0";
38 #[cfg(not(target_os = "macos"))]
39 let glsl_version = b"#version 130\n\0";
40
41 let vert_source = b"
42 uniform mat4 ProjMtx;
43 in vec2 Position;
44 in vec2 UV;
45 in vec4 Color;
46 out vec2 Frag_UV;
47 out vec4 Frag_Color;
48 void main()
49 {
50 Frag_UV = UV;
51 Frag_Color = Color;
52 gl_Position = ProjMtx * vec4(Position.xy,0,1);
53 }
54 \0";
55
56 let frag_source = b"
57 uniform sampler2D Texture;
58 in vec2 Frag_UV;
59 in vec4 Frag_Color;
60 out vec4 Out_Color;
61 void main()
62 {
63 Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
64 }
65 \0";
66
67 let vert_sources = [glsl_version.as_ptr() as *const GLchar,
68 vert_source.as_ptr() as *const GLchar];
69 let vert_sources_len = [glsl_version.len() as GLint - 1,
70 vert_source.len() as GLint - 1];
71 let frag_sources = [glsl_version.as_ptr() as *const GLchar,
72 frag_source.as_ptr() as *const GLchar];
73 let frag_sources_len = [glsl_version.len() as GLint - 1,
74 frag_source.len() as GLint - 1];
75
76 let program = CreateProgram();
77 let vert_shader = CreateShader(gl::VERTEX_SHADER);
78 let frag_shader = CreateShader(gl::FRAGMENT_SHADER);
79 ShaderSource(vert_shader, 2, vert_sources.as_ptr(), vert_sources_len.as_ptr());
80 ShaderSource(frag_shader, 2, frag_sources.as_ptr(), frag_sources_len.as_ptr());
81 CompileShader(vert_shader);
82 CompileShader(frag_shader);
83 AttachShader(program, vert_shader);
84 AttachShader(program, frag_shader);
85 LinkProgram(program);
86 DeleteShader(vert_shader);
87 DeleteShader(frag_shader);
88
89 let locs = Locs{
90 texture: GetUniformLocation(program, b"Texture\0".as_ptr() as _),
91 proj_mtx: GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _),
92 position: GetAttribLocation(program, b"Position\0".as_ptr() as _) as _,
93 uv: GetAttribLocation(program, b"UV\0".as_ptr() as _) as _,
94 color: GetAttribLocation(program, b"Color\0".as_ptr() as _) as _,
95 };
96
97 let vbo = return_param(|x| GenBuffers(1, x) );
98 let ebo = return_param(|x| GenBuffers(1, x) );
99
100 let mut current_texture = 0;
101 GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture);
102
103
104 let font_texture = return_param(|x| GenTextures(1, x));
105 BindTexture(gl::TEXTURE_2D, font_texture);
106 TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
107 TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
108 PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
109
110 {
111 let atlas = imgui.fonts();
112
113 let texture = atlas.build_rgba32_texture();
114 TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as _, texture.width as _, texture.height as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, texture.data.as_ptr() as _);
115
116 atlas.tex_id = (font_texture as usize).into();
117 }
118
119 BindTexture(gl::TEXTURE_2D, current_texture as _);
120
121
122 Self{
123 program,
124 locs,
125 vbo,
126 ebo,
127 font_texture,
128 }
129 }
130 }
131
132 pub fn render(
133 &self,
134 ctx: &mut Context,
135 ) {
136 use imgui::{DrawVert,DrawIdx,DrawCmd,DrawCmdParams};
137
138 unsafe {
139 let last_active_texture = return_param(|x| GetIntegerv(gl::ACTIVE_TEXTURE, x));
140 ActiveTexture(gl::TEXTURE0);
141 let last_program = return_param(|x| GetIntegerv(gl::CURRENT_PROGRAM, x));
142 let last_texture = return_param(|x| GetIntegerv(gl::TEXTURE_BINDING_2D, x));
143 let last_sampler = if BindSampler::is_loaded() { return_param(|x| GetIntegerv(gl::SAMPLER_BINDING, x)) } else { 0 };
144 let last_array_buffer = return_param(|x| GetIntegerv(gl::ARRAY_BUFFER_BINDING, x));
145 let last_element_array_buffer = return_param(|x| GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x));
146 let last_vertex_array = return_param(|x| GetIntegerv(gl::VERTEX_ARRAY_BINDING, x));
147 let last_polygon_mode = return_param(|x: &mut [GLint; 2]| GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr()));
148 let last_viewport = return_param(|x: &mut [GLint; 4]| GetIntegerv(gl::VIEWPORT, x.as_mut_ptr()));
149 let last_scissor_box = return_param(|x: &mut [GLint; 4]| GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr()));
150 let last_blend_src_rgb = return_param(|x| GetIntegerv(gl::BLEND_SRC_RGB, x));
151 let last_blend_dst_rgb = return_param(|x| GetIntegerv(gl::BLEND_DST_RGB, x));
152 let last_blend_src_alpha = return_param(|x| GetIntegerv(gl::BLEND_SRC_ALPHA, x));
153 let last_blend_dst_alpha = return_param(|x| GetIntegerv(gl::BLEND_DST_ALPHA, x));
154 let last_blend_equation_rgb = return_param(|x| GetIntegerv(gl::BLEND_EQUATION_RGB, x));
155 let last_blend_equation_alpha = return_param(|x| GetIntegerv(gl::BLEND_EQUATION_ALPHA, x));
156 let last_enable_blend = IsEnabled(gl::BLEND) == gl::TRUE;
157 let last_enable_cull_face = IsEnabled(gl::CULL_FACE) == gl::TRUE;
158 let last_enable_depth_test = IsEnabled(gl::DEPTH_TEST) == gl::TRUE;
159 let last_enable_scissor_test = IsEnabled(gl::SCISSOR_TEST) == gl::TRUE;
160
161 Enable(gl::BLEND);
162 BlendEquation(gl::FUNC_ADD);
163 BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
164 Disable(gl::CULL_FACE);
165 Disable(gl::DEPTH_TEST);
166 Enable(gl::SCISSOR_TEST);
167 PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
168
169 let [width, height] = ctx.io().display_size;
170 let [scale_w, scale_h] = ctx.io().display_framebuffer_scale;
171
172 let fb_width = width * scale_w;
173 let fb_height = height * scale_h;
174
175 Viewport(0, 0, fb_width as _, fb_height as _);
176 let matrix = [
177 [ 2.0 / width as f32, 0.0, 0.0, 0.0],
178 [ 0.0, 2.0 / -(height as f32), 0.0, 0.0],
179 [ 0.0, 0.0, -1.0, 0.0],
180 [-1.0, 1.0, 0.0, 1.0],
181 ];
182 UseProgram(self.program);
183 Uniform1i(self.locs.texture, 0);
184 UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
185 if BindSampler::is_loaded() { BindSampler(0, 0); }
186
187
188 let vao = return_param(|x| GenVertexArrays(1, x));
189 BindVertexArray(vao);
190 BindBuffer(gl::ARRAY_BUFFER, self.vbo);
191 EnableVertexAttribArray(self.locs.position);
192 EnableVertexAttribArray(self.locs.uv);
193 EnableVertexAttribArray(self.locs.color);
194 VertexAttribPointer(self.locs.position, 2, gl::FLOAT, gl::FALSE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.pos) as _);
195 VertexAttribPointer(self.locs.uv, 2, gl::FLOAT, gl::FALSE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.uv) as _);
196 VertexAttribPointer(self.locs.color, 4, gl::UNSIGNED_BYTE, gl::TRUE, mem::size_of::<DrawVert>() as _, field_offset::<DrawVert, _, _>(|v| &v.col) as _);
197
198
199 let draw_data = ctx.render();
200
201 for draw_list in draw_data.draw_lists() {
202 let vtx_buffer = draw_list.vtx_buffer();
203 let idx_buffer = draw_list.idx_buffer();
204
205 BindBuffer(gl::ARRAY_BUFFER, self.vbo);
206 BufferData(gl::ARRAY_BUFFER, (vtx_buffer.len() * mem::size_of::<DrawVert>()) as _, vtx_buffer.as_ptr() as _, gl::STREAM_DRAW);
207
208 BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
209 BufferData(gl::ELEMENT_ARRAY_BUFFER, (idx_buffer.len() * mem::size_of::<DrawIdx>()) as _, idx_buffer.as_ptr() as _, gl::STREAM_DRAW);
210
211 for cmd in draw_list.commands() {
212 match cmd {
213 DrawCmd::Elements {
214 count,
215 cmd_params: DrawCmdParams {
216 clip_rect: [x, y, z, w],
217 texture_id,
218 idx_offset,
219 ..
220 },
221 } => {
222 BindTexture(gl::TEXTURE_2D, texture_id.id() as _);
223
224 Scissor((x * scale_w) as GLint,
225 (fb_height - w * scale_h) as GLint,
226 ((z - x) * scale_w) as GLint,
227 ((w - y) * scale_h) as GLint);
228
229 let idx_size = if mem::size_of::<DrawIdx>() == 2 { gl::UNSIGNED_SHORT } else { gl::UNSIGNED_INT };
230
231 DrawElements(gl::TRIANGLES, count as _, idx_size, (idx_offset * mem::size_of::<DrawIdx>()) as _);
232 },
233 DrawCmd::ResetRenderState => {
234 unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet");
235 },
236 DrawCmd::RawCallback { .. } => {
237 unimplemented!("Haven't implemented user callbacks yet");
238 }
239 }
240 }
241 }
242
243 DeleteVertexArrays(1, &vao);
244
245 UseProgram(last_program as _);
246 BindTexture(gl::TEXTURE_2D, last_texture as _);
247 if BindSampler::is_loaded() { BindSampler(0, last_sampler as _); }
248 ActiveTexture(last_active_texture as _);
249 BindVertexArray(last_vertex_array as _);
250 BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _);
251 BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _);
252 BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _);
253 BlendFuncSeparate(last_blend_src_rgb as _, last_blend_dst_rgb as _, last_blend_src_alpha as _, last_blend_dst_alpha as _);
254 if last_enable_blend { Enable(gl::BLEND) } else { Disable(gl::BLEND) };
255 if last_enable_cull_face { Enable(gl::CULL_FACE) } else { Disable(gl::CULL_FACE) };
256 if last_enable_depth_test { Enable(gl::DEPTH_TEST) } else { Disable(gl::DEPTH_TEST) };
257 if last_enable_scissor_test { Enable(gl::SCISSOR_TEST) } else { Disable(gl::SCISSOR_TEST) };
258 PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _);
259 Viewport(last_viewport[0] as _, last_viewport[1] as _, last_viewport[2] as _, last_viewport[3] as _);
260 Scissor(last_scissor_box[0] as _, last_scissor_box[1] as _, last_scissor_box[2] as _, last_scissor_box[3] as _);
261 }
262 }
263}
264
265impl Drop for ImguiRenderer {
266 fn drop(&mut self) {
267 unsafe {
268 DeleteBuffers(1, &self.vbo);
269 DeleteBuffers(1, &self.ebo);
270
271 DeleteProgram(self.program);
272
273 DeleteTextures(1, &self.font_texture);
274 }
275 }
276}
277
278fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
279 unsafe {
280 let instance = mem::zeroed::<T>();
281
282 let offset = {
283 let field: &U = f(&instance);
284 field as *const U as usize - &instance as *const T as usize
285 };
286
287 mem::forget(instance);
288
289 offset
290 }
291}
292
293fn return_param<T, F>(f: F) -> T where F: FnOnce(&mut T) {
294 let mut val = unsafe{ mem::zeroed() };
295 f(&mut val);
296 val
297}