View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0015869VTK(No Category)public2015-12-10 08:122016-07-08 09:34
ReporterStephan Rademacher 
Assigned ToSankhesh Jhaveri 
PrioritynormalSeverityminorReproducibilityhave not tried
StatusclosedResolutionfixed 
PlatformOSOS Version
Product Version6.2.0 
Target VersionFixed in Version7.1.0 
Summary0015869: vtkOpenGLGPUVolumeRayCastMapper (possibly others too) releases graphics resources while the wrong OpenGL context is set
DescriptionI am using VTK 6.2.0 on Windows using the OpenGL2 backend.

My application has multiple render windows, all of them running in the same thread. Each render windows contains its own renderers/mappers etc, so nothing is shared between them. Sometimes when running the program, all actors of one of the renderers in one of the windows are removed via

renderer->RemoveAllViewProps().

This causes the actors (and their mappers etc) to release their graphics resources. One of the mappers is a vtkSmartVolumeMapper, which internally uses vtkOpenGLGPUVolumeRayCastMapper. When vtkOpenGLGPUVolumeRayCastMapper releases its graphics resources, it deletes multiple OpenGL objects, including vertex array objects:

void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteBufferObjects()
{
#ifndef __APPLE__
  if (this->CubeVAOId)
    {
    glDeleteVertexArrays(1, &this->CubeVAOId);
    }
#endif

  if (this->CubeVBOId)
    {
    glDeleteBuffers(1, &this->CubeVBOId);
    }

  if (this->CubeIndicesId)
   {
   glDeleteBuffers(1, &this->CubeIndicesId);
   }
}

As OpenGL objects like VAOs are context specific, this only works correctly if the right context is set. That is not ensured however. The OpenGL context of the renderwindow is not made current anywhere in this call chain. As my application has multiple render windows, all living in the same thread, any one of them might be current at the moment.

So when removing the props of renderer1 (living in renderWindow1), renderWindow2 might be current at the moment. renderer1 releases the graphics resources with the context of renderWindow2 still set, thereby destroying OpenGL objects in renderWindow2. (Let's call this case A)

For example, CubeVAOId is '1' for me in both raycasters, which is fine, since they are working on different OpenGL contexts. When case A happens, this means that the glDeleteVertexArrays(1, &this->CubeVAOId) call in raycaster1 deletes the vertex array object of raycaster2. raycaster2 obviously doesn't expect that and no longer renders correctly. The effects range from OpenGL errors / bad renderings to crashes.

Below is a small program demonstrating this. ironProt.vtk is rendered in two render windows. One of the windows has a keypressed callback. As the callback only works if the window is selected, it is also ensured that the OpenGL context for this window is current when the callback occurs. When pressing '9', the actors in the OTHER render window are removed via renderer->RemoveallViewProps(). This leads to a crash on my machine (vtkOpenGLGPUVolumeRayCastMapper::GPURender, line 2700 in glDrawElements, access violation reading 0).

#include "vtkColorTransferFunction.h"
#include "vtkCommand.h"
#include "vtkPiecewiseFunction.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkSmartVolumeMapper.h"
#include "vtkStructuredPoints.h"
#include "vtkStructuredPointsReader.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"

class MyCallBack: public vtkCommand
{
public:

    static MyCallBack *New()
    {
        return new MyCallBack;
    }

    virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData)
    {
        if (eventId != vtkCommand::KeyPressEvent) return;

        vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
        if (interactor == NULL) return;
        
        char* pressedKey = interactor->GetKeySym();

        if (strcmp(pressedKey, "9") == 0)
        {
            // The removes all the props from the OTHER (the SECOND) renderer, while the OpenGL context for the FIRST
            // render window is still set to current. This means the wrong context is set, and when renderer2 frees
            // it's graphics resources, it does so on the wrong context.
            //
            // Note: This only causes problems if you didn't press '8' before doing this. If you did: restart the program
            std::cout << "Removing all view props of the OTHER renderer - this will cause at least OpenGL errors" << std::endl;
            
            // Activating this line makes the problem go away
            //renderWindow2->MakeCurrent();
            
            renderer2->RemoveAllViewProps();
            renderWindow1->Render();
            renderWindow2->Render();
        }
    }

    vtkSmartPointer<vtkRenderer> renderer1;
    vtkSmartPointer<vtkRenderer> renderer2;
    vtkSmartPointer<vtkRenderWindow> renderWindow1;
    vtkSmartPointer<vtkRenderWindow> renderWindow2;
};

