use std::cell::UnsafeCell;
use std::sync::Arc;
use wasm_bindgen::JsCast;
use web_sys::WebGl2RenderingContext as Gl;
use crate::image::format::{Filterable, TextureFormat};
use crate::runtime::{Connection, RenderingContext};
use crate::task::Progress;
use crate::task::{ContextId, GpuTask};
use crate::util::JsId;
use std::hash::{Hash, Hasher};
mod filter_seal {
use super::{
Linear, LinearMipmapLinear, LinearMipmapNearest, Nearest, NearestMipmapLinear,
NearestMipmapNearest,
};
pub trait Seal {}
impl Seal for Nearest {}
impl Seal for Linear {}
impl Seal for NearestMipmapNearest {}
impl Seal for NearestMipmapLinear {}
impl Seal for LinearMipmapNearest {}
impl Seal for LinearMipmapLinear {}
}
pub trait MagnificationFilter: filter_seal::Seal {
const ID: u32;
}
pub trait MinificationFilter: filter_seal::Seal {
const ID: u32;
}
pub unsafe trait CompatibleFilter<F>
where
F: TextureFormat,
{
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Nearest;
impl MinificationFilter for Nearest {
const ID: u32 = Gl::NEAREST;
}
impl MagnificationFilter for Nearest {
const ID: u32 = Gl::NEAREST;
}
unsafe impl<F> CompatibleFilter<F> for Nearest where F: TextureFormat {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Linear;
impl MinificationFilter for Linear {
const ID: u32 = Gl::LINEAR;
}
impl MagnificationFilter for Linear {
const ID: u32 = Gl::LINEAR;
}
unsafe impl<F> CompatibleFilter<F> for Linear where F: TextureFormat + Filterable {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct NearestMipmapNearest;
impl MinificationFilter for NearestMipmapNearest {
const ID: u32 = Gl::NEAREST_MIPMAP_NEAREST;
}
unsafe impl<F> CompatibleFilter<F> for NearestMipmapNearest where F: TextureFormat {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct NearestMipmapLinear;
impl MinificationFilter for NearestMipmapLinear {
const ID: u32 = Gl::NEAREST_MIPMAP_LINEAR;
}
unsafe impl<F> CompatibleFilter<F> for NearestMipmapLinear where F: TextureFormat + Filterable {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct LinearMipmapNearest;
impl MinificationFilter for LinearMipmapNearest {
const ID: u32 = Gl::LINEAR_MIPMAP_NEAREST;
}
unsafe impl<F> CompatibleFilter<F> for LinearMipmapNearest where F: TextureFormat + Filterable {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct LinearMipmapLinear;
impl MinificationFilter for LinearMipmapLinear {
const ID: u32 = Gl::LINEAR_MIPMAP_LINEAR;
}
unsafe impl<F> CompatibleFilter<F> for LinearMipmapLinear where F: TextureFormat + Filterable {}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Wrap {
ClampToEdge = Gl::CLAMP_TO_EDGE as isize,
Repeat = Gl::REPEAT as isize,
MirroredRepeat = Gl::MIRRORED_REPEAT as isize,
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct LODRange {
min: f32,
max: f32,
}
impl Default for LODRange {
fn default() -> Self {
LODRange {
min: -1000.0,
max: 1000.0,
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct SamplerDescriptor<Min, Mag> {
pub minification_filter: Min,
pub magnification_filter: Mag,
pub lod_range: LODRange,
pub wrap_s: Wrap,
pub wrap_t: Wrap,
pub wrap_r: Wrap,
}
impl SamplerDescriptor<NearestMipmapLinear, Linear> {
pub fn default() -> Self {
Default::default()
}
}
macro_rules! impl_default_for_sampler_descriptor {
($min:ident, $mag:ident) => {
impl Default for SamplerDescriptor<$min, $mag> {
fn default() -> Self {
SamplerDescriptor {
minification_filter: $min,
magnification_filter: $mag,
lod_range: LODRange::default(),
wrap_s: Wrap::Repeat,
wrap_t: Wrap::Repeat,
wrap_r: Wrap::Repeat,
}
}
}
};
}
impl_default_for_sampler_descriptor!(Nearest, Nearest);
impl_default_for_sampler_descriptor!(Linear, Nearest);
impl_default_for_sampler_descriptor!(NearestMipmapNearest, Nearest);
impl_default_for_sampler_descriptor!(NearestMipmapLinear, Nearest);
impl_default_for_sampler_descriptor!(LinearMipmapNearest, Nearest);
impl_default_for_sampler_descriptor!(LinearMipmapLinear, Nearest);
impl_default_for_sampler_descriptor!(Nearest, Linear);
impl_default_for_sampler_descriptor!(Linear, Linear);
impl_default_for_sampler_descriptor!(NearestMipmapNearest, Linear);
impl_default_for_sampler_descriptor!(NearestMipmapLinear, Linear);
impl_default_for_sampler_descriptor!(LinearMipmapNearest, Linear);
impl_default_for_sampler_descriptor!(LinearMipmapLinear, Linear);
pub struct Sampler<Min, Mag> {
object_id: u64,
data: Arc<SamplerData>,
descriptor: SamplerDescriptor<Min, Mag>,
}
impl<Min, Mag> Sampler<Min, Mag> {
pub(crate) fn data(&self) -> &Arc<SamplerData> {
&self.data
}
}
impl<Min, Mag> Sampler<Min, Mag>
where
Min: MinificationFilter + Copy + 'static,
Mag: MagnificationFilter + Copy + 'static,
{
pub(crate) fn new<Rc>(
context: &Rc,
object_id: u64,
descriptor: &SamplerDescriptor<Min, Mag>,
) -> Self
where
Rc: RenderingContext + Clone + 'static,
{
let data = Arc::new(SamplerData {
id: UnsafeCell::new(None),
context_id: context.id(),
dropper: Box::new(context.clone()),
});
context.submit(SamplerAllocateCommand {
data: data.clone(),
descriptor: descriptor.clone(),
});
Sampler {
object_id,
data,
descriptor: descriptor.clone(),
}
}
pub fn minification_filter(&self) -> Min {
self.descriptor.minification_filter
}
pub fn magnification_filter(&self) -> Mag {
self.descriptor.magnification_filter
}
pub fn lod_range(&self) -> LODRange {
self.descriptor.lod_range
}
pub fn wrap_s(&self) -> Wrap {
self.descriptor.wrap_s
}
pub fn wrap_t(&self) -> Wrap {
self.descriptor.wrap_t
}
pub fn wrap_r(&self) -> Wrap {
self.descriptor.wrap_r
}
}
impl<Min, Mag> PartialEq for Sampler<Min, Mag> {
fn eq(&self, other: &Self) -> bool {
self.object_id == other.object_id
}
}
impl<Min, Mag> Hash for Sampler<Min, Mag> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.object_id.hash(state);
}
}
pub unsafe trait CompatibleSampler<F>
where
F: TextureFormat,
{
type Min: MinificationFilter;
type Mag: MagnificationFilter;
fn get_ref(&self) -> &Sampler<Self::Min, Self::Mag>;
}
unsafe impl<F, Min, Mag> CompatibleSampler<F> for Sampler<Min, Mag>
where
Min: CompatibleFilter<F> + MinificationFilter,
Mag: CompatibleFilter<F> + MagnificationFilter,
F: TextureFormat,
{
type Min = Min;
type Mag = Mag;
fn get_ref(&self) -> &Sampler<Self::Min, Self::Mag> {
self
}
}
unsafe impl<T, F> CompatibleSampler<F> for &'_ T
where
T: CompatibleSampler<F>,
F: TextureFormat,
{
type Min = T::Min;
type Mag = T::Mag;
fn get_ref(&self) -> &Sampler<Self::Min, Self::Mag> {
<T as CompatibleSampler<F>>::get_ref(*self)
}
}
unsafe impl<T, F> CompatibleSampler<F> for &'_ mut T
where
T: CompatibleSampler<F>,
F: TextureFormat,
{
type Min = T::Min;
type Mag = T::Mag;
fn get_ref(&self) -> &Sampler<Self::Min, Self::Mag> {
<T as CompatibleSampler<F>>::get_ref(*self)
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum CompareFunction {
Equal = Gl::EQUAL as isize,
NotEqual = Gl::NOTEQUAL as isize,
Less = Gl::LESS as isize,
Greater = Gl::GREATER as isize,
LessOrEqual = Gl::LEQUAL as isize,
GreaterOrEqual = Gl::GEQUAL as isize,
Always = Gl::ALWAYS as isize,
Never = Gl::NEVER as isize,
}
#[derive(PartialEq, Clone, Debug)]
pub struct ShadowSamplerDescriptor {
pub compare: CompareFunction,
pub wrap_s: Wrap,
pub wrap_t: Wrap,
pub wrap_r: Wrap,
}
impl Default for ShadowSamplerDescriptor {
fn default() -> Self {
ShadowSamplerDescriptor {
compare: CompareFunction::LessOrEqual,
wrap_s: Wrap::Repeat,
wrap_t: Wrap::Repeat,
wrap_r: Wrap::Repeat,
}
}
}
pub struct ShadowSampler {
object_id: u64,
data: Arc<SamplerData>,
descriptor: ShadowSamplerDescriptor,
}
impl ShadowSampler {
pub(crate) fn new<Rc>(
context: &Rc,
object_id: u64,
descriptor: &ShadowSamplerDescriptor,
) -> ShadowSampler
where
Rc: RenderingContext + Clone + 'static,
{
let data = Arc::new(SamplerData {
id: UnsafeCell::new(None),
context_id: context.id(),
dropper: Box::new(context.clone()),
});
context.submit(ShadowSamplerAllocateCommand {
data: data.clone(),
descriptor: descriptor.clone(),
});
ShadowSampler {
object_id,
data,
descriptor: descriptor.clone(),
}
}
pub(crate) fn data(&self) -> &Arc<SamplerData> {
&self.data
}
pub fn compare(&self) -> CompareFunction {
self.descriptor.compare
}
pub fn wrap_s(&self) -> Wrap {
self.descriptor.wrap_s
}
pub fn wrap_t(&self) -> Wrap {
self.descriptor.wrap_t
}
pub fn wrap_r(&self) -> Wrap {
self.descriptor.wrap_r
}
}
impl PartialEq for ShadowSampler {
fn eq(&self, other: &Self) -> bool {
self.object_id == other.object_id
}
}
impl Hash for ShadowSampler {
fn hash<H: Hasher>(&self, state: &mut H) {
self.object_id.hash(state);
}
}
trait SamplerObjectDropper {
fn drop_sampler_object(&self, id: JsId);
}
impl<T> SamplerObjectDropper for T
where
T: RenderingContext,
{
fn drop_sampler_object(&self, id: JsId) {
self.submit(SamplerDropCommand { id });
}
}
pub(crate) struct SamplerData {
id: UnsafeCell<Option<JsId>>,
context_id: u64,
dropper: Box<dyn SamplerObjectDropper>,
}
impl SamplerData {
pub(crate) fn id(&self) -> Option<JsId> {
unsafe { *self.id.get() }
}
pub(crate) fn context_id(&self) -> u64 {
self.context_id
}
}
impl Drop for SamplerData {
fn drop(&mut self) {
if let Some(id) = self.id() {
self.dropper.drop_sampler_object(id);
}
}
}
struct SamplerAllocateCommand<Min, Mag> {
data: Arc<SamplerData>,
descriptor: SamplerDescriptor<Min, Mag>,
}
unsafe impl<Min, Mag> GpuTask<Connection> for SamplerAllocateCommand<Min, Mag>
where
Min: MinificationFilter,
Mag: MagnificationFilter,
{
type Output = ();
fn context_id(&self) -> ContextId {
ContextId::Any
}
fn progress(&mut self, connection: &mut Connection) -> Progress<Self::Output> {
let (gl, _) = unsafe { connection.unpack_mut() };
let data = &self.data;
let object = gl.create_sampler().unwrap();
let descriptor = &self.descriptor;
if Min::ID != Gl::NEAREST_MIPMAP_LINEAR {
gl.sampler_parameteri(&object, Gl::TEXTURE_MIN_FILTER, Min::ID as i32);
}
if Mag::ID != Gl::LINEAR {
gl.sampler_parameteri(&object, Gl::TEXTURE_MAG_FILTER, Mag::ID as i32);
}
if descriptor.lod_range.min != -1000.0 {
gl.sampler_parameterf(&object, Gl::TEXTURE_MIN_LOD, descriptor.lod_range.min);
}
if descriptor.lod_range.max != 1000.0 {
gl.sampler_parameterf(&object, Gl::TEXTURE_MAX_LOD, descriptor.lod_range.max);
}
if descriptor.wrap_s != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_S, descriptor.wrap_s as i32);
}
if descriptor.wrap_t != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_T, descriptor.wrap_t as i32);
}
if descriptor.wrap_r != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_R, descriptor.wrap_r as i32);
}
unsafe {
*data.id.get() = Some(JsId::from_value(object.into()));
}
Progress::Finished(())
}
}
struct ShadowSamplerAllocateCommand {
data: Arc<SamplerData>,
descriptor: ShadowSamplerDescriptor,
}
unsafe impl GpuTask<Connection> for ShadowSamplerAllocateCommand {
type Output = ();
fn context_id(&self) -> ContextId {
ContextId::Any
}
fn progress(&mut self, connection: &mut Connection) -> Progress<Self::Output> {
let (gl, _) = unsafe { connection.unpack_mut() };
let data = &self.data;
let object = gl.create_sampler().unwrap();
let descriptor = &self.descriptor;
if descriptor.compare != CompareFunction::LessOrEqual {
gl.sampler_parameteri(&object, Gl::TEXTURE_COMPARE_FUNC, descriptor.compare as i32);
}
if descriptor.wrap_s != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_S, descriptor.wrap_s as i32);
}
if descriptor.wrap_t != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_T, descriptor.wrap_t as i32);
}
if descriptor.wrap_r != Wrap::Repeat {
gl.sampler_parameteri(&object, Gl::TEXTURE_WRAP_R, descriptor.wrap_r as i32);
}
unsafe {
*data.id.get() = Some(JsId::from_value(object.into()));
}
Progress::Finished(())
}
}
struct SamplerDropCommand {
id: JsId,
}
unsafe impl GpuTask<Connection> for SamplerDropCommand {
type Output = ();
fn context_id(&self) -> ContextId {
ContextId::Any
}
fn progress(&mut self, connection: &mut Connection) -> Progress<Self::Output> {
let (gl, state) = unsafe { connection.unpack_mut() };
let value = unsafe { JsId::into_value(self.id).unchecked_into() };
state.unref_sampler(&value);
gl.delete_sampler(Some(&value));
Progress::Finished(())
}
}