Skip to main content

golem/
lib.rs

1//! `golem` is an opinionated mostly-safe graphics API
2//!
3//! When possible, `golem` should make simple things safe (bind objects before acting on them, or
4//! check if they're bound for objects that are expensive to bind.) However, when not possible or
5//! convenient (bounds checking the indices in an element buffer, for example), `golem` provides
6//! unsafe APIs with well-defined safety conditions.
7//!
8//! A minimal example to display a triangle:
9//!
10//! ```rust
11//! # use golem::*;
12//! # use golem::Dimension::*;
13//! # fn func(ctx: &Context) -> Result<(), GolemError> {
14//! let vertices = [
15//!     // Position         Color
16//!     -0.5, -0.5,         1.0, 0.0, 0.0, 1.0,
17//!     0.5, -0.5,          0.0, 1.0, 0.0, 1.0,
18//!     0.0, 0.5,           0.0, 0.0, 1.0, 1.0
19//! ];
20//! let indices = [0, 1, 2];
21//!
22//! let mut shader = ShaderProgram::new(
23//!     ctx,
24//!     ShaderDescription {
25//!         vertex_input: &[
26//!             Attribute::new("vert_position", AttributeType::Vector(D2)),
27//!             Attribute::new("vert_color", AttributeType::Vector(D4)),
28//!         ],
29//!         fragment_input: &[Attribute::new("frag_color", AttributeType::Vector(D4))],
30//!         uniforms: &[],
31//!         vertex_shader: r#" void main() {
32//!         gl_Position = vec4(vert_position, 0, 1);
33//!         frag_color = vert_color;
34//!     }"#,
35//!         fragment_shader: r#" void main() {
36//!         gl_FragColor = frag_color;
37//!     }"#,
38//!     },
39//! )?;
40//!
41//! let mut vb = VertexBuffer::new(ctx)?;
42//! let mut eb = ElementBuffer::new(ctx)?;
43//! vb.set_data(&vertices);
44//! eb.set_data(&indices);
45//! shader.bind();
46//!
47//! ctx.clear();
48//! unsafe {
49//!     shader.draw(&vb, &eb, 0..indices.len(), GeometryMode::Triangles)?;
50//! }
51//! # Ok(()) }
52//! ```
53//!
54//! The core type of `golem` is the [`Context`], which is constructed from the [`glow Context`].
55//! From the [`Context`], [`ShaderProgram`]s are created, which take in data from [`Buffer`]s. Once
56//! the data is uploaded to the GPU via [`Buffer::set_data`], it can be drawn via [`ShaderProgram::draw`].
57//!
58//! ## Initializing
59//!
60//! The user is responsible for windowing and providing a valid [`glow Context`] to create a
61//! [`Context`]. You can try out the [`blinds`](https://crates.io/crates/blinds) crate, which works
62//! well with `golem`, but using `winit` directly or other windowing solutions like `sdl2` are also
63//! options.
64//!
65//! ## OpenGL Versions
66//! It currently is implemented via glow, and it targets OpenGL 3.2 on desktop and WebGL 1 (so it
67//! should run on a wide range of hardware.) GL 3.2 is selected for maximum desktop availability,
68//! and WebGL 1 is available on 97% of clients to WebGL's 75% (taken from caniuse.com at time of
69//! writing.)
70//!
71//! [`Context`]: crate::Context
72//! [`glow Context`]: glow::Context
73
74#![cfg_attr(not(feature = "std"), no_std)]
75
76extern crate alloc;
77
78use alloc::fmt::{Display, Formatter, Result as FmtResult};
79use alloc::string::String;
80
81// TODO: add out-of-memory to GolemError?
82// TODO: unsafe audit: check for possible GL error conditions, and track them
83
84use glow::HasContext;
85
86type GlTexture = <glow::Context as HasContext>::Texture;
87type GlProgram = <glow::Context as HasContext>::Program;
88type GlShader = <glow::Context as HasContext>::Shader;
89type GlFramebuffer = <glow::Context as HasContext>::Framebuffer;
90type GlBuffer = <glow::Context as HasContext>::Buffer;
91type GlVertexArray = <glow::Context as HasContext>::VertexArray;
92
93mod attribute;
94mod buffer;
95mod context;
96mod shader;
97mod surface;
98mod texture;
99mod uniform;
100
101pub mod blend;
102pub mod depth;
103
104pub use self::attribute::{Attribute, AttributeType};
105pub use self::buffer::{Buffer, ElementBuffer, VertexBuffer};
106pub use self::context::Context;
107pub use self::shader::{ShaderDescription, ShaderProgram};
108pub use self::surface::Surface;
109pub use self::texture::{Texture, TextureFilter, TextureWrap};
110pub use self::uniform::{Uniform, UniformType, UniformValue};
111
112pub use glow;
113
114pub(crate) enum Position {
115    Input,
116    Output,
117}
118
119/// Used to determine whether shader uniforms are ints or floats
120pub enum NumberType {
121    Int,
122    Float,
123}
124
125/// How a pixel's color is laid out in memory
126pub enum ColorFormat {
127    /// One red pixel byte, followed by one blue, and one green
128    RGB,
129    /// One red, blue, green, then alpha (transparency)
130    RGBA,
131}
132
133impl ColorFormat {
134    pub fn bytes_per_pixel(&self) -> u32 {
135        match self {
136            ColorFormat::RGBA => 4,
137            ColorFormat::RGB => 3,
138        }
139    }
140
141    fn gl_format(&self) -> u32 {
142        match self {
143            ColorFormat::RGB => glow::RGB,
144            ColorFormat::RGBA => glow::RGBA,
145        }
146    }
147}
148
149#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
150/// The dimensionality of a vector or matrix shader input
151///
152/// D2 indicates a Vector2 or Matrix2x2, etc.
153pub enum Dimension {
154    D2 = 2,
155    D3 = 3,
156    D4 = 4,
157}
158
159#[derive(Copy, Clone, Hash, PartialEq, Eq)]
160/// The GeometryMode determines how the data is drawn during [`ShaderProgram::draw`]
161pub enum GeometryMode {
162    /// Each element forms a single point
163    ///
164    /// `[1, 2, 3, 4, 5, 6] -> [(1), (2), (3), (4), (5), (6)]`
165    Points,
166    /// Each pair of elements forms a thin line
167    ///
168    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2), (3, 4), (5, 6)]`
169    Lines,
170    /// Each pair of elements forms a chain of lines
171    ///
172    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]`
173    LineStrip,
174    /// Each pair of elements forms a chain of lines, connected to the original
175    ///
176    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 1)]`
177    LineLoop,
178    /// Each trio of elements forms a distinct triangle
179    ///
180    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2, 3), (4, 5, 6)]`
181    Triangles,
182    /// Each trio of elements forms a triangle, with the next vertex taking the previous two
183    ///
184    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]`
185    TriangleStrip,
186    /// The first elements forms the center of a fan, with each pair of vertices forming a triangle
187    ///
188    /// `[1, 2, 3, 4, 5, 6] -> [(1, 2, 3), (1, 3, 4), (1, 4, 5), (1, 5, 6)]`
189    TriangleFan,
190}
191
192#[derive(Debug)]
193/// The library's error conditions
194pub enum GolemError {
195    /// The OpenGL Shader compilation failed, with the given error message
196    ///
197    /// This may be during vertex-time, fragment-time, or link-time
198    ShaderCompilationError(String),
199    /// Some general error bubbling up from the GL context
200    ContextError(String),
201    /// An attempt was made to bind to an illegal uniform
202    NoSuchUniform(String),
203    /// An operation was performed on a shader that wasn't bound
204    ///
205    /// Shader operations include setting uniforms and drawing
206    NotCurrentProgram,
207    /// A texture filter requiring mipmaps was used when mipmaps were unavailable
208    ///
209    /// Mipmaps are only available for minification, and only for power-of-two sized textures (2x2, 4x4, etc.)
210    MipMapsUnavailable,
211    /// A wrap option was set for a Texture that isn't available
212    ///
213    /// Texture repeats are currently only supported for power-of-2 sized textures (2x2, 4x4, etc.)
214    IllegalWrapOption,
215}
216
217impl From<String> for GolemError {
218    fn from(other: String) -> Self {
219        GolemError::ContextError(other)
220    }
221}
222
223impl Display for GolemError {
224    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
225        match self {
226            GolemError::ShaderCompilationError(e) => write!(fmt, "Shader compilation: {}", e),
227            GolemError::ContextError(e) => write!(fmt, "OpenGL: {}", e),
228            GolemError::NoSuchUniform(e) => write!(fmt, "Illegal uniform: {}", e),
229            GolemError::NotCurrentProgram => write!(fmt, "Shader program not bound"),
230            GolemError::MipMapsUnavailable => write!(fmt, "Mipmaps are unavailable"),
231            GolemError::IllegalWrapOption => write!(fmt, "An illegal texture wrap"),
232        }
233    }
234}
235
236#[cfg(feature = "std")]
237impl std::error::Error for GolemError {}