//! # Renderer Module //! //! The **Specialist** (Executor) layer of the architecture. Owns rendering logic — orchestrates draw calls by binding //! Material pipelines and Mesh vertex buffers into a RenderPass, then submits commands to the GPU queue. //! Does not own hardware resources (Device, Queue); receives references when called by the orchestrator (main.rs). //! Does not own RenderPipelines or shaders — those are managed by PipelineCache and accessed through Material. //! Does not own Surface/TextureView — acquired from Context::begin_frame(). //! //! ## Interaction with Other Modules //! - **context**: receives Device/Queue references and TextureView; does not call begin/end_frame itself. //! - **pipeline_cache**: indirectly via Material — Renderer uses pipelines that PipelineCache compiled. //! - **mesh**: passes vertex/index buffers into set_vertex_buffer/set_index_buffer during draw. //! - **material**: provides the RenderPipeline reference via set_pipeline during draw. pub struct Renderer {} impl Renderer { /// Creates a new Renderer instance with no internal state — it is pure execution logic only. /// Called once at application startup; the same Renderer is reused for all frames. pub fn new() -> Self { Self {} } /// Orchestrates a single draw call: creates an encoder, starts a render pass, binds pipeline + buffers, draws, then submits commands. /// Inputs: device (GPU command source), queue (command submission target), view (surface texture output), mesh (geometry to draw), material (shader/pipeline). /// Returns nothing — side-effect: GPU executes the draw and presents to the surface. Called by the orchestrator (main.rs) once per frame. /// Internal steps: 1) create_command_encoder → 2) begin_render_pass in scoped block → 3) set_pipeline/set_vertex_buffer/draw → drop(render_pass) → 4) submit(encoder.finish()). pub fn render( &self, device: &wgpu::Device, queue: &wgpu::Queue, view: &wgpu::TextureView, mesh: &crate::mesh::Mesh, material: &crate::material::Material, ) { // Create command encoder let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("render encoder"), }); { // Block creation so render_pass is dropped before queue submit // RenderPass start — depth_slice is a new required field in wgpu 30 for multisampled textures. let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view, resolve_target: None, depth_slice: None, // new field; None means no multisample resolve needed ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store, }, })], ..Default::default() }); // Pipeline call — draw with empty vertex buffers (geometry defined in shader) render_pass.set_pipeline(&material.pipeline); // if any indices if let Some(index_buffer) = &mesh.index_buffer { render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16); render_pass.draw_indexed(0..mesh.num_indices, 0, 0..1); } else { render_pass.draw(0..mesh.num_vertices, 0..1); } } // Queue submit queue.submit(std::iter::once(encoder.finish())); } /// Submits a completed command encoder to the GPU queue for execution. /// Inputs: queue (GPU command submission target), encoder (completed command buffer). /// Returns nothing — side-effect: GPU executes all recorded commands. Called by renderer code after render pass completion. pub fn submit_commands(&self, queue: &wgpu::Queue, encoder: wgpu::CommandEncoder) { queue.submit(std::iter::once(encoder.finish())); } }