Skip to content
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

[Merged by Bors] - Migrate to encase from crevice #4339

Closed
wants to merge 10 commits into from

Conversation

teoxoy
Copy link
Contributor

@teoxoy teoxoy commented Mar 26, 2022

Objective

Solution

  • Replace vendored crevice with encase

Changelog

Changed StorageBuffer
Added DynamicStorageBuffer
Replaced UniformVec with UniformBuffer
Replaced DynamicUniformVec with DynamicUniformBuffer

Migration Guide

StorageBuffer

removed set_body(), values(), values_mut(), clear(), push(), append()
added set(), get(), get_mut()

UniformVec -> UniformBuffer

renamed uniform_buffer() to buffer()
removed len(), is_empty(), capacity(), push(), reserve(), clear(), values()
added set(), get()

DynamicUniformVec -> DynamicUniformBuffer

renamed uniform_buffer() to buffer()
removed capacity(), reserve()

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Mar 26, 2022
@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen C-Dependencies A change to the crates that Bevy depends on C-Feature A new feature, making something new possible and removed S-Needs-Triage This issue needs to be labelled labels Mar 26, 2022
@teoxoy teoxoy marked this pull request as ready for review May 4, 2022 12:51
Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

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

This is a naturally-big PR, and so I have a bunch of comments, but I do think it is looking good. :) Great stuff!

@@ -28,3 +28,4 @@ bevy_window = { path = "../bevy_window", version = "0.8.0-dev" }
bitflags = "1.2"
# direct dependency required for derive macro
bytemuck = { version = "1", features = ["derive"] }
encase = { git = "https://github.com/teoxoy/encase", features = ["glam"] }
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
encase = { git = "https://github.com/teoxoy/encase", features = ["glam"] }
encase = { git = "https://github.com/teoxoy/encase", features = ["glam"] }

@@ -291,12 +289,15 @@ impl RenderAsset for StandardMaterial {
flags: flags.bits(),
alpha_cutoff,
};
let value_std140 = value.as_std140();

let byte_buffer = [0; <StandardMaterialUniformData as encase::Size>::SIZE.get() as usize];
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'd prefer to be explicit that this is u8 if it is:

Suggested change
let byte_buffer = [0; <StandardMaterialUniformData as encase::Size>::SIZE.get() as usize];
let byte_buffer = [0u8; <StandardMaterialUniformData as encase::Size>::SIZE.get() as usize];

pub struct GpuLights {
// TODO: this comes first to work around a WGSL alignment issue. We need to solve this issue before releasing the renderer rework
Copy link
Contributor

Choose a reason for hiding this comment

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

Hehehe.

data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>,
}

const _: () = assert!(<GpuClusterLightIndexListsUniform as encase::Size>::SIZE.get() <= 16384);
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks kind of magical. Is this just a way of making a compile-time assertion? I'm guessing encase::Size::SIZE.get() is a constant function so can be evaluated at compile time? Checks... yup. Interesting. It looks weird at first, but it is nice to have it as a compile-time assertion. Maybe add a comment to clarify?

Suggested change
const _: () = assert!(<GpuClusterLightIndexListsUniform as encase::Size>::SIZE.get() <= 16384);
// NOTE: Assert at compile time that the GpuClusterLightIndexListsUniform size is within uniform buffer size limits
const _: () = assert!(<GpuClusterLightIndexListsUniform as encase::Size>::SIZE.get() <= 16384);

We don't have to solve it here, but the maximum uniform buffer binding size is platform-/API-/GPU-dependent so I guess ultimately we would want to not use slices for these things but rather use Vecs and 'make the most of the available space' so to speak. We can do that separately though in my opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's a compile time assertion to make sure whenever there are changes to the struct its size will still be less than 16384.
I intended to add a comment but in the end missed it.

joints: Box::new(entity_joints.try_into().unwrap()),
},),
));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you benchmark this in any way? I know @james7132 put quite some effort into optimising this so we need to be careful with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't but it's a copy-paste from the previous SkinnedMeshJoints::build with the only change being that now each entity has its own Vec of joints.

