Plan OpenGL texture command metadata

This commit is contained in:
2026-06-02 20:59:05 +02:00
parent b4c2117992
commit b6a25474ff
7 changed files with 324 additions and 30 deletions

View File

@@ -275,9 +275,10 @@ Known local toolchain state:
blit filters into GL-facing planned command data while rejecting unsupported 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 enum tokens before a real GL context is needed. It also plans whole recorded
command streams, preserving per-command planned data while counting render command streams, preserving per-command planned data while counting render
passes, draws, passthrough commands, trace commands, unsupported commands, passes, draws, texture uploads, mipmap generation, texture transitions,
and render-pass ordering errors such as state changes outside a pass, nested texture copies, texture readbacks, frame captures, passthrough commands,
passes, and unclosed passes. 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, Desktop VR drawing also consumes backend-owned scissor/depth/blend state,
depth clear masks, active texture units, and fallback 2D texture unbind depth clear masks, active texture units, and fallback 2D texture unbind
targets while retaining the existing VR SDK/platform bridge shape. 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 labeled descriptor counts, backend resource creation counts, plus draw
descriptor vertex/index totals. When `pp_renderer_gl` is available, it also descriptor vertex/index totals. When `pp_renderer_gl` is available, it also
emits an `openGlPlan` JSON object with the planned command count, support emits an `openGlPlan` JSON object with the planned command count, support
status, render-pass/draw/passthrough/trace counts, unsupported command count, status, render-pass/draw/texture-upload/mipmap/transition/copy/readback/
render-pass order error count, and unclosed-pass state. Its capture/passthrough/trace counts, unsupported command count, render-pass
order error count, and unclosed-pass state. Its
`--exercise-clear` mode verifies `--exercise-clear` mode verifies
interrupted-frame recorder clear/reuse behavior and reports the result in interrupted-frame recorder clear/reuse behavior and reports the result in
JSON, and is covered by `pano_cli_record_render_smoke`, JSON, and is covered by `pano_cli_record_render_smoke`,

View File

@@ -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 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 rejection before a runtime GL context is needed. It also plans whole recorded
command streams, preserving per-command planned data while counting render command streams, preserving per-command planned data while counting render
passes, draws, passthrough commands, trace commands, unsupported commands, and passes, draws, texture uploads, mipmap generation, texture transitions, texture
render-pass ordering errors such as state changes outside a pass, nested passes, copies, texture readbacks, frame captures, passthrough commands, trace
and unclosed passes. 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 The existing renderer classes are not yet fully
behind the renderer interfaces. behind the renderer interfaces.
@@ -847,9 +848,11 @@ Results:
- `pp_renderer_gl_command_plan_tests` covers the headless OpenGL command - `pp_renderer_gl_command_plan_tests` covers the headless OpenGL command
planner for recorded render-pass clear masks/values, viewport/scissor state, planner for recorded render-pass clear masks/values, viewport/scissor state,
blend/depth/sampler state, texture format mapping, mesh/draw primitive modes, blend/depth/sampler state, texture format mapping, mesh/draw primitive modes,
draw counts, blit filters, planned command names, unsupported enum rejection, draw counts, texture upload/mipmap/transition/copy/readback/capture metadata,
whole recorded stream planning, valid trace/render/draw/blit ordering, and blit filters and byte totals, planned command names, unsupported enum/state
broken render-pass order detection. 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 - PowerShell analyze automation returns JSON summaries and includes the shader
validation target and renderer-boundary guard. validation target and renderer-boundary guard.
- `windows-msvc-vcpkg-headless` configured through the Visual Studio bundled - `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/ shader-uniform/texture/sampler-bind/upload/mipmap-generation/texture-transition/texture-copy/readback/
frame-capture/blit command/byte totals for agent automation. When frame-capture/blit command/byte totals for agent automation. When
`pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with `pp_renderer_gl` is available, it also emits an `openGlPlan` JSON object with
planned command count, support status, render-pass/draw/passthrough/trace planned command count, support status, render-pass/draw/texture-upload/
counts, unsupported command count, render-pass order error count, and mipmap/transition/copy/readback/capture/passthrough/trace counts,
unclosed-pass state. The unsupported command count, render-pass order error count, and unclosed-pass
state. The
`--exercise-clear` mode deliberately clears an interrupted trace/render pass, `--exercise-clear` mode deliberately clears an interrupted trace/render pass,
verifies stale trace-scope state is rejected, verifies the render context can 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 be reused, and then emits that reset status in JSON. It also has an

View File

@@ -12,6 +12,23 @@ namespace {
&& format.bytes_per_pixel != 0U; && 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 [[nodiscard]] bool requires_render_pass(pp::renderer::RecordedRenderCommandKind kind) noexcept
{ {
switch (kind) { switch (kind) {
@@ -72,6 +89,18 @@ const char* planned_command_kind_name(OpenGlPlannedCommandKind kind) noexcept
return "bind_mesh"; return "bind_mesh";
case OpenGlPlannedCommandKind::draw: case OpenGlPlannedCommandKind::draw:
return "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: case OpenGlPlannedCommandKind::blit_render_target:
return "blit_render_target"; return "blit_render_target";
case OpenGlPlannedCommandKind::end_render_pass: 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.draw_index_count = command.draw_desc.index_count;
planned.supported = planned.primitive_mode != 0U; planned.supported = planned.primitive_mode != 0U;
break; 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: case pp::renderer::RecordedRenderCommandKind::blit_render_target:
planned.kind = OpenGlPlannedCommandKind::blit_render_target; planned.kind = OpenGlPlannedCommandKind::blit_render_target;
planned.blit_filter = blit_filter_for_renderer_filter(command.blit_filter); 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; break;
case pp::renderer::RecordedRenderCommandKind::end_render_pass: case pp::renderer::RecordedRenderCommandKind::end_render_pass:
planned.kind = OpenGlPlannedCommandKind::end_render_pass; planned.kind = OpenGlPlannedCommandKind::end_render_pass;
@@ -168,12 +252,6 @@ OpenGlPlannedCommand plan_recorded_render_command(pp::renderer::RecordedRenderCo
break; break;
case pp::renderer::RecordedRenderCommandKind::bind_shader: case pp::renderer::RecordedRenderCommandKind::bind_shader:
case pp::renderer::RecordedRenderCommandKind::set_shader_uniform: 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; planned.kind = OpenGlPlannedCommandKind::passthrough;
break; break;
default: default:
@@ -219,6 +297,24 @@ OpenGlCommandPlan plan_recorded_render_commands(
record_render_pass_order_error(plan, index); record_render_pass_order_error(plan, index);
} }
break; 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: case OpenGlPlannedCommandKind::passthrough:
++plan.passthrough_command_count; ++plan.passthrough_command_count;
if (planned.requires_render_pass && !in_render_pass) { if (planned.requires_render_pass && !in_render_pass) {

View File

@@ -21,6 +21,12 @@ enum class OpenGlPlannedCommandKind : std::uint8_t {
bind_sampler, bind_sampler,
bind_mesh, bind_mesh,
draw, draw,
upload_texture,
generate_mipmaps,
transition_texture,
copy_texture,
read_texture,
capture_frame,
blit_render_target, blit_render_target,
end_render_pass, end_render_pass,
trace, trace,
@@ -37,7 +43,23 @@ struct OpenGlPlannedCommand {
OpenGlDepthState depth; OpenGlDepthState depth;
OpenGlSamplerState sampler; OpenGlSamplerState sampler;
OpenGlRendererTextureFormat texture_format; OpenGlRendererTextureFormat texture_format;
OpenGlRendererTextureFormat source_texture_format;
OpenGlRendererTextureFormat destination_texture_format;
OpenGlEnumMapping blit_filter; 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 primitive_mode = 0;
std::uint32_t draw_vertex_count = 0; std::uint32_t draw_vertex_count = 0;
std::uint32_t draw_index_count = 0; std::uint32_t draw_index_count = 0;
@@ -51,6 +73,12 @@ struct OpenGlCommandPlan {
std::vector<OpenGlPlannedCommand> commands; std::vector<OpenGlPlannedCommand> commands;
std::uint32_t render_pass_count = 0; std::uint32_t render_pass_count = 0;
std::uint32_t draw_command_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 passthrough_command_count = 0;
std::uint32_t trace_command_count = 0; std::uint32_t trace_command_count = 0;
std::uint32_t unsupported_command_count = 0; std::uint32_t unsupported_command_count = 0;

View File

@@ -384,7 +384,7 @@ if(TARGET pano_cli)
COMMAND pano_cli record-render --width 32 --height 16) COMMAND pano_cli record-render --width 32 --height 16)
set_tests_properties(pano_cli_record_render_smoke PROPERTIES set_tests_properties(pano_cli_record_render_smoke PROPERTIES
LABELS "renderer;integration;desktop-fast" 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 add_test(NAME pano_cli_record_render_exercises_clear_reset
COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear) COMMAND pano_cli record-render --width 32 --height 16 --exercise-clear)

View File

@@ -46,10 +46,26 @@ pp::renderer::RecordedRenderCommand blit_command(pp::renderer::BlitFilter filter
{ {
pp::renderer::RecordedRenderCommand command; pp::renderer::RecordedRenderCommand command;
command.kind = pp::renderer::RecordedRenderCommandKind::blit_render_target; 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; command.blit_filter = filter;
return command; 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) void maps_render_pass_and_state_commands(pp::tests::Harness& h)
{ {
const auto begin = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { 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 }, .mesh_desc = pp::renderer::MeshDesc { .topology = pp::renderer::PrimitiveTopology::lines },
.draw_desc = pp::renderer::DrawDesc { .vertex_count = 4U, .index_count = 2U }, .draw_desc = pp::renderer::DrawDesc { .vertex_count = 4U, .index_count = 2U },
}); });
const auto blit = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { const auto blit = pp::renderer::gl::plan_recorded_render_command(
.kind = pp::renderer::RecordedRenderCommandKind::blit_render_target, blit_command(pp::renderer::BlitFilter::linear));
.blit_filter = pp::renderer::BlitFilter::linear,
});
PP_EXPECT(h, texture.supported); PP_EXPECT(h, texture.supported);
PP_EXPECT(h, texture.requires_render_pass); 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.supported);
PP_EXPECT(h, !blit.requires_render_pass); PP_EXPECT(h, !blit.requires_render_pass);
PP_EXPECT(h, blit.blit_filter.value == 0x2601U); 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) 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<pp::renderer::PrimitiveTopology>(255U), .topology = static_cast<pp::renderer::PrimitiveTopology>(255U),
}, },
}); });
const auto bad_blit = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand { const auto bad_blit = pp::renderer::gl::plan_recorded_render_command(
.kind = pp::renderer::RecordedRenderCommandKind::blit_render_target, blit_command(static_cast<pp::renderer::BlitFilter>(255U)));
.blit_filter = static_cast<pp::renderer::BlitFilter>(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<pp::renderer::TextureState>(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 { const auto unknown = pp::renderer::gl::plan_recorded_render_command(pp::renderer::RecordedRenderCommand {
.kind = static_cast<pp::renderer::RecordedRenderCommandKind>(255U), .kind = static_cast<pp::renderer::RecordedRenderCommandKind>(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_mesh.primitive_mode == 0U);
PP_EXPECT(h, !bad_blit.supported); PP_EXPECT(h, !bad_blit.supported);
PP_EXPECT(h, bad_blit.blit_filter.value == 0U); 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.supported);
PP_EXPECT(h, unknown.kind == pp::renderer::gl::OpenGlPlannedCommandKind::unknown); 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_EXPECT(h, pp::renderer::gl::planned_command_kind_name(
pp::renderer::gl::OpenGlPlannedCommandKind::passthrough) pp::renderer::gl::OpenGlPlannedCommandKind::passthrough)
== std::string_view("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( PP_EXPECT(h, pp::renderer::gl::planned_command_kind_name(
static_cast<pp::renderer::gl::OpenGlPlannedCommandKind>(255U)) static_cast<pp::renderer::gl::OpenGlPlannedCommandKind>(255U))
== std::string_view("unknown")); == 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); 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<pp::renderer::RecordedRenderCommand> 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() int main()
@@ -313,10 +469,12 @@ int main()
pp::tests::Harness harness; pp::tests::Harness harness;
harness.run("maps_render_pass_and_state_commands", maps_render_pass_and_state_commands); 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_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("rejects_unsupported_command_tokens", rejects_unsupported_command_tokens);
harness.run("names_planned_command_kinds", names_planned_command_kinds); harness.run("names_planned_command_kinds", names_planned_command_kinds);
harness.run("plans_valid_recorded_command_streams", plans_valid_recorded_command_streams); 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("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("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(); return harness.finish();
} }

View File

@@ -2785,6 +2785,12 @@ int record_render(int argc, char** argv)
<< ",\"commands\":" << open_gl_plan.commands.size() << ",\"commands\":" << open_gl_plan.commands.size()
<< ",\"renderPasses\":" << open_gl_plan.render_pass_count << ",\"renderPasses\":" << open_gl_plan.render_pass_count
<< ",\"drawCommands\":" << open_gl_plan.draw_command_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 << ",\"passthroughCommands\":" << open_gl_plan.passthrough_command_count
<< ",\"traceCommands\":" << open_gl_plan.trace_command_count << ",\"traceCommands\":" << open_gl_plan.trace_command_count
<< ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_count << ",\"unsupportedCommands\":" << open_gl_plan.unsupported_command_count