Skip to content

Frequently Asked Questions (FAQ)

Sven Nilsen edited this page Dec 3, 2025 · 17 revisions

If your question is not here, please open up an issue.

How do I stay productive with Piston?

This is arguably the most important question:

  1. Put this in your "Cargo.toml":
[profile.dev.package."*"]
opt-level = 3

[profile.release.package."*"]
opt-level = 3

[profile.release]
opt-level = 2

If you have some performance sensitive code, then it is recommended to put it in some library, such that the Rust compiler does not need to re-optimize the code after the first build. The stuff that you put in your top project should be things that are changing frequently.

  1. Use Esc as shortcut for exiting e.g. see WindowSettings::exit_on_esc.

The time you move the mouse cursor to close your window, is usually longer than hitting Esc on the keyboard.

  1. Use bash command while true; do <your_command> || break; done.

This restarts the application immediately after it closes, but stops if there is an error.

  1. Use the Current library.

A lot of game logic depends on application state. Instead of passing application state as many arguments to functions, you can use this library when prototyping. Later, when you stabilize the codebase, you can turn application state into arguments again.

  1. Use the Underscore-Args library.

Rust does not support named arguments, which can be a problem for game development to keep track of many arguments. This library provides a workaround that is reasonably good. Later on, you can wrap your arguments in structs.

  1. Use the Dyon library for scripting.

Life is short and making games take long time. Use a scripting language to increase iteration cycles, while writing performance sensitive code in Rust.

What is the difference between a game engine and a framework, and how do we measure their popularity?

Most people think of a framework as a "lightweight" game engine, but here are some better definitions:

  • A game engine is the set of libraries you use to build a game (whatever that is e.g. loading assets and render stuff)
  • A framework is a system for integrating libraries so they work together to produce some benefit for developers

The core libraries of Piston is a framework, but the ecosystem of the Piston project is a game engine.

Most developers think of Rust "game engines" from the perspective of smaller projects and comparing them to the core libraries of Piston. This is a framework perspective, which is only a small part of the Piston project.

In terms of total downloads and usage across the entire ecosystem that started under the same project, there is no close competitor to Piston. Using that metric, Piston is one of the most popular game engines in the world. However, this is a misleading metric, because it is due to two popular libraries: Image and Freetype, that are both more popular than all the frameworks in Rust, including the Piston core. Some other libraries, such as Wavefront-Obj get also lots of downloads.

With other words, the Piston game engine is most popular in Rust in terms of total downloads and usage, but the Piston framework is not as popular.

Is Piston cross platform?

Yes. Piston uses backend-agnostic design and supports multiple cross platform APIs:

Is the goal of Piston to develop a game engine similar to Unity or Unreal Engine?

Unity and Unreal Engine are products sold against profit, which is different than what Piston does.

Piston libraries are free, but might be part of a commercial project through various companies. You are free to earn money by using Piston in your products, as long as you include the license. Most code is licensed under MIT, but we move what we can to dual licensing between MIT and Apache 2. This is a permissive license combination that is popular in the Rust community.

Piston is about minimizing developer costs. The major motivation to develop the ecosystem and reduce costs is to keep things modular, such that pieces of code can be reused across projects.

Why does the Piston project work on all these different ideas?

To evaluate design of libraries, we need to test them in various applications. This is the safest route to get valid feedback. Often we pick something that the author is interested in, because enjoying to work on something is important.

Can I use Piston libraries without using a Piston window?

Yes. All the libraries are designed to be used with a minimal abstraction.

Why do big projects have so many dependencies?

All big projects rely on two kinds of dependencies:

  1. Dependencies the project controls
  2. Dependencies from other projects

In the Piston project, we try to minimize the number of dependencies and maximize number of standalone libraries. This makes it easier to keep the ecosystem stable and updated:

  • Fewer dependencies for generic libraries (better quality of life for developers)
  • More dependencies for backends and for applications (some complexity is required)

This design increases the number of dependencies required to make stuff run, but in return it provides a more stable ecosystem and less breaking changes.

What are "current objects"?

The Current library is a tool to help productivity. It associates a value per type in a shadow stack that can be accessed in the same thread. It is used for prototyping and not recommended to use in general library design.

Dyon has built-in support for current objects, but using names instead of types. This makes them similar to globals, but more powerful.

How do I make Piston-Graphics2D faster?

The following steps can reduce CPU usage significantly:

  1. Change event loop settings (e.g. enable lazy mode for GUI)
  2. Set triangulation resolution of ellipses to a lower setting
  3. Use "invalidate" regions which are only rendered when e.g. a widget changes

For most simple games and interactive applications, Piston-Graphics2D should be fast enough.

What is the motivation behind the design of Piston-Graphics2D?

Piston-Graphics2D is designed for making workarounds possible, e.g. write custom graphics backends that override default behavior.

Piston-Graphics2D uses an immediate design because this is the most flexible way to program 2D. By default, it converts any shape into triangles which are sent to the backend in chunks.

The graphics backend then might choose to buffer chunks to reduce draw calls. This speeds up rendering approximately 6x.

A problem is when switching frequently back and forth between single colored graphics and textured graphics. Depending on the backend, this might lead to reduction in performance.

My application crashes when running in VirtualBox on Linux, how do I fix it?

Some versions of VirtualBox only supports older OpenGL drivers, which conflicts with the default version V3.2 that many window-backends use in Piston. To fix this error, call .opengl(OpenGL::V3_1) or experiment with older versions when building the window from WindowSettings.

Is Piston used in production?

Right now, while you are reading this, you are probably using parts of Piston without knowing it. Most games that are sold commercially today with Rust code in it, uses parts of Piston.

