mod error;
mod types;
use std::mem;
use glow::HasContext as _;
use super::*;
use crate::collections::IntMap;
use types::ToGl as _;
pub use error::Error;
#[derive(Clone, Debug)]
pub struct ContextInfo {
pub version: glow::Version,
}
impl From<TextureFormat> for (u32, u32, u32) {
fn from(format: TextureFormat) -> Self {
match format {
TextureFormat::Rgb8 => (glow::RGB, glow::RGB, glow::UNSIGNED_BYTE),
TextureFormat::Rgba8 => (glow::RGBA, glow::RGBA, glow::UNSIGNED_BYTE),
TextureFormat::Depth => (glow::DEPTH_COMPONENT32F, glow::DEPTH_COMPONENT, glow::FLOAT),
TextureFormat::Alpha => (glow::ALPHA, glow::ALPHA, glow::UNSIGNED_BYTE),
}
}
}
#[derive(Clone, Copy, Debug)]
struct VertexBuffer {
handle: glow::Buffer,
size: usize,
}
#[derive(Clone, Copy, Debug)]
struct IndexBuffer {
handle: glow::Buffer,
elem: ElementSize,
size: usize,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct ElementSize(usize);
impl ElementSize {
fn size(&self) -> usize {
self.0
}
}
impl TryFrom<usize> for ElementSize {
type Error = usize;
fn try_from(size: usize) -> Result<Self, Self::Error> {
match size {
1 | 2 | 4 => Ok(Self(size)),
_ => Err(size),
}
}
}
#[derive(Debug)]
struct ShaderUniform {
location: Option<glow::UniformLocation>,
kind: UniformKind,
arity: i32,
}
#[derive(Debug)]
struct ShaderTexture {
location: Option<glow::UniformLocation>,
}
#[derive(Debug)]
struct Shader {
program: glow::Program,
textures: Vec<ShaderTexture>,
uniforms: Vec<ShaderUniform>,
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
struct VertexAttributeInternal {
attr_loc: u32,
size: i32,
kind: u32,
offset: i64,
stride: i32,
buffer: usize,
divisor: i32,
}
#[derive(Debug, Clone)]
struct Pipeline {
shader: ShaderId,
layout: Vec<VertexAttributeInternal>,
culling: CullMode,
winding: FaceWinding,
depth: Option<DepthState>,
stencil: Option<StencilState>,
color: Option<ColorState>,
primitive: Primitive,
}
struct Pass {
fb: glow::Framebuffer,
color: Option<TextureId>,
depth: Option<TextureId>,
size: Size<u32>,
}
#[derive(Default)]
struct Active {
pipeline: Option<PipelineId>,
indices: Option<BufferId>,
}
pub struct Context {
gl: glow::Context,
shaders: IntMap<ShaderId, Shader>,
pipelines: IntMap<PipelineId, Pipeline>,
passes: IntMap<PassId, Pass>,
index_buffers: IntMap<BufferId, IndexBuffer>,
vertex_buffers: IntMap<BufferId, VertexBuffer>,
textures: IntMap<TextureId, Texture<glow::Texture>>,
window: platform::LogicalSize,
}
impl Context {
pub fn new(mut gl: glow::Context, window: platform::LogicalSize) -> Result<Context, Error> {
unsafe {
gl.enable(glow::DEBUG_OUTPUT);
gl.debug_message_callback(|_source, _etype, id, severity, message| {
let lvl = match severity {
glow::DEBUG_SEVERITY_LOW => log::Level::Debug,
glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Debug,
_ => log::Level::Debug,
};
log::log!(lvl, "gl: (id {id}): {message}");
});
log::info!("OpenGL {:?}", gl.version());
let vao = gl.create_vertex_array().map_err(Error::Internal)?;
gl.bind_vertex_array(Some(vao));
Ok(Context {
gl,
window,
shaders: IntMap::default(),
pipelines: IntMap::default(),
passes: IntMap::default(),
index_buffers: IntMap::default(),
vertex_buffers: IntMap::default(),
textures: IntMap::default(),
})
}
}
unsafe fn set_blend(&mut self, blend: BlendState) -> Result<(), Error> {
if blend.rgb.is_none() && blend.alpha.is_some() {
return Err(Error::AlphaBlendWithoutColor);
}
if let Some(rgb) = blend.rgb {
self.gl.enable(glow::BLEND);
let BlendEquation {
op: eq_rgb,
sfactor: src_rgb,
dfactor: dst_rgb,
} = rgb;
if let Some(BlendEquation {
op: eq_alpha,
sfactor: src_alpha,
dfactor: dst_alpha,
}) = blend.alpha
{
self.gl.blend_func_separate(
src_rgb.to_gl(),
dst_rgb.to_gl(),
src_alpha.to_gl(),
dst_alpha.to_gl(),
);
self.gl.blend_equation_separate(eq_rgb.to_gl(), eq_alpha.to_gl());
} else {
self.gl.blend_func(src_rgb.to_gl(), dst_rgb.to_gl());
self.gl.blend_equation_separate(eq_rgb.to_gl(), eq_rgb.to_gl());
}
} else {
self.gl.disable(glow::BLEND);
}
Ok(())
}
unsafe fn set_stencil(&mut self, stencil: Option<StencilState>) {
if let Some(stencil) = stencil {
let front = &stencil.front;
let back = &stencil.back;
self.gl.enable(glow::STENCIL_TEST);
self.gl.stencil_op_separate(
glow::FRONT,
front.fail_op.to_gl(),
front.depth_fail_op.to_gl(),
front.pass_op.to_gl(),
);
self.gl.stencil_func_separate(
glow::FRONT,
front.compare.to_gl(),
stencil.reference,
stencil.read_mask,
);
self.gl.stencil_mask_separate(glow::FRONT, stencil.write_mask);
self.gl.stencil_op_separate(
glow::BACK,
back.fail_op.to_gl(),
back.depth_fail_op.to_gl(),
back.pass_op.to_gl(),
);
self.gl.stencil_func_separate(
glow::BACK,
back.compare.to_gl(),
stencil.reference,
stencil.read_mask,
);
self.gl.stencil_mask_separate(glow::BACK, stencil.write_mask);
} else {
self.gl.disable(glow::STENCIL_TEST);
}
}
unsafe fn set_cull_face(&mut self, cull_face: CullMode) {
match cull_face {
CullMode::Nothing => {
self.gl.disable(glow::CULL_FACE);
}
CullMode::Front => {
self.gl.enable(glow::CULL_FACE);
self.gl.cull_face(glow::FRONT);
}
CullMode::Back => {
self.gl.enable(glow::CULL_FACE);
self.gl.cull_face(glow::BACK);
}
}
}
unsafe fn set_color_mask(&mut self, mask: Rgba<bool>) {
self.gl.color_mask(mask.r, mask.g, mask.b, mask.a);
}
unsafe fn load_shader(
&self,
kind: ShaderKind,
source: &str,
) -> Result<glow::Shader, ShaderError> {
let shader = self.gl.create_shader(kind.to_gl()).map_err(ShaderError::Internal)?;
self.gl.shader_source(shader, source);
self.gl.compile_shader(shader);
if !self.gl.get_shader_compile_status(shader) {
let message = self.gl.get_shader_info_log(shader);
return Err(ShaderError::CompilationError { kind, message });
}
Ok(shader)
}
unsafe fn bind_framebuffer(&mut self, framebuffer: Option<glow::Framebuffer>, size: Size<u32>) {
self.gl.bind_framebuffer(glow::FRAMEBUFFER, framebuffer);
self.gl.viewport(0, 0, size.w as i32, size.h as i32);
self.gl.scissor(0, 0, size.w as i32, size.h as i32);
}
}
pub struct FrameBuilder<'a> {
ctx: &'a mut Context,
active: Active,
}
impl<'a> Drop for FrameBuilder<'a> {
fn drop(&mut self) {
unsafe {
self.ctx.gl.bind_buffer(BufferKind::Vertex.to_gl(), None);
self.ctx.gl.bind_buffer(BufferKind::Index.to_gl(), None);
}
}
}
impl<'a> RenderFrame<'a> for FrameBuilder<'a> {
type Error = Error;
fn apply_viewport(&mut self, rect: Rect<i32>) -> Result<(), Error> {
unsafe {
self.ctx.gl.viewport(rect.origin.x, rect.origin.y, rect.size.w, rect.size.h);
}
Ok(())
}
fn apply_scissor(&mut self, rect: Rect<i32>) -> Result<(), Error> {
unsafe {
self.ctx.gl.scissor(rect.origin.x, rect.origin.y, rect.size.w, rect.size.h);
}
Ok(())
}
fn apply_pipeline(&mut self, pipeline: &PipelineId) -> Result<(), Error> {
let desc = &self.ctx.pipelines[pipeline].clone();
let shader = &desc.shader;
let shader = &self.ctx.shaders[shader];
unsafe {
self.ctx.gl.use_program(Some(shader.program));
self.ctx.gl.enable(glow::SCISSOR_TEST);
if let Some(depth) = &desc.depth {
self.ctx.gl.enable(glow::DEPTH_TEST);
self.ctx.gl.depth_func(depth.compare.to_gl());
} else {
self.ctx.gl.disable(glow::DEPTH_TEST);
}
}
unsafe {
self.ctx.gl.front_face(desc.winding.to_gl());
self.ctx.set_cull_face(desc.culling);
self.ctx.set_stencil(desc.stencil);
if let Some(color) = desc.color {
self.ctx.set_blend(color.blend)?;
self.ctx.set_color_mask(color.mask);
}
}
self.active.pipeline = Some(*pipeline);
Ok(())
}
fn apply_bindings(&mut self, bindings: &Bindings) -> Result<(), Error> {
let pip = self.pipeline()?;
let shader = &self.ctx.shaders[&pip.shader];
for (n, img) in shader.textures.iter().enumerate() {
let tex = bindings.textures.get(n).ok_or(Error::TextureLayoutMismatch)?;
if let Some(loc) = img.location {
let tex = self.ctx.textures[tex];
unsafe {
self.ctx.gl.active_texture(glow::TEXTURE0 + n as u32);
self.ctx.gl.bind_texture(tex.target.to_gl(), Some(tex.data));
self.ctx.gl.uniform_1_i32(Some(&loc), n as i32);
}
}
}
for (attr_index, attribute) in pip.layout.iter().enumerate() {
let attr_index = attr_index as u32;
let vb = bindings.vertex[attribute.buffer];
let vb = self.ctx.vertex_buffers[&vb];
unsafe {
self.ctx.gl.bind_buffer(BufferKind::Vertex.to_gl(), Some(vb.handle));
self.ctx.gl.vertex_attrib_pointer_f32(
attr_index,
attribute.size,
attribute.kind,
false,
attribute.stride,
attribute.offset as i32,
);
self.ctx.gl.vertex_attrib_divisor(attr_index, attribute.divisor as u32);
self.ctx.gl.enable_vertex_attrib_array(attr_index);
}
}
if let Some(index) = bindings.index {
let indices = self.ctx.index_buffers[&index];
unsafe { self.ctx.gl.bind_buffer(BufferKind::Index.to_gl(), Some(indices.handle)) };
self.active.indices = Some(index);
} else {
unsafe { self.ctx.gl.bind_buffer(BufferKind::Index.to_gl(), None) };
self.active.indices = None;
}
Ok(())
}
fn apply_uniforms<T: AsBytes>(&mut self, uniforms: &T) -> Result<(), Error> {
let pip = self.pipeline()?;
let shader = &self.ctx.shaders[&pip.shader];
for (i, uniform) in shader.uniforms.iter().enumerate() {
let size = uniform.kind.size() * uniform.arity as usize;
let offset = size * i;
let data = &uniforms.as_bytes()[offset..offset + size];
if offset > size - uniform.kind.size() / 4 {
return Err(Error::UniformLayoutMismatch);
}
unsafe {
if let Some(loc) = &uniform.location {
use bytes::align_to;
use UniformKind::*;
match uniform.kind {
Float1 => self.ctx.gl.uniform_1_f32_slice(Some(loc), align_to::<f32>(data)),
Float2 => self.ctx.gl.uniform_2_f32_slice(Some(loc), align_to::<f32>(data)),
Float3 => self.ctx.gl.uniform_3_f32_slice(Some(loc), align_to::<f32>(data)),
Float4 => self.ctx.gl.uniform_4_f32_slice(Some(loc), align_to::<f32>(data)),
Int1 => self.ctx.gl.uniform_1_i32_slice(Some(loc), align_to::<i32>(data)),
Int2 => self.ctx.gl.uniform_2_i32_slice(Some(loc), align_to::<i32>(data)),
Int3 => self.ctx.gl.uniform_3_i32_slice(Some(loc), align_to::<i32>(data)),
Int4 => self.ctx.gl.uniform_4_i32_slice(Some(loc), align_to::<i32>(data)),
Mat4 => self.ctx.gl.uniform_matrix_4_f32_slice(
Some(loc),
false,
align_to::<f32>(data),
),
}
}
}
}
Ok(())
}
fn clear(&mut self, color: Option<Rgba<f32>>, depth: Option<f32>, stencil: Option<i32>) {
let mut bits = 0;
if let Some(color) = color {
bits |= glow::COLOR_BUFFER_BIT;
unsafe {
self.ctx.gl.clear_color(color.r, color.g, color.b, color.a);
}
}
if let Some(v) = depth {
bits |= glow::DEPTH_BUFFER_BIT;
unsafe {
self.ctx.gl.clear_depth_f32(v);
}
}
if let Some(v) = stencil {
bits |= glow::STENCIL_BUFFER_BIT;
unsafe {
self.ctx.gl.clear_stencil(v);
}
}
if bits != 0 {
unsafe {
self.ctx.gl.clear(bits);
}
}
}
fn begin_default_pass(&mut self, action: PassAction) {
let size = Size::new(self.ctx.window.width as u32, self.ctx.window.height as u32);
unsafe { self.ctx.bind_framebuffer(None, size) }
match action {
PassAction::Nothing => {}
PassAction::Clear {
color,
depth,
stencil,
} => {
self.clear(color, depth, stencil);
}
}
}
fn begin_pass(&mut self, pass: PassId, action: PassAction) {
let pass = &self.ctx.passes[&pass];
unsafe { self.ctx.bind_framebuffer(Some(pass.fb), pass.size) }
match action {
PassAction::Nothing => {}
PassAction::Clear {
color,
depth,
stencil,
} => {
self.clear(color, depth, stencil);
}
}
}
fn end_pass(&mut self) {
unsafe {
self.ctx.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
self.ctx.gl.bind_buffer(BufferKind::Vertex.to_gl(), None);
self.ctx.gl.bind_buffer(BufferKind::Index.to_gl(), None);
}
}
fn draw(&self, offset: usize, elements: usize, instances: usize) -> Result<(), Error> {
if self.active.pipeline.is_none() {
return Err(Error::NoActivePipeline);
}
if self.active.pipeline.is_none() {
return Err(Error::NoActiveIndexBuffer);
}
let pip = self.pipeline()?;
let primitive_type = pip.primitive.to_gl();
if let Some(idx) = self.active.indices {
let indices = self.ctx.index_buffers[&idx];
if elements > indices.size {
return Err(Error::InvalidIndexElementCount(elements));
}
unsafe {
self.ctx.gl.draw_elements_instanced(
primitive_type,
elements as i32,
indices.elem.to_gl(),
indices.elem.size() as i32 * offset as i32,
instances as i32,
);
}
} else {
unsafe {
self.ctx.gl.draw_arrays(primitive_type, 0, elements as i32);
}
}
Ok(())
}
fn commit(self) {
drop(self)
}
}
impl<'a> FrameBuilder<'a> {
fn pipeline(&self) -> Result<&Pipeline, Error> {
let id = self.active.pipeline.ok_or(Error::NoActivePipeline)?;
Ok(&self.ctx.pipelines[&id])
}
}
impl RenderBackend for Context {
type Info = ContextInfo;
type Frame<'a> = FrameBuilder<'a>;
type Error = Error;
fn info(&self) -> ContextInfo {
let version = self.gl.version().clone();
ContextInfo { version }
}
fn frame(&mut self) -> FrameBuilder<'_> {
FrameBuilder {
ctx: self,
active: Active::default(),
}
}
fn shader(
&mut self,
shader: ShaderSource,
meta: ShaderDescriptor,
) -> Result<ShaderId, ShaderError> {
let vertex_shader = shader.vertex;
let fragment_shader = shader.fragment;
let shader = unsafe {
let vertex_shader = self.load_shader(ShaderKind::Vertex, vertex_shader)?;
let fragment_shader = self.load_shader(ShaderKind::Fragment, fragment_shader)?;
let program = self.gl.create_program().map_err(ShaderError::Internal)?;
self.gl.attach_shader(program, vertex_shader);
self.gl.attach_shader(program, fragment_shader);
self.gl.link_program(program);
if !self.gl.get_program_link_status(program) {
let error = self.gl.get_program_info_log(program);
return Err(ShaderError::LinkError(error));
}
self.gl.use_program(Some(program));
let textures = meta
.textures
.iter()
.map(|name| ShaderTexture {
location: self.gl.get_uniform_location(program, name),
})
.collect();
let uniforms = meta
.uniforms
.uniforms
.iter()
.scan(0, |offset, uniform| {
let res = ShaderUniform {
location: self.gl.get_uniform_location(program, &uniform.name),
kind: uniform.kind,
arity: uniform.arity as _,
};
*offset += uniform.kind.size() * uniform.arity;
Some(res)
})
.collect();
Ok(Shader {
program,
textures,
uniforms,
})
}?;
let id = ShaderId::next();
self.shaders.insert(id, shader);
Ok(id)
}
fn texture<T: AsBytes>(&mut self, texture: Texture<T>) -> Result<TextureId, Error> {
let Texture {
data,
size,
access,
target,
format,
wrap,
min_filter,
mag_filter,
} = texture;
let source = data.as_bytes();
if !source.is_empty() {
assert_eq!(format.size(size) as usize, source.len());
}
let (internal_format, format, pixel_type) = format.into();
let source = if source.is_empty() {
None
} else {
Some(source)
};
let wrap = wrap.to_gl() as i32;
let min_filter = min_filter.to_gl() as i32;
let mag_filter = mag_filter.to_gl() as i32;
let handle = unsafe {
let texture = self.gl.create_texture().map_err(Error::Internal)?;
let target = target.to_gl();
self.gl.active_texture(glow::TEXTURE0);
self.gl.bind_texture(target, Some(texture));
assert!(self.gl.is_texture(texture));
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
self.gl.tex_image_2d(
target,
0,
internal_format as i32,
size.w as i32,
size.h as i32,
0,
format,
pixel_type,
source,
);
self.gl.tex_parameter_i32(target, glow::TEXTURE_WRAP_S, wrap);
self.gl.tex_parameter_i32(target, glow::TEXTURE_WRAP_T, wrap);
self.gl.tex_parameter_i32(target, glow::TEXTURE_MIN_FILTER, min_filter);
self.gl.tex_parameter_i32(target, glow::TEXTURE_MAG_FILTER, mag_filter);
texture
};
let id = TextureId::next();
self.textures.insert(
id,
Texture {
data: handle,
size,
access,
target,
format: texture.format,
wrap: texture.wrap,
min_filter: texture.min_filter,
mag_filter: texture.mag_filter,
},
);
Ok(id)
}
fn texture_delete(&mut self, id: TextureId) {
if let Some(t) = self.textures.remove(&id) {
unsafe { self.gl.delete_texture(t.data) };
}
}
fn texture_set_wrap(&mut self, texture: TextureId, wrap: TextureWrap) {
let t = self.textures[&texture];
let target = t.target.to_gl();
let wrap = wrap.to_gl() as i32;
unsafe {
self.gl.bind_texture(target, Some(t.data));
self.gl.tex_parameter_i32(target, glow::TEXTURE_WRAP_S, wrap);
self.gl.tex_parameter_i32(target, glow::TEXTURE_WRAP_T, wrap);
self.gl.bind_texture(target, None);
}
}
fn texture_set_min_filter(&mut self, texture: TextureId, filter: FilterMode) {
let t = self.textures[&texture];
let filter = filter.to_gl();
let target = t.target.to_gl();
unsafe {
self.gl.bind_texture(target, Some(t.data));
self.gl.tex_parameter_i32(target, glow::TEXTURE_MIN_FILTER, filter as i32);
self.gl.bind_texture(target, None);
}
}
fn texture_set_mag_filter(&mut self, texture: TextureId, filter: FilterMode) {
let t = self.textures[&texture];
let filter = filter.to_gl();
let target = t.target.to_gl();
unsafe {
self.gl.bind_texture(target, Some(t.data));
self.gl.tex_parameter_i32(target, glow::TEXTURE_MAG_FILTER, filter as i32);
self.gl.bind_texture(target, None);
}
}
fn texture_resize(
&mut self,
texture: TextureId,
size: Size<u32>,
source: &[color::Rgba8],
) -> Result<(), Error> {
let mut t = self.textures[&texture];
let target = t.target.to_gl();
t.size = size;
let (internal_format, format, pixel_type) = t.format.into();
unsafe {
self.gl.bind_texture(target, Some(t.data));
self.gl.tex_image_2d(
target,
0,
internal_format as i32,
t.size.w as i32,
t.size.h as i32,
0,
format,
pixel_type,
if source.is_empty() {
None
} else {
Some(source.as_bytes())
},
);
self.gl.bind_texture(target, None);
}
Ok(())
}
fn texture_update(
&mut self,
texture: TextureId,
area: Rect<u32>,
source: &[color::Rgba8],
) -> Result<(), Error> {
let t = self.textures[&texture];
let bytes = t.format.size(t.size);
let (_, format, pixel_type) = t.format.into();
assert_eq!(bytes as usize, source.len());
assert!(area.origin.x + area.width() <= t.size.w as _);
assert!(area.origin.y + area.height() <= t.size.h as _);
unsafe {
self.gl.bind_texture(TextureTarget::Texture2D.to_gl(), Some(t.data));
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
self.gl.tex_sub_image_2d(
TextureTarget::Texture2D.to_gl(),
0,
area.origin.x as _,
area.origin.y as _,
area.width() as _,
area.height() as _,
format,
pixel_type,
glow::PixelUnpackData::Slice(source.as_bytes()),
);
}
Ok(())
}
fn pass(&mut self, desc: PassDescriptor) -> Result<PassId, Error> {
let color = desc.color_attachment.map(|t| self.textures[&t]);
let depth = desc.depth_attachment.map(|t| self.textures[&t]);
let size = if let (Some(c), Some(d)) = (color, depth) {
if c.size != d.size {
return Err(Error::AttachmentSizeMismatch);
}
c.size
} else if let Some(t) = color.or(depth) {
t.size
} else {
return Err(Error::MissingAttachment);
};
let (fb, status) = unsafe {
let fb = self.gl.create_framebuffer().map_err(Error::Internal)?;
self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fb));
if let Some(t) = color {
self.gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::COLOR_ATTACHMENT0,
glow::TEXTURE_2D,
Some(t.data),
0,
);
}
if let Some(t) = depth {
self.gl.framebuffer_texture_2d(
glow::FRAMEBUFFER,
glow::DEPTH_ATTACHMENT,
glow::TEXTURE_2D,
Some(t.data),
0,
);
}
(fb, self.gl.check_framebuffer_status(glow::FRAMEBUFFER))
};
if status != glow::FRAMEBUFFER_COMPLETE {
return Err(Error::IncompleteFramebuffer(status));
}
unsafe { self.gl.bind_framebuffer(glow::FRAMEBUFFER, None) };
let id = PassId::next();
let pass = Pass {
fb,
color: desc.color_attachment,
depth: desc.depth_attachment,
size,
};
self.passes.insert(id, pass);
Ok(id)
}
fn pass_delete(&mut self, pass: PassId) {
if let Some(pass) = &self.passes.remove(&pass) {
unsafe { self.gl.delete_framebuffer(pass.fb) };
if let Some(color_texture) = pass.color {
if let Some(t) = self.textures.remove(&color_texture) {
unsafe { self.gl.delete_texture(t.data) };
}
}
if let Some(depth_texture) = pass.depth {
if let Some(t) = self.textures.remove(&depth_texture) {
unsafe { self.gl.delete_texture(t.data) };
}
}
}
}
fn pipeline(&mut self, shader: ShaderId, desc: PipelineDescriptor) -> PipelineId {
let program = self.shaders[&shader].program;
let mut internal: Vec<VertexAttributeInternal> = Vec::new();
let mut offset: i64 = 0;
for (buffer, layout) in desc.layout.iter().enumerate() {
let stride = layout.attrs.iter().map(|a| a.format.size()).sum::<usize>() as i32;
for VertexAttribute { name, format } in layout.attrs {
let attr_loc = unsafe { self.gl.get_attrib_location(program, name) };
let stride = layout.stride.unwrap_or(stride);
let divisor = if layout.step == VertexStep::PerVertex {
0
} else {
layout.step_rate
};
if let Some(attr_loc) = attr_loc {
internal.push(VertexAttributeInternal {
attr_loc,
size: format.components() as i32,
kind: format.to_gl(),
offset,
stride,
divisor,
buffer,
});
}
offset += format.size() as i64
}
}
let id = PipelineId::next();
let pipeline = Pipeline {
layout: internal,
shader,
culling: desc.culling,
winding: desc.winding,
depth: desc.depth,
color: desc.color,
stencil: desc.stencil,
primitive: desc.primitive,
};
self.pipelines.insert(id, pipeline);
id
}
fn pass_read_color_attachment(
&self,
pass: PassId,
mut colors: &mut [color::Rgba8],
) -> Result<(), Error> {
let pass = &self.passes[&pass];
let Some(a) = pass.color else {
return Err(Error::MissingColorAttachment);
};
let tex = self.textures[&a];
let (_, format, pixel_type) = tex.format.into();
unsafe {
self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(pass.fb));
self.gl.read_pixels(
0,
0,
tex.size.w as _,
tex.size.h as _,
format,
pixel_type,
glow::PixelPackData::Slice(colors.as_bytes_mut()),
);
self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None);
}
Ok(())
}
fn pass_read_depth_attachment(
&self,
pass: PassId,
mut colors: &mut [f32],
) -> Result<(), Error> {
let pass = &self.passes[&pass];
let Some(a) = pass.color else {
return Err(Error::MissingDepthAttachment);
};
let tex = self.textures[&a];
let (_, format, pixel_type) = tex.format.into();
unsafe {
self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(pass.fb));
self.gl.read_pixels(
0,
0,
tex.size.w as _,
tex.size.h as _,
format,
pixel_type,
glow::PixelPackData::Slice(colors.as_bytes_mut()),
);
self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None);
}
Ok(())
}
fn buffer<T>(
&mut self,
kind: BufferKind,
usage: BufferUsage,
data: BufferSource<T>,
) -> Result<BufferId, Error> {
let size = match &data {
BufferSource::Slice(data) => mem::size_of_val(*data),
BufferSource::Empty { size, .. } => *size,
};
let handle = unsafe {
let target = kind.to_gl();
let handle = self.gl.create_buffer().map_err(Error::Internal)?;
self.gl.bind_buffer(target, Some(handle));
self.gl.buffer_data_size(target, size as _, usage.to_gl());
if let BufferSource::Slice(data) = data {
self.gl.buffer_sub_data_u8_slice(target, 0, data.as_bytes());
}
handle
};
let id = BufferId::next();
match kind {
BufferKind::Vertex => {
self.vertex_buffers.insert(id, VertexBuffer { handle, size });
}
BufferKind::Index => {
let elem = ElementSize::try_from(mem::size_of::<T>())
.map_err(Error::InvalidIndexElementSize)?;
self.index_buffers.insert(id, IndexBuffer { handle, size, elem });
}
}
Ok(id)
}
fn buffer_update<T>(
&mut self,
buffer: BufferId,
kind: BufferKind,
data: &[T],
) -> Result<(), Error> {
let (handle, size) = match kind {
BufferKind::Index => {
let b = self.index_buffers[&buffer];
(b.handle, b.size)
}
BufferKind::Vertex => {
let b = self.vertex_buffers[&buffer];
(b.handle, b.size)
}
};
if data.len() > size {
return Err(Error::InvalidBufferDataSize(data.len()));
}
let target = kind.to_gl();
unsafe {
self.gl.bind_buffer(target, Some(handle));
self.gl.buffer_sub_data_u8_slice(target, 0, data.as_bytes());
self.gl.bind_buffer(target, None);
}
Ok(())
}
fn buffer_delete(&mut self, id: BufferId, kind: BufferKind) {
match kind {
BufferKind::Index => {
if let Some(buf) = self.index_buffers.remove(&id) {
unsafe { self.gl.delete_buffer(buf.handle) };
}
}
BufferKind::Vertex => {
if let Some(buf) = self.vertex_buffers.remove(&id) {
unsafe { self.gl.delete_buffer(buf.handle) };
}
}
}
}
}
impl EventHandlerBackend for Context {
fn window_resized(&mut self, size: platform::LogicalSize) {
self.window = size;
}
}