64 lines
2.7 KiB
Markdown
64 lines
2.7 KiB
Markdown
# La Boucle de Rendu (Frame Loop)
|
|
|
|
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 :
|
|
|
|
- **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.
|
|
|
|
---
|
|
|
|
## Pourquoi cette séparation est vitale
|
|
|
|
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.
|
|
|
|
---
|
|
|
|
## 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
|
|
// 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 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
|
|
|
|
queue.submit(std::iter::once(encoder.finish()));
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Pourquoi c'est l'étape logique suivante
|
|
|
|
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.
|