Loading up PStats, it looks like most of the time is spent in “Frame->Draw->window1->render#0” (in the GPU graph).
A bit of experimentation seems to suggest that it’s caused by the use of the “M_relative” mouse-mode, perhaps in combination with the “cursor-hidden” setting and/or “base.win.movePointer”.
That’s pretty fascinating. Okay, I’m not sure what to do about that. Certainly the shadow casting resolution doesn’t have zero performance impact, I can see on my own system that the shadow caster resolution does indeed change the fidelity of the shadows.
I think you could get a small performance improvement from changing ‘multisamples 4’ to ‘multisamples 2’. This is also, I suppose, just a theory though.
I can set my own shadow caster to a ridiculous (True, 16384, 16384). What happens here is somewhat interesting. The program starts in a long hitch at a reported 56 fps (though with obvious millisecond lag) and then jumps to a steady 60 after a few seconds.
What did help was changing the window-size to 800x600–although even there the shadow-caster incurs a noticeable hit. (From a frame-rate of 90-odd to around 62.) That implies to me a fill-rate issue, either on the side of my graphics card or of your code.
That is curious! Perhaps either Panda or the graphics-card drivers are cutting it down to whatever maximum they have?
The failure condition has not appeared for me after ~10 full system restarts with a clean pycache. My theory about why this works is that loading, clearing, and then re-loading the physics cleanup procedure does something with working memory that makes the process faster the 2nd time. Feel free to post a report here if you notice the issue persists.
This may be of some interest to people who use Bullet. I changed the player’s mass to the small value of 0.1 to greatly reduce teleporting glitches, especially with stacked boxes. Tiny mass values like this seem to have practical benefit when using the BulletCharacterControllerNode.
Setting the mass at this value also prevents the player from immediately dropping to floor height from a short fall, instead allowing a smooth parabolic fall. For reference this occurs with a standard gravity value of -9.81
Added gamepad support for all sample program functions
This works without a menu by automatically detecting whether or not you have a gamepad plugged in and available, and if not, defaulting to keyboard + mouse. I’ve only tested this on a twin-stick type controller with two triggers.
Changed Subsurface-type skin texture to Emission-type skin texture on the NPC for more realistic skin shading (I’ve found this technique can approach the RenderPipeline skin shading quality with highly detailed character models)
As a note, I have designed character controller updates which significantly improve precision and smoothness of the physics and welding in this sample program, and these have been made part of a stand-alone module. This module will be released sometime in the near future as part of the tech demo.
Added async definition option for the NPC cleanup, NPC animation, and gun animation routine to improve performance
Added Sequence() option for the cleanup routine
Implemented functionality for both heuristics for Mouse and Gamepad
The option can be enabled by setting self.use_seq_cleanup and self.use_async_cleanup appropriately
This was a rather minimal case of async with no await or blocking as there were no models to load in.
Here’s the top of the new cleanup definition for reference:
def is_npc_1_shot_async():
async def gun_anim():
gun_ctrl = actor_data.arm_handgun.get_anim_control('shoot')
if not gun_ctrl.is_playing():
actor_data.arm_handgun.stop()
actor_data.arm_handgun.play("shoot")
actor_data.arm_handgun.set_play_rate(12.0, 'shoot')
base.taskMgr.add(gun_anim())
Using async def is, I believe, critical to achieving acceptable performance in the FPS case in Panda3D where novel models and logic are frequently loaded in and destroyed – and indeed, async would seem to be the preferred method of “level streaming” used to make, for instance, contiguous open world games, or even a menu that renders live while models are loading in.
For the model loading async case, just add two options:
model = await base.loader.load_model('etc/models/model.gltf', blocking=False)
I do notice that the “cleanup hitch” in this sample program may still occur intermittently, even with async and the older physics loading patch in place. So, the issue is still not entirely resolved – except for with the possible exception of using a Sequence() to control the cleanup.
I have updated the Arena FPS Sample Program to utilize complexpbr for PBR IBL rendering of the scene and for actor hardware skinning support (skeletal animations). I believe that this update increases the quality of the program significantly, and demonstrates how to use complexpbr in a practical program. As always, if you run into any issues using this new setup procedure, post a comment below and I will get back to you. PBR IBL screenshots below.
Side-by-side comparison between last major update and the 06/25/23 update:
This example is very similar to the popular YouTube column “My progress in learning…” At least I noticed that cubes were replaced by balls, textures appeared. I really liked the skybox, I think I’ll try to find time to test the performance compared to simplepbr.