use crate::__gl;
use crate::__gl::types::{GLint, GLuint};
use crate::debug::{Object, ObjectType};
use crate::device::Device;
use crate::error::{Error, Result};
use crate::Compare;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Shader(GLuint);
impl Object for Shader {
const TYPE: ObjectType = ObjectType::Shader;
fn handle(&self) -> GLuint {
self.0
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
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, PartialEq, Eq, Hash)]
pub enum ShaderStage {
Vertex,
TessellationControl,
TessellationEvaluation,
Geometry,
Fragment,
Compute,
MeshNv,
TaskNv,
}
bitflags!(
pub struct ShaderFlags: u8 {
const VERBOSE = 0x1;
}
);
bitflags!(
pub struct PipelineFlags: u8 {
const VERBOSE = 0x1;
}
);
#[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,
}
impl Default for Rasterization {
fn default() -> Rasterization {
Rasterization {
depth_clamp: false,
rasterizer_discard: false,
polygon_mode: PolygonMode::Fill,
cull_mode: None,
front_face: FrontFace::CounterClockwise,
depth_bias: false,
}
}
}
#[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,
};
}
impl Default for StencilFace {
fn default() -> StencilFace {
StencilFace::KEEP
}
}
#[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,
}
impl Default for DepthStencil {
fn default() -> DepthStencil {
DepthStencil {
depth_test: false,
depth_write: true,
depth_compare_op: Compare::Less,
stencil_test: false,
stencil_front: StencilFace::default(),
stencil_back: StencilFace::default(),
}
}
}
#[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 compile_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(shader)
};
let status = {
let mut status = 0;
self.0
.GetShaderiv(shader.0, __gl::COMPILE_STATUS, &mut status);
status
};
if status != GLint::from(__gl::TRUE) {
return Err(Error::CompileError(shader));
}
Ok(shader)
}
pub unsafe fn create_shader(
&self,
stage: ShaderStage,
source: &[u8],
flags: ShaderFlags,
) -> Result<Shader> {
let shader = self.compile_shader(stage, source);
if !(flags.contains(ShaderFlags::VERBOSE)) {
return shader;
}
match shader {
Ok(s) | Err(Error::CompileError(s)) => {
if shader.is_err() {
println!("Shader could not be compiled successfully ({:?})", stage);
}
let log = self.get_shader_log(s);
if let Some(msg) = log {
println!("Shader Info Log: {}", msg);
}
}
_ => {}
}
shader
}
pub unsafe fn get_shader_log(&self, shader: Shader) -> Option<String> {
let mut len = {
let mut len = 0;
self.0
.GetShaderiv(shader.0, __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.0, len, &mut len, (&log[..]).as_ptr() as *mut _);
log.truncate(len as usize);
Some(log)
} else {
None
}
}
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 get_pipeline_log(&self, pipeline: Pipeline) -> Option<String> {
let mut len = {
let mut len = 0;
self.0
.GetProgramiv(pipeline.0, __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.0, len, &mut len, (&log[..]).as_ptr() as *mut _);
log.truncate(len as usize);
Some(log)
} else {
None
}
}
pub unsafe fn create_graphics_pipeline<D>(
&self,
desc: D,
flags: PipelineFlags,
) -> Result<Pipeline>
where
D: Into<GraphicsPipelineDesc>,
{
let desc = desc.into();
let shaders: Vec<_> = [
desc.vertex_shader,
desc.tessellation_control_shader,
desc.tessellation_evaluation_shader,
desc.geometry_shader,
desc.fragment_shader,
desc.mesh_shader,
desc.task_shader,
]
.iter()
.filter_map(|&x| x)
.collect();
self.create_pipeline(&shaders, flags)
}
pub unsafe fn create_compute_pipeline(
&self,
compute_shader: Shader,
flags: PipelineFlags,
) -> Result<Pipeline> {
self.create_pipeline(&[compute_shader], flags)
}
pub unsafe fn create_pipeline(
&self,
shaders: &[Shader],
flags: PipelineFlags,
) -> Result<Pipeline> {
let pipeline = self.0.CreateProgram();
self.get_error()?;
for shader in shaders {
self.0.AttachShader(pipeline, shader.0);
}
self.0.LinkProgram(pipeline);
for shader in shaders {
self.0.DetachShader(pipeline, shader.0);
}
let pipeline_result = {
let status = {
let mut status = 0;
self.0
.GetProgramiv(pipeline, __gl::LINK_STATUS, &mut status);
status
};
if status == GLint::from(__gl::TRUE) {
Ok(Pipeline(pipeline))
} else {
Err(Error::LinkError(Pipeline(pipeline)))
}
};
if !flags.contains(PipelineFlags::VERBOSE) {
return pipeline_result;
}
match pipeline_result {
Ok(p) | Err(Error::LinkError(p)) => {
if pipeline_result.is_err() {
println!("Pipeline could not be linked.");
}
if let Some(msg) = self.get_pipeline_log(p) {
println!("Pipeline Info Log: {}", msg);
}
}
_ => {}
}
pipeline_result
}
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 get_work_group_size(&self, pipeline: Pipeline) -> Option<[i32; 3]> {
let mut sizes: [i32; 3] = [-1, -1, -1];
self.0.GetProgramiv(
pipeline.handle(),
__gl::COMPUTE_WORK_GROUP_SIZE,
sizes.as_mut_ptr(),
);
if sizes[0] >= 0 {
Some(sizes)
} else {
None
}
}
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);
}
}