int main()
{
    vtkSmartPointer<vtkStructuredPointsReader> pointsReader = vtkSmartPointer<vtkStructuredPointsReader>::New();
    pointsReader->SetFileName("ironProt.vtk");
    pointsReader->Update();

    vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New();
    colorTransferFunction->AddRGBPoint( 0.0, 0.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint( 64.0, 1.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint(128.0, 0.0, 0.0, 1.0);
    colorTransferFunction->AddRGBPoint(192.0, 0.0, 1.0, 0.0);
    colorTransferFunction->AddRGBPoint(255.0, 0.0, 0.2, 0.0);

    vtkSmartPointer<vtkPiecewiseFunction> opacityTransferFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
    opacityTransferFunction->AddPoint(0, 0.0);
    opacityTransferFunction->AddPoint(255, 1.0);

    vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
    volumeProperty->SetColor(colorTransferFunction);
    volumeProperty->SetScalarOpacity(opacityTransferFunction);
 
    // Create the first renderwindow/renderer/mapper.
    // This is the renderer that will experience problems later on.
    vtkSmartPointer<vtkSmartVolumeMapper> smartMapper1 = vtkSmartPointer<vtkSmartVolumeMapper>::New();
    smartMapper1->SetInputConnection(pointsReader->GetOutputPort());

    vtkSmartPointer<vtkVolume> volume1 = vtkSmartPointer<vtkVolume>::New();
    volume1->SetMapper(smartMapper1);
    volume1->SetProperty(volumeProperty);
 
    vtkSmartPointer<vtkRenderer> renderer1 = vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow1 = vtkSmartPointer<vtkRenderWindow>::New();
    vtkSmartPointer<vtkRenderWindowInteractor> interactor1= vtkSmartPointer<vtkRenderWindowInteractor>::New();
    
    renderWindow1->SetParentId(0);
    renderWindow1->AddRenderer(renderer1);
    renderWindow1->SetWindowName("Victim");
    renderWindow1->SetSize(500, 300);
    renderWindow1->SetPosition(100, 100);
    interactor1->SetRenderWindow(renderWindow1);

    renderer1->AddActor(volume1);
    renderer1->SetBackground(1.0, 1.0, 1.0);

    // Create the second renderwindow/renderer/mapper.
    // This is the renderer we later remove all the actors from, triggering the problems in the first renderer
    vtkSmartPointer<vtkSmartVolumeMapper> smartMapper2 = vtkSmartPointer<vtkSmartVolumeMapper>::New();
    smartMapper2->SetInputConnection(pointsReader->GetOutputPort());

    vtkSmartPointer<vtkVolume> volume2 = vtkSmartPointer<vtkVolume>::New();
    volume2->SetMapper(smartMapper2);
    volume2->SetProperty(volumeProperty);

    vtkSmartPointer<vtkRenderer> renderer2 = vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow2 = vtkSmartPointer<vtkRenderWindow>::New();
    vtkSmartPointer<vtkRenderWindowInteractor> interactor2= vtkSmartPointer<vtkRenderWindowInteractor>::New();
    
    renderWindow2->SetParentId(0);
    renderWindow2->AddRenderer(renderer2);
    renderWindow2->SetWindowName("Villain");
    renderWindow2->SetSize(300, 300);
    renderWindow2->SetPosition(650, 100);
    interactor2->SetRenderWindow(renderWindow2);

    renderer2->AddActor(volume2);
    renderer2->SetBackground(1.0, 1.0, 1.0);

    // Create callback so we can trigger the problem
    vtkSmartPointer<MyCallBack> callback = vtkSmartPointer<MyCallBack>::New();
    callback->renderer1 = renderer1;
    callback->renderer2 = renderer2;
    callback->renderWindow1 = renderWindow1;
    callback->renderWindow2 = renderWindow2;
    interactor1->AddObserver("KeyPressEvent", callback);

    // Let's go
    renderWindow1->Render();
    renderWindow2->Render();
    interactor1->Start();
}

You can activate the commented line
//renderWindow2->MakeCurrent();
to verify that making the window current makes all the difference.

I'm not sure what's the best way to fix this. I guess to be absolutely sure, the raycaster has to do make the window current itself.
TagsNo tags attached.
ProjectTBD
Typecrash
Attached Files

 Relationships

  Notes
(0035534)
Stephan Rademacher (reporter)
2015-12-10 08:16

Sorry, please disregard this comment in the example code:

// Note: This only causes problems if you didn't press '8' before doing this. If you did: restart the program

I forgot to remove that note when I changed the code.
(0035541)
Stephan Rademacher (reporter)
2015-12-11 11:36

It seems bug report http://www.vtk.org/Bug/view.php?id=15765 [^] tackles the same issue.
(0035552)
Stephan Rademacher (reporter)
2015-12-14 07:37

I can confirm vtkOpenGLPolyDataMapper suffers from the same issue.
(0036216)
Sankhesh Jhaveri (manager)
2016-07-06 14:14

Following merge request fixes the issue:
https://gitlab.kitware.com/vtk/vtk/merge_requests/1636 [^]

 Issue History
Date Modified Username Field Change
2015-12-10 08:12 Stephan Rademacher New Issue
2015-12-10 08:16 Stephan Rademacher Note Added: 0035534
2015-12-11 11:36 Stephan Rademacher Note Added: 0035541
2015-12-14 07:37 Stephan Rademacher Note Added: 0035552
2016-07-06 13:17 Sankhesh Jhaveri Assigned To => Sankhesh Jhaveri
2016-07-06 14:14 Sankhesh Jhaveri Note Added: 0036216
2016-07-06 14:14 Sankhesh Jhaveri Status backlog => gerrit review
2016-07-08 09:34 Sankhesh Jhaveri Status gerrit review => closed
2016-07-08 09:34 Sankhesh Jhaveri Resolution open => fixed
2016-07-08 09:34 Sankhesh Jhaveri Fixed in Version => 7.1.0


Copyright © 2000 - 2018 MantisBT Team