Wish: internal preview caching/optimization for RenderContent subclasses

In my plugin I implement various RenderContent subclasses, which use only Fields for storage. Every time I hover the mouse over such a texture or material in a list, or select it with the preview pane visible, I see that Rhino renders a new preview. I observe the same for native Rhino content.

This seems unnecessary, given that RenderContents have a hash – it seems like a preview should be stored and only re-rendered when the hash has changed. I believe this would help reduce the general sluggishness of rendering-related GUI in Rhino.

I’ve cobbled together something for this internally, for materials, in CreatePreview – basically, store a copy of the preview bitmap whenever NotifyIntermediateUpdate has been called, in a dictionary keyed by the preview args ID (so far, this appears to be usable as a hash of the overall preview scene & material), and then also check if a cached bitmap is available whenever CreatePreview is called. This helps greatly, since a new render is only performed when the ID has actually changed, with a cached version being used otherwise, even if the preview is still being rendered (i.e. my current logic is just: use cache if possible, otherwise render a preview when full quality is requested).

However it would be better if this were handled internally, for various reasons, e.g. I have to use ReadDocument to clear out the bitmap cache since there’s not enough context in CreatePreview, to know whether a given bitmap is unlikely to be used again.

For textures I did not find a way to do even this; the CreateTexture2dPreview override is called, but its args have only an image and size, so I don’t really understand the purpose of the override. Trying CreatePreview, I could be missing something, but I have not yet observed it to be called with RenderContentKind.Texture. And I believe it would not help to try to implement caching in texture evaluators, since the sluggishness appears to be due to the many omp threads used for evaluation, which would still be the case whether sampling a cached bitmap or not.

Rhino has more context to work with, and the ability to store private data on content instances, so not only would it be able to handle this for all clients transparently, but it could do a better job of it, as well.

Hi,

I have logged this as https://mcneel.myjetbrains.com/youtrack/issue/RH-60510

1 Like

Thanks, Sir!

JD

This is exactly how it works. Seriously - something very odd must be going on.

  • Andy
1 Like

I was thinking that maybe the large preview wasn’t rendered yet so due to different resolution a new preview is requested?

In case it helps to see what I’m seeing here, here is a vid with Bella as current renderer, using the caching described above:

And here is the same, but with Rhino set as as current renderer:

This is with 7.0.20259.15365.

Are you sure you’re not changing fields on clicks?

When I test this with Cycles Diffuse materials (normally hidden) the system behaves as expected.

I confirm that it works here with cycles & cycles diffuse, but not with other combinations:

  renderer  |  material  |  result
------------+------------+----------
   cycles   |   cycles   |    ok
------------+------------+----------
   cycles   |   bella    |   fail
------------+------------+----------
   bella    |   cycles   |   fail
------------+------------+----------
   bella    |   bella    |   fail

My types & UI are all built automatically using a common algorithm, so I can say with certainty that I am not inadvertently setting any fields. I found one case where I could create an undo record when (due to checks that prevent setting fields unnecessarily) no change would end up being made, but avoiding that has made no difference. When I trace through the sequence of events that occurs when selecting a material, this is what I find:

  1. The system creates my material, my ctor runs, I add my fields.
  2. EtoCollapsibleSection.DataChanged event is raised, I update my UI.

I have put in tracking, to check the render hash at different points; I print hashes for selected RenderContents in DataChanged, after having used them to update UI, and I also print hashes for materials passed to CreatePreview.

Here is a sequence printed, when switching between two materials (bella materials, bella current); you can see that the CreatePreviewEventArgs.Id is also remaining consistent:

(select mat0)

Bella: CreatePreview e.Id: -9997464
Bella:   mat0 => 3614458886
Bella: RenderHash @ DataChanged for mat0: 3614458886
Bella: CreatePreview e.Id: -9997464
Bella:   mat0 => 3614458886

(select mat1)

Bella: CreatePreview e.Id: -1224693330
Bella:   mat1 => 798319109
Bella: RenderHash @ DataChanged for mat1: 798319109
Bella: CreatePreview e.Id: -1224693330
Bella:   mat1 => 798319109

(select mat0)

Bella: CreatePreview e.Id: -9997464
Bella:   mat0 => 3614458886
Bella: RenderHash @ DataChanged for mat0: 3614458886
Bella: CreatePreview e.Id: -9997464
Bella:   mat0 => 3614458886

(select mat1)

Bella: CreatePreview e.Id: -1224693330
Bella:   mat1 => 798319109
Bella: RenderHash @ DataChanged for mat1: 798319109
Bella: CreatePreview e.Id: -1224693330
Bella:   mat1 => 798319109

So this is what I have so far, will keep trying to figure what’s going on.

(and just approaching it from another direction, if there were some fields/hash issue, then I think my caching scheme described above should not be able to work)

The most distinct difference is EtoCollapsibleSection.DataChanged. I don’t use this in the Cycles Diffuse material. Lets ask @maxsoder if he knows if anything here could cause extraneous calls to preview generation (CreatePreview or some path to it).

It is hard to tell. The EtoCollapsibleSection.DataChanged is an event that is triggered because something has changed. In this case the Selection has changed and the event is fired.

JD

I think we’re going to have to be able to repeat this to be able to figure out whats going on at this end.

Can we get a WIP copy of Bella to play with?

  • Andy

Sure thing Andy, let me check some things and I’ll upload a new build later today, so that you check with the most recent code I have. I’ll post again when it’s up.