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
use math::Vector2;

use crate::*;

/// A target which can be both rendered to and referenced as a [`Sprite`] for rendering.
///
/// # Example
///
/// ```no_run
/// # use riddle::{common::Color, image::*, 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 target = SpriteRenderTarget::new(&renderer, vec2(100, 100))?;
///
/// target.render(|target_ctx| {
///     target_ctx.clear(Color::BLUE)
/// })?;
///
/// renderer.render(|render_ctx| {
///     render_ctx.clear(Color::GREEN)?;
///     target.sprite().render_at(render_ctx, vec2(0.0, 0.0))
/// })?;
/// # Ok(()) }
/// ```
pub struct SpriteRenderTarget<Device: WGPUDevice> {
	renderer: Renderer<Device>,

	texture: Texture,
	sprite: Sprite<Device>,
}

impl<Device> SpriteRenderTarget<Device>
where
	Device: WGPUDevice,
{
	/// Create a new render target with the specified dimensions
	pub fn new(renderer: &Renderer<Device>, dimensions: Vector2<u32>) -> Result<Self> {
		let texture = renderer.wgpu_device().with_device_info(|info| {
			Texture::new(
				info.device,
				FilterMode::Linear,
				FilterMode::Linear,
				TextureType::RenderTarget,
				dimensions,
			)
		})?;

		let sprite = Sprite::from_texture(renderer, &texture)?;

		Ok(Self {
			renderer: renderer.clone(),

			texture,
			sprite,
		})
	}

	/// 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 target = SpriteRenderTarget::new(&renderer, vec2(100, 100))?;
	///
	/// target.render(|target_ctx| {
	///     target_ctx.clear(Color::RED)
	/// })?;
	/// # Ok(()) }
	/// ```
	pub fn render<R, F>(&self, f: F) -> std::result::Result<R, RendererError>
	where
		F: FnOnce(
			&mut BufferedRenderer<Device, &SpriteRenderTarget<Device>>,
		) -> std::result::Result<R, RendererError>,
	{
		let encoder = self.renderer.wgpu_device().with_device_info(|info| {
			Ok(info
				.device
				.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }))
		})?;

		let mut ctx = BufferedRenderer::new(self, encoder)?;
		let result = f(&mut ctx)?;
		ctx.present()?;

		Ok(result)
	}

	/// Get the sprite which can be used to render the contents of the render target.
	pub fn sprite(&self) -> &Sprite<Device> {
		&self.sprite
	}
}

impl<Device> WGPURenderTargetDesc<Device> for &SpriteRenderTarget<Device>
where
	Device: WGPUDevice,
{
	fn dimensions(&self) -> Vector2<f32> {
		self.sprite.dimensions()
	}

	#[inline]
	fn with_view<F: FnOnce(&wgpu::TextureView) -> Result<()>>(&self, f: F) -> Result<()> {
		let view = self
			.texture
			.internal
			.texture
			.create_view(&wgpu::TextureViewDescriptor {
				format: Some(wgpu::TextureFormat::Bgra8UnormSrgb),
				dimension: Some(wgpu::TextureViewDimension::D2),
				aspect: wgpu::TextureAspect::All,
				..Default::default()
			});
		f(&view)
	}

	fn renderer(&self) -> &Renderer<Device> {
		&self.renderer
	}

	fn standard_resources(&self) -> &StandardResources {
		self.renderer.standard_res()
	}

	fn begin_render(&self) -> Result<()> {
		Ok(())
	}

	fn end_render(&self) {}
}