From ce33eaaef22513a1bd86211c116de02dd0b8c999 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 2 Jun 2026 20:45:03 +0200 Subject: [PATCH] Plan recorded renderer commands for OpenGL --- CMakeLists.txt | 1 + docs/modernization/build-inventory.md | 5 + docs/modernization/roadmap.md | 10 ++ scripts/automation/platform-build.ps1 | 2 +- src/renderer_gl/command_plan.cpp | 172 ++++++++++++++++++++ src/renderer_gl/command_plan.h | 49 ++++++ tests/CMakeLists.txt | 10 ++ tests/renderer_gl/command_plan_tests.cpp | 193 +++++++++++++++++++++++ 8 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 src/renderer_gl/command_plan.cpp create mode 100644 src/renderer_gl/command_plan.h create mode 100644 tests/renderer_gl/command_plan_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 07275aa..a5cbe9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,7 @@ target_link_libraries(pp_renderer_api if(PP_ENABLE_OPENGL) add_library(pp_renderer_gl STATIC + src/renderer_gl/command_plan.cpp src/renderer_gl/opengl_capabilities.cpp src/renderer_gl/shader_bindings.cpp) target_include_directories(pp_renderer_gl diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index bdb28e7..d5b95c6 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -269,6 +269,11 @@ Known local toolchain state: The Windows entrypoint also consumes backend-owned generic OpenGL error-code/info-string tokens 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 + blit filters into GL-facing planned command data while rejecting unsupported + enum tokens before a real GL context is needed. Desktop VR drawing also consumes backend-owned scissor/depth/blend state, depth clear masks, active texture units, and fallback 2D texture unbind targets while retaining the existing VR SDK/platform bridge shape. diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 4ff697a..976b063 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -528,6 +528,11 @@ 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`. +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 +blit filters into GL-facing planned command data with explicit unsupported-token +rejection before a runtime GL context is needed. The existing renderer classes are not yet fully behind the renderer interfaces. @@ -835,6 +840,11 @@ Results: uniform catalog validation covers the 43 legacy uniform names used by `Shader`, preserves the legacy hash ids, and rejects empty, unnamed, null-name, mismatched-hash, and duplicate-name catalogs. +- `pp_renderer_gl_command_plan_tests` covers the headless OpenGL command + planner for recorded render-pass clear masks/values, viewport/scissor state, + blend/depth/sampler state, texture format mapping, mesh/draw primitive modes, + draw counts, blit filters, planned command names, and unsupported enum + rejection. - PowerShell analyze automation returns JSON summaries and includes the shader validation target and renderer-boundary guard. - `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled diff --git a/scripts/automation/platform-build.ps1 b/scripts/automation/platform-build.ps1 index ed70df2..7b6edee 100644 --- a/scripts/automation/platform-build.ps1 +++ b/scripts/automation/platform-build.ps1 @@ -1,7 +1,7 @@ [CmdletBinding()] param( [string[]]$Presets = @("android-arm64"), - [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_document_ppi_export_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") + [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_document_ppi_export_tests", "pp_renderer_api_tests", "pp_renderer_gl_capabilities_tests", "pp_renderer_gl_command_plan_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" diff --git a/src/renderer_gl/command_plan.cpp b/src/renderer_gl/command_plan.cpp new file mode 100644 index 0000000..040c4be --- /dev/null +++ b/src/renderer_gl/command_plan.cpp @@ -0,0 +1,172 @@ +#include "renderer_gl/command_plan.h" + +namespace pp::renderer::gl { + +namespace { + +[[nodiscard]] bool texture_format_supported(OpenGlRendererTextureFormat format) noexcept +{ + return format.internal_format != 0U + && format.pixel_format != 0U + && format.component_type != 0U + && format.bytes_per_pixel != 0U; +} + +[[nodiscard]] bool requires_render_pass(pp::renderer::RecordedRenderCommandKind kind) noexcept +{ + switch (kind) { + case pp::renderer::RecordedRenderCommandKind::set_viewport: + case pp::renderer::RecordedRenderCommandKind::set_scissor: + case pp::renderer::RecordedRenderCommandKind::set_blend_state: + case pp::renderer::RecordedRenderCommandKind::set_depth_state: + case pp::renderer::RecordedRenderCommandKind::bind_shader: + case pp::renderer::RecordedRenderCommandKind::set_shader_uniform: + case pp::renderer::RecordedRenderCommandKind::bind_texture: + case pp::renderer::RecordedRenderCommandKind::bind_sampler: + case pp::renderer::RecordedRenderCommandKind::bind_mesh: + case pp::renderer::RecordedRenderCommandKind::draw: + return true; + default: + return false; + } +} + +} + +const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept +{ + switch (kind) { + case OpenGlPlannedCommandKind::unknown: + return "unknown"; + case OpenGlPlannedCommandKind::begin_render_pass: + return "begin_render_pass"; + case OpenGlPlannedCommandKind::set_viewport: + return "set_viewport"; + case OpenGlPlannedCommandKind::set_scissor: + return "set_scissor"; + case OpenGlPlannedCommandKind::set_blend_state: + return "set_blend_state"; + case OpenGlPlannedCommandKind::set_depth_state: + return "set_depth_state"; + case OpenGlPlannedCommandKind::bind_texture: + return "bind_texture"; + case OpenGlPlannedCommandKind::bind_sampler: + return "bind_sampler"; + case OpenGlPlannedCommandKind::bind_mesh: + return "bind_mesh"; + case OpenGlPlannedCommandKind::draw: + return "draw"; + case OpenGlPlannedCommandKind::blit_render_target: + return "blit_render_target"; + case OpenGlPlannedCommandKind::end_render_pass: + return "end_render_pass"; + case OpenGlPlannedCommandKind::trace: + return "trace"; + case OpenGlPlannedCommandKind::passthrough: + return "passthrough"; + } + + return "unknown"; +} + +OpenGlPlannedCommand plan_recorded_render_command(pp::renderer::RecordedRenderCommand command) noexcept +{ + OpenGlPlannedCommand planned {}; + planned.requires_render_pass = requires_render_pass(command.kind); + planned.supported = true; + + switch (command.kind) { + case pp::renderer::RecordedRenderCommandKind::begin_render_pass: + planned.kind = OpenGlPlannedCommandKind::begin_render_pass; + planned.clear_mask = clear_mask_for_render_pass(pp::renderer::RenderPassDesc { + .clear_color_enabled = command.clear_color_enabled, + .clear_color = command.clear_color, + .clear_depth_enabled = command.clear_depth_enabled, + .clear_depth = command.clear_depth, + .clear_stencil_enabled = command.clear_stencil_enabled, + .clear_stencil = command.clear_stencil, + }); + planned.clear_values = clear_values_for_render_pass(pp::renderer::RenderPassDesc { + .clear_color_enabled = command.clear_color_enabled, + .clear_color = command.clear_color, + .clear_depth_enabled = command.clear_depth_enabled, + .clear_depth = command.clear_depth, + .clear_stencil_enabled = command.clear_stencil_enabled, + .clear_stencil = command.clear_stencil, + }); + planned.texture_format = texture_format_for_renderer_format(command.target_desc.format); + planned.supported = texture_format_supported(planned.texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::set_viewport: + planned.kind = OpenGlPlannedCommandKind::set_viewport; + planned.viewport = viewport_for_renderer_viewport(command.viewport); + break; + case pp::renderer::RecordedRenderCommandKind::set_scissor: + planned.kind = OpenGlPlannedCommandKind::set_scissor; + planned.scissor = scissor_rect_for_renderer_scissor(command.scissor); + break; + case pp::renderer::RecordedRenderCommandKind::set_blend_state: + planned.kind = OpenGlPlannedCommandKind::set_blend_state; + planned.blend = blend_state_for_renderer_blend_state(command.blend_state); + planned.supported = planned.blend.supported; + break; + case pp::renderer::RecordedRenderCommandKind::set_depth_state: + planned.kind = OpenGlPlannedCommandKind::set_depth_state; + planned.depth = depth_state_for_renderer_depth_state(command.depth_state); + planned.supported = planned.depth.supported; + break; + case pp::renderer::RecordedRenderCommandKind::bind_texture: + planned.kind = OpenGlPlannedCommandKind::bind_texture; + planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); + planned.supported = texture_format_supported(planned.texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::bind_sampler: + planned.kind = OpenGlPlannedCommandKind::bind_sampler; + planned.sampler = sampler_state_for_renderer_sampler_desc(command.sampler_desc); + planned.supported = planned.sampler.supported; + break; + case pp::renderer::RecordedRenderCommandKind::bind_mesh: + planned.kind = OpenGlPlannedCommandKind::bind_mesh; + planned.primitive_mode = primitive_mode_for_renderer_topology(command.mesh_desc.topology); + planned.supported = planned.primitive_mode != 0U; + break; + case pp::renderer::RecordedRenderCommandKind::draw: + planned.kind = OpenGlPlannedCommandKind::draw; + planned.primitive_mode = primitive_mode_for_renderer_topology(command.mesh_desc.topology); + planned.draw_vertex_count = command.draw_desc.vertex_count; + planned.draw_index_count = command.draw_desc.index_count; + planned.supported = planned.primitive_mode != 0U; + break; + case pp::renderer::RecordedRenderCommandKind::blit_render_target: + planned.kind = OpenGlPlannedCommandKind::blit_render_target; + planned.blit_filter = blit_filter_for_renderer_filter(command.blit_filter); + planned.supported = planned.blit_filter.supported; + break; + case pp::renderer::RecordedRenderCommandKind::end_render_pass: + planned.kind = OpenGlPlannedCommandKind::end_render_pass; + break; + case pp::renderer::RecordedRenderCommandKind::trace_marker: + case pp::renderer::RecordedRenderCommandKind::trace_begin_scope: + case pp::renderer::RecordedRenderCommandKind::trace_end_scope: + planned.kind = OpenGlPlannedCommandKind::trace; + break; + case pp::renderer::RecordedRenderCommandKind::bind_shader: + case pp::renderer::RecordedRenderCommandKind::set_shader_uniform: + case pp::renderer::RecordedRenderCommandKind::upload_texture: + case pp::renderer::RecordedRenderCommandKind::generate_mipmaps: + case pp::renderer::RecordedRenderCommandKind::transition_texture: + case pp::renderer::RecordedRenderCommandKind::copy_texture: + case pp::renderer::RecordedRenderCommandKind::read_texture: + case pp::renderer::RecordedRenderCommandKind::capture_frame: + planned.kind = OpenGlPlannedCommandKind::passthrough; + break; + default: + planned.kind = OpenGlPlannedCommandKind::unknown; + planned.supported = false; + break; + } + + return planned; +} + +} diff --git a/src/renderer_gl/command_plan.h b/src/renderer_gl/command_plan.h new file mode 100644 index 0000000..7559e7d --- /dev/null +++ b/src/renderer_gl/command_plan.h @@ -0,0 +1,49 @@ +#pragma once + +#include "renderer_api/recording_renderer.h" +#include "renderer_gl/opengl_capabilities.h" + +#include + +namespace pp::renderer::gl { + +enum class OpenGlPlannedCommandKind : std::uint8_t { + unknown, + begin_render_pass, + set_viewport, + set_scissor, + set_blend_state, + set_depth_state, + bind_texture, + bind_sampler, + bind_mesh, + draw, + blit_render_target, + end_render_pass, + trace, + passthrough, +}; + +struct OpenGlPlannedCommand { + OpenGlPlannedCommandKind kind = OpenGlPlannedCommandKind::unknown; + std::uint32_t clear_mask = 0; + OpenGlClearValues clear_values; + OpenGlViewportRect viewport; + OpenGlScissorRect scissor; + OpenGlBlendState blend; + OpenGlDepthState depth; + OpenGlSamplerState sampler; + OpenGlRendererTextureFormat texture_format; + OpenGlEnumMapping blit_filter; + std::uint32_t primitive_mode = 0; + std::uint32_t draw_vertex_count = 0; + std::uint32_t draw_index_count = 0; + bool requires_render_pass = false; + bool supported = false; +}; + +[[nodiscard]] const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept; +[[nodiscard]] OpenGlPlannedCommand plan_recorded_render_command( + pp::renderer::RecordedRenderCommand command) noexcept; + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fce49f0..053a26f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -206,6 +206,16 @@ if(TARGET pp_renderer_gl) 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") + + add_executable(pp_renderer_gl_command_plan_tests + renderer_gl/command_plan_tests.cpp) + target_link_libraries(pp_renderer_gl_command_plan_tests PRIVATE + pp_renderer_gl + pp_test_harness) + + add_test(NAME pp_renderer_gl_command_plan_tests COMMAND pp_renderer_gl_command_plan_tests) + set_tests_properties(pp_renderer_gl_command_plan_tests PROPERTIES + LABELS "renderer;desktop-fast") endif() add_executable(pp_paint_renderer_compositor_tests diff --git a/tests/renderer_gl/command_plan_tests.cpp b/tests/renderer_gl/command_plan_tests.cpp new file mode 100644 index 0000000..fb2dda9 --- /dev/null +++ b/tests/renderer_gl/command_plan_tests.cpp @@ -0,0 +1,193 @@ +#include "renderer_gl/command_plan.h" +#include "test_harness.h" + +#include + +namespace { + +void maps_render_pass_and_state_commands(pp::tests::Harness& h) +{ + const auto begin = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::begin_render_pass, + .target_desc = pp::renderer::TextureDesc { + .extent = pp::renderer::Extent2D { .width = 64U, .height = 32U }, + .format = pp::renderer::TextureFormat::rgba8, + }, + .clear_color_enabled = true, + .clear_color = pp::renderer::ClearColor { .r = 0.25F, .g = 0.5F, .b = 0.75F, .a = 1.0F }, + .clear_depth_enabled = true, + .clear_depth = 0.875F, + .clear_stencil_enabled = true, + .clear_stencil = 3U, + }); + const auto viewport = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_viewport, + .viewport = pp::renderer::Viewport { .x = 2, .y = 4, .width = 32U, .height = 16U }, + }); + const auto scissor = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_scissor, + .scissor = pp::renderer::ScissorRect { .enabled = true, .x = 3, .y = 5, .width = 8U, .height = 6U }, + }); + const auto blend = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_blend_state, + .blend_state = pp::renderer::BlendState { + .enabled = true, + .source_color = pp::renderer::BlendFactor::source_alpha, + .destination_color = pp::renderer::BlendFactor::one_minus_source_alpha, + }, + }); + const auto depth = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_depth_state, + .depth_state = pp::renderer::DepthState { + .test_enabled = true, + .write_enabled = true, + .compare = pp::renderer::CompareOp::greater, + }, + }); + + PP_EXPECT(h, begin.supported); + PP_EXPECT(h, begin.kind == pp::renderer::gl::OpenGlPlannedCommandKind::begin_render_pass); + PP_EXPECT(h, begin.clear_mask == 0x00004500U); + PP_EXPECT(h, begin.clear_values.color[2] == 0.75F); + PP_EXPECT(h, begin.clear_values.depth == 0.875F); + PP_EXPECT(h, begin.clear_values.stencil == 3U); + PP_EXPECT(h, begin.texture_format.internal_format == 0x8058U); + PP_EXPECT(h, !begin.requires_render_pass); + PP_EXPECT(h, viewport.supported); + PP_EXPECT(h, viewport.requires_render_pass); + PP_EXPECT(h, viewport.viewport.x == 2); + PP_EXPECT(h, viewport.viewport.width == 32); + PP_EXPECT(h, scissor.supported); + PP_EXPECT(h, scissor.scissor.enabled == 1U); + PP_EXPECT(h, scissor.scissor.height == 6); + PP_EXPECT(h, blend.supported); + PP_EXPECT(h, blend.blend.enabled == 1U); + PP_EXPECT(h, blend.blend.source_color_factor == 0x0302U); + PP_EXPECT(h, depth.supported); + PP_EXPECT(h, depth.depth.test_enabled == 1U); + PP_EXPECT(h, depth.depth.compare_function == 0x0204U); +} + +void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) +{ + const auto texture = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_texture, + .texture_desc = pp::renderer::TextureDesc { + .extent = pp::renderer::Extent2D { .width = 16U, .height = 16U }, + .format = pp::renderer::TextureFormat::depth24_stencil8, + }, + }); + const auto sampler = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_sampler, + .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 mesh = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_mesh, + .mesh_desc = pp::renderer::MeshDesc { .topology = pp::renderer::PrimitiveTopology::triangle_strip }, + }); + const auto draw = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::draw, + .mesh_desc = pp::renderer::MeshDesc { .topology = pp::renderer::PrimitiveTopology::lines }, + .draw_desc = pp::renderer::DrawDesc { .vertex_count = 4U, .index_count = 2U }, + }); + const auto blit = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::blit_render_target, + .blit_filter = pp::renderer::BlitFilter::linear, + }); + + PP_EXPECT(h, texture.supported); + PP_EXPECT(h, texture.requires_render_pass); + PP_EXPECT(h, texture.texture_format.internal_format == 0x88F0U); + PP_EXPECT(h, sampler.supported); + PP_EXPECT(h, sampler.sampler.min_filter == 0x2702U); + PP_EXPECT(h, sampler.sampler.wrap_t == 0x8370U); + PP_EXPECT(h, mesh.supported); + PP_EXPECT(h, mesh.primitive_mode == 0x0005U); + PP_EXPECT(h, draw.supported); + PP_EXPECT(h, draw.primitive_mode == 0x0001U); + PP_EXPECT(h, draw.draw_vertex_count == 4U); + PP_EXPECT(h, draw.draw_index_count == 2U); + PP_EXPECT(h, blit.supported); + PP_EXPECT(h, !blit.requires_render_pass); + PP_EXPECT(h, blit.blit_filter.value == 0x2601U); +} + +void rejects_unsupported_command_tokens(pp::tests::Harness& h) +{ + const auto bad_blend = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_blend_state, + .blend_state = pp::renderer::BlendState { + .source_color = static_cast(255U), + }, + }); + const auto bad_depth = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::set_depth_state, + .depth_state = pp::renderer::DepthState { + .compare = static_cast(255U), + }, + }); + const auto bad_sampler = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::bind_sampler, + .sampler_desc = pp::renderer::SamplerDesc { + .address_u = static_cast(255U), + }, + }); + const auto bad_mesh = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::draw, + .mesh_desc = pp::renderer::MeshDesc { + .topology = static_cast(255U), + }, + }); + const auto bad_blit = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = pp::renderer::RecordedRenderCommandKind::blit_render_target, + .blit_filter = static_cast(255U), + }); + const auto unknown = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { + .kind = static_cast(255U), + }); + + PP_EXPECT(h, !bad_blend.supported); + PP_EXPECT(h, bad_blend.blend.source_color_factor == 0U); + PP_EXPECT(h, !bad_depth.supported); + PP_EXPECT(h, bad_depth.depth.compare_function == 0U); + PP_EXPECT(h, !bad_sampler.supported); + PP_EXPECT(h, bad_sampler.sampler.wrap_s == 0U); + PP_EXPECT(h, !bad_mesh.supported); + PP_EXPECT(h, bad_mesh.primitive_mode == 0U); + PP_EXPECT(h, !bad_blit.supported); + PP_EXPECT(h, bad_blit.blit_filter.value == 0U); + PP_EXPECT(h, !unknown.supported); + PP_EXPECT(h, unknown.kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown); +} + +void names_planned_command_kinds(pp::tests::Harness& h) +{ + PP_EXPECT(h, pp::renderer::gl::planned_command_kind_name( + pp::renderer::gl::OpenGlPlannedCommandKind::begin_render_pass) + == std::string_view("begin_render_pass")); + PP_EXPECT(h, pp::renderer::gl::planned_command_kind_name( + pp::renderer::gl::OpenGlPlannedCommandKind::passthrough) + == std::string_view("passthrough")); + PP_EXPECT(h, pp::renderer::gl::planned_command_kind_name( + static_cast(255U)) + == std::string_view("unknown")); +} + +} + +int main() +{ + pp::tests::Harness harness; + harness.run("maps_render_pass_and_state_commands", maps_render_pass_and_state_commands); + harness.run("maps_binding_draw_and_blit_commands", maps_binding_draw_and_blit_commands); + harness.run("rejects_unsupported_command_tokens", rejects_unsupported_command_tokens); + harness.run("names_planned_command_kinds", names_planned_command_kinds); + return harness.finish(); +}