gl: clean up the huge lib.rs file

This commit is contained in:
chyyran 2022-11-14 01:49:51 -05:00
parent 848d87021c
commit d37fc0ccb5
8 changed files with 452 additions and 274 deletions

7
Cargo.lock generated
View file

@ -97,6 +97,12 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
[[package]]
name = "bytemuck"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
[[package]]
name = "byteorder"
version = "1.4.3"
@ -512,6 +518,7 @@ dependencies = [
name = "librashader-runtime-gl"
version = "0.1.0"
dependencies = [
"bytemuck",
"gl",
"glfw",
"librashader",

View file

@ -1,5 +1,27 @@
# librashader
Pure Rust implementation of the RetroArch shader pipeline as a dynamic library.
A preprocessor, compiler, and runtime for RetroArch 'slang' shaders, rewritten in pure Rust.
Heavily WIP
Heavily WIP.
## License
**There is not yet a functioning implementation of librashader but this section outlines its licensing goals in contrast to
RetroArch.**
While librashader is an independent reimplementation of the RetroArch shader pipeline, referencing the RetroArch source
code was indispensable to its creation. As it is therefore considered a derivative work, the core parts of librashader
such as the preprocessor, the preset parser, the reflection library, and the runtimes, are all licensed under GPLv3.
The librashader C API, i.e. its headers and definitions, *not its implementation in `librashader_capi`*,
are unique to librashader and are more permissively licensed, and may allow you to use librashader in your permissively
licensed or proprietary project.
While the code for `librashader_capi` (`librashader.so` and `rashader.dll`) is still under GPLv3,
you may use librashader in a non-GPL work by linking against the MIT licensed `librashader_ld`,
which implements the librashader C API, and thunks its calls to any `librashader.so` or `rashader.dll`
library found in the load path, *provided that `librashader.so` or `rashader.dll` are distributed under the restrictions
of GPLv3*.
Note that if your project is not compatible with GPLv3, you **can not distribute `librashader.so` or `rashader.dll`**
alongside your project, **only `librashader-ld.so` or `rashader-ld.dll`**, which will do nothing without a librashader
implementation in the load path. The end user must obtain the implementation of librashader themselves.

View file

@ -22,6 +22,15 @@ pub enum VariableSemantics {
FloatParameter = 5,
}
impl VariableSemantics {
pub const fn semantics(self) -> SemanticMap<VariableSemantics, ()> {
SemanticMap {
semantics: self,
index: ()
}
}
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
#[repr(i32)]
pub enum TextureSemantics {
@ -68,6 +77,13 @@ impl TextureSemantics {
pub fn is_array(&self) -> bool {
!matches!(self, TextureSemantics::Original | TextureSemantics::Source)
}
pub const fn semantics(self, index: u32) -> SemanticMap<TextureSemantics, u32> {
SemanticMap {
semantics: self,
index
}
}
}
pub struct TypeInfo {

View file

@ -14,3 +14,4 @@ spirv_cross = "0.23.1"
rustc-hash = "1.1.0"
gl = "0.14.0"
glfw = "0.47.0"
bytemuck = "1.12.3"

View file

@ -0,0 +1,81 @@
use std::iter::Filter;
use gl::types::{GLint, GLuint};
use librashader_reflect::back::cross::GlslangGlslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::ShaderReflection;
use librashader_reflect::reflect::TextureSemanticMap;
use librashader_reflect::reflect::VariableSemanticMap;
use rustc_hash::FxHashMap;
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
use crate::framebuffer::Framebuffer;
use crate::util::{Location, VariableLocation, RingBuffer, Size, Texture};
pub struct FilterPass {
pub reflection: ShaderReflection,
pub compiled: ShaderCompilerOutput<String, GlslangGlslContext>,
pub program: GLuint,
pub ubo_location: Location<GLuint>,
pub ubo_ring: Option<RingBuffer<GLuint, 16>>,
pub uniform_buffer: Box<[u8]>,
pub push_buffer: Box<[u8]>,
pub locations: FxHashMap<String, VariableLocation>,
pub framebuffer: Framebuffer,
pub feedback_framebuffer: Framebuffer,
}
impl FilterPass {
fn build_mvp(buffer: &mut [u8], mvp: &[f32]) {
let mvp = bytemuck::cast_slice(mvp);
buffer.copy_from_slice(mvp);
}
fn build_vec4(buffer: &mut [u8], width: u32, height: u32) {
let vec4 = [width as f32, height as f32, 1.0 / width as f32, 1.0/ height as f32];
let vec4 = bytemuck::cast_slice(&vec4);
buffer.copy_from_slice(vec4);
}
fn build_vec4_uniform(location: Location<GLint>, width: u32, height: u32) {
let vec4 = [width as f32, height as f32, 1.0 / width as f32, 1.0/ height as f32];
unsafe {
if location.vertex >= 0 {
gl::Uniform4fv(location.vertex, 1, vec4.as_ptr());
}
if location.fragment >= 0 {
gl::Uniform4fv(location.fragment, 1, vec4.as_ptr());
}
}
}
fn build_semantics(&mut self, mvp: Option<&[f32]>, fb_size: Size, vp_size: Size, original: &Texture, source: &Texture) {
if let Some(variable) = self.reflection.meta.variable_meta.get(&VariableSemantics::MVP) {
let mvp = mvp.unwrap_or(&[
2f32, 0.0, 0.0, 0.0,
0.0, 2.0, 0.0, 0.0,
0.0, 0.0, 2.0, 0.0,
-1.0, -1.0, 0.0, 1.0
]);
let (buffer, offset) = match variable.offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, offset)
};
FilterPass::build_mvp(&mut buffer[offset..][..mvp.len()], mvp)
}
if let Some(variable) = self.reflection.meta.variable_meta.get(&VariableSemantics::Output) {
let location = self.locations.get(&variable.id).expect("variable did not have location mapped").location();
if location.fragment >= 0 || location.vertex >= 0 {
FilterPass::build_vec4_uniform(location, fb_size.width, fb_size.height);
} else {
let (buffer, offset) = match variable.offset {
MemberOffset::Ubo(offset) => (&mut self.uniform_buffer, offset),
MemberOffset::PushConstant(offset) => (&mut self.push_buffer, offset)
};
FilterPass::build_vec4(&mut buffer[offset..][..4], fb_size.width, fb_size.height)
}
}
}
}

View file

@ -0,0 +1,126 @@
use gl::types::{GLenum, GLsizei, GLuint};
use librashader::ShaderFormat;
use crate::util;
use crate::util::Size;
pub struct Framebuffer {
pub image: GLuint,
pub size: Size,
pub format: GLenum,
pub max_levels: u32,
pub levels: u32,
pub framebuffer: GLuint,
pub init: bool
}
impl Drop for Framebuffer {
fn drop(&mut self) {
if self.framebuffer != 0 {
unsafe {
gl::DeleteFramebuffers(1, &self.framebuffer);
}
}
if self.image != 0 {
unsafe {
gl::DeleteTextures(1, &self.image);
}
}
}
}
impl Framebuffer {
pub fn new(max_levels: u32) -> Framebuffer {
let mut framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
}
Framebuffer {
image: 0,
size: Size { width: 1, height: 1 },
format: 0,
max_levels,
levels: 0,
framebuffer,
init: false
}
}
fn init(&mut self, mut size: Size, mut format: ShaderFormat) {
if format == ShaderFormat::Unknown {
format = ShaderFormat::R8G8B8A8Unorm;
}
self.format = GLenum::from(format);
self.size = size;
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
// reset the framebuffer image
if self.image != 0 {
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, 0, 0);
gl::DeleteTextures(1, &self.image);
}
gl::GenTextures(1, &mut self.image);
gl::BindTexture(1, self.image);
if size.width == 0 {
size.width = 1;
}
if size.height == 0 {
size.height = 1;
}
self.levels = util::calc_miplevel(size.width, size.height);
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, self.format, size.width as GLsizei, size.height as GLsizei);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, self.image, 0);
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE {
match status {
gl::FRAMEBUFFER_UNSUPPORTED => {
eprintln!("unsupported fbo");
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, 0, 0);
gl::DeleteTextures(1, &self.image);
gl::GenTextures(1, &mut self.image);
gl::BindTexture(1, self.image);
self.levels = util::calc_miplevel(size.width, size.height);
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, gl::RGBA8, size.width as GLsizei, size.height as GLsizei);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, self.image, 0);
self.init = gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
}
_ => panic!("failed to complete: {status}")
}
} else {
self.init = true;
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindTexture(gl::TEXTURE_2D, 0);
}
}
}

