use euclid::default::Size2D;
use sparkle::gl;
use sparkle::gl::types::{GLuint};
use std::rc::Rc;
use crate::NativeGLContextMethods;
use crate::GLContextAttributes;
use crate::GLContextCapabilities;
use crate::GLFormats;
use crate::GLLimits;
use crate::DrawBuffer;
use crate::ColorAttachmentType;
pub struct GLContext<Native> {
gl_: Rc<gl::Gl>,
native_context: Native,
draw_buffer: Option<DrawBuffer>,
attributes: GLContextAttributes,
capabilities: GLContextCapabilities,
formats: GLFormats,
limits: GLLimits,
extensions: Vec<String>
}
impl<Native> GLContext<Native>
where Native: NativeGLContextMethods,
{
pub fn create(api_type: gl::GlType,
api_version: GLVersion,
shared_with: Option<&Native::Handle>)
-> Result<Self, &'static str> {
Self::create_shared_with_dispatcher(&api_type, api_version, shared_with, None)
}
pub fn create_shared_with_dispatcher(api_type: &gl::GlType,
api_version: GLVersion,
shared_with: Option<&Native::Handle>,
dispatcher: Option<Box<dyn GLContextDispatcher>>)
-> Result<Self, &'static str> {
let native_context = Native::create_shared_with_dispatcher(shared_with,
api_type,
api_version,
dispatcher)?;
let gl_ = match api_type {
gl::GlType::Gl => gl::Gl::gl_fns(
gl::ffi_gl::Gl::load_with(|s| Self::get_proc_address(s) as *const _)
),
gl::GlType::Gles => gl::Gl::gles_fns(
gl::ffi_gles::Gles2::load_with(|s| Self::get_proc_address(s) as *const _)
),
};
native_context.make_current()?;
let extensions = Self::query_extensions(&*gl_, api_version);
let attributes = GLContextAttributes::any();
let formats = GLFormats::detect(&attributes, &extensions[..], api_type, api_version);
let limits = GLLimits::detect(&*gl_);
Ok(GLContext {
gl_: gl_,
native_context: native_context,
draw_buffer: None,
attributes: attributes,
capabilities: GLContextCapabilities::detect(),
formats: formats,
limits: limits,
extensions: extensions
})
}
#[inline(always)]
pub fn get_proc_address(addr: &str) -> *const () {
Native::get_proc_address(addr)
}
#[inline(always)]
pub fn current_handle() -> Option<Native::Handle> {
Native::current_handle()
}
pub fn new(size: Size2D<i32>,
attributes: GLContextAttributes,
color_attachment_type: ColorAttachmentType,
api_type: gl::GlType,
api_version: GLVersion,
shared_with: Option<&Native::Handle>)
-> Result<Self, &'static str> {
Self::new_shared_with_dispatcher(size,
attributes,
color_attachment_type,
api_type,
api_version,
shared_with,
None)
}
pub fn new_shared_with_dispatcher(size: Size2D<i32>,
attributes: GLContextAttributes,
color_attachment_type: ColorAttachmentType,
api_type: gl::GlType,
api_version: GLVersion,
shared_with: Option<&Native::Handle>,
dispatcher: Option<Box<dyn GLContextDispatcher>>)
-> Result<Self, &'static str> {
let mut context =
Self::create_shared_with_dispatcher(&api_type,
api_version,
shared_with,
dispatcher)?;
context.formats = GLFormats::detect(&attributes, &context.extensions[..], &api_type, api_version);
context.attributes = attributes;
context.init_offscreen(size, color_attachment_type)?;
Ok(context)
}
#[inline(always)]
pub fn with_default_color_attachment(size: Size2D<i32>,
attributes: GLContextAttributes,
api_type: gl::GlType,
api_version: GLVersion,
shared_with: Option<&Native::Handle>)
-> Result<Self, &'static str> {
Self::new(size, attributes, ColorAttachmentType::default(), api_type, api_version, shared_with)
}
#[inline(always)]
pub fn make_current(&self) -> Result<(), &'static str> {
self.native_context.make_current()
}
#[inline(always)]
pub fn unbind(&self) -> Result<(), &'static str> {
let ret = self.native_context.unbind();
if self.native_context.is_osmesa() && ret.is_err() {
self.gl().flush();
return Ok(())
}
ret
}
#[inline(always)]
pub fn is_current(&self) -> bool {
self.native_context.is_current()
}
#[inline(always)]
pub fn handle(&self) -> Native::Handle {
self.native_context.handle()
}
pub fn gl(&self) -> &gl::Gl {
&*self.gl_
}
pub fn clone_gl(&self) -> Rc<gl::Gl> {
self.gl_.clone()
}
pub fn borrow_attributes(&self) -> &GLContextAttributes {
&self.attributes
}
pub fn borrow_capabilities(&self) -> &GLContextCapabilities {
&self.capabilities
}
pub fn borrow_formats(&self) -> &GLFormats {
&self.formats
}
pub fn borrow_limits(&self) -> &GLLimits {
&self.limits
}
pub fn borrow_draw_buffer(&self) -> Option<&DrawBuffer> {
self.draw_buffer.as_ref()
}
pub fn get_framebuffer(&self) -> GLuint {
if let Some(ref db) = self.draw_buffer {
return db.get_framebuffer();
}
let mut fb = [0];
unsafe {
self.gl().get_integer_v(gl::FRAMEBUFFER_BINDING, &mut fb);
}
fb[0] as GLuint
}
pub fn draw_buffer_size(&self) -> Option<Size2D<i32>> {
self.draw_buffer.as_ref().map(|db| db.size())
}
pub fn resize(&mut self, size: Size2D<i32>) -> Result<DrawBuffer, &'static str> {
if let Some(draw_buffer) = self.draw_buffer.take() {
let color_attachment_type = draw_buffer.color_attachment_type();
self.init_offscreen(size, color_attachment_type).map(|()| draw_buffer)
} else {
Err("No DrawBuffer found")
}
}
pub fn get_extensions(&self) -> Vec<String> {
self.extensions.clone()
}
fn init_offscreen(&mut self, size: Size2D<i32>, color_attachment_type: ColorAttachmentType) -> Result<(), &'static str> {
self.create_draw_buffer(size, color_attachment_type)?;
debug_assert!(self.is_current());
self.gl().clear_color(0.0, 0.0, 0.0, 0.0);
self.gl().clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
self.gl().scissor(0, 0, size.width, size.height);
self.gl().viewport(0, 0, size.width, size.height);
Ok(())
}
fn create_draw_buffer(&mut self, size: Size2D<i32>, color_attachment_type: ColorAttachmentType) -> Result<(), &'static str> {
self.draw_buffer = Some(DrawBuffer::new(self, size, color_attachment_type)?);
Ok(())
}
fn query_extensions(gl_: &gl::Gl, api_version: GLVersion) -> Vec<String> {
if api_version.major_version() >=3 {
let mut n = [0];
unsafe {
gl_.get_integer_v(gl::NUM_EXTENSIONS, &mut n);
}
let n = n[0] as usize;
let mut extensions = Vec::with_capacity(n);
for index in 0..n {
extensions.push(gl_.get_string_i(gl::EXTENSIONS, index as u32))
}
extensions
} else {
let extensions = gl_.get_string(gl::EXTENSIONS);
extensions.split(&[',',' '][..]).map(|s| s.into()).collect()
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum GLVersion {
Major(u8),
MajorMinor(u8, u8),
}
impl GLVersion {
pub fn major_version(&self) -> u8 {
match *self {
GLVersion::Major(major) => major,
GLVersion::MajorMinor(major, _) => major,
}
}
}
pub trait GLContextDispatcher {
fn dispatch(&self, f: Box<dyn Fn() + Send>);
}