8000 miniquad/src/graphics.rs at gl2 · ricande/miniquad · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1986 lines (1764 loc) · 60.5 KB

graphics.rs

File metadata and controls

1986 lines (1764 loc) · 60.5 KB
use std::{ffi::CString, mem};
mod texture;
use crate::{native::gl::*, Context};
use std::{error::Error, fmt::Display};
pub use texture::{FilterMode, Texture, TextureAccess, TextureFormat, TextureParams, TextureWrap};
fn get_uniform_location(program: GLuint, name: &str) -> Option<i32> {
let cname = CString::new(name).unwrap_or_else(|e| panic!("{}", e));
let location = unsafe { glGetUniformLocation(program, cname.as_ptr()) };
if location == -1 {
return None;
}
Some(location)
}
#[derive(Clone, Copy, Debug)]
pub enum UniformType {
/// One 32-bit wide float (equivalent to `f32`)
Float1,
/// Two 32-bit wide floats (equivalent to `[f32; 2]`)
Float2,
/// Three 32-bit wide floats (equivalent to `[f32; 3]`)
Float3,
/// Four 32-bit wide floats (equivalent to `[f32; 4]`)
Float4,
/// One unsigned 32-bit integers (equivalent to `[u32; 1]`)
Int1,
/// Two unsigned 32-bit integers (equivalent to `[u32; 2]`)
Int2,
/// Three unsigned 32-bit integers (equivalent to `[u32; 3]`)
Int3,
/// Four unsigned 32-bit integers (equivalent to `[u32; 4]`)
Int4,
/// Four by four matrix of 32-bit floats
Mat4,
}
impl UniformType {
/// Byte size for a given UniformType
pub fn size(&self) -> usize {
match self {
UniformType::Float1 => 4,
UniformType::Float2 => 8,
UniformType::Float3 => 12,
UniformType::Float4 => 16,
UniformType::Int1 => 4,
UniformType::Int2 => 8,
UniformType::Int3 => 12,
UniformType::Int4 => 16,
UniformType::Mat4 => 64,
}
}
}
#[derive(Clone)]
pub struct UniformDesc {
name: String,
uniform_type: UniformType,
array_count: usize,
}
#[derive(Clone)]
pub struct UniformBlockLayout {
pub uniforms: Vec<UniformDesc>,
}
impl UniformDesc {
pub fn new(name: &str, uniform_type: UniformType) -> UniformDesc {
UniformDesc {
name: name.to_string(),
uniform_type,
array_count: 1,
}
}
pub fn array(self, array_count: usize) -> UniformDesc {
UniformDesc {
array_count,
..self
}
}
}
#[derive(Clone)]
pub struct ShaderMeta {
pub uniforms: UniformBlockLayout,
pub images: Vec<String>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum VertexFormat {
/// One 32-bit wide float (equivalent to `f32`)
Float1,
/// Two 32-bit wide floats (equivalent to `[f32; 2]`)
Float2,
/// Three 32-bit wide floats (equivalent to `[f32; 3]`)
Float3,
/// Four 32-bit wide floats (equivalent to `[f32; 4]`)
Float4,
/// One unsigned 8-bit integer (equivalent to `u8`)
Byte1,
/// Two unsigned 8-bit integers (equivalent to `[u8; 2]`)
Byte2,
/// Three unsigned 8-bit integers (equivalent to `[u8; 3]`)
Byte3,
/// Four unsigned 8-bit integers (equivalent to `[u8; 4]`)
Byte4,
/// One unsigned 16-bit integer (equivalent to `u16`)
Short1,
/// Two unsigned 16-bit integers (equivalent to `[u16; 2]`)
Short2,
/// Tree unsigned 16-bit integers (equivalent to `[u16; 3]`)
Short3,
/// Four unsigned 16-bit integers (equivalent to `[u16; 4]`)
Short4,
/// One unsigned 32-bit integers (equivalent to `[u32; 1]`)
Int1,
/// Two unsigned 32-bit integers (equivalent to `[u32; 2]`)
Int2,
/// Three unsigned 32-bit integers (equivalent to `[u32; 3]`)
Int3,
/// Four unsigned 32-bit integers (equivalent to `[u32; 4]`)
Int4,
/// Four by four matrix of 32-bit floats
Mat4,
}
impl VertexFormat {
pub fn size(&self) -> i32 {
match self {
VertexFormat::Float1 => 1,
VertexFormat::Float2 => 2,
VertexFormat::Float3 => 3,
VertexFormat::Float4 => 4,
VertexFormat::Byte1 => 1,
VertexFormat::Byte2 => 2,
VertexFormat::Byte3 => 3,
VertexFormat::Byte4 => 4,
VertexFormat::Short1 => 1,
VertexFormat::Short2 => 2,
VertexFormat::Short3 => 3,
VertexFormat::Short4 => 4,
VertexFormat::Int1 => 1,
VertexFormat::Int2 => 2,
VertexFormat::Int3 => 3,
VertexFormat::Int4 => 4,
VertexFormat::Mat4 => 16,
}
}
pub fn byte_len(&self) -> i32 {
match self {
VertexFormat::Float1 => 1 * 4,
VertexFormat::Float2 => 2 * 4,
VertexFormat::Float3 => 3 * 4,
VertexFormat::Float4 => 4 * 4,
VertexFormat::Byte1 => 1,
VertexFormat::Byte2 => 2,
VertexFormat::Byte3 => 3,
VertexFormat::Byte4 => 4,
VertexFormat::Short1 => 1 * 2,
VertexFormat::Short2 => 2 * 2,
VertexFormat::Short3 => 3 * 2,
VertexFormat::Short4 => 4 * 2,
VertexFormat::Int1 => 1 * 4,
VertexFormat::Int2 => 2 * 4,
VertexFormat::Int3 => 3 * 4,
VertexFormat::Int4 => 4 * 4,
VertexFormat::Mat4 => 16 * 4,
}
}
fn type_(&self) -> GLuint {
match self {
VertexFormat::Float1 => GL_FLOAT,
VertexFormat::Float2 => GL_FLOAT,
VertexFormat::Float3 => GL_FLOAT,
VertexFormat::Float4 => GL_FLOAT,
VertexFormat::Byte1 => GL_UNSIGNED_BYTE,
VertexFormat::Byte2 => GL_UNSIGNED_BYTE,
VertexFormat::Byte3 => GL_UNSIGNED_BYTE,
VertexFormat::Byte4 => GL_UNSIGNED_BYTE,
VertexFormat::Short1 => GL_UNSIGNED_SHORT,
VertexFormat::Short2 => GL_UNSIGNED_SHORT,
VertexFormat::Short3 => GL_UNSIGNED_SHORT,
VertexFormat::Short4 => GL_UNSIGNED_SHORT,
VertexFormat::Int1 => GL_UNSIGNED_INT,
VertexFormat::Int2 => GL_UNSIGNED_INT,
VertexFormat::Int3 => GL_UNSIGNED_INT,
VertexFormat::Int4 => GL_UNSIGNED_INT,
VertexFormat::Mat4 => GL_FLOAT,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum VertexStep {
PerVertex,
PerInstance,
}
impl Default for VertexStep {
fn default() -> VertexStep {
VertexStep::PerVertex
}
}
#[derive(Clone, Debug)]
pub struct BufferLayout {
pub stride: i32,
pub step_func: VertexStep,
pub step_rate: i32,
}
impl Default for BufferLayout {
fn default() -> BufferLayout {
BufferLayout {
stride: 0,
step_func: VertexStep::PerVertex,
step_rate: 1,
}
}
}
#[derive(Clone, Debug)]
pub struct VertexAttribute {
pub name: &'static str,
pub format: VertexFormat,
pub buffer_index: usize,
}
impl VertexAttribute {
pub const fn new(name: &'static str, format: VertexFormat) -> VertexAttribute {
Self::with_buffer(name, format, 0)
}
pub const fn with_buffer(
name: &'static str,
format: VertexFormat,
buffer_index: usize,
) -> VertexAttribute {
VertexAttribute {
name,
format,
buffer_index,
}
}
}
#[derive(Clone, Debug)]
pub struct PipelineLayout {
pub buffers: &'static [BufferLayout],
pub attributes: &'static [VertexAttribute],
}
#[derive(Clone, Debug, Copy)]
pub enum ShaderType {
Vertex,
Fragment,
}
#[derive(Clone, Debug)]
pub enum ShaderError {
CompilationError {
shader_type: ShaderType,
error_message: String,
},
LinkError(String),
/// Shader strings should never contains \00 in the middle
FFINulError(std::ffi::NulError),
}
impl From<std::ffi::NulError> for ShaderError {
fn from(e: std::ffi::NulError) -> ShaderError {
ShaderError::FFINulError(e)
}
}
impl Display for ShaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self) // Display the same way as Debug
}
}
impl Error for ShaderError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
#[derive(Clone, Debug, Copy)]
pub struct Shader(usize);
impl Shader {
pub fn new(
ctx: &mut Context,
vertex_shader: &str,
fragment_shader: &str,
meta: ShaderMeta,
) -> Result<Shader, ShaderError> {
let shader = load_shader_internal(vertex_shader, fragment_shader, meta)?;
ctx.shaders.push(shader);
Ok(Shader(ctx.shaders.len() - 1))
}
}
type UniformLocation = Option<GLint>;
pub struct ShaderImage {
gl_loc: UniformLocation,
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct ShaderUniform {
gl_loc: UniformLocation,
_offset: usize,
_size: usize,
uniform_type: UniformType,
array_count: i32,
}
struct ShaderInternal {
program: GLuint,
images: Vec<ShaderImage>,
uniforms: Vec<ShaderUniform>,
}
/// Pixel arithmetic description for blending operations.
/// Will be used in an equation:
/// `equation(sfactor * source_color, dfactor * destination_color)`
/// Where source_color is the new pixel color and destination color is color from the destination buffer.
///
/// Example:
///```
///# use miniquad::{BlendState, BlendFactor, BlendValue, Equation};
///BlendState::new(
/// Equation::Add,
/// BlendFactor::Value(BlendValue::SourceAlpha),
/// BlendFactor::OneMinusValue(BlendValue::SourceAlpha)
///);
///```
/// This will be `source_color * source_color.a + destination_color * (1 - source_color.a)`
/// Wich is quite common set up for alpha blending.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct BlendState {
equation: Equation,
sfactor: BlendFactor,
dfactor: BlendFactor,
}
impl BlendState {
pub fn new(equation: Equation, sfactor: BlendFactor, dfactor: BlendFactor) -> BlendState {
BlendState {
equation,
sfactor,
dfactor,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct StencilState {
pub front: StencilFaceState,
pub back: StencilFaceState,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct StencilFaceState {
/// Operation to use when stencil test fails
pub fail_op: StencilOp,
/// Operation to use when stencil test passes, but depth test fails
pub depth_fail_op: StencilOp,
/// Operation to use when both stencil and depth test pass,
/// or when stencil pass and no depth or depth disabled
pub pass_op: StencilOp,
/// Used for stencil testing with test_ref and test_mask: if (test_ref & test_mask) *test_func* (*stencil* && test_mask)
/// Default is Always, which means "always pass"
pub test_func: CompareFunc,
/// Default value: 0
pub test_ref: i32,
/// Default value: all 1s
pub test_mask: u32,
/// Specifies a bit mask to enable or disable writing of individual bits in the stencil planes
/// Default value: all 1s
pub write_mask: u32,
}
/// Operations performed on current stencil value when comparison test passes or fails.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum StencilOp {
/// Default value
Keep,
Zero,
Replace,
IncrementClamp,
DecrementClamp,
Invert,
IncrementWrap,
DecrementWrap,
}
/// Depth and stencil compare function
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CompareFunc {
/// Default value
Always,
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreaterOrEqual,
}
type ColorMask = (bool, bool, bool, bool);
#[derive(Default, Copy, Clone)]
struct CachedAttribute {
attribute: VertexAttributeInternal,
gl_vbuf: GLuint,
}
struct GlCache {
stored_index_buffer: GLuint,
stored_index_type: Option<IndexType>,
stored_vertex_buffer: GLuint,
stored_texture: GLuint,
index_buffer: GLuint,
index_type: Option<IndexType>,
vertex_buffer: GLuint,
textures: [GLuint; MAX_SHADERSTAGE_IMAGES],
cur_pipeline: Option<Pipeline>,
color_blend: Option<BlendState>,
alpha_blend: Option<BlendState>,
stencil: Option<StencilState>,
color_write: ColorMask,
cull_face: CullFace,
attributes: [Option<CachedAttribute>; MAX_VERTEX_ATTRIBUTES],
}
impl GlCache {
fn bind_buffer(&mut self, target: GLenum, buffer: GLuint, index_type: Option<IndexType>) {
if target == GL_ARRAY_BUFFER {
if self.vertex_buffer != buffer {
self.vertex_buffer = buffer;
unsafe {
glBindBuffer(target, buffer);
}
}
} else {
if self.index_buffer != buffer {
self.index_buffer = buffer;
unsafe {
glBindBuffer(target, buffer);
}
}
self.index_type = index_type;
}
}
fn store_buffer_binding(&mut self, target: GLenum) {
if target == GL_ARRAY_BUFFER {
self.stored_vertex_buffer = self.vertex_buffer;
} else {
self.stored_index_buffer = self.index_buffer;
self.stored_index_type = self.index_type;
}
}
fn restore_buffer_binding(&mut self, target: GLenum) {
if target == GL_ARRAY_BUFFER {
if self.stored_vertex_buffer != 0 {
self.bind_buffer(target, self.stored_vertex_buffer, None);
self.stored_vertex_buffer = 0;
}
} else {
if self.stored_index_buffer != 0 {
self.bind_buffer(target, self.stored_index_buffer, self.stored_index_type);
self.stored_index_buffer = 0;
}
}
}
fn bind_texture(&mut self, slot_index: usize, texture: GLuint) {
unsafe {
glActiveTexture(GL_TEXTURE0 + slot_index as GLuint);
if self.textures[slot_index] != texture {
glBindTexture(GL_TEXTURE_2D, texture);
self.textures[slot_index] = texture;
}
}
}
fn store_texture_binding(&mut self, slot_index: usize) {
self.stored_texture = self.textures[slot_index];
}
fn restore_texture_binding(&mut self, slot_index: usize) {
self.bind_texture(slot_index, self.stored_texture);
}
fn clear_buffer_bindings(&mut self) {
self.bind_buffer(GL_ARRAY_BUFFER, 0, None);
self.vertex_buffer = 0;
self.bind_buffer(GL_ELEMENT_ARRAY_BUFFER, 0, None);
self.index_buffer = 0;
}
fn clear_texture_bindings(&mut self) {
for ix in 0..MAX_SHADERSTAGE_IMAGES {
if self.textures[ix] != 0 {
self.bind_texture(ix, 0);
self.textures[ix] = 0;
}
}
}
}
pub enum PassAction {
Nothing,
Clear {
color: Option<(f32, f32, f32, f32)>,
depth: Option<f32>,
stencil: Option<i32>,
},
}
impl PassAction {
pub fn clear_color(r: f32, g: f32, b: f32, a: f32) -> PassAction {
PassAction::Clear {
color: Some((r, g, b, a)),
depth: Some(1.),
stencil: None,
}
}
}
impl Default for PassAction {
fn default() -> PassAction {
PassAction::Clear {
color: Some((0.0, 0.0, 0.0, 0.0)),
depth: Some(1.),
stencil: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RenderPass(usize);
struct RenderPassInternal {
gl_fb: GLuint,
texture: Texture,
depth_texture: Option<Texture>,
}
impl RenderPass {
pub fn new(
ctx: &mut Context,
color_img: Texture,
depth_img: impl Into<Option<Texture>>,
) -> RenderPass {
let mut gl_fb = 0;
let depth_img = depth_img.into();
unsafe {
glGenFramebuffers(1, &mut gl_fb as *mut _);
glBindFramebuffer(GL_FRAMEBUFFER, gl_fb);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
color_img.texture,
0,
);
if let Some(depth_img) = depth_img {
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D,
depth_img.texture,
0,
);
}
glBindFramebuffer(GL_FRAMEBUFFER, ctx.default_framebuffer);
}
let pass = RenderPassInternal {
gl_fb,
texture: color_img,
depth_texture: depth_img,
};
ctx.passes.push(pass);
RenderPass(ctx.passes.len() - 1)
}
pub fn texture(&self, ctx: &mut Context) -> Texture {
let render_pass = &mut ctx.passes[self.0];
render_pass.texture
}
pub fn delete(&self, ctx: &mut Context) {
let render_pass = &mut ctx.passes[self.0];
unsafe { glDeleteFramebuffers(1, &mut render_pass.gl_fb as *mut _) }
render_pass.texture.delete();
if let Some(depth_texture) = render_pass.depth_texture {
depth_texture.delete();
}
}
}
pub const MAX_VERTEX_ATTRIBUTES: usize = 16;
pub const MAX_SHADERSTAGE_IMAGES: usize = 12;
pub struct Features {
pub instancing: bool,
}
impl Default for Features {
fn default() -> Features {
Features { instancing: true }
}
}
pub struct GraphicsContext {
shaders: Vec<ShaderInternal>,
pipelines: Vec<PipelineInternal>,
passes: Vec<RenderPassInternal>,
default_framebuffer: GLuint,
cache: GlCache,
pub(crate) features: Features,
pub(crate) display: Option<*mut dyn crate::NativeDisplay>,
}
impl GraphicsContext {
pub fn new() -> GraphicsContext {
unsafe {
let mut default_framebuffer: GLuint = 0;
glGetIntegerv(
GL_FRAMEBUFFER_BINDING,
&mut default_framebuffer as *mut _ as *mut _,
);
let mut vao = 0;
glGenVertexArrays(1, &mut vao as *mut _);
glBindVertexArray(vao);
GraphicsContext {
default_framebuffer,
shaders: vec![],
pipelines: vec![],
passes: vec![],
features: Default::default(),
cache: GlCache {
stored_index_buffer: 0,
stored_index_type: None,
stored_vertex_buffer: 0,
index_buffer: 0,
index_type: None,
vertex_buffer: 0,
cur_pipeline: None,
color_blend: None,
alpha_blend: None,
stencil: None,
color_write: (true, true, true, true),
cull_face: CullFace::Nothing,
stored_texture: 0,
textures: [0; MAX_SHADERSTAGE_IMAGES],
attributes: [None; MAX_VERTEX_ATTRIBUTES],
},
display: None,
}
}
}
pub fn features(&self) -> &Features {
&self.features
}
}
impl Context {
pub fn apply_pipeline(&mut self, pipeline: &Pipeline) {
self.cache.cur_pipeline = Some(*pipeline);
{
let pipeline = &self.pipelines[pipeline.0];
let shader = &mut self.shaders[pipeline.shader.0];
unsafe {
glUseProgram(shader.program);
}
unsafe {
glEnable(GL_SCISSOR_TEST);
}
if pipeline.params.depth_write {
unsafe {
glEnable(GL_DEPTH_TEST);
glDepthFunc(pipeline.params.depth_test.into())
}
} else {
unsafe {
glDisable(GL_DEPTH_TEST);
}
}
match pipeline.params.front_face_order {
FrontFaceOrder::Clockwise => unsafe {
glFrontFace(GL_CW);
},
FrontFaceOrder::CounterClockwise => unsafe {
glFrontFace(GL_CCW);
},
}
}
self.set_cull_face(self.pipelines[pipeline.0].params.cull_face);
self.set_blend(
self.pipelines[pipeline.0].params.color_blend,
self.pipelines[pipeline.0].params.alpha_blend,
);
self.set_stencil(self.pipelines[pipeline.0].params.stencil_test);
self.set_color_write(self.pipelines[pipeline.0].params.color_write);
}
pub fn set_cull_face(&mut self, cull_face: CullFace) {
if self.cache.cull_face == cull_face {
return;
}
match cull_face {
CullFace::Nothing => unsafe {
glDisable(GL_CULL_FACE);
},
CullFace::Front => unsafe {
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
},
CullFace::Back => unsafe {
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
},
}
self.cache.cull_face = cull_face;
}
pub fn set_color_write(&mut self, color_write: ColorMask) {
if self.cache.color_write == color_write {
return;
}
let (r, g, b, a) = color_write;
unsafe { glColorMask(r as _, g as _, b as _, a as _) }
self.cache.color_write = color_write;
}
pub fn set_blend(&mut self, color_blend: Option<BlendState>, alpha_blend: Option<BlendState>) {
if color_blend.is_none() && alpha_blend.is_some() {
panic!("AlphaBlend without ColorBlend");
}
if self.cache.color_blend == color_blend && self.cache.alpha_blend == alpha_blend {
return;
}
unsafe {
if let Some(color_blend) = color_blend {
if self.cache.color_blend.is_none() {
glEnable(GL_BLEND);
}
let BlendState {
equation: eq_rgb,
sfactor: src_rgb,
dfactor: dst_rgb,
} = color_blend;
if let Some(BlendState {
equation: eq_alpha,
sfactor: src_alpha,
dfactor: dst_alpha,
}) = alpha_blend
{
glBlendFuncSeparate(
src_rgb.into(),
dst_rgb.into(),
src_alpha.into(),
dst_alpha.into(),
);
glBlendEquationSeparate(eq_rgb.into(), eq_alpha.into());
} else {
glBlendFunc(src_rgb.into(), dst_rgb.into());
glBlendEquationSeparate(eq_rgb.into(), eq_rgb.into());
}
} else if self.cache.color_blend.is_some() {
glDisable(GL_BLEND);
}
}
self.cache.color_blend = color_blend;
self.cache.alpha_blend = alpha_blend;
}
pub fn set_stencil(&mut self, stencil_test: Option<StencilState>) {
if self.cache.stencil == stencil_test {
return;
}
unsafe {
if let Some(stencil) = stencil_test {
if self.cache.stencil.is_none() {
glEnable(GL_STENCIL_TEST);
}
let front = &stencil.front;
glStencilOpSeparate(
GL_FRONT,
front.fail_op.into(),
front.depth_fail_op.into(),
front.pass_op.into(),
);
glStencilFuncSeparate(
GL_FRONT,
front.test_func.into(),
front.test_ref,
front.test_mask,
);
glStencilMaskSeparate(GL_FRONT, front.write_mask);
let back = &stencil.back;
glStencilOpSeparate(
GL_BACK,
back.fail_op.into(),
back.depth_fail_op.into(),
back.pass_op.into(),
);
glStencilFuncSeparate(
GL_BACK,
back.test_func.into(),
back.test_ref.into(),
back.test_mask,
);
glStencilMaskSeparate(GL_BACK, back.write_mask);
} else if self.cache.stencil.is_some() {
glDisable(GL_STENCIL_TEST);
}
}
self.cache.stencil = stencil_test;
}
/// Set a new viewport rectangle.
/// Should be applied after begin_pass.
pub fn apply_viewport(&mut self, x: i32, y: i32, w: i32, h: i32) {
unsafe {
glViewport(x, y, w, h);
}
}
/// Set a new scissor rectangle.
/// Should be applied after begin_pass.
pub fn apply_scissor_rect(&mut self, x: i32, y: i32, w: i32, h: i32) {
unsafe {
glScissor(x, y, w, h);
}
}
pub fn apply_bindings(&mut self, bindings: &Bindings) {
let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
let shader = &self.shaders[pip.shader.0];
for (n, shader_image) in shader.images.iter().enumerate() {
let bindings_image = bindings
.images
.get(n)
.unwrap_or_else(|| panic!("Image count in bindings and shader did not match!"));
if let Some(gl_loc) = shader_image.gl_loc {
unsafe {
self.cache.bind_texture(n, bindings_image.texture);
glUniform1i(gl_loc, n as i32);
}
}
}
self.cache.bind_buffer(
GL_ELEMENT_ARRAY_BUFFER,
bindings.index_buffer.gl_buf,
bindings.index_buffer.index_type,
);
let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
for attr_index in 0..MAX_VERTEX_ATTRIBUTES {
let cached_attr = &mut self.cache.attributes[attr_index];
let pip_attribute = pip.layout.get(attr_index).copied();
if let Some(Some(attribute)) = pip_attribute {
let vb = bindings.vertex_buffers[attribute.buffer_index];
if cached_attr.map_or(true, |cached_attr| {
attribute != cached_attr.attribute || cached_attr.gl_vbuf != vb.gl_buf
}) {
self.cache
.bind_buffer(GL_ARRAY_BUFFER, vb.gl_buf, vb.index_type);
unsafe {
glVertexAttribP 7B3C ointer(
attr_index as GLuint,
attribute.size,
attribute.type_,
GL_FALSE as u8,
attribute.stride,
attribute.offset as *mut _,
);
if self.features.instancing {
glVertexAttribDivisor(attr_index as GLuint, attribute.divisor as u32);
}
glEnableVertexAttribArray(attr_index as GLuint);
};
let cached_attr = &mut self.cache.attributes[attr_index];
*cached_attr = Some(CachedAttribute {
attribute,
gl_vbuf: vb.gl_buf,
});
}
} else {
if cached_attr.is_some() {
unsafe {
glDisableVertexAttribArray(attr_index as GLuint);
}
*cached_attr = None;
}
}
}
}
pub fn apply_uniforms<U>(&mut self, uniforms: &U) {
self.apply_uniforms_from_bytes(uniforms as *const _ as *const u8, std::mem::size_of::<U>())
}
#[doc(hidden)]
/// Apply uniforms data from array of bytes with very special layout.
/// Hidden because `apply_uniforms` is the recommended and safer way to work with uniforms.
pub fn apply_uniforms_from_bytes(&mut self, uniform_ptr: *const u8, size: usize) {
let pip = &self.pipelines[self.cache.cur_pipeline.unwrap().0];
let shader = &self.shaders[pip.shader.0];
let mut offset = 0;
for (_, uniform) in shader.uniforms.iter().enumerate() {
use UniformType::*;
assert!(
offset <= size - uniform.uniform_type.size() / 4,
"Uniforms struct does not match shader uniforms layout"
);
unsafe {
let data = (uniform_ptr as *const f32).offset(offset as isize);
let data_int = (uniform_ptr as *const i32).offset(offset as isize);
if let Some(gl_loc) = uniform.gl_loc {
match uniform.uniform_type {
Float1 => {
glUniform1fv(gl_loc, uniform.array_count, data);
}
Float2 => {
glUniform2fv(gl_loc, uniform.array_count, data);
}
Float3 => {
glUniform3fv(gl_loc, uniform.array_count, data);
}
Float4 => {
glUniform4fv(gl_loc, uniform.array_count, data);
}
Int1 => {
glUniform1iv(gl_loc, uniform.array_count, data_int);
}
Int2 => {
glUniform2iv(gl_loc, uniform.array_count, data_int);
0