use super::*;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
#[doc(hidden)]
pub use crate::context::HasContext;
#[derive(Clone)]
pub struct Context {
context: Arc<crate::context::Context>,
pub(super) vao: crate::context::VertexArray,
programs_old: Arc<RwLock<HashMap<(String, String), Program>>>,
pub programs: Arc<RwLock<HashMap<Vec<u8>, Program>>>,
}
impl Context {
pub fn from_gl_context(context: Arc<crate::context::Context>) -> Result<Self, CoreError> {
unsafe {
if !context.version().is_embedded {
context.enable(crate::context::TEXTURE_CUBE_MAP_SEAMLESS);
}
context.pixel_store_i32(crate::context::UNPACK_ALIGNMENT, 1);
context.pixel_store_i32(crate::context::PACK_ALIGNMENT, 1);
};
let c = unsafe {
let vao = context
.create_vertex_array()
.map_err(CoreError::ContextCreation)?;
Self {
context,
vao,
programs_old: Arc::new(RwLock::new(HashMap::new())),
programs: Arc::new(RwLock::new(HashMap::new())),
}
};
Ok(c)
}
#[deprecated = "Use Context::programs"]
pub fn program(
&self,
vertex_shader_source: String,
fragment_shader_source: String,
callback: impl FnOnce(&Program),
) -> Result<(), CoreError> {
let key = (vertex_shader_source, fragment_shader_source);
let mut programs = self.programs_old.write().unwrap();
if let Some(program) = programs.get(&key) {
callback(program);
} else {
let program = Program::from_source(self, &key.0, &key.1)?;
callback(&program);
programs.insert(key, program);
}
Ok(())
}
pub fn set_scissor(&self, scissor_box: ScissorBox) {
unsafe {
if scissor_box.width > 0 && scissor_box.height > 0 {
self.enable(crate::context::SCISSOR_TEST);
self.scissor(
scissor_box.x,
scissor_box.y,
scissor_box.width as i32,
scissor_box.height as i32,
);
} else {
self.disable(crate::context::SCISSOR_TEST);
}
}
}
pub fn set_viewport(&self, viewport: Viewport) {
unsafe {
self.viewport(
viewport.x,
viewport.y,
viewport.width as i32,
viewport.height as i32,
);
}
}
pub fn set_cull(&self, cull: Cull) {
unsafe {
match cull {
Cull::None => {
self.disable(crate::context::CULL_FACE);
}
Cull::Back => {
self.enable(crate::context::CULL_FACE);
self.cull_face(crate::context::BACK);
}
Cull::Front => {
self.enable(crate::context::CULL_FACE);
self.cull_face(crate::context::FRONT);
}
Cull::FrontAndBack => {
self.enable(crate::context::CULL_FACE);
self.cull_face(crate::context::FRONT_AND_BACK);
}
}
}
}
pub fn set_write_mask(&self, write_mask: WriteMask) {
unsafe {
self.color_mask(
write_mask.red,
write_mask.green,
write_mask.blue,
write_mask.alpha,
);
self.depth_mask(write_mask.depth);
}
}
pub fn set_depth_test(&self, depth_test: DepthTest) {
unsafe {
self.enable(crate::context::DEPTH_TEST);
match depth_test {
DepthTest::Never => {
self.depth_func(crate::context::NEVER);
}
DepthTest::Less => {
self.depth_func(crate::context::LESS);
}
DepthTest::Equal => {
self.depth_func(crate::context::EQUAL);
}
DepthTest::LessOrEqual => {
self.depth_func(crate::context::LEQUAL);
}
DepthTest::Greater => {
self.depth_func(crate::context::GREATER);
}
DepthTest::NotEqual => {
self.depth_func(crate::context::NOTEQUAL);
}
DepthTest::GreaterOrEqual => {
self.depth_func(crate::context::GEQUAL);
}
DepthTest::Always => {
self.depth_func(crate::context::ALWAYS);
}
}
}
}
pub fn set_blend(&self, blend: Blend) {
unsafe {
if let Blend::Enabled {
source_rgb_multiplier,
source_alpha_multiplier,
destination_rgb_multiplier,
destination_alpha_multiplier,
rgb_equation,
alpha_equation,
} = blend
{
self.enable(crate::context::BLEND);
self.blend_func_separate(
Self::blend_const_from_multiplier(source_rgb_multiplier),
Self::blend_const_from_multiplier(destination_rgb_multiplier),
Self::blend_const_from_multiplier(source_alpha_multiplier),
Self::blend_const_from_multiplier(destination_alpha_multiplier),
);
self.blend_equation_separate(
Self::blend_const_from_equation(rgb_equation),
Self::blend_const_from_equation(alpha_equation),
);
} else {
self.disable(crate::context::BLEND);
}
}
}
fn blend_const_from_multiplier(multiplier: BlendMultiplierType) -> u32 {
match multiplier {
BlendMultiplierType::Zero => crate::context::ZERO,
BlendMultiplierType::One => crate::context::ONE,
BlendMultiplierType::SrcColor => crate::context::SRC_COLOR,
BlendMultiplierType::OneMinusSrcColor => crate::context::ONE_MINUS_SRC_COLOR,
BlendMultiplierType::DstColor => crate::context::DST_COLOR,
BlendMultiplierType::OneMinusDstColor => crate::context::ONE_MINUS_DST_COLOR,
BlendMultiplierType::SrcAlpha => crate::context::SRC_ALPHA,
BlendMultiplierType::OneMinusSrcAlpha => crate::context::ONE_MINUS_SRC_ALPHA,
BlendMultiplierType::DstAlpha => crate::context::DST_ALPHA,
BlendMultiplierType::OneMinusDstAlpha => crate::context::ONE_MINUS_DST_ALPHA,
BlendMultiplierType::SrcAlphaSaturate => crate::context::SRC_ALPHA_SATURATE,
}
}
fn blend_const_from_equation(equation: BlendEquationType) -> u32 {
match equation {
BlendEquationType::Add => crate::context::FUNC_ADD,
BlendEquationType::Subtract => crate::context::FUNC_SUBTRACT,
BlendEquationType::ReverseSubtract => crate::context::FUNC_REVERSE_SUBTRACT,
BlendEquationType::Min => crate::context::MIN,
BlendEquationType::Max => crate::context::MAX,
}
}
pub fn set_render_states(&self, render_states: RenderStates) {
self.set_cull(render_states.cull);
self.set_write_mask(render_states.write_mask);
if !render_states.write_mask.depth && render_states.depth_test == DepthTest::Always {
unsafe { self.disable(crate::context::DEPTH_TEST) }
} else {
self.set_depth_test(render_states.depth_test);
}
self.set_blend(render_states.blend);
}
pub fn error_check(&self) -> Result<(), CoreError> {
self.framebuffer_check()?;
unsafe {
let e = self.get_error();
if e != crate::context::NO_ERROR {
Err(CoreError::ContextError(
match e {
crate::context::INVALID_ENUM => "Invalid enum",
crate::context::INVALID_VALUE => "Invalid value",
crate::context::INVALID_OPERATION => "Invalid operation",
crate::context::INVALID_FRAMEBUFFER_OPERATION => {
"Invalid framebuffer operation"
}
crate::context::OUT_OF_MEMORY => "Out of memory",
crate::context::STACK_OVERFLOW => "Stack overflow",
crate::context::STACK_UNDERFLOW => "Stack underflow",
_ => "Unknown",
}
.to_string(),
))?;
}
}
Ok(())
}
fn framebuffer_check(&self) -> Result<(), CoreError> {
unsafe {
match self.check_framebuffer_status(crate::context::FRAMEBUFFER) {
crate::context::FRAMEBUFFER_COMPLETE => Ok(()),
crate::context::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => Err(CoreError::ContextError(
"FRAMEBUFFER_INCOMPLETE_ATTACHMENT".to_string(),
)),
crate::context::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => Err(CoreError::ContextError(
"FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER".to_string(),
)),
crate::context::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
Err(CoreError::ContextError(
"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT".to_string(),
))
}
crate::context::FRAMEBUFFER_UNSUPPORTED => Err(CoreError::ContextError(
"FRAMEBUFFER_UNSUPPORTED".to_string(),
)),
crate::context::FRAMEBUFFER_UNDEFINED => {
Err(CoreError::ContextError("FRAMEBUFFER_UNDEFINED".to_string()))
}
crate::context::FRAMEBUFFER_INCOMPLETE_READ_BUFFER => Err(CoreError::ContextError(
"FRAMEBUFFER_INCOMPLETE_READ_BUFFER".to_string(),
)),
crate::context::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => Err(CoreError::ContextError(
"FRAMEBUFFER_INCOMPLETE_MULTISAMPLE".to_string(),
)),
crate::context::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => Err(
CoreError::ContextError("FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS".to_string()),
),
_ => Err(CoreError::ContextError(
"Unknown framebuffer error".to_string(),
)),
}?;
}
Ok(())
}
}
impl std::fmt::Debug for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("Context");
d.field("programs", &self.programs.read().unwrap().len());
d.finish()
}
}
impl std::ops::Deref for Context {
type Target = Arc<crate::context::Context>;
fn deref(&self) -> &Self::Target {
&self.context
}
}