Viewport optimizations

I’m looking for a way to optimize the viewport rendering of large particle / geometry nodes systems.
Here’s what I’ve figure out

  • setting a “is viewport” check and replacing the original geometry with a proxy, but this kind of ruins the idea of previewing what I have.
  • making the instances display as bounding boxes or points - this greatly increases viewport performance and I can still preview what my scene looks like in the viewport.
  • disabling viewport overlays - combined with displaying meshes as boundingboxes, this gives a great boost, yet when I’m working on something larger, the viewport still gets sluggish.

I’ve noticed that it’s not the rendering that is the problem, Cycles handles billions of polygons like it’s nothing. It’s the viewport that is dying on me.

So to sum it all - I’m looking for a good way to preview my scene with all scattered objects in the viewport render with minimal slowdowns. I don’t want to render a still frame just to see the changes I’ve done. Is there anything else I can do, or I’ve pretty muched caped what blender can do for now?

Hi,
have you already considered this?
https://docs.blender.org/manual/en/latest/render/cycles/render_settings/simplify.html

If not, it’s a good starting point

It doesn’t seem like it’s Blender you should be blaming. It’s the GPU. Graphics cards are not designed to be able to process a lot of geometry especially a lot of triangles smaller than a pixel. You could try using Cycles in the viewport but that might not be a super good solution. The only way is to have less geometry in the viewport. You could try having some small region with full density and nothing else in the viewport as well instead of simplifying the particles and having full amount of them. I don’t think there is a perfect solution for this.

yeah, that works only with particle system children and not with lets say geometry nodes. plus like I said, I’d still like to see the actual number of instances while rendering. For example If i’m using V-Ray, scattered objects would be displayed at a fraction of the actual number in max’s /or any other dcc/ viewport but while rendering interactively all the instances are parsed and you can see what you have actually done. So far my solutions have been the things I listed above. I’m going to try just setting up the geo nodes scatter on a smaller surface, then applying it to the actual geometry and hiding it for the viewport.

True, but I’m running a 3090, there isn’t that much place for upgrade there. Like I mentioned in the other response, it would be really nice to be able to limit the visible particles in the viewport, but still see whem while interactive rendering.

When you have a lot of instances sometimes it works better to realize them , especially if you have a placeholder mesh like a box, or a triangle to minimize polycount… This of course increase the GPU memory but the viewport generally get much more responsive.

I think the main issue is that the viewport can’t handle a high number of instances. But when rendering with cycles it works better.
On some scenes cycles viewport is much more responsive than workbench…

1 Like

It would probably be possible to make a node group with single boolean input and then write and add-on that switches it if there is a single viewport and if it’s in rendered mode and back if it’s in another mode, but it would require using message bus which is a bit complicated. You would have to seriously question if there are any people reading this with the skills required to do so and if they could prove it. :smiley:

Actually, its likely not the viewport either, it’s your CPU and on top of that, there’s a fair chance it’s the single core performance of your CPU.

The reason all the things you tried, actually resulted in increase viewport performance, is because every one of them reduced the calculation load on the CPU before the data was then sent to the GPU to show on the screen.

Some Blender optimisation has improved and even multi-thread some stuff, but there are still bottlenecks and it’s possible you picked the two worse ones. The particle system is a decade old, so no real work being done on that (it’s as fast as it’s going to get, outside of just not using Blenders system).
And Geometry Nodes is still fairly new and ever changing that real performance boosts are likely some time away.

Does a viewport in rendered mode handle the scene, or does it lag as well? (I mean, of course it lags, but is that acceptable for rendered preview?)

Would my proposed scenario where it would be possible to determine if it’s rendered viewport mode from geometry nodes work for you? It seems it might be possible.

It seems it’s possible to detect area change by subscribing to bpy.types.WorkSpace and if you change areas, it’s possible to check if you have only one viewport and subscribe to its shading type change, so it seems it would be possible to change a boolean input of a geometry nodes group so it could be used as a node detecting if there is a single viewport and if it’s rendered. So it would be possible to automatically switch a boolean inside any geometry nodes group containing that group, so you could basically have an IsRenderedViewport kind of node that works OK with an add-on written in Python.

yeah, in rendered mode everything works great. I’m surprised how responsive Cycles is. The solution you are giving though is way over my coding skills :smiley: but I’ll look into it more.

that is possible, as I do have an older CPU ( r5 3600x ) I’m going to upgrade it when the ryzen 9xxx series drops in a few weeks/month

That’s the thing - Cycles renders differently than a GPU, it just uses GPU to do the calculations. I suspected it might be more responsive since it process geometry very differently than the viewport. It might even be sort of a solution to just keep working in rendered preview mode, although it might be way less stable.

Luckily, there is something very wrong with my head, and I just cannot let it go :smiley: , so if you wait a bit, I might write the add-on, if there are no unexpected issues along the way.

1 Like

Yeah, that likely isn’t helping things at all.

