luminance/
lib.rs

1//! # A simple, type-safe and opinionated graphics crate
2//!
3//! luminance is an effort to make graphics rendering simple and elegant. It is a _low-level_
4//! and opinionated graphics API, highly typed (type-level computations, refined types, etc.)
5//! which aims to be simple and performant. Instead of providing users with as many low-level
6//! features as possible, luminance provides you with _some ways_ to do rendering. That has
7//! both advantages and drawbacks:
8//!
9//! - On one side, because the API is opinionated, some dynamic branching and decisions are
10//!   completely removed / optimized. Some operations breaking state mutations or invariant
11//!   violation are not statically constructible, ensuring safety. Because strong typing is
12//!   used, lots of runtime checks are also not needed, helping with performance.
13//! - On the other side, if you want to do something very specific and very low-level, you
14//!   will find luminance not to be friendly as it doesn’t like, most of the time, exposing
15//!   its internal design to the outer world — mostly for runtime safety reason.
16//!
17//! > A note on _safety_: here, _safety_ is not used as with the Rust definiton, but most in
18//! > terms of undefined behavior and unwanted behavior. If something can lead to a weird
19//! > behavior, a crash, a panic or a black screen, it’s considered `unsafe`. That definition
20//! > obviously includes the Rust definiton of safety — memory safety.
21//!
22//! # What’s included?
23//!
24//! luminance is a rendering crate, not a 3D engine nor a video game framework. As so, it doesn’t
25//! include specific concepts, such as lights, materials, asset management nor scene description. It
26//! only provides a rendering library you can plug in whatever you want to.
27//!
28//! > There are several so-called 3D-engines out there on [crates.io](https://crates.io). Feel free
29//! > to have a look around.
30//!
31//! However, luminance comes with several interesting features:
32//!
33//! - **Framebuffers**: framebuffers are used to hold renders. Each time you want to perform a
34//!   render, you need to perform it into a framebuffer. Framebuffers can then be combined with
35//!   each other to produce effects and design render layers — this is called compositing.
36//! - **Shaders**: luminance supports five kinds of shader stages:
37//!     - Vertex shaders.
38//!     - Tessellation control shaders.
39//!     - Tessellation evaluation shaders.
40//!     - Geometry shaders.
41//!     - Fragment shaders.
42//! - **Vertices, indices, primitives and tessellations**: those are used to define a shape you
43//!   can render into a framebuffer with a shader. They are mandatory when it comes to rendering.
44//!   Even if you don’t need vertex data, you still need tessellations to issue draw calls.
45//! - **Textures**: textures represent information packed into arrays on the GPU, and can be used
46//!   to customize a visual aspect or pass information around in shaders. They come in several
47//!   flavours — e.g. 1D, 2D, cube maps, etc.
48//! - **Control on the render state**: the render state is a set of capabilities you can tweak
49//!   to draw frames. It includes:
50//!     - The blending equation and factors. Blending is the process of taking two colors from two
51//!       framebuffers and mixing them.
52//!     - Whether we should have a depth test performed.
53//!     - Face culling.
54//!     - Etc.
55//! - And a lot of other cool things like *GPU commands*, *pipelines*, *uniform interfaces* and so
56//!   on…
57//!
58//! # How to dig in?
59//!
60//! luminance is written to be fairly simple. There are several ways to learn how to use luminance:
61//!
62//! - The [online documentation](https://docs.rs/luminance) is a mandatory start for newcomers.
63//! - The [“Learn luminance” book](https://phaazon.github.io/learn-luminance). Ideal for
64//!   newcomers as well as people already used to luminance, as it’s always updated to the latest
65//!   version — you might learn new things!
66//! - The [luminance-examples](https://github.com/phaazon/luminance-rs/tree/master/examples)
67//!   project. It contains lots of examples describing how to do specifics things. Not adapted for
68//!   newcomers, you will likely want to consult those examples if you’re already familiar with
69//!   graphics programing and to look for how to do a specific thing. Still, for newcomers, the
70//!   [hello-world](../../examples/common/src/hello_world.rs) example might be a good read.
71//!
72//! # Implementation and architecture
73//!
74//! **luminance** has been originally designed around the OpenGL 3.3 and OpenGL 4.5 APIs. However,
75//! it has mutated _a lot_ to adapt to new technologies and modern graphics programming. Even though its API
76//! is _not_ meant to converge towards something like Vulkan, it’s changing over time to meet
77//! better design decisions and performance implications.
78//!
79//! The current state of luminance comprises several crates:
80//!
81//! - A “core” crate, [luminance], which is about all the abstract, common and interface code.
82//! - A proc-macro crate, [luminance-derive], which is exported by [luminance] if you use the `"derive"`
83//!   feature flag. That crate allows to implement various important traits of the core crate.
84//! - A set of _backend implementation_ crates, implementing the [luminance] crate backend interfaces.
85//! - A set of _windowing_ crates, executing your code written with the core and backend crate on native
86//!   systems (most of the time, _windowing platforms_, but not limited to).
87//! - A special crate, [luminance-front], a special _backend_ crate that allows to combine
88//!   several “official” crates to provide a cross-platform experience without having to pick
89//!   several backend crates — the crate does it for you. This crate is mainly designed for end-user
90//!   crates and should be a good fit for most users.
91//!
92//! ## The core crate
93//!
94//! The [luminance] crate gathers all the logic and rendering abstractions necessary to write code
95//! over various graphics technologies. It contains parametric types and functions that abstract over
96//! the actual _implementation type_ — as a convention, the type variable `B` (for backend) is
97//! used.
98//!
99//! Backend types — i.e. `B` — are not provided by [luminance] directly. They are typically
100//! provided by crates containing the name of the technology as suffix, such as [luminance-gl],
101//! [luminance-webgl], luminance-vk, etc. The interface between those backend crates and
102//! luminance is specified in [luminance::backend].
103//!
104//! On a general note, `Something<ConcreteType, u8>` is a monomorphic type that will be usable
105//! **only** with code working over the `ConcreteType` backend. If you want to write a function
106//! that accepts an 8-bit integer something without specifying a concrete type, you will have to
107//! write something along the lines of:
108//!
109//! ```ignore
110//! use luminance::backend::something::Something as SomethingBackend;
111//! use luminance::something::Something;
112//!
113//! fn work<B>(b: &Something<B, u8>) where B: SomethingBackend<u8> {
114//!   todo!();
115//! }
116//! ```
117//!
118//! This kind of code is intended for people writing libraries with luminance. For the more usual case
119//! of using the [luminance-front] crate, you will end up writing something like:
120//!
121//! ```ignore
122//! use luminance_front::something::Something;
123//!
124//! fn work(b: &Something<u8>) {
125//!   todo()!;
126//! }
127//! ```
128//!
129//! > In [luminance-front], the backend type is selected at compile and link time. This is often
130//! > what people want, but keep in mind that [luminance-front] doesn’t allow to have several
131//! > backend types at the same time, which might be something you would like to use, too.
132//!
133//! ## Backend implementations
134//!
135//! Backends implement the [luminance::backend] traits and provide, mostly, a single type for each
136//! implementation. It’s important to understand that a backend crate can provide several backends
137//! (for instance, [luminance-gl] can provide one backend — so one type — for each supported OpenGL
138//! version). That backend type will be used throughout the rest of the ecosystem to deduce subsequent
139//! implementors and associated types.
140//!
141//! If you want to implement a backend, you don’t have to push any code to any `luminance` crate.
142//! `luminance-*` crates are _official_ ones, but you can write your own backend as well. The
143//! interface is highly `unsafe`, though, and based mostly on `unsafe impl` on `unsafe trait`. For
144//! more information, feel free to read the documentation of the [luminance::backend] module.
145//!
146//! ## Windowing
147//!
148//! luminance doesn’t know anything about the context it executes in. That means that it doesn’t
149//! know whether it’s used within SDL, GLFW, glutin, Qt, a web canvas or an embedded specific hardware such as
150//! the Nintendo Switch. That is actually powerful, because it allows luminance to be
151//! completely agnostic of the execution platform it’s running on: one problem less. However, there
152//! is an important point to take into account: a single backend type can be used with several windowing
153//! crates / implementations. That allows to re-use a backend with several windowing
154//! implementations. The backend will typically explain what are the conditions to create it (like,
155//! in OpenGL, the windowing crate must set some specific flags when creating the OpenGL context).
156//!
157//! luminance does not provide a way to create windows because it’s important that it not depend
158//! on windowing libraries – so that end-users can use whatever they like. Furthermore, such
159//! libraries typically implement windowing and event features, which have nothing to do with our
160//! initial purpose.
161//!
162//! A platform crate supporting luminance will typically provide native types by re-exporting
163//! symbols (types, functions, etc.) from a windowing crate and the necessary code to make it
164//! compatible with luminance. That means providing a way to access a backend type, which
165//! implements the [luminance::backend] interface. However, platform crates are not supposed to
166//! be a replacement for the underlying platform system; you will typically still have to depend
167//! it as well.
168//!
169//! ## luminance-derive
170//!
171//! If you are compiling against the `"derive"` feature, you get access to [`luminance-derive`] automatically, which
172//! provides a set of _procedural macros_.
173//!
174//! ### `Vertex`
175//!
176//! The [`Vertex`] derive proc-macro.
177//!
178//! That proc-macro allows you to create custom vertex types easily without having to care about
179//! implementing the required traits.
180//!
181//! The [`Vertex`] trait must be implemented if you want to use a type as vertex (consumed by [`Tess`]).
182//! Either you can decide to implement it on your own, or you could just let this crate do the job for you.
183//!
184//! > Important: the [`Vertex`] trait is `unsafe`, which means that all of its implementors must be
185//! > as well. This is due to the fact that vertex formats include information about the structure of the
186//! > data that will be sent to the backend, and a bad implementation can lead to undefined behaviors.
187//!
188//! You can derive the [`Vertex`] trait if your type follows these conditions:
189//!
190//! - It must be a `struct` with named fields. This is just a temporary limitation that will get
191//!   dropped as soon as the crate is stable enough.
192//! - Its fields must have a type that implements [`VertexAttrib`]. This is mandatory so that the
193//!   backend knows enough about the types used in the structure to correctly align memory, pick
194//!   the right types, etc.
195//! - Its fields must have a type that implements [`HasSemantics`] as well. This trait is just a
196//!   type family that associates a single constant (i.e. the semantics) that the vertex attribute
197//!   uses.
198//! - Each field's type must be different.
199//!
200//! Once all those requirements are met, you can derive [`Vertex`] pretty easily.
201//!
202//! > Note: feel free to look at the [`Semantics`] proc-macro as well, that provides a way
203//! > to generate semantics types in order to completely both implement [`Semantics`] for an
204//! > `enum` of your choice, but also generate *field* types you can use when defining your vertex
205//! > type.
206//!
207//! The syntax is the following:
208//!
209//! ```rust
210//! use luminance::{Vertex, Semantics};
211//!
212//! // visit the Semantics proc-macro documentation for further details
213//! #[derive(Clone, Copy, Debug, PartialEq, Semantics)]
214//! pub enum Semantics {
215//!   #[sem(name = "position", repr = "[f32; 3]", wrapper = "VertexPosition")]
216//!   Position,
217//!   #[sem(name = "color", repr = "[f32; 4]", wrapper = "VertexColor")]
218//!   Color,
219//! }
220//!
221//! #[derive(Clone, Copy, Debug, PartialEq, Vertex)]
222//! #[vertex(sem = "Semantics")] // specify the semantics to use for this type
223//! struct MyVertex {
224//!   position: VertexPosition,
225//!   color: VertexColor,
226//! }
227//! ```
228//!
229//! > Note: the `Semantics` enum must be public because of the implementation of [`HasSemantics`]
230//! > trait.
231//!
232//! Besides the [`Semantics`]-related code, this will:
233//!
234//! - Create a type called `MyVertex`, a struct that will hold a single vertex.
235//! - Implement `Vertex for MyVertex`.
236//!
237//! The proc-macro also supports an optional `#[vertex(instanced = "<bool>")]` struct attribute.
238//! This attribute allows you to specify whether the fields are to be instanced or not. For more
239//! about that, have a look at [`VertexInstancing`].
240//!
241//! ### `Semantics`
242//!
243//! The [`Semantics`] derive proc-macro.
244//!
245//! ### `UniformInterface`
246//!
247//! The [`UniformInterface`] derive proc-macro.
248//!
249//! The procedural macro is very simple to use. You declare a struct as you would normally do:
250//!
251//! ```
252//! use luminance::{shader::{types::Vec4, Uniform}, UniformInterface};
253//!
254//! #[derive(Debug, UniformInterface)]
255//! struct MyIface {
256//!   time: Uniform<f32>,
257//!   resolution: Uniform<Vec4<f32>>,
258//! }
259//! ```
260//!
261//! The effect of this declaration is declaring the `MyIface` struct along with an effective
262//! implementation of `UniformInterface` that will try to get the `"time"` and `"resolution"`
263//! uniforms in the corresponding shader program. If any of the two uniforms fails to map (inactive
264//! uniform, for instance), the whole struct cannot be generated, and an error is arisen (see
265//! `UniformInterface::uniform_interface`’s documentation for further details).
266//!
267//! If you don’t use a parameter in your shader, you might not want the whole interface to fail
268//! building if that parameter cannot be mapped. You can do that via the `#[unbound]` field
269//! attribute:
270//!
271//! ```
272//! # use luminance::{shader::{types::Vec4, Uniform}, UniformInterface};
273//! #[derive(Debug, UniformInterface)]
274//! struct MyIface {
275//!   #[uniform(unbound)]
276//!   time: Uniform<f32>, // if this field cannot be mapped, it’ll be ignored
277//!   resolution: Uniform<Vec4<f32>>,
278//! }
279//! ```
280//!
281//! You can also change the default mapping with the `#[uniform(name = "string_mapping")]`
282//! attribute. This changes the name that must be queried from the shader program for the mapping
283//! to be complete:
284//!
285//! ```
286//! # use luminance::{shader::{types::Vec4, Uniform}, UniformInterface};
287//! #[derive(Debug, UniformInterface)]
288//! struct MyIface {
289//!   time: Uniform<f32>,
290//!   #[uniform(name = "res")]
291//!   resolution: Uniform<Vec4<f32>>, // maps "res" from the shader program
292//! }
293//! ```
294//!
295//! Finally, you can mix both attributes if you want to change the mapping and have an unbound
296//! uniform if it cannot be mapped:
297//!
298//! ```
299//! # use luminance::{shader::{types::Vec4, Uniform}, UniformInterface};
300//! #[derive(Debug, UniformInterface)]
301//! struct MyIface {
302//!   time: Uniform<f32>,
303//!   #[uniform(name = "res", unbound)]
304//!   resolution: Uniform<Vec4<f32>>,
305//! }
306//! ```
307//!
308//! [luminance]: https://crates.io/crates/luminance
309//! [luminance-gl]: https://crates.io/crates/luminance-gl
310//! [luminance-front]: https://crates.io/crates/luminance-front
311//! [luminance::backend]: crate::backend
312//! [`Semantics`]: https://docs.rs/luminance/latest/luminance/vertex/trait.Semantics.html
313//! [`HasSemantics`]: https://docs.rs/luminance/latest/luminance/vertex/trait.HasSemantics.html
314//! [`Tess`]: https://docs.rs/luminance/latest/luminance/tess/struct.Tess.html
315//! [`Vertex`]: https://docs.rs/luminance/latest/luminance/vertex/trait.Vertex.html
316//! [`VertexAttrib`]: https://docs.rs/luminance/latest/luminance/vertex/trait.VertexAttrib.html
317//! [`VertexInstancing`]: https://docs.rs/luminance/latest/luminance/vertex/enum.VertexInstancing.html
318//! [`UniformInterface`]: https://docs.rs/luminance/latest/luminance/shader/program/trait.UniformInterface.html
319
320#![doc(
321  html_logo_url = "https://raw.githubusercontent.com/phaazon/luminance-rs/master/docs/imgs/luminance_alt.svg"
322)]
323#![deny(missing_docs)]
324
325#[cfg(feature = "derive")]
326pub use luminance_derive::*;
327
328pub mod backend;
329pub mod blending;
330pub mod context;
331pub mod depth_stencil;
332pub mod face_culling;
333pub mod framebuffer;
334pub mod pipeline;
335pub mod pixel;
336pub mod query;
337pub mod render_gate;
338pub mod render_state;
339pub mod scissor;
340pub mod shader;
341pub mod shading_gate;
342pub mod tess;
343pub mod tess_gate;
344pub mod texture;
345pub mod vertex;