Add OpenGL renderer capability target

This commit is contained in:
2026-06-01 17:44:00 +02:00
parent d61c7f37c3
commit 9ab73a0354
10 changed files with 236 additions and 25 deletions

View File

@@ -162,6 +162,20 @@ target_link_libraries(pp_renderer_api
PRIVATE
pp_project_warnings)
if(PP_ENABLE_OPENGL)
add_library(pp_renderer_gl STATIC
src/renderer_gl/opengl_capabilities.cpp)
target_include_directories(pp_renderer_gl
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/src")
target_link_libraries(pp_renderer_gl
PUBLIC
pp_renderer_api
pp_project_options
PRIVATE
pp_project_warnings)
endif()
add_library(pp_paint_renderer STATIC
src/paint_renderer/compositor.cpp)
target_include_directories(pp_paint_renderer
@@ -212,6 +226,9 @@ if(PP_BUILD_APP)
PRIVATE
pp_renderer_api
pp_project_warnings)
if(TARGET pp_renderer_gl)
target_link_libraries(pp_legacy_app PRIVATE pp_renderer_gl)
endif()
target_include_directories(pp_legacy_app
PUBLIC

View File

@@ -88,13 +88,14 @@ Known local toolchain state:
treating `windows-clangcl-asan` as a passing sanitizer gate.
- Android arm64 headless configure/build passes through root CMake and the
`platform-build` automation wrapper for `pp_foundation`, `pp_assets`,
`pp_paint`, `pp_document`, `pp_renderer_api`, `pp_paint_renderer`,
`pp_paint`, `pp_document`, `pp_renderer_api`, `pp_renderer_gl`,
`pp_paint_renderer`,
`pp_ui_core`, `pano_cli`, and their current headless test binaries,
including foundation event/logging/task queue coverage, PNG metadata and
decode, PPI header/layout, settings document, document
snapshot/per-layer-frame/move/duration/face-pixel coverage, paint
brush/stroke/stroke-script coverage, renderer shader descriptor coverage,
UI color parsing, and layout XML parse coverage.
brush/stroke/stroke-script coverage, renderer shader descriptor and OpenGL
capability coverage, UI color parsing, and layout XML parse coverage.
- `pano_cli inspect-image` reports PNG IHDR metadata as JSON and is covered by
`pano_cli_inspect_png_metadata_smoke` with a tiny IHDR fixture.
- `pano_cli inspect-project` reports validated PPI thumbnail/body byte layout,
@@ -122,6 +123,10 @@ Known local toolchain state:
- `pp_renderer_api` owns the canonical PanoPainter shader catalog consumed by
the legacy OpenGL app initialization path; `pp_renderer_api_tests` validates
catalog size, key entries, duplicate rejection, and bad path rejection.
- `pp_renderer_gl` owns headless OpenGL runtime capability detection consumed
by the legacy app initialization path; `pp_renderer_gl_capabilities_tests`
validates framebuffer fetch, map-buffer alignment, desktop GL float support,
GLES float/half-float extensions, and WebGL exclusion behavior.
- `windows-msvc-vcpkg-headless` validates manifest install/configure/build/test
for the current headless component matrix; see DEBT-0007 for remaining app
and platform triplet migration.

View File

@@ -384,8 +384,12 @@ Status: started. `pp_renderer_api` exists as a headless renderer-neutral target
with texture descriptor, byte-size, viewport, mesh, readback bounds, command
context, render device, shader program descriptor, mesh, render target,
readback, trace interface validation, and the canonical PanoPainter shader
catalog now consumed by the legacy OpenGL app initialization path. OpenGL
classes are not yet behind these interfaces.
catalog now consumed by the legacy OpenGL app initialization path.
`pp_renderer_gl` now exists as the first OpenGL backend library and owns pure
OpenGL capability detection for framebuffer fetch, map-buffer alignment, and
float texture support. The legacy app delegates extension interpretation to
that backend library, but the existing renderer classes are not yet fully
behind the renderer interfaces.
Implementation tasks:
@@ -404,6 +408,8 @@ Implementation tasks:
- `Sampler`
- `ShaderManager`
- `Shape`
- Keep OpenGL runtime capability decisions in `pp_renderer_gl` with headless
tests before moving GL object lifetimes behind backend types.
- Preserve current shader behavior and asset paths.
- Add deterministic GPU tests:
- clear
@@ -619,6 +625,10 @@ Results:
aggregate stroke-script counts/distances.
- `panopainter_validate_shaders` passed, validating 25 shader programs and 7
shader includes for stage markers and include graph integrity.
- `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless,
and Android arm64 configure/build, covering framebuffer fetch, map-buffer
alignment, desktop GL core float support, GLES float/half-float extensions,
and WebGL exclusion behavior.
- PowerShell analyze automation returns JSON summaries and includes the shader
validation target.
- `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled

View File

@@ -1,7 +1,7 @@
[CmdletBinding()]
param(
[string[]]$Presets = @("android-arm64"),
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_event_tests", "pp_foundation_log_tests", "pp_foundation_parse_tests", "pp_foundation_task_queue_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_assets_image_metadata_tests", "pp_assets_image_pixels_tests", "pp_assets_ppi_header_tests", "pp_assets_settings_document_tests", "pp_paint_brush_tests", "pp_paint_blend_tests", "pp_paint_stroke_tests", "pp_paint_stroke_script_tests", "pp_document_tests", "pp_document_ppi_import_tests", "pp_renderer_api_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_color_tests", "pp_ui_core_layout_value_tests", "pp_ui_core_layout_xml_tests")
[string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_renderer_gl", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_event_tests", "pp_foundation_log_tests", "pp_foundation_parse_tests", "pp_foundation_task_queue_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_assets_image_metadata_tests", "pp_assets_image_pixels_tests", "pp_assets_ppi_header_tests", "pp_assets_settings_document_tests", "pp_paint_brush_tests", "pp_paint_blend_tests", "pp_paint_stroke_tests", "pp_paint_stroke_script_tests", "pp_document_tests", "pp_document_ppi_import_tests", "pp_renderer_api_tests", "pp_renderer_gl_capabilities_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_color_tests", "pp_ui_core_layout_value_tests", "pp_ui_core_layout_xml_tests")
)
$ErrorActionPreference = "Stop"

View File

@@ -3,7 +3,7 @@ set -u
preset="${1:-android-arm64}"
shift || true
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests}"
targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_renderer_gl pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_event_tests pp_foundation_log_tests pp_foundation_parse_tests pp_foundation_task_queue_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_assets_image_metadata_tests pp_assets_image_pixels_tests pp_assets_ppi_header_tests pp_assets_settings_document_tests pp_paint_brush_tests pp_paint_blend_tests pp_paint_stroke_tests pp_paint_stroke_script_tests pp_document_tests pp_document_ppi_import_tests pp_renderer_api_tests pp_renderer_gl_capabilities_tests pp_paint_renderer_compositor_tests pp_ui_core_color_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests}"
start="$(date +%s)"
cmake --preset "$preset"

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "app.h"
#include "renderer_api/shader_catalog.h"
#include "renderer_gl/opengl_capabilities.h"
#include "shader.h"
void App::initShaders()
@@ -13,27 +14,33 @@ void App::initShaders()
render_task([] {
GLint n_exts;
glGetIntegerv(GL_NUM_EXTENSIONS, &n_exts);
std::vector<std::string> extension_storage;
std::vector<std::string_view> extension_views;
extension_storage.reserve(n_exts);
extension_views.reserve(n_exts);
for (int i = 0; i < n_exts; i++)
{
std::string ext = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (ext.find("shader_framebuffer_fetch") != std::string::npos)
ShaderManager::ext_framebuffer_fetch = true;
if (ext.find("map_buffer_alignment") != std::string::npos)
ShaderManager::ext_map_aligned = true;
#if __GLES__ && !__WEB__
if (ext.find("texture_float") != std::string::npos)
ShaderManager::ext_float32 = true;
if (ext.find("texture_float_linear") != std::string::npos)
ShaderManager::ext_float32_linear = true;
if (ext.find("color_buffer_float") != std::string::npos)
ShaderManager::ext_float32 = true;
if (ext.find("texture_half_float") != std::string::npos)
ShaderManager::ext_float16 = true;
if (ext.find("color_buffer_half_float") != std::string::npos)
ShaderManager::ext_float16 = true;
#endif
LOG("EXT: %s", ext.c_str());
extension_storage.emplace_back((const char*)glGetStringi(GL_EXTENSIONS, i));
extension_views.push_back(extension_storage.back());
LOG("EXT: %s", extension_storage.back().c_str());
}
pp::renderer::gl::OpenGlRuntime runtime;
#if __GL__
runtime.desktop_gl = true;
#endif
#if __GLES__
runtime.gles = true;
#endif
#if __WEB__
runtime.web = true;
#endif
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;
});
#if __GL__

