viamd icon indicating copy to clipboard operation
viamd copied to clipboard

Replace GLFW and nativefiledialog with SDL3 + Create layered renderer architecture with GPU API

Open Copilot opened this issue 3 months ago • 4 comments

Consolidates windowing and file dialog dependencies from GLFW + nativefiledialog into SDL3, reducing dependency footprint and leveraging SDL3's native cross-platform file dialogs. Additionally refactors all rendering code into a layered renderer architecture with a standalone low-level GPU API to prepare for future SDL-GPU transition.

Changes

Dependency Management

  • Added SDL3 as submodule (main branch)
  • Removed GLFW and nativefiledialog submodules
  • Updated CMakeLists.txt: static SDL3 build with optional X11 features disabled

Core Migration (src/app/application.cpp)

  • Window/GL context: glfwCreateWindowSDL_CreateWindow, glfwMakeContextCurrentSDL_GL_MakeCurrent
  • Event loop: glfwPollEventsSDL_PollEvent with native SDL event handling
  • File dialogs: NFD_OpenDialog/SaveDialogSDL_ShowOpenFileDialog/ShowSaveFileDialog with async callbacks
  • Timing: glfwGetTimeSDL_GetTicks

ImGui Backend

  • Added imgui_impl_sdl3.{cpp,h} from ImGui 1.90.5, patched for SDL3 API changes:
    • Keyboard events: keysym struct → flat key/scancode/mod fields
    • Constants: SDLK_aSDLK_A, SDL_SYSTEM_CURSOR_ARROWSDL_SYSTEM_CURSOR_DEFAULT
    • Text input: SDL_SetTextInputRectSDL_SetTextInputArea(window, rect, cursor)
  • Updated for ImGui 1.92.5 compatibility: SetPlatformImeDataFnplatform_io.Platform_SetImeDataFn
  • Removed imgui_impl_glfw.{cpp,h}

Layered Renderer Architecture

Low-Level GPU API (src/gfx/gpu.{h,cpp})

NEW: Created abstraction layer for all GPU operations:

  • Texture Management: create_texture_2d/3d(), update_texture(), destroy_texture(), bind_texture()
  • Shader Management: create_shader(), destroy_shader(), use_shader(), set_uniform() (multiple type overloads)
  • Framebuffer Management: create_framebuffer(), destroy_framebuffer(), bind_framebuffer(), attach_texture_to_framebuffer()
  • VAO Management: create_vertex_array(), destroy_vertex_array(), bind_vertex_array()
  • Rendering State: set_viewport(), set_scissor(), enable_blend(), enable_depth_test(), enable_cull_face(), set_depth_mask(), set_color_mask(), clear_color(), clear()
  • Draw Operations: draw_arrays(), draw_elements()

High-Level Renderer Module (src/gfx/renderer.{h,cpp})

  • Moved ~578 lines of rendering code from main.cpp to renderer module
  • Functions migrated:
    • fill_gbuffer() - Molecular representation rendering to GBuffer
    • apply_postprocessing() - All post-processing effects (SSAO, TAA, DoF, etc.)
    • draw_representations_opaque() - Opaque molecular rendering
    • draw_representations_transparent() - Volume rendering (orbitals, electron density)
    • draw_representations_opaque_lean_and_mean() - Optimized rendering for selections/highlights
  • Clean API: main.cpp now uses renderer::*() calls instead of direct OpenGL
  • Uses GPU API: Renderer layer uses low-level GPU API for all OpenGL operations

Component Refactoring

NEW: Started migrating components to use GPU API:

  • Ramachandran Component: Refactored texture creation/destruction to use gpu::create_texture_2d() and gpu::destroy_texture()
  • Replaced glGenFramebuffers/glGenVertexArrays with gpu::create_framebuffer() and gpu::create_vertex_array()
  • Uses glTexStorage2D via GPU API for immutable texture storage

File Dialog Implementation

// Before: Synchronous NFD with manual filter parsing
nfdchar_t* out_path = NULL;
nfdresult_t result = NFD_OpenDialog("jpg,png,bmp", NULL, &out_path);
if (result == NFD_OKAY) { /* use out_path */ }

// After: Async SDL3 with callback and proper cleanup
struct DialogState { const char* result_path; bool completed; };
DialogState state = {NULL, false};
SDL_ShowOpenFileDialog(callback, &state, NULL, filters, count, NULL, false);
while (!state.completed) { SDL_WaitEventTimeout(&event, 10); }

Security & Quality

  • Fixed race condition: changed dialog state from static to local
  • Added buffer overflow protection in filter extension parsing (validate length before allocation)
  • Improved responsiveness: SDL_Delay(10)SDL_WaitEventTimeout(&event, 10)

Architecture Improvement

The new layered architecture provides clean separation of concerns:

Components (ramachandran, veloxchem, etc.)
   ↓
High-level Renderer (renderer.h/cpp)
   ├─> renderer::fill_gbuffer()
   ├─> renderer::apply_postprocessing()
   └─> renderer::clear_gbuffer()
          ↓
Low-level GPU API (gpu.h/cpp)
   ├─> gpu::create_texture()
   ├─> gpu::create_shader()
   ├─> gpu::bind_framebuffer()
   ├─> gpu::set_viewport()
   └─> gpu::draw_arrays()
          ↓
OpenGL

Benefits:

  • All OpenGL calls isolated in GPU API layer
  • Components use GPU API instead of direct OpenGL
  • High-level renderer focuses on rendering logic, not OpenGL details
  • Easy to swap OpenGL for SDL-GPU in future by reimplementing GPU API
  • MDlib rendering code (md_gl_draw()) remains unchanged as requested

Progress

  • ✅ Low-level GPU API layer complete
  • ✅ High-level renderer using GPU API
  • ✅ Ramachandran component partially migrated to GPU API
  • 🔄 Remaining: Complete component migration (ramachandran rendering, veloxchem, etc.)

Notes

  • Manual testing recommended on Windows/macOS to verify native file dialogs and platform-specific behavior
  • Layered renderer architecture ready for future SDL-GPU migration
  • MDlib rendering code remains unchanged as requested
Original prompt

Create a PR that introduces SDL3 as a new dependency. This should replace the existing dependencies on glfw and native file dialogue.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot avatar Nov 23 '25 06:11 Copilot