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)