diff --git a/Cargo.lock b/Cargo.lock index 5fb4edf..50dc676 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,11 +88,11 @@ version = "0.1.1" [[package]] name = "sdl3-src" -version = "3.1.6-preview-508-g2d91f096c" +version = "3.1.6-preview-545-gf2074d7af" [[package]] name = "sdl3-sys" -version = "0.2.0+SDL3-preview-3.1.6-508-g2d91f096c" +version = "0.2.0+SDL3-preview-3.1.6-545-gf2074d7af" dependencies = [ "ash", "cmake", diff --git a/sdl3-src/Cargo.toml b/sdl3-src/Cargo.toml index 2b92902..dbbb3fc 100644 --- a/sdl3-src/Cargo.toml +++ b/sdl3-src/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sdl3-src" -version = "3.1.6-preview-508-g2d91f096c" +version = "3.1.6-preview-545-gf2074d7af" edition = "2021" authors = ["SDL developers"] license = "Zlib" diff --git a/sdl3-src/SDL b/sdl3-src/SDL index 2d91f09..f2074d7 160000 --- a/sdl3-src/SDL +++ b/sdl3-src/SDL @@ -1 +1 @@ -Subproject commit 2d91f096cae00ad06627339519d876ab2df8248e +Subproject commit f2074d7af323e2d7775c742cadf02f13dfa7ea0a diff --git a/sdl3-src/src/lib.rs b/sdl3-src/src/lib.rs index 901a766..d42b694 100644 --- a/sdl3-src/src/lib.rs +++ b/sdl3-src/src/lib.rs @@ -8,7 +8,7 @@ pub const SOURCE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/SDL"); pub const SOURCE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "\\SDL"); /// Revision -pub const REVISION: &str = "SDL3-preview-3.1.6-508-g2d91f096c"; +pub const REVISION: &str = "SDL3-preview-3.1.6-545-gf2074d7af"; /// Version part of the revision pub const VERSION: &str = "3.1.6"; @@ -20,7 +20,7 @@ pub const REVISION_TAG: &str = "preview-3.1.6"; pub const REVISION_TAG_BASE: &str = "preview"; /// Offset from tag part of the revision -pub const REVISION_OFFSET: &str = "508"; +pub const REVISION_OFFSET: &str = "545"; /// Hash part of the revision -pub const REVISION_HASH: &str = "g2d91f096c"; +pub const REVISION_HASH: &str = "gf2074d7af"; diff --git a/sdl3-sys/Cargo.toml b/sdl3-sys/Cargo.toml index 7f2a6f7..711dff4 100644 --- a/sdl3-sys/Cargo.toml +++ b/sdl3-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sdl3-sys" -version = "0.2.0+SDL3-preview-3.1.6-508-g2d91f096c" +version = "0.2.0+SDL3-preview-3.1.6-545-gf2074d7af" edition = "2021" authors = ["Maia S. R."] license = "Zlib" @@ -105,7 +105,7 @@ version = "0.1.2" optional = true [build-dependencies.sdl3-src] -version = "3.1.6-preview-508-g2d91f096c" +version = "3.1.6-preview-545-gf2074d7af" path = "../sdl3-src" optional = true diff --git a/sdl3-sys/README.md b/sdl3-sys/README.md index 312e43b..d96845f 100644 --- a/sdl3-sys/README.md +++ b/sdl3-sys/README.md @@ -1,6 +1,6 @@ # sdl3-sys: Low level Rust bindings for SDL 3 -This version of `sdl3-sys` has bindings for SDL version `3.1.6-preview-508-g2d91f096c` and earlier. +This version of `sdl3-sys` has bindings for SDL version `3.1.6-preview-545-gf2074d7af` and earlier. SDL 3 is ABI stable as of the 3.1.3 preview release, but `sdl3-sys` is new and may have bugs. Please submit an issue at github if you have any issues diff --git a/sdl3-sys/src/generated/asyncio.rs b/sdl3-sys/src/generated/asyncio.rs index 6d7e78a..78eb8ea 100644 --- a/sdl3-sys/src/generated/asyncio.rs +++ b/sdl3-sys/src/generated/asyncio.rs @@ -137,7 +137,7 @@ pub const SDL_ASYNCIO_TASK_CLOSE: SDL_AsyncIOTaskType = SDL_AsyncIOTaskType::CLO /// | ------------------- | --------------- | ----------- | /// | [`COMPLETE`](SDL_AsyncIOResult::COMPLETE) | [`SDL_ASYNCIO_COMPLETE`] | request was completed without error | /// | [`FAILURE`](SDL_AsyncIOResult::FAILURE) | [`SDL_ASYNCIO_FAILURE`] | request failed for some reason; check [`SDL_GetError()`]! | -/// | [`CANCELLED`](SDL_AsyncIOResult::CANCELLED) | [`SDL_ASYNCIO_CANCELLED`] | request was cancelled before completing. | +/// | [`CANCELED`](SDL_AsyncIOResult::CANCELED) | [`SDL_ASYNCIO_CANCELED`] | request was canceled before completing. | #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SDL_AsyncIOResult(pub ::core::ffi::c_int); @@ -156,7 +156,7 @@ impl ::core::fmt::Debug for SDL_AsyncIOResult { f.write_str(match *self { Self::COMPLETE => "SDL_ASYNCIO_COMPLETE", Self::FAILURE => "SDL_ASYNCIO_FAILURE", - Self::CANCELLED => "SDL_ASYNCIO_CANCELLED", + Self::CANCELED => "SDL_ASYNCIO_CANCELED", _ => return write!(f, "SDL_AsyncIOResult({})", self.0), }) @@ -168,16 +168,16 @@ impl SDL_AsyncIOResult { pub const COMPLETE: Self = Self(0); /// request failed for some reason; check [`SDL_GetError()`]! pub const FAILURE: Self = Self(1); - /// request was cancelled before completing. - pub const CANCELLED: Self = Self(2); + /// request was canceled before completing. + pub const CANCELED: Self = Self(2); } /// request was completed without error pub const SDL_ASYNCIO_COMPLETE: SDL_AsyncIOResult = SDL_AsyncIOResult::COMPLETE; /// request failed for some reason; check [`SDL_GetError()`]! pub const SDL_ASYNCIO_FAILURE: SDL_AsyncIOResult = SDL_AsyncIOResult::FAILURE; -/// request was cancelled before completing. -pub const SDL_ASYNCIO_CANCELLED: SDL_AsyncIOResult = SDL_AsyncIOResult::CANCELLED; +/// request was canceled before completing. +pub const SDL_ASYNCIO_CANCELED: SDL_AsyncIOResult = SDL_AsyncIOResult::CANCELED; /// Information about a completed asynchronous I/O request. /// diff --git a/sdl3-sys/src/generated/clipboard.rs b/sdl3-sys/src/generated/clipboard.rs index 73a5c28..a52e01b 100644 --- a/sdl3-sys/src/generated/clipboard.rs +++ b/sdl3-sys/src/generated/clipboard.rs @@ -266,7 +266,7 @@ extern "C" { callback: SDL_ClipboardDataCallback, cleanup: SDL_ClipboardCleanupCallback, userdata: *mut ::core::ffi::c_void, - mime_types: *mut *const ::core::ffi::c_char, + mime_types: *const *const ::core::ffi::c_char, num_mime_types: ::core::primitive::usize, ) -> ::core::primitive::bool; } diff --git a/sdl3-sys/src/generated/events.rs b/sdl3-sys/src/generated/events.rs index 5377000..a25e094 100644 --- a/sdl3-sys/src/generated/events.rs +++ b/sdl3-sys/src/generated/events.rs @@ -146,6 +146,7 @@ use super::video::*; /// | [`FINGER_DOWN`](SDL_EventType::FINGER_DOWN) | [`SDL_EVENT_FINGER_DOWN`] | | /// | [`FINGER_UP`](SDL_EventType::FINGER_UP) | [`SDL_EVENT_FINGER_UP`] | | /// | [`FINGER_MOTION`](SDL_EventType::FINGER_MOTION) | [`SDL_EVENT_FINGER_MOTION`] | | +/// | [`FINGER_CANCELED`](SDL_EventType::FINGER_CANCELED) | [`SDL_EVENT_FINGER_CANCELED`] | | /// | [`CLIPBOARD_UPDATE`](SDL_EventType::CLIPBOARD_UPDATE) | [`SDL_EVENT_CLIPBOARD_UPDATE`] | The clipboard or primary selection changed | /// | [`DROP_FILE`](SDL_EventType::DROP_FILE) | [`SDL_EVENT_DROP_FILE`] | The system requests a file open | /// | [`DROP_TEXT`](SDL_EventType::DROP_TEXT) | [`SDL_EVENT_DROP_TEXT`] | text/plain drag-and-drop event | @@ -279,6 +280,7 @@ impl ::core::fmt::Debug for SDL_EventType { Self::FINGER_DOWN => "SDL_EVENT_FINGER_DOWN", Self::FINGER_UP => "SDL_EVENT_FINGER_UP", Self::FINGER_MOTION => "SDL_EVENT_FINGER_MOTION", + Self::FINGER_CANCELED => "SDL_EVENT_FINGER_CANCELED", Self::CLIPBOARD_UPDATE => "SDL_EVENT_CLIPBOARD_UPDATE", Self::DROP_FILE => "SDL_EVENT_DROP_FILE", Self::DROP_TEXT => "SDL_EVENT_DROP_TEXT", @@ -496,6 +498,7 @@ impl SDL_EventType { pub const FINGER_DOWN: Self = Self(0x700); pub const FINGER_UP: Self = Self(1793); pub const FINGER_MOTION: Self = Self(1794); + pub const FINGER_CANCELED: Self = Self(1795); /// The clipboard or primary selection changed pub const CLIPBOARD_UPDATE: Self = Self(0x900); /// The system requests a file open @@ -748,6 +751,7 @@ pub const SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: SDL_EventType = pub const SDL_EVENT_FINGER_DOWN: SDL_EventType = SDL_EventType::FINGER_DOWN; pub const SDL_EVENT_FINGER_UP: SDL_EventType = SDL_EventType::FINGER_UP; pub const SDL_EVENT_FINGER_MOTION: SDL_EventType = SDL_EventType::FINGER_MOTION; +pub const SDL_EVENT_FINGER_CANCELED: SDL_EventType = SDL_EventType::FINGER_CANCELED; /// The clipboard or primary selection changed pub const SDL_EVENT_CLIPBOARD_UPDATE: SDL_EventType = SDL_EventType::CLIPBOARD_UPDATE; /// The system requests a file open @@ -1463,7 +1467,7 @@ pub struct SDL_RenderEvent { #[derive(Clone, Copy)] #[cfg_attr(feature = "debug-impls", derive(Debug))] pub struct SDL_TouchFingerEvent { - /// [`SDL_EVENT_FINGER_MOTION`] or [`SDL_EVENT_FINGER_DOWN`] or [`SDL_EVENT_FINGER_UP`] + /// [`SDL_EVENT_FINGER_DOWN`], [`SDL_EVENT_FINGER_UP`], [`SDL_EVENT_FINGER_MOTION`], or [`SDL_EVENT_FINGER_CANCELED`] pub r#type: SDL_EventType, pub reserved: Uint32, /// In nanoseconds, populated using [`SDL_GetTicksNS()`] @@ -1684,7 +1688,7 @@ pub struct SDL_ClipboardEvent { /// are we owning the clipboard (internal update) pub owner: ::core::primitive::bool, /// number of mime types - pub n_mime_types: Sint32, + pub num_mime_types: Sint32, /// current mime types pub mime_types: *mut *const ::core::ffi::c_char, } diff --git a/sdl3-sys/src/generated/gpu.rs b/sdl3-sys/src/generated/gpu.rs index e2b420b..3644b07 100644 --- a/sdl3-sys/src/generated/gpu.rs +++ b/sdl3-sys/src/generated/gpu.rs @@ -123,6 +123,29 @@ //! [here](https://github.com/TheSpydog/SDL_gpu_examples) //! . //! +//! ## Performance considerations +//! +//! Here are some basic tips for maximizing your rendering performance. +//! +//! - Beginning a new render pass is relatively expensive. Use as few render +//! passes as you can. +//! - Minimize the amount of state changes. For example, binding a pipeline is +//! relatively cheap, but doing it hundreds of times when you don't need to +//! will slow the performance significantly. +//! - Perform your data uploads as early as possible in the frame. +//! - Don't churn resources. Creating and releasing resources is expensive. +//! It's better to create what you need up front and cache it. +//! - Don't use uniform buffers for large amounts of data (more than a matrix +//! or so). Use a storage buffer instead. +//! - Use cycling correctly. There is a detailed explanation of cycling further +//! below. +//! - Use culling techniques to minimize pixel writes. The less writing the GPU +//! has to do the better. Culling can be a very advanced topic but even +//! simple culling techniques can boost performance significantly. +//! +//! In general try to remember the golden rule of performance: doing things is +//! more expensive than not doing things. Don't Touch The Driver! +//! //! ## FAQ //! //! **Question: When are you adding more advanced features, like ray tracing or @@ -148,6 +171,16 @@ //! reflection to extract the required information from the shader //! automatically instead of manually filling in the struct's values. //! +//! **Question: My application isn't performing very well. Is this the GPU +//! API's fault?** +//! +//! Answer: No. Long answer: The GPU API is a relatively thin layer over the +//! underlying graphics API. While it's possible that we have done something +//! inefficiently, it's very unlikely especially if you are relatively +//! inexperienced with GPU rendering. Please see the performance tips above and +//! make sure you are following them. Additionally, tools like RenderDoc can be +//! very helpful for diagnosing incorrect behavior and performance issues. +//! //! ## System Requirements //! //! **Vulkan:** Supported on Windows, Linux, Nintendo Switch, and certain @@ -2866,8 +2899,10 @@ pub struct SDL_GPUBufferRegion { /// /// Note that the `first_vertex` and `first_instance` parameters are NOT /// compatible with built-in vertex/instance ID variables in shaders (for -/// example, SV_VertexID). If your shader depends on these variables, the -/// correlating draw call parameter MUST be 0. +/// example, SV_VertexID); GPU APIs and shader languages do not define these +/// built-in variables consistently, so if your shader depends on them, the +/// only way to keep behavior consistent and portable is to always pass 0 for +/// the correlating parameter in the draw calls. /// /// ### Availability /// This struct is available since SDL 3.1.3 @@ -2892,8 +2927,10 @@ pub struct SDL_GPUIndirectDrawCommand { /// /// Note that the `first_vertex` and `first_instance` parameters are NOT /// compatible with built-in vertex/instance ID variables in shaders (for -/// example, SV_VertexID). If your shader depends on these variables, the -/// correlating draw call parameter MUST be 0. +/// example, SV_VertexID); GPU APIs and shader languages do not define these +/// built-in variables consistently, so if your shader depends on them, the +/// only way to keep behavior consistent and portable is to always pass 0 for +/// the correlating parameter in the draw calls. /// /// ### Availability /// This struct is available since SDL 3.1.3 @@ -4845,8 +4882,10 @@ extern "C" { /// /// Note that the `first_vertex` and `first_instance` parameters are NOT /// compatible with built-in vertex/instance ID variables in shaders (for - /// example, SV_VertexID). If your shader depends on these variables, the - /// correlating draw call parameter MUST be 0. + /// example, SV_VertexID); GPU APIs and shader languages do not define these + /// built-in variables consistently, so if your shader depends on them, the + /// only way to keep behavior consistent and portable is to always pass 0 for + /// the correlating parameter in the draw calls. /// /// ### Parameters /// - `render_pass`: a render pass handle. @@ -4876,8 +4915,10 @@ extern "C" { /// /// Note that the `first_vertex` and `first_instance` parameters are NOT /// compatible with built-in vertex/instance ID variables in shaders (for - /// example, SV_VertexID). If your shader depends on these variables, the - /// correlating draw call parameter MUST be 0. + /// example, SV_VertexID); GPU APIs and shader languages do not define these + /// built-in variables consistently, so if your shader depends on them, the + /// only way to keep behavior consistent and portable is to always pass 0 for + /// the correlating parameter in the draw calls. /// /// ### Parameters /// - `render_pass`: a render pass handle. diff --git a/sdl3-sys/src/generated/hints.rs b/sdl3-sys/src/generated/hints.rs index 23cc4ee..dba421f 100644 --- a/sdl3-sys/src/generated/hints.rs +++ b/sdl3-sys/src/generated/hints.rs @@ -2337,13 +2337,21 @@ pub const SDL_HINT_MAC_SCROLL_MOMENTUM: *const ::core::ffi::c_char = /// Request [`SDL_AppIterate()`] be called at a specific rate. /// -/// This number is in Hz, so "60" means try to iterate 60 times per second. +/// If this is set to a number, it represents Hz, so "60" means try to iterate +/// 60 times per second. "0" means to iterate as fast as possible. Negative +/// values are illegal, but reserved, in case they are useful in a future +/// revision of SDL. +/// +/// There are other strings that have special meaning. If set to "waitevent", +/// [`SDL_AppIterate`] will not be called until new event(s) have arrived (and been +/// processed by [`SDL_AppEvent`]). This can be useful for apps that are completely +/// idle except in response to input. /// /// On some platforms, or if you are using [`SDL_main`] instead of [`SDL_AppIterate`], /// this hint is ignored. When the hint can be used, it is allowed to be /// changed at any time. /// -/// This defaults to 60, and specifying NULL for the hint's value will restore +/// This defaults to 0, and specifying NULL for the hint's value will restore /// the default. /// /// This hint can be set anytime. diff --git a/sdl3-sys/src/generated/revision.rs b/sdl3-sys/src/generated/revision.rs index 3bb0779..e8dbe18 100644 --- a/sdl3-sys/src/generated/revision.rs +++ b/sdl3-sys/src/generated/revision.rs @@ -17,7 +17,7 @@ apply_cfg!(#[cfg(doc)] => { }); apply_cfg!(#[cfg(not(doc))] => { - pub const SDL_REVISION: *const ::core::ffi::c_char = c"SDL3-preview-3.1.6-508-g2d91f096c".as_ptr(); + pub const SDL_REVISION: *const ::core::ffi::c_char = c"SDL3-preview-3.1.6-545-gf2074d7af".as_ptr(); }); diff --git a/sdl3-sys/src/generated/storage.rs b/sdl3-sys/src/generated/storage.rs index a8d3a3c..77fda6a 100644 --- a/sdl3-sys/src/generated/storage.rs +++ b/sdl3-sys/src/generated/storage.rs @@ -1,4 +1,195 @@ -//! SDL storage container management. +//! The storage API is a high-level API designed to abstract away the +//! portability issues that come up when using something lower-level (in SDL's +//! case, this sits on top of [`SDL_filesystem`]). It is significantly more +//! restrictive than a typical filesystem API, for a number of reasons: +//! +//! 1. **What to Access:** A common pitfall with existing filesystem APIs is +//! the assumption that all storage is monolithic. However, many other +//! platforms (game consoles in particular) are more strict about what _type_ +//! of filesystem is being accessed; for example, game content and user data +//! are usually two separate storage devices with entirely different +//! characteristics (and possibly different low-level APIs altogether!). 2. +//! **How to Access:** Another common mistake is applications assuming that all +//! storage is universally writeable - again, many platforms treat game content +//! and user data as two separate storage devices, and only user data is +//! writeable while game content is read-only. 3. **When to Access:** The most +//! common portability issue with filesystem access is _timing_ - you cannot +//! always assume that the storage device is always accessible all of the time, +//! nor can you assume that there are no limits to how long you have access to +//! a particular device. +//! +//! Consider the following example: +//! +//! ```text +//! void ReadGameData(void) +//! { +//! extern char** fileNames; +//! extern size_t numFiles; +//! for (size_t i = 0; i < numFiles; i += 1) { +//! FILE *data = fopen(fileNames[i], "rwb"); +//! if (data == NULL) { +//! // Something bad happened! +//! } else { +//! // A bunch of stuff happens here +//! fclose(data); +//! } +//! } +//! } +//! +//! void ReadSave(void) +//! { +//! FILE *save = fopen("saves/save0.sav", "rb"); +//! if (save == NULL) { +//! // Something bad happened! +//! } else { +//! // A bunch of stuff happens here +//! fclose(save); +//! } +//! } +//! +//! void WriteSave(void) +//! { +//! FILE *save = fopen("saves/save0.sav", "wb"); +//! if (save == NULL) { +//! // Something bad happened! +//! } else { +//! // A bunch of stuff happens here +//! fclose(save); +//! } +//! } +//! ``` +//! +//! Going over the bullet points again: +//! +//! 1. **What to Access:** This code accesses a global filesystem; game data +//! and saves are all presumed to be in the current working directory (which +//! may or may not be the game's installation folder!). 2. **How to Access:** +//! This code assumes that content paths are writeable, and that save data is +//! also writeable despite being in the same location as the game data. 3. +//! **When to Access:** This code assumes that they can be called at any time, +//! since the filesystem is always accessible and has no limits on how long the +//! filesystem is being accessed. +//! +//! Due to these assumptions, the filesystem code is not portable and will fail +//! under these common scenarios: +//! +//! - The game is installed on a device that is read-only, both content loading +//! and game saves will fail or crash outright +//! - Game/User storage is not implicitly mounted, so no files will be found +//! for either scenario when a platform requires explicitly mounting +//! filesystems +//! - Save data may not be safe since the I/O is not being flushed or +//! validated, so an error occurring elsewhere in the program may result in +//! missing/corrupted save data +//! +//! When using, [`SDL_Storage`], these types of problems are virtually impossible +//! to trip over: +//! +//! ```text +//! void ReadGameData(void) +//! { +//! extern char** fileNames; +//! extern size_t numFiles; +//! +//! SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0); +//! if (title == NULL) { +//! // Something bad happened! +//! } +//! while (!SDL_StorageReady(title)) { +//! SDL_Delay(1); +//! } +//! +//! for (size_t i = 0; i < numFiles; i += 1) { +//! void* dst; +//! Uint64 dstLen = 0; +//! +//! if (SDL_GetStorageFileSize(title, fileNames[i], &dstLen) && dstLen > 0) { +//! dst = SDL_malloc(dstLen); +//! if (SDL_ReadStorageFile(title, fileNames[i], dst, dstLen)) { +//! // A bunch of stuff happens here +//! } else { +//! // Something bad happened! +//! } +//! SDL_free(dst); +//! } else { +//! // Something bad happened! +//! } +//! } +//! +//! SDL_CloseStorage(title); +//! } +//! +//! void ReadSave(void) +//! { +//! SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0); +//! if (user == NULL) { +//! // Something bad happened! +//! } +//! while (!SDL_StorageReady(user)) { +//! SDL_Delay(1); +//! } +//! +//! Uint64 saveLen = 0; +//! if (SDL_GetStorageFileSize(user, "save0.sav", &saveLen) && saveLen > 0) { +//! void* dst = SDL_malloc(saveLen); +//! if (SDL_ReadStorageFile(user, "save0.sav", dst, saveLen)) { +//! // A bunch of stuff happens here +//! } else { +//! // Something bad happened! +//! } +//! SDL_free(dst); +//! } else { +//! // Something bad happened! +//! } +//! +//! SDL_CloseStorage(user); +//! } +//! +//! void WriteSave(void) +//! { +//! SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0); +//! if (user == NULL) { +//! // Something bad happened! +//! } +//! while (!SDL_StorageReady(user)) { +//! SDL_Delay(1); +//! } +//! +//! extern void *saveData; // A bunch of stuff happened here... +//! extern Uint64 saveLen; +//! if (!SDL_WriteStorageFile(user, "save0.sav", saveData, saveLen)) { +//! // Something bad happened! +//! } +//! +//! SDL_CloseStorage(user); +//! } +//! ``` +//! +//! Note the improvements that [`SDL_Storage`] makes: +//! +//! 1. **What to Access:** This code explicitly reads from a title or user +//! storage device based on the context of the function. 2. **How to Access:** +//! This code explicitly uses either a read or write function based on the +//! context of the function. 3. **When to Access:** This code explicitly opens +//! the device when it needs to, and closes it when it is finished working with +//! the filesystem. +//! +//! The result is an application that is significantly more robust against the +//! increasing demands of platforms and their filesystems! +//! +//! A publicly available example of an [`SDL_Storage`] backend is the +//! [Steam Cloud](https://partner.steamgames.com/doc/features/cloud) +//! backend - you can initialize Steamworks when starting the program, and then +//! SDL will recognize that Steamworks is initialized and automatically use +//! ISteamRemoteStorage when the application opens user storage. More +//! importantly, when you _open_ storage it knows to begin a "batch" of +//! filesystem operations, and when you _close_ storage it knows to end and +//! flush the batch. This is used by Steam to support +//! [Dynamic Cloud Sync](https://steamcommunity.com/groups/steamworks/announcements/detail/3142949576401813670) +//! ; users can save data on one PC, put the device to sleep, and then continue +//! playing on another PC (and vice versa) with the save data fully +//! synchronized across all devices, allowing for a seamless experience without +//! having to do full restarts of the program. use super::stdinc::*; @@ -317,6 +508,10 @@ extern "C" { /// Synchronously read a file from a storage container into a client-provided /// buffer. /// + /// The value of `length` must match the length of the file exactly; call + /// [`SDL_GetStorageFileSize()`] to get this value. This behavior may be relaxed in + /// a future release. + /// /// ### Parameters /// - `storage`: a storage container to read from. /// - `path`: the relative path of the file to read.