Skia shader as a background fill variant#1743
Skia shader as a background fill variant#1743LynithDev wants to merge 5 commits intomarc2332:mainfrom
Conversation
|
You can format the code using |
| if let Some(effect) = &self.effect | ||
| && let Some(other_effect) = &other.effect | ||
| { | ||
| std::ptr::eq(effect.inner(), other_effect.inner()) |
There was a problem hiding this comment.
This comparison won't really work if someone decides to compile the same shader source twice.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1743 +/- ##
==========================================
+ Coverage 60.65% 61.19% +0.53%
==========================================
Files 305 311 +6
Lines 38577 39508 +931
==========================================
+ Hits 23400 24177 +777
- Misses 15177 15331 +154 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| #[derive(Debug, Default, Clone, PartialEq)] | ||
| pub struct UniformsBuilder { | ||
| uniforms: std::collections::HashMap<String, UniformValue>, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq)] | ||
| pub enum UniformValue { | ||
| // Scalar floats | ||
| Float(f32), // float | ||
| Float2(f32, f32), // float2 | ||
| Float3(f32, f32, f32), // float3 | ||
| Float4(f32, f32, f32, f32), // float4 | ||
|
|
||
| // Scalar integers | ||
| Int(i32), // int | ||
| Int2(i32, i32), // int2 | ||
| Int3(i32, i32, i32), // int3 | ||
| Int4(i32, i32, i32, i32), // int4 | ||
|
|
||
| // Boolean types | ||
| Bool(bool), // bool | ||
| Bool2(bool, bool), // bool2 | ||
| Bool3(bool, bool, bool), // bool3 | ||
| Bool4(bool, bool, bool, bool), // bool4 | ||
|
|
||
| // Matrices | ||
| Mat2([f32; 4]), // float2x2 | ||
| Mat3([f32; 9]), // float3x3 | ||
| Mat4([f32; 16]), // float4x4 | ||
|
|
||
| // Arrays | ||
| FloatArray(Vec<f32>), | ||
| IntArray(Vec<i32>), | ||
| BoolArray(Vec<bool>), | ||
| } | ||
|
|
||
| impl UniformsBuilder { | ||
| #[inline] | ||
| pub fn new() -> Self { | ||
| Self::default() | ||
| } | ||
|
|
||
| /// Set a uniform value. | ||
| pub fn set(&mut self, name: impl AsRef<str>, value: UniformValue) { | ||
| self.uniforms.insert(name.as_ref().to_string(), value); | ||
| } | ||
|
|
||
| pub fn build(self, shader: &RuntimeEffect) -> Vec<u8> { | ||
| let mut values = Vec::new(); | ||
|
|
||
| for uniform in shader.uniforms() { | ||
| let value = self.uniforms.get(uniform.name()).unwrap_or_else(|| { | ||
| panic!( | ||
| "Uniform '{}' not found for shader. Available uniforms: {:?}", | ||
| uniform.name(), | ||
| self.uniforms.keys().collect::<Vec<_>>() | ||
| ) | ||
| }); | ||
|
|
||
| Self::push_uniform(value, &mut values); | ||
| } | ||
|
|
||
| values | ||
| } | ||
|
|
||
| #[rustfmt::skip] | ||
| fn push_uniform(value: &UniformValue, values: &mut Vec<u8>) { | ||
| macro_rules! push_f32 { | ||
| ($x:expr) => { values.extend($x.to_le_bytes()) }; | ||
| } | ||
| macro_rules! push_i32 { | ||
| ($x:expr) => { values.extend($x.to_le_bytes()) }; | ||
| } | ||
| macro_rules! push_bool32 { | ||
| ($x:expr) => { values.extend(($x as u32).to_le_bytes()) }; | ||
| } | ||
|
|
||
| match value { | ||
| // --- Scalars & Vectors --- | ||
| UniformValue::Float(f) => push_f32!(*f), | ||
| UniformValue::Float2(x,y) => { push_f32!(*x); push_f32!(*y); }, | ||
| UniformValue::Float3(x,y,z) => { push_f32!(*x); push_f32!(*y); push_f32!(*z); }, | ||
| UniformValue::Float4(x,y,z,w) => { push_f32!(*x); push_f32!(*y); push_f32!(*z); push_f32!(*w); }, | ||
|
|
||
| UniformValue::Int(i) => push_i32!(*i), | ||
| UniformValue::Int2(x,y) => { push_i32!(*x); push_i32!(*y); }, | ||
| UniformValue::Int3(x,y,z) => { push_i32!(*x); push_i32!(*y); push_i32!(*z); }, | ||
| UniformValue::Int4(x,y,z,w) => { push_i32!(*x); push_i32!(*y); push_i32!(*z); push_i32!(*w); }, | ||
|
|
||
| UniformValue::Bool(b) => push_bool32!(*b), | ||
| UniformValue::Bool2(x,y) => { push_bool32!(*x); push_bool32!(*y); }, | ||
| UniformValue::Bool3(x,y,z) => { push_bool32!(*x); push_bool32!(*y); push_bool32!(*z); }, | ||
| UniformValue::Bool4(x,y,z,w) => { push_bool32!(*x); push_bool32!(*y); push_bool32!(*z); push_bool32!(*w); }, | ||
|
|
||
| // Matrices (column-major order) | ||
| UniformValue::Mat2(m) => { | ||
| push_f32!(m[0]); push_f32!(m[2]); push_f32!(m[1]); push_f32!(m[3]); | ||
| }, | ||
| UniformValue::Mat3(m) => { | ||
| push_f32!(m[0]); push_f32!(m[3]); push_f32!(m[6]); | ||
| push_f32!(m[1]); push_f32!(m[4]); push_f32!(m[7]); | ||
| push_f32!(m[2]); push_f32!(m[5]); push_f32!(m[8]); | ||
| } | ||
| UniformValue::Mat4(m) => { | ||
| push_f32!(m[0]); push_f32!(m[4]); push_f32!(m[8]); push_f32!(m[12]); | ||
| push_f32!(m[1]); push_f32!(m[5]); push_f32!(m[9]); push_f32!(m[13]); | ||
| push_f32!(m[2]); push_f32!(m[6]); push_f32!(m[10]); push_f32!(m[14]); | ||
| push_f32!(m[3]); push_f32!(m[7]); push_f32!(m[11]); push_f32!(m[15]); | ||
| } | ||
|
|
||
| // --- Arrays (recursive) --- | ||
| UniformValue::FloatArray(items) => { | ||
| for f in items { | ||
| Self::push_uniform(&UniformValue::Float(*f), values); | ||
| } | ||
| } | ||
| UniformValue::IntArray(items) => { | ||
| for i in items { | ||
| Self::push_uniform(&UniformValue::Int(*i), values); | ||
| } | ||
| } | ||
| UniformValue::BoolArray(items) => { | ||
| for b in items { | ||
| Self::push_uniform(&UniformValue::Bool(*b), values); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Remove this, after seeing it now I dont see its value
There was a problem hiding this comment.
The alternative to the example above would look a bit like this:
effect.make_shader(
skia_safe::Data::new_copy(
&[bounds.width().to_le_bytes(), bounds.height().to_le_bytes(), 0.0f32.to_le_bytes(), now.elapsed().as_secs_f32().to_le_bytes()].concat(),
),
&[],
None,
)Yes the values must be in that specific order as well, as thats the order the uniforms in the shader were defined with.
I could make an extension function to RuntimeEffect instead which tries to do this using generics if possible
There was a problem hiding this comment.
The alternative to the example above would look a bit like this:
exactly, its very simple stuff
chore(core): Remove UniformsBuilder
|
|
||
| #[derive(Clone)] | ||
| pub struct ShaderFill { | ||
| effect: Option<RuntimeEffect>, |
There was a problem hiding this comment.
| effect: Option<RuntimeEffect>, |
| unsafe impl Send for ShaderFill {} | ||
| unsafe impl Sync for ShaderFill {} |
There was a problem hiding this comment.
| unsafe impl Send for ShaderFill {} | |
| unsafe impl Sync for ShaderFill {} |
| where | ||
| S: ShaderProvider + Send + Sync + 'static, | ||
| { | ||
| let effect = RuntimeEffect::make_for_shader(sksl, None) |
There was a problem hiding this comment.
Why is this instanciation part of ShaderFill ? Just let the user of this trait it himself, no?
| let effect = RuntimeEffect::make_for_shader(sksl, None) |
There was a problem hiding this comment.
RuntimeEffect::make_for_shader can be very heavy and calling it every repaint is a bad idea.
There was a problem hiding this comment.
fair, by you currently create it on every component render, you should allow ShaderFill to take an opt-in RuntimeEffect then too, so that the user can cache it or something?
There was a problem hiding this comment.
The first part is true and yes I don't know how to properly store cache it without the user's input.
I guess it could.
There was a problem hiding this comment.
You could try allowing to pass Rc<RuntimeEffect>, its how I kinda do it in shader_editor.rs
fc6cd4a to
56c8282
Compare
Adds support to use a Skia shader as a background for anything that derives
StyleExt.Also adds a
UniformsBuilderwith (what I think is) every type of Uniform SKSL accepts.Example usage: