Add renderer render pass clear contract

This commit is contained in:
2026-06-02 16:23:02 +02:00
parent 8232b0efc8
commit 58f163788b
9 changed files with 191 additions and 41 deletions

View File

@@ -285,18 +285,19 @@ Known local toolchain state:
source code reintroduces raw `GL_*`/`WGL_*` constants outside the allowed source code reintroduces raw `GL_*`/`WGL_*` constants outside the allowed
legacy OpenGL implementation files. legacy OpenGL implementation files.
- `pp_renderer_api` exposes a headless `RecordingRenderDevice` that validates - `pp_renderer_api` exposes a headless `RecordingRenderDevice` that validates
backend-owned resource creation, command order, scissor state, depth state, backend-owned resource creation, command order, render-pass color/depth/
blend state, texture-slot binding, sampler-state binding, texture-upload byte stencil clear intent, scissor state, depth state, blend state, texture-slot
counts, shader-uniform writes, readback bounds, frame-capture sources, binding, sampler-state binding, texture-upload byte counts,
destination buffer sizes, and render-target blit regions, records shader-uniform writes, readback bounds, frame-capture sources, destination
render/scissor/depth/blend/shader-uniform/texture-bind/sampler-bind/upload/ buffer sizes, and render-target blit regions, records
readback/frame-capture/blit commands, draw mesh inputs, and records trace render-pass-clear/scissor/depth/blend/shader-uniform/texture-bind/
markers without a window or GL context. sampler-bind/upload/readback/frame-capture/blit commands, draw mesh inputs,
and records trace markers without a window or GL context.
- `pano_cli record-render` exposes the recording renderer through JSON - `pano_cli record-render` exposes the recording renderer through JSON
automation, including scissor/depth/blend/shader-uniform/texture-bind/ automation, including render-pass/depth-clear counts, scissor/depth/blend/
sampler-bind/upload/readback/frame-capture/blit command and byte totals, shader-uniform/texture-bind/sampler-bind/upload/readback/frame-capture/blit
backend resource creation counts, plus draw vertex/index totals, and is command and byte totals, backend resource creation counts, plus draw
covered by vertex/index totals, and is covered by
`pano_cli_record_render_smoke` plus `pano_cli_record_render_smoke` plus
`pano_cli_record_render_rejects_oversized_target`. `pano_cli_record_render_rejects_oversized_target`.
- `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory` - `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory`

View File

