use std::{ mem, fmt };
use std::error::Error;
use crate::version::Api;
use crate::version::Version;
use crate::context::CommandContext;
use crate::backend::Facade;
use crate::BufferExt;
use crate::GlObject;
use crate::ContextExt;
use crate::CapabilitiesSource;
use crate::TransformFeedbackSessionExt;
use crate::buffer::{Buffer, BufferAnySlice};
use crate::index::PrimitiveType;
use crate::program::OutputPrimitives;
use crate::program::Program;
use crate::vertex::Vertex;
use crate::gl;
#[derive(Debug)]
pub struct TransformFeedbackSession<'a> {
buffer: BufferAnySlice<'a>,
program: &'a Program,
}
#[derive(Debug, Clone)]
pub enum TransformFeedbackSessionCreationError {
NotSupported,
WrongVertexFormat,
}
impl fmt::Display for TransformFeedbackSessionCreationError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::TransformFeedbackSessionCreationError::*;
let desc = match *self {
NotSupported =>
"Transform feedback is not supported by the OpenGL implementation",
WrongVertexFormat =>
"The format of the output doesn't match what the program is expected to output",
};
fmt.write_str(desc)
}
}
impl Error for TransformFeedbackSessionCreationError {}
#[inline]
pub fn is_transform_feedback_supported<F: ?Sized>(facade: &F) -> bool where F: Facade {
let context = facade.get_context();
context.get_version() >= &Version(Api::Gl, 3, 0) ||
context.get_version() >= &Version(Api::GlEs, 3, 0) ||
context.get_extensions().gl_ext_transform_feedback
}
impl<'a> TransformFeedbackSession<'a> {
pub fn new<F: ?Sized, V>(facade: &F, program: &'a Program, buffer: &'a mut Buffer<[V]>)
-> Result<TransformFeedbackSession<'a>, TransformFeedbackSessionCreationError>
where F: Facade, V: Vertex + Copy + Send + 'static
{
if !is_transform_feedback_supported(facade) {
return Err(TransformFeedbackSessionCreationError::NotSupported);
}
if !program.transform_feedback_matches(&<V as Vertex>::build_bindings(),
mem::size_of::<V>())
{
return Err(TransformFeedbackSessionCreationError::WrongVertexFormat);
}
Ok(TransformFeedbackSession {
buffer: buffer.as_slice_any(),
program,
})
}
}
impl<'a> TransformFeedbackSessionExt for TransformFeedbackSession<'a> {
fn bind(&self, ctxt: &mut CommandContext<'_>, draw_primitives: PrimitiveType) {
if ctxt.state.transform_feedback_enabled.is_some() {
unimplemented!();
}
self.buffer.bind_to_transform_feedback(ctxt, 0);
unsafe {
let primitives = match (self.program.get_output_primitives(), draw_primitives) {
(Some(OutputPrimitives::Points), _) => gl::POINTS,
(Some(OutputPrimitives::Lines), _) => gl::LINES,
(Some(OutputPrimitives::Triangles), _) => gl::TRIANGLES,
(Some(OutputPrimitives::Quads), _) => panic!(), (None, PrimitiveType::Points) => gl::POINTS,
(None, PrimitiveType::LinesList) => gl::LINES,
(None, PrimitiveType::LinesListAdjacency) => gl::LINES,
(None, PrimitiveType::LineStrip) => gl::LINES,
(None, PrimitiveType::LineStripAdjacency) => gl::LINES,
(None, PrimitiveType::LineLoop) => gl::LINES,
(None, PrimitiveType::TrianglesList) => gl::TRIANGLES,
(None, PrimitiveType::TrianglesListAdjacency) => gl::TRIANGLES,
(None, PrimitiveType::TriangleStrip) => gl::TRIANGLES,
(None, PrimitiveType::TriangleStripAdjacency) => gl::TRIANGLES,
(None, PrimitiveType::TriangleFan) => gl::TRIANGLES,
(None, PrimitiveType::Patches { .. }) => unreachable!(),
};
ctxt.gl.BeginTransformFeedback(primitives);
ctxt.state.transform_feedback_enabled = Some(primitives);
ctxt.state.transform_feedback_paused = false;
}
}
#[inline]
fn unbind(ctxt: &mut CommandContext<'_>) {
if ctxt.state.transform_feedback_enabled.is_none() {
return;
}
unsafe {
ctxt.gl.EndTransformFeedback();
ctxt.state.transform_feedback_enabled = None;
ctxt.state.transform_feedback_paused = false;
}
}
fn ensure_buffer_out_of_transform_feedback(ctxt: &mut CommandContext<'_>, buffer: gl::types::GLuint) {
if ctxt.state.transform_feedback_enabled.is_none() {
return;
}
let mut needs_unbind = false;
for elem in ctxt.state.indexed_transform_feedback_buffer_bindings.iter_mut() {
if elem.buffer == buffer {
needs_unbind = true;
break;
}
}
if needs_unbind {
TransformFeedbackSession::unbind(ctxt);
}
}
}
impl<'a> Drop for TransformFeedbackSession<'a> {
#[inline]
fn drop(&mut self) {
let mut ctxt = self.buffer.get_context().make_current();
Self::ensure_buffer_out_of_transform_feedback(&mut ctxt, self.buffer.get_id());
}
}