xgl/sys/opengl/
context.rs

1// Copyright (C) 2025 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: (Apache-2.0 OR MIT)
3
4use std::error::Error as StdError;
5use std::ffi::CString;
6use std::fmt::Debug;
7use std::fmt::Display;
8use std::fmt::Formatter;
9use std::fmt::Result as FmtResult;
10use std::mem::MaybeUninit;
11use std::ops::BitOr;
12use std::ops::BitOrAssign;
13use std::ptr::null_mut;
14use std::slice;
15
16use crate::sys::BuiltinType;
17use crate::sys::Gl;
18use crate::sys::Sealed;
19
20use super::gl;
21
22
23#[derive(Debug)]
24pub struct Program(u32);
25
26#[derive(Debug)]
27pub struct Shader(u32);
28
29#[derive(Debug)]
30pub struct Framebuffer(u32);
31
32#[derive(Debug)]
33pub struct Texture(u32);
34
35#[derive(Debug)]
36pub struct VertexArrayObject(u32);
37
38#[derive(Debug)]
39pub struct VertexBufferObject(u32);
40
41#[derive(Debug)]
42pub struct UniformLocation(i32);
43
44
45#[derive(Eq, PartialEq)]
46pub struct Error(u32);
47
48impl Debug for Error {
49  fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
50    Display::fmt(self, f)
51  }
52}
53
54impl Display for Error {
55  fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
56    let err = match self.0 {
57      gl::INVALID_ENUM => "invalid enum",
58      gl::INVALID_VALUE => "invalid value",
59      gl::INVALID_OPERATION => "invalid operation",
60      gl::OUT_OF_MEMORY => "out of memory",
61      gl::INVALID_FRAMEBUFFER_OPERATION => "invalid framebuffer operation",
62      _ => return write!(f, "OpenGL error: {:#x}", self.0),
63    };
64    write!(f, "OpenGL error: {err} ({:#x})", self.0)
65  }
66}
67
68impl StdError for Error {}
69
70
71#[repr(u32)]
72#[non_exhaustive]
73#[derive(Clone, Copy, Debug)]
74pub enum Type {
75  Float = gl::FLOAT,
76  Short = gl::SHORT,
77  UnsignedByte = gl::UNSIGNED_BYTE,
78  UnsignedShort = gl::UNSIGNED_SHORT,
79}
80
81
82#[repr(u32)]
83#[non_exhaustive]
84#[derive(Clone, Copy, Debug)]
85pub enum Capability {
86  Blend = gl::BLEND,
87  CullFace = gl::CULL_FACE,
88  DepthTest = gl::DEPTH_TEST,
89  FramebufferSRGB = gl::FRAMEBUFFER_SRGB,
90  Multisample = gl::MULTISAMPLE,
91  ScissorTest = gl::SCISSOR_TEST,
92}
93
94
95#[repr(u32)]
96#[non_exhaustive]
97#[derive(Clone, Copy, Debug)]
98pub enum Func {
99  LessOrEqual = gl::LEQUAL,
100  Greater = gl::GREATER,
101}
102
103
104#[repr(u32)]
105#[non_exhaustive]
106#[derive(Clone, Copy, Debug)]
107pub enum Factor {
108  SrcAlpha = gl::SRC_ALPHA,
109  OneMinusSrcAlpha = gl::ONE_MINUS_SRC_ALPHA,
110}
111
112
113#[repr(u32)]
114#[non_exhaustive]
115#[derive(Clone, Copy, Debug)]
116pub enum FrontFace {
117  ClockWise = gl::CW,
118}
119
120
121#[repr(u32)]
122#[non_exhaustive]
123#[derive(Clone, Copy, Debug)]
124pub enum CullFace {
125  Back = gl::BACK,
126}
127
128
129#[repr(u32)]
130#[non_exhaustive]
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132pub enum Primitive {
133  Lines = gl::LINES,
134  Triangles = gl::TRIANGLES,
135  TriangleFan = gl::TRIANGLE_FAN,
136  TriangleStrip = gl::TRIANGLE_STRIP,
137}
138
139
140#[derive(Clone, Copy, Debug)]
141pub struct ClearMask(u32);
142
143#[expect(non_upper_case_globals)]
144impl ClearMask {
145  pub const ColorBuffer: Self = Self(gl::COLOR_BUFFER_BIT);
146  pub const DepthBuffer: Self = Self(gl::DEPTH_BUFFER_BIT);
147}
148
149impl BitOr for ClearMask {
150  type Output = Self;
151
152  #[inline]
153  fn bitor(self, other: Self) -> Self::Output {
154    let mut result = self;
155    result |= other;
156    result
157  }
158}
159
160impl BitOrAssign for ClearMask {
161  #[inline]
162  fn bitor_assign(&mut self, other: Self) {
163    self.0 |= other.0;
164  }
165}
166
167
168#[derive(Debug, Eq, PartialEq)]
169pub struct FramebufferStatus(u32);
170
171#[expect(non_upper_case_globals)]
172impl FramebufferStatus {
173  pub const Complete: Self = Self(gl::FRAMEBUFFER_COMPLETE);
174}
175
176impl Display for FramebufferStatus {
177  fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
178    write!(f, "{:#x}", self.0)
179  }
180}
181
182
183#[repr(u32)]
184#[non_exhaustive]
185#[derive(Clone, Copy, Debug)]
186pub enum ShaderType {
187  Fragment = gl::FRAGMENT_SHADER,
188  Vertex = gl::VERTEX_SHADER,
189}
190
191impl ShaderType {
192  pub fn as_str(&self) -> &'static str {
193    match self {
194      Self::Fragment => "fragment",
195      Self::Vertex => "vertex",
196    }
197  }
198}
199
200
201#[repr(u32)]
202#[non_exhaustive]
203#[derive(Clone, Copy, Debug)]
204pub enum VertexBufferTarget {
205  Array = gl::ARRAY_BUFFER,
206  ElementArray = gl::ELEMENT_ARRAY_BUFFER,
207}
208
209#[repr(u32)]
210#[non_exhaustive]
211#[derive(Clone, Copy, Debug)]
212pub enum VertexBufferUsage {
213  DynamicDraw = gl::DYNAMIC_DRAW,
214  StaticDraw = gl::STATIC_DRAW,
215  StreamDraw = gl::STREAM_DRAW,
216}
217
218
219#[repr(u32)]
220#[non_exhaustive]
221#[derive(Clone, Copy, Debug)]
222pub enum TextureTarget {
223  Texture2D = gl::TEXTURE_2D,
224  Texture2DArray = gl::TEXTURE_2D_ARRAY,
225}
226
227#[repr(u32)]
228#[non_exhaustive]
229#[derive(Clone, Copy, Debug)]
230pub enum TextureInternalFormat {
231  Gray8 = gl::R8,
232  Depth = gl::DEPTH_COMPONENT,
233  RGB8 = gl::RGB8,
234  SRGB8 = gl::SRGB8,
235  RGBA8 = gl::RGBA8,
236  SRGBA8 = gl::SRGB8_ALPHA8,
237}
238
239#[repr(u32)]
240#[non_exhaustive]
241#[derive(Clone, Copy, Debug)]
242pub enum TexturePixelFormat {
243  Gray = gl::RED,
244  Depth = gl::DEPTH_COMPONENT,
245  RGB = gl::RGB,
246  RGBA = gl::RGBA,
247}
248
249#[repr(u32)]
250#[non_exhaustive]
251#[derive(Clone, Copy, Debug)]
252pub enum TextureCompareMode {
253  RefToTexture = gl::COMPARE_REF_TO_TEXTURE,
254}
255
256#[repr(u32)]
257#[non_exhaustive]
258#[derive(Clone, Copy, Debug)]
259pub enum TextureWrap {
260  ClampToEdge = gl::CLAMP_TO_EDGE,
261  Repeat = gl::REPEAT,
262}
263
264#[repr(u32)]
265#[non_exhaustive]
266#[derive(Clone, Copy, Debug)]
267pub enum TextureFilterType {
268  Minimize = gl::TEXTURE_MIN_FILTER,
269  Magnify = gl::TEXTURE_MAG_FILTER,
270}
271
272#[repr(u32)]
273#[non_exhaustive]
274#[derive(Clone, Copy, Debug)]
275pub enum TextureFilter {
276  Linear = gl::LINEAR,
277  LinearMipmapLinear = gl::LINEAR_MIPMAP_LINEAR,
278}
279
280
281impl BuiltinType<Context> for u16 {
282  fn as_type() -> Type {
283    Type::UnsignedShort
284  }
285}
286
287
288/// The OpenGL context in use.
289///
290/// A context is guaranteed to be cheaply cloneable.
291#[derive(Clone, Debug, Default)]
292pub struct Context {}
293
294impl Context {
295  fn check_program(&self, program: &Program, status_attrib: u32) -> Result<(), Vec<u8>> {
296    let mut status = MaybeUninit::uninit();
297    let () = unsafe { gl::GetProgramiv(program.0, status_attrib, status.as_mut_ptr()) };
298    let status = unsafe { status.assume_init() };
299    if status == i32::from(false) {
300      let mut info_len = MaybeUninit::uninit();
301      let () = unsafe { gl::GetProgramiv(program.0, gl::INFO_LOG_LENGTH, info_len.as_mut_ptr()) };
302      let info_len = unsafe { info_len.assume_init() };
303
304      let mut log = Vec::<u8>::with_capacity(info_len as _);
305      let () = unsafe {
306        gl::GetProgramInfoLog(program.0, info_len, null_mut(), log.as_mut_ptr().cast());
307      };
308      let () = unsafe { log.set_len(info_len as _) };
309      Err(log)
310    } else {
311      Ok(())
312    }
313  }
314
315  #[inline]
316  fn set_uniform_matrices_impl(&self, location: &UniformLocation, matrices: &[[f32; 16]]) {
317    let transpose = 0;
318    let () = unsafe {
319      gl::UniformMatrix4fv(
320        location.0,
321        matrices.len() as _,
322        transpose,
323        matrices.as_ptr().cast(),
324      )
325    };
326    debug_assert_eq!(self.error(), Ok(()));
327  }
328}
329
330impl Sealed for Context {}
331
332impl Gl for Context {
333  type Error = Error;
334
335  type Capability = Capability;
336  type ClearMask = ClearMask;
337  type CullFace = CullFace;
338  type Factor = Factor;
339  type FramebufferStatus = FramebufferStatus;
340  type FrontFace = FrontFace;
341  type Func = Func;
342  type Primitive = Primitive;
343  type ShaderType = ShaderType;
344  type TextureCompareMode = TextureCompareMode;
345  type TextureFilter = TextureFilter;
346  type TextureFilterType = TextureFilterType;
347  type TextureInternalFormat = TextureInternalFormat;
348  type TexturePixelFormat = TexturePixelFormat;
349  type TextureTarget = TextureTarget;
350  type TextureWrap = TextureWrap;
351  type Type = Type;
352  type VertexBufferTarget = VertexBufferTarget;
353  type VertexBufferUsage = VertexBufferUsage;
354
355  type Framebuffer = Framebuffer;
356  type Program = Program;
357  type Shader = Shader;
358  type Texture = Texture;
359  type VertexArrayObject = VertexArrayObject;
360  type VertexBufferObject = VertexBufferObject;
361
362  type UniformLocation = UniformLocation;
363
364  #[inline]
365  fn error(&self) -> Result<(), Error> {
366    let error = unsafe { gl::GetError() };
367    if error == gl::NO_ERROR {
368      Ok(())
369    } else {
370      Err(Error(error))
371    }
372  }
373
374  #[inline]
375  fn enable(&self, capability: Capability) {
376    let () = unsafe { gl::Enable(capability as _) };
377    debug_assert_eq!(self.error(), Ok(()));
378  }
379
380  #[inline]
381  fn disable(&self, capability: Capability) {
382    let () = unsafe { gl::Disable(capability as _) };
383    debug_assert_eq!(self.error(), Ok(()));
384  }
385
386  #[inline]
387  fn set_depth_func(&self, func: Func) {
388    let () = unsafe { gl::DepthFunc(func as _) };
389    debug_assert_eq!(self.error(), Ok(()));
390  }
391
392  #[inline]
393  fn set_blend_func(&self, src_factor: Factor, dst_factor: Factor) {
394    let () = unsafe { gl::BlendFunc(src_factor as _, dst_factor as _) };
395    debug_assert_eq!(self.error(), Ok(()));
396  }
397
398  #[inline]
399  fn set_front_face(&self, face: FrontFace) {
400    let () = unsafe { gl::FrontFace(face as _) };
401    debug_assert_eq!(self.error(), Ok(()));
402  }
403
404  #[inline]
405  fn set_cull_face(&self, face: CullFace) {
406    let () = unsafe { gl::CullFace(face as _) };
407    debug_assert_eq!(self.error(), Ok(()));
408  }
409
410  #[inline]
411  fn set_viewport(&self, x: i32, y: i32, w: i32, h: i32) {
412    let () = unsafe { gl::Viewport(x, y, w, h) };
413    debug_assert_eq!(self.error(), Ok(()));
414  }
415
416  #[inline]
417  fn set_clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
418    let () = unsafe { gl::ClearColor(r, g, b, a) };
419    debug_assert_eq!(self.error(), Ok(()));
420  }
421
422  #[inline]
423  fn set_pixel_unpack_alignment(&self, alignment: u32) {
424    let () = unsafe { gl::PixelStorei(gl::UNPACK_ALIGNMENT, alignment as _) };
425    debug_assert_eq!(self.error(), Ok(()));
426  }
427
428  #[inline]
429  fn clear(&self, mask: ClearMask) {
430    let () = unsafe { gl::Clear(mask.0) };
431    debug_assert_eq!(self.error(), Ok(()));
432  }
433
434  #[inline]
435  fn draw_arrays(&self, primitive: Primitive, count: i32) {
436    let () = unsafe { gl::DrawArrays(primitive as _, 0, count) };
437    debug_assert_eq!(self.error(), Ok(()));
438  }
439
440  #[inline]
441  fn draw_arrays_instanced(&self, primitive: Primitive, count: i32, instance_count: i32) {
442    let () = unsafe { gl::DrawArraysInstanced(primitive as _, 0, count, instance_count) };
443    debug_assert_eq!(self.error(), Ok(()));
444  }
445
446  #[inline]
447  fn draw_elements<T>(&self, primitive: Primitive, count: i32)
448  where
449    T: BuiltinType<Self>,
450  {
451    let () = unsafe { gl::DrawElements(primitive as _, count, T::as_type() as _, null_mut()) };
452    debug_assert_eq!(self.error(), Ok(()));
453  }
454
455  #[inline]
456  fn create_framebuffer(&self) -> Result<Framebuffer, Error> {
457    let mut fbo = 0;
458    let () = unsafe { gl::GenFramebuffers(1, &mut fbo) };
459    let () = self.error()?;
460    Ok(Framebuffer(fbo))
461  }
462
463  #[inline]
464  fn delete_framebuffer(&self, fbo: &Framebuffer) {
465    let () = unsafe { gl::DeleteFramebuffers(1, &fbo.0) };
466    debug_assert_eq!(self.error(), Ok(()));
467  }
468
469  #[inline]
470  fn bind_framebuffer(&self, fbo: Option<&Framebuffer>) {
471    let fbo = fbo.map(|fbo| fbo.0).unwrap_or(0);
472    let () = unsafe { gl::BindFramebuffer(gl::FRAMEBUFFER, fbo) };
473    debug_assert_eq!(self.error(), Ok(()));
474  }
475
476  #[inline]
477  fn set_framebuffer_depth_texture(&self, texture_target: TextureTarget, texture: &Texture) {
478    let mipmap_level = 0;
479    let () = unsafe {
480      gl::FramebufferTexture2D(
481        gl::FRAMEBUFFER,
482        gl::DEPTH_ATTACHMENT,
483        texture_target as _,
484        texture.0,
485        mipmap_level,
486      )
487    };
488    debug_assert_eq!(self.error(), Ok(()));
489  }
490
491  #[inline]
492  fn unset_draw_buffer(&self) {
493    let () = unsafe { gl::DrawBuffer(gl::NONE) };
494    debug_assert_eq!(self.error(), Ok(()));
495  }
496
497  #[inline]
498  fn unset_read_buffer(&self) {
499    let () = unsafe { gl::ReadBuffer(gl::NONE) };
500    debug_assert_eq!(self.error(), Ok(()));
501  }
502
503  #[inline]
504  fn check_framebuffer_status(&self) -> FramebufferStatus {
505    let status = unsafe { gl::CheckFramebufferStatus(gl::FRAMEBUFFER) };
506    FramebufferStatus(status)
507  }
508
509  #[inline]
510  fn create_shader(&self, ty: ShaderType) -> Option<Shader> {
511    let shader = unsafe { gl::CreateShader(ty as _) };
512    if shader != 0 {
513      Some(Shader(shader))
514    } else {
515      None
516    }
517  }
518
519  #[inline]
520  fn delete_shader(&self, shader: &Shader) {
521    let () = unsafe { gl::DeleteShader(shader.0) };
522    debug_assert_eq!(self.error(), Ok(()));
523  }
524
525  #[inline]
526  fn set_shader_source(&self, shader: &Shader, source: &str) {
527    let srcs = [source.as_ptr().cast::<u8>()];
528    let src_lens = [source.len() as i32];
529    let () = unsafe {
530      gl::ShaderSource(
531        shader.0,
532        srcs.len() as _,
533        srcs.as_ptr().cast(),
534        src_lens.as_ptr(),
535      )
536    };
537    debug_assert_eq!(self.error(), Ok(()));
538  }
539
540  #[inline]
541  fn compile_shader(&self, shader: &Shader) -> Result<(), Vec<u8>> {
542    let () = unsafe { gl::CompileShader(shader.0) };
543    let mut status = MaybeUninit::uninit();
544    let () = unsafe { gl::GetShaderiv(shader.0, gl::COMPILE_STATUS, status.as_mut_ptr()) };
545    let status = unsafe { status.assume_init() };
546    if status == i32::from(false) {
547      let mut info_len = MaybeUninit::uninit();
548      let () = unsafe { gl::GetShaderiv(shader.0, gl::INFO_LOG_LENGTH, info_len.as_mut_ptr()) };
549      let info_len = unsafe { info_len.assume_init() };
550
551      let mut log = Vec::<u8>::with_capacity(info_len as _);
552      let () =
553        unsafe { gl::GetShaderInfoLog(shader.0, info_len, null_mut(), log.as_mut_ptr().cast()) };
554      let () = unsafe { log.set_len(info_len as _) };
555      Err(log)
556    } else {
557      Ok(())
558    }
559  }
560
561  #[inline]
562  fn attach_shader(&self, program: &Program, shader: &Shader) {
563    let () = unsafe { gl::AttachShader(program.0, shader.0) };
564    debug_assert_eq!(self.error(), Ok(()));
565  }
566
567  #[inline]
568  fn detach_shader(&self, program: &Program, shader: &Shader) {
569    let () = unsafe { gl::DetachShader(program.0, shader.0) };
570    debug_assert_eq!(self.error(), Ok(()));
571  }
572
573  #[inline]
574  fn create_program(&self) -> Option<Program> {
575    let program = unsafe { gl::CreateProgram() };
576    if program != 0 {
577      Some(Program(program))
578    } else {
579      None
580    }
581  }
582
583  #[inline]
584  fn delete_program(&self, program: &Program) {
585    let () = unsafe { gl::DeleteProgram(program.0) };
586    debug_assert_eq!(self.error(), Ok(()));
587  }
588
589  fn link_program(&self, program: &Program) -> Result<(), Vec<u8>> {
590    let () = unsafe { gl::LinkProgram(program.0) };
591    let () = self.check_program(program, gl::LINK_STATUS)?;
592    Ok(())
593  }
594
595  fn validate_program(&self, program: &Program) -> Result<(), Vec<u8>> {
596    let () = unsafe { gl::ValidateProgram(program.0) };
597    let () = self.check_program(program, gl::VALIDATE_STATUS)?;
598    Ok(())
599  }
600
601  #[inline]
602  fn use_program(&self, program: &Program) {
603    let () = unsafe { gl::UseProgram(program.0) };
604    debug_assert_eq!(self.error(), Ok(()));
605  }
606
607  #[inline]
608  fn attrib_location(&self, program: &Program, attrib: &str) -> Option<u32> {
609    // SANITY: Callers have to ensure there are no NUL bytes inside
610    //         `attrib`.
611    let cattrib = CString::new(attrib).unwrap();
612    let idx = unsafe { gl::GetAttribLocation(program.0, cattrib.as_ptr()) };
613    if idx >= 0 {
614      Some(u32::try_from(idx).unwrap())
615    } else {
616      None
617    }
618  }
619
620  #[inline]
621  fn uniform_location(&self, program: &Program, uniform: &str) -> Option<UniformLocation> {
622    // SANITY: Callers have to ensure there are no NUL bytes inside
623    //         `uniform`.
624    let cuniform = CString::new(uniform).unwrap();
625    let idx = unsafe { gl::GetUniformLocation(program.0, cuniform.as_ptr()) };
626    if idx >= -1 {
627      Some(UniformLocation(idx))
628    } else {
629      None
630    }
631  }
632
633  #[inline]
634  fn uniform_fv<const N: usize>(&self, program: &Program, location: &UniformLocation) -> [f32; N] {
635    let mut data = MaybeUninit::<[f32; N]>::uninit();
636    let () = unsafe { gl::GetUniformfv(program.0, location.0, data.as_mut_ptr().cast()) };
637    // SANITY: We know that program and location are valid, so the above
638    //         call should be infallible.
639    assert_eq!(self.error(), Ok(()));
640
641    // SAFETY: On success, `data` will be written by `GetUniformfv`.
642    unsafe { data.assume_init() }
643  }
644
645  #[inline]
646  fn set_uniform_1i(&self, location: &UniformLocation, data: i32) {
647    let () = unsafe { gl::Uniform1i(location.0, data) };
648    debug_assert_eq!(self.error(), Ok(()));
649  }
650
651  #[inline]
652  fn set_uniform_1ui(&self, location: &UniformLocation, data: u32) {
653    let () = unsafe { gl::Uniform1ui(location.0, data) };
654    debug_assert_eq!(self.error(), Ok(()));
655  }
656
657  #[inline]
658  fn set_uniform_1iv(&self, location: &UniformLocation, data: &[i32]) {
659    let () = unsafe { gl::Uniform1iv(location.0, data.len() as _, data.as_ptr()) };
660    debug_assert_eq!(self.error(), Ok(()));
661  }
662
663  #[inline]
664  fn set_uniform_1fv(&self, location: &UniformLocation, data: &[f32]) {
665    let () = unsafe { gl::Uniform1fv(location.0, data.len() as _, data.as_ptr()) };
666    debug_assert_eq!(self.error(), Ok(()));
667  }
668
669  #[inline]
670  fn set_uniform_3f(&self, location: &UniformLocation, data: &[f32; 3]) {
671    let () = unsafe { gl::Uniform3fv(location.0, 1, data.as_ptr()) };
672    debug_assert_eq!(self.error(), Ok(()));
673  }
674
675  #[inline]
676  fn set_uniform_4f(&self, location: &UniformLocation, data: &[f32; 4]) {
677    let () = unsafe { gl::Uniform4fv(location.0, 1, data.as_ptr()) };
678    debug_assert_eq!(self.error(), Ok(()));
679  }
680
681  #[inline]
682  fn set_uniform_matrices(&self, location: &UniformLocation, matrices: &[[f32; 16]]) {
683    self.set_uniform_matrices_impl(location, matrices)
684  }
685
686  #[inline]
687  fn set_uniform_matrix(&self, location: &UniformLocation, matrix: &[f32; 16]) {
688    self.set_uniform_matrices_impl(location, slice::from_ref(matrix))
689  }
690
691  #[inline]
692  fn create_vertex_buffer(&self) -> Result<VertexBufferObject, Error> {
693    let mut vbo = 0;
694    let () = unsafe { gl::GenBuffers(1, &mut vbo) };
695    let () = self.error()?;
696    Ok(VertexBufferObject(vbo))
697  }
698
699  #[inline]
700  fn delete_vertex_buffer(&self, vbo: &VertexBufferObject) {
701    let () = unsafe { gl::DeleteBuffers(1, &vbo.0) };
702    debug_assert_eq!(self.error(), Ok(()));
703  }
704
705  #[inline]
706  fn bind_vertex_buffer(&self, target: VertexBufferTarget, vbo: Option<&VertexBufferObject>) {
707    let vbo = vbo.map(|vbo| vbo.0).unwrap_or(0);
708    let () = unsafe { gl::BindBuffer(target as _, vbo) };
709    debug_assert_eq!(self.error(), Ok(()));
710  }
711
712  #[inline]
713  fn set_vertex_buffer_data<T>(
714    &self,
715    target: VertexBufferTarget,
716    usage: VertexBufferUsage,
717    data: &[T],
718  ) {
719    let () = unsafe {
720      gl::BufferData(
721        target as _,
722        size_of_val(data) as _,
723        data.as_ptr().cast(),
724        usage as _,
725      )
726    };
727    debug_assert_eq!(self.error(), Ok(()));
728  }
729
730  #[inline]
731  fn set_vertex_buffer_sub_data<T>(&self, target: VertexBufferTarget, data: &[T], offset: i32) {
732    let () = unsafe {
733      gl::BufferSubData(
734        target as _,
735        offset as _,
736        size_of_val(data) as _,
737        data.as_ptr().cast(),
738      )
739    };
740    debug_assert_eq!(self.error(), Ok(()));
741  }
742
743  #[inline]
744  fn create_vertex_array(&self) -> Result<VertexArrayObject, Error> {
745    let mut vao = 0;
746    let () = unsafe { gl::GenVertexArrays(1, &mut vao) };
747    let () = self.error()?;
748    Ok(VertexArrayObject(vao))
749  }
750
751  #[inline]
752  fn delete_vertex_array(&self, vao: &VertexArrayObject) {
753    let () = unsafe { gl::DeleteVertexArrays(1, &vao.0) };
754    debug_assert_eq!(self.error(), Ok(()));
755  }
756
757  #[inline]
758  fn bind_vertex_array(&self, vao: Option<&VertexArrayObject>) {
759    let vao = vao.map(|vao| vao.0).unwrap_or(0);
760    let () = unsafe { gl::BindVertexArray(vao) };
761    debug_assert_eq!(self.error(), Ok(()));
762  }
763
764  #[inline]
765  fn enable_vertex_attrib_array(&self, idx: u32) {
766    let () = unsafe { gl::EnableVertexAttribArray(idx) };
767    debug_assert_eq!(self.error(), Ok(()));
768  }
769
770  #[inline]
771  fn set_vertex_attrib_pointer(
772    &self,
773    idx: u32,
774    size: i32,
775    ty: Type,
776    normalize: bool,
777    stride: i32,
778    offset: i32,
779  ) {
780    let () = unsafe {
781      gl::VertexAttribPointer(
782        idx,
783        size,
784        ty as _,
785        normalize as u8,
786        stride,
787        offset as *const _,
788      )
789    };
790    debug_assert_eq!(self.error(), Ok(()));
791  }
792
793  fn create_texture(&self) -> Result<Texture, Error> {
794    let mut id = 0;
795    let () = unsafe { gl::GenTextures(1, &mut id) };
796    let () = self.error()?;
797    Ok(Texture(id))
798  }
799
800  #[inline]
801  fn delete_texture(&self, texture: &Texture) {
802    let () = unsafe { gl::DeleteTextures(1, &texture.0) };
803    debug_assert_eq!(self.error(), Ok(()));
804  }
805
806  #[inline]
807  fn bind_texture(&self, target: TextureTarget, texture: Option<&Texture>) {
808    let texture = texture.map(|texture| texture.0).unwrap_or(0);
809    let () = unsafe { gl::BindTexture(target as _, texture) };
810    debug_assert_eq!(self.error(), Ok(()));
811  }
812
813  #[inline]
814  fn set_active_texture_unit(&self, unit: u32) {
815    let () = unsafe { gl::ActiveTexture(gl::TEXTURE0 + unit) };
816    debug_assert_eq!(self.error(), Ok(()));
817  }
818
819  #[inline]
820  fn set_texture_image_2d(
821    &self,
822    target: TextureTarget,
823    internal_format: TextureInternalFormat,
824    pixel_format: TexturePixelFormat,
825    channel_type: Type,
826    w: u32,
827    h: u32,
828    pixels: Option<&[u8]>,
829  ) -> Result<(), Error> {
830    let level = 0;
831    let border = 0;
832
833    let () = unsafe {
834      gl::TexImage2D(
835        target as _,
836        level,
837        internal_format as _,
838        w as _,
839        h as _,
840        border,
841        pixel_format as _,
842        channel_type as _,
843        pixels
844          .map(|pixels| pixels.as_ptr().cast())
845          .unwrap_or_default(),
846      )
847    };
848    let () = self.error()?;
849    Ok(())
850  }
851
852  #[inline]
853  fn set_texture_image_3d(
854    &self,
855    target: TextureTarget,
856    internal_format: TextureInternalFormat,
857    pixel_format: TexturePixelFormat,
858    channel_type: Type,
859    w: u32,
860    h: u32,
861    count: u32,
862    pixels: Option<&[u8]>,
863  ) -> Result<(), Error> {
864    let level = 0;
865    let border = 0;
866
867    let () = unsafe {
868      gl::TexImage3D(
869        target as _,
870        level,
871        internal_format as _,
872        w as _,
873        h as _,
874        count as _,
875        border,
876        pixel_format as _,
877        channel_type as _,
878        pixels
879          .map(|pixels| pixels.as_ptr().cast())
880          .unwrap_or_default(),
881      )
882    };
883    let () = self.error()?;
884    Ok(())
885  }
886
887  #[inline]
888  fn set_texture_sub_image_3d(
889    &self,
890    target: TextureTarget,
891    pixel_format: TexturePixelFormat,
892    channel_type: Type,
893    x: u32,
894    y: u32,
895    z: u32,
896    w: u32,
897    h: u32,
898    pixels: &[u8],
899  ) -> Result<(), Error> {
900    let level = 0;
901    let depth = 1;
902
903    let () = unsafe {
904      gl::TexSubImage3D(
905        target as _,
906        level,
907        x as _,
908        y as _,
909        z as _,
910        w as _,
911        h as _,
912        depth,
913        pixel_format as _,
914        channel_type as _,
915        pixels.as_ptr().cast(),
916      )
917    };
918    let () = self.error()?;
919    Ok(())
920  }
921
922  #[inline]
923  fn set_texture_filter(
924    &self,
925    target: TextureTarget,
926    ty: TextureFilterType,
927    filter: TextureFilter,
928  ) {
929    let () = unsafe { gl::TexParameteri(target as _, ty as _, filter as _) };
930    debug_assert_eq!(self.error(), Ok(()));
931  }
932
933  #[inline]
934  fn set_texture_compare_mode(&self, target: TextureTarget, mode: TextureCompareMode) {
935    let () = unsafe { gl::TexParameteri(target as _, gl::TEXTURE_COMPARE_MODE, mode as _) };
936    debug_assert_eq!(self.error(), Ok(()));
937  }
938
939  #[inline]
940  fn set_texture_compare_func(&self, target: TextureTarget, func: Func) {
941    let () = unsafe { gl::TexParameteri(target as _, gl::TEXTURE_COMPARE_FUNC, func as _) };
942    debug_assert_eq!(self.error(), Ok(()));
943  }
944
945  #[inline]
946  fn set_texture_wrap(&self, target: TextureTarget, wrap: TextureWrap) {
947    let () = unsafe { gl::TexParameteri(target as _, gl::TEXTURE_WRAP_S, wrap as _) };
948    let () = unsafe { gl::TexParameteri(target as _, gl::TEXTURE_WRAP_T, wrap as _) };
949    debug_assert_eq!(self.error(), Ok(()));
950  }
951
952  #[inline]
953  fn generate_mipmaps(&self, target: TextureTarget) {
954    let () = unsafe { gl::GenerateMipmap(target as _) };
955    debug_assert_eq!(self.error(), Ok(()));
956  }
957}
958
959
960#[cfg(test)]
961mod tests {
962  use super::*;
963
964
965  /// Check that the `Debug` impl for the [`Error`] type works as
966  /// expected.
967  #[test]
968  fn error_debug_impl() {
969    let err = Error(gl::INVALID_VALUE);
970    let s = format!("{err:?}");
971    assert_eq!(s, "OpenGL error: invalid value (0x501)");
972
973    let err = Error(0xfff);
974    let s = format!("{err:?}");
975    assert_eq!(s, "OpenGL error: 0xfff");
976  }
977}