From c18297f2210d232153bdd22fe9132677c0e69cae Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 2 Jun 2026 20:33:20 +0200 Subject: [PATCH] Map renderer sampler state to OpenGL --- docs/modernization/build-inventory.md | 3 +- docs/modernization/roadmap.md | 9 +-- src/renderer_gl/opengl_capabilities.cpp | 53 ++++++++++++++++++ src/renderer_gl/opengl_capabilities.h | 14 +++++ tests/renderer_gl/capabilities_tests.cpp | 71 ++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 5 deletions(-) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 03b54bd..10acc55 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -210,7 +210,8 @@ Known local toolchain state: texture-target mapping used by `TextureCube`. It also owns and validates sampler wrap S/T/R, min/mag filter, and desktop border-color parameter mapping used by legacy `Sampler`, plus renderer API - sampler filter/address-mode to OpenGL token mapping including mirrored-repeat. + sampler filter/address-mode to OpenGL token mapping including mirrored-repeat + and aggregate renderer API sampler-state to OpenGL min/mag/wrap mapping. The PanoPainter shader attribute binding catalog, shader stage tokens, compile/link status queries, active-uniform count query, and matrix-uniform transpose token used diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index b59f826..560595f 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -465,10 +465,11 @@ OpenGL texture-target mapping used by `TextureCube` also lives in `pp_renderer_gl`. The legacy app delegates extension, upload-format, framebuffer diagnostic, framebuffer blit, render-target setup, clear-state, 2D/cube texture setup, mesh draw-mode, and cube-face texture-target -interpretation to that backend library. Sampler wrap, min/mag filter, and desktop border-color parameter -mapping for legacy `Sampler` also lives in `pp_renderer_gl`. Renderer API sampler -filter/address-mode to OpenGL token mapping, including mirrored-repeat, is also -tested there. The PanoPainter shader attribute +interpretation to that backend library. Sampler wrap, min/mag filter, and +desktop border-color parameter mapping for legacy `Sampler` also lives in +`pp_renderer_gl`. Renderer API sampler filter/address-mode to OpenGL token +mapping, including mirrored-repeat, and aggregate renderer API sampler-state to +OpenGL min/mag/wrap mapping are also tested there. The PanoPainter shader attribute binding catalog, shader stage tokens, compile/link status queries, active-uniform count query, and matrix-uniform transpose token also live in `pp_renderer_gl` and are consumed by legacy `Shader` creation. Renderer API blend factor/op to diff --git a/src/renderer_gl/opengl_capabilities.cpp b/src/renderer_gl/opengl_capabilities.cpp index b857605..4586c37 100644 --- a/src/renderer_gl/opengl_capabilities.cpp +++ b/src/renderer_gl/opengl_capabilities.cpp @@ -116,6 +116,9 @@ constexpr std::uint32_t gl_texture_cube_map_negative_y = 0x8518U; constexpr std::uint32_t gl_texture_cube_map_positive_z = 0x8519U; constexpr std::uint32_t gl_texture_cube_map_negative_z = 0x851AU; constexpr std::uint32_t gl_linear = 0x2601U; +constexpr std::uint32_t gl_nearest_mipmap_nearest = 0x2700U; +constexpr std::uint32_t gl_linear_mipmap_nearest = 0x2701U; +constexpr std::uint32_t gl_nearest_mipmap_linear = 0x2702U; constexpr std::uint32_t gl_linear_mipmap_linear = 0x2703U; constexpr std::uint32_t gl_nearest = 0x2600U; constexpr std::uint32_t gl_texture_mag_filter = 0x2800U; @@ -940,6 +943,34 @@ OpenGlEnumMapping sampler_filter_for_renderer_filter(pp::renderer::SamplerFilter } } +OpenGlEnumMapping sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter min_filter, + pp::renderer::SamplerFilter mip_filter) noexcept +{ + switch (min_filter) { + case pp::renderer::SamplerFilter::nearest: + switch (mip_filter) { + case pp::renderer::SamplerFilter::nearest: + return OpenGlEnumMapping { .value = gl_nearest_mipmap_nearest, .supported = true }; + case pp::renderer::SamplerFilter::linear: + return OpenGlEnumMapping { .value = gl_nearest_mipmap_linear, .supported = true }; + default: + return OpenGlEnumMapping {}; + } + case pp::renderer::SamplerFilter::linear: + switch (mip_filter) { + case pp::renderer::SamplerFilter::nearest: + return OpenGlEnumMapping { .value = gl_linear_mipmap_nearest, .supported = true }; + case pp::renderer::SamplerFilter::linear: + return OpenGlEnumMapping { .value = gl_linear_mipmap_linear, .supported = true }; + default: + return OpenGlEnumMapping {}; + } + default: + return OpenGlEnumMapping {}; + } +} + OpenGlEnumMapping sampler_address_mode_for_renderer_mode(pp::renderer::SamplerAddressMode mode) noexcept { switch (mode) { @@ -956,6 +987,28 @@ OpenGlEnumMapping sampler_address_mode_for_renderer_mode(pp::renderer::SamplerAd } } +OpenGlSamplerState sampler_state_for_renderer_sampler_desc(pp::renderer::SamplerDesc desc) noexcept +{ + const auto min_filter = sampler_min_filter_for_renderer_filters(desc.min_filter, desc.mip_filter); + const auto mag_filter = sampler_filter_for_renderer_filter(desc.mag_filter); + const auto address_u = sampler_address_mode_for_renderer_mode(desc.address_u); + const auto address_v = sampler_address_mode_for_renderer_mode(desc.address_v); + const auto address_w = sampler_address_mode_for_renderer_mode(desc.address_w); + + return OpenGlSamplerState { + .min_filter = min_filter.value, + .mag_filter = mag_filter.value, + .wrap_s = address_u.value, + .wrap_t = address_v.value, + .wrap_r = address_w.value, + .supported = min_filter.supported + && mag_filter.supported + && address_u.supported + && address_v.supported + && address_w.supported, + }; +} + std::uint32_t active_texture_unit(std::uint32_t unit_index) noexcept { return gl_texture0 + unit_index; diff --git a/src/renderer_gl/opengl_capabilities.h b/src/renderer_gl/opengl_capabilities.h index 6614ae2..b3f0bdd 100644 --- a/src/renderer_gl/opengl_capabilities.h +++ b/src/renderer_gl/opengl_capabilities.h @@ -34,6 +34,15 @@ struct OpenGlTextureParameter { std::uint32_t value = 0; }; +struct OpenGlSamplerState { + std::uint32_t min_filter = 0; + std::uint32_t mag_filter = 0; + std::uint32_t wrap_s = 0; + std::uint32_t wrap_t = 0; + std::uint32_t wrap_r = 0; + bool supported = false; +}; + struct OpenGlViewportRect { std::int32_t x = 0; std::int32_t y = 0; @@ -219,8 +228,13 @@ struct OpenGlWindowsWglContextConfig { [[nodiscard]] std::uint32_t clamp_to_border_texture_wrap() noexcept; [[nodiscard]] OpenGlEnumMapping sampler_filter_for_renderer_filter( pp::renderer::SamplerFilter filter) noexcept; +[[nodiscard]] OpenGlEnumMapping sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter min_filter, + pp::renderer::SamplerFilter mip_filter) noexcept; [[nodiscard]] OpenGlEnumMapping sampler_address_mode_for_renderer_mode( pp::renderer::SamplerAddressMode mode) noexcept; +[[nodiscard]] OpenGlSamplerState sampler_state_for_renderer_sampler_desc( + pp::renderer::SamplerDesc desc) noexcept; [[nodiscard]] std::uint32_t active_texture_unit(std::uint32_t unit_index) noexcept; [[nodiscard]] std::uint32_t texture_cube_map_target() noexcept; [[nodiscard]] std::uint32_t cube_map_allocation_face_texture_target(std::uint32_t face_index) noexcept; diff --git a/tests/renderer_gl/capabilities_tests.cpp b/tests/renderer_gl/capabilities_tests.cpp index fed0f4d..3332ab1 100644 --- a/tests/renderer_gl/capabilities_tests.cpp +++ b/tests/renderer_gl/capabilities_tests.cpp @@ -440,6 +440,24 @@ void maps_renderer_sampler_tokens(pp::tests::Harness& h) pp::renderer::SamplerAddressMode::clamp_to_border); const auto invalid_mode = pp::renderer::gl::sampler_address_mode_for_renderer_mode( static_cast(255U)); + const auto nearest_nearest = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter::nearest, + pp::renderer::SamplerFilter::nearest); + const auto linear_nearest = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter::linear, + pp::renderer::SamplerFilter::nearest); + const auto nearest_linear = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter::nearest, + pp::renderer::SamplerFilter::linear); + const auto linear_linear = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter::linear, + pp::renderer::SamplerFilter::linear); + const auto invalid_min_filter = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + static_cast(255U), + pp::renderer::SamplerFilter::linear); + const auto invalid_mip_filter = pp::renderer::gl::sampler_min_filter_for_renderer_filters( + pp::renderer::SamplerFilter::linear, + static_cast(255U)); PP_EXPECT(h, nearest.supported); PP_EXPECT(h, nearest.value == 0x2600U); @@ -457,6 +475,58 @@ void maps_renderer_sampler_tokens(pp::tests::Harness& h) PP_EXPECT(h, clamp_to_border.value == 0x812DU); PP_EXPECT(h, !invalid_mode.supported); PP_EXPECT(h, invalid_mode.value == 0U); + PP_EXPECT(h, nearest_nearest.supported); + PP_EXPECT(h, nearest_nearest.value == 0x2700U); + PP_EXPECT(h, linear_nearest.supported); + PP_EXPECT(h, linear_nearest.value == 0x2701U); + PP_EXPECT(h, nearest_linear.supported); + PP_EXPECT(h, nearest_linear.value == 0x2702U); + PP_EXPECT(h, linear_linear.supported); + PP_EXPECT(h, linear_linear.value == 0x2703U); + PP_EXPECT(h, !invalid_min_filter.supported); + PP_EXPECT(h, invalid_min_filter.value == 0U); + PP_EXPECT(h, !invalid_mip_filter.supported); + PP_EXPECT(h, invalid_mip_filter.value == 0U); +} + +void maps_renderer_sampler_states(pp::tests::Harness& h) +{ + const auto default_sampler = pp::renderer::gl::sampler_state_for_renderer_sampler_desc( + pp::renderer::SamplerDesc {}); + const auto mixed_sampler = pp::renderer::gl::sampler_state_for_renderer_sampler_desc( + pp::renderer::SamplerDesc { + .min_filter = pp::renderer::SamplerFilter::nearest, + .mag_filter = pp::renderer::SamplerFilter::linear, + .mip_filter = pp::renderer::SamplerFilter::linear, + .address_u = pp::renderer::SamplerAddressMode::repeat, + .address_v = pp::renderer::SamplerAddressMode::mirrored_repeat, + .address_w = pp::renderer::SamplerAddressMode::clamp_to_border, + }); + const auto invalid_filter = pp::renderer::gl::sampler_state_for_renderer_sampler_desc( + pp::renderer::SamplerDesc { + .min_filter = static_cast(255U), + }); + const auto invalid_address = pp::renderer::gl::sampler_state_for_renderer_sampler_desc( + pp::renderer::SamplerDesc { + .address_w = static_cast(255U), + }); + + PP_EXPECT(h, default_sampler.supported); + PP_EXPECT(h, default_sampler.min_filter == 0x2703U); + PP_EXPECT(h, default_sampler.mag_filter == 0x2601U); + PP_EXPECT(h, default_sampler.wrap_s == 0x812FU); + PP_EXPECT(h, default_sampler.wrap_t == 0x812FU); + PP_EXPECT(h, default_sampler.wrap_r == 0x812FU); + PP_EXPECT(h, mixed_sampler.supported); + PP_EXPECT(h, mixed_sampler.min_filter == 0x2702U); + PP_EXPECT(h, mixed_sampler.mag_filter == 0x2601U); + PP_EXPECT(h, mixed_sampler.wrap_s == 0x2901U); + PP_EXPECT(h, mixed_sampler.wrap_t == 0x8370U); + PP_EXPECT(h, mixed_sampler.wrap_r == 0x812DU); + PP_EXPECT(h, !invalid_filter.supported); + PP_EXPECT(h, invalid_filter.min_filter == 0U); + PP_EXPECT(h, !invalid_address.supported); + PP_EXPECT(h, invalid_address.wrap_r == 0U); } void exposes_shader_attribute_binding_catalog(pp::tests::Harness& h) @@ -899,6 +969,7 @@ int main() harness.run("exposes_default_render_target_texture_parameters", exposes_default_render_target_texture_parameters); harness.run("maps_sampler_parameters", maps_sampler_parameters); harness.run("maps_renderer_sampler_tokens", maps_renderer_sampler_tokens); + harness.run("maps_renderer_sampler_states", maps_renderer_sampler_states); harness.run("exposes_shader_attribute_binding_catalog", exposes_shader_attribute_binding_catalog); harness.run("maps_app_initialization_parameters", maps_app_initialization_parameters); harness.run("maps_renderer_viewports_and_scissors", maps_renderer_viewports_and_scissors);