"Unable to eglMakeCurrent" when using vtkEGLRenderWindow

Hi there. I’m attempting to build a client server application where a user can request high resolution renders of a data cube from various camera positions. The request contain information about the camera such as Position and ViewUp. When I attempt to update the camera position, render and then capture the resulting frame, I get a long list of the same error repeated over again:

Warning: In /home/jonathan/VTK-8.2.0/Rendering/OpenGL2/vtkEGLRenderWindow.cxx, line 588
vtkEGLRenderWindow (0x7f81fc031500): Unable to eglMakeCurrent: 12290

If I attempt another request, it usually works. From what I’ve seen, it seems to alternate between producing an error and producing the correct output with each turn.

Here is the code I’m using to setup my vtk objects:

    DataCube dataCube;
    vtkSmartPointer<vtkFloatArray> floatArray;
    vtkSmartPointer<vtkImageImport> imageImport;
    vtkSmartPointer<vtkSmartVolumeMapper> volumeMapper;
    vtkSmartPointer<vtkNamedColors> colors;
    vtkSmartPointer<vtkRenderer> ren1;
    vtkSmartPointer<vtkEGLRenderWindow> renWin;
    vtkSmartPointer<vtkPiecewiseFunction> opacityTransferFunction;
    vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction; 
    vtkSmartPointer<vtkVolumeProperty> volumeProperty;
    vtkSmartPointer<vtkVolume> volume;
    vtkSmartPointer<vtkRenderWindowInteractor> iren;
    vtkSmartPointer<vtkImageData> imageData;

void createEGLRenderOnServer(){
    cout << "Creating EGL render on server." << endl;
    floatArray = vtkSmartPointer<vtkFloatArray>::New();
    floatArray->SetName("Float Array");
    floatArray->SetArray(dataCube.floatArray, dataCube.num_pixels, 1);

    // Create vtkImageImport object in order to use an array as input data to the volume mapper.
    imageImport = vtkSmartPointer<vtkImageImport>::New();
    imageImport->SetDataScalarTypeToFloat();
    imageImport->SetWholeExtent(0, dataCube.dimx - 1, 0, dataCube.dimy - 1, 0, dataCube.dimz - 1);
    imageImport->SetDataExtentToWholeExtent();
    imageImport->SetImportVoidPointer(floatArray->GetVoidPointer(0));
    imageImport->SetNumberOfScalarComponents(1);
    imageImport->SetDataSpacing(1.0, (double)(dataCube.dimx) / dataCube.dimy, (double)(dataCube.dimx) / dataCube.dimz);
    imageImport->Update();

    // The mapper / ray cast function know how to render the data
    volumeMapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
    volumeMapper->SetBlendModeToComposite(); // composite first
    volumeMapper->SetInputConnection(imageImport->GetOutputPort());
    volumeMapper->CroppingOn();

    // Create the standard renderer, render window
    // and interactor
    colors = vtkSmartPointer<vtkNamedColors>::New();

    ren1 = vtkSmartPointer<vtkRenderer>::New();

    renWin = vtkSmartPointer<vtkEGLRenderWindow>::New();
    renWin->Initialize();
    renWin->AddRenderer(ren1);

    // Create transfer mapping scalar value to opacity
    // Data values for ds9.arr 540x450x201 are in range [-0.139794;0.153026]
    opacityTransferFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
    opacityTransferFunction->AddPoint(-0.0, 0.0);
    opacityTransferFunction->AddPoint(0.16, 1.0);

    // Create transfer mapping scalar value to color
    colorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New();
    colorTransferFunction->AddRGBPoint(-0.0, 0.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint(0.16, 1.0, 1.0, 1.0);

    // The property describes how the data will look
    volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
    volumeProperty->SetColor(colorTransferFunction);
    volumeProperty->SetScalarOpacity(opacityTransferFunction);
    volumeProperty->SetInterpolationTypeToNearest();

    // The volume holds the mapper and the property and
    // can be used to position/orient the volume
    volume = vtkSmartPointer<vtkVolume>::New();
    volume->SetMapper(volumeMapper);
    volume->SetProperty(volumeProperty);

    ren1->AddVolume(volume);
    ren1->SetBackground(colors->GetColor3d("Wheat").GetData());
    ren1->GetActiveCamera()->Azimuth(45);
    ren1->GetActiveCamera()->Elevation(30);
    ren1->ResetCameraClippingRange();
    ren1->ResetCamera();
    //renWin->SwapBuffersOff();
    renWin->SetSize(600, 600);

    renWin->Render();
    renWin->Frame();
    renWin->WaitForCompletion();
    captureScreenShotOfCurrentEGLRender();

    double * position = ren1->GetActiveCamera()->GetFocalPoint();
    cout << position[0] << ' ' << position[1] << ' ' << position[2] << endl;
    cout << "Finished setting up EGL render on server." << endl;
    return;
  }

And then changing the camera and capturing the data:

// Set variables.
    ren1->ResetCamera();
    ren1->GetActiveCamera()->SetPosition(position.Get(0),position.Get(1),position.Get(2));
    ren1->GetActiveCamera()->SetViewUp(view_up.Get(0),view_up.Get(1),view_up.Get(2));

renWin->Render();
    renWin->Frame();
    renWin->WaitForCompletion();

By now the error has already shown up, but when it doesn’t I’m able to retrieve the (correct) data through the following:

 vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = 
      vtkSmartPointer<vtkWindowToImageFilter>::New();
    windowToImageFilter->SetInput(renWin);
    windowToImageFilter->SetInputBufferTypeToRGBA(); //also record the alpha (transparency) channel
    windowToImageFilter->ReadFrontBufferOff(); // read from the back buffer
    windowToImageFilter->Update();

    imageData = windowToImageFilter->GetOutput();

I was told on the vtk gitlab issues webpage that “It looks like a misuse” but am unsure how I am misusing it. If anyone can help, it would be much appreciated.

Are you using multiple threads?

I am. Requests can come in while another is being executed. However I have run tests where I make sure to leave a significant amount of time between requests. And regardless, it usually alternates between error and success.

I’ve also run tests where I make the program sleep between steps, as from my understanding, calling renWin->Render(), hands the process off to the GPU. So for an example, I’ve tried:
cout << “Sleep 1…” << endl;
sleep(5);
renWin->Render();
cout << “Sleep 2…” << endl;
sleep(5);
renWin->Frame();
cout << “Sleep 3…” << endl;
sleep(5);
renWin->WaitForCompletion();
cout << “Done!” << endl;

To no avail.

Most likely that’s the issue then. As far as I know, all rendering related method calls must be executed from the main thread. If client requests are unrelated then it is probably safer and more efficient to start a separate rendering process for each client.

Sorry if I was unclear. There is a separate rendering process for each client. However, an individual client can make another request to their rendering process before their request is complete. However, due to the nature of the application (for now), this won’t happen.