ajout material et pipeline_cache
This commit is contained in:
+48
-57
@@ -1,72 +1,63 @@
|
||||
# DRAFT — Frame Loop
|
||||
# La Boucle de Rendu (Frame Loop)
|
||||
|
||||
## The "Frame Loop" (preparing the draw)
|
||||
Pour afficher quelque chose, nous suivons un cycle immuable appelé la Frame Lifetime. Dans ton `main.rs` (l'orchestrateur), le flux est désormais le suivant :
|
||||
|
||||
To display something, you must follow an immutable cycle called the **Frame Lifetime**. In your Renderer (or a dedicated method of Context), you will need to:
|
||||
- **Context::begin_frame() :** Acquiert la surface texture et crée la TextureView.
|
||||
- **Renderer::render(...)** : Utilise le CommandEncoder pour écrire les ordres de dessin.
|
||||
- **Context::end_frame()** : Soumet les commandes à la file (`queue`) et présente l'image.
|
||||
|
||||
1. **Acquire a surface texture** — ask the surface for the Back Buffer texture (where you'll draw).
|
||||
2. **Create a texture view** — WGPU doesn't draw directly on the texture, but on a "view" into that texture.
|
||||
3. **Create a command encoder** — this is the notebook where you write your orders ("clear screen", "draw triangle", "finalize").
|
||||
4. **Submit commands** — send this notebook to the GPU via the Queue.
|
||||
---
|
||||
|
||||
### Why this is the logical next step
|
||||
## Pourquoi cette séparation est vitale
|
||||
|
||||
Because without this, your `configure` call does nothing. You've reserved space on the GPU; now you need to learn how to "grasp" it to work on it.
|
||||
Le bloc `{ let mut render_pass = ... }` est crucial. Dans Rust, `render_pass` emprunte mutablement `encoder`. Il doit être détruit (via la fin du bloc ou un `drop()`) avant que tu puisses appeler `encoder.finish()`. Si tu oublies cela, le compilateur Rust refusera de compiler, empêchant ainsi des bugs critiques de synchronisation GPU.
|
||||
|
||||
Here is the skeleton of this cycle that you should implement:
|
||||
---
|
||||
|
||||
## Ressources : Persistantes vs Par-Frame
|
||||
|
||||
Avec notre nouvelle architecture "Atelier", la distinction est devenue encore plus nette :
|
||||
|
||||
| Élément | Durée de vie | Pourquoi ? |
|
||||
|---------|-------------|------------|
|
||||
| SurfaceConfiguration | Persistante | Ne change qu'au redimensionnement. |
|
||||
| RenderPipeline | Persistante | Stocké dans le PipelineCache (`Arc`), compilation unique. |
|
||||
| Material | Persistante | Définit le look ; partage le pipeline via `Arc`. |
|
||||
| Mesh | Persistante | Les données géométriques sont envoyées une fois au GPU. |
|
||||
| CommandEncoder | Par-Frame | Ton "carnet de notes" temporaire pour les ordres du GPU. |
|
||||
| TextureView | Par-Frame | Fenêtre temporaire sur la texture active du swapchain. |
|
||||
|
||||
---
|
||||
|
||||
## Ce qui a changé dans l'implémentation
|
||||
|
||||
Le Renderer n'est plus le propriétaire de la Surface. Sa méthode `render` est devenue un orchestrateur généraliste :
|
||||
|
||||
```rust
|
||||
// In your Renderer (or Context)
|
||||
pub fn render(&self) -> Result<(), WsgError> {
|
||||
// 1. Acquire the texture to draw on
|
||||
let frame = self.surface.get_current_texture()
|
||||
.map_err(|_| WsgError::SurfaceIncompatible)?;
|
||||
|
||||
// 2. Create the view (the "channel" to the texture)
|
||||
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
// 3. Create the command encoder
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
});
|
||||
|
||||
// --- This is where we'll draw later ---
|
||||
// Le Renderer ne connait plus la surface, il reçoit la vue
|
||||
pub fn render(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
view: &wgpu::TextureView,
|
||||
mesh: &Mesh,
|
||||
material: &Material
|
||||
) {
|
||||
let mut encoder = device.create_command_encoder(...);
|
||||
|
||||
{
|
||||
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLUE), // Blue background for testing
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
}
|
||||
let mut render_pass = encoder.begin_render_pass(...);
|
||||
render_pass.set_pipeline(&material.pipeline); // Recette via Material
|
||||
render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||
// ... dessin ...
|
||||
} // render_pass est automatiquement drop ici
|
||||
|
||||
// 4. Submit and present
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
frame.present();
|
||||
|
||||
Ok(())
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
```
|
||||
|
||||
### Why this separation is vital
|
||||
---
|
||||
|
||||
You'll notice that the `{ let _render_pass ... }` block is delimited by braces. This is very important in Rust: `render_pass` must be dropped before calling `encoder.finish()`. If you forget this, your program will crash because you'd be submitting orders while the "notebook" is still being written.
|
||||
## Pourquoi c'est l'étape logique suivante
|
||||
|
||||
|
||||
## Persistent vs. Per-Frame Resources
|
||||
|
||||
| Element | Lifetime | Why? |
|
||||
|---------|----------|------|
|
||||
| `SurfaceConfiguration` | Persistent | Only changes on resize |
|
||||
| `RenderPipeline` | Persistent | Very expensive to create (shader compilation) |
|
||||
| Buffers (Vertex/Index) | Persistent | Geometry data doesn't change every frame |
|
||||
| `CommandEncoder` | Frame | Temporary "notebook" for frame commands |
|
||||
| `TextureView` | Frame | View into the active Swapchain texture |
|
||||
En déléguant la gestion du Pipeline au Material et la possession de la Surface au Context, ton Renderer est devenu un moteur d'exécution pur. Il n'a plus besoin d'être réinitialisé quand la fenêtre change ou quand tu changes de shader : il est prêt à dessiner n'importe quel combo Mesh/Material que tu lui passes en paramètre.
|
||||
|
||||
Reference in New Issue
Block a user