use std::borrow::Borrow;
use std::ops::Deref;
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use web_sys::{WebGl2RenderingContext as Gl, WebGlProgram};
use crate::runtime::CreateGraphicsPipelineError;
pub unsafe trait TransformFeedback {
const ATTRIBUTE_DESCRIPTORS: &'static [TransformFeedbackAttributeDescriptor];
}
pub unsafe trait TypedTransformFeedbackLayout {
type LayoutDescription: Into<TransformFeedbackLayoutDescriptor>;
const LAYOUT_DESCRIPTION: Self::LayoutDescription;
}
macro_rules! impl_typed_transform_feedback_layout {
($n:tt, $($T:ident),*) => {
#[allow(unused_parens)]
unsafe impl<$($T),*> TypedTransformFeedbackLayout for ($($T),*)
where
$($T: TransformFeedback),*
{
type LayoutDescription = [&'static [TransformFeedbackAttributeDescriptor]; $n];
const LAYOUT_DESCRIPTION: Self::LayoutDescription = [
$($T::ATTRIBUTE_DESCRIPTORS),*
];
}
}
}
impl_typed_transform_feedback_layout!(1, T0);
impl_typed_transform_feedback_layout!(2, T0, T1);
impl_typed_transform_feedback_layout!(3, T0, T1, T2);
impl_typed_transform_feedback_layout!(4, T0, T1, T2, T3);
impl_typed_transform_feedback_layout!(5, T0, T1, T2, T3, T4);
impl_typed_transform_feedback_layout!(6, T0, T1, T2, T3, T4, T5);
impl_typed_transform_feedback_layout!(7, T0, T1, T2, T3, T4, T5, T6);
impl_typed_transform_feedback_layout!(8, T0, T1, T2, T3, T4, T5, T6, T7);
impl_typed_transform_feedback_layout!(9, T0, T1, T2, T3, T4, T5, T6, T7, T8);
impl_typed_transform_feedback_layout!(10, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_typed_transform_feedback_layout!(11, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_typed_transform_feedback_layout!(12, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_typed_transform_feedback_layout!(13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_typed_transform_feedback_layout!(
14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13
);
impl_typed_transform_feedback_layout!(
15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
);
impl_typed_transform_feedback_layout!(
16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
);
impl Into<TransformFeedbackLayoutDescriptor> for () {
fn into(self) -> TransformFeedbackLayoutDescriptor {
TransformFeedbackLayoutDescriptor {
layout: Vec::new(),
}
}
}
macro_rules! impl_into_transform_feedback_layout_descriptor {
($n:tt) => {
impl Into<TransformFeedbackLayoutDescriptor>
for [&'static [TransformFeedbackAttributeDescriptor]; $n]
{
fn into(self) -> TransformFeedbackLayoutDescriptor {
let mut attribute_count = 0;
for i in 0..$n {
attribute_count += self[i].len();
}
let mut builder = TransformFeedbackLayoutDescriptorBuilder::new(Some(
TransformFeedbackLayoutAllocationHint {
bind_slot_count: $n,
attribute_count: attribute_count as u8,
},
));
for i in 0..$n {
let mut slot = builder.add_buffer_slot();
for attribute in self[i] {
slot.add_attribute(attribute.clone());
}
}
builder.finish()
}
}
};
}
impl_into_transform_feedback_layout_descriptor!(1);
impl_into_transform_feedback_layout_descriptor!(2);
impl_into_transform_feedback_layout_descriptor!(3);
impl_into_transform_feedback_layout_descriptor!(4);
impl_into_transform_feedback_layout_descriptor!(5);
impl_into_transform_feedback_layout_descriptor!(6);
impl_into_transform_feedback_layout_descriptor!(7);
impl_into_transform_feedback_layout_descriptor!(8);
impl_into_transform_feedback_layout_descriptor!(9);
impl_into_transform_feedback_layout_descriptor!(10);
impl_into_transform_feedback_layout_descriptor!(11);
impl_into_transform_feedback_layout_descriptor!(12);
impl_into_transform_feedback_layout_descriptor!(13);
impl_into_transform_feedback_layout_descriptor!(14);
impl_into_transform_feedback_layout_descriptor!(15);
impl_into_transform_feedback_layout_descriptor!(16);
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct TransformFeedbackAttributeDescriptor {
pub ident: TransformFeedbackAttributeIdentifier,
pub attribute_type: TransformFeedbackAttributeType,
pub size: usize,
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub enum TransformFeedbackAttributeIdentifier {
Dynamic(String),
Static(&'static str),
}
impl From<String> for TransformFeedbackAttributeIdentifier {
fn from(ident: String) -> Self {
TransformFeedbackAttributeIdentifier::Dynamic(ident)
}
}
impl From<&'static str> for TransformFeedbackAttributeIdentifier {
fn from(ident: &'static str) -> Self {
TransformFeedbackAttributeIdentifier::Static(ident)
}
}
impl Deref for TransformFeedbackAttributeIdentifier {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
TransformFeedbackAttributeIdentifier::Dynamic(ident) => ident,
TransformFeedbackAttributeIdentifier::Static(ident) => ident,
}
}
}
impl Serialize for TransformFeedbackAttributeIdentifier {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.borrow())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum TransformFeedbackAttributeType {
Float,
FloatVector2,
FloatVector3,
FloatVector4,
FloatMatrix2x2,
FloatMatrix2x3,
FloatMatrix2x4,
FloatMatrix3x2,
FloatMatrix3x3,
FloatMatrix3x4,
FloatMatrix4x2,
FloatMatrix4x3,
FloatMatrix4x4,
Integer,
IntegerVector2,
IntegerVector3,
IntegerVector4,
UnsignedInteger,
UnsignedIntegerVector2,
UnsignedIntegerVector3,
UnsignedIntegerVector4,
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct TransformFeedbackLayoutDescriptor {
layout: Vec<LayoutElement>,
}
impl TransformFeedbackLayoutDescriptor {
pub(crate) fn check_compatibility(
&self,
program: &WebGlProgram,
gl: &Gl,
) -> Result<(), CreateGraphicsPipelineError> {
let mut index = 0;
for group in self.buffer_slots() {
for attribute in group.attributes() {
let info = gl.get_transform_feedback_varying(program, index).unwrap();
if info.size() != attribute.size as i32 {
return Err(CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
));
}
match attribute.attribute_type {
TransformFeedbackAttributeType::Float => {
if info.type_() != Gl::FLOAT {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatVector2 => {
if info.type_() != Gl::FLOAT_VEC2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatVector3 => {
if info.type_() != Gl::FLOAT_VEC3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatVector4 => {
if info.type_() != Gl::FLOAT_VEC4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix2x2 => {
if info.type_() != Gl::FLOAT_MAT2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix2x3 => {
if info.type_() != Gl::FLOAT_MAT2X3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix2x4 => {
if info.type_() != Gl::FLOAT_MAT2X4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix3x2 => {
if info.type_() != Gl::FLOAT_MAT3X2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix3x3 => {
if info.type_() != Gl::FLOAT_MAT3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix3x4 => {
if info.type_() != Gl::FLOAT_MAT3X4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix4x2 => {
if info.type_() != Gl::FLOAT_MAT4X2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix4x3 => {
if info.type_() != Gl::FLOAT_MAT4X3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::FloatMatrix4x4 => {
if info.type_() != Gl::FLOAT_MAT4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::Integer => {
if info.type_() != Gl::INT {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::IntegerVector2 => {
if info.type_() != Gl::INT_VEC2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::IntegerVector3 => {
if info.type_() != Gl::INT_VEC3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::IntegerVector4 => {
if info.type_() != Gl::INT_VEC4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::UnsignedInteger => {
if info.type_() != Gl::UNSIGNED_INT {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::UnsignedIntegerVector2 => {
if info.type_() != Gl::UNSIGNED_INT_VEC2 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::UnsignedIntegerVector3 => {
if info.type_() != Gl::UNSIGNED_INT_VEC3 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
TransformFeedbackAttributeType::UnsignedIntegerVector4 => {
if info.type_() != Gl::UNSIGNED_INT_VEC4 {
return Err(
CreateGraphicsPipelineError::TransformFeedbackTypeMismatch(
attribute.ident.to_string(),
),
);
}
}
}
index += 1;
}
}
Ok(())
}
pub fn buffer_slots(&self) -> TransformFeedbackBufferSlots {
TransformFeedbackBufferSlots {
layout: self,
cursor: -1,
}
}
}
pub struct TransformFeedbackBufferSlots<'a> {
layout: &'a TransformFeedbackLayoutDescriptor,
cursor: isize,
}
impl<'a> Iterator for TransformFeedbackBufferSlots<'a> {
type Item = TransformFeedbackBufferSlotRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor < 0 {
self.cursor += 1;
Some(TransformFeedbackBufferSlotRef {
layout: self.layout,
start: 0,
})
} else {
while let Some(element) = self.layout.layout.get(self.cursor as usize) {
self.cursor += 1;
if let LayoutElement::NextBindSlot = element {
return Some(TransformFeedbackBufferSlotRef {
layout: self.layout,
start: self.cursor as usize,
});
}
}
None
}
}
}
pub struct TransformFeedbackBufferSlotRef<'a> {
layout: &'a TransformFeedbackLayoutDescriptor,
start: usize,
}
impl<'a> TransformFeedbackBufferSlotRef<'a> {
pub fn attributes(&self) -> TransformFeedbackBufferSlotAttributes {
TransformFeedbackBufferSlotAttributes {
layout: &self.layout.layout,
cursor: self.start,
}
}
}
pub struct TransformFeedbackBufferSlotAttributes<'a> {
layout: &'a Vec<LayoutElement>,
cursor: usize,
}
impl<'a> Iterator for TransformFeedbackBufferSlotAttributes<'a> {
type Item = &'a TransformFeedbackAttributeDescriptor;
fn next(&mut self) -> Option<Self::Item> {
if let Some(LayoutElement::NextAttribute(attribute)) = self.layout.get(self.cursor) {
self.cursor += 1;
Some(attribute)
} else {
None
}
}
}
#[derive(Clone, PartialEq, Hash, Eq, Debug)]
enum LayoutElement {
NextAttribute(TransformFeedbackAttributeDescriptor),
NextBindSlot,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct TransformFeedbackLayoutAllocationHint {
pub bind_slot_count: u8,
pub attribute_count: u8,
}
pub struct TransformFeedbackLayoutDescriptorBuilder {
layout: Vec<LayoutElement>,
}
impl TransformFeedbackLayoutDescriptorBuilder {
pub fn new(allocation_hint: Option<TransformFeedbackLayoutAllocationHint>) -> Self {
let layout = if let Some(hint) = allocation_hint {
Vec::with_capacity((hint.bind_slot_count - 1 + hint.attribute_count) as usize)
} else {
Vec::new()
};
TransformFeedbackLayoutDescriptorBuilder { layout }
}
pub fn add_buffer_slot(&mut self) -> TransformFeedbackBufferSlotAttributeAttacher {
if self.layout.len() > 0 {
self.layout.push(LayoutElement::NextBindSlot)
}
TransformFeedbackBufferSlotAttributeAttacher {
layout_builder: self,
}
}
pub fn finish(self) -> TransformFeedbackLayoutDescriptor {
TransformFeedbackLayoutDescriptor {
layout: self.layout,
}
}
}
pub struct TransformFeedbackBufferSlotAttributeAttacher<'a> {
layout_builder: &'a mut TransformFeedbackLayoutDescriptorBuilder,
}
impl<'a> TransformFeedbackBufferSlotAttributeAttacher<'a> {
pub fn add_attribute(
&mut self,
attribute_descriptor: TransformFeedbackAttributeDescriptor,
) -> &mut TransformFeedbackBufferSlotAttributeAttacher<'a> {
self.layout_builder
.layout
.push(LayoutElement::NextAttribute(attribute_descriptor));
self
}
}
pub unsafe trait TransformFeedbackAttribute {
const TYPE: TransformFeedbackAttributeType;
const SIZE: usize;
}
unsafe impl TransformFeedbackAttribute for f32 {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::Float;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [f32; 2] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatVector2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [f32; 3] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatVector3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [f32; 4] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatVector4;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 2]; 2] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix2x2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 3]; 2] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix2x3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 4]; 2] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix2x4;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 2]; 3] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix3x2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 3]; 3] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix3x3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 4]; 3] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix3x4;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 2]; 4] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix4x2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 3]; 4] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix4x3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [[f32; 4]; 4] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::FloatMatrix4x4;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for i32 {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::Integer;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [i32; 2] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::IntegerVector2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [i32; 3] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::IntegerVector3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [i32; 4] {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::IntegerVector4;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for u32 {
const TYPE: TransformFeedbackAttributeType = TransformFeedbackAttributeType::UnsignedInteger;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [u32; 2] {
const TYPE: TransformFeedbackAttributeType =
TransformFeedbackAttributeType::UnsignedIntegerVector2;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [u32; 3] {
const TYPE: TransformFeedbackAttributeType =
TransformFeedbackAttributeType::UnsignedIntegerVector3;
const SIZE: usize = 1;
}
unsafe impl TransformFeedbackAttribute for [u32; 4] {
const TYPE: TransformFeedbackAttributeType =
TransformFeedbackAttributeType::UnsignedIntegerVector4;
const SIZE: usize = 1;
}
pub(crate) struct TransformFeedbackVaryings<'a>(pub &'a TransformFeedbackLayoutDescriptor);
impl<'a> Serialize for TransformFeedbackVaryings<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let TransformFeedbackVaryings(layout) = self;
let layout = &layout.layout;
let mut seq = serializer.serialize_seq(Some(layout.len()))?;
for element in layout {
match element {
LayoutElement::NextAttribute(descriptor) => {
seq.serialize_element(&descriptor.ident)?;
}
LayoutElement::NextBindSlot => {
seq.serialize_element("gl_NextBuffer")?;
}
}
}
seq.end()
}
}