diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 8dc838d..8bd1d74 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -371,8 +371,8 @@ Known local toolchain state: filters/wraps, and render-target formats resolve through backend-owned overloads. The Windows entrypoint also consumes backend-owned generic OpenGL - error-code/info-string tokens and WGL core-context/pixel-format attribute - catalogs. + error-code/info-string tokens, runtime string query ordering, and WGL + core-context/pixel-format attribute catalogs. The headless OpenGL command planner consumes `pp_renderer_api` recorded commands and maps render-pass clear masks/values, viewport/scissor state, blend/depth/sampler state, texture formats, primitive modes, draw counts, and @@ -391,9 +391,10 @@ Known local toolchain state: counts so automation can assert backend interpretation without an OpenGL context. Desktop VR drawing also consumes backend-owned scissor/depth/blend state, - depth clear masks, active texture units, and fallback 2D texture unbind - targets; VR SDK start/stop now dispatches through `PlatformServices` while - retaining the existing Windows OpenVR bridge shape. + blend/depth state query-restore, depth clear masks, active texture units, + and fallback 2D texture unbind targets; VR SDK start/stop now dispatches + through `PlatformServices` while retaining the existing Windows OpenVR bridge + shape. Canvas mode overlay, mask, and transform paths also consume backend-owned blend/depth state, active texture units, 2D texture copy targets, and RGBA8 readback format tokens. @@ -599,12 +600,13 @@ Known local toolchain state: shared layout parser. - `pp_renderer_gl` owns the tested `OpenGlInitialState` startup depth/blend policy and dispatch application consumed by `App::init`, tested runtime - version/vendor/renderer/GLSL string query dispatch, tested default clear + version/vendor/renderer/GLSL string query dispatch consumed by `App::init` + and Windows startup logging/title construction, tested default clear color/buffer dispatch consumed by `App::clear`, tested app UI viewport/scissor dispatch consumed by `App::draw` and `App::vr_draw_ui`, - tested generic capability/buffer-clear dispatch consumed by VR draw state - setup, tested saved-state snapshot/restore dispatch consumed by the retained - `gl_state` utility, tested texture lifecycle/readback dispatch consumed by + tested generic capability query/apply and buffer-clear dispatch consumed by + VR draw state setup and restore, tested saved-state snapshot/restore dispatch + consumed by the retained `gl_state` utility, tested texture lifecycle/readback dispatch consumed by the retained `Texture2D` utility, tested framebuffer blit/readback dispatch consumed by retained `RTT` resize/copy/readback paths, tested framebuffer bind/restore dispatch consumed by retained `RTT` render-target pass entry diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index c608c3a..05ffd4d 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -781,7 +781,9 @@ delegates to the backend dispatch path instead of hard-coding the policy or operation order. OpenGL runtime version/vendor/renderer/GLSL string queries now also use a tested `pp_renderer_gl` dispatch contract, leaving `App::init` to log the -result while the backend owns the query set and order. +result while the backend owns the query set and order. The Windows entrypoint +also uses that contract for early context logging and renderer-name window +title construction before replacing the temporary WGL context. The default app clear color and color-buffer clear operation now dispatch through `pp_renderer_gl` as well, moving another direct OpenGL operation out of `App::clear` while preserving the current gray clear behavior. @@ -792,10 +794,10 @@ the live OpenGL call sequence. VR UI framebuffer viewport and scissor-test setup now also consumes those `pp_renderer_gl` contracts, keeping desktop and VR UI rendering aligned while the retained OpenVR app path is split incrementally. -VR draw blend/depth state transitions and depth-buffer clears now use generic -tested `pp_renderer_gl` capability and clear dispatch contracts, reducing -direct OpenGL execution in the retained VR app path without changing state -restore behavior. +VR draw blend/depth state snapshots, transitions, restore, and depth-buffer +clears now use generic tested `pp_renderer_gl` capability query/apply and clear +dispatch contracts, reducing direct OpenGL execution in the retained VR app +path without changing state restore behavior. The retained `gl_state` save/restore utility now snapshots and restores through tested `pp_renderer_gl` saved-state dispatch contracts, covering capability state, viewport, clear color, framebuffer/program bindings, active texture, @@ -1081,7 +1083,8 @@ longer expose raw OpenGL enum defaults; default texture formats, sampler filters/wraps, and render-target formats are resolved through backend-owned overloads. The Windows entrypoint now delegates generic OpenGL error-code/info-string -tokens and WGL core-context/pixel-format attribute catalogs to `pp_renderer_gl`. +tokens, runtime string query ordering, and WGL core-context/pixel-format +attribute catalogs to `pp_renderer_gl`. The headless OpenGL command planner now consumes `pp_renderer_api` recorded commands and maps render-pass clear masks/values, viewport/scissor state, blend/depth/sampler state, texture formats, primitive modes, draw counts, and @@ -1966,9 +1969,9 @@ Results: - Android arm64 configured with NDK 29.0.14206865 through the platform-build wrapper and compiled headless foundation/tool/test targets. - Desktop VR drawing now routes generic OpenGL scissor/depth/blend state, - depth clears, active texture units, and fallback 2D texture unbinds through - the renderer GL backend mapping; platform VR SDK bridges remain isolated for - later platform-shell extraction. + blend/depth state snapshots and restores, depth clears, active texture units, + and fallback 2D texture unbinds through the renderer GL backend mapping; + platform VR SDK bridges remain isolated for later platform-shell extraction. - Canvas mode overlay, mask, and transform paths now route generic OpenGL blend/depth state, active texture units, 2D copy targets, and RGBA8 readback formats through the renderer GL backend mapping. diff --git a/src/app_vr.cpp b/src/app_vr.cpp index 7c832b7..3f0d4ea 100644 --- a/src/app_vr.cpp +++ b/src/app_vr.cpp @@ -29,6 +29,11 @@ void disable_opengl_state(std::uint32_t state) noexcept glDisable(static_cast(state)); } +std::uint8_t is_opengl_state_enabled(std::uint32_t state) noexcept +{ + return static_cast(glIsEnabled(static_cast(state))); +} + void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept { glViewport(static_cast(x), static_cast(y), static_cast(width), static_cast(height)); @@ -75,6 +80,20 @@ void apply_vr_render_capability(std::uint32_t state, bool enabled) LOG("OpenGL VR render state failed: %s", status.message); } +bool query_vr_render_capability(std::uint32_t state) +{ + const auto result = pp::renderer::gl::query_opengl_capability_state( + state, + pp::renderer::gl::OpenGlCapabilityStateQueryDispatch { + .is_enabled = is_opengl_state_enabled, + }); + if (!result.ok()) { + LOG("OpenGL VR render state query failed: %s", result.status().message); + return false; + } + return result.value(); +} + void clear_vr_depth_buffer() { const auto status = pp::renderer::gl::clear_opengl_buffers( @@ -262,8 +281,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat glm::vec3 origin = glm::vec3(0, 0, -1) * glm::transpose(glm::mat3(pose)); vr_rot = glm::lookAt({ 0, 0, 0 }, origin, { 0, 1, 0 }); - auto blend = glIsEnabled(pp::renderer::gl::blend_state()); - auto depth = glIsEnabled(pp::renderer::gl::depth_test_state()); + const bool blend = query_vr_render_capability(pp::renderer::gl::blend_state()); + const bool depth = query_vr_render_capability(pp::renderer::gl::depth_test_state()); apply_vr_render_capability(pp::renderer::gl::blend_state(), false); apply_vr_render_capability(pp::renderer::gl::depth_test_state(), false); @@ -556,8 +575,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat mode->on_Draw(ortho_proj, proj, camera); */ - apply_vr_render_capability(pp::renderer::gl::blend_state(), blend != 0U); - apply_vr_render_capability(pp::renderer::gl::depth_test_state(), depth != 0U); + apply_vr_render_capability(pp::renderer::gl::blend_state(), blend); + apply_vr_render_capability(pp::renderer::gl::depth_test_state(), depth); sampler.unbind(); } diff --git a/src/main.cpp b/src/main.cpp index fd654c2..ec9b9a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,11 @@ void win32_renderdoc_frame_start() { } void win32_renderdoc_frame_end() { } #endif +const char* query_opengl_string(std::uint32_t name) noexcept +{ + return reinterpret_cast(glGetString(static_cast(name))); +} + HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY); HRESULT(*SetProcessDpiAwareness_fn)(PROCESS_DPI_AWARENESS value); void init_shcore_API() @@ -853,17 +858,28 @@ int main(int argc, char** argv) return 0; } - LOG("GL version: %s", glGetString(static_cast(pp::renderer::gl::version_string_name()))); - LOG("GL vendor: %s", glGetString(static_cast(pp::renderer::gl::vendor_string_name()))); - LOG("GL renderer: %s", glGetString(static_cast(pp::renderer::gl::renderer_string_name()))); + auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {}; + const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info( + pp::renderer::gl::OpenGlRuntimeInfoDispatch { + .get_string = query_opengl_string, + }); + if (runtime_info_result.ok()) { + runtime_info = runtime_info_result.value(); + LOG("GL version: %s", runtime_info.version); + LOG("GL vendor: %s", runtime_info.vendor); + LOG("GL renderer: %s", runtime_info.renderer); + } else { + LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message); + } #ifdef USE_RENDERDOC if (!win32_renderdoc_init()) LOG("Renderdoc not started"); #endif // USE_RENDERDOC + const auto renderer_name = std::string(runtime_info.renderer != nullptr ? runtime_info.renderer : ""); swprintf_s(window_title, L"PanoPainter %s (%s)", g_version_number_w, - str2wstr((char*)glGetString(static_cast(pp::renderer::gl::renderer_string_name()))).c_str()); + str2wstr(renderer_name).c_str()); // If supported create a 3.3 context if (GLAD_WGL_ARB_create_context) diff --git a/src/renderer_gl/opengl_capabilities.cpp b/src/renderer_gl/opengl_capabilities.cpp index a77db2b..83d5bd5 100644 --- a/src/renderer_gl/opengl_capabilities.cpp +++ b/src/renderer_gl/opengl_capabilities.cpp @@ -526,6 +526,18 @@ pp::foundation::Status apply_opengl_capability( return pp::foundation::Status::success(); } +pp::foundation::Result query_opengl_capability_state( + std::uint32_t state, + OpenGlCapabilityStateQueryDispatch dispatch) noexcept +{ + if (dispatch.is_enabled == nullptr) { + return pp::foundation::Result::failure( + pp::foundation::Status::invalid_argument("OpenGL capability state query callback must not be null")); + } + + return pp::foundation::Result::success(dispatch.is_enabled(state) != 0U); +} + pp::foundation::Status apply_opengl_render_platform_hints( OpenGlRenderPlatformHintDispatch dispatch) noexcept { diff --git a/src/renderer_gl/opengl_capabilities.h b/src/renderer_gl/opengl_capabilities.h index 45f3492..6902551 100644 --- a/src/renderer_gl/opengl_capabilities.h +++ b/src/renderer_gl/opengl_capabilities.h @@ -530,6 +530,10 @@ struct OpenGlCapabilityDispatch { OpenGlCapabilityFn disable = nullptr; }; +struct OpenGlCapabilityStateQueryDispatch { + OpenGlIsEnabledFn is_enabled = nullptr; +}; + struct OpenGlRenderPlatformHintDispatch { OpenGlCapabilityFn enable = nullptr; }; @@ -760,6 +764,9 @@ struct OpenGlMeshDeleteDispatch { std::uint32_t state, bool enabled, OpenGlCapabilityDispatch dispatch) noexcept; +[[nodiscard]] pp::foundation::Result query_opengl_capability_state( + std::uint32_t state, + OpenGlCapabilityStateQueryDispatch dispatch) noexcept; [[nodiscard]] pp::foundation::Status apply_opengl_render_platform_hints( OpenGlRenderPlatformHintDispatch dispatch) noexcept; [[nodiscard]] pp::foundation::Status apply_opengl_debug_output_states( diff --git a/tests/renderer_gl/capabilities_tests.cpp b/tests/renderer_gl/capabilities_tests.cpp index 5ddd684..7379541 100644 --- a/tests/renderer_gl/capabilities_tests.cpp +++ b/tests/renderer_gl/capabilities_tests.cpp @@ -2246,6 +2246,35 @@ void rejects_incomplete_generic_capability_dispatch(pp::tests::Harness& h) PP_EXPECT(h, status.code == pp::foundation::StatusCode::invalid_argument); } +void queries_generic_capability_state(pp::tests::Harness& h) +{ + const auto blend = pp::renderer::gl::query_opengl_capability_state( + 0x0BE2U, + pp::renderer::gl::OpenGlCapabilityStateQueryDispatch { + .is_enabled = record_is_enabled, + }); + const auto depth = pp::renderer::gl::query_opengl_capability_state( + 0x0B71U, + pp::renderer::gl::OpenGlCapabilityStateQueryDispatch { + .is_enabled = record_is_enabled, + }); + + PP_EXPECT(h, blend.ok()); + PP_EXPECT(h, blend.value()); + PP_EXPECT(h, depth.ok()); + PP_EXPECT(h, !depth.value()); +} + +void rejects_incomplete_generic_capability_state_query(pp::tests::Harness& h) +{ + const auto result = pp::renderer::gl::query_opengl_capability_state( + 0x0BE2U, + pp::renderer::gl::OpenGlCapabilityStateQueryDispatch {}); + + PP_EXPECT(h, !result.ok()); + PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument); +} + void applies_render_platform_hints(pp::tests::Harness& h) { recorded_state_calls.clear(); @@ -4435,6 +4464,8 @@ int main() harness.run("rejects_incomplete_scissor_test_dispatch", rejects_incomplete_scissor_test_dispatch); harness.run("applies_generic_capability_dispatch", applies_generic_capability_dispatch); harness.run("rejects_incomplete_generic_capability_dispatch", rejects_incomplete_generic_capability_dispatch); + harness.run("queries_generic_capability_state", queries_generic_capability_state); + harness.run("rejects_incomplete_generic_capability_state_query", rejects_incomplete_generic_capability_state_query); harness.run("applies_render_platform_hints", applies_render_platform_hints); harness.run("rejects_incomplete_render_platform_hint_dispatch", rejects_incomplete_render_platform_hint_dispatch); harness.run("applies_debug_output_states", applies_debug_output_states);