shdrlib 0.2.0

High-level shader compilation and rendering library built on wgpu and naga
Documentation
use crate::managers::asset_manager::AssetManager;
use wgpu;

/// RuntimeManager handles executing render passes with your pipelines
/// Holds references to pipelines by name and executes them
pub struct RuntimeManager<'a> {
    asset_manager: &'a AssetManager,
    surface: Option<wgpu::Surface<'a>>,
    surface_config: Option<wgpu::SurfaceConfiguration>,
}

impl<'a> RuntimeManager<'a> {
    /// Create a new runtime manager that references an AssetManager
    pub fn new(asset_manager: &'a AssetManager) -> Self {
        Self {
            asset_manager,
            surface: None,
            surface_config: None,
        }
    }

    /// Configure a surface for rendering (optional - for window rendering)
    pub fn with_surface(
        mut self,
        surface: wgpu::Surface<'a>,
        width: u32,
        height: u32,
        format: wgpu::TextureFormat,
    ) -> Self {
        let config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format,
            width,
            height,
            present_mode: wgpu::PresentMode::Fifo,
            alpha_mode: wgpu::CompositeAlphaMode::Auto,
            view_formats: vec![],
            desired_maximum_frame_latency: 2,
        };

        surface.configure(self.asset_manager.device(), &config);
        self.surface = Some(surface);
        self.surface_config = Some(config);
        self
    }

    /// Execute a render pass with a specific pipeline
    /// Returns the command buffer for submission
    pub fn render_pass(
        &self,
        pipeline_name: &str,
        target_view: &wgpu::TextureView,
        clear_color: wgpu::Color,
        vertex_count: u32,
    ) -> Result<wgpu::CommandBuffer, String> {
        // Get the pipeline
        let duo = self.asset_manager.get_pipeline(pipeline_name)
            .ok_or_else(|| format!("Pipeline '{}' not found", pipeline_name))?;

        // Create command encoder
        let mut encoder = self.asset_manager.device().create_command_encoder(
            &wgpu::CommandEncoderDescriptor {
                label: Some("Render Encoder"),
            }
        );

        // Begin render pass
        {
            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                label: Some("Render Pass"),
                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                    view: target_view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(clear_color),
                        store: wgpu::StoreOp::Store,
                    },
                })],
                depth_stencil_attachment: None,
                timestamp_writes: None,
                occlusion_query_set: None,
            });

            // Set the pipeline and draw
            render_pass.set_pipeline(&duo.pipeline);
            render_pass.draw(0..vertex_count, 0..1);
        }

        Ok(encoder.finish())
    }

    /// Convenience method: render to surface (if configured)
    pub fn render_to_surface(
        &self,
        pipeline_name: &str,
        clear_color: wgpu::Color,
        vertex_count: u32,
    ) -> Result<(), String> {
        let surface = self.surface.as_ref()
            .ok_or("Surface not configured")?;

        // Get surface texture
        let output = surface.get_current_texture()
            .map_err(|e| format!("Failed to get surface texture: {:?}", e))?;

        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());

        // Render
        let command_buffer = self.render_pass(pipeline_name, &view, clear_color, vertex_count)?;

        // Submit and present
        self.asset_manager.queue().submit(Some(command_buffer));
        output.present();

        Ok(())
    }

    /// Render to a texture and return it (for offscreen rendering)
    pub fn render_to_texture(
        &self,
        pipeline_name: &str,
        width: u32,
        height: u32,
        format: wgpu::TextureFormat,
        clear_color: wgpu::Color,
        vertex_count: u32,
    ) -> Result<wgpu::Texture, String> {
        // Create texture
        let texture = self.asset_manager.device().create_texture(&wgpu::TextureDescriptor {
            label: Some("Render Target"),
            size: wgpu::Extent3d {
                width,
                height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format,
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
            view_formats: &[],
        });

        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());

        // Render to it
        let command_buffer = self.render_pass(pipeline_name, &view, clear_color, vertex_count)?;
        self.asset_manager.queue().submit(Some(command_buffer));

        Ok(texture)
    }
}