Would I benchmark it via the custom_skinned_mesh example by tracking the FPS?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that line is wrong. What I meant to do is: entity_joints.into_boxed_slice().try_into().unwrap().
I'm getting around 1000FPS on master and around 900-950 on this branch (with the change above).

Any ideas how to improve it?

Copy link

Choose a reason for hiding this comment

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

This really regresses performance, in my test with 500+ skinned meshes the extract_skinned_meshes now takes 24ms+ per frame, before this PR it was only 1.5 - 2ms.

Can we not continue using a single vec like it was before?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have an in-repo test for the performance of this system? If not, what is a simple and minimal way of stressing it? Could we load one skinned Mesh asset and spawn it many times then animate them all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I ended up reverting the SkinnedMesh changes and added a note about the usage of BufferVec with the help of @superdump.

@@ -145,12 +143,15 @@ impl RenderAsset for ColorMaterial {
color: material.color.as_linear_rgba_f32().into(),
flags: flags.bits(),
};
let value_std140 = value.as_std140();

let byte_buffer = [0; <ColorMaterialUniformData as encase::Size>::SIZE.get() as usize];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let byte_buffer = [0; <ColorMaterialUniformData as encase::Size>::SIZE.get() as usize];
let byte_buffer = [0u8; <ColorMaterialUniformData as encase::Size>::SIZE.get() as usize];

]

