#[cfg(feature = "opengl")]
use glow::HasContext;
use crate::{
GpuAdapterInfo, GpuBackendType, GpuCaps, GpuContext, GpuDeviceType, GpuError, GpuResult,
TextureFormat,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OpenGLVersion {
pub major: u32,
pub minor: u32,
pub is_es: bool,
}
impl OpenGLVersion {
pub const GL_3_3: Self = Self {
major: 3,
minor: 3,
is_es: false,
};
pub const GL_4_0: Self = Self {
major: 4,
minor: 0,
is_es: false,
};
pub const GL_4_1: Self = Self {
major: 4,
minor: 1,
is_es: false,
};
pub const GL_4_2: Self = Self {
major: 4,
minor: 2,
is_es: false,
};
pub const GL_4_3: Self = Self {
major: 4,
minor: 3,
is_es: false,
};
pub const GL_4_4: Self = Self {
major: 4,
minor: 4,
is_es: false,
};
pub const GL_4_5: Self = Self {
major: 4,
minor: 5,
is_es: false,
};
pub const GL_4_6: Self = Self {
major: 4,
minor: 6,
is_es: false,
};
pub const GLES_2_0: Self = Self {
major: 2,
minor: 0,
is_es: true,
};
pub const GLES_3_0: Self = Self {
major: 3,
minor: 0,
is_es: true,
};
pub const GLES_3_1: Self = Self {
major: 3,
minor: 1,
is_es: true,
};
pub const GLES_3_2: Self = Self {
major: 3,
minor: 2,
is_es: true,
};
pub const fn new(major: u32, minor: u32, is_es: bool) -> Self {
Self {
major,
minor,
is_es,
}
}
pub fn meets(&self, min: &Self) -> bool {
if self.is_es != min.is_es {
return false;
}
(self.major, self.minor) >= (min.major, min.minor)
}
}
impl std::fmt::Display for OpenGLVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_es {
write!(f, "OpenGL ES {}.{}", self.major, self.minor)
} else {
write!(f, "OpenGL {}.{}", self.major, self.minor)
}
}
}
#[derive(Debug, Clone)]
pub struct OpenGLContextConfig {
pub min_version: OpenGLVersion,
pub debug: bool,
pub core_profile: bool,
pub forward_compatible: bool,
}
impl Default for OpenGLContextConfig {
fn default() -> Self {
Self {
min_version: OpenGLVersion::GL_3_3,
debug: cfg!(debug_assertions),
core_profile: true,
forward_compatible: true,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct OpenGLCaps {
pub base: GpuCaps,
pub version: Option<OpenGLVersion>,
pub glsl_version: Option<u32>,
pub max_texture_units: u32,
pub max_combined_texture_image_units: u32,
pub max_vertex_attribs: u32,
pub max_uniform_buffer_bindings: u32,
pub max_uniform_block_size: u32,
pub max_vertex_uniform_components: u32,
pub max_fragment_uniform_components: u32,
pub max_varying_components: u32,
pub max_color_attachments: u32,
pub max_draw_buffers: u32,
pub max_samples: u32,
pub max_renderbuffer_size: u32,
pub max_viewport_dims: [u32; 2],
pub max_texture_lod_bias: f32,
pub max_anisotropy: f32,
pub compute_shaders: bool,
pub geometry_shaders: bool,
pub tessellation_shaders: bool,
pub shader_storage_buffers: bool,
pub shader_image_load_store: bool,
pub multi_draw_indirect: bool,
pub buffer_storage: bool,
pub texture_storage: bool,
pub direct_state_access: bool,
pub debug_output: bool,
pub clip_control: bool,
pub seamless_cubemap: bool,
pub texture_filter_anisotropic: bool,
pub instanced_arrays: bool,
pub vertex_array_objects: bool,
pub framebuffer_objects: bool,
pub srgb_framebuffer: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GLShaderType {
Vertex,
Fragment,
Geometry,
TessControl,
TessEvaluation,
Compute,
}
#[cfg(feature = "opengl")]
impl GLShaderType {
pub fn to_gl(self) -> u32 {
match self {
Self::Vertex => glow::VERTEX_SHADER,
Self::Fragment => glow::FRAGMENT_SHADER,
Self::Geometry => glow::GEOMETRY_SHADER,
Self::TessControl => glow::TESS_CONTROL_SHADER,
Self::TessEvaluation => glow::TESS_EVALUATION_SHADER,
Self::Compute => glow::COMPUTE_SHADER,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct GLTextureFormat {
pub internal_format: u32,
pub format: u32,
pub data_type: u32,
}
#[cfg(feature = "opengl")]
pub fn texture_format_to_gl(format: TextureFormat) -> GLTextureFormat {
match format {
TextureFormat::Rgba8Unorm => GLTextureFormat {
internal_format: glow::RGBA8,
format: glow::RGBA,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::Rgba8UnormSrgb => GLTextureFormat {
internal_format: glow::SRGB8_ALPHA8,
format: glow::RGBA,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::Bgra8Unorm => GLTextureFormat {
internal_format: glow::RGBA8,
format: glow::BGRA,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::Bgra8UnormSrgb => GLTextureFormat {
internal_format: glow::SRGB8_ALPHA8,
format: glow::BGRA,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::R8Unorm => GLTextureFormat {
internal_format: glow::R8,
format: glow::RED,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::Rg8Unorm => GLTextureFormat {
internal_format: glow::RG8,
format: glow::RG,
data_type: glow::UNSIGNED_BYTE,
},
TextureFormat::Rgba16Float => GLTextureFormat {
internal_format: glow::RGBA16F,
format: glow::RGBA,
data_type: glow::HALF_FLOAT,
},
TextureFormat::Rgba32Float => GLTextureFormat {
internal_format: glow::RGBA32F,
format: glow::RGBA,
data_type: glow::FLOAT,
},
TextureFormat::Depth24Stencil8 => GLTextureFormat {
internal_format: glow::DEPTH24_STENCIL8,
format: glow::DEPTH_STENCIL,
data_type: glow::UNSIGNED_INT_24_8,
},
TextureFormat::Depth32Float => GLTextureFormat {
internal_format: glow::DEPTH_COMPONENT32F,
format: glow::DEPTH_COMPONENT,
data_type: glow::FLOAT,
},
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLBlendFactor {
Zero,
#[default]
One,
SrcColor,
OneMinusSrcColor,
DstColor,
OneMinusDstColor,
SrcAlpha,
OneMinusSrcAlpha,
DstAlpha,
OneMinusDstAlpha,
ConstantColor,
OneMinusConstantColor,
ConstantAlpha,
OneMinusConstantAlpha,
SrcAlphaSaturate,
}
#[cfg(feature = "opengl")]
impl GLBlendFactor {
pub fn to_gl(self) -> u32 {
match self {
Self::Zero => glow::ZERO,
Self::One => glow::ONE,
Self::SrcColor => glow::SRC_COLOR,
Self::OneMinusSrcColor => glow::ONE_MINUS_SRC_COLOR,
Self::DstColor => glow::DST_COLOR,
Self::OneMinusDstColor => glow::ONE_MINUS_DST_COLOR,
Self::SrcAlpha => glow::SRC_ALPHA,
Self::OneMinusSrcAlpha => glow::ONE_MINUS_SRC_ALPHA,
Self::DstAlpha => glow::DST_ALPHA,
Self::OneMinusDstAlpha => glow::ONE_MINUS_DST_ALPHA,
Self::ConstantColor => glow::CONSTANT_COLOR,
Self::OneMinusConstantColor => glow::ONE_MINUS_CONSTANT_COLOR,
Self::ConstantAlpha => glow::CONSTANT_ALPHA,
Self::OneMinusConstantAlpha => glow::ONE_MINUS_CONSTANT_ALPHA,
Self::SrcAlphaSaturate => glow::SRC_ALPHA_SATURATE,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLBlendEquation {
#[default]
Add,
Subtract,
ReverseSubtract,
Min,
Max,
}
#[cfg(feature = "opengl")]
impl GLBlendEquation {
pub fn to_gl(self) -> u32 {
match self {
Self::Add => glow::FUNC_ADD,
Self::Subtract => glow::FUNC_SUBTRACT,
Self::ReverseSubtract => glow::FUNC_REVERSE_SUBTRACT,
Self::Min => glow::MIN,
Self::Max => glow::MAX,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLCompareFunc {
Never,
Less,
Equal,
LessEqual,
Greater,
NotEqual,
GreaterEqual,
#[default]
Always,
}
#[cfg(feature = "opengl")]
impl GLCompareFunc {
pub fn to_gl(self) -> u32 {
match self {
Self::Never => glow::NEVER,
Self::Less => glow::LESS,
Self::Equal => glow::EQUAL,
Self::LessEqual => glow::LEQUAL,
Self::Greater => glow::GREATER,
Self::NotEqual => glow::NOTEQUAL,
Self::GreaterEqual => glow::GEQUAL,
Self::Always => glow::ALWAYS,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLStencilOp {
#[default]
Keep,
Zero,
Replace,
Incr,
IncrWrap,
Decr,
DecrWrap,
Invert,
}
#[cfg(feature = "opengl")]
impl GLStencilOp {
pub fn to_gl(self) -> u32 {
match self {
Self::Keep => glow::KEEP,
Self::Zero => glow::ZERO,
Self::Replace => glow::REPLACE,
Self::Incr => glow::INCR,
Self::IncrWrap => glow::INCR_WRAP,
Self::Decr => glow::DECR,
Self::DecrWrap => glow::DECR_WRAP,
Self::Invert => glow::INVERT,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLCullFace {
#[default]
None,
Front,
Back,
FrontAndBack,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLFrontFace {
#[default]
Ccw,
Cw,
}
#[cfg(feature = "opengl")]
impl GLFrontFace {
pub fn to_gl(self) -> u32 {
match self {
Self::Ccw => glow::CCW,
Self::Cw => glow::CW,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum GLPolygonMode {
#[default]
Fill,
Line,
Point,
}
#[cfg(feature = "opengl")]
impl GLPolygonMode {
pub fn to_gl(self) -> u32 {
match self {
Self::Fill => glow::FILL,
Self::Line => glow::LINE,
Self::Point => glow::POINT,
}
}
}
#[cfg(feature = "opengl")]
pub struct OpenGLContext {
gl: glow::Context,
adapter_info: GpuAdapterInfo,
caps: OpenGLCaps,
current_vao: Option<glow::VertexArray>,
current_program: Option<glow::Program>,
current_framebuffer: Option<glow::Framebuffer>,
}
#[cfg(feature = "opengl")]
impl OpenGLContext {
pub unsafe fn from_glow(gl: glow::Context) -> GpuResult<Self> {
unsafe { Self::from_glow_with_config(gl, OpenGLContextConfig::default()) }
}
pub unsafe fn from_glow_with_config(
gl: glow::Context,
config: OpenGLContextConfig,
) -> GpuResult<Self> {
unsafe {
let version_str = gl.get_parameter_string(glow::VERSION);
let (major, minor, is_es) = Self::parse_version(&version_str)?;
let version = OpenGLVersion::new(major, minor, is_es);
if !version.meets(&config.min_version) {
return Err(GpuError::DeviceCreation(format!(
"OpenGL {} required, found {}",
config.min_version, version
)));
}
let renderer = gl.get_parameter_string(glow::RENDERER);
let vendor = gl.get_parameter_string(glow::VENDOR);
let glsl_version_str = gl.get_parameter_string(glow::SHADING_LANGUAGE_VERSION);
let glsl_version = Self::parse_glsl_version(&glsl_version_str);
let caps = Self::query_caps(&gl, version, glsl_version);
let adapter_info = GpuAdapterInfo {
name: renderer.clone(),
vendor: vendor.clone(),
backend: GpuBackendType::OpenGL,
device_type: Self::detect_device_type(&renderer, &vendor),
};
if config.debug && caps.debug_output {
gl.enable(glow::DEBUG_OUTPUT);
gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS);
}
Ok(Self {
gl,
adapter_info,
caps,
current_vao: None,
current_program: None,
current_framebuffer: None,
})
}
}
fn parse_version(version: &str) -> GpuResult<(u32, u32, bool)> {
let is_es = version.contains("ES");
let numbers: Vec<&str> = version
.split(|c: char| !c.is_ascii_digit())
.filter(|s| !s.is_empty())
.collect();
if numbers.len() >= 2 {
let major = numbers[0].parse().unwrap_or(0);
let minor = numbers[1].parse().unwrap_or(0);
Ok((major, minor, is_es))
} else {
Err(GpuError::DeviceCreation(format!(
"Failed to parse OpenGL version: {}",
version
)))
}
}
fn parse_glsl_version(version: &str) -> Option<u32> {
let numbers: Vec<&str> = version
.split(|c: char| !c.is_ascii_digit())
.filter(|s| !s.is_empty())
.collect();
if numbers.len() >= 2 {
let major: u32 = numbers[0].parse().ok()?;
let minor: u32 = numbers[1].parse().ok()?;
Some(major * 100 + minor)
} else {
None
}
}
fn detect_device_type(renderer: &str, vendor: &str) -> GpuDeviceType {
let renderer_lower = renderer.to_lowercase();
let vendor_lower = vendor.to_lowercase();
if renderer_lower.contains("llvmpipe")
|| renderer_lower.contains("software")
|| renderer_lower.contains("swiftshader")
{
return GpuDeviceType::Cpu;
}
if renderer_lower.contains("intel") || vendor_lower.contains("intel") {
if renderer_lower.contains("arc") {
return GpuDeviceType::Discrete;
}
return GpuDeviceType::Integrated;
}
if renderer_lower.contains("nvidia")
|| renderer_lower.contains("geforce")
|| renderer_lower.contains("quadro")
|| vendor_lower.contains("nvidia")
{
return GpuDeviceType::Discrete;
}
if renderer_lower.contains("amd")
|| renderer_lower.contains("radeon")
|| vendor_lower.contains("amd")
|| vendor_lower.contains("ati")
{
return GpuDeviceType::Discrete;
}
GpuDeviceType::Unknown
}
unsafe fn query_caps(
gl: &glow::Context,
version: OpenGLVersion,
glsl_version: Option<u32>,
) -> OpenGLCaps {
unsafe {
let get_int = |pname| gl.get_parameter_i32(pname) as u32;
let get_float = |pname| gl.get_parameter_f32(pname);
let max_texture_size = get_int(glow::MAX_TEXTURE_SIZE);
let max_samples = get_int(glow::MAX_SAMPLES);
let extensions = Self::get_extensions(gl);
let has_ext = |name: &str| extensions.iter().any(|e| e == name);
let compute_shaders = version.meets(&OpenGLVersion::GL_4_3)
|| version.meets(&OpenGLVersion::GLES_3_1)
|| has_ext("GL_ARB_compute_shader");
let geometry_shaders = version.meets(&OpenGLVersion::GL_3_3)
|| has_ext("GL_ARB_geometry_shader4")
|| has_ext("GL_EXT_geometry_shader");
let tessellation_shaders =
version.meets(&OpenGLVersion::GL_4_0) || has_ext("GL_ARB_tessellation_shader");
let shader_storage_buffers = version.meets(&OpenGLVersion::GL_4_3)
|| version.meets(&OpenGLVersion::GLES_3_1)
|| has_ext("GL_ARB_shader_storage_buffer_object");
let shader_image_load_store = version.meets(&OpenGLVersion::GL_4_2)
|| version.meets(&OpenGLVersion::GLES_3_1)
|| has_ext("GL_ARB_shader_image_load_store");
let multi_draw_indirect =
version.meets(&OpenGLVersion::GL_4_3) || has_ext("GL_ARB_multi_draw_indirect");
let buffer_storage =
version.meets(&OpenGLVersion::GL_4_4) || has_ext("GL_ARB_buffer_storage");
let texture_storage = version.meets(&OpenGLVersion::GL_4_2)
|| version.meets(&OpenGLVersion::GLES_3_0)
|| has_ext("GL_ARB_texture_storage");
let direct_state_access =
version.meets(&OpenGLVersion::GL_4_5) || has_ext("GL_ARB_direct_state_access");
let debug_output = version.meets(&OpenGLVersion::GL_4_3)
|| has_ext("GL_KHR_debug")
|| has_ext("GL_ARB_debug_output");
let clip_control =
version.meets(&OpenGLVersion::GL_4_5) || has_ext("GL_ARB_clip_control");
let seamless_cubemap =
version.meets(&OpenGLVersion::GL_3_3) || has_ext("GL_ARB_seamless_cube_map");
let texture_filter_anisotropic = has_ext("GL_EXT_texture_filter_anisotropic")
|| has_ext("GL_ARB_texture_filter_anisotropic");
let max_anisotropy = if texture_filter_anisotropic {
get_float(0x84FF) } else {
0.0
};
let instanced_arrays = version.meets(&OpenGLVersion::GL_3_3)
|| version.meets(&OpenGLVersion::GLES_3_0)
|| has_ext("GL_ARB_instanced_arrays");
let vertex_array_objects = version.meets(&OpenGLVersion::GL_3_3)
|| version.meets(&OpenGLVersion::GLES_3_0)
|| has_ext("GL_ARB_vertex_array_object");
let framebuffer_objects = version.meets(&OpenGLVersion::GL_3_3)
|| version.meets(&OpenGLVersion::GLES_2_0)
|| has_ext("GL_ARB_framebuffer_object");
let srgb_framebuffer = version.meets(&OpenGLVersion::GL_3_3)
|| has_ext("GL_ARB_framebuffer_sRGB")
|| has_ext("GL_EXT_sRGB");
let max_viewport_dims = {
let mut dims = [0i32; 2];
gl.get_parameter_i32_slice(glow::MAX_VIEWPORT_DIMS, &mut dims);
[dims[0] as u32, dims[1] as u32]
};
OpenGLCaps {
base: GpuCaps {
max_texture_size,
max_render_target_size: get_int(glow::MAX_RENDERBUFFER_SIZE),
msaa_support: max_samples > 1,
max_msaa_samples: max_samples,
compute_support: compute_shaders,
instancing_support: instanced_arrays,
},
version: Some(version),
glsl_version,
max_texture_units: get_int(glow::MAX_TEXTURE_IMAGE_UNITS),
max_combined_texture_image_units: get_int(glow::MAX_COMBINED_TEXTURE_IMAGE_UNITS),
max_vertex_attribs: get_int(glow::MAX_VERTEX_ATTRIBS),
max_uniform_buffer_bindings: if version.meets(&OpenGLVersion::GL_3_3) {
get_int(glow::MAX_UNIFORM_BUFFER_BINDINGS)
} else {
0
},
max_uniform_block_size: if version.meets(&OpenGLVersion::GL_3_3) {
get_int(glow::MAX_UNIFORM_BLOCK_SIZE)
} else {
0
},
max_vertex_uniform_components: get_int(glow::MAX_VERTEX_UNIFORM_COMPONENTS),
max_fragment_uniform_components: get_int(glow::MAX_FRAGMENT_UNIFORM_COMPONENTS),
max_varying_components: if version.meets(&OpenGLVersion::GL_3_3) {
get_int(glow::MAX_VARYING_COMPONENTS)
} else {
0
},
max_color_attachments: get_int(glow::MAX_COLOR_ATTACHMENTS),
max_draw_buffers: get_int(glow::MAX_DRAW_BUFFERS),
max_samples,
max_renderbuffer_size: get_int(glow::MAX_RENDERBUFFER_SIZE),
max_viewport_dims,
max_texture_lod_bias: get_float(glow::MAX_TEXTURE_LOD_BIAS),
max_anisotropy,
compute_shaders,
geometry_shaders,
tessellation_shaders,
shader_storage_buffers,
shader_image_load_store,
multi_draw_indirect,
buffer_storage,
texture_storage,
direct_state_access,
debug_output,
clip_control,
seamless_cubemap,
texture_filter_anisotropic,
instanced_arrays,
vertex_array_objects,
framebuffer_objects,
srgb_framebuffer,
}
}
}
unsafe fn get_extensions(gl: &glow::Context) -> Vec<String> {
unsafe {
let num_extensions = gl.get_parameter_i32(glow::NUM_EXTENSIONS) as u32;
(0..num_extensions)
.map(|i| gl.get_parameter_indexed_string(glow::EXTENSIONS, i))
.collect()
}
}
pub fn gl(&self) -> &glow::Context {
&self.gl
}
pub fn opengl_caps(&self) -> &OpenGLCaps {
&self.caps
}
pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
unsafe {
self.gl.clear_color(r, g, b, a);
self.gl.clear(glow::COLOR_BUFFER_BIT);
}
}
pub fn clear_depth(&self, depth: f32) {
unsafe {
self.gl.clear_depth_f32(depth);
self.gl.clear(glow::DEPTH_BUFFER_BIT);
}
}
pub fn clear_stencil(&self, stencil: i32) {
unsafe {
self.gl.clear_stencil(stencil);
self.gl.clear(glow::STENCIL_BUFFER_BIT);
}
}
pub fn viewport(&self, x: i32, y: i32, width: i32, height: i32) {
unsafe {
self.gl.viewport(x, y, width, height);
}
}
pub fn scissor(&self, x: i32, y: i32, width: i32, height: i32) {
unsafe {
self.gl.scissor(x, y, width, height);
}
}
pub fn enable_scissor(&self) {
unsafe {
self.gl.enable(glow::SCISSOR_TEST);
}
}
pub fn disable_scissor(&self) {
unsafe {
self.gl.disable(glow::SCISSOR_TEST);
}
}
pub fn enable_depth_test(&self) {
unsafe {
self.gl.enable(glow::DEPTH_TEST);
}
}
pub fn disable_depth_test(&self) {
unsafe {
self.gl.disable(glow::DEPTH_TEST);
}
}
pub fn depth_func(&self, func: GLCompareFunc) {
unsafe {
self.gl.depth_func(func.to_gl());
}
}
pub fn depth_mask(&self, enabled: bool) {
unsafe {
self.gl.depth_mask(enabled);
}
}
pub fn enable_blend(&self) {
unsafe {
self.gl.enable(glow::BLEND);
}
}
pub fn disable_blend(&self) {
unsafe {
self.gl.disable(glow::BLEND);
}
}
pub fn blend_func(&self, src: GLBlendFactor, dst: GLBlendFactor) {
unsafe {
self.gl.blend_func(src.to_gl(), dst.to_gl());
}
}
pub fn blend_func_separate(
&self,
src_rgb: GLBlendFactor,
dst_rgb: GLBlendFactor,
src_alpha: GLBlendFactor,
dst_alpha: GLBlendFactor,
) {
unsafe {
self.gl.blend_func_separate(
src_rgb.to_gl(),
dst_rgb.to_gl(),
src_alpha.to_gl(),
dst_alpha.to_gl(),
);
}
}
pub fn blend_equation(&self, mode: GLBlendEquation) {
unsafe {
self.gl.blend_equation(mode.to_gl());
}
}
pub fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) {
unsafe {
self.gl.blend_color(r, g, b, a);
}
}
pub fn enable_cull_face(&self) {
unsafe {
self.gl.enable(glow::CULL_FACE);
}
}
pub fn disable_cull_face(&self) {
unsafe {
self.gl.disable(glow::CULL_FACE);
}
}
pub fn cull_face(&self, mode: GLCullFace) {
unsafe {
match mode {
GLCullFace::None => self.gl.disable(glow::CULL_FACE),
GLCullFace::Front => {
self.gl.enable(glow::CULL_FACE);
self.gl.cull_face(glow::FRONT);
}
GLCullFace::Back => {
self.gl.enable(glow::CULL_FACE);
self.gl.cull_face(glow::BACK);
}
GLCullFace::FrontAndBack => {
self.gl.enable(glow::CULL_FACE);
self.gl.cull_face(glow::FRONT_AND_BACK);
}
}
}
}
pub fn front_face(&self, winding: GLFrontFace) {
unsafe {
self.gl.front_face(winding.to_gl());
}
}
pub fn polygon_mode(&self, mode: GLPolygonMode) {
unsafe {
self.gl.polygon_mode(glow::FRONT_AND_BACK, mode.to_gl());
}
}
pub fn draw_arrays(&self, mode: u32, first: i32, count: i32) {
unsafe {
self.gl.draw_arrays(mode, first, count);
}
}
pub fn draw_elements(&self, mode: u32, count: i32, element_type: u32, offset: i32) {
unsafe {
self.gl.draw_elements(mode, count, element_type, offset);
}
}
pub fn draw_arrays_instanced(&self, mode: u32, first: i32, count: i32, instance_count: i32) {
unsafe {
self.gl
.draw_arrays_instanced(mode, first, count, instance_count);
}
}
pub fn draw_elements_instanced(
&self,
mode: u32,
count: i32,
element_type: u32,
offset: i32,
instance_count: i32,
) {
unsafe {
self.gl
.draw_elements_instanced(mode, count, element_type, offset, instance_count);
}
}
pub fn finish(&self) {
unsafe {
self.gl.finish();
}
}
pub fn flush_gl(&self) {
unsafe {
self.gl.flush();
}
}
}
#[cfg(feature = "opengl")]
impl GpuContext for OpenGLContext {
fn backend_type(&self) -> GpuBackendType {
GpuBackendType::OpenGL
}
fn adapter_info(&self) -> &GpuAdapterInfo {
&self.adapter_info
}
fn flush(&self) {
self.flush_gl();
}
fn submit_and_wait(&self) {
self.finish();
}
fn is_valid(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_opengl_version() {
let v = OpenGLVersion::new(4, 5, false);
assert_eq!(v.major, 4);
assert_eq!(v.minor, 5);
assert!(!v.is_es);
assert_eq!(format!("{}", v), "OpenGL 4.5");
let es = OpenGLVersion::GLES_3_0;
assert!(es.is_es);
assert_eq!(format!("{}", es), "OpenGL ES 3.0");
}
#[test]
fn test_version_comparison() {
assert!(OpenGLVersion::GL_4_5.meets(&OpenGLVersion::GL_3_3));
assert!(OpenGLVersion::GL_4_5.meets(&OpenGLVersion::GL_4_5));
assert!(!OpenGLVersion::GL_3_3.meets(&OpenGLVersion::GL_4_5));
assert!(!OpenGLVersion::GL_4_5.meets(&OpenGLVersion::GLES_3_0)); }
#[test]
fn test_opengl_config() {
let config = OpenGLContextConfig::default();
assert_eq!(config.min_version, OpenGLVersion::GL_3_3);
assert!(config.core_profile);
}
#[test]
fn test_opengl_caps_default() {
let caps = OpenGLCaps::default();
assert_eq!(caps.base.max_texture_size, 0);
assert!(caps.version.is_none());
}
#[test]
fn test_blend_factor() {
let factor = GLBlendFactor::SrcAlpha;
assert_eq!(factor, GLBlendFactor::SrcAlpha);
}
#[test]
fn test_compare_func() {
let func = GLCompareFunc::Less;
assert_eq!(func, GLCompareFunc::Less);
}
}