Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 82 additions & 72 deletions plugins/visualizer/src/VisualizerCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1235,78 +1235,85 @@ void Visualizer::initialize(uint window_width_pixels, uint window_height_pixels,

primaryShader.useShader();

// Initialize frame buffer only for windowed mode
if (!headless) {
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
glGenFramebuffers(1, &framebufferID);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
// Initialize shadow-map framebuffer (needed for both windowed and headless modes)
glGenFramebuffers(1, &framebufferID);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);

// Depth texture. Slower than a depth buffer, but you can sample it later in your shader
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, shadow_buffer_size.x, shadow_buffer_size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
// Depth texture. Slower than a depth buffer, but you can sample it later in your shader
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &depthTexture);
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, shadow_buffer_size.x, shadow_buffer_size.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// clamp to border so any lookup outside [0,1] returns 1.0 (no shadow)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
// clamp to border so any lookup outside [0,1] returns 1.0 (no shadow)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

// enable hardware depth comparison
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
// enable hardware depth comparison
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);

if (!checkerrors()) {
helios_runtime_error("ERROR (Visualizer::initialize): OpenGL setup failed during texture configuration. "
"This may indicate graphics driver issues or insufficient OpenGL support.");
}

// restore default active texture for subsequent texture setup
glActiveTexture(GL_TEXTURE0);

glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);

glDrawBuffer(GL_NONE); // No color buffer is drawn to.

// Always check that our framebuffer is ok
int max_checks = 10000;
int checks = 0;
while (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE && checks < max_checks) {
checks++;
}
// Check framebuffer completeness instead of using assert
GLenum framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
std::string error_message = "ERROR (Visualizer::initialize): Framebuffer is incomplete. Status: ";
switch (framebuffer_status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
error_message += "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT - Framebuffer attachment is incomplete";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
error_message += "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - No attachments";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
error_message += "GL_FRAMEBUFFER_UNSUPPORTED - Unsupported framebuffer format";
break;
default:
error_message += "Unknown framebuffer error code: " + std::to_string(framebuffer_status);
break;
}
if (!checkerrors()) {
helios_runtime_error("ERROR (Visualizer::initialize): OpenGL setup failed during texture configuration. "
"This may indicate graphics driver issues or insufficient OpenGL support.");
}

// restore default active texture for subsequent texture setup
glActiveTexture(GL_TEXTURE0);

glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);

glDrawBuffer(GL_NONE); // No color buffer is drawn to.

// Always check that our framebuffer is ok
int max_checks = 10000;
int checks = 0;
while (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE && checks < max_checks) {
checks++;
}
// Check framebuffer completeness instead of using assert
GLenum framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
std::string error_message = "ERROR (Visualizer::initialize): ";
if (headless) {
error_message += "Headless shadow-map framebuffer";
} else {
error_message += "Framebuffer";
}
error_message += " is incomplete. Status: ";
switch (framebuffer_status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
error_message += "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT - Framebuffer attachment is incomplete";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
error_message += "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - No attachments";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
error_message += "GL_FRAMEBUFFER_UNSUPPORTED - Unsupported framebuffer format";
break;
default:
error_message += "Unknown framebuffer error code: " + std::to_string(framebuffer_status);
break;
}
if (!headless) {
error_message += ". This typically occurs in CI environments with limited graphics support or missing GPU drivers.";
helios_runtime_error(error_message);
}
helios_runtime_error(error_message);
}

// Finished OpenGL setup
// Check for OpenGL errors after framebuffer setup
if (!checkerrors()) {
helios_runtime_error("ERROR (Visualizer::initialize): Framebuffer setup failed. "
"This indicates issues with OpenGL framebuffer operations, often related to graphics driver limitations or insufficient resources.");
}
} else {
// Set framebuffer dimensions for headless mode (no framebuffer created)
// Finished OpenGL setup
// Check for OpenGL errors after framebuffer setup
if (!checkerrors()) {
helios_runtime_error("ERROR (Visualizer::initialize): Framebuffer setup failed. "
"This indicates issues with OpenGL framebuffer operations, often related to graphics driver limitations or insufficient resources.");
}

if (headless) {
// Set framebuffer dimensions for headless mode
Wframebuffer = Wdisplay;
Hframebuffer = Hdisplay;
}
Expand Down Expand Up @@ -1410,16 +1417,19 @@ void Visualizer::initialize(uint window_width_pixels, uint window_height_pixels,
}

Visualizer::~Visualizer() {
// Clean up resources for both headless and windowed modes
// Clean up shadow-map FBO (needed for both headless and windowed modes)
if (framebufferID != 0) {
glDeleteFramebuffers(1, &framebufferID);
framebufferID = 0;
}
if (depthTexture != 0) {
glDeleteTextures(1, &depthTexture);
depthTexture = 0;
}

// Clean up offscreen resources for headless mode
if (headless) {
cleanupOffscreenFramebuffer();
} else {
if (framebufferID != 0) {
glDeleteFramebuffers(1, &framebufferID);
}
if (depthTexture != 0) {
glDeleteTextures(1, &depthTexture);
}
}

// Clean up common OpenGL resources regardless of mode
Expand Down
Loading