keeshond/renderer/
opengl.rs

1//! OpenGL 3 rendering backend
2
3use std::io::Cursor;
4use std::rc::Rc;
5use std::cell::RefCell;
6
7use glium::{Frame, Surface, Program, ProgramCreationError, IndexBuffer, VertexBuffer};
8use glium::texture::{RawImage2d, Texture2d, UncompressedFloatFormat};
9use glium::uniforms::{Sampler, MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
10use glium::program::ProgramCreationInput;
11use glium::backend::Facade;
12use crate::thirdparty::glium_sdl2::{DisplayBuild, SDL2Facade};
13
14use crate::datapack::{DataId, DataStore, PreparedStore, PreparedStoreError, DataPreparer};
15
16use crate::renderer::{DrawControl, DrawControlResult, DrawTransform, DrawSpriteRawInfo, DrawBackgroundColorInfo, RendererError, Sheet, Shader, ViewportMode, DrawSpriteInfo, Color};
17use crate::renderer::draw_control_private::{DrawControlBackend, RendererNewInfo};
18
19#[cfg(feature = "imgui_feature")] use imgui_glium_renderer;
20#[cfg(feature = "imgui_feature")] use glium::uniforms::SamplerBehavior;
21
22const INSTANCES_MAX : usize = 2048; // How many instances to store in each instance buffer.
23const INSTANCE_BUFFER_MAX: usize = 4; // How many instance buffers to have.
24const INSTANCE_RUN_COARSENESS : usize = 128; // The "resolution" of start index for new batches within a given instance buffer.
25                                             // Use to tune performance of batch breaks vs VAO memory bloat
26
27const UNBORKED_IDENTITY_TRANSFORM : [f32; 9] = [2.0, 0.0, 0.0, 0.0, -2.0, 0.0, -1.0, 1.0, 1.0];
28const UNBORKED_UPSCALE_TRANSFORM : [f32; 9] = [2.0, 0.0, 0.0, 0.0, 2.0, 0.0, -1.0, -1.0, 1.0];
29
30#[derive(Copy, Clone, Eq, PartialEq)]
31enum RendererOp
32{
33    #[allow(dead_code)]
34    BackgroundColor,
35    #[allow(dead_code)]
36    Sprite
37}
38
39impl Default for RendererOp
40{
41    fn default() -> Self
42    {
43        RendererOp::BackgroundColor
44    }
45}
46
47#[derive(Copy, Clone)]
48struct Vertex
49{
50    a_position : [f32; 2]
51}
52
53implement_vertex!(Vertex, a_position);
54
55#[derive(Copy, Clone, Default)]
56struct Instance
57{
58    a_transform_col1 : [f32; 2],
59    a_transform_col2 : [f32; 2],
60    a_transform_col3 : [f32; 2],
61    a_transform_offset : [f32; 2],
62    a_tex_coords : [f32; 4],
63    a_rgba : [f32; 4]
64}
65
66implement_vertex!(Instance, a_transform_col1, a_transform_col2, a_transform_col3,
67    a_transform_offset, a_tex_coords, a_rgba);
68
69const VERTEX_QUAD : [Vertex; 4] =
70[
71    Vertex { a_position: [ 0.0, 0.0 ] },
72    Vertex { a_position: [ 1.0, 0.0 ] },
73    Vertex { a_position: [ 1.0, 1.0 ] },
74    Vertex { a_position: [ 0.0, 1.0 ] }
75];
76const INDEX_QUAD : [u16; 6] = [ 0, 1, 2, 2, 3, 0 ];
77
78struct GpuSpriteSlice
79{
80    pub texture_x : f32,
81    pub texture_y : f32,
82    pub texture_w : f32,
83    pub texture_h : f32,
84    pub scale_x : f32,
85    pub scale_y : f32,
86    pub offset_x : f32,
87    pub offset_y : f32,
88}
89
90struct GpuSheetHandle
91{
92    texture : Rc<Texture2d>,
93    mag_filter : MagnifySamplerFilter,
94    min_filter : MinifySamplerFilter,
95    slices : Vec<GpuSpriteSlice>
96}
97
98struct GpuSheetPreparer
99{
100    display : Rc<RefCell<SDL2Facade>>,
101    #[allow(dead_code)]
102    default_texture : Rc<Texture2d>,
103    texture_filtering : bool,
104    #[cfg(feature = "imgui_feature")]
105    imgui_renderer : Rc<RefCell<Option<imgui_glium_renderer::Renderer>>>
106}
107
108impl GpuSheetPreparer
109{
110    fn texture_raw_from_data(data : &Vec<u8>, palette : &Option<Vec<Color>>) -> Vec<u8>
111    {
112        if let Some(palette) = palette
113        {
114            let mut expanded = Vec::with_capacity(data.len() * 4);
115
116            for i in 0..data.len()
117            {
118                let color = data[i] as usize;
119
120                if color < palette.len()
121                {
122                    expanded.push(palette[color].r);
123                    expanded.push(palette[color].g);
124                    expanded.push(palette[color].b);
125                    expanded.push(palette[color].a);
126                }
127                else
128                {
129                    for _ in 0..4
130                    {
131                        expanded.push(0);
132                    }
133                }
134            }
135
136            return expanded;
137        }
138
139        data.clone()
140    }
141
142    fn make_sheet_handle(&mut self, data: &mut Sheet) -> GpuSheetHandle
143    {
144        let texture;
145
146        {
147            let display_ref: &SDL2Facade = &self.display.borrow();
148            let image_raw = GpuSheetPreparer::texture_raw_from_data(&data.image_raw, &data.palette);
149
150            let image = RawImage2d::from_raw_rgba(image_raw, (data.width, data.height));
151            texture = Rc::new(Texture2d::new(display_ref, image).expect("Texture upload failed"));
152        }
153
154        self.make_sheet_handle_from_texture(data, texture)
155    }
156
157    fn make_sheet_handle_from_texture(&mut self, data: &mut Sheet, texture : Rc<Texture2d>) -> GpuSheetHandle
158    {
159        let (mag_filter, min_filter) = self.sample_filters(data);
160
161        let mut slices = Vec::new();
162
163        for slice in &data.metadata.slices
164        {
165            let texture_w = slice.texture_w / data.width as f32;
166            let texture_h = slice.texture_h / data.height as f32;
167
168            slices.push(GpuSpriteSlice
169            {
170                texture_x: slice.texture_x / data.width as f32,
171                texture_y: slice.texture_y / data.height as f32,
172                texture_w,
173                texture_h,
174                scale_x: slice.texture_w,
175                scale_y: slice.texture_h,
176                offset_x: -slice.origin_x / slice.texture_w,
177                offset_y: -slice.origin_y / slice.texture_h,
178            })
179        }
180
181        GpuSheetHandle
182        {
183            texture,
184            mag_filter,
185            min_filter,
186            slices
187        }
188    }
189
190    #[cfg(feature = "imgui_feature")]
191    fn update_imgui_texture(&mut self, id : DataId<Sheet>, handle : &mut GpuSheetHandle)
192    {
193        let mut imgui_borrow = self.imgui_renderer.borrow_mut();
194
195        if let Some(imgui_renderer) = imgui_borrow.as_mut()
196        {
197            imgui_renderer.textures().replace(imgui::TextureId::from(id.index()), imgui_glium_renderer::Texture
198            {
199                texture : handle.texture.clone(),
200                sampler : SamplerBehavior
201                {
202                    wrap_function: (SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat),
203                    minify_filter: handle.min_filter,
204                    magnify_filter: handle.mag_filter,
205                    .. Default::default()
206                }
207            });
208        }
209    }
210
211    fn sample_filters(&mut self, data : &mut Sheet) -> (MagnifySamplerFilter, MinifySamplerFilter)
212    {
213        let filtered;
214
215        if let Some(filter) = data.metadata.texture_filtering
216        {
217            filtered = filter;
218        }
219        else
220        {
221            filtered = self.texture_filtering;
222        }
223
224        let mag_filter = match filtered
225        {
226            true => MagnifySamplerFilter::Linear,
227            false => MagnifySamplerFilter::Nearest
228        };
229        let min_filter = match filtered
230        {
231            true => MinifySamplerFilter::Linear,
232            false => MinifySamplerFilter::Nearest
233        };
234
235        (mag_filter, min_filter)
236    }
237}
238
239impl DataPreparer<Sheet, GpuSheetHandle> for GpuSheetPreparer
240{
241    #[allow(unused_variables)]
242    fn prepare(&mut self, data : &mut Sheet, id : DataId<Sheet>) -> GpuSheetHandle
243    {
244        #[allow(unused_mut)]
245        let mut handle = self.make_sheet_handle(data);
246
247        data.texture_dirty = false;
248
249        #[cfg(feature = "imgui_feature")]
250        self.update_imgui_texture(id, &mut handle);
251
252        handle
253    }
254
255    #[allow(unused_variables)]
256    fn unprepare(&mut self, _prepared : &mut GpuSheetHandle, id : DataId<Sheet>)
257    {
258        #[cfg(feature = "imgui_feature")]
259        {
260            let mut imgui_borrow = self.imgui_renderer.borrow_mut();
261
262            if let Some(imgui_renderer) = imgui_borrow.as_mut()
263            {
264                imgui_renderer.textures().replace(imgui::TextureId::from(id.index()), imgui_glium_renderer::Texture
265                {
266                    texture : self.default_texture.clone(),
267                    sampler : SamplerBehavior
268                    {
269                        wrap_function: (SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat),
270                        minify_filter: MinifySamplerFilter::Linear,
271                        magnify_filter: MagnifySamplerFilter::Linear,
272                        .. Default::default()
273                    }
274                });
275            }
276        }
277    }
278
279    #[allow(unused_variables)]
280    fn reprepare(&mut self, data : &mut Sheet, prepared : &mut GpuSheetHandle, id : DataId<Sheet>)
281    {
282        if data.texture_dirty
283        {
284            *prepared = self.make_sheet_handle(data);
285        }
286        else
287        {
288            *prepared = self.make_sheet_handle_from_texture(data, prepared.texture.clone());
289        }
290
291        #[cfg(feature = "imgui_feature")]
292        self.update_imgui_texture(id, prepared);
293
294        data.texture_dirty = false;
295    }
296}
297
298#[allow(unused_mut)]
299fn modify_shader_header(mut shader_text : &str) -> String
300{
301    #[cfg(not(target_os = "emscripten"))]
302    {
303        shader_text.to_string()
304    }
305    #[cfg(target_os = "emscripten")]
306    {
307        shader_text.replace("#version 140", "#version 300 es\nprecision mediump float;")
308    }
309}
310
311struct GpuShaderHandle
312{
313    program : Program
314}
315
316struct GpuShaderPreparer
317{
318    display : Rc<RefCell<SDL2Facade>>
319}
320
321impl DataPreparer<Shader, GpuShaderHandle> for GpuShaderPreparer
322{
323    fn prepare(&mut self, data : &mut Shader, _id : DataId<Shader>) -> GpuShaderHandle
324    {
325        let program = make_program(&self.display.borrow(),
326            &modify_shader_header(include_str!("shader/vert_standard_140.glsl")),
327            &modify_shader_header(&data.program_source)).expect("Shader compilation failed");
328        
329        GpuShaderHandle
330        {
331            program
332        }
333    }
334    fn unprepare(&mut self, _prepared : &mut GpuShaderHandle, _id : DataId<Shader>)
335    {
336        
337    }
338}
339
340#[derive(Copy, Clone, Eq, PartialEq, Default)]
341struct InstanceBufferParam
342{
343    op : RendererOp,
344    sheet : DataId<Sheet>,
345    shader : DataId<Shader>
346}
347
348fn make_program(display : &SDL2Facade, vertex_shader : &str, fragment_shader : &str) -> Result<Program, ProgramCreationError>
349{
350    let source = ProgramCreationInput::SourceCode
351    {
352        vertex_shader,
353        fragment_shader,
354        geometry_shader : None,
355        tessellation_control_shader : None,
356        tessellation_evaluation_shader : None,
357        transform_feedback_varyings : None,
358        uses_point_size : false,
359        outputs_srgb : true
360    };
361    
362    glium::Program::new(display, source)
363}
364
365fn upscale_instance_from_transform(transform : &DrawTransform) -> Instance
366{
367    Instance
368    {
369        a_transform_col1 : [ transform.mat.x.x, transform.mat.x.y ],
370        a_transform_col2 : [ transform.mat.y.x, transform.mat.y.y ],
371        a_transform_col3 : [ transform.mat.z.x, transform.mat.z.y ],
372        a_transform_offset : [ 0.0, 0.0 ],
373        a_tex_coords : [0.0, 0.0, 1.0, 1.0],
374        a_rgba : [1.0, 1.0, 1.0, 1.0],
375    }
376}
377
378#[derive(Copy, Clone, Default)]
379struct BatchRun
380{
381    start : usize,
382    len : usize,
383    param : InstanceBufferParam
384}
385
386pub(crate) struct GlDrawControl
387{
388    display : Rc<RefCell<SDL2Facade>>,
389    window : Rc<RefCell<sdl2::video::Window>>,
390    base_width : f32,
391    base_height : f32,
392    scale_width : f32,
393    scale_height : f32,
394    need_recalc_viewport : bool,
395    viewport_offset : (f32, f32),
396    viewport_mode : ViewportMode,
397    quad_buffer : VertexBuffer<Vertex>,
398    index_buffer : IndexBuffer<u16>,
399    instance_buffer_src : Box<[Instance ; INSTANCES_MAX]>,
400    instance_buffer : Vec<VertexBuffer<Instance>>,
401    instance_buffer_flip : usize,
402    default_texture : Rc<Texture2d>,
403    shader_list : Vec<Program>,
404    scissor : Option<glium::Rect>,
405    frame : Option<Frame>,
406    pixel_scale : u32,
407    last_pixel_scale : u32,
408    pixel_texture : Option<Texture2d>,
409    pixel_upscale_texture : Option<Texture2d>,
410    current_index : usize,
411    index_runs : Vec<BatchRun>,
412    current_run : BatchRun,
413    last_view_transform : DrawTransform,
414    gpu_sheet_store : PreparedStore<Sheet, GpuSheetHandle>,
415    gpu_shader_store : PreparedStore<Shader, GpuShaderHandle>,
416    #[cfg(feature = "imgui_feature")]
417    imgui_renderer : Rc<RefCell<Option<imgui_glium_renderer::Renderer>>>
418}
419
420impl GlDrawControl
421{
422    fn perform_draw_batch(&mut self)
423    {
424        if self.current_index <= 0
425        {
426            return;
427        }
428
429        self.index_runs.push(self.current_run);
430
431        self.instance_buffer[self.instance_buffer_flip].invalidate();
432        self.instance_buffer[self.instance_buffer_flip].write(&self.instance_buffer_src[..]);
433        
434        // I can't belive Rust is making me do this
435        let index_skip_list = self.index_runs.clone();
436        
437        let mut do_draw = | first : usize, last : usize, param : &InstanceBufferParam |
438        {
439            if first >= last
440            {
441                return;
442            }
443            
444            if let Some(frame) = &mut self.frame
445            {
446                let mut texture_ref = self.default_texture.as_ref();
447                let mut shader_ref = &self.shader_list[param.op as usize];
448                let mut mag_filter = MagnifySamplerFilter::Linear;
449                let mut min_filter = MinifySamplerFilter::Linear;
450                
451                if let Some(gpu_sheet_info) = self.gpu_sheet_store.get(param.sheet)
452                {
453                    texture_ref = gpu_sheet_info.texture.as_ref();
454                    mag_filter = gpu_sheet_info.mag_filter;
455                    min_filter = gpu_sheet_info.min_filter;
456                }
457                if let Some(gpu_shader_info) = self.gpu_shader_store.get(param.shader)
458                {
459                    shader_ref = &gpu_shader_info.program;
460                }
461                
462                let uniform = uniform!
463                {
464                    t_texture : Sampler::new(texture_ref)
465                        .magnify_filter(mag_filter).minify_filter(min_filter)
466                        .wrap_function(SamplerWrapFunction::Repeat),
467                    u_tex_size : [ texture_ref.width() as f32, texture_ref.height() as f32 ]
468                };
469                
470                match self.viewport_mode
471                {
472                    ViewportMode::Independent | ViewportMode::OneToOneDpiScaled | ViewportMode::OneToOneUnscaled =>
473                    {
474                        let params = glium::DrawParameters
475                        {
476                            blend : glium::draw_parameters::Blend::alpha_blending(),
477                            scissor : self.scissor,
478                            .. Default::default()
479                        };
480                        
481                        frame.draw((&self.quad_buffer,
482                            self.instance_buffer[self.instance_buffer_flip].slice(first..last)
483                                .expect("Failed to prepare instance buffer").per_instance()
484                                .expect("Failed to prepare instance buffer")),
485                            &self.index_buffer, shader_ref,
486                            &uniform, &params).expect("Draw operation failed");
487                    },
488                    ViewportMode::Pixel =>
489                    {
490                        let params = glium::DrawParameters
491                        {
492                            blend : glium::draw_parameters::Blend::alpha_blending(),
493                            scissor : self.scissor,
494                            multisampling : false,
495                            dithering : false,
496                            .. Default::default()
497                        };
498                        
499                        self.pixel_texture.as_ref().unwrap().as_surface().draw((&self.quad_buffer,
500                            self.instance_buffer[self.instance_buffer_flip].slice(first..last)
501                                .expect("Failed to prepare instance buffer").per_instance()
502                                .expect("Failed to prepare instance buffer")),
503                            &self.index_buffer, shader_ref,
504                            &uniform, &params).expect("Draw operation failed");
505                    }
506                }
507            }
508        };
509
510        for run in &index_skip_list
511        {
512            do_draw(run.start, run.start + run.len, &run.param);
513        }
514
515        self.flip_instance_buffers();
516    }
517
518    fn check_batch_break(&mut self, param : InstanceBufferParam)
519    {
520        let skip_index = (self.current_index + INSTANCE_RUN_COARSENESS) / INSTANCE_RUN_COARSENESS * INSTANCE_RUN_COARSENESS;
521
522        if skip_index >= INSTANCES_MAX
523        {
524            self.perform_draw_batch();
525        }
526
527        if self.current_index == 0
528        {
529            self.current_run.param = param;
530        }
531        else if self.current_run.param != param
532        {
533            self.index_runs.push(self.current_run);
534            self.current_index = skip_index;
535            self.current_run = BatchRun { start : self.current_index, len : 0, param };
536        }
537    }
538
539    fn flip_instance_buffers(&mut self)
540    {
541        self.instance_buffer_flip = (self.instance_buffer_flip + 1) % self.instance_buffer.len();
542        self.current_index = 0;
543        self.current_run = BatchRun { start : 0, len : 0, param : InstanceBufferParam::default() };
544        self.index_runs.clear();
545
546        if self.instance_buffer_flip % (self.instance_buffer.len() / 2) == 0
547        {
548            self.display.borrow().get_context().flush();
549        }
550    }
551    
552    fn update_pixel_upscale(&mut self)
553    {
554        if self.pixel_scale == self.last_pixel_scale
555        {
556            return;
557        }
558        
559        self.last_pixel_scale = self.pixel_scale;
560        let display_ref : &SDL2Facade = &self.display.borrow();
561        let pixel_surface = self.pixel_texture.as_mut().unwrap().as_surface();
562        let (width, height) = pixel_surface.get_dimensions();
563        
564        self.pixel_upscale_texture = Some(Texture2d::empty_with_format(
565            display_ref, UncompressedFloatFormat::U8U8U8U8, glium::texture::MipmapsOption::NoMipmap,
566            width * self.pixel_scale, height * self.pixel_scale).expect("Could not create upscale framebuffer"));
567    }
568}
569
570impl DrawControl for GlDrawControl
571{
572    fn screen_transform(&self) -> &DrawTransform
573    {
574        &self.last_view_transform
575    }
576
577    fn draw_sprite(&mut self, draw_info: &DrawSpriteInfo)
578    {
579        let mut transform = draw_info.transform.clone();
580        let mut transform_offset_x = -0.5;
581        let mut transform_offset_y = -0.5;
582        let mut texture_x = 0.0;
583        let mut texture_y = 0.0;
584        let mut texture_w = 1.0;
585        let mut texture_h = 1.0;
586        let mut sheet_id = DataId::new(0);
587
588        transform.translate(draw_info.x, draw_info.y);
589        if draw_info.angle != 0.0
590        {
591            transform.rotate(draw_info.angle);
592        }
593
594        let mut got_slice = false;
595
596        if let Some(gpu_sheet_info) = self.gpu_sheet_store.get(draw_info.sheet_id)
597        {
598            if draw_info.slice < gpu_sheet_info.slices.len()
599            {
600                let slice = &gpu_sheet_info.slices[draw_info.slice];
601
602                transform.scale(draw_info.scale_x * slice.scale_x, draw_info.scale_y * slice.scale_y);
603
604                sheet_id = draw_info.sheet_id;
605                transform_offset_x = slice.offset_x;
606                transform_offset_y = slice.offset_y;
607                texture_x = slice.texture_x;
608                texture_y = slice.texture_y;
609                texture_w = slice.texture_w;
610                texture_h = slice.texture_h;
611
612                got_slice = true;
613            }
614        }
615
616        if !got_slice
617        {
618            transform.scale(draw_info.scale_x * 32.0, draw_info.scale_y * 32.0);
619        }
620
621        self.draw_sprite_raw(&DrawSpriteRawInfo
622        {
623            sheet_id,
624            shader_id: draw_info.shader_id,
625            transform: transform,
626            transform_offset_x,
627            transform_offset_y,
628            texture_x,
629            texture_y,
630            texture_w,
631            texture_h,
632            r: draw_info.r,
633            g: draw_info.g,
634            b: draw_info.b,
635            alpha: draw_info.alpha
636        });
637    }
638
639    fn draw_sprite_raw(&mut self, draw_info : &DrawSpriteRawInfo)
640    {
641        let instance_param = InstanceBufferParam
642        {
643            op : RendererOp::Sprite,
644            sheet : draw_info.sheet_id,
645            shader : draw_info.shader_id
646        };
647
648        self.check_batch_break(instance_param);
649
650        let transform = &draw_info.transform;
651        let instance = Instance
652        {
653            a_transform_col1 : [ transform.mat.x.x, transform.mat.x.y ],
654            a_transform_col2 : [ transform.mat.y.x, transform.mat.y.y ],
655            a_transform_col3 : [ transform.mat.z.x, transform.mat.z.y ],
656            a_transform_offset : [ draw_info.transform_offset_x, draw_info.transform_offset_y ],
657            a_tex_coords : [ draw_info.texture_x, draw_info.texture_y, draw_info.texture_w, draw_info.texture_h ],
658            a_rgba : [ draw_info.r, draw_info.g, draw_info.b, draw_info.alpha ],
659        };
660        self.instance_buffer_src[self.current_index] = instance;
661
662        self.current_index += 1;
663        self.current_run.len += 1;
664    }
665
666    fn draw_background_color(&mut self, draw_info : &DrawBackgroundColorInfo )
667    {
668        let instance_param = InstanceBufferParam
669        {
670            op : RendererOp::BackgroundColor,
671            sheet : DataId::new(0),
672            shader : draw_info.shader_id
673        };
674
675        self.check_batch_break(instance_param);
676
677        let instance = Instance
678        {
679            a_transform_col1 : [ 2.0, 0.0 ],
680            a_transform_col2 : [ 0.0, -2.0 ],
681            a_transform_col3 : [ -1.0, 1.0 ],
682            a_transform_offset : [ 0.0, 0.0 ],
683            a_tex_coords : [ 0.0, 0.0, 1.0, 1.0 ],
684            a_rgba : [ draw_info.r, draw_info.g, draw_info.b, draw_info.alpha ],
685        };
686        self.instance_buffer_src[self.current_index] = instance;
687
688        self.current_index += 1;
689        self.current_run.len += 1;
690    }
691}
692
693impl DrawControlBackend for GlDrawControl
694{
695    fn new(renderer_new_info : &mut RendererNewInfo) -> DrawControlResult where Self : Sized
696    {
697        let video_subsystem = &renderer_new_info.video_subsystem;
698
699        let (width, height) = crate::renderer::calculate_window_size(renderer_new_info.width as f32,
700                                                                     renderer_new_info.height as f32,
701                                                                     renderer_new_info.default_window_scale,
702                                                                     video_subsystem);
703
704        let mut window_builder = video_subsystem.window("", width, height);
705        
706        window_builder.hidden().position_centered().resizable().allow_highdpi();
707
708        #[cfg(not(target_os = "emscripten"))]
709        {
710            video_subsystem.gl_attr().set_context_profile(sdl2::video::GLProfile::Core);
711            video_subsystem.gl_attr().set_context_version(3, 2);
712        }
713        #[cfg(target_os = "emscripten")]
714        {
715            video_subsystem.gl_attr().set_context_profile(sdl2::video::GLProfile::GLES);
716            video_subsystem.gl_attr().set_context_version(3, 0);
717        }
718        
719        let display_raw = window_builder.build_glium().map_err(
720            |error| RendererError::RendererInitFailed(format!("Could not create game window: {}", error)))?;
721        
722        let mut pixel_texture = None;
723        
724        match renderer_new_info.viewport_mode
725        {
726            ViewportMode::Independent | ViewportMode::OneToOneDpiScaled | ViewportMode::OneToOneUnscaled => (),
727            ViewportMode::Pixel =>
728            {
729                pixel_texture = Some(Texture2d::empty_with_format(
730                    &display_raw, UncompressedFloatFormat::U8U8U8U8, glium::texture::MipmapsOption::NoMipmap, renderer_new_info.width, renderer_new_info.height).map_err(
731                    |error| RendererError::RendererInitFailed(format!("Could not create framebuffer: {}", error)))?);
732            }
733        };
734        
735        let quad_buffer = glium::VertexBuffer::new(&display_raw, &VERTEX_QUAD).map_err(
736            |error| RendererError::RendererInitFailed(format!("Could not create vertex buffer: {}", error)))?;
737        let index_buffer = glium::IndexBuffer::new(&display_raw,
738            glium::index::PrimitiveType::TrianglesList, &INDEX_QUAD).map_err(
739            |error| RendererError::RendererInitFailed(format!("Could not create index buffer: {}", error)))?;
740
741        let mut instance_buffer = Vec::new();
742
743        for _ in 0..INSTANCE_BUFFER_MAX
744        {
745            instance_buffer.push(glium::VertexBuffer::<Instance>::empty_dynamic(&display_raw, INSTANCES_MAX).map_err(
746                |error| RendererError::RendererInitFailed(format!("Could not create instance buffer: {}", error)))?);
747        }
748        
749        let context = display_raw.get_context();
750        info!("Renderer: {} {}", context.get_opengl_vendor_string(), context.get_opengl_renderer_string());
751        info!("OpenGL version: {}", context.get_opengl_version_string());
752        if let Some(free_vram) = context.get_free_video_memory()
753        {
754            info!("VRAM: {} MiB free", free_vram / 1024 / 1024);
755        }
756        
757        let shader_background = make_program(&display_raw,
758            &modify_shader_header(include_str!("shader/vert_standard_140.glsl")),
759            &modify_shader_header(include_str!("shader/frag_solidcolor_140.glsl"))).map_err(
760            |error| RendererError::RendererInitFailed(format!("Could not load shader: {}", error)))?;
761        let shader_sprite = make_program(&display_raw,
762            &modify_shader_header(include_str!("shader/vert_standard_140.glsl")),
763            &modify_shader_header(include_str!("shader/frag_sprite_140.glsl"))).map_err(
764            |error| RendererError::RendererInitFailed(format!("Could not load shader: {}", error)))?;
765        
766        let shader_list = vec!(shader_background, shader_sprite);
767
768        let (default_texture, default_width, default_height) = crate::renderer::png_to_raw(
769            Cursor::new(include_bytes!("data/default.png").to_vec()))?;
770        
771        let image = RawImage2d::from_raw_rgba(default_texture, (default_width, default_height));
772        let default_texture = Rc::new(Texture2d::new(&display_raw, image).map_err(
773            |error| RendererError::RendererInitFailed(format!("Could not upload texture: {}", error)))?);
774        
775        let window = display_raw.window_clone();
776        
777        let display = Rc::new(RefCell::new(display_raw));
778        #[cfg(feature = "imgui_feature")]
779        let imgui_renderer = Rc::new(RefCell::new(None));
780        let sheet_data_preparer = Box::new(GpuSheetPreparer
781        {
782            display : display.clone(),
783            default_texture: default_texture.clone(),
784            texture_filtering : renderer_new_info.texture_filtering,
785            #[cfg(feature = "imgui_feature")]
786            imgui_renderer: imgui_renderer.clone()
787        });
788        let shader_data_preparer = Box::new(GpuShaderPreparer
789        {
790            display : display.clone()
791        });
792        
793        let gpu_sheet_store = PreparedStore::new(&mut renderer_new_info.resources.store_mut(),
794            sheet_data_preparer).map_err(
795            |error| RendererError::LoadResourceFailed(format!("Failed to create GPU sheet handle store: {}", error)))?;
796        
797        let gpu_shader_store = PreparedStore::new(&mut renderer_new_info.resources.store_mut(),
798            shader_data_preparer).map_err(
799            |error| RendererError::LoadResourceFailed(format!("Failed to create GPU shader handle store: {}", error)))?;
800        
801        Ok((Box::new(GlDrawControl
802        {
803            display,
804            window : window.clone(),
805            viewport_offset : (0.0, 0.0),
806            viewport_mode : renderer_new_info.viewport_mode,
807            base_width : renderer_new_info.width as f32,
808            base_height : renderer_new_info.height as f32,
809            scale_width : 0.0,
810            scale_height : 0.0,
811            need_recalc_viewport : false,
812            quad_buffer,
813            index_buffer,
814            instance_buffer_src : Box::new([Default::default() ; INSTANCES_MAX]),
815            instance_buffer,
816            instance_buffer_flip : 0,
817            default_texture,
818            shader_list,
819            scissor : None,
820            frame : None,
821            pixel_scale : 1,
822            last_pixel_scale : 0,
823            pixel_texture,
824            pixel_upscale_texture : None,
825            current_index : 0,
826            index_runs : Vec::new(),
827            current_run : BatchRun { start : 0, len : 0, param : InstanceBufferParam::default() },
828            last_view_transform : DrawTransform::identity(),
829            gpu_sheet_store,
830            gpu_shader_store,
831            #[cfg(feature = "imgui_feature")]
832            imgui_renderer,
833        }), window))
834    }
835    
836    fn sync_sheet_store(&mut self, sheet_store : &mut DataStore<Sheet>) -> Result<(), PreparedStoreError>
837    {
838        self.gpu_sheet_store.sync(sheet_store)
839    }
840    
841    fn sync_shader_store(&mut self, shader_store : &mut DataStore<Shader>) -> Result<(), PreparedStoreError>
842    {
843        self.gpu_shader_store.sync(shader_store)
844    }
845    
846    #[cfg(feature = "imgui_feature")]
847    fn draw_imgui(&mut self, ui : imgui::Ui)
848    {
849        self.perform_draw_batch();
850        
851        if let Some(frame) = &mut self.frame
852        {
853            let mut imgui_borrow = self.imgui_renderer.borrow_mut();
854
855            if let Some(imgui_renderer) = imgui_borrow.as_mut()
856            {
857                let draw_data = ui.render();
858                imgui_renderer.render(frame, &draw_data).expect("imgui render failed");
859            }
860        }
861    }
862    
863    fn recalculate_viewport(&mut self, base_width : f32, base_height : f32)
864    {
865        self.base_width = base_width;
866        self.base_height = base_height;
867
868        if self.frame.is_some()
869        {
870            self.need_recalc_viewport = true;
871            return;
872        }
873
874        let (width, height) = self.window.borrow().size();
875        let info = crate::renderer::recalculate_viewport_common(base_width, base_height, width, height, self.viewport_mode);
876
877        self.scale_width = info.scale_width;
878        self.scale_height = info.scale_height;
879        self.viewport_offset = (info.scale_offset_x, info.scale_offset_y);
880        self.pixel_scale = info.pixel_scale;
881        self.scissor = match info.scissor
882        {
883            Some(scissor) =>
884            {
885                Some(glium::Rect
886                {
887                    left : scissor.x,
888                    bottom : scissor.y,
889                    width : scissor.w,
890                    height : scissor.h
891                })
892            },
893            None => None
894        }
895    }
896    
897    fn start_drawing(&mut self)
898    {
899        if self.frame.is_some()
900        {
901            panic!("Cannot call draw() twice without a present()");
902        }
903
904        if self.need_recalc_viewport
905        {
906            self.recalculate_viewport(self.base_width, self.base_height);
907
908            self.need_recalc_viewport = false;
909        }
910        
911        self.frame = Some(self.display.borrow().draw());
912        
913        self.frame.as_mut().unwrap().clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
914        
915        let mut view_transform = DrawTransform::from_array(&UNBORKED_IDENTITY_TRANSFORM);
916        
917        match self.viewport_mode
918        {
919            ViewportMode::Independent | ViewportMode::OneToOneDpiScaled | ViewportMode::OneToOneUnscaled =>
920            {
921                let (x, y) = self.viewport_offset;
922                
923                view_transform.scale(1.0 / self.scale_width, 1.0 / self.scale_height);
924                view_transform.translate(x, y);
925            },
926            ViewportMode::Pixel =>
927            {
928                let mut pixel_surface = self.pixel_texture.as_mut().unwrap().as_surface();
929                let (width, height) = pixel_surface.get_dimensions();
930                
931                pixel_surface.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
932                
933                view_transform.scale(1.0 / width as f32, 1.0 / height as f32);
934            }
935        }
936        
937        self.last_view_transform = view_transform.clone();
938        self.flip_instance_buffers();
939    }
940
941    fn end_drawing(&mut self)
942    {
943        self.perform_draw_batch();
944
945        self.display.borrow().get_context().flush();
946    }
947    
948    fn present(&mut self)
949    {
950        if self.frame.is_some()
951        {
952            match self.viewport_mode
953            {
954                ViewportMode::Independent | ViewportMode::OneToOneDpiScaled | ViewportMode::OneToOneUnscaled => (),
955                ViewportMode::Pixel =>
956                {
957                    // Low-res pixel to high-res pixel
958                    
959                    let mut transform = DrawTransform::from_array(&UNBORKED_UPSCALE_TRANSFORM);
960                    
961                    let params = glium::DrawParameters::default();
962
963                    self.instance_buffer_src[0] = upscale_instance_from_transform(&transform);
964                    self.instance_buffer[self.instance_buffer_flip].invalidate();
965                    self.instance_buffer[self.instance_buffer_flip].write(&self.instance_buffer_src[..]);
966                    
967                    self.update_pixel_upscale();
968                    
969                    let uniform = uniform!
970                    {
971                        t_texture : Sampler::new(self.pixel_texture.as_ref().unwrap())
972                            .magnify_filter(MagnifySamplerFilter::Nearest)
973                            .minify_filter(MinifySamplerFilter::Nearest)
974                            .wrap_function(SamplerWrapFunction::Clamp)
975                    };
976                    
977                    self.pixel_upscale_texture.as_mut().unwrap().as_surface().draw((&self.quad_buffer,
978                        self.instance_buffer[self.instance_buffer_flip].slice(0..1)
979                            .expect("Failed to prepare instance buffer").per_instance()
980                            .expect("Failed to prepare instance buffer")),
981                        &self.index_buffer, &self.shader_list[RendererOp::Sprite as usize],
982                        &uniform, &params).expect("Draw operation failed");
983                    
984                    // High-res pixel to screen
985                    
986                    let uniform = uniform!
987                    {
988                        t_texture : Sampler::new(self.pixel_upscale_texture.as_ref().unwrap())
989                            .magnify_filter(MagnifySamplerFilter::Linear)
990                            .minify_filter(MinifySamplerFilter::Linear)
991                            .wrap_function(SamplerWrapFunction::Clamp)
992                    };
993                    
994                    let (x, y) = self.viewport_offset;
995                    transform.scale(1.0 / self.scale_width, 1.0 / self.scale_height);
996                    transform.translate(x, y);
997
998                    self.instance_buffer_src[0] = upscale_instance_from_transform(&transform);
999                    self.instance_buffer[self.instance_buffer_flip].invalidate();
1000                    self.instance_buffer[self.instance_buffer_flip].write(&self.instance_buffer_src[..]);
1001                    
1002                    self.frame.as_mut().unwrap().draw((&self.quad_buffer,
1003                        self.instance_buffer[self.instance_buffer_flip].slice(0..1)
1004                            .expect("Failed to prepare instance buffer").per_instance()
1005                            .expect("Failed to prepare instance buffer")),
1006                        &self.index_buffer, &self.shader_list[RendererOp::Sprite as usize],
1007                        &uniform, &params).expect("Draw operation failed");
1008                }
1009            }
1010            
1011            let mut frame = None;        
1012            std::mem::swap(&mut frame, &mut self.frame);
1013            
1014            frame.unwrap().finish().expect("Draw finish failed");
1015        }
1016    }
1017    
1018    #[cfg(feature = "imgui_feature")]
1019    fn init_imgui_renderer(&mut self, imgui : &mut imgui::Context) -> Result<(), RendererError>
1020    {
1021        let mut imgui_borrow = self.imgui_renderer.borrow_mut();
1022
1023        if imgui_borrow.is_some()
1024        {
1025            return Err(RendererError::ImguiRendererFailed("Already initialized".to_string()));
1026        }
1027        
1028        let mut imgui_renderer = imgui_glium_renderer::Renderer::init(
1029            imgui, &*self.display.borrow()).map_err(
1030            |error| RendererError::ImguiRendererFailed(format!("Could not initialize imgui renderer: {}", error)))?;
1031
1032        imgui_renderer.textures().replace(imgui::TextureId::from(0), imgui_glium_renderer::Texture
1033        {
1034            texture : self.default_texture.clone(),
1035            sampler : SamplerBehavior
1036            {
1037                wrap_function: (SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat, SamplerWrapFunction::Repeat),
1038                minify_filter: MinifySamplerFilter::Linear,
1039                magnify_filter: MagnifySamplerFilter::Linear,
1040                .. Default::default()
1041            }
1042        });
1043        
1044        *imgui_borrow = Some(imgui_renderer);
1045        
1046        Ok(())
1047    }
1048
1049    #[cfg(feature = "imgui_feature")]
1050    fn update_imgui_renderer(&mut self, imgui : &mut imgui::Context)
1051    {
1052        let mut imgui_borrow = self.imgui_renderer.borrow_mut();
1053
1054        if let Some(imgui_renderer) = imgui_borrow.as_mut()
1055        {
1056            if !imgui.fonts().is_built()
1057            {
1058                imgui_renderer.reload_font_texture(imgui).expect("Font texture update failed");
1059            }
1060        }
1061    }
1062}