Skip to content

Commit

Permalink
Add some more documentation for unsafe_textures feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Cobrand committed Sep 5, 2017
1 parent a0da0cf commit 59a9591
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ script:
- |
travis-cargo build -- --features "gfx image ttf mixer" &&
travis-cargo test -- --features "gfx image ttf mixer" &&
travis-cargo --only stable doc -- --all-features
travis-cargo --only stable doc -- --features "gfx image ttf mixer"
after_success:
- travis-cargo --only stable doc-upload
env:
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ If you want a library compatible with earlier versions of SDL, please see
* image
* mixer
* ttf
* unsafe\_textures (to get rid of the lifetime on `Texture`, at the cost of potential unsafety)

# Requirements

Expand Down Expand Up @@ -304,6 +303,19 @@ You can see the full list in the `examples/` folder. Some examples require some
Replace "gfx" by the feature(s) needed for the example you want.

# About the `unsafe_textures` feature

In the `sdl2::render` module, `Texture` has by default lifetimes to prevent it from out-living its parent `TextureCreator`.
These lifetimes are sometimes too hard to deal with in Rust, and so you have the option to enable the `unsafe_textures` feature.

This removes the lifetimes on the `Texture`s, at the cost of optional manual memory management. If you want to manually destroy
the `Texture`s you use, you can call the `destroy` method of your `Texture`s, but beware that *it should not* be called if none of
the parents (`Canvas` or `TextureCreator`) are alive. If you do not call this method, the memory will simply be freed when
the last `Canvas` or the last `TextureCreator` will be freed.

There is no online documentation for this feature, however you can build it yourself in your project by enabling the feature in your
Cargo.toml, running `cargo doc` and accessing `target/doc/sdl2/index.html` via a browser.

# OpenGL

