begin et end frame dans context

This commit is contained in:
Jérôme Bousquié
2026-07-04 14:57:29 +02:00
parent 4fd39d32c6
commit 4b7a1b05e5
3 changed files with 98 additions and 1 deletions
+42
View File
@@ -0,0 +1,42 @@
# Technical Architecture Summary
## Three-Layer Structure
### Manager Layer (`Context`)
- **Responsibility:** Owner of hardware resource lifecycles (`Device`, `Queue`, `Surface`, `SurfaceConfiguration`).
- **Role:** Encapsulates windowing system and swapchain complexity. Exposes high-level methods such as `begin_frame()` and `end_frame()` to orchestrate GPU transactions.
### Specialist Layer (`Renderer`)
- **Responsibility:** Owner of rendering logic (`RenderPipeline`, `Shaders`, `Buffers`).
- **Role:** Executes actual drawing. Does not own hardware resources — uses references (`&Device`, `&TextureView`) provided at call time.
### Orchestrator Layer (`main.rs`)
- **Responsibility:** Business logic and execution loop.
- **Role:** Calls `Context` methods to obtain the target texture, passes that target to the `Renderer`, then triggers presentation.
---
## Benefits Analysis
### 1. Independence & Modularity
- **Runtime Agnosticism:** By avoiding any async calls or dependencies on runtimes like Tokio within the library, we ensure the code is portable and can be integrated into any kind of project (game, visualization tool, UI).
- **Decoupling:** The `Renderer` does not know about the existence of the windowing system (Winit). It could just as well draw onto an off-screen texture for headless rendering.
### 2. Performance & Efficiency
- **CPU/GPU Parallelism:** Using the `begin_frame` / `end_frame` pattern with a per-frame `CommandEncoder`, we maximize GPU utilization: the CPU prepares commands for one frame while the GPU executes those from the previous frame.
- **Persistent Resource Management:** The `Renderer` retains heavy objects (`RenderPipeline`) compiled only once. Conversely, ephemeral objects (`CommandEncoder`, `TextureView`) are created and freed quickly, minimizing long-term memory footprint.
### 3. Robustness (Safety & Errors)
- **Explicit Error Handling:** Use of `Result` types and safe methods such as `.first()` (instead of manual indexing) prevents panics during initialization or resizing, protecting the application against graphics driver instabilities.
---
## Summary
This structure transforms what is often called "spaghetti" graphics code — a mix of window management and shader computation — into a clean, predictable pipeline. The `Context` prepares the ground, the `Renderer` performs the drawing, and the orchestrator maintains the rhythm.
+31 -1
View File
@@ -64,7 +64,12 @@ impl Context {
/// Inputs: adapter (GPU capabilities), width/height (surface resolution).
/// Returns Ok(()) on success or SurfaceIncompatible if no SRGB format + alpha mode exist.
/// Typically called by the renderer when window size changes.
pub fn configure(&self, adapter: &wgpu::Adapter, width: u32, height: u32) -> Result<(), WsgError> {
pub fn configure(
&self,
adapter: &wgpu::Adapter,
width: u32,
height: u32,
) -> Result<(), WsgError> {
let caps = self.surface.get_capabilities(adapter);
// Prefer SRGB format for color accuracy; fall back to first available if none (technical point documented above).
let format = caps
@@ -95,4 +100,29 @@ impl Context {
self.surface.configure(&self.device, &config);
Ok(())
}
/// Acquires the next surface texture for rendering this frame. Returns an error variant
/// describing why acquisition failed (timeout, occlusion, surface lost, etc.).
/// Typically called by the orchestrator (main.rs) at the start of each frame loop iteration.
pub fn begin_frame(&self) -> Result<wgpu::SurfaceTexture, WsgError> {
// In wgpu 30, get_current_texture() returns CurrentSurfaceTexture enum directly (not Result).
// All variants are matched to provide explicit error handling instead of panicking.
match self.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(texture) => Ok(texture),
wgpu::CurrentSurfaceTexture::Suboptimal(_texture) => Err(WsgError::SuboptimalTexture),
wgpu::CurrentSurfaceTexture::Timeout => Err(WsgError::FrameTimeout),
wgpu::CurrentSurfaceTexture::Occluded => Err(WsgError::Occluded),
wgpu::CurrentSurfaceTexture::Outdated => Err(WsgError::Outdated),
wgpu::CurrentSurfaceTexture::Lost => Err(WsgError::Lost),
wgpu::CurrentSurfaceTexture::Validation => Err(WsgError::Validation),
}
}
/// Presents the rendered frame by submitting the acquired surface texture to the GPU queue.
/// The frame must have been obtained via begin_frame(); calling present() twice on the same
/// texture is undefined behavior. Called by the orchestrator after rendering commands are complete.
pub fn end_frame(&self, frame: wgpu::SurfaceTexture) {
// In wgpu 30, present() moved from SurfaceTexture::present() to Queue::present(frame).
self.queue.present(frame);
}
}
+25
View File
@@ -50,4 +50,29 @@ pub enum WsgError {
/// Caller: `Context::configure()` when selecting a render format/alpha mode from surface capabilities.
#[error("No compatible render format or alpha mode found for the surface")]
SurfaceIncompatible,
/// A timeout was encountered while acquiring a surface texture. Skip this frame and retry.
#[error("Frame acquisition timed out")]
FrameTimeout,
/// The window is occluded (minimized or behind another window). Skip until visible.
#[error("Window is occluded")]
Occluded,
/// The underlying surface changed — call configure() before retrying.
#[error("Surface configuration outdated; reconfigure required")]
Outdated,
/// The surface has been lost and needs to be recreated.
#[error("Surface lost")]
Lost,
/// A validation error inside get_current_texture() was raised.
#[error("Validation error during frame acquisition")]
Validation,
/// Successfully acquired the surface texture but it no longer matches the surface properties.
/// Reconfigure recommended for optimal performance.
#[error("Acquired suboptimal surface texture; reconfigure recommended")]
SuboptimalTexture,
}