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 {}