Route VR and startup GL state through backend

This commit is contained in:
2026-06-04 20:37:38 +02:00
parent 967a15f15f
commit f55b1882c0
7 changed files with 116 additions and 26 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -29,6 +29,11 @@ void disable_opengl_state(std::uint32_t state) noexcept
glDisable(static_cast<GLenum>(state));
}
std::uint8_t is_opengl_state_enabled(std::uint32_t state) noexcept
{
return static_cast<std::uint8_t>(glIsEnabled(static_cast<GLenum>(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<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(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();
}

View File

@@ -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<const char*>(glGetString(static_cast<GLenum>(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<GLenum>(pp::renderer::gl::version_string_name())));
LOG("GL vendor: %s", glGetString(static_cast<GLenum>(pp::renderer::gl::vendor_string_name())));
LOG("GL renderer: %s", glGetString(static_cast<GLenum>(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<GLenum>(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)

View File

@@ -526,6 +526,18 @@ pp::foundation::Status apply_opengl_capability(
return pp::foundation::Status::success();
}
pp::foundation::Result<bool> query_opengl_capability_state(
std::uint32_t state,
OpenGlCapabilityStateQueryDispatch dispatch) noexcept
{
if (dispatch.is_enabled == nullptr) {
return pp::foundation::Result<bool>::failure(
pp::foundation::Status::invalid_argument("OpenGL capability state query callback must not be null"));
}
return pp::foundation::Result<bool>::success(dispatch.is_enabled(state) != 0U);
}
pp::foundation::Status apply_opengl_render_platform_hints(
OpenGlRenderPlatformHintDispatch dispatch) noexcept
{

View File

@@ -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<bool> 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(

View File

@@ -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);