gl: start work on frame history
This commit is contained in:
parent
b1beb0e46f
commit
848d87021c
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -458,6 +458,9 @@ checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
|||
[[package]]
|
||||
name = "librashader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librashader-preprocess"
|
||||
|
@ -472,6 +475,7 @@ dependencies = [
|
|||
name = "librashader-presets"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"librashader",
|
||||
"nom",
|
||||
"nom_locate",
|
||||
"thiserror",
|
||||
|
|
|
@ -9,3 +9,4 @@ edition = "2021"
|
|||
thiserror = "1.0.37"
|
||||
nom = "7.1.1"
|
||||
nom_locate = "4.0.0"
|
||||
librashader = { path = "../librashader" }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::error::{ParseErrorKind, ParsePresetError};
|
||||
use crate::parse::{remove_if, Span, Token};
|
||||
use crate::{FilterMode, ScaleFactor, ScaleType, WrapMode};
|
||||
use crate::{ScaleFactor, ScaleType};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::combinator::{eof, map_res};
|
||||
|
@ -12,6 +12,7 @@ use std::fs::File;
|
|||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use librashader::{FilterMode, WrapMode};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Value {
|
||||
|
|
|
@ -2,25 +2,7 @@ use crate::error::ParsePresetError;
|
|||
use std::convert::Infallible;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub enum FilterMode {
|
||||
#[default]
|
||||
Linear = 0,
|
||||
Nearest,
|
||||
Unspecified,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub enum WrapMode {
|
||||
#[default]
|
||||
ClampToBorder = 0,
|
||||
ClampToEdge,
|
||||
Repeat,
|
||||
MirroredRepeat,
|
||||
}
|
||||
use librashader::{FilterMode, WrapMode};
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Default, Copy, Clone, Debug)]
|
||||
|
@ -43,20 +25,6 @@ impl Default for ScaleFactor {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for WrapMode {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"clamp_to_border" => WrapMode::ClampToBorder,
|
||||
"clamp_to_edge" => WrapMode::ClampToEdge,
|
||||
"repeat" => WrapMode::Repeat,
|
||||
"mirrored_repeat" => WrapMode::MirroredRepeat,
|
||||
_ => WrapMode::ClampToBorder,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ScaleType {
|
||||
type Err = ParsePresetError;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
|||
for (index, parameter) in preset.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(parameter.name.clone(), UniformSemantic::Variable(SemanticMap {
|
||||
semantics: VariableSemantics::FloatParameter,
|
||||
index: index as u32
|
||||
index: ()
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
"librashader" = { path = "../librashader" }
|
||||
"librashader" = { path = "../librashader", features = ["opengl"] }
|
||||
"librashader-presets" = { path = "../librashader-presets" }
|
||||
"librashader-preprocess" = { path = "../librashader-preprocess" }
|
||||
"librashader-reflect" = { path = "../librashader-reflect" }
|
||||
|
|
0
librashader-runtime-gl/src/filter.rs
Normal file
0
librashader-runtime-gl/src/filter.rs
Normal file
|
@ -1,14 +1,17 @@
|
|||
mod hello_triangle;
|
||||
mod filter;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::iter::Filter;
|
||||
use std::path::Path;
|
||||
use gl::types::{GLenum, GLint, GLsizeiptr, GLuint};
|
||||
use gl::types::{GLenum, GLint, GLsizei, GLsizeiptr, GLuint};
|
||||
use glfw::Key::P;
|
||||
use rustc_hash::FxHashMap;
|
||||
use spirv_cross::spirv::Decoration;
|
||||
|
||||
use librashader::ShaderSource;
|
||||
use librashader_presets::ShaderPassConfig;
|
||||
use librashader::{ShaderFormat, ShaderSource};
|
||||
use librashader_presets::{ShaderPassConfig, ShaderPreset};
|
||||
use librashader_reflect::back::{CompileShader, ShaderCompilerOutput};
|
||||
use librashader_reflect::back::cross::{GlslangGlslContext, GlVersion};
|
||||
use librashader_reflect::back::targets::{FromCompilation, GLSL};
|
||||
|
@ -18,7 +21,21 @@ use librashader_reflect::reflect::{ReflectSemantics, ReflectShader, ShaderReflec
|
|||
use librashader_reflect::reflect::semantics::{MemberOffset, SemanticMap, TextureSemantics, VariableMeta, VariableSemantics};
|
||||
use librashader_reflect::reflect::{TextureSemanticMap, VariableSemanticMap};
|
||||
|
||||
pub fn load_pass_semantics(uniform_semantics: &mut FxHashMap<String, UniformSemantic>, texture_semantics: &mut FxHashMap<String, SemanticMap<TextureSemantics>>,
|
||||
unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
||||
let shader = gl::CreateShader(stage);
|
||||
gl::ShaderSource(shader, 1, &source.as_bytes().as_ptr().cast(), std::ptr::null());
|
||||
gl::CompileShader(shader);
|
||||
|
||||
let mut compile_status = 0;
|
||||
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status);
|
||||
|
||||
if compile_status == 0 {
|
||||
panic!("failed to compile")
|
||||
}
|
||||
shader
|
||||
}
|
||||
|
||||
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 {
|
||||
return;
|
||||
|
@ -102,14 +119,149 @@ pub struct FilterPass {
|
|||
ubo_ring: Option<RingBuffer<GLuint, 16>>,
|
||||
uniform_buffer: Vec<u8>,
|
||||
push_buffer: Vec<u8>,
|
||||
locations: FxHashMap<String, ParameterLocation>
|
||||
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 {
|
||||
reflections: Vec<ShaderReflection>,
|
||||
compiled: Vec<ShaderCompilerOutput<String, GlslangGlslContext>>,
|
||||
programs: Vec<GLuint>,
|
||||
ubo_location: Location<GLint>,
|
||||
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 {
|
||||
|
@ -144,9 +296,9 @@ pub fn reflect_parameter(pipeline: GLuint, meta: &VariableMeta) -> ParameterLoca
|
|||
}
|
||||
}
|
||||
}
|
||||
// todo: init gl
|
||||
|
||||
pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
||||
impl FilterChain {
|
||||
pub fn load(path: impl AsRef<Path>) -> Result<FilterChain, Box<dyn Error>> {
|
||||
let preset = librashader_presets::ShaderPreset::try_parse(path)?;
|
||||
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
|
||||
let mut texture_semantics: FxHashMap<String, SemanticMap<TextureSemantics>> = Default::default();
|
||||
|
@ -197,19 +349,11 @@ pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
|||
|
||||
let mut filters = Vec::new();
|
||||
|
||||
// initialize passes
|
||||
for (index, (config, source, mut reflect)) in passes.into_iter().enumerate() {
|
||||
let mut semantics = semantics.clone();
|
||||
|
||||
// insert parameters parsed from source
|
||||
// for (index, parameter) in source.parameters.iter().enumerate() {
|
||||
// semantics.uniform_semantics.insert(parameter.id.clone(), UniformSemantic::Variable(SemanticMap {
|
||||
// semantics: VariableSemantics::FloatParameter,
|
||||
// index: 0
|
||||
// }));
|
||||
// }
|
||||
|
||||
let reflection = reflect.reflect(index as u32, &semantics)?;
|
||||
|
||||
let glsl = reflect.compile(GlVersion::V4_60)?;
|
||||
|
||||
let vertex_resources = glsl.context.compiler.vertex.get_shader_resources()?;
|
||||
|
@ -286,6 +430,7 @@ pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
|||
locations.insert(param.id.clone(), reflect_parameter(program, param));
|
||||
}
|
||||
|
||||
|
||||
// eprintln!("{:#?}", semantics);
|
||||
eprintln!("{:#?}", reflection.meta);
|
||||
eprintln!("{:#?}", locations);
|
||||
|
@ -303,10 +448,16 @@ pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
|||
ubo_ring,
|
||||
uniform_buffer,
|
||||
push_buffer,
|
||||
locations
|
||||
locations,
|
||||
|
||||
// no idea if this works.
|
||||
// retroarch checks if feedback frames are used but we'll just init it tbh.
|
||||
framebuffer: Framebuffer::new(1),
|
||||
feedback_framebuffer: Framebuffer::new(1)
|
||||
});
|
||||
}
|
||||
|
||||
eprintln!("{:?}", filters.iter().map(|f| f.program).collect::<Vec<_>>());
|
||||
// let mut glprogram: Vec<GLuint> = Vec::new();
|
||||
// for compilation in &compiled {
|
||||
// // compilation.context.compiler.vertex
|
||||
|
@ -322,23 +473,51 @@ pub fn load(path: impl AsRef<Path>) -> Result<(), Box<dyn Error>>{
|
|||
// non_uniform_semantics: Default::default(),
|
||||
// }));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// todo: apply shader pass
|
||||
// gl3.cpp: 1942
|
||||
|
||||
unsafe fn gl_compile_shader(stage: GLenum, source: &str) -> GLuint {
|
||||
let shader = gl::CreateShader(stage);
|
||||
gl::ShaderSource(shader, 1, &source.as_bytes().as_ptr().cast(), std::ptr::null());
|
||||
gl::CompileShader(shader);
|
||||
|
||||
let mut compile_status = 0;
|
||||
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status);
|
||||
|
||||
if compile_status == 0 {
|
||||
panic!("failed to compile")
|
||||
Ok(FilterChain {
|
||||
passes: filters,
|
||||
semantics,
|
||||
preset,
|
||||
original_history: vec![],
|
||||
history: vec![],
|
||||
feedback: vec![]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// how much info do we actually need?
|
||||
fn frame(&mut self, count: u64, vp: &Viewport, input: &Texture, clear: bool) {
|
||||
|
||||
// todo: deal with the mess that is frame history
|
||||
}
|
||||
shader
|
||||
}
|
||||
|
||||
#[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 {
|
||||
use super::*;
|
||||
|
@ -346,9 +525,9 @@ mod tests {
|
|||
#[test]
|
||||
fn triangle() {
|
||||
let (glfw, window, events, shader, vao) = hello_triangle::setup();
|
||||
// load("../test/basic.slangp")
|
||||
load("../test/slang-shaders/crt/crt-royale.slangp")
|
||||
.unwrap();
|
||||
FilterChain::load("../test/basic.slangp").unwrap();
|
||||
// FilterChain::load("../test/slang-shaders/crt/crt-royale.slangp").unwrap();
|
||||
|
||||
hello_triangle::do_loop(glfw, window, events, shader, vao);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,4 +5,9 @@ edition = "2021"
|
|||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = [ "opengl" ]
|
||||
opengl = ["gl"]
|
||||
|
||||
[dependencies]
|
||||
gl = { version = "0.14.0", optional = true }
|
50
librashader/src/gl.rs
Normal file
50
librashader/src/gl.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use crate::{FilterMode, ShaderFormat, WrapMode};
|
||||
|
||||
impl From<ShaderFormat> for gl::types::GLenum {
|
||||
fn from(format: ShaderFormat) -> Self {
|
||||
match format {
|
||||
ShaderFormat::Unknown => 0 as gl::types::GLenum,
|
||||
ShaderFormat::R8Unorm => gl::R8,
|
||||
ShaderFormat::R8Uint => gl::R8UI,
|
||||
ShaderFormat::R8Sint => gl::R8I,
|
||||
ShaderFormat::R8G8Unorm => gl::RG8,
|
||||
ShaderFormat::R8G8Uint => gl::RG8UI,
|
||||
ShaderFormat::R8G8Sint => gl::RG8I,
|
||||
ShaderFormat::R8G8B8A8Unorm => gl::RGBA8,
|
||||
ShaderFormat::R8G8B8A8Uint => gl::RGBA8UI,
|
||||
ShaderFormat::R8G8B8A8Sint => gl::RGBA8I,
|
||||
ShaderFormat::R8G8B8A8Srgb => gl::SRGB8_ALPHA8,
|
||||
ShaderFormat::A2B10G10R10UnormPack32 => gl::RGB10_A2,
|
||||
ShaderFormat::A2B10G10R10UintPack32 => gl::RGB10_A2UI,
|
||||
ShaderFormat::R16Uint => gl::R16UI,
|
||||
ShaderFormat::R16Sint => gl::R16I,
|
||||
ShaderFormat::R16Sfloat => gl::R16F,
|
||||
ShaderFormat::R16G16Uint => gl::RG16UI,
|
||||
ShaderFormat::R16G16Sint => gl::RG16I,
|
||||
ShaderFormat::R16G16Sfloat => gl::RG16F,
|
||||
ShaderFormat::R16G16B16A16Uint => gl::RGBA16UI,
|
||||
ShaderFormat::R16G16B16A16Sint => gl::RGBA16I,
|
||||
ShaderFormat::R16G16B16A16Sfloat => gl::RGBA16F,
|
||||
ShaderFormat::R32Uint => gl::R32UI,
|
||||
ShaderFormat::R32Sint => gl::R32I,
|
||||
ShaderFormat::R32Sfloat => gl::R32F,
|
||||
ShaderFormat::R32G32Uint => gl::RG32UI,
|
||||
ShaderFormat::R32G32Sint => gl::RG32I,
|
||||
ShaderFormat::R32G32Sfloat => gl::RG32F,
|
||||
ShaderFormat::R32G32B32A32Uint => gl::RGBA32UI,
|
||||
ShaderFormat::R32G32B32A32Sint => gl::RGBA32I,
|
||||
ShaderFormat::R32G32B32A32Sfloat => gl::RGBA32F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapMode> for gl::types::GLenum {
|
||||
fn from(value: WrapMode) -> Self {
|
||||
match value {
|
||||
WrapMode::ClampToBorder => gl::CLAMP_TO_BORDER,
|
||||
WrapMode::ClampToEdge => gl::CLAMP_TO_EDGE,
|
||||
WrapMode::Repeat => gl::REPEAT,
|
||||
WrapMode::MirroredRepeat => gl::MIRRORED_REPEAT
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
#[cfg(feature = "opengl")]
|
||||
pub mod gl;
|
||||
|
||||
use std::convert::Infallible;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -65,6 +68,40 @@ pub enum ShaderFormat {
|
|||
R32G32B32A32Sfloat,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub enum FilterMode {
|
||||
#[default]
|
||||
Linear = 0,
|
||||
Nearest,
|
||||
Unspecified,
|
||||
}
|
||||
|
||||
impl FromStr for WrapMode {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"clamp_to_border" => WrapMode::ClampToBorder,
|
||||
"clamp_to_edge" => WrapMode::ClampToEdge,
|
||||
"repeat" => WrapMode::Repeat,
|
||||
"mirrored_repeat" => WrapMode::MirroredRepeat,
|
||||
_ => WrapMode::ClampToBorder,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub enum WrapMode {
|
||||
#[default]
|
||||
ClampToBorder = 0,
|
||||
ClampToEdge,
|
||||
Repeat,
|
||||
MirroredRepeat,
|
||||
}
|
||||
|
||||
impl FromStr for ShaderFormat {
|
||||
type Err = Infallible;
|
||||
|
||||
|
|
Loading…
Reference in a new issue