Sounds like a plan, that should be around a 40-50% increase in single core processing no matter what new 9000 series CPU you get (maybe helped a bit by new AM5 board and some faster DDR5 RAM.

bl_info = {
    "name": "Is Rendered Viewport Node",
    "author": "Martynas Žiemys",
    "version": (1, 0),
    "blender": (4, 2, 0),
    "location": "Geometry Nodes Group",
    "description": "Geometry Nodes group IsRenderedViewport",
    "warning": "",
    "doc_url": "",
    "category": "Geometry Nodes",
}


import bpy
from bpy.app.handlers import persistent

@persistent
def rendered_viewport_load_handler(dummy):
    if "IsRenderedViewport" not in bpy.data.node_groups:
        node_group = bpy.data.node_groups.new('IsRenderedViewport', 'GeometryNodeTree')
        node_group.interface.new_socket(
            "IsRenderedViewport", 
            in_out="OUTPUT", 
            socket_type='NodeSocketBool'
            )
        outNode = node_group.nodes.new('NodeGroupOutput')        
    
    bpy.msgbus.clear_by_owner(bpy.context.workspace)
    def on_workspace_change(*args):
        print("Workspace changed!")
        bpy.msgbus.clear_by_owner(bpy.context.window_manager)
        group = bpy.data.node_groups["IsRenderedViewport"]
        rendered_input = group.nodes["Group Output"].inputs[0]
        areas = bpy.context.screen.areas
        views = [area for area in areas if area.type == 'VIEW_3D']
        if len(views) == 1:
            def on_shading_change(*args):
                print("Shading changed!")
                group = bpy.data.node_groups["IsRenderedViewport"]
                rendered_input = group.nodes["Group Output"].inputs[0]
                areas = bpy.context.screen.areas
                views = [area for area in areas if area.type == 'VIEW_3D']
                if views[0].spaces[0].shading.type == 'RENDERED':
                    if not rendered_input.default_value:
                        rendered_input.default_value = True
                else:
                    if rendered_input.default_value:
                        rendered_input.default_value = False
                            
            bpy.msgbus.subscribe_rna(
                key=views[0].spaces[0].shading.path_resolve("type", False), 
                owner=bpy.context.window_manager,
                args=(1, 2, 3),
                notify=on_shading_change,
            )
        else:
            rendered_input.default_value = False

    bpy.msgbus.subscribe_rna(
        key=bpy.types.WorkSpace,
        owner=bpy.context.workspace,
        args=(1, 2, 3),
        notify=on_workspace_change,
    )
    on_workspace_change(1,2,3)

def register():
    bpy.app.handlers.load_post.append(rendered_viewport_load_handler)

def unregister():
    bpy.app.handlers.load_post.remove(rendered_viewport_load_handler)


if __name__ == "__main__":
    register()

Save from Blender’s Text Editor(saves correct encoding) as something like is_rendered_viewport.py and install as add-on.

Add node menu, Group submenu:
image

Seems to work:

IsRenderedViewport

As you can see it only detects if it’s a rendered viewport, so you still need to deal with render.
It’s made as a legacy kind of add-on(bl_info in the .py) so should work in 4.1 and might not work in 4.3. Should work when loading a new scene(it creates a new node group in every loaded file if enabled though). Works only with one viewport, it should set the boolean to False if more 3d Viewports are open(you probably don’t want full thing loaded in rendered and in regular viewport).

But in my tests it takes forever to build many instances anyway, so might not be amazing. I think it’s good in cases where you are OK to wait till the instances are made and then want to navigate while they are visible, other way it’s not much different than just rendering.

In you regular viewport you might want to have simpler geometry or less of it and avoid many small triangles in relation to rendered pixel size.

For the future: if anyone wants to improve, change, use, sell or do whatever they want(permitted by GPL) with the code I share, please do. Assume GNU GPL license as required by API’s license and use as intended, don’t ask. :smiley:

1 Like

amazing, thanks a lot!
will test it out later.

It’s weird though. I cannot seem to make it so it’s smoother in Rendered preview than in Material Preview mode no matter what I do. It lags in Rendered viewport more no matter what I do in 4.2. Maybe it’s really your CPU, or something. I am not sure how rendered view can be smoother. Maybe you should update GPU drivers…

Edit: Oh, OK :smiley: Got it to work as expected. With really high poly instances, the viewport is completely non responsive, but still usable in rendered mode. It works :smiley: :smiley: :smiley: It’s not the amount of instances, but the poly count per instance.

Yep, 500 000 tris per Suzanne with 500 000 Suzannes does it for me:

Can move rendered sort of fine(ish), if I switch to material mode, have to close Blender
:smiley: :smiley: :smiley:

Makes no sense to have such dense geometry on the instances though. If it lags in viewport it means, there is way too much geometry per pixel(or per whatever chunk of pixels GPU tries to process at once) and you cannot see it in any case, I think it’s worth to optimize in any case. I think your instances are too high-poly.

See this nice explanation of what is going on: https://youtu.be/hf27qsQPRLQ?t=705

yeah, I understand the difference between the viewport drawing poligons and how Cycles does it. but in many cases it does make sense to have a lot of detail in what you are scattering, whether you are rendering a very high res image, or trying to get more than a single shot out of a scene, If I have the VRam to render it, I’m not going to bother optimizing it :smiley:

It just seems like there may be way too much geometry, not like slightly too much. Many times too much. Might bring unnecessary struggle while working, that’s all. Of course, computer work hours is better than your work hours… But is that the case?.. Well, I have no idea about the actual specifics, I am sure you are in a better position to decide, but it’s worth considering. In my test case that’s like 500 times too much though, sure it’s extreme, but even 20 times too much is still kind of really bad. In any case, hopefully, the add-on helps.

I’ve noticed that its significantly harder to replicate this issue with boxes, spheres or monkeys. usually when I’m working with grass or tree models is where it starts to show a low. Thanks for the addon again!