So, the answer is yes. However, it is not the way you think.

When people say "Piston" it can mean lots of different things. There is a saying:

"Everybody uses some part of Piston, nobody uses all parts of Piston"

In the beginning, the Piston project was 10% of the total Rust ecosystem. Since we were early on the stage, some libraries that started under the Piston project became very popular.

For example, the Image library started under the Piston project and is used by over 1 billion people every day through popular Internet Browsers such as Chrome or Firefox. Most games that are being sold commercially in Rust, use this library.

Piston's architecture was designed before there were good Rust libraries for windowing (e.g. Winit) and graphics (e.g. WGPU). However, due to Piston's modular architecture, it continues to be one of the most stable and reusable ecosystems for game developers.

Most people who use the core libraries of Piston today are game developer enthusiasts and hobbyists, that have been using Piston for years and do not see any good reason to switch out their software stack. This is because the modular design makes experimentation with game engine design easier, allowing a great amount of creative freedom, but also keeping the ecosystem stable and requiring little maintenance. Once in a while, some developers switch to Piston after lacking workarounds in other game engines.

For the most of the time, Piston is in a quiet "Zombie mode". The project seems dead for long periods, but suddenly a lot of things happen in short amount of time.

Piston has been used in a few projects, but for most professional game developers, it is used during training at work to learn to write Rust code, coming from a background of e.g. C++ or C#.

How do I know upgrades will not break my code and how do I use Semver properly?

The first non-zero number in the Semver version that Cargo uses tells whether a breaking change happened.

For example:

  • 0.0.1 breaks when changed to 0.0.2
  • 0.1.0 breaks when changed to 0.2.0
  • 1.0.0 breaks when changed to 2.0.0

It is a good idea to always use x.y.z (all 3 numbers), without any ^ or =. Semver is smarter than you think and you do not need to "help" it. Just use the plain 3 numbers with no trickery.

The Piston project uses a tool, Eco, which fetches "Cargo.toml" files directly from the web and then runs an analysis. It outputs a file with recommended updates. When this is done, the whole ecosystem should be updated and consistent.

One problem some people have is that finding the right version that works with another can be a mentally hard problem. To solve this you can use Eco, and make a list of links to "Cargo.toml" files. You do not have to list all dependencies. Eco will help you to stay updated.

What is the idiomatic way of organizing code?

The default pattern for Piston is the model/view/controller pattern:

  • A model is simply some data structure, database etc.
  • A view is how something is rendered
  • A controller stores the state, transforms input events into other events or actions, or deals with application logic in general.

Currently, there is no traits defined for this abstraction, it is simply a way of splitting up code into reusable parts.

Two simple examples of this pattern is timer_controller and button_controller. More libraries will come, but you can take a look here for an updated overview.

This code is reusable for any model or view, but it is also more work to set up. You might want to use a tailored API for your use cases, e.g. Conrod for UI. This is why Piston uses a modular design with a small core, so the code that is reused for particular projects, e.g. editors, can be worked on in parallel with other projects.

What we mean by "idiomatic" code is that this pattern does work and has a predictable maintenance cost for large projects. It is not meant to mean that you can't do any better or that everyone should program this way. The great thing about Rust is that it allows a wide style of designs, e.g. functional APIs, stream processing etc. You should not think that one particular flavor is superior to another, but learn different techniques and figure out the trade-offs. You know best what works for you! 😄

What is the idiomatic way of handling events?

Piston uses event traits with the input::GenericEvent trait. This is to reduce breaking APIs and let controllers work with custom defined event types.

Generic controller libraries depend on the core input library directly, to avoid breaking changes when the window and event_loop core libraries change.

If you are writing a generic controller library, do the following steps:

  1. Add pistoncore-input to Cargo.toml
  2. Add extern crate input; at the top of your library.
  3. Add use input::GenericEvent; to import the generic event trait.
  4. Use a signature fn event<E: GenericEvent>(&mut self, e: &E, ...)
  5. Import the event traits that are needed, e.g. use input::UpdateEvent;
  6. Use the style if let Some(args) = e.update_args() { ... } or e.update(|args| { ... });

Do not handle the enum variant directly, but use event traits. This allows your code to be upgraded more easily.

In generic application code, you do the same steps but with the piston crate. The input crate is reexported under piston::input. Write use piston::input::GenericEvent; instead of use input::GenericEvent;.

If you are only creating a demo or an example, you can use the piston_window crate. This library reexports all event traits directly under piston_window, so you write use piston_window::*;.

What is the idiomatic way of drawing 2D for generic backends?

use graphics::{Context, Graphics};
use graphics::character::CharacterCache;

impl MyView {
    pub fn draw<G: Graphics>(&self, c: &Context, g: &mut G) { ... }
}

You can also write this:

use graphics::{Context, Graphics};
use graphics::character::CharacterCache;

impl MyView {
    pub fn draw(&self, c: &Context, g: &mut impl Graphics) { ... }
}

When you use a character glyph cache:

use graphics::{Context, Graphics};
use graphics::character::CharacterCache;

impl MyView {
    pub fn draw<C, G>(&self, glyphs: &mut C, c: &Context, g: &mut G)
        where C: CharacterCache, G: Graphics<Texture = C::Texture>
    { ... }
}

This makes sure that the graphics backend accepts the texture type used by the character glyph cache.

Should I use the "piston_window" crate in libraries?

No. The "piston_window" crate is intended for application level code only.

For libraries, use the "piston" crate and "piston2d-graphics" to write generic code. This will make the code more reusable across projects.

Clone this wiki locally