vtkCompositePolyDataMapper keeps per-block color after switching to scalar coloring (9.7.x, Windows)

Hi,

After upgrading to VTK 9.7.1, we’re seeing a rendering issue with vtkCompositePolyDataMapper and vtkCompositeDataDisplayAttributes.

We first give a set of blocks a solid color (SetBlockColor) and render. We then switch the same blocks to scalar coloring: RemoveBlockColor, per-block LUT, array name, scalar range, scalar mode and SetBlockScalarVisibility(true). After rendering again, some or all of the blocks still show the solid color.

It happens only on Windows (GitHub Actions windows-2022, offscreen), and only intermittently.We have never seen it on Linux, and never with 9.6.2, which we are pinning for now. Our guess is that it’s related to the new mapper caching in 9.7.0 (TBO/IBO cache, partial updates).

The attached script reproduces the sequence of calls and counts the pixels still drawn with the solid color:

"""Blocks keep their solid color after switching to scalar coloring (VTK 9.7.x).

Usage: python repro_block_color_to_scalars.py [iterations]
"""

import sys

import vtk
from vtkmodules.util.numpy_support import vtk_to_numpy

MAGENTA = (1.0, 0.0, 1.0)


def run_once() -> int:
    blocks = vtk.vtkMultiBlockDataSet()
    for i in range(40):
        plane = vtk.vtkPlaneSource()
        plane.SetOrigin(i % 8, i // 8, 0)
        plane.SetPoint1(i % 8 + 1, i // 8, 0)
        plane.SetPoint2(i % 8, i // 8 + 1, 0)
        plane.SetResolution(10, 10)
        plane.Update()
        polydata = plane.GetOutput()
        scalars = vtk.vtkFloatArray()
        scalars.SetName("values")
        for p in range(polydata.GetNumberOfPoints()):
            x, y, _ = polydata.GetPoint(p)
            scalars.InsertNextValue((x * 37 + y * 11) % 200 - 100)
        polydata.GetPointData().AddArray(scalars)
        blocks.SetBlock(i, polydata)

    mapper = vtk.vtkCompositePolyDataMapper()
    attributes = vtk.vtkCompositeDataDisplayAttributes()
    mapper.SetCompositeDataDisplayAttributes(attributes)
    mapper.SetInputDataObject(blocks)
    actor = vtk.vtkActor()
    actor.SetMapper(mapper)
    renderer = vtk.vtkRenderer()
    renderer.AddActor(actor)
    window = vtk.vtkRenderWindow()
    window.SetOffScreenRendering(True)
    window.SetSize(600, 400)
    window.AddRenderer(renderer)
    renderer.ResetCamera()
    block_list = [blocks.GetBlock(i) for i in range(40)]

    # 1. Solid color on every block
    for block in block_list:
        attributes.SetBlockColor(block, MAGENTA)
    window.Render()

    # 2. Switch every block to scalar coloring
    lut = vtk.vtkColorTransferFunction()
    lut.AddRGBPoint(-100, 0.1, 0.1, 0.4)
    lut.AddRGBPoint(100, 0.9, 0.9, 0.9)
    for block in block_list:
        block.GetPointData().SetActiveScalars("values")
        attributes.RemoveBlockColor(block)
        attributes.SetBlockLookupTable(block, lut)
        attributes.SetBlockArrayName(block, "values")
        attributes.SetBlockScalarRange(block, vtk.vtkVector2d(-100, 100))
        attributes.SetBlockScalarMode(block, vtk.VTK_SCALAR_MODE_USE_POINT_DATA)
        attributes.SetBlockScalarVisibility(block, True)
    mapper.ScalarVisibilityOn()
    mapper.SetColorModeToMapScalars()
    mapper.Modified()
    window.Render()

    # 3. Count pixels still drawn with the solid color (expected: 0)
    grabber = vtk.vtkWindowToImageFilter()
    grabber.SetInput(window)
    grabber.Update()
    rgb = vtk_to_numpy(grabber.GetOutput().GetPointData().GetScalars())
    window.Finalize()
    return int(((rgb[:, 0] > 240) & (rgb[:, 1] < 15) & (rgb[:, 2] > 240)).sum())


if __name__ == "__main__":
    iterations = int(sys.argv[1]) if len(sys.argv) > 1 else 20
    print("VTK", vtk.vtkVersion.GetVTKVersionFull())
    failures = sum(run_once() > 0 for _ in range(iterations))
    print(f"{failures}/{iterations} failing iterations")
    sys.exit(1 if failures else 0)

Hello @MaxNumerique,

Thank you for sharing the reproducer.

If you are referring to vtk/vtk!13324, this is likely not the cause. The TBO cache is enabled only for the low-memory mapper, which is the default in WebAssembly but not on desktop.

Since your script uses vtkCompositePolyDataMapper (which utilizes vtkOpenGLBatchedPolyDataMapper for leaves) rather than explicitly creating a vtkOpenGLLowMemoryBatchedPolyDataMapper, the issue likely stems from a different source.

Feel free to open an issue - https://gitlab.kitware.com/vtk/vtk/-/work_items/new

Thank you !

Just opened an issue here