tiny_gl/gl/
mod.rs

1use bitflags::bitflags;
2use core::{ffi::CStr, num::NonZeroU32};
3
4#[cfg(feature = "alloc")]
5use alloc::string::String;
6
7pub mod ffi;
8
9#[derive(Copy, Clone)]
10pub struct Shader(pub NonZeroU32);
11
12#[derive(Copy, Clone)]
13pub struct Program(pub NonZeroU32);
14
15#[derive(Copy, Clone)]
16pub struct Buffer(pub NonZeroU32);
17
18#[derive(Copy, Clone)]
19pub struct VertexArray(pub NonZeroU32);
20
21#[derive(Copy, Clone)]
22pub struct Texture(pub NonZeroU32);
23
24#[derive(Copy, Clone)]
25pub struct Framebuffer(pub NonZeroU32);
26
27#[derive(Copy, Clone)]
28pub struct UniformLocation(pub i32);
29
30#[repr(u32)]
31pub enum ShaderType {
32    Vertex = ffi::GL_VERTEX_SHADER,
33    Fragment = ffi::GL_FRAGMENT_SHADER,
34    Geometry = ffi::GL_GEOMETRY_SHADER,
35    Compute = ffi::GL_COMPUTE_SHADER,
36}
37
38#[derive(Copy, Clone)]
39#[repr(u32)]
40pub enum DrawMode {
41    Points = ffi::GL_POINTS,
42    Lines = ffi::GL_LINES,
43    LineLoop = ffi::GL_LINE_LOOP,
44    LineStrip = ffi::GL_LINE_STRIP,
45    Triangles = ffi::GL_TRIANGLES,
46    TriangleStrip = ffi::GL_TRIANGLE_STRIP,
47    TriangleFan = ffi::GL_TRIANGLE_FAN,
48    LinesAdjacency = ffi::GL_LINES_ADJACENCY,
49    LineStripAdjacency = ffi::GL_LINE_STRIP_ADJACENCY,
50    TrianglesAdjacency = ffi::GL_TRIANGLES_ADJACENCY,
51    TriangleStripAdjacency = ffi::GL_TRIANGLE_STRIP_ADJACENCY,
52    Patches = ffi::GL_PATCHES,
53}
54
55#[repr(u32)]
56pub enum BufferUsage {
57    /// The data store contents will be modified once and used at most a few times.
58    /// The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
59    StreamDraw = ffi::GL_STREAM_DRAW,
60
61    /// The data store contents will be modified once and used at most a few times.
62    /// The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
63    StreamRead = ffi::GL_STREAM_READ,
64
65    /// The data store contents will be modified once and used at most a few times.
66    /// The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
67    StreamCopy = ffi::GL_STREAM_COPY,
68
69    /// The data store contents will be modified once and used many times.
70    /// The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
71    StaticDraw = ffi::GL_STATIC_DRAW,
72
73    /// The data store contents will be modified once and used many times.
74    /// The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
75    StaticRead = ffi::GL_STATIC_READ,
76
77    /// The data store contents will be modified once and used many times.
78    /// The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
79    StaticCopy = ffi::GL_STATIC_COPY,
80
81    /// The data store contents will be modified repeatedly and used many times.
82    /// The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
83    DynamicDraw = ffi::GL_DYNAMIC_DRAW,
84
85    /// The data store contents will be modified repeatedly and used many times.
86    /// The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
87    DynamicRead = ffi::GL_DYNAMIC_READ,
88
89    /// The data store contents will be modified repeatedly and used many times.
90    /// The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
91    DynamicCopy = ffi::GL_DYNAMIC_COPY,
92}
93
94#[derive(Copy, Clone)]
95#[repr(u32)]
96pub enum VertexAttribDataType {
97    Byte = ffi::GL_BYTE,
98    Short = ffi::GL_SHORT,
99    Int = ffi::GL_INT,
100    Fixed = ffi::GL_FIXED,
101    Float = ffi::GL_FLOAT,
102    HalfFloat = ffi::GL_HALF_FLOAT,
103    Double = ffi::GL_DOUBLE,
104    UnsignedByte = ffi::GL_UNSIGNED_BYTE,
105    UnsignedShort = ffi::GL_UNSIGNED_SHORT,
106    UnsignedInt = ffi::GL_UNSIGNED_INT,
107    // Currently unsupported:
108    // GL_INT_2_10_10_10_REV
109    // GL_UNSIGNED_INT_2_10_10_10_REV
110    // GL_UNSIGNED_INT_10F_11F_11F_REV
111}
112
113#[repr(u32)]
114pub enum TextureTarget {
115    Texture1D = ffi::GL_TEXTURE_1D,
116    Texture2D = ffi::GL_TEXTURE_2D,
117    Texture3D = ffi::GL_TEXTURE_3D,
118    Texture1DArray = ffi::GL_TEXTURE_1D_ARRAY,
119    Texture2DArray = ffi::GL_TEXTURE_2D_ARRAY,
120    TextureRectangle = ffi::GL_TEXTURE_RECTANGLE,
121    TextureCubeMap = ffi::GL_TEXTURE_CUBE_MAP,
122    TextureCubeMapArray = ffi::GL_TEXTURE_CUBE_MAP_ARRAY,
123    TextureBuffer = ffi::GL_TEXTURE_BUFFER,
124    Texture2DMultisample = ffi::GL_TEXTURE_2D_MULTISAMPLE,
125    Texture2DMultisampleArray = ffi::GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
126}
127
128#[derive(Copy, Clone)]
129#[repr(u32)]
130pub enum TextureSizedInternalFormat {
131    Rgba8 = ffi::GL_RGBA8,
132    Depth24Stencil8 = ffi::GL_DEPTH24_STENCIL8,
133    //TODO Others are available...
134}
135
136#[derive(Copy, Clone)]
137pub enum FramebufferAttachment {
138    /// May range from zero to the value of GL_MAX_COLOR_ATTACHMENTS minus one.
139    ColorAttachment(u8),
140    DepthAttachment,
141    StencilAttachment,
142    DepthStencilAttachment,
143}
144
145impl FramebufferAttachment {
146    const fn to_u32(self) -> u32 {
147        match self {
148            FramebufferAttachment::ColorAttachment(i) => ffi::GL_COLOR_ATTACHMENT0 + i as u32,
149            FramebufferAttachment::DepthAttachment => ffi::GL_DEPTH_ATTACHMENT,
150            FramebufferAttachment::StencilAttachment => ffi::GL_STENCIL_ATTACHMENT,
151            FramebufferAttachment::DepthStencilAttachment => ffi::GL_DEPTH_STENCIL_ATTACHMENT,
152        }
153    }
154}
155
156bitflags! {
157    #[derive(Copy, Clone)]
158    pub struct ClearMask: u32 {
159        const COLOR_BUFFER = ffi::GL_COLOR_BUFFER_BIT;
160        const DEPTH_BUFFER = ffi::GL_DEPTH_BUFFER_BIT;
161        const STENCIL_BUFFER = ffi::GL_STENCIL_BUFFER_BIT;
162    }
163}
164
165#[derive(Copy, Clone)]
166#[repr(u32)]
167pub enum Capability {
168    DepthTest = ffi::GL_DEPTH_TEST,
169    DebugOutput = ffi::GL_DEBUG_OUTPUT,
170}
171
172#[derive(Copy, Clone)]
173#[repr(u32)]
174pub enum DepthFunction {
175    Never = ffi::GL_NEVER,
176    Less = ffi::GL_LESS,
177    Equal = ffi::GL_EQUAL,
178    Lequal = ffi::GL_LEQUAL,
179    Greater = ffi::GL_GREATER,
180    Notequal = ffi::GL_NOTEQUAL,
181    Gequal = ffi::GL_GEQUAL,
182    Always = ffi::GL_ALWAYS,
183}
184
185/// Sets the depth function used for depth buffer comparisons.
186/// The initial value is [`DepthFunction::Less`].
187///
188/// # Notes
189/// See [OpenGL documentation](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDepthFunc.xhtml).
190///
191/// Wraps [`ffi::glDepthFunc`].
192pub unsafe fn depth_function(func: DepthFunction) {
193    ffi::glDepthFunc(func as _);
194}
195
196/// Enables or disables writing to the depth buffer.
197///
198/// # Notes
199/// See [OpenGL documentation](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDepthMask.xhtml).
200///
201/// Wraps [`ffi::glDepthMask`].
202pub unsafe fn depth_mask(flag: bool) {
203    ffi::glDepthMask(flag as _);
204}
205
206/// Enables or disables depth testing.
207///
208/// # Notes
209/// See [OpenGL documentation](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnable.xhtml).
210///
211/// Wraps [`ffi::glEnable`] and [`ffi::glDisable`].
212pub unsafe fn depth_test(value: bool) {
213    if value {
214        enable(Capability::DepthTest);
215    } else {
216        disable(Capability::DepthTest);
217    }
218}
219
220/// Enables a GL capability.
221///
222/// # Notes
223/// See [OpenGL documentation](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnable.xhtml).
224///
225/// Wraps [`ffi::glEnable`].
226pub unsafe fn enable(capability: Capability) {
227    ffi::glEnable(capability as _);
228}
229
230/// Disables a GL capability.
231///
232/// # Notes
233/// See [OpenGL documentation](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnable.xhtml).
234///
235/// Wraps [`ffi::glDisable`].
236pub unsafe fn disable(capability: Capability) {
237    ffi::glDisable(capability as _);
238}
239
240pub unsafe fn create_shader(ty: ShaderType) -> Option<Shader> {
241    NonZeroU32::new(ffi::glCreateShader(ty as _)).map(Shader)
242}
243
244pub unsafe fn delete_shader(shader: Shader) {
245    ffi::glDeleteShader(shader.0.get());
246}
247
248pub unsafe fn shader_source(shader: Shader, source: &str) {
249    let ptr = source.as_ptr() as *const ffi::GLchar;
250    let length = source.len() as i32;
251    ffi::glShaderSource(shader.0.get(), 1, &ptr, &length);
252}
253
254pub unsafe fn compile_shader(shader: Shader) {
255    ffi::glCompileShader(shader.0.get());
256}
257
258pub unsafe fn get_shader_compile_status(shader: Shader) -> bool {
259    let mut status = 0;
260    ffi::glGetShaderiv(shader.0.get(), ffi::GL_COMPILE_STATUS, &mut status);
261    status == 1
262}
263
264#[cfg(feature = "alloc")]
265pub unsafe fn get_shader_info_log(shader: Shader) -> String {
266    let mut length = 0;
267    ffi::glGetShaderiv(shader.0.get(), ffi::GL_INFO_LOG_LENGTH, &mut length);
268
269    if length > 0 {
270        use core::mem::MaybeUninit;
271
272        use alloc::vec::Vec;
273
274        let mut log: Vec<MaybeUninit<u8>> = Vec::with_capacity(length as _);
275
276        ffi::glGetShaderInfoLog(shader.0.get(), length, &mut length, log.as_mut_ptr() as _);
277
278        log.set_len(length as _);
279
280        let log: Vec<u8> = core::mem::transmute(log);
281        String::from_utf8_unchecked(log)
282    } else {
283        String::new()
284    }
285}
286
287pub unsafe fn create_program() -> Option<Program> {
288    NonZeroU32::new(ffi::glCreateProgram()).map(Program)
289}
290
291pub unsafe fn attach_shader(program: Program, shader: Shader) {
292    ffi::glAttachShader(program.0.get(), shader.0.get());
293}
294
295pub unsafe fn link_program(program: Program) {
296    ffi::glLinkProgram(program.0.get());
297}
298
299pub unsafe fn use_program(program: Option<Program>) {
300    let raw = program.map(|p| p.0.get()).unwrap_or(0);
301
302    ffi::glUseProgram(raw);
303}
304
305pub unsafe fn get_program_link_status(program: Program) -> bool {
306    let mut status = 0;
307    ffi::glGetProgramiv(program.0.get(), ffi::GL_LINK_STATUS, &mut status);
308    status == 1
309}
310
311pub unsafe fn delete_program(program: Program) {
312    ffi::glDeleteProgram(program.0.get());
313}
314
315#[cfg(feature = "alloc")]
316pub unsafe fn get_program_info_log(program: Program) -> String {
317    let mut length = 0;
318    ffi::glGetProgramiv(program.0.get(), ffi::GL_INFO_LOG_LENGTH, &mut length);
319
320    if length > 0 {
321        use core::mem::MaybeUninit;
322
323        use alloc::vec::Vec;
324
325        let mut log: Vec<MaybeUninit<u8>> = Vec::with_capacity(length as _);
326
327        ffi::glGetProgramInfoLog(program.0.get(), length, &mut length, log.as_mut_ptr() as _);
328
329        log.set_len(length as _);
330
331        let log: Vec<u8> = core::mem::transmute(log);
332        String::from_utf8_unchecked(log)
333    } else {
334        String::new()
335    }
336}
337
338pub unsafe fn draw_arrays(mode: DrawMode, first: i32, count: i32) {
339    ffi::glDrawArrays(mode as _, first, count);
340}
341
342pub unsafe fn create_named_buffer() -> Option<Buffer> {
343    let mut buffer = 0;
344    ffi::glCreateBuffers(1, &mut buffer);
345    NonZeroU32::new(buffer).map(Buffer)
346}
347
348/// creates and initializes a buffer object's data store.
349///
350/// # Parameters
351/// - `buffer`: The buffer object to initialize.
352/// - `size`: The size of the buffer object's new data store.
353/// - `usage`: The usage pattern of the buffer object's data store.
354///
355/// # Notes
356/// Wraps [ffi::glNamedBufferData].
357pub unsafe fn named_buffer_data_size(buffer: Buffer, size: i32, usage: BufferUsage) {
358    ffi::glNamedBufferData(buffer.0.get(), size as _, core::ptr::null(), usage as _);
359}
360
361/// creates and initializes a buffer object's data store.
362///
363/// # Parameters
364/// - `buffer`: The buffer object to initialize.
365/// - `data`: The data that will be copied into the buffer object's data store for initialization.
366/// - `usage`: The usage pattern of the buffer object's data store.
367///
368/// # Notes
369/// Wraps [ffi::glNamedBufferData].
370pub unsafe fn named_buffer_data_u8_slice(buffer: Buffer, data: &[u8], usage: BufferUsage) {
371    ffi::glNamedBufferData(
372        buffer.0.get(),
373        data.len() as _,
374        data.as_ptr() as _,
375        usage as _,
376    );
377}
378
379pub unsafe fn delete_buffer(buffer: Buffer) {
380    ffi::glDeleteBuffers(1, &buffer.0.get());
381}
382
383pub unsafe fn create_named_vertex_array() -> Option<VertexArray> {
384    let mut vertex_array = 0;
385    ffi::glCreateVertexArrays(1, &mut vertex_array);
386    NonZeroU32::new(vertex_array).map(VertexArray)
387}
388
389pub unsafe fn delete_vertex_array(vertex_array: VertexArray) {
390    ffi::glDeleteVertexArrays(1, &vertex_array.0.get());
391}
392
393pub unsafe fn enable_vertex_array_attrib(vertex_array: VertexArray, index: u32) {
394    ffi::glEnableVertexArrayAttrib(vertex_array.0.get(), index);
395}
396
397pub unsafe fn vertex_array_element_buffer(vertex_array: VertexArray, buffer: Buffer) {
398    ffi::glVertexArrayElementBuffer(vertex_array.0.get(), buffer.0.get());
399}
400
401pub unsafe fn vertex_array_vertex_buffer(
402    vertex_array: VertexArray,
403    binding_index: u32,
404    buffer: Option<Buffer>,
405    offset: i32,
406    stride: i32,
407) {
408    let buffer = buffer.map_or(0, |b| b.0.get());
409    ffi::glVertexArrayVertexBuffer(
410        vertex_array.0.get(),
411        binding_index,
412        buffer,
413        offset as _,
414        stride,
415    );
416}
417
418pub unsafe fn vertex_array_attrib_format(
419    vertex_array: VertexArray,
420    index: u32,
421    size: i32,
422    data_type: VertexAttribDataType,
423    normalized: bool,
424    relative_offset: u32,
425) {
426    ffi::glVertexArrayAttribFormat(
427        vertex_array.0.get(),
428        index,
429        size,
430        data_type as _,
431        normalized as _,
432        relative_offset as _,
433    );
434}
435
436pub unsafe fn vertex_array_attrib_binding(
437    vertex_array: VertexArray,
438    attrib_index: u32,
439    binding_index: u32,
440) {
441    ffi::glVertexArrayAttribBinding(vertex_array.0.get(), attrib_index, binding_index);
442}
443
444pub unsafe fn create_texture(target: TextureTarget) -> Option<Texture> {
445    let mut texture = 0;
446    ffi::glCreateTextures(target as _, 1, &mut texture);
447    NonZeroU32::new(texture).map(Texture)
448}
449
450pub unsafe fn delete_texture(texture: Texture) {
451    ffi::glDeleteTextures(1, &texture.0.get());
452}
453
454pub unsafe fn texture_storage_2d(
455    texture: Texture,
456    levels: i32,
457    internal_format: TextureSizedInternalFormat,
458    width: i32,
459    height: i32,
460) {
461    ffi::glTextureStorage2D(
462        texture.0.get(),
463        levels as _,
464        internal_format as _,
465        width as _,
466        height as _,
467    );
468}
469
470pub unsafe fn create_framebuffer() -> Option<Framebuffer> {
471    let mut framebuffer = 0;
472    ffi::glCreateFramebuffers(1, &mut framebuffer);
473    NonZeroU32::new(framebuffer).map(Framebuffer)
474}
475
476pub unsafe fn delete_framebuffer(framebuffer: Framebuffer) {
477    ffi::glDeleteFramebuffers(1, &framebuffer.0.get());
478}
479
480pub unsafe fn named_framebuffer_texture(
481    framebuffer: Framebuffer,
482    attachment: FramebufferAttachment,
483    texture: Texture,
484    level: i32,
485) {
486    ffi::glNamedFramebufferTexture(
487        framebuffer.0.get(),
488        attachment.to_u32(),
489        texture.0.get(),
490        level as _,
491    );
492}
493
494pub unsafe fn bind_vertex_array(vertex_array: VertexArray) {
495    ffi::glBindVertexArray(vertex_array.0.get());
496}
497
498//TODO Return Option<UniformLocation> when raw == -1
499pub unsafe fn get_uniform_location(program: Program, name: &CStr) -> UniformLocation {
500    let raw = ffi::glGetUniformLocation(program.0.get(), name.as_ptr() as _);
501    UniformLocation(raw)
502}
503
504pub unsafe fn program_uniform_1_f32(program: Program, location: UniformLocation, value: f32) {
505    ffi::glProgramUniform1f(program.0.get(), location.0, value);
506}
507
508/// Specify the value of a uniform variable for a specified program object
509/// This function takes a single 4x4 matrix.
510///
511/// # Parameters
512/// - `program`: The program object to which the uniform variable belongs.
513/// - `location`: The location of the uniform variable.
514/// - `transpose`: Specifies whether to transpose the matrix.
515/// - `value`: The matrix value.
516///
517/// # Notes
518///
519/// Wraps [ffi::glProgramUniformMatrix4fv].
520pub unsafe fn program_uniform_1_mat4(
521    program: Program,
522    location: UniformLocation,
523    transpose: bool,
524    value: &[f32],
525) {
526    ffi::glProgramUniformMatrix4fv(
527        program.0.get(),
528        location.0,
529        1,
530        transpose as _,
531        value.as_ptr(),
532    );
533}
534
535pub unsafe fn clear_color(red: f32, green: f32, blue: f32, alpha: f32) {
536    ffi::glClearColor(red, green, blue, alpha);
537}
538
539pub unsafe fn clear(mask: ClearMask) {
540    ffi::glClear(mask.bits());
541}
542
543pub unsafe fn viewport(x: i32, y: i32, width: i32, height: i32) {
544    ffi::glViewport(x, y, width, height);
545}
546
547//TODO Make functions for all of these:
548// glNamedBufferStorage
549//
550// glDebugMessageCallback
551// glDebugMessageControl