@@ -724,10 +724,11 @@ Results:
validation, texture-upload byte-count validation, frame-capture byte-size and validation, texture-upload byte-count validation, frame-capture byte-size and
command-order validation, render-target blit validation, texture-slot binding command-order validation, render-target blit validation, texture-slot binding
validation, blend-state validation, scissor-state validation, validation, blend-state validation, scissor-state validation,
shader-uniform write validation, backend-neutral resource factory validation, render-pass color/depth/stencil clear validation, shader-uniform write
recording scissor/depth/blend/shader-uniform/texture/sampler-bind/upload/ validation, backend-neutral resource factory validation, recording
readback/frame-capture/blit command capture, draw mesh-input capture, and render-pass clear/scissor/depth/blend/shader-uniform/texture/sampler-bind/
invalid catalog rejection. upload/readback/frame-capture/blit command capture, draw mesh-input capture,
and invalid catalog rejection.
- `pp_paint_renderer_compositor_tests` passed. - `pp_paint_renderer_compositor_tests` passed.
- `pp_ui_core_color_tests` passed. - `pp_ui_core_color_tests` passed.
- `pp_ui_core_layout_value_tests` passed. - `pp_ui_core_layout_value_tests` passed.
@@ -822,20 +823,21 @@ Results:
implementation files. implementation files.
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict - `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
renderer-owned resource factory and renderer-owned resource factory and
command-order/scissor-state/depth-state/blend-state/texture-bind/ command-order/render-pass-clear/scissor-state/depth-state/blend-state/
sampler-bind/shader-uniform/texture-upload/readback/frame-capture/blit texture-bind/sampler-bind/shader-uniform/texture-upload/readback/
validation; it creates validated textures, render targets, shaders, meshes, frame-capture/blit validation; it creates validated textures, render targets,
and readback buffers, then records commands, trace markers, scissor state, shaders, meshes, and readback buffers, then records commands, trace markers,
depth state, blend state, shader uniform writes, texture/sampler binds, draw render-pass color/depth/stencil clear intent, scissor state, depth state,
mesh inputs, uploads/readbacks, frame captures, and render-target blits, blend state, shader uniform writes, texture/sampler binds, draw mesh inputs,
giving automation a backend-neutral render path that does not require a uploads/readbacks, frame captures, and render-target blits, giving automation
window or GL context. a backend-neutral render path that does not require a window or GL context.
- `pano_cli record-render` exercises that headless recording renderer and emits - `pano_cli record-render` exercises that headless recording renderer and emits
JSON command counts, resource creation counts, target dimensions, backend JSON command counts, resource creation counts, target dimensions, backend
name, trace/draw summary, and draw vertex/index totals, scissor/depth/ name, trace/draw summary, render-pass/depth-clear counts, and draw
blend-state plus shader-uniform/texture/sampler-bind/upload/readback/ vertex/index totals, scissor/depth/blend-state plus shader-uniform/texture/
frame-capture/blit command/byte totals for agent automation, with an sampler-bind/upload/readback/frame-capture/blit command/byte totals for
expected-failure smoke for oversized render/readback targets. agent automation, with an expected-failure smoke for oversized
render/readback targets.
- `pano_cli simulate-document-history` exercises pure document history - `pano_cli simulate-document-history` exercises pure document history
apply/undo/redo behavior and emits JSON layer/frame/history state for agent apply/undo/redo behavior and emits JSON layer/frame/history state for agent
automation. automation.

View File

@@ -94,7 +94,7 @@ RecordingCommandContext::RecordingCommandContext(std::vector<RecordedRenderComma
pp::foundation::Status RecordingCommandContext::begin_render_pass( pp::foundation::Status RecordingCommandContext::begin_render_pass(
IRenderTarget& target, IRenderTarget& target,
ClearColor clear_color) noexcept RenderPassDesc desc) noexcept
{ {
if (in_render_pass_) { if (in_render_pass_) {
return pp::foundation::Status::invalid_argument("render pass is already active"); return pp::foundation::Status::invalid_argument("render pass is already active");
@@ -110,6 +110,11 @@ pp::foundation::Status RecordingCommandContext::begin_render_pass(
return size_status.status(); return size_status.status();
} }
const auto render_pass_status = validate_render_pass_desc(desc);
if (!render_pass_status.ok()) {
return render_pass_status;
}
in_render_pass_ = true; in_render_pass_ = true;
shader_bound_ = false; shader_bound_ = false;
mesh_bound_ = false; mesh_bound_ = false;
@@ -117,7 +122,12 @@ pp::foundation::Status RecordingCommandContext::begin_render_pass(
push_command(commands_, RecordedRenderCommand { push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::begin_render_pass, .kind = RecordedRenderCommandKind::begin_render_pass,
.target_desc = active_target_, .target_desc = active_target_,
.clear_color = clear_color, .clear_color_enabled = desc.clear_color_enabled,
.clear_color = desc.clear_color,
.clear_depth_enabled = desc.clear_depth_enabled,
.clear_depth = desc.clear_depth,
.clear_stencil_enabled = desc.clear_stencil_enabled,
.clear_stencil = desc.clear_stencil,
}); });
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }

View File

@@ -30,7 +30,12 @@ enum class RecordedRenderCommandKind : std::uint8_t {
struct RecordedRenderCommand { struct RecordedRenderCommand {
RecordedRenderCommandKind kind = RecordedRenderCommandKind::draw; RecordedRenderCommandKind kind = RecordedRenderCommandKind::draw;
TextureDesc target_desc {}; TextureDesc target_desc {};
bool clear_color_enabled = false;
ClearColor clear_color {}; ClearColor clear_color {};
bool clear_depth_enabled = false;
float clear_depth = 1.0F;
bool clear_stencil_enabled = false;
std::uint8_t clear_stencil = 0;
Viewport viewport {}; Viewport viewport {};
ScissorRect scissor {}; ScissorRect scissor {};
BlendState blend_state {}; BlendState blend_state {};
@@ -107,7 +112,7 @@ public:
[[nodiscard]] pp::foundation::Status begin_render_pass( [[nodiscard]] pp::foundation::Status begin_render_pass(
IRenderTarget& target, IRenderTarget& target,
ClearColor clear_color) noexcept override; RenderPassDesc desc) noexcept override;
[[nodiscard]] pp::foundation::Status set_viewport(Viewport viewport) noexcept override; [[nodiscard]] pp::foundation::Status set_viewport(Viewport viewport) noexcept override;
[[nodiscard]] pp::foundation::Status set_scissor(ScissorRect scissor) noexcept override; [[nodiscard]] pp::foundation::Status set_scissor(ScissorRect scissor) noexcept override;
[[nodiscard]] pp::foundation::Status set_blend_state(BlendState state) noexcept override; [[nodiscard]] pp::foundation::Status set_blend_state(BlendState state) noexcept override;

View File

@@ -163,6 +163,27 @@ pp::foundation::Status validate_scissor(ScissorRect scissor, Extent2D target_ext
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }
pp::foundation::Status validate_render_pass_desc(RenderPassDesc desc) noexcept
{
if (desc.clear_color_enabled
&& (!std::isfinite(desc.clear_color.r)
|| !std::isfinite(desc.clear_color.g)
|| !std::isfinite(desc.clear_color.b)
|| !std::isfinite(desc.clear_color.a))) {
return pp::foundation::Status::invalid_argument("render pass clear color must be finite");
}
if (desc.clear_depth_enabled && !std::isfinite(desc.clear_depth)) {
return pp::foundation::Status::invalid_argument("render pass clear depth must be finite");
}
if (desc.clear_depth_enabled && (desc.clear_depth < 0.0F || desc.clear_depth > 1.0F)) {
return pp::foundation::Status::out_of_range("render pass clear depth must be within 0..1");
}
return pp::foundation::Status::success();
}
pp::foundation::Status validate_blend_factor(BlendFactor factor) noexcept pp::foundation::Status validate_blend_factor(BlendFactor factor) noexcept
{ {
switch (factor) { switch (factor) {

View File

@@ -64,6 +64,15 @@ struct ClearColor {
float a = 0.0F; float a = 0.0F;
}; };
struct RenderPassDesc {
bool clear_color_enabled = true;
ClearColor clear_color;
bool clear_depth_enabled = false;
float clear_depth = 1.0F;
bool clear_stencil_enabled = false;
std::uint8_t clear_stencil = 0;
};
enum class PrimitiveTopology : std::uint8_t { enum class PrimitiveTopology : std::uint8_t {
triangles, triangles,
triangle_strip, triangle_strip,
@@ -201,7 +210,7 @@ public:
virtual ~ICommandContext() = default; virtual ~ICommandContext() = default;
[[nodiscard]] virtual pp::foundation::Status begin_render_pass( [[nodiscard]] virtual pp::foundation::Status begin_render_pass(
IRenderTarget& target, IRenderTarget& target,
ClearColor clear_color) noexcept = 0; RenderPassDesc desc) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status set_viewport(Viewport viewport) noexcept = 0; [[nodiscard]] virtual pp::foundation::Status set_viewport(Viewport viewport) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status set_scissor(ScissorRect scissor) noexcept = 0; [[nodiscard]] virtual pp::foundation::Status set_scissor(ScissorRect scissor) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status set_blend_state(BlendState state) noexcept = 0; [[nodiscard]] virtual pp::foundation::Status set_blend_state(BlendState state) noexcept = 0;
@@ -260,6 +269,7 @@ public:
[[nodiscard]] pp::foundation::Status validate_extent(Extent2D extent) noexcept; [[nodiscard]] pp::foundation::Status validate_extent(Extent2D extent) noexcept;
[[nodiscard]] pp::foundation::Status validate_viewport(Viewport viewport, Extent2D target_extent) noexcept; [[nodiscard]] pp::foundation::Status validate_viewport(Viewport viewport, Extent2D target_extent) noexcept;
[[nodiscard]] pp::foundation::Status validate_scissor(ScissorRect scissor, Extent2D target_extent) noexcept; [[nodiscard]] pp::foundation::Status validate_scissor(ScissorRect scissor, Extent2D target_extent) noexcept;
[[nodiscard]] pp::foundation::Status validate_render_pass_desc(RenderPassDesc desc) noexcept;
[[nodiscard]] pp::foundation::Status validate_blend_factor(BlendFactor factor) noexcept; [[nodiscard]] pp::foundation::Status validate_blend_factor(BlendFactor factor) noexcept;
[[nodiscard]] pp::foundation::Status validate_blend_op(BlendOp op) noexcept; [[nodiscard]] pp::foundation::Status validate_blend_op(BlendOp op) noexcept;
[[nodiscard]] pp::foundation::Status validate_blend_state(BlendState state) noexcept; [[nodiscard]] pp::foundation::Status validate_blend_state(BlendState state) noexcept;

View File

@@ -365,7 +365,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\".*\"width\":32.*\"height\":16.*\"createdResources\":6.*\"commands\":17.*\"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.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048") PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"createdResources\":6.*\"commands\":17.*\"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.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048.*\"blitCommands\":1.*\"blitSourceBytes\":2048.*\"blitDestinationBytes\":2048")
add_test(NAME pano_cli_record_render_rejects_oversized_target add_test(NAME pano_cli_record_render_rejects_oversized_target
COMMAND "${CMAKE_COMMAND}" COMMAND "${CMAKE_COMMAND}"

View File

@@ -5,6 +5,7 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <limits>
#include <memory> #include <memory>
#include <new> #include <new>
#include <string_view> #include <string_view>
@@ -40,6 +41,7 @@ using pp::renderer::RecordingRenderDevice;
using pp::renderer::RecordingRenderTarget; using pp::renderer::RecordingRenderTarget;
using pp::renderer::RecordingShaderProgram; using pp::renderer::RecordingShaderProgram;
using pp::renderer::RecordingTexture2D; using pp::renderer::RecordingTexture2D;
using pp::renderer::RenderPassDesc;
using pp::renderer::SamplerAddressMode; using pp::renderer::SamplerAddressMode;
using pp::renderer::sampler_address_mode_name; using pp::renderer::sampler_address_mode_name;
using pp::renderer::SamplerDesc; using pp::renderer::SamplerDesc;
@@ -72,6 +74,7 @@ using pp::renderer::validate_compare_op;
using pp::renderer::validate_depth_state; using pp::renderer::validate_depth_state;
using pp::renderer::validate_mesh_desc; using pp::renderer::validate_mesh_desc;
using pp::renderer::validate_readback_region; using pp::renderer::validate_readback_region;
using pp::renderer::validate_render_pass_desc;
using pp::renderer::validate_sampler_address_mode; using pp::renderer::validate_sampler_address_mode;
using pp::renderer::validate_sampler_desc; using pp::renderer::validate_sampler_desc;
using pp::renderer::validate_sampler_filter; using pp::renderer::validate_sampler_filter;
@@ -192,9 +195,14 @@ class FakeCommandContext final : public ICommandContext {
public: public:
[[nodiscard]] pp::foundation::Status begin_render_pass( [[nodiscard]] pp::foundation::Status begin_render_pass(
IRenderTarget& target, IRenderTarget& target,
ClearColor) noexcept override RenderPassDesc desc) noexcept override
{ {
const auto render_pass_status = validate_render_pass_desc(desc);
if (!render_pass_status.ok()) {
return render_pass_status;
}
in_render_pass = true; in_render_pass = true;
last_render_pass_desc = desc;
return validate_extent(target.color_desc().extent); return validate_extent(target.color_desc().extent);
} }
@@ -405,6 +413,7 @@ public:
} }
bool in_render_pass = false; bool in_render_pass = false;
RenderPassDesc last_render_pass_desc {};
const char* shader_name = nullptr; const char* shader_name = nullptr;
const char* last_uniform_name = nullptr; const char* last_uniform_name = nullptr;
std::size_t last_uniform_bytes = 0; std::size_t last_uniform_bytes = 0;
@@ -801,6 +810,50 @@ void validates_viewports_and_mesh_descriptors(pp::tests::Harness& h)
PP_EXPECT(h, invalid_slot.code == StatusCode::out_of_range); PP_EXPECT(h, invalid_slot.code == StatusCode::out_of_range);
} }
void validates_render_pass_descriptors(pp::tests::Harness& h)
{
const auto valid = validate_render_pass_desc(RenderPassDesc {
.clear_color = ClearColor { .r = 0.1F, .g = 0.2F, .b = 0.3F, .a = 1.0F },
.clear_depth_enabled = true,
.clear_depth = 0.5F,
.clear_stencil_enabled = true,
.clear_stencil = 7,
});
const auto no_clear = validate_render_pass_desc(RenderPassDesc {
.clear_color_enabled = false,
.clear_color = ClearColor {},
.clear_depth_enabled = false,
.clear_stencil_enabled = false,
});
const auto bad_color = validate_render_pass_desc(RenderPassDesc {
.clear_color = ClearColor {
.r = std::numeric_limits<float>::infinity(),
.g = 0.0F,
.b = 0.0F,
.a = 1.0F,
},
});
const auto nan_depth = validate_render_pass_desc(RenderPassDesc {
.clear_color = ClearColor {},
.clear_depth_enabled = true,
.clear_depth = std::numeric_limits<float>::quiet_NaN(),
});
const auto out_of_range_depth = validate_render_pass_desc(RenderPassDesc {
.clear_color = ClearColor {},
.clear_depth_enabled = true,
.clear_depth = 1.5F,
});
PP_EXPECT(h, valid.ok());
PP_EXPECT(h, no_clear.ok());
PP_EXPECT(h, !bad_color.ok());
PP_EXPECT(h, bad_color.code == StatusCode::invalid_argument);
PP_EXPECT(h, !nan_depth.ok());
PP_EXPECT(h, nan_depth.code == StatusCode::invalid_argument);
PP_EXPECT(h, !out_of_range_depth.ok());
PP_EXPECT(h, out_of_range_depth.code == StatusCode::out_of_range);
}
void validates_shader_program_descriptors(pp::tests::Harness& h) void validates_shader_program_descriptors(pp::tests::Harness& h)
{ {
constexpr char vertex_source[] = "#version 330 core\nvoid main(){}"; constexpr char vertex_source[] = "#version 330 core\nvoid main(){}";
@@ -951,7 +1004,12 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, device.trace_recorder.last_name == std::string_view("begin")); PP_EXPECT(h, device.trace_recorder.last_name == std::string_view("begin"));
auto& context = device.immediate_context(); auto& context = device.immediate_context();
PP_EXPECT(h, context.begin_render_pass(target, ClearColor { .r = 0.1F, .g = 0.2F, .b = 0.3F, .a = 1.0F }).ok()); PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {
.clear_color = ClearColor { .r = 0.1F, .g = 0.2F, .b = 0.3F, .a = 1.0F },
.clear_depth_enabled = true,
.clear_depth = 0.75F,
})
.ok());
PP_EXPECT(h, context.set_viewport(Viewport { .x = 0, .y = 0, .width = 64, .height = 32 }).ok()); PP_EXPECT(h, context.set_viewport(Viewport { .x = 0, .y = 0, .width = 64, .height = 32 }).ok());
PP_EXPECT(h, context.set_scissor(ScissorRect { .enabled = true, .x = 4, .y = 5, .width = 16, .height = 8 }).ok()); PP_EXPECT(h, context.set_scissor(ScissorRect { .enabled = true, .x = 4, .y = 5, .width = 16, .height = 8 }).ok());
PP_EXPECT(h, context.set_blend_state(BlendState { PP_EXPECT(h, context.set_blend_state(BlendState {
@@ -1007,6 +1065,9 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, device.context.last_blend_state.enabled); PP_EXPECT(h, device.context.last_blend_state.enabled);
PP_EXPECT(h, device.context.last_blend_state.source_color == BlendFactor::source_alpha); PP_EXPECT(h, device.context.last_blend_state.source_color == BlendFactor::source_alpha);
PP_EXPECT(h, device.context.last_blend_state.destination_color == BlendFactor::one_minus_source_alpha); PP_EXPECT(h, device.context.last_blend_state.destination_color == BlendFactor::one_minus_source_alpha);
PP_EXPECT(h, device.context.last_render_pass_desc.clear_color.a == 1.0F);
PP_EXPECT(h, device.context.last_render_pass_desc.clear_depth_enabled);
PP_EXPECT(h, device.context.last_render_pass_desc.clear_depth == 0.75F);
PP_EXPECT(h, device.context.last_depth_state.test_enabled); PP_EXPECT(h, device.context.last_depth_state.test_enabled);
PP_EXPECT(h, device.context.last_depth_state.write_enabled); PP_EXPECT(h, device.context.last_depth_state.write_enabled);
PP_EXPECT(h, device.context.last_depth_state.compare == CompareOp::less_or_equal); PP_EXPECT(h, device.context.last_depth_state.compare == CompareOp::less_or_equal);
@@ -1109,7 +1170,7 @@ void recording_renderer_records_shader_uniform_writes(pp::tests::Harness& h)
PP_EXPECT(h, !before_begin.ok()); PP_EXPECT(h, !before_begin.ok());
PP_EXPECT(h, before_begin.code == StatusCode::invalid_argument); PP_EXPECT(h, before_begin.code == StatusCode::invalid_argument);
PP_EXPECT(h, context.begin_render_pass(target, ClearColor {}).ok()); PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok());
const auto before_shader = context.set_shader_uniform("mvp", uniform_bytes); const auto before_shader = context.set_shader_uniform("mvp", uniform_bytes);
PP_EXPECT(h, !before_shader.ok()); PP_EXPECT(h, !before_shader.ok());
PP_EXPECT(h, before_shader.code == StatusCode::invalid_argument); PP_EXPECT(h, before_shader.code == StatusCode::invalid_argument);
@@ -1163,7 +1224,14 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
device.trace()->marker("renderer", "frame"); device.trace()->marker("renderer", "frame");
auto& context = device.immediate_context(); auto& context = device.immediate_context();
PP_EXPECT(h, context.begin_render_pass(target, ClearColor { .r = 0.2F, .g = 0.3F, .b = 0.4F, .a = 1.0F }).ok()); PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {
.clear_color = ClearColor { .r = 0.2F, .g = 0.3F, .b = 0.4F, .a = 1.0F },
.clear_depth_enabled = true,
.clear_depth = 1.0F,
.clear_stencil_enabled = true,
.clear_stencil = 7,
})
.ok());
PP_EXPECT(h, context.set_viewport(Viewport { .x = 0, .y = 0, .width = 64, .height = 32 }).ok()); PP_EXPECT(h, context.set_viewport(Viewport { .x = 0, .y = 0, .width = 64, .height = 32 }).ok());
PP_EXPECT(h, context.set_scissor(ScissorRect { .enabled = true, .x = 4, .y = 6, .width = 16, .height = 8 }).ok()); PP_EXPECT(h, context.set_scissor(ScissorRect { .enabled = true, .x = 4, .y = 6, .width = 16, .height = 8 }).ok());
PP_EXPECT(h, context.set_blend_state(BlendState { PP_EXPECT(h, context.set_blend_state(BlendState {
@@ -1201,7 +1269,12 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
PP_EXPECT(h, commands[0].name == std::string_view("frame")); PP_EXPECT(h, commands[0].name == std::string_view("frame"));
PP_EXPECT(h, commands[1].kind == RecordedRenderCommandKind::begin_render_pass); PP_EXPECT(h, commands[1].kind == RecordedRenderCommandKind::begin_render_pass);
PP_EXPECT(h, commands[1].target_desc.extent.width == 64U); PP_EXPECT(h, commands[1].target_desc.extent.width == 64U);
PP_EXPECT(h, commands[1].clear_color_enabled);
PP_EXPECT(h, commands[1].clear_color.a == 1.0F); PP_EXPECT(h, commands[1].clear_color.a == 1.0F);
PP_EXPECT(h, commands[1].clear_depth_enabled);
PP_EXPECT(h, commands[1].clear_depth == 1.0F);
PP_EXPECT(h, commands[1].clear_stencil_enabled);
PP_EXPECT(h, commands[1].clear_stencil == 7U);
PP_EXPECT(h, commands[2].kind == RecordedRenderCommandKind::set_viewport); PP_EXPECT(h, commands[2].kind == RecordedRenderCommandKind::set_viewport);
PP_EXPECT(h, commands[2].viewport.height == 32U); PP_EXPECT(h, commands[2].viewport.height == 32U);
PP_EXPECT(h, commands[3].kind == RecordedRenderCommandKind::set_scissor); PP_EXPECT(h, commands[3].kind == RecordedRenderCommandKind::set_scissor);
@@ -1350,12 +1423,21 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
PP_EXPECT(h, !sampler_before_begin.ok()); PP_EXPECT(h, !sampler_before_begin.ok());
PP_EXPECT(h, sampler_before_begin.code == StatusCode::invalid_argument); PP_EXPECT(h, sampler_before_begin.code == StatusCode::invalid_argument);
const auto invalid_target = context.begin_render_pass(non_render_target, ClearColor {}); const auto invalid_target = context.begin_render_pass(non_render_target, RenderPassDesc {});
PP_EXPECT(h, !invalid_target.ok()); PP_EXPECT(h, !invalid_target.ok());
PP_EXPECT(h, invalid_target.code == StatusCode::invalid_argument); PP_EXPECT(h, invalid_target.code == StatusCode::invalid_argument);
PP_EXPECT(h, device.commands().empty()); PP_EXPECT(h, device.commands().empty());
PP_EXPECT(h, context.begin_render_pass(target, ClearColor {}).ok()); const auto invalid_clear_depth = context.begin_render_pass(target, RenderPassDesc {
.clear_color = ClearColor {},
.clear_depth_enabled = true,
.clear_depth = -0.25F,
});
PP_EXPECT(h, !invalid_clear_depth.ok());
PP_EXPECT(h, invalid_clear_depth.code == StatusCode::out_of_range);
PP_EXPECT(h, device.commands().empty());
PP_EXPECT(h, context.begin_render_pass(target, RenderPassDesc {}).ok());
const auto upload_during_render_pass = context.upload_texture( const auto upload_during_render_pass = context.upload_texture(
texture, texture,
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 }, ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
@@ -1383,7 +1465,7 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
PP_EXPECT(h, !blit_during_render_pass.ok()); PP_EXPECT(h, !blit_during_render_pass.ok());
PP_EXPECT(h, blit_during_render_pass.code == StatusCode::invalid_argument); PP_EXPECT(h, blit_during_render_pass.code == StatusCode::invalid_argument);
const auto nested_begin = context.begin_render_pass(target, ClearColor {}); const auto nested_begin = context.begin_render_pass(target, RenderPassDesc {});
PP_EXPECT(h, !nested_begin.ok()); PP_EXPECT(h, !nested_begin.ok());
PP_EXPECT(h, nested_begin.code == StatusCode::invalid_argument); PP_EXPECT(h, nested_begin.code == StatusCode::invalid_argument);
@@ -1577,6 +1659,7 @@ int main()
harness.run("validates_depth_contract", validates_depth_contract); harness.run("validates_depth_contract", validates_depth_contract);
harness.run("validates_sampler_contract", validates_sampler_contract); harness.run("validates_sampler_contract", validates_sampler_contract);
harness.run("validates_viewports_and_mesh_descriptors", validates_viewports_and_mesh_descriptors); harness.run("validates_viewports_and_mesh_descriptors", validates_viewports_and_mesh_descriptors);
harness.run("validates_render_pass_descriptors", validates_render_pass_descriptors);
harness.run("validates_shader_program_descriptors", validates_shader_program_descriptors); harness.run("validates_shader_program_descriptors", validates_shader_program_descriptors);
harness.run("validates_shader_uniform_writes", validates_shader_uniform_writes); harness.run("validates_shader_uniform_writes", validates_shader_uniform_writes);
harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog); harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog);

View File

@@ -2297,7 +2297,11 @@ int record_render(int argc, char** argv)
const auto begin_status = context.begin_render_pass( const auto begin_status = context.begin_render_pass(
*target.value(), *target.value(),
pp::renderer::ClearColor { .r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F }); pp::renderer::RenderPassDesc {
.clear_color = pp::renderer::ClearColor { .r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F },
.clear_depth_enabled = true,
.clear_depth = 1.0F,
});
if (!begin_status.ok()) { if (!begin_status.ok()) {
print_error("record-render", begin_status.message); print_error("record-render", begin_status.message);
return 2; return 2;
@@ -2435,6 +2439,9 @@ int record_render(int argc, char** argv)
std::size_t capture_commands = 0; std::size_t capture_commands = 0;
std::size_t blit_commands = 0; std::size_t blit_commands = 0;
std::size_t trace_markers = 0; std::size_t trace_markers = 0;
std::size_t render_passes = 0;
std::size_t depth_clears = 0;
std::size_t stencil_clears = 0;
std::uint64_t draw_vertices = 0; std::uint64_t draw_vertices = 0;
std::uint64_t draw_indices = 0; std::uint64_t draw_indices = 0;
std::uint64_t uniform_bytes = 0; std::uint64_t uniform_bytes = 0;
@@ -2446,7 +2453,15 @@ int record_render(int argc, char** argv)
std::uint64_t blit_destination_bytes = 0; std::uint64_t blit_destination_bytes = 0;
const auto commands = device.commands(); const auto commands = device.commands();
for (const auto& command : commands) { for (const auto& command : commands) {
if (command.kind == pp::renderer::RecordedRenderCommandKind::draw) { if (command.kind == pp::renderer::RecordedRenderCommandKind::begin_render_pass) {
++render_passes;
if (command.clear_depth_enabled) {
++depth_clears;
}
if (command.clear_stencil_enabled) {
++stencil_clears;
}
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::draw) {
++draw_commands; ++draw_commands;
draw_vertices += command.mesh_desc.vertex_count; draw_vertices += command.mesh_desc.vertex_count;
draw_indices += command.mesh_desc.index_count; draw_indices += command.mesh_desc.index_count;
@@ -2492,6 +2507,9 @@ int record_render(int argc, char** argv)
<< ",\"format\":\"rgba8\"}" << ",\"format\":\"rgba8\"}"
<< ",\"createdResources\":" << created_resources << ",\"createdResources\":" << created_resources
<< ",\"commands\":" << commands.size() << ",\"commands\":" << commands.size()
<< ",\"renderPasses\":" << render_passes
<< ",\"depthClears\":" << depth_clears
<< ",\"stencilClears\":" << stencil_clears
<< ",\"drawCommands\":" << draw_commands << ",\"drawCommands\":" << draw_commands
<< ",\"drawVertices\":" << draw_vertices << ",\"drawVertices\":" << draw_vertices
<< ",\"drawIndices\":" << draw_indices << ",\"drawIndices\":" << draw_indices