View file

@ -1,5 +1,8 @@
mod hello_triangle;
mod filter;
mod filter_pass;
mod util;
mod framebuffer;
use std::collections::HashMap;
use std::error::Error;
@ -9,8 +12,10 @@ use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint};
use glfw::Key::P;
use rustc_hash::FxHashMap;
use spirv_cross::spirv::Decoration;
use filter_pass::FilterPass;
use framebuffer::Framebuffer;
use librashader::{ShaderFormat, ShaderSource};
use librashader::{FilterMode, ShaderFormat, ShaderSource, WrapMode};
use librashader_presets::{ShaderPassConfig, ShaderPreset};
use librashader_reflect::back::{CompileShader, ShaderCompilerOutput};
use librashader_reflect::back::cross::{GlslangGlslContext, GlVersion};
@ -20,6 +25,7 @@ use librashader_reflect::reflect::cross::CrossReflect;
use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, ShaderReflection, UniformSemantic};
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
use librashader_reflect::reflect::{TextureSemanticMap, VariableSemanticMap};
use util::{Location, VariableLocation, RingBuffer, Size, Texture, TextureMeta, Viewport};
unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
let shader = gl::CreateShader(stage);
@ -35,6 +41,7 @@ unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
shader
}
impl FilterChain {
fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic>, texture_semantics: &mut FxHashMap<String, SemanticMap<TextureSemantics>>,
config: &ShaderPassConfig) {
let Some(alias) = &config.alias else {
@ -67,204 +74,9 @@ fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic
semantics: TextureSemantics::PassFeedback,
index
}));
}
pub struct RingBuffer<T, const SIZE: usize> {
items: [T; SIZE],
index: usize
}
impl <T, const SIZE: usize> RingBuffer<T, SIZE>
where T: Copy, T: Default
{
pub fn new() -> Self {
Self {
items: [T::default(); SIZE],
index: 0
}
}
}
impl <T, const SIZE: usize> RingBuffer<T, SIZE> {
pub fn current(&self) -> &T {
&self.items[self.index]
}
pub fn next(&mut self) {
self.index += 1;
if self.index >= SIZE {
self.index = 0
}
}
}
#[derive(Debug)]
pub struct Location<T> {
vertex: T,
fragment: T,
}
#[derive(Debug)]
pub enum ParameterLocation {
Ubo(Location<GLint>),
Push(Location<GLint>),
}
pub struct FilterPass {
reflection: ShaderReflection,
compiled: ShaderCompilerOutput<String, GlslangGlslContext>,
program: GLuint,
ubo_location: Location<GLuint>,
ubo_ring: Option<RingBuffer<GLuint, 16>>,
uniform_buffer: Vec<u8>,
push_buffer: Vec<u8>,
locations: FxHashMap<String, ParameterLocation>,
framebuffer: Framebuffer,
feedback_framebuffer: Framebuffer,
}
pub struct Framebuffer {
image: GLuint,
size: Size,
format: GLenum,
max_levels: u32,
levels: u32,
framebuffer: GLuint,
init: bool
}
impl Drop for Framebuffer {
fn drop(&mut self) {
if self.framebuffer != 0 {
unsafe {
gl::DeleteFramebuffers(1, &self.framebuffer);
}
}
if self.image != 0 {
unsafe {
gl::DeleteTextures(1, &self.image);
}
}
}
}
impl Framebuffer {
pub fn new(max_levels: u32) -> Framebuffer {
let mut framebuffer = 0;
unsafe {
gl::GenFramebuffers(1, &mut framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
}
Framebuffer {
image: 0,
size: Size { width: 1, height: 1 },
format: 0,
max_levels,
levels: 0,
framebuffer,
init: false
}
}
fn init(&mut self, mut size: Size, mut format: ShaderFormat) {
if format == ShaderFormat::Unknown {
format = ShaderFormat::R8G8B8A8Unorm;
}
self.format = GLenum::from(format);
self.size = size;
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
// reset the framebuffer image
if self.image != 0 {
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, 0, 0);
gl::DeleteTextures(1, &self.image);
}
gl::GenTextures(1, &mut self.image);
gl::BindTexture(1, self.image);
if size.width == 0 {
size.width = 1;
}
if size.height == 0 {
size.height = 1;
}
self.levels = calc_miplevel(size.width, size.height);
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, self.format, size.width as GLsizei, size.height as GLsizei);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, self.image, 0);
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
if status != gl::FRAMEBUFFER_COMPLETE {
match status {
gl::FRAMEBUFFER_UNSUPPORTED => {
eprintln!("unsupported fbo");
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, 0, 0);
gl::DeleteTextures(1, &self.image);
gl::GenTextures(1, &mut self.image);
gl::BindTexture(1, self.image);
self.levels = calc_miplevel(size.width, size.height);
if self.levels > self.max_levels {
self.levels = self.max_levels;
}
if self.levels == 0 {
self.levels = 1;
}
gl::TexStorage2D(gl::TEXTURE_2D, self.levels as GLsizei, gl::RGBA8, size.width as GLsizei, size.height as GLsizei);
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, self.image, 0);
self.init = gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE;
}
_ => panic!("failed to complete: {status}")
}
} else {
self.init = true;
}
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::BindTexture(gl::TEXTURE_2D, 0);
}
}
}
pub fn calc_miplevel(width: u32, height: u32) -> u32 {
let mut size = std::cmp::max(width, height);
let mut levels = 0;
while size != 0 {
levels += 1;
size >>= 1;
}
return levels;
}
pub struct FilterChain {
passes: Vec<FilterPass>,
semantics: ReflectSemantics,
preset: ShaderPreset,
original_history: Vec<Framebuffer>,
history: Vec<Texture>,
feedback: Vec<Texture>
}
pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLocation {
fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> VariableLocation {
// todo: support both ubo and pushco
// todo: fix this.
match meta.offset {
@ -275,7 +87,7 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
ParameterLocation::Ubo(Location {
VariableLocation::Ubo(Location {
vertex,
fragment
})
@ -288,7 +100,7 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
let vertex = gl::GetUniformLocation(pipeline, vert_name.as_ptr().cast());
let fragment = gl::GetUniformLocation(pipeline, frag_name.as_ptr().cast());
ParameterLocation::Push(Location {
VariableLocation::Push(Location {
vertex,
fragment
})
@ -297,6 +109,18 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
}
}
}
pub struct FilterChain {
passes: Vec<FilterPass>,
semantics: ReflectSemantics,
preset: ShaderPreset,
original_history: Vec<Framebuffer>,
history: Vec<Texture>,
feedback: Vec<Texture>
}
impl FilterChain {
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
let preset = librashader_presets::ShaderPreset::try_parse(path)?;
@ -326,7 +150,7 @@ impl FilterChain {
// todo: this can probably be extracted out.
for details in &passes {
load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0)
FilterChain::load_pass_semantics(&mut uniform_semantics, &mut texture_semantics, details.0)
}
// add lut params
@ -405,8 +229,8 @@ impl FilterChain {
let size = ubo.size;
let mut ring: RingBuffer<GLuint, 16> = RingBuffer::new();
unsafe {
gl::GenBuffers(16, ring.items.as_mut_ptr());
for buffer in &ring.items {
gl::GenBuffers(16, ring.items_mut().as_mut_ptr());
for buffer in ring.items() {
gl::BindBuffer(gl::UNIFORM_BUFFER, *buffer);
gl::BufferData(gl::UNIFORM_BUFFER, size as GLsizeiptr, std::ptr::null(), gl::STREAM_DRAW);
}
@ -417,17 +241,17 @@ impl FilterChain {
None
};
let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.size as usize).unwrap_or(0)];
let push_buffer = vec![0; reflection.push_constant.as_ref().map(|push| push.size as usize).unwrap_or(0)];
let uniform_buffer = vec![0; reflection.ubo.as_ref().map(|ubo| ubo.size as usize).unwrap_or(0)].into_boxed_slice();
let push_buffer = vec![0; reflection.push_constant.as_ref().map(|push| push.size as usize).unwrap_or(0)].into_boxed_slice();
// todo: reflect indexed parameters
let mut locations = FxHashMap::default();
for param in reflection.meta.parameter_meta.values() {
locations.insert(param.id.clone(), reflect_parameter(program, param));
locations.insert(param.id.clone(), FilterChain::reflect_parameter(program, param));
}
for param in reflection.meta.variable_meta.values() {
locations.insert(param.id.clone(), reflect_parameter(program, param));
locations.insert(param.id.clone(), FilterChain::reflect_parameter(program, param));
}
@ -489,34 +313,36 @@ impl FilterChain {
// how much info do we actually need?
fn frame(&mut self, count: u64, vp: &Viewport, input: &Texture, clear: bool) {
// fn frame(&mut self, count: u64, vp: &Viewport, input: &Texture, clear: bool) {
//
// // todo: make copy
//
// let original = Texture {
// handle: input.handle,
// format: self.preset.shaders.first().,
// size: Size {},
// padded_size: Size {}
// };
// // todo: deal with the mess that is frame history
// }
fn do_final_pass(&mut self, count: u64, vp: &Viewport, input: Texture, clear: bool, mvp: &[f32]) {
// todo: make copy
// todo: get filter info from pass data.
let original = TextureMeta {
texture: input,
filter: gl::LINEAR,
mip_filter: gl::LINEAR_MIPMAP_LINEAR,
wrap_mode: Default::default()
};
// todo: deal with the mess that is frame history
}
}
#[derive(Debug, Copy, Clone)]
struct Viewport {
x: i32,
y: i32,
width: i32,
height: i32
}
#[derive(Debug, Copy, Clone)]
struct Size {
width: u32,
height: u32,
}
#[derive(Debug, Copy, Clone)]
struct Texture {
handle: GLuint,
format: GLenum,
size: Size,
padded_size: Size
}
#[cfg(test)]
mod tests {

View file

@ -0,0 +1,99 @@
use gl::types::{GLenum, GLint, GLuint};
use librashader::WrapMode;
#[derive(Debug, Copy, Clone)]
pub struct Location<T> {
pub vertex: T,
pub fragment: T,
}
#[derive(Debug)]
pub enum VariableLocation {
Ubo(Location<GLint>),
Push(Location<GLint>),
}
impl VariableLocation {
pub fn location(&self) -> Location<GLint> {
match self {
VariableLocation::Ubo(l) | VariableLocation::Push(l) => *l
}
}
}
pub fn calc_miplevel(width: u32, height: u32) -> u32 {
let mut size = std::cmp::max(width, height);
let mut levels = 0;
while size != 0 {
levels += 1;
size >>= 1;
}
return levels;
}
pub struct TextureMeta {
pub texture: Texture,
pub filter: GLenum,
pub mip_filter: GLenum,
pub wrap_mode: WrapMode
}
#[derive(Debug, Copy, Clone)]
pub struct Viewport {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32
}
#[derive(Debug, Copy, Clone)]
pub struct Size {
pub width: u32,
pub height: u32,
}
#[derive(Debug, Copy, Clone)]
pub struct Texture {
pub handle: GLuint,
pub format: GLenum,
pub size: Size,
pub padded_size: Size
}
impl <T, const SIZE: usize> RingBuffer<T, SIZE> {
pub fn current(&self) -> &T {
&self.items[self.index]
}
pub fn next(&mut self) {
self.index += 1;
if self.index >= SIZE {
self.index = 0
}
}
}
pub struct RingBuffer<T, const SIZE: usize> {
items: [T; SIZE],
index: usize
}
impl <T, const SIZE: usize> RingBuffer<T, SIZE>
where T: Copy, T: Default
{
pub fn new() -> Self {
Self {
items: [T::default(); SIZE],
index: 0
}
}
pub fn items(&self) -> &[T; SIZE] {
&self.items
}
pub fn items_mut(&mut self) -> &mut [T; SIZE] {
&mut self.items
}
}