Mesa3d Aspect_GraphicDeviceDefinitionError

Good afternoon,

Currently I am building a small piece of code to make pictures of .stp files. The code will run on a system without any gpu/graphics, so I am trying to use the mesa3d software implementation of OpenGL. The code works, but when i call the method that creates a screenshot a second time, i get an exception:

Microsoft C++ exception: Aspect_GraphicDeviceDefinitionError at memory location ...
The exception is thrown by the line "view->SetBackgroundColor(Quantity_Color(Quantity_NOC_WHITE));"

When I print the message of the exception, the following is shown:
OpenGl_Window::CreateWindow: SetPixelFormat failed. Error code: 0

This is the code:

unsigned char* dumpShape(const TopoDS_Shape& shape, const Standard_Integer width, const Standard_Integer height)
{
// prepare viewer
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
Handle(V3d_Viewer) viewer = new V3d_Viewer(graphicDriver);
viewer->SetDefaultLights();
viewer->SetLightOn();

Handle(AIS_InteractiveContext) context = new AIS_InteractiveContext(viewer);
// prepare off-screen view
Handle(V3d_View) view = viewer->CreateView();
Handle(Aspect_NeutralWindow) wnd = new Aspect_NeutralWindow();
wnd->SetSize(width, height);
wnd->SetVirtual(true);
view->SetWindow(wnd);
view->SetBackgroundColor(Quantity_Color(Quantity_NOC_WHITE));
view->MustBeResized();
// prepare presentation filled by shape
Handle(AIS_Shape) presentation = new AIS_Shape(shape);
context->Display(presentation, Standard_False);
context->SetDisplayMode(presentation, AIS_Shaded, Standard_False);
view->FitAll();
view->Redraw();

// prepare pixmap image
Image_AlienPixMap img;
if (!view->ToPixMap(img, width, height))
return nullptr;

const Standard_Byte* bytes_img = img.Data();

// Calculate the size of the image data
int dataSize = width * height * 3; // Assuming 24-bit RGB data (3 bytes per pixel)

// Allocate memory for the image data
unsigned char* imageData = new unsigned char[dataSize];

// imageData byte array
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
// Calculate the index in bytes_img
int index = i * width + j;
imageData[index * 3] = static_cast<unsigned char>(bytes_img[index * 3 + 2]); // Blue
imageData[index * 3 + 1] = static_cast<unsigned char>(bytes_img[index * 3 + 1]); // Green
imageData[index * 3 + 2] = static_cast<unsigned char>(bytes_img[index * 3]); // Red
}
return imageData;
}
return nullptr;
}

bool ImgFromStep(const char* stpContent, unsigned char** imageDataPtr)
{
StepFileReader sfr;
const TopoDS_Shape& shape = sfr.ShapeFromFile(stpContent);
unsigned char* newImageData = dumpShape(shape, 600, 600);
unsigned char* newImageData2 = dumpShape(shape, 600, 600); //This second method call makes the program crash.

*imageDataPtr = newImageData;

return true;
}

Some additional information:
-OpenCascade version 7.8
-mesa-llvmpipe-24.0.3 (opengl32.dll taken from https://github.com/mmozeiko/build-mesa/releases/tag/24.0.3 and extracted next to the .exe of my program. Without this mesa opengl32.dll, the program runs fine.)
-Although the viewer should be hidden, some weird squares/pieces of the viewer are displayed during the first dumpShape call.

I'm wondering if you could point me in the right direction, I don't think opencascade is the problem, but (my use of) mesa.
If you need any additional information, let me know!

Thank you in advance!
Martijn

gkv311 n's picture

Here you are setting to View a Window that have undefined zero window id (Aspect_NeutralWindow::SetNativeHandle is not called):

Handle(Aspect_NeutralWindow) wnd = new Aspect_NeutralWindow();
wnd->SetSize(width, height);
wnd->SetVirtual(true);
view->SetWindow(wnd);

Even if you are using a 'virtual' window and use software OpenGL implementation - you still need to create a valid platform-dependent window for that. There is a code sample how this could be done. There might be a way to use completely offscreen rendering in case of a Mesa library, but OCCT doesn't know about it when you just replace opengl32.dll library.

I guess that some mess happens when you pass 0 window id to OpenGL on Windows platform, like drawing into Desktop window or something like this - this would clarify why you are able to create a first screenshot and why something appears on the screen.