Skip to content

Conversation

@tychedelia
Copy link
Member

We remove the M: Material trait bound in #19667, however we still had an implicit dependency on pbr via MeshPipelineKey and the concrete specializer type that prevents other rendering paths (i.e. 2d) from using our specialization infrastructure.

Here we more completely erase pbr specifics in MaterialProperties by the following pattern:

  • Make specialize_* a world exclusive system.
  • Have an even higher level erased specialization thunk than just the user specializer that accepts &mut World and can look up it's own specializer.

I think there's more opportunity here to make this more ECS-y in the future by using entities to reference the specializer or something similar. But that's an exercise left for later once we work out what additional refactors may be needed to get 2d to work.

Tested a few scenes to confirm things still work.

@tychedelia tychedelia added A-Rendering Drawing game state to the screen C-Code-Quality A section of code that is hard to understand or change D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jan 7, 2026
@ecoskey ecoskey self-requested a review January 7, 2026 04:00
@alice-i-cecile alice-i-cecile requested a review from atlv24 January 7, 2026 04:01
type_id: TypeId,
}

impl ErasedMeshPipelineKey {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not sound:

let me_peek_uninit_bytes = format!(
    "{:?}",
    ErasedMeshPipelineKey::new(MaybeUninit::<u64>::uninit())
);

miri says:

error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C"
  |
  = note: Undefined Behavior occurred here
  = note: (no span available)
  = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
  = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
  = note: BACKTRACE:

We either need more bounds on T, or make this type not pub, or to have useless Debug and no Hash/PartialEq impls. (PartialEq and Hash are also unsound by this same principle)

As far as i know, theres no trait that enforces T to actually be all init: MaybeUninit::uninit() is an init value, because MaybeUninit doesnt have to have init bytes to be init. Theres also padding issues to account for.

This is also UB:

ErasedMeshPipelineKey::new(0u64).downcast::<&u64>();

(I dont know why, but miri doesnt hit the type_id assert, it just hits UB)

Here's a fix to make it safe and sound: tychedelia#5

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay yeah, thanks for checking this. The From impl makes more sense anyway. Tbh this probably could be dyn Any just for maximal flexibility as we only hit it on the slow path. We'd just have to do the same vtable shenanigans that we do for the user key.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 7, 2026

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-22408

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@atlv24
Copy link
Contributor

atlv24 commented Jan 7, 2026

Fixes CI and pixel eagle: tychedelia#6

pub user_specialize: Option<
fn(
&MaterialPipeline,
&dyn Any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is exactly what i wanted but couldnt figure out how to make the implementation work yay

continue;
};
#[derive(SystemParam)]
pub(crate) struct SpecializePrepassSystemParam<'w, 's> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so much nicer extracted into a sys param

Copy link
Contributor

@atlv24 atlv24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfect

}

/// Common [`Material`] properties, calculated for a specific material instance.
#[derive(Default)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets try to keep this Default, can we make base_specialize: Option<BaseSpecializeFn>?

@atlv24 atlv24 mentioned this pull request Jan 8, 2026
pub specialize: Option<
pub base_specialize: Option<BaseSpecializeFn>,
pub prepass_specialize: Option<PrepassSpecializeFn>,
pub user_specialize: Option<
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is intentional, but tychedelia#8 to add this as a type

@alice-i-cecile alice-i-cecile added this to the 0.19 milestone Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Code-Quality A section of code that is hard to understand or change D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants