Move shader feature negotiation into renderer backend
This commit is contained in:
@@ -793,7 +793,11 @@ Known warnings after the current CMake app build:
|
|||||||
so app shader startup asks the backend for desktop GL/GLES/WebGL policy
|
so app shader startup asks the backend for desktop GL/GLES/WebGL policy
|
||||||
instead of carrying local platform branches. It also owns headless-tested
|
instead of carrying local platform branches. It also owns headless-tested
|
||||||
OpenGL extension enumeration through `query_opengl_extensions`, moving the
|
OpenGL extension enumeration through `query_opengl_extensions`, moving the
|
||||||
extension count/string query loop out of `app_shaders.cpp`.
|
extension count/string query loop out of `app_shaders.cpp`. Startup feature
|
||||||
|
negotiation now uses `query_opengl_capability_detection`, so extension
|
||||||
|
enumeration, runtime capability policy, and renderer-neutral feature
|
||||||
|
conversion are validated together before the retained `ShaderManager` static
|
||||||
|
flags are updated.
|
||||||
- `pp_legacy_ui_core` is an object-library containment boundary because the
|
- `pp_legacy_ui_core` is an object-library containment boundary because the
|
||||||
retained base `Node` controls still depend on legacy renderer and app
|
retained base `Node` controls still depend on legacy renderer and app
|
||||||
headers. It should shrink as layout parsing, colors, generic controls, and
|
headers. It should shrink as layout parsing, colors, generic controls, and
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, and direct command execution is centralized in `src/legacy_app_shell_services.*`; SonarPen availability/startup now routes through `PlatformServices`, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and rely on the legacy platform adapter for the retained iOS SonarPen bridge | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pp_platform_api_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter |
|
| DEBT-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, and direct command execution is centralized in `src/legacy_app_shell_services.*`; SonarPen availability/startup now routes through `PlatformServices`, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and rely on the legacy platform adapter for the retained iOS SonarPen bridge | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pp_platform_api_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter |
|
||||||
| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter |
|
| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter |
|
||||||
| DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy open/save/settings/message-box dialogs and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter |
|
| DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy open/save/settings/message-box dialogs and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter |
|
||||||
| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::render_device_features` as the backend conversion point. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. Actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, and brush-preview compositing still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior |
|
| DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. Actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, and brush-preview compositing still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior |
|
||||||
| DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, PBO readback through `App::rec_loop`, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters |
|
| DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, PBO readback through `App::rec_loop`, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters |
|
||||||
| DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download`/license curl helpers now ask `PlatformServices` for the Android TLS-verification bypass policy, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, progress/message UI, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters |
|
| DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download`/license curl helpers now ask `PlatformServices` for the Android TLS-verification bypass policy, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, progress/message UI, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pp_platform_api_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters |
|
||||||
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |
|
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts before delegating import execution to `src/legacy_brush_package_import_services.*`, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |
|
||||||
|
|||||||
@@ -680,6 +680,13 @@ OpenGL extension enumeration now also lives in `pp_renderer_gl` through a
|
|||||||
dispatch-tested `query_opengl_extensions` helper; shader startup still logs and
|
dispatch-tested `query_opengl_extensions` helper; shader startup still logs and
|
||||||
applies the resulting feature flags, but the GL extension query loop is no
|
applies the resulting feature flags, but the GL extension query loop is no
|
||||||
longer app-owned.
|
longer app-owned.
|
||||||
|
OpenGL shader startup feature negotiation now also flows through
|
||||||
|
`pp_renderer_gl::query_opengl_capability_detection` and
|
||||||
|
`detect_opengl_feature_state`, so extension enumeration, desktop/GLES/WebGL
|
||||||
|
capability policy, and renderer-neutral feature conversion are tested together
|
||||||
|
behind the backend boundary. `App::initShaders` remains a legacy adapter that
|
||||||
|
copies the backend-owned feature snapshot into retained `ShaderManager` static
|
||||||
|
flags until `ShaderManager` itself becomes an OpenGL backend service.
|
||||||
Prepared-file save/download handoff is now also part of the service contract,
|
Prepared-file save/download handoff is now also part of the service contract,
|
||||||
so iOS/Web export completion routes through `PlatformServices` after the app
|
so iOS/Web export completion routes through `PlatformServices` after the app
|
||||||
writes the temporary/exported payload.
|
writes the temporary/exported payload.
|
||||||
|
|||||||
@@ -6,15 +6,14 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] pp::renderer::gl::OpenGlCapabilities shader_manager_capabilities() noexcept
|
void apply_shader_manager_feature_state(pp::renderer::gl::OpenGlFeatureState feature_state) noexcept
|
||||||
{
|
{
|
||||||
pp::renderer::gl::OpenGlCapabilities capabilities;
|
ShaderManager::ext_framebuffer_fetch = feature_state.capabilities.framebuffer_fetch;
|
||||||
capabilities.framebuffer_fetch = ShaderManager::ext_framebuffer_fetch;
|
ShaderManager::ext_map_aligned = feature_state.capabilities.map_buffer_alignment;
|
||||||
capabilities.map_buffer_alignment = ShaderManager::ext_map_aligned;
|
ShaderManager::ext_float32 = feature_state.capabilities.float32_textures;
|
||||||
capabilities.float32_textures = ShaderManager::ext_float32;
|
ShaderManager::ext_float32_linear = feature_state.capabilities.float32_linear;
|
||||||
capabilities.float32_linear = ShaderManager::ext_float32_linear;
|
ShaderManager::ext_float16 = feature_state.capabilities.float16_textures;
|
||||||
capabilities.float16_textures = ShaderManager::ext_float16;
|
ShaderManager::set_render_device_features(feature_state.features);
|
||||||
return capabilities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void query_gl_integer(std::uint32_t name, std::int32_t* value) noexcept
|
void query_gl_integer(std::uint32_t name, std::int32_t* value) noexcept
|
||||||
@@ -40,42 +39,28 @@ void App::initShaders()
|
|||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
|
|
||||||
render_task([] {
|
render_task([] {
|
||||||
const auto extensions_result = pp::renderer::gl::query_opengl_extensions(
|
const auto detection_result = pp::renderer::gl::query_opengl_capability_detection(
|
||||||
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||||
.get_integer = query_gl_integer,
|
.get_integer = query_gl_integer,
|
||||||
.get_string_indexed = query_gl_string_indexed,
|
.get_string_indexed = query_gl_string_indexed,
|
||||||
});
|
},
|
||||||
if (!extensions_result.ok()) {
|
pp::renderer::gl::opengl_runtime_for_current_build());
|
||||||
LOG("OpenGL extension query failed: %s", extensions_result.status().message);
|
if (!detection_result.ok()) {
|
||||||
|
LOG("OpenGL capability detection failed: %s", detection_result.status().message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& extension_storage = extensions_result.value();
|
const auto& detection = detection_result.value();
|
||||||
std::vector<std::string_view> extension_views;
|
for (const auto& extension : detection.extensions) {
|
||||||
extension_views.reserve(extension_storage.size());
|
|
||||||
for (const auto& extension : extension_storage) {
|
|
||||||
extension_views.push_back(extension);
|
|
||||||
LOG("EXT: %s", extension.c_str());
|
LOG("EXT: %s", extension.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto runtime = pp::renderer::gl::opengl_runtime_for_current_build();
|
apply_shader_manager_feature_state(detection.feature_state);
|
||||||
const auto capabilities = pp::renderer::gl::detect_opengl_capabilities(extension_views, runtime);
|
|
||||||
ShaderManager::ext_framebuffer_fetch = capabilities.framebuffer_fetch;
|
|
||||||
ShaderManager::ext_map_aligned = capabilities.map_buffer_alignment;
|
|
||||||
ShaderManager::ext_float32 = capabilities.float32_textures;
|
|
||||||
ShaderManager::ext_float32_linear = capabilities.float32_linear;
|
|
||||||
ShaderManager::ext_float16 = capabilities.float16_textures;
|
|
||||||
ShaderManager::set_render_device_features(pp::renderer::gl::render_device_features(capabilities));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (pp::renderer::gl::opengl_runtime_for_current_build().desktop_gl) {
|
apply_shader_manager_feature_state(pp::renderer::gl::detect_opengl_feature_state(
|
||||||
// In OpenGL 3.3 these should be already available.
|
std::span<const std::string_view> {},
|
||||||
ShaderManager::ext_float32_linear = true;
|
pp::renderer::gl::opengl_runtime_for_current_build()));
|
||||||
ShaderManager::ext_float32 = true;
|
|
||||||
ShaderManager::ext_float16 = true;
|
|
||||||
}
|
|
||||||
ShaderManager::set_render_device_features(
|
|
||||||
pp::renderer::gl::render_device_features(shader_manager_capabilities()));
|
|
||||||
|
|
||||||
LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled");
|
LOG("Shader Extension shader_framebuffer_fetch: %s", ShaderManager::ext_framebuffer_fetch ? "enabled" : "disabled");
|
||||||
|
|
||||||
|
|||||||
@@ -228,6 +228,17 @@ pp::renderer::RenderDeviceFeatures render_device_features(OpenGlCapabilities cap
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpenGlFeatureState detect_opengl_feature_state(
|
||||||
|
std::span<const std::string_view> extensions,
|
||||||
|
OpenGlRuntime runtime) noexcept
|
||||||
|
{
|
||||||
|
const auto capabilities = detect_opengl_capabilities(extensions, runtime);
|
||||||
|
return OpenGlFeatureState {
|
||||||
|
.capabilities = capabilities,
|
||||||
|
.features = render_device_features(capabilities),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
OpenGlInitialState panopainter_initial_state() noexcept
|
OpenGlInitialState panopainter_initial_state() noexcept
|
||||||
{
|
{
|
||||||
return OpenGlInitialState {
|
return OpenGlInitialState {
|
||||||
@@ -369,6 +380,28 @@ pp::foundation::Result<std::vector<std::string>> query_opengl_extensions(
|
|||||||
return pp::foundation::Result<std::vector<std::string>>::success(std::move(extensions));
|
return pp::foundation::Result<std::vector<std::string>>::success(std::move(extensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<OpenGlCapabilityDetectionResult> query_opengl_capability_detection(
|
||||||
|
OpenGlExtensionQueryDispatch dispatch,
|
||||||
|
OpenGlRuntime runtime)
|
||||||
|
{
|
||||||
|
auto extensions_result = query_opengl_extensions(dispatch);
|
||||||
|
if (!extensions_result.ok()) {
|
||||||
|
return pp::foundation::Result<OpenGlCapabilityDetectionResult>::failure(extensions_result.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto extensions = std::move(extensions_result.value());
|
||||||
|
std::vector<std::string_view> extension_views;
|
||||||
|
extension_views.reserve(extensions.size());
|
||||||
|
for (const auto& extension : extensions) {
|
||||||
|
extension_views.push_back(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<OpenGlCapabilityDetectionResult>::success(OpenGlCapabilityDetectionResult {
|
||||||
|
.extensions = std::move(extensions),
|
||||||
|
.feature_state = detect_opengl_feature_state(extension_views, runtime),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
OpenGlDefaultClear panopainter_default_clear() noexcept
|
OpenGlDefaultClear panopainter_default_clear() noexcept
|
||||||
{
|
{
|
||||||
return OpenGlDefaultClear {
|
return OpenGlDefaultClear {
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ struct OpenGlCapabilities {
|
|||||||
bool float16_textures = false;
|
bool float16_textures = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct OpenGlFeatureState {
|
||||||
|
OpenGlCapabilities capabilities;
|
||||||
|
pp::renderer::RenderDeviceFeatures features;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OpenGlCapabilityDetectionResult {
|
||||||
|
std::vector<std::string> extensions;
|
||||||
|
OpenGlFeatureState feature_state;
|
||||||
|
};
|
||||||
|
|
||||||
struct OpenGlPixelFormat {
|
struct OpenGlPixelFormat {
|
||||||
std::uint32_t internal_format = 0;
|
std::uint32_t internal_format = 0;
|
||||||
std::uint32_t pixel_format = 0;
|
std::uint32_t pixel_format = 0;
|
||||||
@@ -689,6 +699,12 @@ struct OpenGlMeshDeleteDispatch {
|
|||||||
[[nodiscard]] OpenGlRuntime opengl_runtime_for_current_build() noexcept;
|
[[nodiscard]] OpenGlRuntime opengl_runtime_for_current_build() noexcept;
|
||||||
[[nodiscard]] pp::renderer::RenderDeviceFeatures render_device_features(
|
[[nodiscard]] pp::renderer::RenderDeviceFeatures render_device_features(
|
||||||
OpenGlCapabilities capabilities) noexcept;
|
OpenGlCapabilities capabilities) noexcept;
|
||||||
|
[[nodiscard]] OpenGlFeatureState detect_opengl_feature_state(
|
||||||
|
std::span<const std::string_view> extensions,
|
||||||
|
OpenGlRuntime runtime) noexcept;
|
||||||
|
[[nodiscard]] pp::foundation::Result<OpenGlCapabilityDetectionResult> query_opengl_capability_detection(
|
||||||
|
OpenGlExtensionQueryDispatch dispatch,
|
||||||
|
OpenGlRuntime runtime);
|
||||||
[[nodiscard]] OpenGlInitialState panopainter_initial_state() noexcept;
|
[[nodiscard]] OpenGlInitialState panopainter_initial_state() noexcept;
|
||||||
[[nodiscard]] pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispatch) noexcept;
|
[[nodiscard]] pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispatch) noexcept;
|
||||||
[[nodiscard]] pp::foundation::Result<OpenGlSavedState> snapshot_opengl_state(
|
[[nodiscard]] pp::foundation::Result<OpenGlSavedState> snapshot_opengl_state(
|
||||||
|
|||||||
@@ -1025,6 +1025,40 @@ void treats_desktop_gl_float_rendering_as_core(pp::tests::Harness& h)
|
|||||||
PP_EXPECT(h, features.float32_render_targets);
|
PP_EXPECT(h, features.float32_render_targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void detects_feature_state_from_extension_capabilities(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
constexpr std::array<std::string_view, 2> extensions {
|
||||||
|
"GL_EXT_shader_framebuffer_fetch",
|
||||||
|
"GL_ARB_map_buffer_alignment",
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto feature_state = pp::renderer::gl::detect_opengl_feature_state(
|
||||||
|
extensions,
|
||||||
|
pp::renderer::gl::OpenGlRuntime {});
|
||||||
|
|
||||||
|
PP_EXPECT(h, feature_state.capabilities.framebuffer_fetch);
|
||||||
|
PP_EXPECT(h, feature_state.capabilities.map_buffer_alignment);
|
||||||
|
PP_EXPECT(h, !feature_state.capabilities.float32_textures);
|
||||||
|
PP_EXPECT(h, feature_state.features.framebuffer_fetch);
|
||||||
|
PP_EXPECT(h, feature_state.features.texture_copy);
|
||||||
|
PP_EXPECT(h, feature_state.features.render_target_blit);
|
||||||
|
PP_EXPECT(h, !feature_state.features.float32_render_targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void detects_desktop_feature_state_without_extensions(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto feature_state = pp::renderer::gl::detect_opengl_feature_state(
|
||||||
|
{},
|
||||||
|
pp::renderer::gl::OpenGlRuntime { .desktop_gl = true });
|
||||||
|
|
||||||
|
PP_EXPECT(h, !feature_state.capabilities.framebuffer_fetch);
|
||||||
|
PP_EXPECT(h, feature_state.capabilities.float32_textures);
|
||||||
|
PP_EXPECT(h, feature_state.capabilities.float32_linear);
|
||||||
|
PP_EXPECT(h, feature_state.capabilities.float16_textures);
|
||||||
|
PP_EXPECT(h, feature_state.features.float32_render_targets);
|
||||||
|
PP_EXPECT(h, feature_state.features.float16_render_targets);
|
||||||
|
}
|
||||||
|
|
||||||
void detects_gles_texture_float_extensions(pp::tests::Harness& h)
|
void detects_gles_texture_float_extensions(pp::tests::Harness& h)
|
||||||
{
|
{
|
||||||
constexpr std::array<std::string_view, 3> extensions {
|
constexpr std::array<std::string_view, 3> extensions {
|
||||||
@@ -1890,6 +1924,58 @@ void rejects_incomplete_extension_query_dispatch(pp::tests::Harness& h)
|
|||||||
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
|
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void queries_app_capability_detection(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
recorded_integer_queries.clear();
|
||||||
|
recorded_indexed_string_queries.clear();
|
||||||
|
recorded_extension_count = 3;
|
||||||
|
|
||||||
|
const auto result = pp::renderer::gl::query_opengl_capability_detection(
|
||||||
|
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||||
|
.get_integer = record_get_integer,
|
||||||
|
.get_string_indexed = record_indexed_string_query,
|
||||||
|
},
|
||||||
|
pp::renderer::gl::OpenGlRuntime {});
|
||||||
|
|
||||||
|
PP_EXPECT(h, result.ok());
|
||||||
|
PP_EXPECT(h, recorded_integer_queries.size() == 1U);
|
||||||
|
PP_EXPECT(h, recorded_integer_queries[0] == 0x821DU);
|
||||||
|
PP_EXPECT(h, recorded_indexed_string_queries.size() == 3U);
|
||||||
|
PP_EXPECT(h, result.value().extensions.size() == 3U);
|
||||||
|
PP_EXPECT(h, result.value().extensions[0] == "GL_EXT_shader_framebuffer_fetch");
|
||||||
|
PP_EXPECT(h, result.value().feature_state.capabilities.framebuffer_fetch);
|
||||||
|
PP_EXPECT(h, result.value().feature_state.capabilities.map_buffer_alignment);
|
||||||
|
PP_EXPECT(h, !result.value().feature_state.capabilities.float32_textures);
|
||||||
|
PP_EXPECT(h, result.value().feature_state.features.framebuffer_fetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void query_capability_detection_preserves_webgl_float_policy(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
recorded_extension_count = 3;
|
||||||
|
|
||||||
|
const auto result = pp::renderer::gl::query_opengl_capability_detection(
|
||||||
|
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||||
|
.get_integer = record_get_integer,
|
||||||
|
.get_string_indexed = record_indexed_string_query,
|
||||||
|
},
|
||||||
|
pp::renderer::gl::OpenGlRuntime { .gles = true, .web = true });
|
||||||
|
|
||||||
|
PP_EXPECT(h, result.ok());
|
||||||
|
PP_EXPECT(h, result.value().feature_state.capabilities.framebuffer_fetch);
|
||||||
|
PP_EXPECT(h, !result.value().feature_state.capabilities.float32_textures);
|
||||||
|
PP_EXPECT(h, !result.value().feature_state.features.float32_render_targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rejects_incomplete_capability_detection_dispatch(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const auto result = pp::renderer::gl::query_opengl_capability_detection(
|
||||||
|
pp::renderer::gl::OpenGlExtensionQueryDispatch {},
|
||||||
|
pp::renderer::gl::OpenGlRuntime {});
|
||||||
|
|
||||||
|
PP_EXPECT(h, !result.ok());
|
||||||
|
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
void clears_app_default_target(pp::tests::Harness& h)
|
void clears_app_default_target(pp::tests::Harness& h)
|
||||||
{
|
{
|
||||||
recorded_clear_calls.clear();
|
recorded_clear_calls.clear();
|
||||||
@@ -4185,6 +4271,8 @@ int main()
|
|||||||
pp::tests::Harness harness;
|
pp::tests::Harness harness;
|
||||||
harness.run("detects_common_extension_capabilities", detects_common_extension_capabilities);
|
harness.run("detects_common_extension_capabilities", detects_common_extension_capabilities);
|
||||||
harness.run("treats_desktop_gl_float_rendering_as_core", treats_desktop_gl_float_rendering_as_core);
|
harness.run("treats_desktop_gl_float_rendering_as_core", treats_desktop_gl_float_rendering_as_core);
|
||||||
|
harness.run("detects_feature_state_from_extension_capabilities", detects_feature_state_from_extension_capabilities);
|
||||||
|
harness.run("detects_desktop_feature_state_without_extensions", detects_desktop_feature_state_without_extensions);
|
||||||
harness.run("detects_gles_texture_float_extensions", detects_gles_texture_float_extensions);
|
harness.run("detects_gles_texture_float_extensions", detects_gles_texture_float_extensions);
|
||||||
harness.run("ignores_gles_texture_extensions_for_webgl_runtime", ignores_gles_texture_extensions_for_webgl_runtime);
|
harness.run("ignores_gles_texture_extensions_for_webgl_runtime", ignores_gles_texture_extensions_for_webgl_runtime);
|
||||||
harness.run("classifies_current_build_opengl_runtime", classifies_current_build_opengl_runtime);
|
harness.run("classifies_current_build_opengl_runtime", classifies_current_build_opengl_runtime);
|
||||||
@@ -4219,6 +4307,9 @@ int main()
|
|||||||
harness.run("converts_null_extension_names_to_empty_strings", converts_null_extension_names_to_empty_strings);
|
harness.run("converts_null_extension_names_to_empty_strings", converts_null_extension_names_to_empty_strings);
|
||||||
harness.run("treats_negative_extension_count_as_empty", treats_negative_extension_count_as_empty);
|
harness.run("treats_negative_extension_count_as_empty", treats_negative_extension_count_as_empty);
|
||||||
harness.run("rejects_incomplete_extension_query_dispatch", rejects_incomplete_extension_query_dispatch);
|
harness.run("rejects_incomplete_extension_query_dispatch", rejects_incomplete_extension_query_dispatch);
|
||||||
|
harness.run("queries_app_capability_detection", queries_app_capability_detection);
|
||||||
|
harness.run("query_capability_detection_preserves_webgl_float_policy", query_capability_detection_preserves_webgl_float_policy);
|
||||||
|
harness.run("rejects_incomplete_capability_detection_dispatch", rejects_incomplete_capability_detection_dispatch);
|
||||||
harness.run("clears_app_default_target", clears_app_default_target);
|
harness.run("clears_app_default_target", clears_app_default_target);
|
||||||
harness.run("rejects_incomplete_app_clear_dispatch", rejects_incomplete_app_clear_dispatch);
|
harness.run("rejects_incomplete_app_clear_dispatch", rejects_incomplete_app_clear_dispatch);
|
||||||
harness.run("applies_viewport_dispatch", applies_viewport_dispatch);
|
harness.run("applies_viewport_dispatch", applies_viewport_dispatch);
|
||||||
|
|||||||
Reference in New Issue
Block a user