Last updated: 2026-03-05
Goal: migrate Engine5 rendering from compatibility-style OpenGL usage to a true core-profile pipeline, so demo/SimpleDemo can be debugged in NVIDIA NSight with meaningful draw/resource visibility.
- In scope: Windows + SDL OpenGL context creation, render-device submission path, shader syntax alignment, debug markers/labels, SimpleDemo debug profile.
- Out of scope: non-OpenGL backends, full renderer rewrite, unrelated demo migration.
- Primary target: Win64 + Delphi build of
SimpleDemowith core context + debug context.
- Minimum required OpenGL version remains
3.0(may be raised to3.2/3.3if migration requires it). - By default, engine should request the highest available core context supported by driver/platform (example:
4.5), not a fixed low version. - Context creation should use descending version negotiation (high -> lower) until success, with clear logs of requested/actual version.
- Runtime should continue using highest-version features when available via existing version checks/extensions (this is existing behavior and is preserved).
Goal: introduce safe migration toggle and baseline logs before behavior changes.
Changes:
- Apus.Engine.API.pas: add OpenGL context request record (profile/version/debug/forward-compatible flags).
- Apus.Engine.GameApp.pas: add app-level setting to request core context explicitly (default off for first step).
- Apus.Engine.OpenGL.pas: log requested vs actual profile/version/flags.
Exit criteria:
- Engine can run with old path unchanged.
- Log clearly shows requested and actual context parameters.
Goal: make context creation explicit and profile-aware on all platforms.
Changes:
- Apus.Engine.API.pas: change
ISystemPlatform.CreateOpenGLContextsignature to accept context request and return actual info. - Apus.Engine.WindowsPlatform.pas: implement new signature.
- Apus.Engine.SDLplatform.pas: implement new signature.
- Apus.Engine.OpenGL.pas: pass request from renderer init.
Exit criteria:
- Both platform backends compile.
- No functional behavior change yet when compatibility mode is requested.
- Version negotiation API supports "prefer highest available" + explicit minimum.
Goal: create true core profile context on Windows.
Changes:
- Apus.Engine.WindowsPlatform.pas:
- create temporary legacy context only to load WGL extensions;
- use
wglCreateContextAttribsARBfor final context with requested major/minor/profile/flags; - implement descending version attempts (for example 4.6..3.x with configured minimum);
- keep controlled fallback path (compatibility request only, no silent fallback when core requested).
- extra/dglOpenGL.pas: verify required WGL symbols are available; add missing declarations if needed.
Exit criteria:
- On Windows, log reports core profile when requested.
- Context creation fails loudly (with clear log) if driver cannot provide requested core profile.
- Successful context normally matches highest supported version on target machine.
Goal: stop forcing compatibility profile on SDL path.
Changes:
- Apus.Engine.SDLplatform.pas:
- replace
SDL_GL_CONTEXT_PROFILE_COMPATIBILITYwith request-driven profile (COREfor migration target); - apply version negotiation strategy consistent with Windows path;
- set debug/forward-compatible flags from request.
- replace
Exit criteria:
- SDL backend creates core profile when requested.
- No hardcoded compatibility profile remains.
Goal: remove client-memory vertex submission dependency and enforce VAO/VBO-ready path.
Changes:
- Apus.Engine.OpenGL.pas:
- add and bind a default VAO for core profile;
- refactor
TRenderDevice.SetupAttributesso attribute pointers are bound againstGL_ARRAY_BUFFERdata, not raw CPU pointers; - add explicit path for buffer-backed draws and isolate temporary staging fallback.
- Apus.Engine.Graphics.pas: extend
IRenderDevicecontract for buffer-based draw calls (or dedicated bind phase). - Apus.Engine.ResManGL.pas: ensure VBO/IBO bind helpers are used by draw path, not just by callers.
Exit criteria:
- Main draw path does not rely on client-side vertex arrays.
- Core profile draw calls execute without
GL_INVALID_OPERATION.
Ordering note:
- This stage should be completed before enabling core profile as default in platform context creation (Stages 2-3 rollout switch).
Goal: eliminate remaining RAM-fed hotspots used by SimpleDemo and core scenes.
Changes:
- Apus.Engine.Draw.pas:
- move particles/bands/common quads to dynamic VBO/IBO ring buffers;
- keep API shape, change backend submission.
- Apus.Engine.TextDraw.pas: migrate glyph quad batching to VBO path.
- Apus.Engine.Mesh.pas: verify mesh path always uses index/vertex buffers.
Exit criteria:
SimpleDemorender passes (UI, lines, particles, text) use GPU buffers.- No frequent per-draw CPU pointer feed in profiler/logs for these paths.
Goal: remove compatibility-era GLSL assumptions from active path.
Changes:
- Apus.Engine.PainterGL2.pas:
- update old shader strings (
attribute/varying/gl_FragColor/texture2D) to core-compatible syntax, or route 2D path throughShadersGL.
- update old shader strings (
- Apus.Engine.ShadersGL.pas: keep unified
#version 330style and explicitin/out.
Exit criteria:
- Active shaders compile under core profile without compatibility features.
- No runtime fallback to compatibility GLSL syntax.
Goal: make frame debugging useful in NSight.
Changes:
- Apus.Engine.OpenGL.pas:
- enable
glDebugMessageCallback/filtering in debug context; - add debug groups around frame/scene/UI/particles passes.
- enable
- Apus.Engine.ResManGL.pas:
- label textures, buffers, FBOs via
glObjectLabelfrom engine resource names.
- label textures, buffers, FBOs via
- Apus.Engine.Draw.pas: add targeted markers for major batches.
Exit criteria:
- NSight capture shows named resources and meaningful event grouping.
- GL debug output surfaces real API mistakes during development builds.
Goal: guarantee reproducible NSight-friendly launch configuration.
Changes:
- demo/SimpleDemo/SimpleDemoApp.pas: request core+debug context in demo settings (dev build path).
- demo/SimpleDemo/SimpleDemo.dproj:
- add/adjust a dedicated
NSightconfig (Win64, debug info enabled, optimizations disabled).
- add/adjust a dedicated
- demo/SimpleDemo/SimpleDemo.lpi: optional matching FPC profile if needed for parity checks.
Exit criteria:
SimpleDemostarts with core debug context in Win64 config.- NSight can attach/capture with symbols and stable frame structure.
Goal: lock migration result and remove temporary fallback paths.
Changes:
- engine5_feature_roadmap.md: update R-01 status and acceptance checklist.
- engine_work_ahead.md: log completed migration steps and remaining blockers.
- Optional cleanup: remove compatibility-only code paths that are no longer used.
Exit criteria:
- R-01 acceptance criteria reached:
- main render path does not require compatibility profile;
- targeted geometry submission uses VBO/IBO;
SimpleDemois inspectable in NSight with useful draw/resource visibility.
- Risk: hidden client-array usage in old utility paths.
- Mitigation: add runtime asserts in core mode when
GL_ARRAY_BUFFER=0during attribute setup.
- Mitigation: add runtime asserts in core mode when
- Risk: shader regressions on older drivers.
- Mitigation: keep explicit core-version requirement and clear startup diagnostics.
- Risk: performance regressions due to naive dynamic buffer updates.
- Mitigation: use ring-buffer strategy and batch size telemetry.
- Stages 0-1 (API/toggle + negotiation contract, no default behavior break).
- Stages 4-5 (remove client-array dependence and migrate SimpleDemo-critical draw paths to VBO/IBO).
- Stage 6 (core-compatible shader path cleanup).
- Stages 2-3 (enable core-context creation path and make it default after validation).
- Stages 7-8 (NSight instrumentation/profile).
- Stage 9 (cleanup and roadmap status updates).
- Stage 0: baseline request/actual logging + app-level request toggles added.
- Stage 1:
ISystemPlatform.CreateOpenGLContextextended to request/actual contract; Windows + SDL backends migrated to new signature. - Stage 2-3: core-context startup path enabled and set as default policy:
Apus.Engine.WindowsPlatform: context creation now uses a temporary context to read available GL version, then performs a single modern-context creation attempt from that negotiated version/request settings (no version-by-version probing loop).Apus.Engine.OpenGL: startup now fails explicitly ifcorewas requested but actual context is not core or request was not accepted.- default app-level request now targets core profile (
glCoreContext=true), while-GLCOMPATremains available as explicit override.
- Stage 4: core-profile safety baseline completed in
TRenderDevice:- default VAO creation/bind in core profile;
- runtime assert for missing
GL_ARRAY_BUFFERduring attribute setup in core mode; - automatic stream VBO/IBO fallback for RAM-fed draw calls;
- render-device bind tracking uses internal GL extension interface (
IRenderDeviceBindTracking), not backend-agnosticIRenderDevice.
- Stage 5: high-usage immediate paths now pass through stream/buffer-backed submission, reducing direct client-array dependency for
SimpleDemo-critical draw/text paths.Apus.Engine.TextDraw:FlushTextCachenow uploads to explicit dynamic VB + static IB and renders via bound buffers.Apus.Engine.Draw: 2DParticles(...)andBand(...)paths now upload to dynamic VB/IB and render via buffer-backed indexed draws.Apus.Engine.Draw: pointer-basedIndexedMesh(...)overloads now use dynamic scratch VB/IB upload + bound-buffer indexed draw (used byMesh/Model3Dimmediate paths).Apus.Engine.OpenGL.TRenderDevice: stream VBO/IBO fallback now covers pointer-fed draws in compatibility mode as well (whenGL_ARRAY_BUFFERis not pre-bound), while preserving explicit client-pointer fallback safety for mixed states.- Validation note:
SimpleDemobuild/runtime confirms text/particles/spinner paths; mesh-path runtime validation is pending a mesh-using demo target.
- Stage 6: shader syntax/core alignment completed for desktop/core render path.
Apus.Engine.PainterGL2: desktop shader strings switched to GLSL core-style (#version 330,in/out,texture, explicit fragment output), while preserving existing GLES shader path under{$IFDEF GLES}.- review follow-up: removed GL state queries from draw hot path (
TRenderDevicenow uses tracked array/element buffer bindings). - review follow-up: removed per-draw
MaxIndexInBufferscan from hot path; RAM-backed indexed stream upload is constrained to ranged overload with explicitvrtCount. Apus.Engine.ShadersGL: active desktop shader generation remains unified on#version 330with explicitin/out.- no compatibility GLSL fallback is used in desktop runtime path; legacy GLSL tokens remain only in GLES-conditional shader branch.
- Stage 7: NSight instrumentation baseline implemented.
Apus.Engine.OpenGL: debug callback wiring (KHR/ARB), notification filtering, frame/RT debug groups, present marker.Apus.Engine.ResManGL: object labels added for VBO/IBO/FBO allocations (textures were already labeled).
- Stage 8: SimpleDemo NSight profile implemented.
demo/SimpleDemo/SimpleDemo.dproj: added dedicatedNSightbuild configuration (debug info in image, optimizations disabled, stack frames enabled).
- Stage 9: validation and rollout updates completed for mandatory core-profile migration path.
SimpleDemoruns on core profile (runtime-confirmed).- migration status synchronized in roadmap/work-ahead docs.
- NSight runtime capture validation passed on
SimpleDemo(usable event grouping/resource visibility).
Issues found during code review of Stages 4-6 implementation. Bugs must be fixed before enabling core profile; optimization items can be deferred.
-
TIndexBufferGL.Resizebinds to wrong target (Apus.Engine.ResManGL)- Status: fixed.
Resizenow usesGL_ELEMENT_ARRAY_BUFFERand matching element-buffer tracking calls.
-
TrackArrayBufferBinding/TrackElementBufferBindinginIRenderDevice(Apus.Engine.Graphics)- Status: fixed.
- Tracking methods moved out of backend-agnostic
IRenderDeviceintoIRenderDeviceBindTrackinginternal extension interface. ResManGLnow uses optional interface query (Supports) for GL-only tracking.
-
Double bind/unbind churn in
ResManGLupload paths- Every
Upload/Resizedoes bind в†’ work в†’ unbind(0), thenTDrawerimmediately re-binds viaUseVertexBuffer. Two redundant state changes per draw. - Low priority: only matters at high draw-call counts.
- Every
-
Bind/draw/unbind boilerplate in
TDrawer- Every draw site in
Apus.Engine.Drawrepeats 5-line pattern:UseVB в†’ UseIB в†’ DrawIndexed в†’ UseVB(nil) в†’ UseIB(nil). - Extract a helper method to reduce repetition and state-leak risk.
- Every draw site in
-
bandIndarray over-allocated- Changed from
4*MaxParticleCountto6*MaxParticleCount(60KB at default 5000). Real worst case for band rendering is(segmentCount-1)*6which is typically much less. Low priority.
- Changed from
-
Shader source selection is compile-time only (
PainterGL2){$IFDEF GLES}splits shader strings at compile time. Desktop build always gets#version 330even in compatibility context.- Works (compatibility contexts accept
#version 330), but runtime selection based onoglContextInfowould be cleaner and more flexible.
-
Whitespace noise in diffs
- Stages 4-6 commits contain ~30-40% whitespace-only line changes mixed with real changes. Complicates review and pollutes
git blame. - Enforce: separate formatting commits or avoid unnecessary whitespace changes entirely.
- Stages 4-6 commits contain ~30-40% whitespace-only line changes mixed with real changes. Complicates review and pollutes
-
Stream VBO growth strategy
- Current power-of-2 growth with
GL_STREAM_DRAW+glBufferSubDatais correct and sufficient for SimpleDemo. - For higher draw-call workloads, consider orphaning (
glBufferDatawith new size each frame) or persistent mapping (GL_MAP_PERSISTENT_BIT, requires GL 4.4) as future optimization.
- Current power-of-2 growth with