diff --git a/.travis.yml b/.travis.yml index 13a94f04977..630a0eefafb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/README.md b/README.md index e30b1cb6add..a441f8f171c 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/src/sdl2/render.rs b/src/sdl2/render.rs index c4d3705461d..485e09eaa20 100644 --- a/src/sdl2/render.rs +++ b/src/sdl2/render.rs @@ -792,7 +792,7 @@ impl TextureCreator { /// /// 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. /// @@ -873,6 +873,7 @@ impl TextureCreator { } + /// 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 { @@ -882,6 +883,7 @@ impl TextureCreator { } } + /// 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 { @@ -1318,6 +1320,12 @@ impl Canvas { /// 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(&self, format: F, @@ -1338,6 +1346,10 @@ impl Canvas { } /// 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(&self, @@ -1351,6 +1363,10 @@ impl Canvas { } /// 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(&self, @@ -1364,6 +1380,10 @@ impl Canvas { } /// 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(&self, @@ -1381,6 +1401,10 @@ impl Canvas { /// # 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> (&self, @@ -1397,6 +1421,11 @@ impl Canvas { } #[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, @@ -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, @@ -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) }