-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Option for software skinning in MeshInstance #40313
Option for software skinning in MeshInstance #40313
Conversation
skeleton test.zip |
@volzhs does that issue still occur if you select the mesh and click "make unique"? It looks like the skinning takes place in the MeshInstance, but it alters the mesh itself, so if multiple MeshInstances share a mesh, they will also share an animation. |
@clayjohn "Make Sub-Resources Unique" on mesh does not make a difference. |
cc11895
to
ee57de3
Compare
@volzhs I've just pushed an update that should fix your issue with instances playing the same animation. Let me know if everything still works fine for you when you have a chance to test it. |
@pouleyKetchoupp yes! it definitely fixed my issue. 👍 |
oh... unfortunately, I found another issue. mesh_instance.mesh = load(another_mesh_path) and got error when trying to update its material mesh_instance.material_override = material
I found workaround. mesh_instance.mesh = load(another_mesh_path)
mesh_instance.software_skinning = false
mesh_instance.software_skinning = true
mesh_instance.material_override = material now all meshes of skeleton are visible again and no error with updating material. |
Ok, the issues with changing meshes and materials shouldn't be too hard to fix, I just haven't spent enough time to check different cases yet. I'm updating the PR description to add the remaining things to do. |
ee57de3
to
e2d196b
Compare
@lawnjelly I've just added you as co-author since you've been helping with the code itself. |
Oh that's ok I didn't do much. Might be able to help in some of the optimization though. Some other things for the todo list:
|
@lawnjelly Thanks! It's all added to the todo list. For switching the fallback between USE_SKELETON_SOFTWARE and the new software skinning, I would love some help :) I'm not so sure where to handle it, and whether it means moving the new software skinning code somewhere deeper on the render side. |
I think we could simply add a 'can_hardware_skin' boolean function to the renderers, to be called after they initialize. The renderer detects the hardware caps (this is already done to select the USE_SKELETON_SOFTWARE path). If the function returns false, we automatically select software skinning when the mesh instance is setup. I'll try and add this to my branch / make a PR to yours. |
Ok it turned out it was very simple to get querying the renderer working, so simple I'll just write it here:
Then in mesh_instance.cpp add this line where required (could be done in
You will also need to add this to the mesh_instance.cpp headers:
This is assuming that visual server globals is okay to use from here, and isn't better called indirectly (I'll check that with reduz when we get around to it, as there is an indirection layer in the visual server for threading but it shouldn't be much more involved). The arvr_interface_gdnative seems to also use it in this way, but that's no guarantee it is best. |
For project settings, add to visual_server.cpp, line 2435:
These can be accessed in mesh_instance.cpp with
Also add this header to mesh_instance.cpp
All these strings are likely to be slow, so that increases the case for deciding all this as a once off at startup. Not quite sure where the best place for this might be (possibly outside mesh_instance itself), as it is just some setup and storing a fast access bool.. @clayjohn any ideas? |
BTW the latest change you made to the skinning loop resulted in a decrease in FPS from 580fps to 440fps, it is probably interfering with the ability to auto-vectorize, so I've swapped it back to the original in my branch:
I'll have a look at a couple of optimization approaches. |
Did a first look at some optimization. Pre-creating the weights in a linear buffer (instead of reading them from the write buffer) gave a modest increase (560 - 580fps), but I'm not sure it would be worth it with the extra memory use. Removing the branches for the compressed weights and compressed indices didn't yield much. Had a little look at normals, I don't think it will slow things down that much (580fps -> 504fps), however what is less than ideal is that the default format stores normals compressed (8 bit per channel it looks like). It is possible to decompress them, transform then recompress, but it might be better to force the vertex format to use uncompressed normals when using software skinning, which would be more accurate and might be a bit faster. If we were going to support the compressed normal format, SIMD intrinsics might be worth exploring because it has saturated math. In contrast I suspect the main transforms will already be auto-vectorized with SSE2, at least on x86_64, although we might benefit from CPU detection and intrinsics on ARMv7 where neon is optional, on ARMv8 it is mandated so should auto-vectorize. And it is highly likely that x86 machines will support the hardware path anyway. Depending on the model it is possible that looping over the vertex by bone could conceivably be quicker, although it would need testing, I've seen this approach used in the past. It looked unlikely to be quicker if vertices were averaging 4 bones, however in models with a lot of single / double weighted verts it could be interesting to try, though it could potentially be less cache friendly in large models. |
@lawnjelly Thanks for all the detailed feedback! I'll try to find some time for an update in the coming days. Concerning normal compression, it could be as simple as what we're doing for vertices ( |
e2d196b
to
ab08c46
Compare
I've just pushed a new version with:
@lawnjelly @clayjohn Are there more features needed like tangent transformation, or some extra optimization you would like to try? @volzhs I couldn't reproduce your issue with setting the mesh and material after my last changes. Could you please try with this version and provide me with a project if you can still reproduce it? |
NormalsI just had a quick look and a couple of points about transforming the normal... It is potentially a little different to transforming the vertex positions. First you may only need to transform by the rotation (i.e. the 3x3 basis). This is worth testing, I don't really know the shader in detail, it's kind of hard to follow, it seems to be transforming by the whole matrix too... As an example, if you have a normal pointing to the left (-1, 0, 0) then apply a translate with no rotation (4, 3, 0), the normal is now (3, 3, 0) i.e. pointing top right. Which doesn't sound right. Second we should technically be using the transpose of the inverse of the matrix I believe: It so happens that this is the same as the rotation matrix in cases of uniform scale (i.e. the inverse is the transpose), but it may not hold for non uniform scale. From a look at the shader it looks like this is being assumed, so I guess this is okay (but interestingly means that normals in the engine are probably currently wrong with non uniform scale!). SettingsI was wondering whether having a per mesh setting to decide whether to transform normals might be useful, with a default to transform them. I suppose we could have a project setting with 3 enum values - force transform all normals, use per mesh settings, or force no transform for all. Setting whether to transform using software skinning per mesh I wasn't so sure about. I think in practice it will usually be an all or nothing thing, not sure on others views about that. |
Normals: Settings: Maybe for now we could have only global settings, including for transforming normals? I would rather start simple as long as we don't have specific use cases that require finer settings. I can make these project settings enum type with Enabled/Disabled so we can add a third entry "Per-mesh settings" if needed in some scenarios in the future without breaking compatibility. Do you think it would make sense? |
When I get a spare moment I'll try and check this out properly and see if there are any problems with the normals on moving the meshes. It could be that it's fine, but would be nice to confirm this.
Yup sure. Although sometimes when things don't get added on the first go they get forgotten (or it becomes more difficult to get the political will to change) on a subsequent PR. On the other hand having skinning options appear on all mesh instances (that might not be skinned) could be confusing. |
@lawnjelly @pouleyKetchoupp We have 2 paths for transforming normals. By default we do a simple transform that breaks with non-uniform scaling. But we also have the option to use a proper transform if users need non-uniform scaling and are okay with the performance cost godot/drivers/gles3/shaders/scene.glsl Lines 453 to 465 in 89f57ae
Settings IMO forcing software skeleton should be a project setting, but ensuring correct normals should be a per-mesh setting. This way it lines up with the way we have |
47c58a1
to
835182e
Compare
It's updated. For the crash, I've just added a check in |
Looks good to me apart from those minor tweaks I mentioned. I would double check Can't check it out at runtime as I'm away from home but am satisfied from previous versions that it should run good. 👍 |
Option in MeshInstance to enable software skinning, in order to test against the current USE_SKELETON_SOFTWARE path which causes problems with bad performance. Co-authored-by: lawnjelly <[email protected]>
835182e
to
f954471
Compare
@lawnjelly I've made the changes you requested and tested visibility on and off on parent nodes and on mesh instances, everything seems fine. |
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 fine to me, unless clayjohn / akien can see anything else that needs changing.
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 happy with it! Let's get it merged!
Thanks a ton to all involved! :) |
@pouleyKetchoupp : I've tracked down the regression causing the materials not to be working, more by luck than judgement, but if you look in The old code has a If you place a wrapper here:
The regression is gone. Essentially I think it is treating non skinned meshes as though they were. I hesitate to make a PR though, because really I'm unfamiliar with the plumbing of this PR, only the skinning function, so I'll leave it up to you to fix. |
This fixes a regression from PR godotengine#40313 (support for software skinning in MeshInstance). Before, the base mesh was always updated on load even if not skinning was used, which caused mesh instance materials to be reset on the rendering side. Now the base mesh is set only when it has been modified, or when switching software skinning on or off. In this case the mesh instance materials are always updated properly afterwards.
Just a note of something that just occurred to me last night because it is so 'obvious' we may have missed it .. does the skeleton update take account of culling? If not we should fix this. (If not then this is an existing problem with skeleton update, but becomes more pressing with software skinning.) |
@lawnjelly Good call! It doesn't look like Skeleton does anything like that, so it would be a great next step for 3d optimization in general. |
Option in
MeshInstance
to enable software skinning, in order to test against the currentUSE_SKELETON_SOFTWARE
path which causes problems with bad performance.Co-authored by @lawnjelly
More details in #37696.
Remaining things to do around this feature:
mesh
andoverride_material
properties (Option for software skinning in MeshInstance #40313 (comment))