[licenses]
unlicensed = "deny"
copyleft = "deny"
allow = [
"MIT",
"MIT-0",
Copy link
Contributor

Choose a reason for hiding this comment

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

MIT-0 appears to be MIT with no attribution requirement. I don't think this will be a controversial addition at all, but any license addition is noteworthy and so... @cart ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I saw you guys already had 0BSD which is essentially the same.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah this is pretty much what we're doing with Bevy (and what rust does). I'm cool with this.

@@ -87,8 +87,13 @@ impl RenderAsset for CustomMaterial {
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());

let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
let byte_buffer = [0u8; <Vec4 as encase::Size>::SIZE.get() as usize];

@@ -73,8 +73,13 @@ impl RenderAsset for CustomMaterial {
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());

let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
let byte_buffer = [0u8; <Vec4 as encase::Size>::SIZE.get() as usize];

Also, this seems to be a very common pattern that is possibly worth a small helper function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed (for small writes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems it can't be made into a generic function because you can't (yet) use associated constants in const expressions (for the array length).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, no problem. It's not a big deal.

@@ -70,8 +68,13 @@ impl RenderAsset for CustomMaterial {
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());

let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let byte_buffer = [0; <Vec4 as encase::Size>::SIZE.get() as usize];
let byte_buffer = [0u8; <Vec4 as encase::Size>::SIZE.get() as usize];

.maybe_get_path(BEVY)
.map(|bevy_path| {
let mut segments = bevy_path.segments;
segments.push(BevyManifest::parse_str("render"));
Copy link

Choose a reason for hiding this comment

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

bevy::render::render_resource::encase
segments.push(BevyManifest::parse_str("render"));
segments.push(BevyManifest::parse_str("render_resource"));

}
})
.or_else(|| bevy_manifest.maybe_get_path(BEVY_RENDER))
.map(|bevy_render_path| {
Copy link

Choose a reason for hiding this comment

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

This second map needs to be inside the or_else, otherwise it gets appended to the end of the first code path above using BEVY.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, thanks!

Copy link
Member

@cart cart left a comment

Choose a reason for hiding this comment

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

I'm generally very on board for this. Just a few comments around naming / type exports. I'm specifically interested in @teoxoy's thoughts here. I don't want to merge until we've come to an agreement, but I won't block merging on upstream changes to encase, provided we're on the same page.

@@ -44,6 +44,9 @@ pub use wgpu::{
VertexStepMode,
};

pub use bevy_crevice::*;
pub mod encase {
pub use bevy_encase_derive::ShaderType;
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to export ShaderType (both the derive and the trait) directly at the render_resource level. This will be a relatively common derive, the longer "nested" path is regrettable, and the context of encase isn't something most bevy users should be thinking about.

I'd also prefer it if we renamed this module from encase to shader_type, given that there will almost certainly be cases where users import those types directly. From the perspective of bevy users, using the encase name is less helpful. I understand that this is a form of debranding, but I think we could solve the issue of "visible credit" in a way that doesn't affect UX (ex: adding encase to the bevy readme like we do for our other core libs. the readme is almost certainly more visible than a niche import). I'd like to hash out this point before merging, but I won't block this PR on making that work (as I believe doing that would require adjusting the encase derive impl).

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe alternatively, do the full encase re-export (under the encase name), but then at the render_resource level export everything that will cover normal bevy use cases. This also gives us full control over the namespace / gives us the ability to type alias stuff without stomping on naming assumptions baked into encase.

I've been experimenting with moving the ShaderType export "out" of the blanket encase export, and sadly if you do this theres a "conflict" error:

// in render_resource/mod.rs

pub use bevy_encase_derive::ShaderType;
pub use encase::ShaderType;

This can be fixed by either:

  1. Moving the blanket export up one level (merging it into render_resource). This doesn't seem tenable though because of present and future type conflicts (ex our DynamicUniformBuffer conflicts with encase's).
  2. Remove encase's derive export. Obviously this also isn't tenable because encase needs to be viable as a standalone library.
  3. Add an optional feature flag to disable encase's derive export.

None of those are ideal. (3) feels like a reasonable compromise, but thats solely @teoxoy's call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The following seems to work

pub use self::encase::{ShaderType, Size as ShaderSize};

I think having the full encase re-export and then re-exporting the ShaderType and ShaderSize from it at the root of render_resource is a good approach since it provides the flexibilty of having the full re-export and also the ease of use for ShaderType and ShaderSize.

I'll push a commit with this change - let me know what you think!

Copy link
Member

Choose a reason for hiding this comment

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

Sadly that only currently compiles because the examples are accessing the encase path:
image

Trying to use the render_resource::ShaderType derive results in the the encase version of the derive being used.

Copy link
Member

Choose a reason for hiding this comment

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

Arg wait nevermind my bad. I was seeing a different error message! The examples do need to be updated to use the new paths, but everything appears to be fine. I'll push the tweaks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yeah I somehow missed those. Thanks!

@@ -87,8 +87,13 @@ impl RenderAsset for CustomMaterial {
(render_device, material_pipeline): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>> {
let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32());

let byte_buffer = [0u8; Vec4::SIZE.get() as usize];
Copy link
Member

Choose a reason for hiding this comment

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

Given that Size is now a part of our "global public api", I'd prefer it if this was ShaderSize::SHADER_SIZE. SIZE feels too generic / too easy to conflate with "size of Vec4 type on the CPU".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree with the ShaderSize rename however I'm not sure about ShaderSize::SHADER_SIZE vs ShaderSize::SIZE yet. What do you think? If SHADER_SIZE is not a hard requirement, maybe we could go with the re-export Size as ShaderSize for now?

Copy link
Member

Choose a reason for hiding this comment

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

Definitely not a hard requirement! The type alias is perfectly fine for this pr. The rename from Size->ShaderSize does make it a bit more self documenting on import, but in the actual code I still think X::SIZE is a liability. SIZE is just too generic of a concept. Imo the extra clarity of X::SHADER_SIZE would benefit all encase users (not just bevy).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a solid argument. I used to use the full disambiguation syntax (i.e. <x as encase::Size>::SIZE) but later removed since it's not really ergonomic. I'll rename encase's Size::SIZE to ShaderSize::SHADER_SIZE in the next major release; let me know if you'd like the change to happen sooner since I don't have an ETA on that.

Copy link
Member

Choose a reason for hiding this comment

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

Awesome. Much appreciated! Definitely no rush. I'd prefer it if it happened before Bevy 0.8 (mid july) to avoid breaking people twice, but even if that doesn't work out its not the biggest deal in the world.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label May 18, 2022
@alice-i-cecile alice-i-cecile added this to the Bevy 0.8 milestone May 18, 2022
@alice-i-cecile alice-i-cecile removed the C-Feature A new feature, making something new possible label May 18, 2022
@cart
Copy link
Member

cart commented May 18, 2022

bors r+

@cart
Copy link
Member

cart commented May 18, 2022

Very nice work! I think bevy graphics devs/users are gonna love these changes ❤️

bors bot pushed a commit that referenced this pull request May 18, 2022
# Objective

- Unify buffer APIs
- Also see #4272

## Solution

- Replace vendored `crevice` with `encase`

---

## Changelog

Changed `StorageBuffer`
Added `DynamicStorageBuffer`
Replaced `UniformVec` with `UniformBuffer`
Replaced `DynamicUniformVec` with `DynamicUniformBuffer`

## Migration Guide

### `StorageBuffer`

removed `set_body()`, `values()`, `values_mut()`, `clear()`, `push()`, `append()`
added `set()`, `get()`, `get_mut()`

### `UniformVec` -> `UniformBuffer`

renamed `uniform_buffer()` to `buffer()`
removed `len()`, `is_empty()`, `capacity()`, `push()`, `reserve()`, `clear()`, `values()`
added `set()`, `get()`

### `DynamicUniformVec` -> `DynamicUniformBuffer`

renamed `uniform_buffer()` to `buffer()`
removed `capacity()`, `reserve()`


Co-authored-by: Carter Anderson <[email protected]>
@bors bors bot changed the title Migrate to encase from crevice [Merged by Bors] - Migrate to encase from crevice May 18, 2022
@bors bors bot closed this May 18, 2022
@teoxoy teoxoy deleted the encase branch May 20, 2022 20:32
bors bot pushed a commit that referenced this pull request May 20, 2022
…es (#4816)

# Objective

fixes #4811 (caused by #4339 [[exact change](https://github.com/bevyengine/bevy/pull/4339/files#diff-4bf3ed03d4129aad9f5678ba19f9b14ee8e3e61d6f6365e82197b01c74468b10R712-R721)] - where the buffer type has been changed from `UniformVec` to `BufferVec`)

## Solution

Use uniform buffer usage for `SkinnedMeshUniform` instead of all usages due to the `Default` derive.
@alice-i-cecile alice-i-cecile added the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Jun 4, 2022
james7132 pushed a commit to james7132/bevy that referenced this pull request Jun 7, 2022
…es (bevyengine#4816)

# Objective

fixes bevyengine#4811 (caused by bevyengine#4339 [[exact change](https://github.com/bevyengine/bevy/pull/4339/files#diff-4bf3ed03d4129aad9f5678ba19f9b14ee8e3e61d6f6365e82197b01c74468b10R712-R721)] - where the buffer type has been changed from `UniformVec` to `BufferVec`)

## Solution

Use uniform buffer usage for `SkinnedMeshUniform` instead of all usages due to the `Default` derive.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

- Unify buffer APIs
- Also see bevyengine#4272

## Solution

- Replace vendored `crevice` with `encase`

---

## Changelog

Changed `StorageBuffer`
Added `DynamicStorageBuffer`
Replaced `UniformVec` with `UniformBuffer`
Replaced `DynamicUniformVec` with `DynamicUniformBuffer`

## Migration Guide

### `StorageBuffer`

removed `set_body()`, `values()`, `values_mut()`, `clear()`, `push()`, `append()`
added `set()`, `get()`, `get_mut()`

### `UniformVec` -> `UniformBuffer`

renamed `uniform_buffer()` to `buffer()`
removed `len()`, `is_empty()`, `capacity()`, `push()`, `reserve()`, `clear()`, `values()`
added `set()`, `get()`

### `DynamicUniformVec` -> `DynamicUniformBuffer`

renamed `uniform_buffer()` to `buffer()`
removed `capacity()`, `reserve()`


Co-authored-by: Carter Anderson <[email protected]>
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
…es (bevyengine#4816)

# Objective

fixes bevyengine#4811 (caused by bevyengine#4339 [[exact change](https://github.com/bevyengine/bevy/pull/4339/files#diff-4bf3ed03d4129aad9f5678ba19f9b14ee8e3e61d6f6365e82197b01c74468b10R712-R721)] - where the buffer type has been changed from `UniformVec` to `BufferVec`)

## Solution

Use uniform buffer usage for `SkinnedMeshUniform` instead of all usages due to the `Default` derive.
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-Dependencies A change to the crates that Bevy depends on M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide X-Controversial There is active debate or serious implications around merging this PR
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants