-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Abstraction dev tools #12427
base: main
Are you sure you want to change the base?
Abstraction dev tools #12427
Conversation
.add_systems(Startup, setup) | ||
.add_systems( | ||
Update, | ||
( | ||
customize_text.run_if(resource_changed::<FpsOverlayConfig>), | ||
update_text, | ||
change_visibility.run_if(resource_changed::<DevToolsStore>), |
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 don't like this. This will run every time any tool has been changed, but I can't think of a solution to only run if a specific tool has been changed
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 think we can relax on that somehow by just making a global system that manages this, based on one-shot systems, I'm not sure how effective this can be do though. At the same time, that would be useless without some kind o cache to store what was changed and what was not.
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.
If we had some kind of cache storing what was changed, I don't think there would be any reason for complicating it with the global system. I'd just let the user interact with the cache and use it in a run_if
condition.
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 think this is a fine level of granularity for now: I wouldn't expect DevToolsStore
to change regularly at all.
If it becomes a problem we can also cache a Local
within the system to short-circuit.
crates/bevy_dev_tools/src/lib.rs
Outdated
|
||
/// Unique identifier for [`DevTool`]. | ||
#[derive(Debug, Hash, Eq, PartialEq, Clone)] | ||
pub struct DevToolId(pub Uuid); |
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'm wondering: Why not use a TypeId map instead of Uuid? I think its much more simple.
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.
Do we want to identify each dev tool with a different type? We could use plugin struct for it, but what if plugin adds more than one dev tool?
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 was expecting to identify every tool with a single config type registered to it, but I see that's a similar approach taken by the gizmos multiple configs, and we aren't storing configs in this map by now, so, with what we have now on this PR, the TypeId doesn't make sense.
I can see number of reasons why offering a config type though. For example, we might want to do more than enable and disable things, we might want this DevToolsStore to store entire configurations in the future, like storing the FpsOverlayConfig here, and let the user refer to it from the TypeId (which can be easily created and so).
Co-authored-by: Pablo Reinhardt <[email protected]>
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 finally have the energy to review this: sorry for keeping you waiting! Generally impressed with the polish and thought here, but I think we should try harder to remove the need for UUIDs and try to be more consistent with other parts of the engine.
I like:
- .register_dev_tool
- a central registry
- a way to iterate over all of the devtools
- pulling shared config like "is_enabled" out into the struct
I don't like:
- the need to use UUIDs
- the fact that it's unclear where we should store tool-specific config
I think we should:
- borrow from the [
Asset
] trait heavily, and create a matchingDevTool
trait - use the type that implements
DevTool
to store tool-specific config - add a
DevTool: Reflect
bound: this will be essential for editor use cases and we need to make sure it works now - use TypeId keys
In summary:
- Every dev tool has its own config type, which may be a unit struct.
- The
DevTool
trait is implemented for these types. DevToolConfig
stores both common settings (e.g. is_enabled) and tool specific settings, using aBox<dyn DevTool>
.DevToolsStore
is a wrapper overHashMap<TypeId, DevToolConfig>
.- We add dev tools using
app.init_dev_tool::<D: DevTool>
, with a matchingapp.insert_dev_tool(d: D)
method for when you want to supply config.
Co-authored-by: Alice Cecile <[email protected]>
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.
Looks good! Just a few comments and a request: Can you change the new ui debug overlay to use this abstraction? see #10420
} | ||
} | ||
|
||
impl DevToolConfig { |
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.
Is there a reason why those two impl Blocks aren't together?
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.
Also, I think we might want helper methods to return the inner config as &dyn Reflect and &mut dyn Reflect.
fn insert_dev_tool<D: DevTool>(&mut self, value: D) -> &mut Self { | ||
let dev_tool = DevToolConfig::new(TypeId::of::<D>(), value); |
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.
fn insert_dev_tool<D: DevTool>(&mut self, value: D) -> &mut Self { | |
let dev_tool = DevToolConfig::new(TypeId::of::<D>(), value); | |
fn insert_dev_tool<D: DevTool>(&mut self, tool_config: D) -> &mut Self { | |
let dev_tool = DevToolConfig::new(TypeId::of::<D>(), tool_config); |
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.
Just a better naming here, but can be a opinion question.
I'm not convinced we should have a "dev tool" abstraction like this:
I think, if we were to embrace a generic pattern like this, it should be done at the Plugin level and after significant consideration about "plugin/feature configuration and lifecycles". |
I agree that's a nebulous concept, but at the same time, this crate was created with the goal of more than offering users a simple tool interface: it was meant for helping with the editor development, and IMO this kind of abstraction can help the engine in the future.
Agree with the point of this making change detection looking weird, but at the same time, this resource can help with creating and managing tools at runtime and helping with a global access that maybe can use by the crate itself.
Agreed, we shouldn't change any behavior of any feature we want to show. For example, it wouldn't be acceptable to refactor frame count just to do the fps overlay. But, in this field, if we need a refactoring of some kind, this should be considered more a question of "do the user have enough freedom so they can build something like this from their own code?". So some refactor can maybe be considered more a question of freedom than changing existent behavior.
I think one of the reasons to have this is helping with runtime config: there are tools that are just added and nothing more, so we create this
In fact, in the future, we can maybe add and remove plugins at runtime, which maybe defeats the purpose of this abstraction (not in the sense of a global dev tool store, though), you can just remove plugins added here or even remove systems and there you have disabled the tool. These are very good points about this abstraction, and I think we really need to clarify what a dev tool really is. Mainly things that are meant to debug and we expect to have in the editor? And Users can even add their own, and the editor will handle the rest in some way, I really think this abstraction can help in that kind of sense |
@cart I understand your concerns. I'll aim to write up a proper response and design vision in an RFC, and link it here (and the linked issue). I think there's very good reasons to want a cohesive method of control (even if this might not be the best possible design), but those aren't clearly articulated anywhere. |
Blocked on the RFC |
Objective
Solution
DevTool
that holds unique id and state.DevToolsStore
resource that stores newDevTool
s.