mod clear_state;
#[doc(inline)]
pub use clear_state::*;
mod color_target;
#[doc(inline)]
pub use color_target::*;
mod depth_target;
#[doc(inline)]
pub use depth_target::*;
mod multisample;
#[doc(inline)]
pub use multisample::*;
mod color_target_multisample;
#[doc(inline)]
pub use color_target_multisample::*;
mod depth_target_multisample;
#[doc(inline)]
pub use depth_target_multisample::*;
use crate::core::*;
use crate::context::Framebuffer;
pub struct RenderTarget<'a> {
id: Option<Framebuffer>,
color: Option<ColorTarget<'a>>,
depth: Option<DepthTarget<'a>>,
pub(crate) context: Context,
width: u32,
height: u32,
}
impl<'a> RenderTarget<'a> {
pub fn screen(context: &Context, width: u32, height: u32) -> Self {
Self {
context: context.clone(),
id: None,
color: None,
depth: None,
width,
height,
}
}
pub fn new(color: ColorTarget<'a>, depth: DepthTarget<'a>) -> Self {
let width = color.width();
let height = color.height();
Self {
context: color.context.clone(),
id: Some(new_framebuffer(&color.context)),
color: Some(color),
depth: Some(depth),
width,
height,
}
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn clear(&self, clear_state: ClearState) -> &Self {
self.clear_partially(self.scissor_box(), clear_state)
}
pub fn clear_partially(&self, scissor_box: ScissorBox, clear_state: ClearState) -> &Self {
self.context.set_scissor(scissor_box);
self.bind(crate::context::DRAW_FRAMEBUFFER);
clear_state.apply(&self.context);
self
}
pub fn write(&self, render: impl FnOnce()) -> &Self {
self.write_partially(self.scissor_box(), render)
}
pub fn write_partially(&self, scissor_box: ScissorBox, render: impl FnOnce()) -> &Self {
self.context.set_scissor(scissor_box);
self.bind(crate::context::DRAW_FRAMEBUFFER);
render();
if let Some(ref color) = self.color {
color.generate_mip_maps();
}
self
}
pub fn read_color<T: TextureDataType>(&self) -> Vec<T> {
self.read_color_partially(self.scissor_box())
}
pub fn read_color_partially<T: TextureDataType>(&self, scissor_box: ScissorBox) -> Vec<T> {
if self.id.is_some() && self.color.is_none() {
panic!("cannot read color from a render target without a color target");
}
self.bind(crate::context::DRAW_FRAMEBUFFER);
self.bind(crate::context::READ_FRAMEBUFFER);
let mut data_size = std::mem::size_of::<T>();
if data_size / T::size() as usize == 1 {
data_size *= 4 / T::size() as usize
}
let mut bytes =
vec![0u8; scissor_box.width as usize * scissor_box.height as usize * data_size];
unsafe {
self.context.read_pixels(
scissor_box.x,
scissor_box.y,
scissor_box.width as i32,
scissor_box.height as i32,
format_from_data_type::<T>(),
T::data_type(),
crate::context::PixelPackData::Slice(&mut bytes),
);
}
let mut pixels = from_byte_slice(&bytes).to_vec();
flip_y(
&mut pixels,
scissor_box.width as usize,
scissor_box.height as usize,
);
pixels
}
#[cfg(not(target_arch = "wasm32"))]
pub fn read_depth(&self) -> Vec<f32> {
self.read_depth_partially(self.scissor_box())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn read_depth_partially(&self, scissor_box: ScissorBox) -> Vec<f32> {
if self.id.is_some() && self.depth.is_none() {
panic!("cannot read depth from a render target without a depth target");
}
self.bind(crate::context::DRAW_FRAMEBUFFER);
self.bind(crate::context::READ_FRAMEBUFFER);
let mut pixels = vec![0u8; scissor_box.width as usize * scissor_box.height as usize * 4];
unsafe {
self.context.read_pixels(
scissor_box.x,
scissor_box.y,
scissor_box.width as i32,
scissor_box.height as i32,
crate::context::DEPTH_COMPONENT,
crate::context::FLOAT,
crate::context::PixelPackData::Slice(&mut pixels),
);
}
from_byte_slice(&pixels).to_vec()
}
#[deprecated = "use apply_screen_effect with a CopyEffect instead"]
pub fn copy_from(
&self,
color_texture: ColorTexture,
depth_texture: DepthTexture,
viewport: Viewport,
write_mask: WriteMask,
) -> &Self {
#[allow(deprecated)]
self.copy_partially_from(
self.scissor_box(),
color_texture,
depth_texture,
viewport,
write_mask,
)
}
#[deprecated = "use apply_screen_effect_partially with a CopyEffect instead"]
pub fn copy_partially_from(
&self,
scissor_box: ScissorBox,
color_texture: ColorTexture,
depth_texture: DepthTexture,
viewport: Viewport,
write_mask: WriteMask,
) -> &Self {
self.write_partially(scissor_box, || {
let mut id = (0b1u16 << 15).to_le_bytes().to_vec();
id.extend(
(0b1u16 << 13
| 0b1u16 << 12
| 0b1u16 << 10
| color_texture.id()
| depth_texture.id())
.to_le_bytes(),
);
let mut programs = self.context.programs.write().unwrap();
let program = programs.entry(id).or_insert_with(|| {
let fragment_shader_source = format!(
"{}\n{}\n
in vec2 uvs;
layout (location = 0) out vec4 color;
void main()
{{
color = sample_color(uvs);
gl_FragDepth = sample_depth(uvs);
}}",
color_texture.fragment_shader_source(),
depth_texture.fragment_shader_source()
);
Program::from_source(
&self.context,
full_screen_vertex_shader_source(),
&fragment_shader_source,
)
.expect("Failed compiling shader")
});
color_texture.use_uniforms(program);
depth_texture.use_uniforms(program);
full_screen_draw(
&self.context,
&program,
RenderStates {
depth_test: DepthTest::Always,
write_mask,
..Default::default()
},
viewport,
)
})
}
#[deprecated = "use apply_screen_effect with a CopyEffect instead"]
pub fn copy_from_color(
&self,
color_texture: ColorTexture,
viewport: Viewport,
write_mask: WriteMask,
) -> &Self {
#[allow(deprecated)]
self.copy_partially_from_color(self.scissor_box(), color_texture, viewport, write_mask)
}
#[deprecated = "use apply_screen_effect_partially with a CopyEffect instead"]
pub fn copy_partially_from_color(
&self,
scissor_box: ScissorBox,
color_texture: ColorTexture,
viewport: Viewport,
write_mask: WriteMask,
) -> &Self {
self.write_partially(scissor_box, || {
let mut id = (0b1u16 << 15).to_le_bytes().to_vec();
id.extend(
(0b1u16 << 13 | 0b1u16 << 11 | 0b1u16 << 10 | color_texture.id()).to_le_bytes(),
);
let mut programs = self.context.programs.write().unwrap();
let program = programs.entry(id).or_insert_with(|| {
let fragment_shader_source = format!(
"{}\nin vec2 uvs;
layout (location = 0) out vec4 color;
void main()
{{
color = sample_color(uvs);
}}",
color_texture.fragment_shader_source()
);
Program::from_source(
&self.context,
full_screen_vertex_shader_source(),
&fragment_shader_source,
)
.expect("Failed compiling shader")
});
color_texture.use_uniforms(program);
full_screen_draw(
&self.context,
&program,
RenderStates {
depth_test: DepthTest::Always,
write_mask,
..Default::default()
},
viewport,
)
})
}
#[deprecated = "use apply_screen_effect with a CopyEffect instead"]
pub fn copy_from_depth(&self, depth_texture: DepthTexture, viewport: Viewport) -> &Self {
#[allow(deprecated)]
self.copy_partially_from_depth(self.scissor_box(), depth_texture, viewport)
}
#[deprecated = "use apply_screen_effect_partially with a CopyEffect instead"]
pub fn copy_partially_from_depth(
&self,
scissor_box: ScissorBox,
depth_texture: DepthTexture,
viewport: Viewport,
) -> &Self {
self.write_partially(scissor_box, || {
let mut id = (0b1u16 << 15).to_le_bytes().to_vec();
id.extend((0b1u16 << 13 | 0b1u16 << 10 | depth_texture.id()).to_le_bytes());
let mut programs = self.context.programs.write().unwrap();
let program = programs.entry(id).or_insert_with(|| {
let fragment_shader_source = format!(
"{}\n
in vec2 uvs;
void main()
{{
gl_FragDepth = sample_depth(uvs);
}}",
depth_texture.fragment_shader_source(),
);
Program::from_source(
&self.context,
full_screen_vertex_shader_source(),
&fragment_shader_source,
)
.expect("Failed compiling shader")
});
depth_texture.use_uniforms(program);
full_screen_draw(
&self.context,
&program,
RenderStates {
depth_test: DepthTest::Always,
write_mask: WriteMask::DEPTH,
..Default::default()
},
viewport,
)
})
}
pub fn from_framebuffer(
context: &Context,
width: u32,
height: u32,
framebuffer: Framebuffer,
) -> Self {
Self {
id: Some(framebuffer),
color: None,
depth: None,
context: context.clone(),
width,
height,
}
}
pub fn into_framebuffer(mut self) -> Option<Framebuffer> {
self.id.take()
}
pub(in crate::core) fn blit_to(&self, target: &RenderTarget) {
self.bind(crate::context::DRAW_FRAMEBUFFER);
target.bind(crate::context::DRAW_FRAMEBUFFER);
let target_is_screen = target.color.is_none() && target.depth.is_none();
let mask = if self.color.is_some() && (target.color.is_some() || target_is_screen) {
let mut mask = crate::context::COLOR_BUFFER_BIT;
if self.depth.is_some() && (target.depth.is_some() || target_is_screen) {
mask |= crate::context::DEPTH_BUFFER_BIT;
}
mask
} else if self.depth.is_some() && (target.depth.is_some() || target_is_screen) {
crate::context::DEPTH_BUFFER_BIT
} else {
unreachable!()
};
unsafe {
self.context
.bind_framebuffer(crate::context::READ_FRAMEBUFFER, self.id);
self.context.blit_framebuffer(
0,
0,
self.width as i32,
self.height as i32,
0,
0,
target.width as i32,
target.height as i32,
mask,
crate::context::NEAREST,
);
}
}
fn new_color(color: ColorTarget<'a>) -> Self {
let width = color.width();
let height = color.height();
Self {
context: color.context.clone(),
id: Some(new_framebuffer(&color.context)),
color: Some(color),
depth: None,
width,
height,
}
}
fn new_depth(depth: DepthTarget<'a>) -> Self {
let width = depth.width();
let height = depth.height();
Self {
context: depth.context.clone(),
id: Some(new_framebuffer(&depth.context)),
depth: Some(depth),
color: None,
width,
height,
}
}
fn bind(&self, target: u32) {
unsafe {
self.context.bind_framebuffer(target, self.id);
}
if let Some(ref color) = self.color {
color.bind(&self.context);
}
if let Some(ref depth) = self.depth {
depth.bind();
}
}
}
impl Drop for RenderTarget<'_> {
fn drop(&mut self) {
unsafe {
if let Some(id) = self.id {
self.context.delete_framebuffer(id);
}
}
}
}
fn size_with_mip(size: u32, mip: Option<u32>) -> u32 {
if let Some(mip) = mip {
size / 2u32.pow(mip)
} else {
size
}
}
fn new_framebuffer(context: &Context) -> crate::context::Framebuffer {
unsafe {
context
.create_framebuffer()
.expect("Failed creating frame buffer")
}
}
#[cfg(debug_assertions)]
fn multisample_sanity_check(context: &Context, number_of_samples: u32) {
let max_samples: u32 = unsafe {
context
.get_parameter_i32(crate::context::MAX_SAMPLES)
.try_into()
.unwrap()
};
if number_of_samples > max_samples {
panic!("number_of_samples ({}) for multisample target is larger than supported number of samples: {}", number_of_samples, max_samples);
}
if (number_of_samples != 0) && number_of_samples & (number_of_samples - 1) != 0 {
panic!("number_of_samples ({}) for multisample target must be a power of 2 (and larger than 0).", number_of_samples);
}
}
macro_rules! impl_render_target_core_extensions_body {
() => {
pub fn scissor_box(&self) -> ScissorBox {
ScissorBox::new_at_origo(self.width(), self.height())
}
pub fn viewport(&self) -> Viewport {
Viewport::new_at_origo(self.width(), self.height())
}
};
}
macro_rules! impl_render_target_core_extensions {
($name:ident < $a:ident : $ta:tt , $b:ident : $tb:tt >) => {
impl<$a: $ta, $b: $tb> $name<$a, $b> {
impl_render_target_core_extensions_body!();
}
};
($name:ident < $a:ident : $ta:tt >) => {
impl<$a: $ta> $name<$a> {
impl_render_target_core_extensions_body!();
}
};
($name:ident < $lt:lifetime >) => {
impl<$lt> $name<$lt> {
impl_render_target_core_extensions_body!();
}
};
($name:ty) => {
impl $name {
impl_render_target_core_extensions_body!();
}
};
}
impl_render_target_core_extensions!(RenderTarget<'a>);
impl_render_target_core_extensions!(ColorTarget<'a>);
impl_render_target_core_extensions!(DepthTarget<'a>);
impl_render_target_core_extensions!(
RenderTargetMultisample<C: TextureDataType, D: DepthTextureDataType>
);
impl_render_target_core_extensions!(ColorTargetMultisample<C: TextureDataType>);
impl_render_target_core_extensions!(DepthTargetMultisample<D: DepthTextureDataType>);