use crate::native::gl::*;
use std::{error::Error, fmt::Display};
mod gl;
pub use gl::raw_gl;
#[cfg(target_vendor = "apple")]
mod metal;
pub use gl::GlContext;
#[cfg(target_vendor = "apple")]
pub use metal::MetalContext;
#[derive(Clone, Copy, Debug)]
pub enum UniformType {
Float1,
Float2,
Float3,
Float4,
Int1,
Int2,
Int3,
Int4,
Mat4,
}
impl 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(Debug, Clone)]
pub struct UniformDesc {
pub name: String,
pub uniform_type: UniformType,
pub array_count: usize,
}
#[derive(Debug, 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 {
Float1,
Float2,
Float3,
Float4,
Byte1,
Byte2,
Byte3,
Byte4,
Short1,
Short2,
Short3,
Short4,
Int1,
Int2,
Int3,
Int4,
Mat4,
}
impl VertexFormat {
pub fn components(&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 size_bytes(&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, Debug, Default, PartialEq)]
pub enum VertexStep {
#[default]
PerVertex,
PerInstance,
}
#[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,
pub gl_pass_as_float: bool,
}
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,
gl_pass_as_float: true,
}
}
}
#[derive(Clone, Debug)]
pub struct PipelineLayout {
pub buffers: &'static [BufferLayout],
pub attributes: &'static [VertexAttribute],
}
#[derive(Clone, Debug, Copy)]
pub enum ShaderType {
Vertex,
Fragment,
}
impl Display for ShaderType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Vertex => write!(f, "Vertex"),
Self::Fragment => write!(f, "Fragment"),
}
}
}
#[derive(Clone, Debug)]
pub enum ShaderError {
CompilationError {
shader_type: ShaderType,
error_message: String,
},
LinkError(String),
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 {
match self {
Self::CompilationError {
shader_type,
error_message,
} => write!(f, "{shader_type} shader error:\n{error_message}"),
Self::LinkError(msg) => write!(f, "Link shader error:\n{msg}"),
Self::FFINulError(e) => write!(f, "{e}"),
}
}
}
impl Error for ShaderError {}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum TextureFormat {
RGB8,
RGBA8,
RGBA16F,
Depth,
Depth32,
Alpha,
}
impl TextureFormat {
pub fn size(self, width: u32, height: u32) -> u32 {
let square = width * height;
match self {
TextureFormat::RGB8 => 3 * square,
TextureFormat::RGBA8 => 4 * square,
TextureFormat::RGBA16F => 8 * square,
TextureFormat::Depth => 2 * square,
TextureFormat::Depth32 => 4 * square,
TextureFormat::Alpha => 1 * square,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TextureWrap {
Repeat,
Mirror,
Clamp,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum FilterMode {
Linear,
Nearest,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum MipmapFilterMode {
None,
Linear,
Nearest,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum TextureAccess {
Static,
RenderTarget,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TextureKind {
Texture2D,
CubeMap,
}
#[derive(Debug, Copy, Clone)]
pub struct TextureParams {
pub kind: TextureKind,
pub format: TextureFormat,
pub wrap: TextureWrap,
pub min_filter: FilterMode,
pub mag_filter: FilterMode,
pub mipmap_filter: MipmapFilterMode,
pub width: u32,
pub height: u32,
pub allocate_mipmaps: bool,
pub sample_count: i32,
}
impl Default for TextureParams {
fn default() -> Self {
TextureParams {
kind: TextureKind::Texture2D,
format: TextureFormat::RGBA8,
wrap: TextureWrap::Clamp,
min_filter: FilterMode::Linear,
mag_filter: FilterMode::Linear,
mipmap_filter: MipmapFilterMode::None,
width: 0,
height: 0,
allocate_mipmaps: false,
sample_count: 1,
}
}
}
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub struct ShaderId(usize);
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub(crate) enum TextureIdInner {
Managed(usize),
Raw(RawId),
}
#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
pub struct TextureId(TextureIdInner);
impl TextureId {
pub fn from_raw_id(raw_id: RawId) -> TextureId {
TextureId(TextureIdInner::Raw(raw_id))
}
}
#[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 {
pub fail_op: StencilOp,
pub depth_fail_op: StencilOp,
pub pass_op: StencilOp,
pub test_func: CompareFunc,
pub test_ref: i32,
pub test_mask: u32,
pub write_mask: u32,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum StencilOp {
Keep,
Zero,
Replace,
IncrementClamp,
DecrementClamp,
Invert,
IncrementWrap,
DecrementWrap,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CompareFunc {
Always,
Never,
Less,
Equal,
LessOrEqual,
Greater,
NotEqual,
GreaterOrEqual,
}
type ColorMask = (bool, bool, bool, bool);
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);
pub const MAX_VERTEX_ATTRIBUTES: usize = 16;
pub const MAX_SHADERSTAGE_IMAGES: usize = 12;
#[derive(Clone, Debug)]
pub struct Features {
pub instancing: bool,
pub resolve_attachments: bool,
}
impl Default for Features {
fn default() -> Features {
Features {
instancing: true,
resolve_attachments: true,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CullFace {
Nothing,
Front,
Back,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FrontFaceOrder {
Clockwise,
CounterClockwise,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Comparison {
Never,
Less,
LessOrEqual,
Greater,
GreaterOrEqual,
Equal,
NotEqual,
Always,
}
impl From<Comparison> for GLenum {
fn from(cmp: Comparison) -> Self {
match cmp {
Comparison::Never => GL_NEVER,
Comparison::Less => GL_LESS,
Comparison::LessOrEqual => GL_LEQUAL,
Comparison::Greater => GL_GREATER,
Comparison::GreaterOrEqual => GL_GEQUAL,
Comparison::Equal => GL_EQUAL,
Comparison::NotEqual => GL_NOTEQUAL,
Comparison::Always => GL_ALWAYS,
}
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum Equation {
#[default]
Add,
Subtract,
ReverseSubtract,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum BlendValue {
SourceColor,
SourceAlpha,
DestinationColor,
DestinationAlpha,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum BlendFactor {
Zero,
One,
Value(BlendValue),
OneMinusValue(BlendValue),
SourceAlphaSaturate,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum PrimitiveType {
Triangles,
Lines,
Points,
}
impl From<PrimitiveType> for GLenum {
fn from(primitive_type: PrimitiveType) -> Self {
match primitive_type {
PrimitiveType::Triangles => GL_TRIANGLES,
PrimitiveType::Lines => GL_LINES,
PrimitiveType::Points => GL_POINTS,
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct PipelineParams {
pub cull_face: CullFace,
pub front_face_order: FrontFaceOrder,
pub depth_test: Comparison,
pub depth_write: bool,
pub depth_write_offset: Option<(f32, f32)>,
pub color_blend: Option<BlendState>,
pub alpha_blend: Option<BlendState>,
pub stencil_test: Option<StencilState>,
pub color_write: ColorMask,
pub primitive_type: PrimitiveType,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Pipeline(usize);
impl Default for PipelineParams {
fn default() -> PipelineParams {
PipelineParams {
cull_face: CullFace::Nothing,
front_face_order: FrontFaceOrder::CounterClockwise,
depth_test: Comparison::Always, depth_write: false, depth_write_offset: None,
color_blend: None,
alpha_blend: None,
stencil_test: None,
color_write: (true, true, true, true),
primitive_type: PrimitiveType::Triangles,
}
}
}
#[derive(Clone, Debug)]
pub struct Bindings {
pub vertex_buffers: Vec<BufferId>,
pub index_buffer: BufferId,
pub images: Vec<TextureId>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BufferType {
VertexBuffer,
IndexBuffer,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum BufferUsage {
Immutable,
Dynamic,
Stream,
}
fn gl_buffer_target(buffer_type: &BufferType) -> GLenum {
match buffer_type {
BufferType::VertexBuffer => GL_ARRAY_BUFFER,
BufferType::IndexBuffer => GL_ELEMENT_ARRAY_BUFFER,
}
}
fn gl_usage(usage: &BufferUsage) -> GLenum {
match usage {
BufferUsage::Immutable => GL_STATIC_DRAW,
BufferUsage::Dynamic => GL_DYNAMIC_DRAW,
BufferUsage::Stream => GL_STREAM_DRAW,
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct BufferId(usize);
#[derive(Clone, Copy)]
pub struct ElapsedQuery {
gl_query: GLuint,
}
impl Default for ElapsedQuery {
fn default() -> Self {
Self::new()
}
}
impl ElapsedQuery {
pub fn new() -> ElapsedQuery {
ElapsedQuery { gl_query: 0 }
}
pub fn begin_query(&mut self) {
if self.gl_query == 0 {
unsafe { glGenQueries(1, &mut self.gl_query) };
}
unsafe { glBeginQuery(GL_TIME_ELAPSED, self.gl_query) };
}
pub fn end_query(&mut self) {
unsafe { glEndQuery(GL_TIME_ELAPSED) };
}
pub fn get_result(&self) -> u64 {
0
}
pub fn is_supported() -> bool {
unimplemented!();
}
pub fn is_available(&self) -> bool {
false
}
pub fn delete(&mut self) {
unsafe { glDeleteQueries(1, &self.gl_query) }
self.gl_query = 0;
}
}
pub struct Arg<'a> {
ptr: *const std::ffi::c_void,
element_size: usize,
size: usize,
is_slice: bool,
_phantom: std::marker::PhantomData<&'a ()>,
}
pub enum TextureSource<'a> {
Empty,
Bytes(&'a [u8]),
Array(&'a [&'a [&'a [u8]]]),
}
pub enum BufferSource<'a> {
Slice(Arg<'a>),
Empty { size: usize, element_size: usize },
}
impl<'a> BufferSource<'a> {
pub fn empty<T>(size: usize) -> BufferSource<'a> {
let element_size = std::mem::size_of::<T>();
BufferSource::Empty {
size: size * std::mem::size_of::<T>(),
element_size,
}
}
pub fn slice<T>(data: &'a [T]) -> BufferSource<'a> {
BufferSource::Slice(Arg {
ptr: data.as_ptr() as _,
size: std::mem::size_of_val(data),
element_size: std::mem::size_of::<T>(),
is_slice: true,
_phantom: std::marker::PhantomData,
})
}
pub unsafe fn pointer(ptr: *const u8, size: usize, element_size: usize) -> BufferSource<'a> {
BufferSource::Slice(Arg {
ptr: ptr as _,
size,
element_size,
is_slice: true,
_phantom: std::marker::PhantomData,
})
}
}
pub struct UniformsSource<'a>(Arg<'a>);
impl<'a> UniformsSource<'a> {
pub fn table<T>(data: &'a T) -> UniformsSource<'a> {
Self(Arg {
ptr: data as *const T as _,
size: std::mem::size_of_val(data),
element_size: std::mem::size_of::<T>(),
is_slice: false,
_phantom: std::marker::PhantomData,
})
}
}
#[derive(Debug)]
pub enum ShaderSource<'a> {
Glsl { vertex: &'a str, fragment: &'a str },
Msl { program: &'a str },
}
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq)]
pub enum RawId {
OpenGl(crate::native::gl::GLuint),
#[cfg(target_vendor = "apple")]
Metal(*mut objc::runtime::Object),
}
unsafe impl Send for RawId {}
unsafe impl Sync for RawId {}
#[derive(Clone, Debug, Default)]
pub struct GlslSupport {
pub v130: bool,
pub v150: bool,
pub v330: bool,
pub v300es: bool,
pub v100_ext: bool,
pub v100: bool,
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Backend {
Metal,
OpenGl,
}
#[derive(Clone, Debug)]
pub struct ContextInfo {
pub backend: Backend,
pub gl_version_string: String,
pub glsl_support: GlslSupport,
pub features: Features,
}
impl ContextInfo {
pub fn has_integer_attributes(&self) -> bool {
match self.backend {
Backend::Metal => true,
Backend::OpenGl => {
self.glsl_support.v150 | self.glsl_support.v300es | self.glsl_support.v330
}
}
}
}
pub trait RenderingBackend {
fn info(&self) -> ContextInfo;
fn new_shader(
&mut self,
shader: ShaderSource,
meta: ShaderMeta,
) -> Result<ShaderId, ShaderError>;
fn new_texture(
&mut self,
access: TextureAccess,
data: TextureSource,
params: TextureParams,
) -> TextureId;
fn new_render_texture(&mut self, params: TextureParams) -> TextureId {
self.new_texture(TextureAccess::RenderTarget, TextureSource::Empty, params)
}
fn new_texture_from_data_and_format(
&mut self,
bytes: &[u8],
params: TextureParams,
) -> TextureId {
self.new_texture(TextureAccess::Static, TextureSource::Bytes(bytes), params)
}
fn new_texture_from_rgba8(&mut self, width: u16, height: u16, bytes: &[u8]) -> TextureId {
assert_eq!(width as usize * height as usize * 4, bytes.len());
self.new_texture_from_data_and_format(
bytes,
TextureParams {
kind: TextureKind::Texture2D,
width: width as _,
height: height as _,
format: TextureFormat::RGBA8,
wrap: TextureWrap::Clamp,
min_filter: FilterMode::Linear,
mag_filter: FilterMode::Linear,
mipmap_filter: MipmapFilterMode::None,
allocate_mipmaps: false,
sample_count: 1,
},
)
}
fn texture_params(&self, texture: TextureId) -> TextureParams;
fn texture_size(&self, texture: TextureId) -> (u32, u32) {
let params = self.texture_params(texture);
(params.width, params.height)
}
unsafe fn texture_raw_id(&self, texture: TextureId) -> RawId;
fn texture_update(&mut self, texture: TextureId, bytes: &[u8]) {
let (width, height) = self.texture_size(texture);
self.texture_update_part(texture, 0 as _, 0 as _, width as _, height as _, bytes)
}
fn texture_set_filter(
&mut self,
texture: TextureId,
filter: FilterMode,
mipmap_filter: MipmapFilterMode,
) {
self.texture_set_min_filter(texture, filter, mipmap_filter);
self.texture_set_mag_filter(texture, filter);
}
fn texture_set_min_filter(
&mut self,
texture: TextureId,
filter: FilterMode,
mipmap_filter: MipmapFilterMode,
);
fn texture_set_mag_filter(&mut self, texture: TextureId, filter: FilterMode);
fn texture_set_wrap(&mut self, texture: TextureId, wrap_x: TextureWrap, wrap_y: TextureWrap);
fn texture_generate_mipmaps(&mut self, texture: TextureId);
fn texture_resize(&mut self, texture: TextureId, width: u32, height: u32, bytes: Option<&[u8]>);
fn texture_read_pixels(&mut self, texture: TextureId, bytes: &mut [u8]);
fn texture_update_part(
&mut self,
texture: TextureId,
x_offset: i32,
y_offset: i32,
width: i32,
height: i32,
bytes: &[u8],
);
fn new_render_pass(
&mut self,
color_img: TextureId,
depth_img: Option<TextureId>,
) -> RenderPass {
self.new_render_pass_mrt(&[color_img], None, depth_img)
}
fn new_render_pass_mrt(
&mut self,
color_img: &[TextureId],
resolve_img: Option<&[TextureId]>,
depth_img: Option<TextureId>,
) -> RenderPass;
fn render_pass_texture(&self, render_pass: RenderPass) -> TextureId {
let textures = self.render_pass_color_attachments(render_pass);
#[allow(clippy::len_zero)]
if textures.len() == 0 {
panic!("depth-only render pass");
}
if textures.len() != 1 {
panic!("multiple render target render pass");
}
textures[0]
}
fn render_pass_color_attachments(&self, render_pass: RenderPass) -> &[TextureId];
fn delete_render_pass(&mut self, render_pass: RenderPass);
fn new_pipeline(
&mut self,
buffer_layout: &[BufferLayout],
attributes: &[VertexAttribute],
shader: ShaderId,
params: PipelineParams,
) -> Pipeline;
fn apply_pipeline(&mut self, pipeline: &Pipeline);
fn delete_pipeline(&mut self, pipeline: Pipeline);
fn new_buffer(&mut self, type_: BufferType, usage: BufferUsage, data: BufferSource)
-> BufferId;
fn buffer_update(&mut self, buffer: BufferId, data: BufferSource);
fn buffer_size(&mut self, buffer: BufferId) -> usize;
fn delete_buffer(&mut self, buffer: BufferId);
fn delete_texture(&mut self, texture: TextureId);
fn delete_shader(&mut self, program: ShaderId);
fn apply_viewport(&mut self, x: i32, y: i32, w: i32, h: i32);
fn apply_scissor_rect(&mut self, x: i32, y: i32, w: i32, h: i32);
fn apply_bindings_from_slice(
&mut self,
vertex_buffers: &[BufferId],
index_buffer: BufferId,
textures: &[TextureId],
);
fn apply_bindings(&mut self, bindings: &Bindings) {
self.apply_bindings_from_slice(
&bindings.vertex_buffers,
bindings.index_buffer,
&bindings.images,
);
}
fn apply_uniforms(&mut self, uniforms: UniformsSource) {
self.apply_uniforms_from_bytes(uniforms.0.ptr as _, uniforms.0.size)
}
fn apply_uniforms_from_bytes(&mut self, uniform_ptr: *const u8, size: usize);
fn clear(
&mut self,
color: Option<(f32, f32, f32, f32)>,
depth: Option<f32>,
stencil: Option<i32>,
);
fn begin_default_pass(&mut self, action: PassAction);
fn begin_pass(&mut self, pass: Option<RenderPass>, action: PassAction);
fn end_render_pass(&mut self);
fn commit_frame(&mut self);
fn draw(&self, base_element: i32, num_elements: i32, num_instances: i32);
}