If you want to use OpenGL, you also need the
Expand Down
64 changes: 60 additions & 4 deletions src/sdl2/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ impl<T> TextureCreator<T> {
///
/// If format is `None`, the format will be the one the parent Window or Surface uses.
///
/// If format is `Some(pixel_format)`
/// If format is `Some(pixel_format)`, the default will be overridden, and the texture will be
/// created with the specified format if possible. If the PixelFormat is not supported, this
/// will return an error.
///
Expand Down Expand Up @@ -873,6 +873,7 @@ impl<T> TextureCreator<T> {
}


/// Create a texture from its raw `SDL_Texture`.
#[cfg(not(feature = "unsafe_textures"))]
#[inline]
pub unsafe fn raw_create_texture(&self, raw: *mut ll::SDL_Texture) -> Texture {
Expand All @@ -882,6 +883,7 @@ impl<T> TextureCreator<T> {
}
}

/// Create a texture from its raw `SDL_Texture`. Should be used with care.
#[cfg(feature = "unsafe_textures")]
pub unsafe fn raw_create_texture(&self, raw: *mut ll::SDL_Texture) -> Texture {
Texture {
Expand Down Expand Up @@ -1318,6 +1320,12 @@ impl<T: RenderTarget> Canvas<T> {
/// You should prefer the default format if possible to have performance gains and to avoid
/// unsupported Pixel Formats that can cause errors. However, be careful with the default
/// `PixelFormat` if you want to create transparent textures.
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature,
/// because lifetimes otherwise prevent `Canvas` from creating and accessing `Texture`s at the
/// same time.
#[cfg(feature = "unsafe_textures")]
pub fn create_texture<F>(&self,
format: F,
Expand All @@ -1338,6 +1346,10 @@ impl<T: RenderTarget> Canvas<T> {
}

/// Shorthand for `create_texture(format, TextureAccess::Static, width, height)`
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature.
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_static<F>(&self,
Expand All @@ -1351,6 +1363,10 @@ impl<T: RenderTarget> Canvas<T> {
}

/// Shorthand for `create_texture(format, TextureAccess::Streaming, width, height)`
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature.
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_streaming<F>(&self,
Expand All @@ -1364,6 +1380,10 @@ impl<T: RenderTarget> Canvas<T> {
}

/// Shorthand for `create_texture(format, TextureAccess::Target, width, height)`
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature.
#[cfg(feature = "unsafe_textures")]
#[inline]
pub fn create_texture_target<F>(&self,
Expand All @@ -1381,6 +1401,10 @@ impl<T: RenderTarget> Canvas<T> {
/// # Remarks
///
/// The access hint for the created texture is `TextureAccess::Static`.
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature.
#[cfg(feature = "unsafe_textures")]
pub fn create_texture_from_surface<S: AsRef<SurfaceRef>>
(&self,
Expand All @@ -1397,6 +1421,11 @@ impl<T: RenderTarget> Canvas<T> {
}

#[cfg(feature = "unsafe_textures")]
/// Create a texture from its raw `SDL_Texture`. Should be used with care.
///
/// # Notes
///
/// Note that this method is only accessible in Canvas with the `unsafe_textures` feature.
pub unsafe fn raw_create_texture(&self, raw: *mut ll::SDL_Texture) -> Texture {
Texture {
raw: raw,
Expand All @@ -1414,15 +1443,40 @@ pub struct TextureQuery {

/// A texture for a rendering context.
///
/// Every Texture is owned by a `TextureCreator`.
/// A `Texture` cannot outlive the `TextureCreator`
/// Every Texture is owned by a `TextureCreator` or `Canvas` (the latter is only possible with the
/// `unsafe_textures` feature).
///
/// # Diffences between with and without `unsafe_textures` feature
///
/// A `Texture` can be safely accessed after the `Canvas` is dropped.
/// Without the `unsafe_textures`, a texture is owned by a `TextureCreator` and a `Texture` cannot
/// outlive its parent `TextureCreator` thanks to lifetimes. A texture is destroyed via its `Drop`
/// implementation. While this is the most "Rust"-y way of doing things currently, it is pretty
/// cumbersome to use in some cases.
///
/// That is why the feature `unsafe_textures` was brought to life: the lifetimes are gone, meaning
/// that `Texture`s *can* outlive their parents. That means that the `Texture`s are not destroyed
/// on `Drop`, but destroyed when their parents are. That means if you create 10 000 textures with
/// this feature, they will only be destroyed after you drop the `Canvas` and every
/// `TextureCreator` linked to it. While this feature is enabled, this is the safest way to free
/// the memory taken by the `Texture`s, but there is still another, unsafe way to destroy the
/// `Texture` before its `Canvas`: the method `destroy`. This method is unsafe because *you* have
/// to make sure the parent `Canvas` or `TextureCreator` is still alive while calling this method.
///
/// **Calling the `destroy` method while no parent is alive is undefined behavior**
///
/// With the `unsafe_textures` feature, a `Texture` can be safely accessed (but not destroyed) after
/// the `Canvas` is dropped, but since any access (except `destroy`) requires the original `Canvas`,
/// it is not possible to access a `Texture` while the `Canvas` is dropped.
#[cfg(feature = "unsafe_textures")]
pub struct Texture {
raw: *mut ll::SDL_Texture,
}

/// A texture for a rendering context.
///
/// Every Texture is owned by a `TextureCreator`. Internally, a texture is destroyed via its `Drop`
/// implementation. A texture can only be used by the `Canvas` it was originally created from, it
/// is undefined behavior otherwise.
#[cfg(not(feature = "unsafe_textures"))]
pub struct Texture<'r> {
raw: *mut ll::SDL_Texture,
Expand Down Expand Up @@ -1454,6 +1508,8 @@ impl Texture {
/// Note however that you don't *have* to destroy a Texture before its Canvas,
/// since whenever Canvas is destroyed, the SDL implementation will automatically
/// destroy all the children Textures of that Canvas.
///
/// **Calling this method while no parent is alive is undefined behavior**
pub unsafe fn destroy(self) {
ll::SDL_DestroyTexture(self.raw)
}
Expand Down

0 comments on commit 59a9591

Please sign in to comment.