-
Notifications
You must be signed in to change notification settings - Fork 6k
Implement hashCode and == for FragmentShader #28353
Conversation
| 'FragmentShader floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); | ||
| } | ||
| _update(floatUniforms); | ||
| _hashCode = hashValues(_spirvHash, hashList(floatUniforms)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How expensive are these precalculated hash values? Can this be done lazily? (If we want to implement == correctly then we'd need to save the buffers any way.
flar
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that for correctness we should either drop back to using identity for == or go all the way and keep the data around for eventual array comparison.
From some of the comments it appears that developers might be pre-disposed to creating and saving these shaders for reuse so the opportunity cost of requiring identity == for the optimizations might be favorable? If so, then let's just go back to identity ==.
Another point to consider is that if apps frequently recreate the objects with different uniforms on most all "reuses" then a full-on comparison might be wasted effort?
(Also, consider that we only recently implemented == for some of the shader/filter objects in the past couple of years, so there is a history of identity comparisons in the framework).
If you agree, then perhaps turn this PR into an implementation comment that a full == implementation would be expensive.
On another front, one of the tasks on the DisplayList follow-up list (flutter/flutter#85737) is to stop turning these Flutter objects immediately into Skia objects at the native level, but only do so on the fly at the point of first use. If that gets done, then we might be keeping these arrays around at the native level and be able to implement a native == implementation that does array comparisons.
Also, I'm curious how large these arrays are in practice? Do we have any data on how much shader language info apps would tend to have?
|
I notice that there is an update method for the uniforms. This would create problems for the framework which tends to assume that Shader/Filter objects are immutable. These objects get stored in the framework and reused on subsequent frames. If their values can change without any reconstruction of the associated widgets then that could cause a problem. I would consider using a constructor to construct a new shader from an existing shader which reuses its program, but contains new uniforms. |
|
Tagging @Hixie on the issue of FragmentShader objects implementing the mutable method "update(new uniform values)" and how that might break assumptions in the framework. |
|
@flar is correct that Shaders should be immutable. Separate from this, there's the extra issue that you can't implement hashCode and operator== on a mutable class in any case. That's just semantically incorrect. Equality of objects must be consistent in time. |
|
My suggestion for hashcode/== would be to back off to identity implementations for now and to change the update() method to any of the following:
Note that if you break the program out into a separate object then hashcode/== could be implemented with identity on the program and array comparison on the uniforms. This would assume that developers would cache the program for reuse since it has a (potentially) expensive translation and so identity hash and == would work well for it, but if they frequently change the uniforms and the uniform arrays are small, then keeping them stored in the Dart object would allow us to hash and compare them directly. |
|
(Sorry for so many edits to my last comment. I'm done with the rewrites now.) Another way to view this is that things like ImageFilter.blur are specifying a specific predetermined program and giving it new values that tend to be implemented as uniforms. ImageShader is a specific program that only needs an image to bind as the source pixels and a few parameters that get turned into uniforms for that program. As such, FragmentShader(Program, ...uniforms...) becomes an instantiation of a specific program specified elsewhere (the Program constructor) with a given set of uniforms. This then mirrors the built-in GPU-shader-based classes better. Also, one could potentially replace the FragmentShader constructor with |
|
At this point I'm leaning strongly towards having FragmentProgram be its own class and towards having it be a factory class for the Shader objects themselves instead of a FragmentShader class with a constructor. This would help steer the developer towards considering the Program as something they create and reuse (since it is a factory class), and we could implement == on the constructed shaders easily by remembering the array of uniforms.
Thoughts on this style @Hixie ? |
|
Seems reasonable. |
|
Thank you both for looking into this, I appreciate the examples! It sounds like using a factory pattern is the way to go given that In the meantime, I think it would make sense to close this PR.
That makes sense - is this written down somewhere? I was following the Dart documentation for |
|
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#common-boilerplates-for-operator--and-hashcode mentions "If you do override equality, you should use @immutable on the class hierarchy in question" but in general the reason you don't want operator== or hashCode to ever change during the lifetime of an object is that they mostly exist to allow the objects to be put in Maps or Sets, and if they change identity while in a Map or Set things will just break horribly (e.g. you won't be able to find the instance anymore since its hash code will be different). |
FragmentShader now implements hashCode and the
==operator.flutter/flutter#89082
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.