1pub use glow;
2
3#[cfg(feature = "derive")]
4extern crate solstice_derive;
5
6pub mod buffer;
7pub mod canvas;
8pub mod image;
9pub mod mesh;
10pub mod quad_batch;
11pub mod shader;
12pub mod texture;
13pub mod vertex;
14pub mod viewport;
15
16mod gl;
17
18use glow::HasContext;
19use slotmap::SlotMap;
20use std::{
21 fmt::{Debug, Error, Formatter},
22 str::FromStr,
23};
24
25#[derive(Debug)]
26pub enum GraphicsError {
27 ShaderError(shader::ShaderError),
28 TextureError,
29 BufferError,
30 FramebufferError,
31 RenderbufferError,
32}
33
34impl std::fmt::Display for GraphicsError {
35 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
36 write!(f, "{:?}", self)
37 }
38}
39
40impl std::error::Error for GraphicsError {}
41
42type GLContext = glow::Context;
43
44type GLBuffer = <GLContext as HasContext>::Buffer;
45type GLProgram = <GLContext as HasContext>::Program;
46type GLTexture = <GLContext as HasContext>::Texture;
47type GLFramebuffer = <GLContext as HasContext>::Framebuffer;
48type GLRenderbuffer = <GLContext as HasContext>::Renderbuffer;
49type GLUniformLocation = <GLContext as HasContext>::UniformLocation;
50
51slotmap::new_key_type! {
52 pub struct ShaderKey;
53 pub struct BufferKey;
54 pub struct TextureKey;
55 pub struct FramebufferKey;
56 pub struct RenderbufferKey;
57}
58
59pub struct DebugGroup<'a> {
60 ctx: &'a GLContext,
61}
62
63impl<'a> DebugGroup<'a> {
64 pub fn new(ctx: &'a GLContext, message: &str) -> Self {
65 if ctx.supports_debug() {
66 unsafe {
67 ctx.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, 0, message);
68 }
69 }
70 Self { ctx }
71 }
72}
73
74impl<'a> Drop for DebugGroup<'a> {
75 fn drop(&mut self) {
76 if self.ctx.supports_debug() {
77 unsafe {
78 self.ctx.pop_debug_group();
79 }
80 }
81 }
82}
83
84#[derive(Copy, Clone, Debug, Eq, PartialEq)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86pub enum PixelFormat {
87 Unknown,
88
89 LUMINANCE,
91 RG8,
92 RGB8,
93 RGBA8,
94 SRGBA8,
95 R16,
96 RG16,
97 RGBA16,
98 R16F,
99 RG16F,
100 RGBA16F,
101 R32F,
102 RG32F,
103 RGBA32F,
104 Alpha,
105
106 Stencil8,
108 Depth16,
109 Depth24,
110 Depth32F,
111 Depth24Stencil8,
112 Depth32fStencil8,
113}
114
115fn target_to_index(target: canvas::Target) -> usize {
116 match target {
117 canvas::Target::Draw => 0,
118 canvas::Target::Read => 1,
119 canvas::Target::All => 0,
120 }
121}
122
123fn buffer_type_to_index(buffer_type: buffer::BufferType) -> usize {
124 match buffer_type {
125 buffer::BufferType::Vertex => 0,
126 buffer::BufferType::Index => 1,
127 }
128}
129
130#[derive(Debug, Copy, Clone, Eq, PartialEq)]
131pub enum VertexWinding {
132 ClockWise,
133 CounterClockWise,
134}
135
136#[derive(Debug, Copy, Clone, Eq, PartialEq)]
137pub enum DepthFunction {
138 Never,
139 Less,
140 Equal,
141 LessEqual,
142 Greater,
143 NotEqual,
144 GreaterEqual,
145 Always,
146}
147
148impl DepthFunction {
149 pub fn to_gl(&self) -> u32 {
150 match self {
151 DepthFunction::Never => glow::NEVER,
152 DepthFunction::Less => glow::LESS,
153 DepthFunction::Equal => glow::EQUAL,
154 DepthFunction::LessEqual => glow::LEQUAL,
155 DepthFunction::Greater => glow::GREATER,
156 DepthFunction::NotEqual => glow::NOTEQUAL,
157 DepthFunction::GreaterEqual => glow::GEQUAL,
158 DepthFunction::Always => glow::ALWAYS,
159 }
160 }
161}
162
163#[derive(Debug, Copy, Clone, Eq, PartialEq)]
164pub enum CullFace {
165 Back,
166 Front,
167 FrontAndBack,
168}
169
170impl CullFace {
171 pub fn to_gl(&self) -> u32 {
172 match self {
173 CullFace::Back => glow::BACK,
174 CullFace::Front => glow::FRONT,
175 CullFace::FrontAndBack => glow::FRONT_AND_BACK,
176 }
177 }
178}
179
180pub enum Feature {
181 DepthTest(DepthFunction),
182 CullFace(CullFace, VertexWinding),
183}
184
185struct GLConstants {
186 max_vertex_attributes: usize,
187 max_texture_units: usize,
188}
189
190#[derive(Copy, Clone, Debug, Eq, PartialEq)]
191pub enum DrawMode {
192 Points,
193 Lines,
194 LineLoop,
195 LineStrip,
196 Triangles,
197 TriangleStrip,
198 TriangleFan,
199}
200
201#[derive(Copy, Clone, PartialEq, Eq)]
202pub struct TextureUnit {
203 index: u32,
204 gl: u32,
205}
206
207impl From<u32> for TextureUnit {
208 fn from(v: u32) -> Self {
209 let inner = match v {
210 0 => glow::TEXTURE0,
211 1 => glow::TEXTURE1,
212 2 => glow::TEXTURE2,
213 3 => glow::TEXTURE3,
214 4 => glow::TEXTURE4,
215 5 => glow::TEXTURE5,
216 6 => glow::TEXTURE6,
217 7 => glow::TEXTURE7,
218 8 => glow::TEXTURE8,
219 9 => glow::TEXTURE9,
220 10 => glow::TEXTURE10,
221 11 => glow::TEXTURE11,
222 12 => glow::TEXTURE12,
223 13 => glow::TEXTURE13,
224 14 => glow::TEXTURE14,
225 15 => glow::TEXTURE15,
226 16 => glow::TEXTURE16,
227 17 => glow::TEXTURE17,
228 18 => glow::TEXTURE18,
229 19 => glow::TEXTURE19,
230 20 => glow::TEXTURE20,
231 21 => glow::TEXTURE21,
232 22 => glow::TEXTURE22,
233 23 => glow::TEXTURE23,
234 24 => glow::TEXTURE24,
235 25 => glow::TEXTURE25,
236 26 => glow::TEXTURE26,
237 27 => glow::TEXTURE27,
238 28 => glow::TEXTURE28,
239 29 => glow::TEXTURE29,
240 30 => glow::TEXTURE30,
241 31 => glow::TEXTURE31,
242 _ => panic!("unsupported texture unit: {}", v),
243 };
244 TextureUnit {
245 index: v,
246 gl: inner,
247 }
248 }
249}
250
251impl From<i32> for TextureUnit {
252 fn from(v: i32) -> Self {
253 (v as u32).into()
254 }
255}
256
257impl From<usize> for TextureUnit {
258 fn from(v: usize) -> Self {
259 (v as u32).into()
260 }
261}
262
263#[derive(Copy, Clone, Default)]
264pub struct GLVersion {
265 major: u32,
266 minor: u32,
267 gles: bool,
268}
269
270impl FromStr for GLVersion {
271 type Err = ();
272
273 fn from_str(s: &str) -> Result<Self, Self::Err> {
274 let (major, minor, gles, webgl) = if s.starts_with("WebGL ") {
277 (s.chars().nth(6), s.chars().nth(8), true, true)
278 } else if s.contains("OpenGL ES ") {
279 (s.chars().nth(10), s.chars().nth(12), true, false)
280 } else {
281 (s.chars().next(), s.chars().nth(2), false, false)
282 };
283
284 let major_incr = if webgl { 1 } else { 0 };
287
288 let major = major.and_then(|c| c.to_digit(10));
289 let minor = minor.and_then(|c| c.to_digit(10));
290 match (major, minor) {
291 (Some(major), Some(minor)) => Ok(Self {
292 major: major + major_incr as u32,
293 minor: minor as u32,
294 gles,
295 }),
296 _ => Err(()),
297 }
298 }
299}
300
301impl Debug for GLVersion {
302 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
303 write!(
304 f,
305 "GLVersion {{ major: {}, minor: {}, ES: {} }}",
306 self.major, self.minor, self.gles
307 )
308 }
309}
310
311pub struct Context {
313 ctx: GLContext,
314 version: GLVersion,
315 gl_constants: GLConstants,
316 shaders: SlotMap<ShaderKey, GLProgram>,
317 active_shader: Option<ShaderKey>,
318 buffers: SlotMap<BufferKey, GLBuffer>,
319 active_buffers: [Option<BufferKey>; 2],
320 textures: SlotMap<TextureKey, GLTexture>,
321 bound_textures: Vec<Vec<Option<GLTexture>>>,
322 framebuffers: SlotMap<FramebufferKey, GLFramebuffer>,
323 active_framebuffer: [Option<FramebufferKey>; 2],
324 renderbuffers: SlotMap<RenderbufferKey, GLRenderbuffer>,
325 active_renderbuffer: Option<RenderbufferKey>,
326 current_texture_unit: TextureUnit,
327 current_viewport: viewport::Viewport<i32>,
328 current_scissor: Option<viewport::Viewport<i32>>,
329 enabled_attributes: u32, }
331
332impl Context {
333 pub fn new(ctx: GLContext) -> Self {
334 let gl_constants = GLConstants {
335 max_vertex_attributes: unsafe {
336 ctx.get_parameter_i32(glow::MAX_VERTEX_ATTRIBS) as usize
337 },
338 max_texture_units: unsafe {
339 ctx.get_parameter_i32(glow::MAX_COMBINED_TEXTURE_IMAGE_UNITS) as usize
340 },
341 };
342
343 let bound_textures = texture::TextureType::enumerate()
344 .iter()
345 .map(|_tt| vec![None; gl_constants.max_texture_units])
346 .collect();
347
348 for texture_unit in 0..gl_constants.max_texture_units {
349 unsafe {
350 ctx.active_texture(glow::TEXTURE0 + texture_unit as u32);
351 for texture_type in texture::TextureType::enumerate() {
353 if texture_type.is_supported() {
354 ctx.bind_texture(gl::texture::to_gl(*texture_type), None);
355 }
356 }
357 }
358 }
359 unsafe { ctx.active_texture(glow::TEXTURE0) }
360 unsafe {
361 ctx.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
363 ctx.enable(glow::BLEND);
364 ctx.blend_equation(glow::FUNC_ADD);
365 ctx.blend_func_separate(
366 glow::SRC_ALPHA,
367 glow::ONE_MINUS_SRC_ALPHA,
368 glow::ONE,
369 glow::ONE_MINUS_SRC_ALPHA,
370 );
371
372 ctx.bind_vertex_array(ctx.create_vertex_array().ok());
373 }
374
375 let version = {
376 let str_version = unsafe { ctx.get_parameter_string(glow::VERSION) };
377 str_version.parse::<GLVersion>().unwrap_or_default()
378 };
379
380 let mut ctx = Self {
381 ctx,
382 version,
383 gl_constants,
384 shaders: SlotMap::with_key(),
385 active_shader: None,
386 buffers: SlotMap::with_key(),
387 active_buffers: [None; 2],
388 textures: SlotMap::with_key(),
389 bound_textures,
390 framebuffers: SlotMap::with_key(),
391 active_framebuffer: [None; 2],
392 renderbuffers: SlotMap::with_key(),
393 active_renderbuffer: None,
394 current_texture_unit: 0.into(),
395 current_viewport: viewport::Viewport::default(),
396 current_scissor: None,
397 enabled_attributes: std::u32::MAX,
398 };
399 ctx.set_vertex_attributes(0, &[]);
400 ctx
401 }
402
403 pub fn enable(&mut self, feature: Feature) {
404 match feature {
405 Feature::DepthTest(func) => unsafe {
406 self.ctx.enable(glow::DEPTH_TEST);
407 self.ctx.depth_func(func.to_gl());
408 },
409 Feature::CullFace(cull_face, winding_order) => unsafe {
410 self.ctx.enable(glow::CULL_FACE);
411 self.ctx.cull_face(cull_face.to_gl());
412 self.ctx
413 .front_face(gl::vertex_winding::to_gl(winding_order));
414 },
415 }
416 }
417
418 pub fn disable(&mut self, feature: Feature) {
419 match feature {
420 Feature::DepthTest(_) => unsafe { self.ctx.disable(glow::DEPTH_TEST) },
421 Feature::CullFace(_, _) => unsafe { self.ctx.disable(glow::CULL_FACE) },
422 }
423 }
424
425 pub fn new_debug_group(&self, message: &str) -> DebugGroup {
426 DebugGroup::new(&self.ctx, message)
427 }
428
429 pub fn new_buffer(
430 &mut self,
431 size: usize,
432 buffer_type: buffer::BufferType,
433 usage: buffer::Usage,
434 initial_data: Option<&[u8]>,
435 ) -> Result<BufferKey, GraphicsError> {
436 let vbo = unsafe {
437 let vbo = self
438 .ctx
439 .create_buffer()
440 .map_err(|_| GraphicsError::BufferError)?;
441 self.ctx.bind_buffer(buffer_type.into(), Some(vbo));
442 if let Some(initial_data) = initial_data {
443 self.ctx
444 .buffer_data_u8_slice(buffer_type.into(), initial_data, usage.to_gl());
445 } else {
446 self.ctx
447 .buffer_data_size(buffer_type.into(), size as _, usage.to_gl());
448 }
449
450 vbo
451 };
452 let buffer_key = self.buffers.insert(vbo);
453 self.active_buffers[buffer_type_to_index(buffer_type)] = Some(buffer_key);
454 Ok(buffer_key)
455 }
456
457 pub fn destroy_buffer(&mut self, buffer: &buffer::Buffer) {
458 if let Some(gl_buffer) = self.buffers.remove(buffer.handle()) {
459 unsafe {
460 self.ctx.delete_buffer(gl_buffer);
461 }
462 }
463 }
464
465 pub fn bind_buffer(&mut self, buffer_key: BufferKey, buffer_type: buffer::BufferType) {
466 if let Some(&vbo) = self.buffers.get(buffer_key) {
467 let buffer_index = buffer_type_to_index(buffer_type);
468 match self.active_buffers.get_mut(buffer_index) {
469 Some(Some(active_buffer)) => {
470 if active_buffer != &buffer_key {
471 *active_buffer = buffer_key;
472 unsafe { self.ctx.bind_buffer(buffer_type.into(), Some(vbo)) };
473 }
474 }
475 _ => {
476 self.active_buffers[buffer_index] = Some(buffer_key);
477 unsafe { self.ctx.bind_buffer(buffer_type.into(), Some(vbo)) };
478 }
479 }
480 }
481 }
482
483 pub fn buffer_static_draw(&self, buffer: &buffer::Buffer, data: &[u8], offset: usize) {
484 let target = buffer.buffer_type().into();
485 unsafe {
486 self.ctx
487 .buffer_sub_data_u8_slice(target, offset as i32, data)
488 }
489 }
490
491 fn buffer_stream_draw(&self, map: &buffer::MappedBuffer) {
492 let buffer = map.inner();
493 let target = buffer.buffer_type().into();
494 let data = map.memory_map();
495
496 unsafe {
497 self.ctx
500 .buffer_data_size(target, buffer.size() as i32, buffer.usage().to_gl());
501 self.ctx.buffer_sub_data_u8_slice(target, 0, data);
502 }
503 }
504
505 pub fn unmap_buffer(&mut self, map: &buffer::MappedBuffer) {
506 let buffer = map.inner();
507 self.bind_buffer(buffer.handle(), buffer.buffer_type());
508 if self.buffers.get(buffer.handle()).is_some() {
509 if let Some(modified_range) = map.modified_range() {
510 let modified_offset =
511 std::cmp::min(modified_range.offset, buffer.size().saturating_sub(1));
512 let modified_size = std::cmp::min(
513 modified_range.size,
514 buffer.size().saturating_sub(modified_range.offset),
515 );
516 match buffer.usage() {
517 buffer::Usage::Stream => self.buffer_stream_draw(map),
518 buffer::Usage::Static => self.buffer_static_draw(
519 buffer,
520 &map.memory_map()[modified_offset..(modified_size + modified_offset)],
521 modified_offset,
522 ),
523 buffer::Usage::Dynamic => {
524 if modified_size >= buffer.size() / 3 {
525 self.buffer_stream_draw(map);
526 } else {
527 self.buffer_static_draw(
528 buffer,
529 &map.memory_map()
530 [modified_offset..(modified_size + modified_offset)],
531 modified_offset,
532 );
533 }
534 }
535 }
536 }
537 } else {
538 log::warn!(
539 "attempted to unmap non-existant buffer, {:?}",
540 buffer.handle()
541 );
542 }
543 }
544
545 pub fn new_shader(
546 &mut self,
547 vertex_source: &str,
548 fragment_source: &str,
549 ) -> Result<ShaderKey, shader::ShaderError> {
550 use shader::*;
551 let program = unsafe {
552 let gl = &self.ctx;
553 let vertex = gl
554 .create_shader(glow::VERTEX_SHADER)
555 .map_err(|_| ShaderError::ResourceCreationError)?;
556 gl.shader_source(vertex, vertex_source);
557 gl.compile_shader(vertex);
558 if !gl.get_shader_compile_status(vertex) {
559 let err = Err(ShaderError::VertexCompileError(
560 gl.get_shader_info_log(vertex),
561 ));
562 gl.delete_shader(vertex);
563 return err;
564 }
565 let fragment = gl
566 .create_shader(glow::FRAGMENT_SHADER)
567 .expect("Failed to create Fragment shader.");
568 gl.shader_source(fragment, fragment_source);
569 gl.compile_shader(fragment);
570 if !gl.get_shader_compile_status(fragment) {
571 let err = Err(ShaderError::FragmentCompileError(
572 gl.get_shader_info_log(fragment),
573 ));
574 gl.delete_shader(fragment);
575 return err;
576 }
577 let program = gl.create_program().expect("Failed to create program.");
578 gl.attach_shader(program, vertex);
579 gl.attach_shader(program, fragment);
580 gl.link_program(program);
581 if !gl.get_program_link_status(program) {
582 let err = Err(ShaderError::LinkError(gl.get_program_info_log(program)));
583 gl.delete_program(program);
584 return err;
585 }
586
587 program
588 };
589
590 Ok(self.shaders.insert(program))
591 }
592
593 pub fn get_shader_attributes(&self, shader: ShaderKey) -> Vec<shader::Attribute> {
594 if let Some(program) = self.shaders.get(shader).cloned() {
595 let count = unsafe { self.ctx.get_active_attributes(program) };
596 let mut attributes = Vec::with_capacity(count as usize);
597 for index in 0..count {
598 unsafe {
599 let glow::ActiveAttribute { name, size, atype } =
600 self.ctx.get_active_attribute(program, index).unwrap();
601 if let Some(location) = self.ctx.get_attrib_location(program, name.as_str()) {
602 attributes.push(shader::Attribute {
604 name,
605 size,
606 atype: gl::attribute::from_gl(atype),
607 location,
608 });
609 }
610 }
611 }
612 attributes.sort_by(|a, b| a.location.partial_cmp(&b.location).unwrap());
613 attributes
614 } else {
615 Vec::new()
616 }
617 }
618
619 pub fn get_shader_uniforms(&self, shader: ShaderKey) -> Vec<shader::Uniform> {
620 unsafe fn get_initial_uniform_data(
621 gl: &glow::Context,
622 utype: u32,
623 program: GLProgram,
624 location: &GLUniformLocation,
625 ) -> shader::RawUniformValue {
626 use shader::RawUniformValue;
627 macro_rules! get_uniform_data {
628 (f32, 1, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
629 let mut data = [0.; 1];
630 $gl.get_uniform_f32($program, $location, &mut data);
631 RawUniformValue::$uni_ty(data[0].into())
632 }};
633 (i32, 1, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
634 let mut data = [0; 1];
635 $gl.get_uniform_i32($program, $location, &mut data);
636 RawUniformValue::$uni_ty(data[0].into())
637 }};
638 (f32, $data_size:expr, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
639 let mut data = [0.; $data_size];
640 $gl.get_uniform_f32($program, $location, &mut data);
641 RawUniformValue::$uni_ty(data.into())
642 }};
643 (i32, $data_size:expr, $uni_ty:ident, $gl:expr, $program:expr, $location:expr) => {{
644 let mut data = [0; $data_size];
645 $gl.get_uniform_i32($program, $location, &mut data);
646 RawUniformValue::$uni_ty(data.into())
647 }};
648 }
649
650 match utype {
651 glow::FLOAT => get_uniform_data!(f32, 1, Float, gl, program, location),
652 glow::FLOAT_VEC2 => get_uniform_data!(f32, 2, Vec2, gl, program, location),
653 glow::FLOAT_VEC3 => get_uniform_data!(f32, 3, Vec3, gl, program, location),
654 glow::FLOAT_VEC4 => get_uniform_data!(f32, 4, Vec4, gl, program, location),
655 glow::FLOAT_MAT2 => get_uniform_data!(f32, 4, Mat2, gl, program, location),
656 glow::FLOAT_MAT3 => get_uniform_data!(f32, 9, Mat3, gl, program, location),
657 glow::FLOAT_MAT4 => get_uniform_data!(f32, 16, Mat4, gl, program, location),
658 glow::INT | glow::SAMPLER_2D | glow::SAMPLER_CUBE => {
659 get_uniform_data!(i32, 1, SignedInt, gl, program, location)
660 }
661 glow::INT_VEC2 => get_uniform_data!(i32, 2, IntVec2, gl, program, location),
662 glow::INT_VEC3 => get_uniform_data!(i32, 3, IntVec3, gl, program, location),
663 glow::INT_VEC4 => get_uniform_data!(i32, 4, IntVec4, gl, program, location),
664 _ => {
665 panic!("failed to match uniform type");
666 }
667 }
668 }
669
670 use shader::{Uniform, UniformLocation};
671 let gl = &self.ctx;
672 if let Some(program) = self.shaders.get(shader).cloned() {
673 let count = unsafe { gl.get_active_uniforms(program) };
674 let mut uniforms = Vec::with_capacity(count as usize);
675 for index in 0..count {
676 unsafe {
677 let glow::ActiveUniform { name, size, utype } =
678 gl.get_active_uniform(program, index).unwrap();
679 if size > 1 {
680 let name = name.trim_end_matches("[0]");
681 uniforms.extend((0..size).filter_map(|i| {
682 let name = format!("{}[{}]", name, i);
683 gl.get_uniform_location(program, name.as_str())
684 .map(|location| {
685 let initial_data =
686 get_initial_uniform_data(&gl, utype, program, &location);
687 let location = UniformLocation(location);
688 Uniform {
689 name,
690 size: 1,
691 utype,
692 location,
693 initial_data,
694 }
695 })
696 }));
697 } else {
698 if let Some(location) = gl.get_uniform_location(program, name.as_str()) {
699 let initial_data =
700 get_initial_uniform_data(&gl, utype, program, &location);
701 let location = UniformLocation(location);
702 uniforms.push(Uniform {
703 name,
704 size,
705 utype,
706 location,
707 initial_data,
708 });
709 }
710 }
711 }
712 }
713 uniforms
714 } else {
715 Default::default()
716 }
717 }
718
719 pub fn destroy_shader(&mut self, shader: ShaderKey) {
720 match self.shaders.remove(shader) {
721 None => (),
722 Some(shader) => unsafe {
723 self.ctx.delete_program(shader);
724 },
725 }
726 }
727
728 pub fn use_shader<S: shader::Shader + ?Sized>(&mut self, shader: Option<&S>) {
729 match shader {
730 None => {
731 if self.active_shader.is_some() {
732 self.active_shader = None;
733 unsafe {
734 self.ctx.use_program(None);
735 }
736 }
737 }
738 Some(shader) => {
739 if self.active_shader != Some(shader.handle()) {
740 match self.shaders.get(shader.handle()) {
741 None => log::warn!(
742 "Attempting to bind shader not in cache: {:?}",
743 shader.handle()
744 ),
745 Some(gl_shader) => {
746 self.active_shader = Some(shader.handle());
747 unsafe { self.ctx.use_program(Some(*gl_shader)) }
748 }
749 }
750 }
751 }
752 }
753 }
754
755 pub fn new_texture(
756 &mut self,
757 texture_type: texture::TextureType,
758 ) -> Result<TextureKey, GraphicsError> {
759 unsafe {
760 let handle = self
761 .ctx
762 .create_texture()
763 .map_err(|_| GraphicsError::TextureError)?;
764 let texture = self.textures.insert(handle);
765 self.ctx.active_texture(glow::TEXTURE0);
766 self.bind_texture_to_unit(texture_type, texture, 0.into());
767 Ok(texture)
768 }
769 }
770
771 pub fn destroy_texture(&mut self, texture_key: TextureKey) {
772 match self.textures.remove(texture_key) {
773 None => (),
774 Some(texture) => unsafe { self.ctx.delete_texture(texture) },
775 }
776 }
777
778 pub fn bind_texture_to_unit(
779 &mut self,
780 texture_type: texture::TextureType,
781 texture_key: TextureKey,
782 texture_unit: TextureUnit,
783 ) {
784 let TextureUnit { index, gl: unit } = texture_unit;
785 let texture_unit_index = index as usize;
786 match (
787 self.textures.get(texture_key),
788 self.bound_textures[texture_type.to_index()][texture_unit_index],
789 ) {
790 (Some(&texture), None) => {
791 if texture_unit != self.current_texture_unit {
792 unsafe {
793 self.ctx.active_texture(unit);
794 }
795 self.current_texture_unit = texture_unit;
796 }
797 self.bound_textures[texture_type.to_index()][texture_unit_index] = Some(texture);
798 unsafe {
799 self.ctx
800 .bind_texture(gl::texture::to_gl(texture_type), Some(texture))
801 }
802 }
803 (Some(&texture), Some(bound_texture)) => {
804 if texture != bound_texture {
805 if texture_unit != self.current_texture_unit {
806 unsafe {
807 self.ctx.active_texture(unit);
808 }
809 self.current_texture_unit = texture_unit;
810 }
811 self.bound_textures[texture_type.to_index()][texture_unit_index] =
812 Some(texture);
813 unsafe {
814 self.ctx
815 .bind_texture(gl::texture::to_gl(texture_type), Some(texture))
816 }
817 }
818 }
819 (None, Some(_)) => {
820 if texture_unit != self.current_texture_unit {
821 unsafe {
822 self.ctx.active_texture(unit);
823 }
824 self.current_texture_unit = texture_unit;
825 }
826 self.bound_textures[texture_type.to_index()][texture_unit_index] = None;
827 unsafe {
828 self.ctx
829 .bind_texture(gl::texture::to_gl(texture_type), None)
830 }
831 }
832 (None, None) => (),
833 }
834 }
835
836 pub fn new_framebuffer(&mut self) -> Result<FramebufferKey, GraphicsError> {
837 let framebuffer = unsafe {
838 self.ctx
839 .create_framebuffer()
840 .map_err(|_| GraphicsError::FramebufferError)?
841 };
842 Ok(self.framebuffers.insert(framebuffer))
843 }
844
845 pub fn destroy_framebuffer(&mut self, framebuffer_key: FramebufferKey) {
846 match self.framebuffers.remove(framebuffer_key) {
847 None => (),
848 Some(framebuffer) => unsafe { self.ctx.delete_framebuffer(framebuffer) },
849 }
850 }
851
852 pub fn bind_framebuffer(
853 &mut self,
854 target: canvas::Target,
855 framebuffer_key: Option<FramebufferKey>,
856 ) {
857 let target_index = target_to_index(target);
858 match (framebuffer_key, self.active_framebuffer[target_index]) {
859 (None, None) => (),
860 (Some(framebuffer_key), None) => match self.framebuffers.get(framebuffer_key) {
861 None => (),
862 Some(framebuffer) => {
863 self.active_framebuffer[target_index] = Some(framebuffer_key);
864 unsafe {
865 self.ctx
866 .bind_framebuffer(target.to_gl(), Some(*framebuffer))
867 }
868 }
869 },
870 (Some(framebuffer_key), Some(current_framebuffer_key)) => {
871 if framebuffer_key != current_framebuffer_key {
872 match self.framebuffers.get(framebuffer_key) {
873 None => (),
874 Some(framebuffer) => {
875 self.active_framebuffer[target_index] = Some(framebuffer_key);
876 unsafe {
877 self.ctx
878 .bind_framebuffer(target.to_gl(), Some(*framebuffer))
879 }
880 }
881 }
882 }
883 }
884 (None, Some(_current_framebuffer_key)) => {
885 self.active_framebuffer[target_index] = None;
886 unsafe { self.ctx.bind_framebuffer(target.to_gl(), None) }
887 }
888 }
889 }
890
891 pub fn check_framebuffer_status(&self, target: canvas::Target) -> canvas::Status {
892 match unsafe { self.ctx.check_framebuffer_status(target.to_gl()) } {
893 glow::FRAMEBUFFER_COMPLETE => canvas::Status::Complete,
894 glow::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => canvas::Status::IncompleteAttachment,
895 glow::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => canvas::Status::MissingAttachment,
896 glow::FRAMEBUFFER_UNSUPPORTED => canvas::Status::Unsupported,
897 glow::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => canvas::Status::IncompleteMultisample,
898 _ => canvas::Status::Unknown,
899 }
900 }
901
902 pub fn get_active_framebuffer(&self, target: canvas::Target) -> Option<FramebufferKey> {
903 self.active_framebuffer[target_to_index(target)]
904 }
905
906 pub fn framebuffer_texture(
907 &mut self,
908 target: canvas::Target,
909 attachment: canvas::Attachment,
910 texture_type: texture::TextureType,
911 texture_key: TextureKey,
912 level: u32,
913 ) {
914 unsafe {
915 self.ctx.framebuffer_texture_2d(
916 target.to_gl(),
917 attachment.to_gl(),
918 gl::texture::to_gl(texture_type),
919 self.textures.get(texture_key).copied(),
920 level as i32,
921 )
922 }
923 }
924
925 pub fn new_renderbuffer(&mut self) -> Result<RenderbufferKey, GraphicsError> {
926 let renderbuffer = unsafe {
927 self.ctx
928 .create_renderbuffer()
929 .map_err(|_| GraphicsError::RenderbufferError)?
930 };
931 Ok(self.renderbuffers.insert(renderbuffer))
932 }
933
934 pub fn bind_renderbuffer(&mut self, renderbuffer: Option<RenderbufferKey>) {
935 if self.active_renderbuffer != renderbuffer {
936 self.active_renderbuffer = renderbuffer;
937 let gl_renderbuffer =
938 renderbuffer.and_then(|renderbuffer| self.renderbuffers.get(renderbuffer).cloned());
939 unsafe {
940 self.ctx
941 .bind_renderbuffer(glow::RENDERBUFFER, gl_renderbuffer);
942 }
943 }
944 }
945
946 pub fn renderbuffer_storage(&mut self, format: PixelFormat, width: i32, height: i32) {
947 let gl_format = gl::pixel_format::to_gl(format, &self.version, true);
948 unsafe {
949 self.ctx
950 .renderbuffer_storage(glow::RENDERBUFFER, gl_format.internal, width, height)
951 }
952 }
953
954 pub fn framebuffer_renderbuffer(
955 &mut self,
956 attachment: canvas::Attachment,
957 renderbuffer: Option<RenderbufferKey>,
958 ) {
959 let gl_renderbuffer =
960 renderbuffer.and_then(|renderbuffer| self.renderbuffers.get(renderbuffer).cloned());
961 unsafe {
962 self.ctx.framebuffer_renderbuffer(
963 glow::FRAMEBUFFER,
964 attachment.to_gl(),
965 glow::RENDERBUFFER,
966 gl_renderbuffer,
967 )
968 }
969 }
970
971 pub fn destroy_renderbuffer(&mut self, renderbuffer_key: RenderbufferKey) {
972 match self.renderbuffers.remove(renderbuffer_key) {
973 None => (),
974 Some(renderbuffer) => unsafe { self.ctx.delete_renderbuffer(renderbuffer) },
975 }
976 }
977
978 pub fn set_vertex_attributes(
979 &mut self,
980 desired: u32,
981 binding_info: &[Option<mesh::BindingInfo>],
982 ) {
983 let diff = desired ^ self.enabled_attributes;
984 for i in 0..self.gl_constants.max_vertex_attributes as u32 {
985 let bit = 1 << i;
986
987 if diff & bit != 0 {
988 if desired & bit != 0 {
989 unsafe {
990 self.ctx.enable_vertex_attrib_array(i);
991 }
992 } else {
993 unsafe {
994 self.ctx.disable_vertex_attrib_array(i);
995 }
996 }
997 }
998
999 if desired & bit != 0 {
1000 let (vertex_format, stride, step, buffer_key, buffer_type) =
1001 binding_info[i as usize].unwrap();
1002 self.bind_buffer(buffer_key, buffer_type);
1003 let (data_type, elements_count, _instances_count) = vertex_format.atype.to_gl();
1004 unsafe {
1005 self.ctx.vertex_attrib_divisor(i, step);
1006 use vertex::AttributeType;
1007 match vertex_format.atype {
1008 AttributeType::F32
1009 | AttributeType::F32F32
1010 | AttributeType::F32F32F32
1011 | AttributeType::F32F32F32F32
1012 | AttributeType::F32x2x2
1013 | AttributeType::F32x3x3
1014 | AttributeType::F32x4x4 => self.ctx.vertex_attrib_pointer_f32(
1015 i,
1016 elements_count,
1017 data_type,
1018 vertex_format.normalize,
1019 stride as i32,
1020 vertex_format.offset as i32,
1021 ),
1022 AttributeType::I32
1023 | AttributeType::I32I32
1024 | AttributeType::I32I32I32
1025 | AttributeType::I32I32I32I32 => self.ctx.vertex_attrib_pointer_i32(
1026 i,
1027 elements_count,
1028 data_type,
1029 stride as i32,
1030 vertex_format.offset as i32,
1031 ),
1032 }
1033 }
1034 }
1035 }
1036
1037 self.enabled_attributes = desired;
1038 }
1039
1040 pub fn set_uniform_by_location(
1041 &self,
1042 location: &shader::UniformLocation,
1043 data: &shader::RawUniformValue,
1044 ) {
1045 assert!(
1046 self.active_shader.is_some(),
1047 "Setting a uniform without an active shader."
1048 );
1049 use shader::RawUniformValue;
1050 let location = Some(&location.0);
1051 unsafe {
1052 match data {
1053 RawUniformValue::SignedInt(data) => self.ctx.uniform_1_i32(location, *data),
1054 RawUniformValue::Float(data) => self.ctx.uniform_1_f32(location, *data),
1055 RawUniformValue::Mat2(data) => self.ctx.uniform_matrix_2_f32_slice(
1056 location,
1057 false,
1058 &AsRef::<[f32; 4]>::as_ref(data)[..],
1059 ),
1060 RawUniformValue::Mat3(data) => self.ctx.uniform_matrix_3_f32_slice(
1061 location,
1062 false,
1063 &AsRef::<[f32; 9]>::as_ref(data)[..],
1064 ),
1065 RawUniformValue::Mat4(data) => self.ctx.uniform_matrix_4_f32_slice(
1066 location,
1067 false,
1068 &AsRef::<[f32; 16]>::as_ref(data)[..],
1069 ),
1070 RawUniformValue::Vec2(data) => {
1071 self.ctx.uniform_2_f32_slice(location, data.as_ref())
1072 }
1073 RawUniformValue::Vec3(data) => {
1074 self.ctx.uniform_3_f32_slice(location, data.as_ref())
1075 }
1076 RawUniformValue::Vec4(data) => {
1077 self.ctx.uniform_4_f32_slice(location, data.as_ref())
1078 }
1079 RawUniformValue::IntVec2(data) => {
1080 self.ctx.uniform_2_i32_slice(location, data.as_ref())
1081 }
1082 RawUniformValue::IntVec3(data) => {
1083 self.ctx.uniform_3_i32_slice(location, data.as_ref())
1084 }
1085 RawUniformValue::IntVec4(data) => {
1086 self.ctx.uniform_4_i32_slice(location, data.as_ref())
1087 }
1088 }
1089 }
1090 }
1091
1092 pub fn draw_arrays(&self, mode: DrawMode, first: i32, count: i32) {
1093 unsafe {
1094 self.ctx
1095 .draw_arrays(gl::draw_mode::to_gl(mode), first, count);
1096 }
1097 }
1098
1099 pub fn draw_elements(&self, mode: DrawMode, count: i32, element_type: u32, offset: i32) {
1100 unsafe {
1101 self.ctx
1102 .draw_elements(gl::draw_mode::to_gl(mode), count, element_type, offset);
1103 }
1104 }
1105
1106 pub fn draw_arrays_instanced(
1107 &self,
1108 mode: DrawMode,
1109 first: i32,
1110 count: i32,
1111 instance_count: i32,
1112 ) {
1113 unsafe {
1114 self.ctx
1115 .draw_arrays_instanced(gl::draw_mode::to_gl(mode), first, count, instance_count)
1116 }
1117 }
1118
1119 pub fn draw_elements_instanced(
1120 &self,
1121 mode: DrawMode,
1122 count: i32,
1123 element_type: u32,
1124 offset: i32,
1125 instance_count: i32,
1126 ) {
1127 unsafe {
1128 self.ctx.draw_elements_instanced(
1129 gl::draw_mode::to_gl(mode),
1130 count,
1131 element_type as u32,
1132 offset,
1133 instance_count,
1134 )
1135 }
1136 }
1137
1138 pub fn set_viewport(&mut self, x: i32, y: i32, width: i32, height: i32) {
1139 let new_viewport = viewport::Viewport::new(x, y, width, height);
1140 if self.current_viewport != new_viewport {
1141 self.current_viewport = new_viewport;
1142 unsafe { self.ctx.viewport(x, y, width, height) }
1143 }
1144 }
1145
1146 pub fn viewport(&self) -> viewport::Viewport<i32> {
1147 self.current_viewport
1148 }
1149
1150 pub fn set_scissor(&mut self, region: Option<viewport::Viewport<i32>>) {
1151 match (region, &mut self.current_scissor) {
1152 (None, Some(_current)) => {
1153 unsafe {
1154 self.ctx.disable(glow::SCISSOR_TEST);
1155 }
1156 self.current_scissor = None;
1157 }
1158 (Some(new), None) => {
1159 unsafe {
1160 self.ctx.enable(glow::SCISSOR_TEST);
1161 self.ctx
1162 .scissor(new.x(), new.y(), new.width(), new.height());
1163 }
1164 self.current_scissor = Some(new);
1165 }
1166 (Some(new), Some(current)) => {
1167 if &new != current {
1168 unsafe {
1169 self.ctx
1170 .scissor(new.x(), new.y(), new.width(), new.height());
1171 }
1172 *current = new;
1173 }
1174 }
1175 (None, None) => {}
1176 }
1177 }
1178
1179 pub fn clear_color(&self, red: f32, green: f32, blue: f32, alpha: f32) {
1180 unsafe { self.ctx.clear_color(red, green, blue, alpha) }
1181 }
1182
1183 pub fn clear(&self) {
1184 unsafe {
1185 self.ctx
1186 .clear(glow::COLOR_BUFFER_BIT | glow::DEPTH_BUFFER_BIT | glow::STENCIL_BUFFER_BIT);
1187 }
1188 }
1189
1190 pub fn read_pixels(
1191 &self,
1192 x: i32,
1193 y: i32,
1194 width: i32,
1195 height: i32,
1196 format: PixelFormat,
1197 data: &mut [u8],
1198 ) {
1199 let gl::TextureFormat { external, ty, .. } =
1200 gl::pixel_format::to_gl(format, &self.version, false);
1201 unsafe {
1202 self.ctx.read_pixels(
1203 x,
1204 y,
1205 width,
1206 height,
1207 external,
1208 ty,
1209 glow::PixelPackData::Slice(data),
1210 )
1211 }
1212 }
1213
1214 pub fn debug_message_callback<F>(&self, mut callback: F)
1215 where
1216 F: FnMut(DebugSource, DebugType, u32, DebugSeverity, &str),
1217 {
1218 if self.ctx.supports_debug() {
1219 unsafe {
1220 self.ctx.enable(glow::DEBUG_OUTPUT);
1221 self.ctx
1222 .debug_message_callback(|source, event_type, id, severity, msg| {
1223 let source = match source {
1224 glow::DEBUG_SOURCE_API => DebugSource::API,
1225 glow::DEBUG_SOURCE_WINDOW_SYSTEM => DebugSource::WindowSystem,
1226 glow::DEBUG_SOURCE_SHADER_COMPILER => DebugSource::ShaderCompiler,
1227 glow::DEBUG_SOURCE_THIRD_PARTY => DebugSource::ThirdParty,
1228 glow::DEBUG_SOURCE_APPLICATION => DebugSource::Application,
1229 glow::DEBUG_SOURCE_OTHER => DebugSource::Other,
1230 _ => DebugSource::Other,
1231 };
1232
1233 let event_type = match event_type {
1234 glow::DEBUG_TYPE_ERROR => DebugType::Error,
1235 glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => DebugType::DeprecatedBehavior,
1236 glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => DebugType::DeprecatedBehavior,
1237 glow::DEBUG_TYPE_PORTABILITY => DebugType::Portability,
1238 glow::DEBUG_TYPE_PERFORMANCE => DebugType::Performance,
1239 glow::DEBUG_TYPE_MARKER => DebugType::Marker,
1240 glow::DEBUG_TYPE_PUSH_GROUP => DebugType::PushGroup,
1241 glow::DEBUG_TYPE_POP_GROUP => DebugType::PopGroup,
1242 glow::DEBUG_TYPE_OTHER => DebugType::Other,
1243 _ => DebugType::Other,
1244 };
1245
1246 let severity = match severity {
1247 glow::DEBUG_SEVERITY_HIGH => DebugSeverity::High,
1248 glow::DEBUG_SEVERITY_MEDIUM => DebugSeverity::Medium,
1249 glow::DEBUG_SEVERITY_LOW => DebugSeverity::Low,
1250 glow::DEBUG_SEVERITY_NOTIFICATION => DebugSeverity::Notification,
1251 _ => DebugSeverity::Notification,
1252 };
1253
1254 callback(source, event_type, id, severity, msg)
1255 });
1256 }
1257 }
1258 }
1259}
1260
1261#[derive(Debug)]
1262pub enum DebugSeverity {
1263 High,
1264 Medium,
1265 Low,
1266 Notification,
1267}
1268
1269#[derive(Debug)]
1270pub enum DebugType {
1271 Error,
1272 DeprecatedBehavior,
1273 UndefinedBehavior,
1274 Portability,
1275 Performance,
1276 Marker,
1277 PushGroup,
1278 PopGroup,
1279 Other,
1280}
1281
1282#[derive(Debug)]
1283pub enum DebugSource {
1284 API,
1285 WindowSystem,
1286 ShaderCompiler,
1287 ThirdParty,
1288 Application,
1289 Other,
1290}
1291
1292impl texture::TextureUpdate for Context {
1293 fn set_texture_sub_data(
1294 &mut self,
1295 texture_key: TextureKey,
1296 texture: texture::TextureInfo,
1297 texture_type: texture::TextureType,
1298 data: &[u8],
1299 x_offset: u32,
1300 y_offset: u32,
1301 ) {
1302 let gl::TextureFormat { external, ty, .. } =
1303 gl::pixel_format::to_gl(texture.get_format(), &self.version, false);
1304 let width = texture.width();
1305 let height = texture.height();
1306 let gl_target = gl::texture::to_gl(texture_type);
1307 self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1308 unsafe {
1309 self.ctx.tex_sub_image_2d(
1310 gl_target,
1311 0,
1312 x_offset as i32,
1313 y_offset as i32,
1314 width as i32,
1315 height as i32,
1316 external,
1317 ty,
1318 glow::PixelUnpackData::Slice(data),
1319 );
1320 if texture.mipmaps() {
1321 self.ctx.generate_mipmap(gl_target);
1322 }
1323 }
1324 }
1325
1326 fn set_texture_data(
1327 &mut self,
1328 texture_key: TextureKey,
1329 texture: texture::TextureInfo,
1330 texture_type: texture::TextureType,
1331 data: Option<&[u8]>,
1332 ) {
1333 let gl::TextureFormat {
1334 internal,
1335 external,
1336 ty,
1337 swizzle,
1338 } = gl::pixel_format::to_gl(texture.get_format(), &self.version, false);
1339 let width = texture.width();
1340 let height = texture.height();
1341 let gl_target = gl::texture::to_gl(texture_type);
1342 self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1343 unsafe {
1344 if let Some(swizzle) = swizzle {
1345 self.ctx
1346 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_R, swizzle[0]);
1347 self.ctx
1348 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_G, swizzle[1]);
1349 self.ctx
1350 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_B, swizzle[2]);
1351 self.ctx
1352 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_A, swizzle[3]);
1353 }
1354 self.ctx.tex_image_2d(
1355 gl_target,
1356 0,
1357 internal as i32,
1358 width as i32,
1359 height as i32,
1360 0,
1361 external,
1362 ty,
1363 data,
1364 );
1365 if texture.mipmaps() {
1366 self.ctx.generate_mipmap(gl_target);
1367 }
1368 }
1369 }
1370
1371 #[cfg(target_arch = "wasm32")]
1372 fn set_texture_data_with_html_image<T: texture::Texture>(
1373 &mut self,
1374 texture: T,
1375 data: &web_sys::HtmlImageElement,
1376 ) {
1377 let texture_info = texture.get_texture_info();
1378 let gl::TextureFormat {
1379 internal,
1380 external,
1381 ty,
1382 swizzle,
1383 } = gl::pixel_format::to_gl(texture_info.get_format(), &self.version, false);
1384 let gl_target = gl::texture::to_gl(texture.get_texture_type());
1385 self.bind_texture_to_unit(
1386 texture.get_texture_type(),
1387 texture.get_texture_key(),
1388 0.into(),
1389 );
1390 unsafe {
1391 if let Some(swizzle) = swizzle {
1392 self.ctx
1393 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_R, swizzle[0]);
1394 self.ctx
1395 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_G, swizzle[1]);
1396 self.ctx
1397 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_B, swizzle[2]);
1398 self.ctx
1399 .tex_parameter_i32(gl_target, glow::TEXTURE_SWIZZLE_A, swizzle[3]);
1400 }
1401 self.ctx.tex_image_2d_with_html_image(
1402 gl_target,
1403 0,
1404 internal as i32,
1405 external,
1406 ty,
1407 data,
1408 );
1409 if texture_info.mipmaps() {
1410 self.ctx.generate_mipmap(gl_target);
1411 }
1412 }
1413 }
1414
1415 fn set_texture_wrap(
1416 &mut self,
1417 texture_key: TextureKey,
1418 texture_type: texture::TextureType,
1419 wrap: texture::Wrap,
1420 ) {
1421 let gl_target = gl::texture::to_gl(texture_type);
1422 unsafe {
1423 self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1424 self.ctx.tex_parameter_i32(
1425 gl_target,
1426 glow::TEXTURE_WRAP_S,
1427 gl::wrap_mode::to_gl(wrap.s()) as i32,
1428 );
1429 self.ctx.tex_parameter_i32(
1430 gl_target,
1431 glow::TEXTURE_WRAP_T,
1432 gl::wrap_mode::to_gl(wrap.t()) as i32,
1433 );
1434 use texture::TextureType;
1435 match texture_type {
1436 TextureType::Tex2D | TextureType::Tex2DArray | TextureType::Cube => (),
1437 TextureType::Volume => self.ctx.tex_parameter_i32(
1438 gl_target,
1439 glow::TEXTURE_WRAP_R,
1440 gl::wrap_mode::to_gl(wrap.r()) as i32,
1441 ),
1442 }
1443 }
1444 }
1445
1446 fn set_texture_filter(
1447 &mut self,
1448 texture_key: TextureKey,
1449 texture_type: texture::TextureType,
1450 filter: texture::Filter,
1451 ) {
1452 use texture::FilterMode;
1453
1454 let gl_min = match filter.min() {
1455 FilterMode::Nearest => glow::NEAREST,
1456 FilterMode::Linear | FilterMode::None => glow::LINEAR,
1457 };
1458 let gl_mag = match filter.mag() {
1459 FilterMode::Nearest => glow::NEAREST,
1460 FilterMode::Linear | FilterMode::None => glow::LINEAR,
1461 };
1462
1463 let gl_min = match filter.mipmap() {
1464 FilterMode::None => gl_min,
1465 FilterMode::Nearest | FilterMode::Linear => match (filter.min(), filter.mipmap()) {
1466 (FilterMode::Nearest, FilterMode::Nearest) => glow::NEAREST_MIPMAP_NEAREST,
1467 (FilterMode::Nearest, FilterMode::Linear) => glow::NEAREST_MIPMAP_LINEAR,
1468 (FilterMode::Linear, FilterMode::Nearest) => glow::LINEAR_MIPMAP_NEAREST,
1469 (FilterMode::Linear, FilterMode::Linear) => glow::LINEAR_MIPMAP_LINEAR,
1470 _ => glow::LINEAR,
1471 },
1472 };
1473
1474 let gl_target = gl::texture::to_gl(texture_type);
1475 unsafe {
1476 self.bind_texture_to_unit(texture_type, texture_key, 0.into());
1477 self.ctx
1478 .tex_parameter_i32(gl_target, glow::TEXTURE_MIN_FILTER, gl_min as i32);
1479 self.ctx
1480 .tex_parameter_i32(gl_target, glow::TEXTURE_MAG_FILTER, gl_mag as i32);
1481 }
1482 }
1483}
1484
1485impl Drop for Context {
1486 fn drop(&mut self) {
1487 for (_, shader) in self.shaders.drain() {
1488 unsafe {
1489 self.ctx.delete_program(shader);
1490 }
1491 }
1492
1493 for (_, buffer) in self.buffers.drain() {
1494 unsafe { self.ctx.delete_buffer(buffer) }
1495 }
1496 }
1497}
1498
1499pub trait Renderer {
1500 fn clear(&mut self, settings: ClearSettings);
1501 fn draw<S, M>(&mut self, shader: &S, geometry: &Geometry<M>, settings: PipelineSettings)
1502 where
1503 S: shader::Shader + ?Sized,
1504 M: mesh::Mesh;
1505}
1506
1507impl Renderer for Context {
1508 fn clear(&mut self, settings: ClearSettings) {
1509 let ClearSettings {
1510 color,
1511 depth,
1512 stencil,
1513 target,
1514 scissor,
1515 } = settings;
1516 self.set_scissor(scissor);
1517
1518 let mut clear_bits = 0;
1519
1520 if let Some(color) = color {
1521 let Color::<f32> {
1522 red,
1523 blue,
1524 green,
1525 alpha,
1526 } = color.into();
1527 unsafe {
1528 self.ctx.clear_color(red, green, blue, alpha);
1529 }
1530 clear_bits |= glow::COLOR_BUFFER_BIT;
1531 }
1532
1533 if let Some(depth) = depth {
1534 unsafe {
1535 self.ctx.clear_depth_f32(depth.0);
1536 }
1537 clear_bits |= glow::DEPTH_BUFFER_BIT;
1538 }
1539
1540 if let Some(stencil) = stencil {
1541 unsafe {
1542 self.ctx.clear_stencil(stencil);
1543 }
1544 clear_bits |= glow::STENCIL_BUFFER_BIT;
1545 }
1546
1547 self.bind_framebuffer(
1548 canvas::Target::All,
1549 target.map(canvas::Canvas::get_framebuffer_key),
1550 );
1551 unsafe {
1552 self.ctx.clear(clear_bits);
1553 }
1554 }
1555
1556 fn draw<S, M>(&mut self, shader: &S, geometry: &Geometry<M>, settings: PipelineSettings)
1557 where
1558 S: shader::Shader + ?Sized,
1559 M: mesh::Mesh,
1560 {
1561 self.use_shader(Some(shader));
1562
1563 if let Some(depth_state) = settings.depth_state {
1564 self.enable(Feature::DepthTest(depth_state.function));
1565 } else {
1566 self.disable(Feature::DepthTest(DepthFunction::Never));
1567 }
1568 self.set_scissor(settings.scissor_state);
1569
1570 self.bind_framebuffer(
1571 canvas::Target::All,
1572 settings
1573 .framebuffer
1574 .map(canvas::Canvas::get_framebuffer_key),
1575 );
1576
1577 let Geometry {
1578 mesh,
1579 draw_range,
1580 draw_mode,
1581 instance_count,
1582 ..
1583 } = geometry;
1584
1585 let attached_attributes = mesh.attachments();
1586 let (desired_attribute_state, attributes) = prepare_draw(shader, &attached_attributes);
1587 self.set_vertex_attributes(desired_attribute_state, &attributes);
1588
1589 mesh.draw(
1590 self,
1591 draw_range.clone(),
1592 *draw_mode,
1593 *instance_count as usize,
1594 );
1595 }
1596}
1597
1598fn prepare_draw<'a, S: shader::Shader + ?Sized>(
1599 shader: &S,
1600 attached_attributes: &'a [mesh::AttachedAttributes],
1601) -> (u32, [Option<mesh::BindingInfo<'a>>; 32]) {
1602 let attached_bindings = attached_attributes
1604 .iter()
1605 .flat_map(|attributes| {
1606 attributes
1607 .formats
1608 .iter()
1609 .map(|binding| {
1610 (
1611 binding,
1612 attributes.stride,
1613 attributes.step,
1614 attributes.buffer.handle(),
1615 attributes.buffer.buffer_type(),
1616 )
1617 })
1618 .collect::<Vec<_>>()
1619 })
1620 .collect::<Vec<_>>();
1621
1622 let mut desired_attribute_state = 0u32;
1623 let mut attributes = [None; 32];
1624 for attr in shader::Shader::attributes(shader).iter() {
1625 let binding = attached_bindings
1626 .iter()
1627 .find(|(binding, ..)| binding.name == attr.name.as_str())
1628 .cloned();
1629 if let Some(binding) = binding {
1630 desired_attribute_state |= 1 << attr.location;
1631 attributes[attr.location as usize] = Some(binding);
1632 }
1633 }
1634 (desired_attribute_state, attributes)
1635}
1636
1637#[derive(Debug, Clone, PartialEq)]
1638pub struct Geometry<M> {
1639 pub mesh: M,
1640 pub draw_range: std::ops::Range<usize>,
1641 pub draw_mode: DrawMode,
1642 pub instance_count: u32,
1643}
1644
1645#[derive(Copy, Clone, Default, Debug, PartialOrd, PartialEq)]
1648pub struct ClampedF32(f32);
1649
1650impl From<f32> for ClampedF32 {
1651 fn from(v: f32) -> Self {
1652 let v = if v < 0. {
1653 0f32
1654 } else if v > 1. {
1655 1.
1656 } else if v.is_nan() {
1657 0.
1658 } else {
1659 v
1660 };
1661
1662 ClampedF32(v)
1663 }
1664}
1665
1666impl Eq for ClampedF32 {}
1667impl Ord for ClampedF32 {
1668 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1669 self.0.partial_cmp(&other.0).expect("infallible")
1670 }
1671}
1672
1673impl std::ops::Deref for ClampedF32 {
1674 type Target = f32;
1675
1676 fn deref(&self) -> &Self::Target {
1677 &self.0
1678 }
1679}
1680
1681#[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)]
1682pub struct Color<T> {
1683 pub red: T,
1684 pub blue: T,
1685 pub green: T,
1686 pub alpha: T,
1687}
1688
1689impl From<Color<f32>> for Color<ClampedF32> {
1690 fn from(c: Color<f32>) -> Self {
1691 Self {
1692 red: c.red.into(),
1693 blue: c.blue.into(),
1694 green: c.green.into(),
1695 alpha: c.alpha.into(),
1696 }
1697 }
1698}
1699
1700impl From<Color<ClampedF32>> for Color<f32> {
1701 fn from(c: Color<ClampedF32>) -> Self {
1702 Self {
1703 red: c.red.0,
1704 blue: c.blue.0,
1705 green: c.green.0,
1706 alpha: c.alpha.0,
1707 }
1708 }
1709}
1710
1711#[derive(Debug, Copy, Clone, PartialEq)]
1712pub struct ClearSettings<'a> {
1713 pub color: Option<Color<ClampedF32>>,
1714 pub depth: Option<ClampedF32>,
1715 pub stencil: Option<i32>, pub target: Option<&'a canvas::Canvas>,
1717 pub scissor: Option<viewport::Viewport<i32>>,
1718}
1719
1720impl Default for ClearSettings<'_> {
1721 fn default() -> Self {
1722 Self {
1723 color: Some(Color::default()),
1724 depth: Some(ClampedF32(1.)),
1725 stencil: Some(0),
1726 target: None,
1727 scissor: None,
1728 }
1729 }
1730}
1731
1732#[derive(Debug, Clone, Eq, PartialEq)]
1733pub struct DepthState {
1734 pub function: DepthFunction,
1735 pub range: std::ops::RangeInclusive<ClampedF32>,
1736 pub write_mask: bool,
1737}
1738
1739impl Default for DepthState {
1740 fn default() -> Self {
1741 Self {
1742 function: DepthFunction::Less,
1743 range: ClampedF32(0.)..=ClampedF32(1.),
1744 write_mask: true,
1745 }
1746 }
1747}
1748
1749#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1750pub struct CullingState {
1751 pub mode: CullFace,
1752 pub winding: VertexWinding,
1753}
1754
1755#[derive(Debug, Copy, Clone, PartialEq)]
1756pub struct PolygonState {
1757 pub culling_state: Option<CullingState>,
1758 pub polygon_offset_units: f32,
1759 pub polygon_offset_factor: f32,
1760}
1761
1762impl Default for PolygonState {
1763 fn default() -> Self {
1764 Self {
1765 culling_state: None,
1766 polygon_offset_units: 0.0,
1767 polygon_offset_factor: 0.0,
1768 }
1769 }
1770}
1771
1772#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1773pub enum BlendSource {
1774 Zero,
1775 One,
1776 SourceColor,
1777 OneMinusSourceColor,
1778 DestinationColor,
1779 OneMinusDestinationColor,
1780 SourceAlpha,
1781 OneMinusSourceAlpha,
1782 DestinationAlpha,
1783 OneMinusDestinationAlpha,
1784 ConstantColor,
1785 OneMinusConstantColor,
1786 ConstantAlpha,
1787 OneMinusConstantAlpha,
1788 SourceAlphaSaturate,
1789}
1790
1791#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1792pub enum BlendDestination {
1793 Zero,
1794 One,
1795 SourceColor,
1796 OneMinusSourceColor,
1797 DestinationColor,
1798 OneMinusDestinationColor,
1799 SourceAlpha,
1800 OneMinusSourceAlpha,
1801 DestinationAlpha,
1802 OneMinusDestinationAlpha,
1803 ConstantColor,
1804 OneMinusConstantColor,
1805 ConstantAlpha,
1806 OneMinusConstantAlpha,
1807}
1808
1809#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1810pub enum BlendEquation {
1811 Add,
1812 Subtract,
1813 ReverseSubtract,
1814 Min,
1815 Max,
1816}
1817
1818#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1819pub struct BlendState {
1820 pub destination_rgb: BlendDestination,
1821 pub source_rgb: BlendSource,
1822 pub destination_alpha: BlendDestination,
1823 pub source_alpha: BlendSource,
1824 pub color: Color<ClampedF32>,
1825 pub equation_rgb: BlendEquation,
1826 pub equation_alpha: BlendEquation,
1827}
1828
1829impl Default for BlendState {
1830 fn default() -> Self {
1831 Self {
1832 destination_rgb: BlendDestination::Zero,
1833 source_rgb: BlendSource::One,
1834 destination_alpha: BlendDestination::Zero,
1835 source_alpha: BlendSource::One,
1836 color: Default::default(),
1837 equation_rgb: BlendEquation::Add,
1838 equation_alpha: BlendEquation::Add,
1839 }
1840 }
1841}
1842
1843#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1844pub enum StencilFunction {
1845 Never,
1846 Less,
1847 LessOrEqual,
1848 Greater,
1849 GreaterOrEqual,
1850 Equal,
1851 NoteEqual,
1852 Always,
1853}
1854
1855#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1856pub struct StencilState {
1857 pub function: StencilFunction,
1858 }
1860
1861impl Default for StencilState {
1862 fn default() -> Self {
1863 Self {
1864 function: StencilFunction::Always,
1865 }
1866 }
1867}
1868
1869#[derive(Debug, Clone, PartialEq)]
1870pub struct PipelineSettings<'a> {
1871 pub viewport: viewport::Viewport<i32>,
1872 pub framebuffer: Option<&'a canvas::Canvas>,
1873 pub polygon_state: PolygonState,
1874 pub depth_state: Option<DepthState>,
1875 pub blend_state: Option<BlendState>,
1876 pub stencil_state: Option<StencilState>,
1877 pub scissor_state: Option<viewport::Viewport<i32>>,
1878}
1879
1880impl<'a> Default for PipelineSettings<'a> {
1881 fn default() -> Self {
1882 Self {
1883 viewport: Default::default(),
1884 framebuffer: None,
1885 depth_state: Some(DepthState::default()),
1886 polygon_state: Default::default(),
1887 blend_state: None,
1888 stencil_state: None,
1889 scissor_state: None,
1890 }
1891 }
1892}
1893
1894#[cfg(all(test, not(target_os = "linux")))]
1895mod tests {
1896 use super::*;
1897
1898 #[test]
1899 fn pipeline() {
1900 let pipeline_settings = PipelineSettings::default();
1901 println!("{:#?}", pipeline_settings);
1902
1903 let pipeline_settings = PipelineSettings {
1904 viewport: viewport::Viewport::new(0, 0, 720, 480),
1905 ..PipelineSettings::default()
1906 };
1907 println!("{:#?}", pipeline_settings);
1908
1909 let pipeline_settings = PipelineSettings {
1910 depth_state: Some(DepthState {
1911 function: DepthFunction::Never,
1912 ..DepthState::default()
1913 }),
1914 ..pipeline_settings
1915 };
1916 println!("{:#?}", pipeline_settings);
1917 }
1918
1919 #[repr(C)]
1920 #[derive(Debug, Copy, Clone, PartialEq, Default, bytemuck::Zeroable, bytemuck::Pod)]
1921 struct TestVertex {
1922 color: f32,
1923 position: f32,
1924 }
1925
1926 use vertex::VertexFormat;
1927 impl vertex::Vertex for TestVertex {
1928 fn build_bindings() -> &'static [VertexFormat] {
1929 &[
1930 VertexFormat {
1931 name: "color",
1932 offset: 0,
1933 atype: vertex::AttributeType::F32,
1934 normalize: false,
1935 },
1936 VertexFormat {
1937 name: "position",
1938 offset: std::mem::size_of::<f32>(),
1939 atype: vertex::AttributeType::F32,
1940 normalize: false,
1941 },
1942 ]
1943 }
1944 }
1945
1946 #[cfg(target_os = "windows")]
1947 fn get_headless_context(
1948 width: u32,
1949 height: u32,
1950 ) -> (glow::Context, glutin::Context<glutin::PossiblyCurrent>) {
1951 use glutin::platform::windows::EventLoopExtWindows;
1952 let el = glutin::event_loop::EventLoop::<()>::new_any_thread();
1953 let window = glutin::ContextBuilder::new()
1954 .build_headless(&el, glutin::dpi::PhysicalSize::new(width, height))
1955 .unwrap();
1956 let window = unsafe { window.make_current().unwrap() };
1957 (
1958 unsafe { glow::Context::from_loader_function(|name| window.get_proc_address(name)) },
1959 window,
1960 )
1961 }
1962
1963 #[cfg(target_os = "windows")]
1964 #[test]
1965 fn basic() {
1966 let (ctx, _window) = get_headless_context(100, 100);
1967 let ctx = Context::new(ctx);
1968 ctx.clear();
1969 }
1970
1971 #[cfg(target_os = "windows")]
1972 #[test]
1973 fn unused_vertex_attribute() {
1974 let (ctx, _window) = get_headless_context(100, 100);
1975 let mut ctx = Context::new(ctx);
1976
1977 let mesh = mesh::VertexMesh::with_data(
1978 &mut ctx,
1979 &[
1980 TestVertex {
1981 color: 0.,
1982 position: 1.,
1983 },
1984 TestVertex {
1985 color: 1.,
1986 position: 2.,
1987 },
1988 TestVertex {
1989 color: 2.,
1990 position: 3.,
1991 },
1992 ],
1993 )
1994 .unwrap();
1995
1996 const SRC: &str = r#"
1997varying vec4 vColor;
1998
1999#ifdef VERTEX
2000layout(location = 2) attribute vec4 position;
2001
2002void main() {
2003 gl_Position = position;
2004}
2005#endif
2006
2007#ifdef FRAGMENT
2008void main() {
2009 fragColor = vec4(1., 1., 1., 1.);
2010}
2011#endif"#;
2012
2013 let (vert, frag) = shader::DynamicShader::create_source(SRC, SRC);
2014 let shader = shader::DynamicShader::new(&mut ctx, &vert, &frag).unwrap();
2015 ctx.use_shader(Some(&shader));
2016
2017 Renderer::draw(
2018 &mut ctx,
2019 &shader,
2020 &super::Geometry {
2021 mesh: &mesh,
2022 draw_range: 0..1,
2023 draw_mode: DrawMode::Triangles,
2024 instance_count: 1,
2025 },
2026 PipelineSettings::default(),
2027 );
2028 }
2029
2030 #[cfg(target_os = "windows")]
2031 #[test]
2032 fn mapped_mesh() {
2033 let (ctx, _window) = get_headless_context(100, 100);
2034 let mut ctx = Context::new(ctx);
2035
2036 let vertices = [
2037 TestVertex {
2038 color: 0.,
2039 position: 1.,
2040 },
2041 TestVertex {
2042 color: 1.,
2043 position: 2.,
2044 },
2045 TestVertex {
2046 color: 2.,
2047 position: 3.,
2048 },
2049 ];
2050
2051 let indices = [0u32, 1, 2];
2052
2053 {
2054 let mut mesh = mesh::MappedVertexMesh::new(&mut ctx, 3).unwrap();
2055 mesh.set_vertices(&vertices, 0);
2056
2057 let mapped_verts = mesh.get_vertices();
2058 assert_eq!(vertices, mapped_verts);
2059 }
2060
2061 {
2062 let mut mesh = mesh::MappedIndexedMesh::new(&mut ctx, 3, 3).unwrap();
2063 mesh.set_vertices(&vertices, 0);
2064 mesh.set_indices(&indices, 0);
2065
2066 assert_eq!(vertices, mesh.get_vertices());
2067 assert_eq!(indices, mesh.get_indices());
2068 }
2069 }
2070
2071 #[cfg(target_os = "windows")]
2072 #[test]
2073 fn mapped_image() {
2074 use super::PixelFormat;
2075 use image::*;
2076 use texture::*;
2077
2078 let (ctx, _window) = get_headless_context(100, 100);
2079 let mut ctx = Context::new(ctx);
2080 {
2081 let data = vec![234; 3 * 3 * 4];
2083
2084 let mut image = MappedImage::with_data(
2085 &mut ctx,
2086 TextureType::Tex2D,
2087 PixelFormat::RGBA8,
2088 3,
2089 3,
2090 data.clone(),
2091 Settings::default(),
2092 )
2093 .unwrap();
2094 let pixel_stride = image.pixel_stride();
2095 assert_eq!(image.get_pixels(), data);
2096
2097 let pixel = [1, 2, 3, 4];
2098 image.set_pixels(viewport::Viewport::new(0, 0, 1, 1), &pixel);
2099 assert_eq!(image.get_pixels()[..4], pixel);
2100
2101 let pixel = [0, 0, 1, 0];
2102 image.set_pixel(0, 0, &pixel);
2103 assert_eq!(image.get_pixels()[..4], pixel);
2104 assert_eq!(image.get_pixel(0, 0), pixel);
2105
2106 assert_eq!(image.get_pixel(0, 1), [234, 234, 234, 234]);
2107 image.set_pixel(0, 1, &pixel);
2108 assert_eq!(image.get_pixel(0, 1), pixel);
2109 assert_eq!(
2110 image.get_pixels()[(3 * pixel_stride)..(4 * pixel_stride)],
2111 pixel
2112 );
2113 }
2114
2115 {
2116 let data = vec![234; 3 * 3 * 3];
2118
2119 let mut image = MappedImage::with_data(
2120 &mut ctx,
2121 TextureType::Tex2D,
2122 PixelFormat::RGB8,
2123 3,
2124 3,
2125 data.clone(),
2126 Settings::default(),
2127 )
2128 .unwrap();
2129 let pixel_stride = image.pixel_stride();
2130 assert_eq!(image.get_pixels(), data);
2131
2132 let pixel = [1, 2, 3];
2133 image.set_pixels(viewport::Viewport::new(0, 0, 1, 1), &pixel);
2134 assert_eq!(image.get_pixels()[..pixel_stride], pixel);
2135
2136 let pixel = [0, 0, 1];
2137 image.set_pixel(0, 0, &pixel);
2138 assert_eq!(image.get_pixels()[..pixel_stride], pixel);
2139 assert_eq!(image.get_pixel(0, 0), pixel);
2140
2141 assert_eq!(image.get_pixel(0, 1), [234, 234, 234]);
2142 image.set_pixel(0, 1, &pixel);
2143 assert_eq!(image.get_pixel(0, 1), pixel);
2144 assert_eq!(
2145 image.get_pixels()[(3 * pixel_stride)..(4 * pixel_stride)],
2146 pixel
2147 );
2148 }
2149 }
2150
2151 #[cfg(target_os = "windows")]
2152 #[test]
2153 fn quad_batch_test() {
2154 let (ctx, _window) = get_headless_context(100, 100);
2155 let mut ctx = Context::new(ctx);
2156
2157 let quad = quad_batch::Quad::from(viewport::Viewport::new(0., 0., 1., 1.)).map(|(x, y)| {
2158 TestVertex {
2159 color: y,
2160 position: x,
2161 }
2162 });
2163 let mut batch = quad_batch::QuadBatch::<TestVertex>::new(&mut ctx, 1).unwrap();
2164 let index = batch.push(quad.clone());
2165
2166 assert_eq!(batch.get_quad(index).unwrap(), quad);
2167 }
2168}