Closed marwie closed 2 months ago
the max particle size is just a preallocated buffer. it auto-resizes with the following code:
if (particleCount > this.maxParticles) {
this.expandBuffers(particleCount);
}
we can make it 1000, too. we don't want to make it too small, so we don't reallocate and copy buffer at the first few frames of the animation too often.
You can also try to do a profiler on Quest to see why it's very slow. The trail renderer is pretty slow in general. I was working on a webGPU implementation of this. I tested I could use webGPU as compute shader API and webGL as renderer at the same time.
Regarding the preallocation and expanding: I understand that. I'm just curious if this could still be exposed (and by default if nothing is set it can still have your predefined values) - I'm just thinking of cases where we have many particle systems in a scene that may just render a small amount of particles (defined by the user). If I understand it correctly these buffers are owned by each particleSystem and not by the BatchedRenderer, right?
I will check with the profiler and let you know when I have any more information
Regarding the preallocation and expanding: I understand that. I'm just curious if this could still be exposed (and by default if nothing is set it can still have your predefined values) - I'm just thinking of cases where we have many particle systems in a scene that may just render a small amount of particles (defined by the user). If I understand it correctly these buffers are owned by each particleSystem and not by the BatchedRenderer, right?
Only BatchedRenderer owns buffers. If the game/app is not optimized, each particle system has its own material and texture, we simulate the movement and shading properties on CPU and write them to batched GPU buffers. In the ideal case for performance, even if you have many particle systems, but many of them could share the same texture atlas and use same shader, so they could be put in a few batches.
Batch renders are only good if the number of draw calls is the bottle neck of the application, which is the case for most WebGL based games. if particle count is the bottle neck, the batch render is not an optimization.
I think then one issue on our side could be that the BatchedRenderer is currently created per ParticleSystem
instead of being created once and shared.
check code in BatchedRenderer.ts, only those conditions are met, we batch them to a single renderer.
private static equals(a: StoredBatchSettings, b: VFXBatchSettings) {
return (
a.material.side === b.material.side &&
a.material.blending === b.material.blending &&
a.material.transparent === b.material.transparent &&
a.material.type === b.material.type &&
a.material.alphaTest === b.material.alphaTest &&
(a.material as any).map === (b.material as any).map &&
a.renderMode === b.renderMode &&
a.uTileCount === b.uTileCount &&
a.vTileCount === b.vTileCount &&
a.instancingGeometry === b.instancingGeometry &&
a.renderOrder === b.renderOrder &&
a.layers.mask === b.layers.mask
);
}
You mean even when creating multiple BatchedParticleRenderers they would still automagically batch particles together if the equals
check is met?
only if when you create multiple ParticleSystems. users can not create batchedRenderer directly
I'm not exactly sure I understand it correctly. Let's say the following code runs e.g. 5 times:
this._batchSystem = new BatchedParticleRenderer();
this._batchSystem.name = this.gameObject.name;
this._container.add(this._batchSystem);
this._interface = new ParticleSystemInterface(this);
this._particleSystem = new _ParticleSystem(this._interface);
this._particleSystem.addBehavior(new SizeBehaviour(this));
this._particleSystem.addBehavior(new ColorBehaviour(this));
this._particleSystem.addBehavior(new TextureSheetAnimationBehaviour(this));
this._particleSystem.addBehavior(new RotationBehaviour(this));
this._particleSystem.addBehavior(new VelocityBehaviour(this));
this._particleSystem.addBehavior(new TrailBehaviour(this));
this._batchSystem.addSystem(this._particleSystem);
The particle mesh + material is the same for all 5 instances. Would the particles be batched together and rendered in a single drawcall despite 5x new BatchedParticleRenderer()
or would this be an anti-pattern and we now loose all optimization of three.quarks and we have 5 draw calls?
I'm not exactly sure I understand it correctly. Let's say the following code runs e.g. 5 times:
this._batchSystem = new BatchedParticleRenderer(); this._batchSystem.name = this.gameObject.name; this._container.add(this._batchSystem); this._interface = new ParticleSystemInterface(this); this._particleSystem = new _ParticleSystem(this._interface); this._particleSystem.addBehavior(new SizeBehaviour(this)); this._particleSystem.addBehavior(new ColorBehaviour(this)); this._particleSystem.addBehavior(new TextureSheetAnimationBehaviour(this)); this._particleSystem.addBehavior(new RotationBehaviour(this)); this._particleSystem.addBehavior(new VelocityBehaviour(this)); this._particleSystem.addBehavior(new TrailBehaviour(this)); this._batchSystem.addSystem(this._particleSystem);
The particle mesh + material is the same for all 5 instances. Would the particles be batched together and rendered in a single drawcall despite 5x
new BatchedParticleRenderer()
or would this be an anti-pattern and we now loose all optimization of three.quarks and we have 5 draw calls?
yeah only BatchedParticleRenderer batches multiple system. you should create a single batchedParticleRenderer in the entire scene.
Ok thank you.
If i want to remove all particles that belong to one particle system: how would i do that? Is removing the particle system from the renderer enough?
Yes. just remove it from the renderer. and remove it from the scene to keep your scene clear as well
Ok thank you.
If i want to remove all particles that belong to one particle system: how would i do that? Is removing the particle system from the renderer enough?
By removing it from the scene you mean the emitter? Will the particles that this system has emitted be removed from being rendered as well when I remove the system from the BatchedRenderer?
yes, remove the particle system emitter from the scene. the particle has emitted will be removed if you remove the system from BatchedRender.
Ok thank you for all the information!
Would it be possible to expose the registered ParticleSystems on the BatchedRenderer?
Like a getter renderer.systems
or getSystems(targetarray)
if you're concerned about users modifying the underlying array
The reason for this is that during pre-warming I call update
on the batched renderer several times - but I only want to prewarm the newly added particle system
Edit: I guess this can already be archieved by iterating the batches
Another question: I have started to refactor the particlesystem to use one shared batch renderer
When testing this in a scene that shoots arrows and each arrow spawns particles until it gets destroyed and removed from the renderer. My logs show that the batch count however stays the same even after all arrows have been destroyed
when starting the scene and shooting first arrows
later when all arrows have been destroyed
do you have an idea what could be causing this? I'm double checking that all particles have the same material and mesh
Or is this expected? These particles all share the same geometry (no textures) - i would have expected this would produce only one particle batch that has multiple systems assigned.
The log belows two numbers: batches with any systems
and in brackets (batchesRenderer.batches.length)
EDIT: please disregard the comment above. I double checked all values here and when stepping into the method i found that the texture check was causing new batches to be created: https://github.com/Alchemist0823/three.quarks/blob/dcb9136226196c75630053b66f846f97e082c97f/src/BatchedRenderer.ts#L90 After fixing this I dont get more batches than expected anymore
Would it be possible to expose the registered ParticleSystems on the BatchedRenderer? Like a getter
renderer.systems
orgetSystems(targetarray)
if you're concerned about users modifying the underlying arrayThe reason for this is that during pre-warming I call
update
on the batched renderer several times - but I only want to prewarm the newly added particle systemEdit: I guess this can already be archieved by iterating the batches
There is a prewarm option in Particle System. does that work in your case? let me know if doesn't. Another contributor added that. I didn't do extensive tests.
Hello,
I recently noticed a huge performance difference on Quest using trail particles - trail particles are currently very slow on Quest to the point where I can't really use them easily in my scenario.
Could that be related to the default maxParticles size being 10x the size of a normal sprite batch? I'm also wondering why the max size is hardcoded for both of these cases? Would it be possible to expose this with the VFXBatchSettings interface? E.g. in my case i want to render a single particle trail following an arrow so I surely don't need that many particles. Also in most cases I'm dealing with particle counts < 100 or < 200
https://github.com/Alchemist0823/three.quarks/blob/dcb9136226196c75630053b66f846f97e082c97f/src/TrailBatch.ts#L38
https://github.com/Alchemist0823/three.quarks/blob/dcb9136226196c75630053b66f846f97e082c97f/src/SpriteBatch.ts#L43