View File

@@ -0,0 +1,53 @@
#include "renderer_gl/opengl_capabilities.h"
namespace pp::renderer::gl {
namespace {
[[nodiscard]] bool contains(std::string_view text, std::string_view needle) noexcept
{
return text.find(needle) != std::string_view::npos;
}
}
OpenGlCapabilities detect_opengl_capabilities(
std::span<const std::string_view> extensions,
OpenGlRuntime runtime) noexcept
{
OpenGlCapabilities capabilities;
if (runtime.desktop_gl) {
capabilities.float32_textures = true;
capabilities.float32_linear = true;
capabilities.float16_textures = true;
}
for (const auto extension : extensions) {
if (contains(extension, "shader_framebuffer_fetch")) {
capabilities.framebuffer_fetch = true;
}
if (contains(extension, "map_buffer_alignment")) {
capabilities.map_buffer_alignment = true;
}
if (runtime.gles && !runtime.web) {
if (contains(extension, "texture_float") || contains(extension, "color_buffer_float")) {
capabilities.float32_textures = true;
}
if (contains(extension, "texture_float_linear")) {
capabilities.float32_linear = true;
}
if (contains(extension, "texture_half_float") || contains(extension, "color_buffer_half_float")) {
capabilities.float16_textures = true;
}
}
}
return capabilities;
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <span>
#include <string_view>
namespace pp::renderer::gl {
struct OpenGlRuntime {
bool desktop_gl = false;
bool gles = false;
bool web = false;
};
struct OpenGlCapabilities {
bool framebuffer_fetch = false;
bool map_buffer_alignment = false;
bool float32_textures = false;
bool float32_linear = false;
bool float16_textures = false;
};
[[nodiscard]] OpenGlCapabilities detect_opengl_capabilities(
std::span<const std::string_view> extensions,
OpenGlRuntime runtime) noexcept;
}

View File

@@ -186,6 +186,18 @@ add_test(NAME pp_renderer_api_tests COMMAND pp_renderer_api_tests)
set_tests_properties(pp_renderer_api_tests PROPERTIES
LABELS "renderer;desktop-fast")
if(TARGET pp_renderer_gl)
add_executable(pp_renderer_gl_capabilities_tests
renderer_gl/capabilities_tests.cpp)
target_link_libraries(pp_renderer_gl_capabilities_tests PRIVATE
pp_renderer_gl
pp_test_harness)
add_test(NAME pp_renderer_gl_capabilities_tests COMMAND pp_renderer_gl_capabilities_tests)
set_tests_properties(pp_renderer_gl_capabilities_tests PROPERTIES
LABELS "renderer;desktop-fast")
endif()
add_executable(pp_paint_renderer_compositor_tests
paint_renderer/compositor_tests.cpp)
target_link_libraries(pp_paint_renderer_compositor_tests PRIVATE

View File

@@ -0,0 +1,81 @@
#include "renderer_gl/opengl_capabilities.h"
#include "test_harness.h"
#include <array>
#include <string_view>
namespace {
void detects_common_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 capabilities = pp::renderer::gl::detect_opengl_capabilities(
extensions,
pp::renderer::gl::OpenGlRuntime {});
PP_EXPECT(h, capabilities.framebuffer_fetch);
PP_EXPECT(h, capabilities.map_buffer_alignment);
PP_EXPECT(h, !capabilities.float32_textures);
PP_EXPECT(h, !capabilities.float16_textures);
}
void treats_desktop_gl_float_rendering_as_core(pp::tests::Harness& h)
{
const auto capabilities = pp::renderer::gl::detect_opengl_capabilities(
{},
pp::renderer::gl::OpenGlRuntime { .desktop_gl = true });
PP_EXPECT(h, capabilities.float32_textures);
PP_EXPECT(h, capabilities.float32_linear);
PP_EXPECT(h, capabilities.float16_textures);
}
void detects_gles_texture_float_extensions(pp::tests::Harness& h)
{
constexpr std::array<std::string_view, 3> extensions {
"GL_OES_texture_float",
"GL_OES_texture_float_linear",
"GL_EXT_color_buffer_half_float",
};
const auto capabilities = pp::renderer::gl::detect_opengl_capabilities(
extensions,
pp::renderer::gl::OpenGlRuntime { .gles = true });
PP_EXPECT(h, capabilities.float32_textures);
PP_EXPECT(h, capabilities.float32_linear);
PP_EXPECT(h, capabilities.float16_textures);
}
void ignores_gles_texture_extensions_for_webgl_runtime(pp::tests::Harness& h)
{
constexpr std::array<std::string_view, 3> extensions {
"GL_OES_texture_float",
"GL_OES_texture_float_linear",
"GL_EXT_color_buffer_half_float",
};
const auto capabilities = pp::renderer::gl::detect_opengl_capabilities(
extensions,
pp::renderer::gl::OpenGlRuntime { .gles = true, .web = true });
PP_EXPECT(h, !capabilities.float32_textures);
PP_EXPECT(h, !capabilities.float32_linear);
PP_EXPECT(h, !capabilities.float16_textures);
}
}
int main()
{
pp::tests::Harness harness;
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("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);
return harness.finish();
}