1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
use crate::{ext::*, math::*, platform::*, *}; use riddle_common::define_handles; /// A simple 2D sprite based renderer. /// /// A renderer can be created for a Window and holds a reference to the window, which will /// keep the window alive as long as the renderer is alive. /// /// # Example /// /// ```no_run /// use riddle::{common::Color, platform::*, renderer::*, math::*, *}; /// fn main() -> Result<(), RiddleError> { /// let rdl = RiddleLib::new()?; /// let window = WindowBuilder::new().build(rdl.context())?; /// /// let renderer = Renderer::new_from_window(&window)?; /// /// let mut render_ctx /*: impl RenderContext*/ = renderer.begin_render()?; /// render_ctx.clear(Color::RED)?; /// /// // Change the current transform matrix, and draw a rect /// render_ctx.set_transform(glam::Mat4::from_scale(glam::vec3(2.0, 2.0, 1.0)).into())?; /// render_ctx.fill_rect(&Rect::new(vec2(0.0, 0.0), vec2(10.0, 10.0)), Color::GREEN)?; /// /// render_ctx.present()?; /// Ok(()) /// } /// ``` pub struct Renderer { weak_self: RendererWeak, wgpu_device: Box<dyn RendererWGPUDevice>, standard_res: StandardResources, } define_handles!(<Renderer>::weak_self, pub RendererHandle, pub RendererWeak); impl Renderer { /// Initialize a new Renderer, creating a WGPU device for the window. /// /// # Example /// /// ```no_run /// # use riddle::{common::Color, platform::*, renderer::*, math::*, *}; /// # fn main() -> Result<(), RiddleError> { /// let rdl = RiddleLib::new()?; /// let window = WindowBuilder::new().build(rdl.context())?; /// /// let renderer = Renderer::new_from_window(&window)?; /// # Ok(()) } /// ``` pub fn new_from_window(window: &Window) -> Result<RendererHandle> { let wgpu_device = WindowWGPUDevice::new(window)?; Self::new_from_device(Box::new(wgpu_device)) } /// Get the frame dimensions as reported by the [`RendererWGPUDevice`]. /// /// In the case of a default Window renderer, this will be the internal size of /// the window in logical units. /// /// # Example /// /// ```no_run /// # use riddle::{common::Color, platform::*, renderer::*, math::*, *}; /// # fn main() -> Result<(), RiddleError> { /// let rdl = RiddleLib::new()?; /// let window = WindowBuilder::new().dimensions(300, 400).build(rdl.context())?; /// /// let renderer = Renderer::new_from_window(&window)?; /// /// assert_eq!(vec2(300.0, 400.0), renderer.dimensions()); /// # Ok(()) } /// ``` pub fn dimensions(&self) -> Vector2<f32> { self.wgpu_device.viewport_dimensions() } /// Get a render context for the current swap chain frame. /// /// # Example /// /// ```no_run /// # use riddle::{common::Color, platform::*, renderer::*, math::*, *}; /// # fn main() -> Result<(), RiddleError> { /// # let rdl = RiddleLib::new()?; /// # let window = WindowBuilder::new().build(rdl.context())?; /// let renderer = Renderer::new_from_window(&window)?; /// /// let mut render_ctx = renderer.begin_render()?; /// render_ctx.clear(Color::RED); /// render_ctx.present(); /// # Ok(()) } /// ``` pub fn begin_render<'a>(&'a self) -> Result<impl RenderContext + 'a> { let encoder = self .wgpu_device .device() .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); let target = SwapChainFrameTarget::new(self, self.dimensions()); BufferedRenderer::new(target, encoder) } pub(crate) fn standard_res(&self) -> &StandardResources { &self.standard_res } } impl RendererWGPU for Renderer { fn wgpu_device(&self) -> &dyn RendererWGPUDevice { Box::as_ref(&self.wgpu_device) } /// Or the renderer can be built on top of existing WGPU contexts, to allow the simple /// renderer to be used on top of custom renderers. fn new_from_device(wgpu_device: Box<dyn RendererWGPUDevice>) -> Result<RendererHandle> { let vs = include_bytes!("shaders/default.vert.spv"); let fs = include_bytes!("shaders/default.frag.spv"); let sprite_shader = Shader::from_readers( wgpu_device.device(), std::io::Cursor::new(&vs[..]), std::io::Cursor::new(&fs[..]), wgpu::PrimitiveTopology::TriangleList, )?; let mut white_img = image::Image::new(1, 1); white_img.set_pixel(0, 0, Color::from([0xFF; 4])); let white_tex = Texture::from_image( wgpu_device.device(), wgpu_device.queue(), white_img, FilterMode::Nearest, FilterMode::Nearest, TextureType::Plain, )? .into(); let standard_res = StandardResources { default_shader: sprite_shader, white_tex, }; Ok(RendererHandle::new(|weak_self| Self { weak_self, wgpu_device, standard_res, })) } } #[doc(hidden)] #[derive(Clone)] pub struct StandardResources { pub(super) default_shader: ShaderHandle, pub(super) white_tex: TextureHandle, }