use crate::__gl;
use crate::__gl::types::{GLint, GLuint};
use crate::debug::{Object, ObjectType};
use crate::device::Device;
use crate::error::Result;
use crate::Compare;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Shader(GLuint);
impl Object for Shader {
const TYPE: ObjectType = ObjectType::Shader;
fn handle(&self) -> GLuint {
self.0
}
}
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct Pipeline(pub(crate) GLuint);
impl Object for Pipeline {
const TYPE: ObjectType = ObjectType::Pipeline;
fn handle(&self) -> GLuint {
self.0
}
}
#[derive(Debug, Clone, Copy)]
pub enum ShaderStage {
Vertex,
TessellationControl,
TessellationEvaluation,
Geometry,
Fragment,
Compute,
MeshNv,
TaskNv,
}
#[derive(Copy, Clone)]
pub struct GraphicsPipelineDesc {
pub vertex_shader: Option<Shader>,
pub tessellation_control_shader: Option<Shader>,
pub tessellation_evaluation_shader: Option<Shader>,
pub geometry_shader: Option<Shader>,
pub fragment_shader: Option<Shader>,
pub mesh_shader: Option<Shader>,
pub task_shader: Option<Shader>,
}
#[derive(Copy, Clone)]
pub struct VertexPipelineDesc {
pub vertex_shader: Shader,
pub tessellation_control_shader: Option<Shader>,
pub tessellation_evaluation_shader: Option<Shader>,
pub geometry_shader: Option<Shader>,
pub fragment_shader: Option<Shader>,
}
impl From<VertexPipelineDesc> for GraphicsPipelineDesc {
fn from(desc: VertexPipelineDesc) -> Self {
GraphicsPipelineDesc {
vertex_shader: Some(desc.vertex_shader),
tessellation_control_shader: desc.tessellation_control_shader,
tessellation_evaluation_shader: desc.tessellation_evaluation_shader,
geometry_shader: desc.geometry_shader,
fragment_shader: desc.fragment_shader,
mesh_shader: None,
task_shader: None,
}
}
}
#[derive(Copy, Clone)]
pub struct MeshPipelineDesc {
pub mesh_shader: Shader,
pub task_shader: Option<Shader>,
pub fragment_shader: Option<Shader>,
}
impl From<MeshPipelineDesc> for GraphicsPipelineDesc {
fn from(desc: MeshPipelineDesc) -> Self {
GraphicsPipelineDesc {
vertex_shader: None,
tessellation_control_shader: None,
tessellation_evaluation_shader: None,
geometry_shader: None,
fragment_shader: desc.fragment_shader,
mesh_shader: Some(desc.mesh_shader),
task_shader: desc.task_shader,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct InputAssembly {
pub primitive_restart: Option<u32>,
}
#[derive(Debug, Copy, Clone)]
pub struct Rasterization {
pub depth_clamp: bool,
pub rasterizer_discard: bool,
pub polygon_mode: PolygonMode,
pub cull_mode: Option<CullMode>,
pub front_face: FrontFace,
pub depth_bias: bool,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PolygonMode {
Point = __gl::POINT,
Line = __gl::LINE,
Fill = __gl::FILL,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CullMode {
Front = __gl::FRONT,
Back = __gl::BACK,
FrontBack = __gl::FRONT_AND_BACK,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FrontFace {
CounterClockwise = __gl::CCW,
Clockwise = __gl::CW,
}
#[derive(Debug, Clone)]
pub struct ColorBlend {
pub attachments: Vec<ColorBlendAttachment>,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BlendFactor {
Zero = __gl::ZERO,
One = __gl::ONE,
SrcColor = __gl::SRC_COLOR,
OneMinusSrcColor = __gl::ONE_MINUS_SRC_COLOR,
DstColor = __gl::DST_COLOR,
OneMinusDstColor = __gl::ONE_MINUS_DST_COLOR,
SrcAlpha = __gl::SRC_ALPHA,
OneMinusSrcAlpha = __gl::ONE_MINUS_SRC_ALPHA,
DstAlpha = __gl::DST_ALPHA,
OneMinusDstAlpha = __gl::ONE_MINUS_DST_ALPHA,
ConstantColor = __gl::CONSTANT_COLOR,
OneMinusConstantColor = __gl::ONE_MINUS_CONSTANT_COLOR,
ConstantAlpha = __gl::CONSTANT_ALPHA,
OneMinusConstantAlpha = __gl::ONE_MINUS_CONSTANT_ALPHA,
SrcAlphaSaturate = __gl::SRC_ALPHA_SATURATE,
Src1Color = __gl::SRC1_COLOR,
OneMinusSrc1Color = __gl::ONE_MINUS_SRC1_COLOR,
Src1Alpha = __gl::SRC1_ALPHA,
OneMinusSrc1Alpha = __gl::ONE_MINUS_SRC1_ALPHA,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BlendOp {
Add = __gl::FUNC_ADD,
Substract = __gl::FUNC_SUBTRACT,
ReverseSubstract = __gl::FUNC_REVERSE_SUBTRACT,
Min = __gl::MIN,
Max = __gl::MAX,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BlendChannel {
pub src_factor: BlendFactor,
pub dst_factor: BlendFactor,
pub blend_op: BlendOp,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ColorBlendAttachment {
pub blend_enable: bool,
pub color: BlendChannel,
pub alpha: BlendChannel,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StencilOp {
Keep = __gl::KEEP,
Zero = __gl::ZERO,
Replace = __gl::REPLACE,
IncrementClamp = __gl::INCR,
DecrementClamp = __gl::DECR,
Invert = __gl::INVERT,
IncrementWrap = __gl::INCR_WRAP,
DecrementWrap = __gl::DECR_WRAP,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct StencilFace {
pub fail: StencilOp,
pub pass: StencilOp,
pub depth_fail: StencilOp,
pub compare_op: Compare,
pub compare_mask: u32,
pub reference: u32,
}
impl StencilFace {
pub const KEEP: StencilFace = StencilFace {
fail: StencilOp::Keep,
pass: StencilOp::Keep,
depth_fail: StencilOp::Keep,
compare_op: Compare::Always,
compare_mask: !0,
reference: 0,
};
}
#[derive(Debug, Copy, Clone)]
pub struct DepthStencil {
pub depth_test: bool,
pub depth_write: bool,
pub depth_compare_op: Compare,
pub stencil_test: bool,
pub stencil_front: StencilFace,
pub stencil_back: StencilFace,
}
#[derive(Debug, Copy, Clone)]
pub struct Multisample {
pub sample_shading: bool,
pub min_sample_shading: f32,
pub sample_mask: u64,
pub alpha_to_coverage: bool,
pub alpha_to_one: bool,
}
impl Device {
unsafe fn check_pipeline_log(&self, pipeline: GLuint) {
let log = {
let mut len = {
let mut len = 0;
self.0
.GetProgramiv(pipeline, __gl::INFO_LOG_LENGTH, &mut len);
len
};
if len > 0 {
let mut log = String::with_capacity(len as usize);
log.extend(std::iter::repeat('\0').take(len as usize));
self.0
.GetProgramInfoLog(pipeline, len, &mut len, (&log[..]).as_ptr() as *mut _);
log.truncate(len as usize);
log
} else {
String::new()
}
};
if !log.is_empty() {
println!("Pipeline Info Log: {}", log);
}
}
pub unsafe fn create_shader(&self, stage: ShaderStage, source: &[u8]) -> Result<Shader> {
let stage = match stage {
ShaderStage::Vertex => __gl::VERTEX_SHADER,
ShaderStage::TessellationControl => __gl::TESS_CONTROL_SHADER,
ShaderStage::TessellationEvaluation => __gl::TESS_EVALUATION_SHADER,
ShaderStage::Geometry => __gl::GEOMETRY_SHADER,
ShaderStage::Fragment => __gl::FRAGMENT_SHADER,
ShaderStage::Compute => __gl::COMPUTE_SHADER,
ShaderStage::MeshNv => __gl::MESH_SHADER_NV,
ShaderStage::TaskNv => __gl::TASK_SHADER_NV,
};
let shader = {
let shader = self.0.CreateShader(stage);
self.get_error()?;
self.0.ShaderSource(
shader,
1,
&(source.as_ptr() as *const _),
&(source.len() as _),
);
self.0.CompileShader(shader);
shader
};
let status = {
let mut status = 0;
self.0
.GetShaderiv(shader, __gl::COMPILE_STATUS, &mut status);
status
};
if status != GLint::from(__gl::TRUE) {
println!("Shader could not be compiled successfully ({:?})", stage);
}
let log = {
let mut len = {
let mut len = 0;
self.0.GetShaderiv(shader, __gl::INFO_LOG_LENGTH, &mut len);
len
};
if len > 0 {
let mut log = String::with_capacity(len as usize);
log.extend(std::iter::repeat('\0').take(len as usize));
self.0
.GetShaderInfoLog(shader, len, &mut len, (&log[..]).as_ptr() as *mut _);
log.truncate(len as usize);
log
} else {
String::new()
}
};
if !log.is_empty() {
println!("Shader Info Log: {}", log);
}
Ok(Shader(shader))
}
pub unsafe fn delete_shader(&self, shader: Shader) {
self.0.DeleteShader(shader.0);
}
pub unsafe fn delete_shaders(&self, shaders: &[Shader]) {
for shader in shaders.iter() {
self.0.DeleteShader(shader.0);
}
}
pub unsafe fn create_graphics_pipeline<D>(&self, desc: D) -> Result<Pipeline>
where
D: Into<GraphicsPipelineDesc>,
{
let desc = desc.into();
let pipeline = self.0.CreateProgram();
self.get_error()?;
if let Some(vs) = desc.vertex_shader {
self.0.AttachShader(pipeline, vs.0);
}
if let Some(tsc) = desc.tessellation_control_shader {
self.0.AttachShader(pipeline, tsc.0);
}
if let Some(tse) = desc.tessellation_evaluation_shader {
self.0.AttachShader(pipeline, tse.0);
}
if let Some(geometry) = desc.geometry_shader {
self.0.AttachShader(pipeline, geometry.0);
}
if let Some(fragment) = desc.fragment_shader {
self.0.AttachShader(pipeline, fragment.0);
}
if let Some(ms) = desc.mesh_shader {
self.0.AttachShader(pipeline, ms.0);
}
if let Some(ts) = desc.task_shader {
self.0.AttachShader(pipeline, ts.0);
}
self.0.LinkProgram(pipeline);
if let Some(vs) = desc.vertex_shader {
self.0.DetachShader(pipeline, vs.0);
}
if let Some(tsc) = desc.tessellation_control_shader {
self.0.DetachShader(pipeline, tsc.0);
}
if let Some(tse) = desc.tessellation_evaluation_shader {
self.0.DetachShader(pipeline, tse.0);
}
if let Some(geometry) = desc.geometry_shader {
self.0.DetachShader(pipeline, geometry.0);
}
if let Some(fragment) = desc.fragment_shader {
self.0.DetachShader(pipeline, fragment.0);
}
if let Some(ms) = desc.mesh_shader {
self.0.DetachShader(pipeline, ms.0);
}
if let Some(ts) = desc.task_shader {
self.0.DetachShader(pipeline, ts.0);
}
let status = {
let mut status = 0;
self.0
.GetProgramiv(pipeline, __gl::LINK_STATUS, &mut status);
status
};
if status != GLint::from(__gl::TRUE) {
println!("Graphics pipeline could not be linked successfully");
}
self.check_pipeline_log(pipeline);
Ok(Pipeline(pipeline))
}
pub unsafe fn create_compute_pipeline(&self, compute_shader: Shader) -> Result<Pipeline> {
let pipeline = self.0.CreateProgram();
self.get_error()?;
self.0.AttachShader(pipeline, compute_shader.0);
self.0.LinkProgram(pipeline);
self.0.DetachShader(pipeline, compute_shader.0);
let status = {
let mut status = 0;
self.0
.GetProgramiv(pipeline, __gl::LINK_STATUS, &mut status);
status
};
if status != GLint::from(__gl::TRUE) {
println!("Compute pipeline could not be linked successfully");
}
self.check_pipeline_log(pipeline);
Ok(Pipeline(pipeline))
}
pub unsafe fn delete_pipeline(&self, pipeline: Pipeline) {
self.0.DeleteProgram(pipeline.0);
}
pub unsafe fn delete_pipelines(&self, pipelines: &[Pipeline]) {
for pipeline in pipelines {
self.0.DeleteProgram(pipeline.0);
}
}
pub unsafe fn bind_input_assembly_state(&self, state: InputAssembly) {
match state.primitive_restart {
Some(index) => {
self.0.Enable(__gl::PRIMITIVE_RESTART);
self.0.PrimitiveRestartIndex(index);
}
None => {
self.0.Disable(__gl::PRIMITIVE_RESTART);
}
}
}
pub unsafe fn bind_color_blend_state(&self, state: &ColorBlend) {
for (i, attachment) in state.attachments.iter().enumerate() {
let slot = i as u32;
if attachment.blend_enable {
self.0.Enablei(__gl::BLEND, slot);
self.0.BlendEquationSeparatei(
slot,
attachment.color.blend_op as _,
attachment.alpha.blend_op as _,
);
self.0.BlendFuncSeparatei(
slot,
attachment.color.src_factor as _,
attachment.color.dst_factor as _,
attachment.alpha.src_factor as _,
attachment.alpha.dst_factor as _,
);
} else {
self.0.Disablei(__gl::BLEND, slot);
}
}
}
pub unsafe fn bind_depth_stencil_state(&self, state: &DepthStencil) {
if state.depth_test {
self.0.Enable(__gl::DEPTH_TEST);
self.0.DepthMask(if state.depth_write {
__gl::TRUE
} else {
__gl::FALSE
});
self.0.DepthFunc(state.depth_compare_op as _);
} else {
self.0.Disable(__gl::DEPTH_TEST);
}
if state.stencil_test {
self.0.Enable(__gl::STENCIL_TEST);
self.0.StencilFuncSeparate(
__gl::FRONT,
state.stencil_front.compare_op as _,
state.stencil_front.reference as _,
state.stencil_front.compare_mask,
);
self.0.StencilOpSeparate(
__gl::FRONT,
state.stencil_front.fail as _,
state.stencil_front.depth_fail as _,
state.stencil_front.pass as _,
);
self.0.StencilFuncSeparate(
__gl::BACK,
state.stencil_back.compare_op as _,
state.stencil_back.reference as _,
state.stencil_back.compare_mask,
);
self.0.StencilOpSeparate(
__gl::BACK,
state.stencil_back.fail as _,
state.stencil_back.depth_fail as _,
state.stencil_back.pass as _,
);
} else {
self.0.Disable(__gl::STENCIL_TEST);
}
}
pub unsafe fn bind_rasterization_state(&self, state: &Rasterization) {
if state.depth_clamp {
self.0.Enable(__gl::DEPTH_CLAMP);
} else {
self.0.Disable(__gl::DEPTH_CLAMP);
}
if state.rasterizer_discard {
self.0.Enable(__gl::RASTERIZER_DISCARD);
} else {
self.0.Disable(__gl::RASTERIZER_DISCARD);
}
let bias_primitive = match state.polygon_mode {
PolygonMode::Point => __gl::POLYGON_OFFSET_POINT,
PolygonMode::Line => __gl::POLYGON_OFFSET_LINE,
PolygonMode::Fill => __gl::POLYGON_OFFSET_FILL,
};
if state.depth_bias {
self.0.Enable(bias_primitive);
} else {
self.0.Disable(bias_primitive);
}
self.0
.PolygonMode(__gl::FRONT_AND_BACK, state.polygon_mode as _);
self.0.FrontFace(state.front_face as _);
match state.cull_mode {
Some(cull) => {
self.0.Enable(__gl::CULL_FACE);
self.0.CullFace(cull as _);
}
None => {
self.0.Disable(__gl::CULL_FACE);
}
}
}
pub unsafe fn bind_multisample_state(&self, state: Option<&Multisample>) {
match state {
Some(state) => {
self.0.Enable(__gl::MULTISAMPLE);
if state.sample_shading {
self.0.Enable(__gl::SAMPLE_SHADING);
self.0.MinSampleShading(state.min_sample_shading);
} else {
self.0.Disable(__gl::SAMPLE_SHADING);
}
self.0
.SampleMaski(0, (state.sample_mask & 0xFFFF_FFFF) as _);
self.0
.SampleMaski(1, ((state.sample_mask >> 32) & 0xFFFF_FFFF) as _);
if state.alpha_to_coverage {
self.0.Enable(__gl::SAMPLE_ALPHA_TO_COVERAGE);
} else {
self.0.Disable(__gl::SAMPLE_ALPHA_TO_COVERAGE);
}
if state.alpha_to_one {
self.0.Enable(__gl::SAMPLE_ALPHA_TO_ONE);
} else {
self.0.Disable(__gl::SAMPLE_ALPHA_TO_ONE);
}
}
None => {
self.0.Disable(__gl::MULTISAMPLE);
}
}
}
pub unsafe fn bind_pipeline(&self, pipeline: Pipeline) {
self.0.UseProgram(pipeline.0);
}
}