Rendering using all resources of GPU

After spending about a day in the office debugging, I figured out that my GPU is not being used for the rendering. With the Intel UHD Graphics 630 I am getting horrendous issues and a 100% workload on that GPU. However, it appears that I have (in my work laptop) an Nvidia Quadro P600.

The thing is, I can’t assume people have a powerful graphics card. Which brings me to my question.
Why is my performance so bad, even though I am not doing that much on the screen?

For background on the project, it is a configurator for a product. all the elements that are onscreen can be configured by the user (adding various options to the sides, change size of frame etc…) You can compare it to a car configurator where you can select the options you would like.

The main framerate issue I am seeing is when I use the transformcontrols (clicking on one of the objects and trying to move them. The gizmo appears when you click on it). Even after I disable that, performance takes a serious hit. Though I don’t know for sure why that is the case.

Edit: Meshes and data is added in the threejs.component.ts and most of the codebehind is in the threejscontroller.ts

I have cloned the repository and run it. It works, but is not very fast, even though my computer has a GTX 1070 card (enabled for my browser in NVidia control panel) and is otherwise very powerful. I think you are right to worry about users with weaker specs.

I do not have full overview of your project, and am unlikely to ever get it. I did check a few things, though.

Your 3D models are of modest size, which is good.

The textures are much larger. Remember that they will be decompressed during use, so while the file size matters for loading, the resolution matters more for the runtime load. See if you can get away with lower resolution for some textures. I wouldn’t surprise me if 256x256 looked just as good as 1024x1024.

Also make sure to use mipmapping, if you don’t already. Mipmapping improves both looks and performance, while doubling the required texture memory, if I am not wrong. Edit: Actually I think I was wrong. Memory required is increased by 50%, if I am not wrong…

You can reduce the number of draw calls drastically by using instancing instead of cloning. That would be trivial for the floor boards etc.

1 Like

Yeah, I have no real idea why it is so slow. if its even slow with a 1070 than I’ve got more of a problem than I thought. The textures are completely tileable, so I could lower their resolution and increase tiling. that’ll reduce that footprint which might also work quite well.

I am not sure if I am using mipmaps, I would guess I am since all my textures are power of 2, but I am not doing much to get it to run using this.

I am currently not cloning anything, there are 8 meshes in the scene. Which confuses me even further (unless you mean sub-objects of my fbx files). But this will need to be changed once I make the size variable.

How do I discern what is causing my GPU overload? is there any analytic tool for threejs that can identify which part of the rendering/computing is bottlenecking?

Edit: Looking at this example, it is probably something I am doing wrong, since they are getting a really good result. This is pretty similar to what I need, but with meshes as well as materials.

:+1:

Oh, I was under the impression you were doing cloning. Do you know if the identical subobjects of the FBX are sharing geometry or have duplicated geometry? Especially if they have duplicated geometry, but anyway, you may want to remove all but one and draw them with instancing instead.

I tried the three.js devtools, but it did not show the objects.

Ah, that one was nice. Mmm…

Ah, now I have another hypothesis. You may be struggling with repeated unnecessary raycasting happening in event handling, since you have many transform controls enabled in the scene. Then rendering itself is not a bottleneck. I think it really shouldn’t be.

I can also tell you there are 3 render passes going on. Actual rendering, an outline pass and antialiasing. I’ll try adding a console log to see if my events are being called too frequently

since you have many transform controls enabled in the scene.

I don’t think I do, I am adding the object (attach) to the transformcontrol and when I click on something else I detach the object in that transformcontrol and attach the new one. Or am I understanding wrongly how that works?

No, I think you are right and my hypothesis was wrong. :slight_smile:

This is from Chrome performance profiler (recommended!) after orbiting around for 13 seconds without clicking anything:

I got very low FPS. Note that I was running this with the dev server. I suspect that instant-update thing to be the culprit. Do you have the same issues with a cleaner production server setup?

I have tried running it after building just now and got the same results, kind of expected given the tax on gpu instead of cpu.

I have tried something with my window, I made it quarter of screen in size, that seemed to have a significant impact on the runtime. Making it run quite a bit better. I really need to know how the example I linked was created. But looking at their github, everything they have is custom written…

In my case I got really low FPS, but also really low GPU load. 4 ms GPU time during 13 seconds, but with only a few FPS, maybe 10. So with the maximal ~144 FPS it would use around 60 ms on rendering, which is still not much. I think we are facing very different problems.

Update: I ran a build and served from dist/ConfiguratorAngular. Now orbiting runs smoothly, but TransformControls still lag horribly.

Update2: The number which I am referring to as “GPU time” above is what the profiler calls “Rendering”. I actually don’t think it is correct to refer to that as GPU time, at least not in the sense that no other time is GPU time. Note that WebGL calls like drawArrays, uniformMatrix4fv and vertexAttribPointer are registrered under the “Scripting” category.

That immense function call chunk load time is making me think I have some recursion going on. Don’t think I do but I have not yet isolated the problem. Not rendering any objects reduces the gpu load by about 30% though… I think my code is broken on multiple levels honestly…

I noticed an oversight (I think), it wasn’t the rendering that was slow.
It was the render function that was being called way too much.

I was calling the render function once per frame, but another time when the orbit camera was changing positions which made it get called twice as much.

That increased the frames but did not get rid of the GPU footprint. I’m at about 90% still but it runs way smoother and now I can actually debug. If you get the chance, can you try running again and check if it runs well for you too?

I think it is a good practice to use requestAnimationFrame whenever requesting a render, because it leaves the “last word” to the browser. It has been some time since I read about that function, but I think it will ignore successive requests with the same callback given.

Rendering the same image repeatedly is wasteful, so if you have control over everything that affects the scene, you may get rid of the whole animation loop and only render when needed.

That is more or less what I concluded as well. I realised I needed to update the scene only when

  • an object moves
  • a material changes
  • an object is added

From these 3 I can call a render funciton on the scene and reduce the amount of calls to the renderer. However this causes a bit of an issue with fbx loading, since it is done async (I assume with a promise) it would not update the scene. Should I in this case update the scene in the onLoad function of my loader or do you have a better idea?

Yes, updating on load makes sense. But again, I think it is best to call the render function indirectly via requestAnimationFrame.

That may be a great speedup. On 100% load, the FPS will start deteriorating, but it will obviously not go above 100% load. 90% load means the GPU is not a bottleneck anymore. At least that is my understanding. I don’t think 90% GPU load on “Intel HD” is that bad. Maybe tricks like reducing texture resolutions and using instanced geometries for the repeated objects will help further.

True about the workload on the GPU, than again my project is not finished and there will be some added complexity. I will downscale my materials and see what that does for the GPU. This might be a problem some time later, but as of right now I can continue working.

Doesnt requestAnimationFrame cause an update loop to be called? Im getting a bunch of calls logged every second, Keep in mind I am using a composer to add some post-processing. (threejscontroller.ts line 69 which is called on the component initilialization and onward from then on)

The usual way requestAnimationFrame is used in examples is that you have a function which updates the frame and (anywhere in the function body) passes itself to requestAnimationFrame. This will cause an asynchronous “loop”. But requestAnimationFrame itself only tells the browser that when it is ready to update its frame, please run the passed function and then update the frame.

1 Like

Ah I see, so running the requestAnimationFrame function without a function parameter. will only execute it once.

No, you run it with a function parameter, but the function you pass to it does not request a new frame. That way you avoid the eternal “looping” behavior.

2 Likes