Skip to content

Commit e0349e1

Browse files
committed
use approx equality for prop value updates
1 parent 6c91b7b commit e0349e1

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

Source/DynamicProperties/Props.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,31 +57,68 @@ internal abstract class Prop
5757
internal abstract class Prop<T>(T value) : Prop
5858
{
5959
internal T Value = value;
60+
61+
internal abstract bool UpdateIfChanged(T value);
6062
public override string ToString() => Value.ToString();
6163
}
6264

6365
internal class PropColor(Color value) : Prop<Color>(value)
6466
{
67+
internal override bool UpdateIfChanged(Color value)
68+
{
69+
if (Utils.ApproxEquals(value, Value)) return false;
70+
Value = value;
71+
return true;
72+
}
73+
6574
internal override void Write(int id, MaterialPropertyBlock mpb) => mpb.SetColor(id, Value);
6675
}
6776

6877
internal class PropFloat(float value) : Prop<float>(value)
6978
{
79+
internal override bool UpdateIfChanged(float value)
80+
{
81+
if (Utils.ApproxEqualsRel(value, Value)) return false;
82+
Value = value;
83+
return true;
84+
}
85+
7086
internal override void Write(int id, MaterialPropertyBlock mpb) => mpb.SetFloat(id, Value);
7187
}
7288

7389
internal class PropInt(int value) : Prop<int>(value)
7490
{
91+
internal override bool UpdateIfChanged(int value)
92+
{
93+
if (value == Value) return false;
94+
Value = value;
95+
return true;
96+
}
97+
7598
internal override void Write(int id, MaterialPropertyBlock mpb) => mpb.SetInt(id, Value);
7699
}
77100

78101
internal class PropTexture(Texture value) : Prop<Texture>(value)
79102
{
103+
internal override bool UpdateIfChanged(Texture value)
104+
{
105+
if (ReferenceEquals(value, Value)) return false;
106+
Value = value;
107+
return true;
108+
}
109+
80110
internal override void Write(int id, MaterialPropertyBlock mpb) => mpb.SetTexture(id, Value);
81111
}
82112

83113
internal class PropVector(Vector4 value) : Prop<Vector4>(value)
84114
{
115+
internal override bool UpdateIfChanged(Vector4 value)
116+
{
117+
if (Utils.ApproxEqualsRel(value, Value)) return false;
118+
Value = value;
119+
return true;
120+
}
121+
85122
internal override void Write(int id, MaterialPropertyBlock mpb) => mpb.SetVector(id, Value);
86123
}
87124

@@ -118,17 +155,15 @@ public sealed class Props(int priority) : IDisposable
118155
public void SuppressEagerUpdatesThisFrame()
119156
{
120157
SuppressEagerUpdate = true;
121-
MaterialPropertyManager.Instance.ScheduleLateUpdate(this);
158+
MaterialPropertyManager.Instance?.ScheduleLateUpdate(this);
122159
}
123160

124161
[MethodImpl(MethodImplOptions.AggressiveInlining)]
125162
private void _internalSet<T, TProp>(int id, T value) where TProp : Prop<T>
126163
{
127164
if (props.TryGetValue(id, out var prop)) {
128165
if (prop is TProp typedProp) {
129-
if (EqualityComparer<T>.Default.Equals(value, typedProp.Value)) return;
130-
131-
typedProp.Value = value;
166+
if (!typedProp.UpdateIfChanged(value)) return;
132167

133168
if (!SuppressEagerUpdate) {
134169
OnValueChanged(this);
@@ -179,7 +214,7 @@ internal void Write(int id, MaterialPropertyBlock mpb)
179214
throw new KeyNotFoundException($"property {PropIdToName.Get(id)} not found");
180215
}
181216

182-
MaterialPropertyManager.Instance.LogDebug(
217+
MaterialPropertyManager.Instance?.LogDebug(
183218
$"writing property {PropIdToName.Get(id)} = {prop}");
184219

185220
prop.Write(id, mpb);

Source/DynamicProperties/Utils.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using UnityEngine;
4+
5+
namespace Shabby.DynamicProperties;
6+
7+
public static class Utils
8+
{
9+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
10+
public static bool ApproxEqualsAbs(float a, float b, float eps) =>
11+
Math.Abs(b - a) <= eps;
12+
13+
/// https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
14+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
15+
public static bool ApproxEqualsRel(float a, float b,
16+
float absDiff = 1e-4f, float relDiff = float.Epsilon)
17+
{
18+
if (a == b) return true;
19+
20+
var diff = Math.Abs(a - b);
21+
if (diff < absDiff) return true;
22+
23+
a = Math.Abs(a);
24+
b = Math.Abs(b);
25+
var largest = b > a ? b : a;
26+
return diff <= largest * relDiff;
27+
}
28+
29+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
30+
public static bool ApproxEqualsRel(Vector4 a, Vector4 b,
31+
float absDiff = 1e-4f, float relDiff = float.Epsilon) =>
32+
ApproxEqualsRel(a.x, b.x, absDiff, relDiff) &&
33+
ApproxEqualsRel(a.y, b.y, absDiff, relDiff) &&
34+
ApproxEqualsRel(a.z, b.z, absDiff, relDiff) &&
35+
ApproxEqualsRel(a.w, b.w, absDiff, relDiff);
36+
37+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
38+
public static bool ApproxEquals(Color a, Color b, float eps = 1e-2f) =>
39+
ApproxEqualsAbs(a.r, b.r, eps) && ApproxEqualsAbs(a.g, b.g, eps) &&
40+
ApproxEqualsAbs(a.b, b.b, eps) && ApproxEqualsAbs(a.a, b.a, eps);
41+
}

0 commit comments

Comments
 (0)