use glow::HasContext;
use librashader_reflect::reflect::ShaderReflection;
use librashader_common::map::FastHashMap;
use librashader_common::{ImageFormat, Size, Viewport};
use librashader_preprocess::ShaderSource;
use librashader_presets::PassMeta;
use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, UniformBinding};
use librashader_runtime::binding::{
BindSemantics, ContextOffset, HdrUniformInputs, SensorUniformInputs, TextureInput,
UniformInputs,
};
use librashader_runtime::filter_pass::FilterPassMeta;
use librashader_runtime::render_target::RenderTarget;
use crate::binding::{GlUniformBinder, GlUniformStorage, UniformLocation, VariableLocation};
use crate::filter_chain::FilterCommon;
use crate::gl::{BindTexture, GLFramebuffer, GLInterface, UboRing};
use crate::options::FrameOptionsGL;
use crate::samplers::SamplerSet;
use crate::{error, GLImage};
use crate::texture::InputTexture;
pub struct UniformOffset {
pub location: VariableLocation,
pub offset: MemberOffset,
}
impl UniformOffset {
pub fn new(location: VariableLocation, offset: MemberOffset) -> Self {
Self { location, offset }
}
}
pub(crate) struct FilterPass<T: GLInterface> {
pub reflection: ShaderReflection,
pub program: glow::Program,
pub ubo_location: UniformLocation<Option<u32>>,
pub ubo_ring: Option<T::UboRing>,
pub(crate) uniform_storage: GlUniformStorage,
pub uniform_bindings: FastHashMap<UniformBinding, UniformOffset>,
pub source: ShaderSource,
pub meta: PassMeta,
}
impl TextureInput for InputTexture {
fn size(&self) -> Size<u32> {
self.image.size
}
}
impl ContextOffset<GlUniformBinder, VariableLocation, glow::Context> for UniformOffset {
fn offset(&self) -> MemberOffset {
self.offset
}
fn context(&self) -> VariableLocation {
self.location
}
}
impl<T: GLInterface> BindSemantics<GlUniformBinder, VariableLocation> for FilterPass<T> {
type InputTexture = InputTexture;
type SamplerSet = SamplerSet;
type DescriptorSet<'a> = ();
type DeviceContext = glow::Context;
type UniformOffset = UniformOffset;
fn bind_texture<'a>(
_descriptors: &mut Self::DescriptorSet<'a>,
samplers: &Self::SamplerSet,
binding: &TextureBinding,
texture: &Self::InputTexture,
device: &Self::DeviceContext,
) {
T::BindTexture::bind_texture(device, samplers, binding, texture);
}
}
impl<T: GLInterface> FilterPass<T> {
pub(crate) fn draw(
&mut self,
pass_index: usize,
parent: &FilterCommon,
frame_count: u32,
options: &FrameOptionsGL,
viewport: &Viewport<&GLImage>,
original: &InputTexture,
source: &InputTexture,
output: RenderTarget<GLFramebuffer, i32>,
output_size_override: Option<Size<u32>>,
) -> error::Result<()> {
let framebuffer = output.output;
if self.meta.mipmap_input && !parent.disable_mipmaps {
T::BindTexture::gen_mipmaps(&parent.context, source);
}
unsafe {
framebuffer.bind::<T::FramebufferInterface>()?;
parent.context.use_program(Some(self.program));
}
self.build_semantics(
pass_index,
parent,
output.mvp,
frame_count,
options,
output_size_override.unwrap_or(framebuffer.size),
viewport,
original,
source,
);
if self
.ubo_location
.vertex
.is_some_and(|index| index != glow::INVALID_INDEX)
&& self
.ubo_location
.fragment
.is_some_and(|index| index != glow::INVALID_INDEX)
{
if let (Some(ubo), Some(ring)) = (&self.reflection.ubo, &mut self.ubo_ring) {
ring.bind_for_frame(
&parent.context,
ubo,
&self.ubo_location,
&self.uniform_storage,
)
}
}
unsafe {
framebuffer.clear::<T::FramebufferInterface, false>();
parent.context.viewport(
output.x,
output.y,
output.size.width as i32,
output.size.height as i32,
);
if parent.caps.framebuffer_srgb {
if framebuffer.format == glow::SRGB8_ALPHA8 {
parent.context.enable(glow::FRAMEBUFFER_SRGB);
} else {
parent.context.disable(glow::FRAMEBUFFER_SRGB);
}
}
parent.context.draw_arrays(glow::TRIANGLE_STRIP, 0, 4);
if parent.caps.framebuffer_srgb {
parent.context.disable(glow::FRAMEBUFFER_SRGB);
}
parent.context.bind_framebuffer(glow::FRAMEBUFFER, None);
}
Ok(())
}
}
impl<T: GLInterface> FilterPassMeta for FilterPass<T> {
fn framebuffer_format(&self) -> ImageFormat {
self.source.format
}
fn meta(&self) -> &PassMeta {
&self.meta
}
}
impl<T: GLInterface> FilterPass<T> {
fn build_semantics(
&mut self,
pass_index: usize,
parent: &FilterCommon,
mvp: &[f32; 16],
frame_count: u32,
options: &FrameOptionsGL,
fb_size: Size<u32>,
viewport: &Viewport<&GLImage>,
original: &InputTexture,
source: &InputTexture,
) {
Self::bind_semantics(
&parent.context,
&parent.samplers,
&mut self.uniform_storage,
&mut (),
UniformInputs {
mvp,
frame_count,
rotation: options.rotation,
total_subframes: options.total_subframes,
current_subframe: options.current_subframe,
frame_direction: options.frame_direction,
aspect_ratio: options.aspect_ratio,
frames_per_second: options.frames_per_second,
frametime_delta: options.frametime_delta,
framebuffer_size: fb_size,
viewport_size: viewport.output.size,
hdr_inputs: HdrUniformInputs {
color_space: options.color_space,
brightness_nits: options.brightness_nits,
expand_gamut: options.expand_gamut,
},
sensor_inputs: SensorUniformInputs {
gyroscope: options.gyroscope,
accelerometer: options.accelerometer,
accelerometer_rest: options.accelerometer_rest,
},
},
original,
source,
&self.uniform_bindings,
&self.reflection.meta.texture_meta,
parent.output_textures[0..pass_index]
.iter()
.map(|o| o.bound()),
parent.feedback_textures.iter().map(|o| o.bound()),
parent.history_textures.iter().map(|o| o.bound()),
parent.luts.iter().map(|(u, i)| (*u, i)),
&self.source.parameters,
&parent.config,
);
}
}