diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 4f5a8fb..0c2f507 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -275,9 +275,10 @@ Known local toolchain state: blit filters into GL-facing planned command data while rejecting unsupported enum tokens before a real GL context is needed. It also plans whole recorded command streams, preserving per-command planned data while counting render - passes, draws, passthrough commands, trace commands, unsupported commands, - and render-pass ordering errors such as state changes outside a pass, nested - passes, and unclosed passes. + passes, draws, texture uploads, mipmap generation, texture transitions, + texture copies, texture readbacks, frame captures, passthrough commands, + trace commands, unsupported commands, and render-pass ordering errors such as + state changes outside a pass, nested passes, and unclosed passes. 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. @@ -342,8 +343,9 @@ Known local toolchain state: labeled descriptor counts, backend resource creation counts, plus draw descriptor vertex/index totals. When `pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with the planned command count, support - status, render-pass/draw/passthrough/trace counts, unsupported command count, - render-pass order error count, and unclosed-pass state. Its + status, render-pass/draw/texture-upload/mipmap/transition/copy/readback/ + capture/passthrough/trace counts, unsupported command count, render-pass + order error count, and unclosed-pass state. Its `--exercise-clear` mode verifies interrupted-frame recorder clear/reuse behavior and reports the result in JSON, and is covered by `pano_cli_record_render_smoke`, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index b202b54..c666991 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -534,9 +534,10 @@ 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. It also plans whole recorded command streams, preserving per-command planned data while counting render -passes, draws, passthrough commands, trace commands, unsupported commands, and -render-pass ordering errors such as state changes outside a pass, nested passes, -and unclosed passes. +passes, draws, texture uploads, mipmap generation, texture transitions, texture +copies, texture readbacks, frame captures, passthrough commands, trace +commands, unsupported commands, and render-pass ordering errors such as state +changes outside a pass, nested passes, and unclosed passes. The existing renderer classes are not yet fully behind the renderer interfaces. @@ -847,9 +848,11 @@ Results: - `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, unsupported enum rejection, - whole recorded stream planning, valid trace/render/draw/blit ordering, and - broken render-pass order detection. + draw counts, texture upload/mipmap/transition/copy/readback/capture metadata, + blit filters and byte totals, planned command names, unsupported enum/state + rejection, whole recorded stream planning, valid trace/render/draw/blit + ordering, typed texture-command counts, and broken render-pass order + detection. - 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 @@ -892,9 +895,10 @@ Results: shader-uniform/texture/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/ frame-capture/blit command/byte totals for agent automation. When `pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with - planned command count, support status, render-pass/draw/passthrough/trace - counts, unsupported command count, render-pass order error count, and - unclosed-pass state. The + planned command count, support status, render-pass/draw/texture-upload/ + mipmap/transition/copy/readback/capture/passthrough/trace counts, + unsupported command count, render-pass order error count, and unclosed-pass + state. The `--exercise-clear` mode deliberately clears an interrupted trace/render pass, verifies stale trace-scope state is rejected, verifies the render context can be reused, and then emits that reset status in JSON. It also has an diff --git a/src/renderer_gl/command_plan.cpp b/src/renderer_gl/command_plan.cpp index 6fef601..2d17744 100644 --- a/src/renderer_gl/command_plan.cpp +++ b/src/renderer_gl/command_plan.cpp @@ -12,6 +12,23 @@ namespace { && format.bytes_per_pixel != 0U; } +[[nodiscard]] bool texture_state_supported(pp::renderer::TextureState state) noexcept +{ + switch (state) { + case pp::renderer::TextureState::undefined: + case pp::renderer::TextureState::shader_read: + case pp::renderer::TextureState::render_target: + case pp::renderer::TextureState::upload_destination: + case pp::renderer::TextureState::copy_source: + case pp::renderer::TextureState::copy_destination: + case pp::renderer::TextureState::readback_source: + case pp::renderer::TextureState::present: + return true; + default: + return false; + } +} + [[nodiscard]] bool requires_render_pass(pp::renderer::RecordedRenderCommandKind kind) noexcept { switch (kind) { @@ -72,6 +89,18 @@ const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept return "bind_mesh"; case OpenGlPlannedCommandKind::draw: return "draw"; + case OpenGlPlannedCommandKind::upload_texture: + return "upload_texture"; + case OpenGlPlannedCommandKind::generate_mipmaps: + return "generate_mipmaps"; + case OpenGlPlannedCommandKind::transition_texture: + return "transition_texture"; + case OpenGlPlannedCommandKind::copy_texture: + return "copy_texture"; + case OpenGlPlannedCommandKind::read_texture: + return "read_texture"; + case OpenGlPlannedCommandKind::capture_frame: + return "capture_frame"; case OpenGlPlannedCommandKind::blit_render_target: return "blit_render_target"; case OpenGlPlannedCommandKind::end_render_pass: @@ -153,10 +182,65 @@ OpenGlPlannedCommand plan_recorded_render_command(pp::renderer::RecordedRenderCo planned.draw_index_count = command.draw_desc.index_count; planned.supported = planned.primitive_mode != 0U; break; + case pp::renderer::RecordedRenderCommandKind::upload_texture: + planned.kind = OpenGlPlannedCommandKind::upload_texture; + planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); + planned.readback_region = command.readback_region; + planned.upload_bytes = command.upload_bytes; + planned.supported = texture_format_supported(planned.texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::generate_mipmaps: + planned.kind = OpenGlPlannedCommandKind::generate_mipmaps; + planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); + planned.generated_mip_levels = command.generated_mip_levels; + planned.generated_mip_bytes = command.generated_mip_bytes; + planned.supported = texture_format_supported(planned.texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::transition_texture: + planned.kind = OpenGlPlannedCommandKind::transition_texture; + planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); + planned.before_state = command.before_state; + planned.after_state = command.after_state; + planned.supported = texture_format_supported(planned.texture_format) + && texture_state_supported(planned.before_state) + && texture_state_supported(planned.after_state); + break; + case pp::renderer::RecordedRenderCommandKind::copy_texture: + planned.kind = OpenGlPlannedCommandKind::copy_texture; + planned.source_texture_format = texture_format_for_renderer_format(command.source_desc.format); + planned.destination_texture_format = texture_format_for_renderer_format(command.destination_desc.format); + planned.source_region = command.source_region; + planned.destination_region = command.destination_region; + planned.copy_source_bytes = command.copy_source_bytes; + planned.copy_destination_bytes = command.copy_destination_bytes; + planned.supported = texture_format_supported(planned.source_texture_format) + && texture_format_supported(planned.destination_texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::read_texture: + planned.kind = OpenGlPlannedCommandKind::read_texture; + planned.texture_format = texture_format_for_renderer_format(command.texture_desc.format); + planned.readback_region = command.readback_region; + planned.readback_bytes = command.readback_bytes; + planned.supported = texture_format_supported(planned.texture_format); + break; + case pp::renderer::RecordedRenderCommandKind::capture_frame: + planned.kind = OpenGlPlannedCommandKind::capture_frame; + planned.texture_format = texture_format_for_renderer_format(command.target_desc.format); + planned.capture_bytes = command.capture_bytes; + planned.supported = texture_format_supported(planned.texture_format); + 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; + planned.source_texture_format = texture_format_for_renderer_format(command.source_desc.format); + planned.destination_texture_format = texture_format_for_renderer_format(command.destination_desc.format); + planned.source_region = command.source_region; + planned.destination_region = command.destination_region; + planned.blit_source_bytes = command.blit_source_bytes; + planned.blit_destination_bytes = command.blit_destination_bytes; + planned.supported = planned.blit_filter.supported + && texture_format_supported(planned.source_texture_format) + && texture_format_supported(planned.destination_texture_format); break; case pp::renderer::RecordedRenderCommandKind::end_render_pass: planned.kind = OpenGlPlannedCommandKind::end_render_pass; @@ -168,12 +252,6 @@ OpenGlPlannedCommand plan_recorded_render_command(pp::renderer::RecordedRenderCo 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: @@ -219,6 +297,24 @@ OpenGlCommandPlan plan_recorded_render_commands( record_render_pass_order_error(plan, index); } break; + case OpenGlPlannedCommandKind::upload_texture: + ++plan.upload_command_count; + break; + case OpenGlPlannedCommandKind::generate_mipmaps: + ++plan.mipmap_command_count; + break; + case OpenGlPlannedCommandKind::transition_texture: + ++plan.transition_command_count; + break; + case OpenGlPlannedCommandKind::copy_texture: + ++plan.copy_command_count; + break; + case OpenGlPlannedCommandKind::read_texture: + ++plan.readback_command_count; + break; + case OpenGlPlannedCommandKind::capture_frame: + ++plan.capture_command_count; + break; case OpenGlPlannedCommandKind::passthrough: ++plan.passthrough_command_count; if (planned.requires_render_pass && !in_render_pass) { diff --git a/src/renderer_gl/command_plan.h b/src/renderer_gl/command_plan.h index 0c42e5a..19ba1db 100644 --- a/src/renderer_gl/command_plan.h +++ b/src/renderer_gl/command_plan.h @@ -21,6 +21,12 @@ enum class OpenGlPlannedCommandKind : std::uint8_t { bind_sampler, bind_mesh, draw, + upload_texture, + generate_mipmaps, + transition_texture, + copy_texture, + read_texture, + capture_frame, blit_render_target, end_render_pass, trace, @@ -37,7 +43,23 @@ struct OpenGlPlannedCommand { OpenGlDepthState depth; OpenGlSamplerState sampler; OpenGlRendererTextureFormat texture_format; + OpenGlRendererTextureFormat source_texture_format; + OpenGlRendererTextureFormat destination_texture_format; OpenGlEnumMapping blit_filter; + pp::renderer::TextureState before_state = pp::renderer::TextureState::undefined; + pp::renderer::TextureState after_state = pp::renderer::TextureState::undefined; + pp::renderer::ReadbackRegion readback_region; + pp::renderer::ReadbackRegion source_region; + pp::renderer::ReadbackRegion destination_region; + std::uint64_t upload_bytes = 0; + std::uint32_t generated_mip_levels = 0; + std::uint64_t generated_mip_bytes = 0; + std::uint64_t copy_source_bytes = 0; + std::uint64_t copy_destination_bytes = 0; + std::uint64_t readback_bytes = 0; + std::uint64_t capture_bytes = 0; + std::uint64_t blit_source_bytes = 0; + std::uint64_t blit_destination_bytes = 0; std::uint32_t primitive_mode = 0; std::uint32_t draw_vertex_count = 0; std::uint32_t draw_index_count = 0; @@ -51,6 +73,12 @@ struct OpenGlCommandPlan { std::vector commands; std::uint32_t render_pass_count = 0; std::uint32_t draw_command_count = 0; + std::uint32_t upload_command_count = 0; + std::uint32_t mipmap_command_count = 0; + std::uint32_t transition_command_count = 0; + std::uint32_t copy_command_count = 0; + std::uint32_t readback_command_count = 0; + std::uint32_t capture_command_count = 0; std::uint32_t passthrough_command_count = 0; std::uint32_t trace_command_count = 0; std::uint32_t unsupported_command_count = 0; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5964ac8..5aeddc8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -384,7 +384,7 @@ if(TARGET pano_cli) COMMAND pano_cli record-render --width 32 --height 16) set_tests_properties(pano_cli_record_render_smoke PROPERTIES LABELS "renderer;integration;desktop-fast" - PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"framebufferFetch\":false.*\"explicitTextureTransitions\":true.*\"textureCopy\":true.*\"renderTargetBlit\":true.*\"frameCapture\":true.*\"float16RenderTargets\":false.*\"float32RenderTargets\":false.*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"exercisedClearReset\":false.*\"clearRejectedOrphanedTraceEnd\":false.*\"clearReusedRenderPass\":false.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"openGlPlan\":.*\"available\":true.*\"supported\":true.*\"commands\":25.*\"renderPasses\":1.*\"drawCommands\":1.*\"passthroughCommands\":11.*\"traceCommands\":3.*\"unsupportedCommands\":0.*\"renderPassOrderErrors\":0.*\"endedInRenderPass\":false.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"transitionCommands\":4.*\"transitionToUpload\":1.*\"transitionToShaderRead\":2.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1") + PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"framebufferFetch\":false.*\"explicitTextureTransitions\":true.*\"textureCopy\":true.*\"renderTargetBlit\":true.*\"frameCapture\":true.*\"float16RenderTargets\":false.*\"float32RenderTargets\":false.*\"width\":32.*\"height\":16.*\"createdResources\":7.*\"exercisedClearReset\":false.*\"clearRejectedOrphanedTraceEnd\":false.*\"clearReusedRenderPass\":false.*\"labeledCommandDescriptors\":16.*\"commands\":25.*\"openGlPlan\":.*\"available\":true.*\"supported\":true.*\"commands\":25.*\"renderPasses\":1.*\"drawCommands\":1.*\"uploadCommands\":1.*\"mipmapCommands\":1.*\"transitionCommands\":4.*\"copyCommands\":1.*\"readbackCommands\":1.*\"captureCommands\":1.*\"passthroughCommands\":2.*\"traceCommands\":3.*\"unsupportedCommands\":0.*\"renderPassOrderErrors\":0.*\"endedInRenderPass\":false.*\"renderPasses\":1.*\"depthClears\":1.*\"stencilClears\":0.*\"drawCommands\":1.*\"drawVertices\":3.*\"drawIndices\":3.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"uniformCommands\":1.*\"uniformBytes\":64.*\"bindTextureCommands\":1.*\"bindSamplerCommands\":1.*\"boundTextureBytes\":2048.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"mipmapCommands\":1.*\"mipmapLevels\":3.*\"mipmapBytes\":84.*\"transitionCommands\":4.*\"transitionToUpload\":1.*\"transitionToShaderRead\":2.*\"copyCommands\":1.*\"copySourceBytes\":2048.*\"copyDestinationBytes\":2048.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048.*\"traceMarkers\":1.*\"traceBeginScopes\":1.*\"traceEndScopes\":1") add_test(NAME pano_cli_record_render_exercises_clear_reset COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear) diff --git a/tests/renderer_gl/command_plan_tests.cpp b/tests/renderer_gl/command_plan_tests.cpp index c4cae76..bb34df0 100644 --- a/tests/renderer_gl/command_plan_tests.cpp +++ b/tests/renderer_gl/command_plan_tests.cpp @@ -46,10 +46,26 @@ pp::renderer::RecordedRenderCommand blit_command(pp::renderer::BlitFilter filter { pp::renderer::RecordedRenderCommand command; command.kind = pp::renderer::RecordedRenderCommandKind::blit_render_target; + command.source_desc.format = pp::renderer::TextureFormat::rgba8; + command.destination_desc.format = pp::renderer::TextureFormat::rgba8; command.blit_filter = filter; return command; } +pp::renderer::ReadbackRegion region( + std::uint32_t x, + std::uint32_t y, + std::uint32_t width, + std::uint32_t height) noexcept +{ + pp::renderer::ReadbackRegion result; + result.x = x; + result.y = y; + result.width = width; + result.height = height; + return result; +} + void maps_render_pass_and_state_commands(pp::tests::Harness& h) { const auto begin = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { @@ -142,10 +158,8 @@ void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) .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, - }); + const auto blit = pp::renderer::gl::plan_recorded_render_command( + blit_command(pp::renderer::BlitFilter::linear)); PP_EXPECT(h, texture.supported); PP_EXPECT(h, texture.requires_render_pass); @@ -162,6 +176,84 @@ void maps_binding_draw_and_blit_commands(pp::tests::Harness& h) PP_EXPECT(h, blit.supported); PP_EXPECT(h, !blit.requires_render_pass); PP_EXPECT(h, blit.blit_filter.value == 0x2601U); + PP_EXPECT(h, blit.source_texture_format.internal_format == 0x8058U); + PP_EXPECT(h, blit.destination_texture_format.internal_format == 0x8058U); +} + +void maps_texture_io_and_capture_commands(pp::tests::Harness& h) +{ + pp::renderer::RecordedRenderCommand upload_command; + upload_command.kind = pp::renderer::RecordedRenderCommandKind::upload_texture; + upload_command.texture_desc.format = pp::renderer::TextureFormat::r8; + upload_command.readback_region = region(1U, 2U, 3U, 4U); + upload_command.upload_bytes = 12U; + + pp::renderer::RecordedRenderCommand mipmap_command; + mipmap_command.kind = pp::renderer::RecordedRenderCommandKind::generate_mipmaps; + mipmap_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + mipmap_command.generated_mip_levels = 4U; + mipmap_command.generated_mip_bytes = 340U; + + pp::renderer::RecordedRenderCommand transition_command; + transition_command.kind = pp::renderer::RecordedRenderCommandKind::transition_texture; + transition_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + transition_command.before_state = pp::renderer::TextureState::upload_destination; + transition_command.after_state = pp::renderer::TextureState::shader_read; + + pp::renderer::RecordedRenderCommand copy_command; + copy_command.kind = pp::renderer::RecordedRenderCommandKind::copy_texture; + copy_command.source_desc.format = pp::renderer::TextureFormat::rgba8; + copy_command.destination_desc.format = pp::renderer::TextureFormat::r8; + copy_command.source_region = region(0U, 1U, 8U, 4U); + copy_command.destination_region = region(2U, 3U, 8U, 4U); + copy_command.copy_source_bytes = 128U; + copy_command.copy_destination_bytes = 32U; + + pp::renderer::RecordedRenderCommand read_command; + read_command.kind = pp::renderer::RecordedRenderCommandKind::read_texture; + read_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + read_command.readback_region = region(4U, 5U, 6U, 7U); + read_command.readback_bytes = 168U; + + pp::renderer::RecordedRenderCommand capture_command; + capture_command.kind = pp::renderer::RecordedRenderCommandKind::capture_frame; + capture_command.target_desc.format = pp::renderer::TextureFormat::rgba8; + capture_command.capture_bytes = 512U; + + const auto upload = pp::renderer::gl::plan_recorded_render_command(upload_command); + const auto mipmap = pp::renderer::gl::plan_recorded_render_command(mipmap_command); + const auto transition = pp::renderer::gl::plan_recorded_render_command(transition_command); + const auto copy = pp::renderer::gl::plan_recorded_render_command(copy_command); + const auto read = pp::renderer::gl::plan_recorded_render_command(read_command); + const auto capture = pp::renderer::gl::plan_recorded_render_command(capture_command); + + PP_EXPECT(h, upload.supported); + PP_EXPECT(h, upload.kind == pp::renderer::gl::OpenGlPlannedCommandKind::upload_texture); + PP_EXPECT(h, upload.texture_format.internal_format == 0x8229U); + PP_EXPECT(h, upload.readback_region.y == 2U); + PP_EXPECT(h, upload.upload_bytes == 12U); + PP_EXPECT(h, mipmap.supported); + PP_EXPECT(h, mipmap.kind == pp::renderer::gl::OpenGlPlannedCommandKind::generate_mipmaps); + PP_EXPECT(h, mipmap.generated_mip_levels == 4U); + PP_EXPECT(h, mipmap.generated_mip_bytes == 340U); + PP_EXPECT(h, transition.supported); + PP_EXPECT(h, transition.kind == pp::renderer::gl::OpenGlPlannedCommandKind::transition_texture); + PP_EXPECT(h, transition.before_state == pp::renderer::TextureState::upload_destination); + PP_EXPECT(h, transition.after_state == pp::renderer::TextureState::shader_read); + PP_EXPECT(h, copy.supported); + PP_EXPECT(h, copy.kind == pp::renderer::gl::OpenGlPlannedCommandKind::copy_texture); + PP_EXPECT(h, copy.source_texture_format.internal_format == 0x8058U); + PP_EXPECT(h, copy.destination_texture_format.internal_format == 0x8229U); + PP_EXPECT(h, copy.destination_region.x == 2U); + PP_EXPECT(h, copy.copy_source_bytes == 128U); + PP_EXPECT(h, copy.copy_destination_bytes == 32U); + PP_EXPECT(h, read.supported); + PP_EXPECT(h, read.kind == pp::renderer::gl::OpenGlPlannedCommandKind::read_texture); + PP_EXPECT(h, read.readback_region.width == 6U); + PP_EXPECT(h, read.readback_bytes == 168U); + PP_EXPECT(h, capture.supported); + PP_EXPECT(h, capture.kind == pp::renderer::gl::OpenGlPlannedCommandKind::capture_frame); + PP_EXPECT(h, capture.capture_bytes == 512U); } void rejects_unsupported_command_tokens(pp::tests::Harness& h) @@ -190,10 +282,14 @@ void rejects_unsupported_command_tokens(pp::tests::Harness& h) .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 bad_blit = pp::renderer::gl::plan_recorded_render_command( + blit_command(static_cast(255U))); + pp::renderer::RecordedRenderCommand bad_transition_command; + bad_transition_command.kind = pp::renderer::RecordedRenderCommandKind::transition_texture; + bad_transition_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + bad_transition_command.before_state = static_cast(255U); + bad_transition_command.after_state = pp::renderer::TextureState::shader_read; + const auto bad_transition = pp::renderer::gl::plan_recorded_render_command(bad_transition_command); const auto unknown = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { .kind = static_cast(255U), }); @@ -208,6 +304,8 @@ void rejects_unsupported_command_tokens(pp::tests::Harness& h) 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, !bad_transition.supported); + PP_EXPECT(h, bad_transition.kind == pp::renderer::gl::OpenGlPlannedCommandKind::transition_texture); PP_EXPECT(h, !unknown.supported); PP_EXPECT(h, unknown.kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown); } @@ -220,6 +318,9 @@ void names_planned_command_kinds(pp::tests::Harness& h) 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( + pp::renderer::gl::OpenGlPlannedCommandKind::copy_texture) + == std::string_view("copy_texture")); PP_EXPECT(h, pp::renderer::gl::planned_command_kind_name( static_cast(255U)) == std::string_view("unknown")); @@ -306,6 +407,61 @@ void tracks_unsupported_commands_in_streams(pp::tests::Harness& h) PP_EXPECT(h, plan.commands[3].kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown); } +void counts_typed_texture_commands_in_streams(pp::tests::Harness& h) +{ + pp::renderer::RecordedRenderCommand upload_command; + upload_command.kind = pp::renderer::RecordedRenderCommandKind::upload_texture; + upload_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + upload_command.upload_bytes = 4U; + + pp::renderer::RecordedRenderCommand mipmap_command; + mipmap_command.kind = pp::renderer::RecordedRenderCommandKind::generate_mipmaps; + mipmap_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + mipmap_command.generated_mip_levels = 2U; + + pp::renderer::RecordedRenderCommand transition_command; + transition_command.kind = pp::renderer::RecordedRenderCommandKind::transition_texture; + transition_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + transition_command.before_state = pp::renderer::TextureState::copy_destination; + transition_command.after_state = pp::renderer::TextureState::shader_read; + + pp::renderer::RecordedRenderCommand copy_command; + copy_command.kind = pp::renderer::RecordedRenderCommandKind::copy_texture; + copy_command.source_desc.format = pp::renderer::TextureFormat::rgba8; + copy_command.destination_desc.format = pp::renderer::TextureFormat::rgba8; + + pp::renderer::RecordedRenderCommand read_command; + read_command.kind = pp::renderer::RecordedRenderCommandKind::read_texture; + read_command.texture_desc.format = pp::renderer::TextureFormat::rgba8; + + pp::renderer::RecordedRenderCommand capture_command; + capture_command.kind = pp::renderer::RecordedRenderCommandKind::capture_frame; + capture_command.target_desc.format = pp::renderer::TextureFormat::rgba8; + + const std::vector commands { + upload_command, + mipmap_command, + transition_command, + copy_command, + read_command, + capture_command, + }; + + const auto plan = pp::renderer::gl::plan_recorded_render_commands(commands); + + PP_EXPECT(h, plan.supported); + PP_EXPECT(h, plan.upload_command_count == 1U); + PP_EXPECT(h, plan.mipmap_command_count == 1U); + PP_EXPECT(h, plan.transition_command_count == 1U); + PP_EXPECT(h, plan.copy_command_count == 1U); + PP_EXPECT(h, plan.readback_command_count == 1U); + PP_EXPECT(h, plan.capture_command_count == 1U); + PP_EXPECT(h, plan.passthrough_command_count == 0U); + PP_EXPECT(h, plan.render_pass_order_error_count == 0U); + PP_EXPECT(h, plan.commands[0].kind == pp::renderer::gl::OpenGlPlannedCommandKind::upload_texture); + PP_EXPECT(h, plan.commands[5].kind == pp::renderer::gl::OpenGlPlannedCommandKind::capture_frame); +} + } int main() @@ -313,10 +469,12 @@ 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("maps_texture_io_and_capture_commands", maps_texture_io_and_capture_commands); harness.run("rejects_unsupported_command_tokens", rejects_unsupported_command_tokens); harness.run("names_planned_command_kinds", names_planned_command_kinds); harness.run("plans_valid_recorded_command_streams", plans_valid_recorded_command_streams); harness.run("flags_broken_render_pass_command_order", flags_broken_render_pass_command_order); harness.run("tracks_unsupported_commands_in_streams", tracks_unsupported_commands_in_streams); + harness.run("counts_typed_texture_commands_in_streams", counts_typed_texture_commands_in_streams); return harness.finish(); } diff --git a/tools/pano_cli/main.cpp b/tools/pano_cli/main.cpp index 8b57a42..26b0ad6 100644 --- a/tools/pano_cli/main.cpp +++ b/tools/pano_cli/main.cpp @@ -2785,6 +2785,12 @@ int record_render(int argc, char** argv) << ",\"commands\":" << open_gl_plan.commands.size() << ",\"renderPasses\":" << open_gl_plan.render_pass_count << ",\"drawCommands\":" << open_gl_plan.draw_command_count + << ",\"uploadCommands\":" << open_gl_plan.upload_command_count + << ",\"mipmapCommands\":" << open_gl_plan.mipmap_command_count + << ",\"transitionCommands\":" << open_gl_plan.transition_command_count + << ",\"copyCommands\":" << open_gl_plan.copy_command_count + << ",\"readbackCommands\":" << open_gl_plan.readback_command_count + << ",\"captureCommands\":" << open_gl_plan.capture_command_count << ",\"passthroughCommands\":" << open_gl_plan.passthrough_command_count << ",\"traceCommands\":" << open_gl_plan.trace_command_count << ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_count