Add renderer depth state contract

This commit is contained in:
2026-06-02 15:50:59 +02:00
parent 9a7e1c4def
commit b68ddc42c6
9 changed files with 243 additions and 58 deletions

View File

@@ -285,14 +285,14 @@ 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
command order, scissor state, blend state, texture-slot binding, command order, scissor state, depth state, blend state, texture-slot binding,
texture-upload byte counts, readback bounds, frame-capture sources, texture-upload byte counts, readback bounds, frame-capture sources,
destination buffer sizes, and render-target blit regions, records destination buffer sizes, and render-target blit regions, records
render/scissor/blend/texture-bind/upload/readback/frame-capture/blit render/scissor/depth/blend/texture-bind/upload/readback/frame-capture/blit
commands, and records trace markers without a window or GL context. commands, 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/blend/texture-bind/upload/readback/frame- automation, including scissor/depth/blend/texture-bind/upload/readback/
capture/blit command and byte totals, and is covered by frame-capture/blit command and byte 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

@@ -419,9 +419,9 @@ context, render device, shader program descriptor, mesh, render target,
readback byte-size helpers, texture-upload/readback command validation, readback byte-size helpers, texture-upload/readback command validation,
frame-capture byte-size helpers, frame-capture command validation, frame-capture byte-size helpers, frame-capture command validation,
render-target blit validation, texture-slot binding validation, blend-state render-target blit validation, texture-slot binding validation, blend-state
validation, scissor-state validation, trace interface validation, and the validation, scissor-state validation, depth-state validation, trace interface
canonical PanoPainter shader catalog now consumed by the legacy OpenGL app validation, and the canonical PanoPainter shader catalog now consumed by the
initialization path. legacy OpenGL app initialization path.
`pp_renderer_gl` now exists as the first OpenGL backend library and owns pure `pp_renderer_gl` now exists as the first OpenGL backend library and owns pure
OpenGL capability detection for framebuffer fetch, map-buffer alignment, and OpenGL capability detection for framebuffer fetch, map-buffer alignment, and
float texture support. It also owns the OpenGL texture upload-type mapping used float texture support. It also owns the OpenGL texture upload-type mapping used
@@ -724,7 +724,7 @@ 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, recording validation, blend-state validation, scissor-state validation, recording
scissor/blend/texture-bind/upload/readback/frame-capture/blit command scissor/depth/blend/texture-bind/upload/readback/frame-capture/blit command
capture, and invalid catalog rejection. 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.
@@ -819,16 +819,16 @@ Results:
reintroduces raw `GL_*`/`WGL_*` constants outside the allowed legacy OpenGL reintroduces raw `GL_*`/`WGL_*` constants outside the allowed legacy OpenGL
implementation files. implementation files.
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict - `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
command-order/scissor-state/blend-state/texture-bind/texture-upload/readback/ command-order/scissor-state/depth-state/blend-state/texture-bind/
frame-capture/blit validation; it records commands, trace markers, scissor texture-upload/readback/frame-capture/blit validation; it records commands,
state, blend state, texture binds, uploads/readbacks, frame captures, and trace markers, scissor state, depth state, blend state, texture binds,
render-target blits, giving automation a backend-neutral render path that uploads/readbacks, frame captures, and render-target blits, giving automation
does not require a 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, target dimensions, backend name, trace/draw summary, and JSON command counts, target dimensions, backend name, trace/draw summary, and
scissor/blend-state plus texture-bind/upload/readback/frame-capture/blit scissor/depth/blend-state plus texture-bind/upload/readback/frame-capture/
command/byte totals for agent automation, with an expected-failure smoke for blit command/byte totals for agent automation, with an expected-failure smoke
oversized render/readback targets. 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

@@ -158,6 +158,24 @@ pp::foundation::Status RecordingCommandContext::set_blend_state(BlendState state
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }
pp::foundation::Status RecordingCommandContext::set_depth_state(DepthState state) noexcept
{
if (!in_render_pass_) {
return pp::foundation::Status::invalid_argument("render pass has not begun");
}
const auto status = validate_depth_state(state);
if (!status.ok()) {
return status;
}
push_command(commands_, RecordedRenderCommand {
.kind = RecordedRenderCommandKind::set_depth_state,
.depth_state = state,
});
return pp::foundation::Status::success();
}
pp::foundation::Status RecordingCommandContext::bind_shader(IShaderProgram& shader) noexcept pp::foundation::Status RecordingCommandContext::bind_shader(IShaderProgram& shader) noexcept
{ {
if (!in_render_pass_) { if (!in_render_pass_) {
@@ -440,6 +458,8 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
return "set_scissor"; return "set_scissor";
case RecordedRenderCommandKind::set_blend_state: case RecordedRenderCommandKind::set_blend_state:
return "set_blend_state"; return "set_blend_state";
case RecordedRenderCommandKind::set_depth_state:
return "set_depth_state";
case RecordedRenderCommandKind::bind_shader: case RecordedRenderCommandKind::bind_shader:
return "bind_shader"; return "bind_shader";
case RecordedRenderCommandKind::bind_texture: case RecordedRenderCommandKind::bind_texture:

View File

@@ -12,6 +12,7 @@ enum class RecordedRenderCommandKind : std::uint8_t {
set_viewport, set_viewport,
set_scissor, set_scissor,
set_blend_state, set_blend_state,
set_depth_state,
bind_shader, bind_shader,
bind_texture, bind_texture,
bind_mesh, bind_mesh,
@@ -31,6 +32,7 @@ struct RecordedRenderCommand {
Viewport viewport {}; Viewport viewport {};
ScissorRect scissor {}; ScissorRect scissor {};
BlendState blend_state {}; BlendState blend_state {};
DepthState depth_state {};
MeshDesc mesh_desc {}; MeshDesc mesh_desc {};
TextureDesc texture_desc {}; TextureDesc texture_desc {};
std::uint32_t texture_slot = 0; std::uint32_t texture_slot = 0;
@@ -104,6 +106,7 @@ public:
[[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;
[[nodiscard]] pp::foundation::Status set_depth_state(DepthState state) noexcept override;
[[nodiscard]] pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept override; [[nodiscard]] pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept override;
[[nodiscard]] pp::foundation::Status bind_texture( [[nodiscard]] pp::foundation::Status bind_texture(
std::uint32_t slot, std::uint32_t slot,

View File

@@ -220,6 +220,28 @@ pp::foundation::Status validate_blend_state(BlendState state) noexcept
return validate_blend_op(state.alpha_op); return validate_blend_op(state.alpha_op);
} }
pp::foundation::Status validate_compare_op(CompareOp op) noexcept
{
switch (op) {
case CompareOp::never:
case CompareOp::less:
case CompareOp::equal:
case CompareOp::less_or_equal:
case CompareOp::greater:
case CompareOp::not_equal:
case CompareOp::greater_or_equal:
case CompareOp::always:
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("depth compare operation is not supported");
}
pp::foundation::Status validate_depth_state(DepthState state) noexcept
{
return validate_compare_op(state.compare);
}
pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept
{ {
if (desc.vertex_count == 0) { if (desc.vertex_count == 0) {
@@ -447,4 +469,28 @@ const char* blend_op_name(BlendOp op) noexcept
return "unknown"; return "unknown";
} }
const char* compare_op_name(CompareOp op) noexcept
{
switch (op) {
case CompareOp::never:
return "never";
case CompareOp::less:
return "less";
case CompareOp::equal:
return "equal";
case CompareOp::less_or_equal:
return "less_or_equal";
case CompareOp::greater:
return "greater";
case CompareOp::not_equal:
return "not_equal";
case CompareOp::greater_or_equal:
return "greater_or_equal";
case CompareOp::always:
return "always";
}
return "unknown";
}
} }

View File

@@ -88,6 +88,17 @@ enum class BlendOp : std::uint8_t {
reverse_subtract, reverse_subtract,
}; };
enum class CompareOp : std::uint8_t {
never,
less,
equal,
less_or_equal,
greater,
not_equal,
greater_or_equal,
always,
};
struct BlendState { struct BlendState {
bool enabled = false; bool enabled = false;
BlendFactor source_color = BlendFactor::one; BlendFactor source_color = BlendFactor::one;
@@ -102,6 +113,12 @@ struct BlendState {
bool write_a = true; bool write_a = true;
}; };
struct DepthState {
bool test_enabled = false;
bool write_enabled = false;
CompareOp compare = CompareOp::less_or_equal;
};
struct MeshDesc { struct MeshDesc {
std::uint32_t vertex_count = 0; std::uint32_t vertex_count = 0;
std::uint32_t index_count = 0; std::uint32_t index_count = 0;
@@ -165,6 +182,7 @@ public:
[[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;
[[nodiscard]] virtual pp::foundation::Status set_depth_state(DepthState state) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept = 0; [[nodiscard]] virtual pp::foundation::Status bind_shader(IShaderProgram& shader) noexcept = 0;
[[nodiscard]] virtual pp::foundation::Status bind_texture( [[nodiscard]] virtual pp::foundation::Status bind_texture(
std::uint32_t slot, std::uint32_t slot,
@@ -206,6 +224,8 @@ public:
[[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;
[[nodiscard]] pp::foundation::Status validate_compare_op(CompareOp op) noexcept;
[[nodiscard]] pp::foundation::Status validate_depth_state(DepthState state) noexcept;
[[nodiscard]] pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept; [[nodiscard]] pp::foundation::Status validate_mesh_desc(MeshDesc desc) noexcept;
[[nodiscard]] pp::foundation::Status validate_texture_slot(std::uint32_t slot) noexcept; [[nodiscard]] pp::foundation::Status validate_texture_slot(std::uint32_t slot) noexcept;
[[nodiscard]] pp::foundation::Status validate_shader_program_desc(ShaderProgramDesc desc) noexcept; [[nodiscard]] pp::foundation::Status validate_shader_program_desc(ShaderProgramDesc desc) noexcept;
@@ -224,5 +244,6 @@ public:
[[nodiscard]] const char* blit_filter_name(BlitFilter filter) noexcept; [[nodiscard]] const char* blit_filter_name(BlitFilter filter) noexcept;
[[nodiscard]] const char* blend_factor_name(BlendFactor factor) noexcept; [[nodiscard]] const char* blend_factor_name(BlendFactor factor) noexcept;
[[nodiscard]] const char* blend_op_name(BlendOp op) noexcept; [[nodiscard]] const char* blend_op_name(BlendOp op) noexcept;
[[nodiscard]] const char* compare_op_name(CompareOp op) 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.*\"commands\":14.*\"drawCommands\":1.*\"scissorCommands\":1.*\"blendCommands\":1.*\"bindTextureCommands\":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.*\"commands\":15.*\"drawCommands\":1.*\"scissorCommands\":1.*\"blendCommands\":1.*\"depthCommands\":1.*\"bindTextureCommands\":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

@@ -16,6 +16,9 @@ using pp::renderer::BlendOp;
using pp::renderer::blend_op_name; using pp::renderer::blend_op_name;
using pp::renderer::BlendState; using pp::renderer::BlendState;
using pp::renderer::ClearColor; using pp::renderer::ClearColor;
using pp::renderer::CompareOp;
using pp::renderer::compare_op_name;
using pp::renderer::DepthState;
using pp::renderer::Extent2D; using pp::renderer::Extent2D;
using pp::renderer::frame_capture_byte_size; using pp::renderer::frame_capture_byte_size;
using pp::renderer::ICommandContext; using pp::renderer::ICommandContext;
@@ -56,6 +59,8 @@ using pp::renderer::validate_blit_filter;
using pp::renderer::validate_blend_factor; using pp::renderer::validate_blend_factor;
using pp::renderer::validate_blend_op; using pp::renderer::validate_blend_op;
using pp::renderer::validate_blend_state; using pp::renderer::validate_blend_state;
using pp::renderer::validate_compare_op;
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_scissor; using pp::renderer::validate_scissor;
@@ -184,6 +189,19 @@ public:
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }
[[nodiscard]] pp::foundation::Status set_depth_state(DepthState state) noexcept override
{
if (!in_render_pass) {
return pp::foundation::Status::invalid_argument("render pass has not begun");
}
const auto status = validate_depth_state(state);
if (!status.ok()) {
return status;
}
last_depth_state = state;
return pp::foundation::Status::success();
}
[[nodiscard]] pp::foundation::Status bind_texture( [[nodiscard]] pp::foundation::Status bind_texture(
std::uint32_t slot, std::uint32_t slot,
pp::renderer::ITexture2D& texture) noexcept override pp::renderer::ITexture2D& texture) noexcept override
@@ -303,6 +321,7 @@ public:
const char* shader_name = nullptr; const char* shader_name = nullptr;
ScissorRect last_scissor {}; ScissorRect last_scissor {};
BlendState last_blend_state {}; BlendState last_blend_state {};
DepthState last_depth_state {};
std::uint32_t last_texture_slot = 0; std::uint32_t last_texture_slot = 0;
std::uint64_t last_texture_bytes = 0; std::uint64_t last_texture_bytes = 0;
std::uint64_t last_upload_bytes = 0; std::uint64_t last_upload_bytes = 0;
@@ -510,6 +529,27 @@ void validates_blend_contract(pp::tests::Harness& h)
PP_EXPECT(h, blend_op_name(static_cast<BlendOp>(255)) == std::string_view("unknown")); PP_EXPECT(h, blend_op_name(static_cast<BlendOp>(255)) == std::string_view("unknown"));
} }
void validates_depth_contract(pp::tests::Harness& h)
{
const DepthState read_write_depth {
.test_enabled = true,
.write_enabled = true,
.compare = CompareOp::less_or_equal,
};
PP_EXPECT(h, validate_depth_state(read_write_depth).ok());
PP_EXPECT(h, validate_compare_op(CompareOp::always).ok());
PP_EXPECT(h, compare_op_name(CompareOp::greater_or_equal) == std::string_view("greater_or_equal"));
auto bad_compare = read_write_depth;
bad_compare.compare = static_cast<CompareOp>(255);
const auto bad_compare_status = validate_depth_state(bad_compare);
PP_EXPECT(h, !bad_compare_status.ok());
PP_EXPECT(h, bad_compare_status.code == StatusCode::invalid_argument);
PP_EXPECT(h, compare_op_name(static_cast<CompareOp>(255)) == std::string_view("unknown"));
}
void validates_viewports_and_mesh_descriptors(pp::tests::Harness& h) void validates_viewports_and_mesh_descriptors(pp::tests::Harness& h)
{ {
const Extent2D target { .width = 64, .height = 32 }; const Extent2D target { .width = 64, .height = 32 };
@@ -692,6 +732,12 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
.destination_color = BlendFactor::one_minus_source_alpha, .destination_color = BlendFactor::one_minus_source_alpha,
}) })
.ok()); .ok());
PP_EXPECT(h, context.set_depth_state(DepthState {
.test_enabled = true,
.write_enabled = true,
.compare = CompareOp::less_or_equal,
})
.ok());
PP_EXPECT(h, context.bind_shader(shader).ok()); PP_EXPECT(h, context.bind_shader(shader).ok());
PP_EXPECT(h, context.bind_texture(2, texture).ok()); PP_EXPECT(h, context.bind_texture(2, texture).ok());
PP_EXPECT(h, context.bind_mesh(mesh).ok()); PP_EXPECT(h, context.bind_mesh(mesh).ok());
@@ -726,6 +772,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_depth_state.test_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_texture_slot == 2U); PP_EXPECT(h, device.context.last_texture_slot == 2U);
PP_EXPECT(h, device.context.last_texture_bytes == 8192U); PP_EXPECT(h, device.context.last_texture_bytes == 8192U);
PP_EXPECT(h, device.context.last_upload_bytes == 80U); PP_EXPECT(h, device.context.last_upload_bytes == 80U);
@@ -774,6 +823,12 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
.destination_alpha = BlendFactor::one_minus_source_alpha, .destination_alpha = BlendFactor::one_minus_source_alpha,
}) })
.ok()); .ok());
PP_EXPECT(h, context.set_depth_state(DepthState {
.test_enabled = true,
.write_enabled = true,
.compare = CompareOp::less_or_equal,
})
.ok());
PP_EXPECT(h, context.bind_shader(shader).ok()); PP_EXPECT(h, context.bind_shader(shader).ok());
PP_EXPECT(h, context.bind_texture(1, texture).ok()); PP_EXPECT(h, context.bind_texture(1, texture).ok());
PP_EXPECT(h, context.bind_mesh(mesh).ok()); PP_EXPECT(h, context.bind_mesh(mesh).ok());
@@ -781,7 +836,7 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
context.end_render_pass(); context.end_render_pass();
const auto commands = device.commands(); const auto commands = device.commands();
PP_EXPECT(h, commands.size() == 10U); PP_EXPECT(h, commands.size() == 11U);
PP_EXPECT(h, commands[0].kind == RecordedRenderCommandKind::trace_marker); PP_EXPECT(h, commands[0].kind == RecordedRenderCommandKind::trace_marker);
PP_EXPECT(h, commands[0].component == std::string_view("renderer")); PP_EXPECT(h, commands[0].component == std::string_view("renderer"));
PP_EXPECT(h, commands[0].name == std::string_view("frame")); PP_EXPECT(h, commands[0].name == std::string_view("frame"));
@@ -799,17 +854,22 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
PP_EXPECT(h, commands[4].blend_state.enabled); PP_EXPECT(h, commands[4].blend_state.enabled);
PP_EXPECT(h, commands[4].blend_state.destination_color == BlendFactor::one_minus_source_alpha); PP_EXPECT(h, commands[4].blend_state.destination_color == BlendFactor::one_minus_source_alpha);
PP_EXPECT(h, recorded_render_command_kind_name(commands[4].kind) == std::string_view("set_blend_state")); PP_EXPECT(h, recorded_render_command_kind_name(commands[4].kind) == std::string_view("set_blend_state"));
PP_EXPECT(h, commands[5].kind == RecordedRenderCommandKind::bind_shader); PP_EXPECT(h, commands[5].kind == RecordedRenderCommandKind::set_depth_state);
PP_EXPECT(h, commands[5].name == std::string_view("recorded-shader")); PP_EXPECT(h, commands[5].depth_state.test_enabled);
PP_EXPECT(h, commands[6].kind == RecordedRenderCommandKind::bind_texture); PP_EXPECT(h, commands[5].depth_state.write_enabled);
PP_EXPECT(h, commands[6].texture_slot == 1U); PP_EXPECT(h, commands[5].depth_state.compare == CompareOp::less_or_equal);
PP_EXPECT(h, commands[6].texture_desc.extent.height == 32U); PP_EXPECT(h, recorded_render_command_kind_name(commands[5].kind) == std::string_view("set_depth_state"));
PP_EXPECT(h, recorded_render_command_kind_name(commands[6].kind) == std::string_view("bind_texture")); PP_EXPECT(h, commands[6].kind == RecordedRenderCommandKind::bind_shader);
PP_EXPECT(h, commands[7].kind == RecordedRenderCommandKind::bind_mesh); PP_EXPECT(h, commands[6].name == std::string_view("recorded-shader"));
PP_EXPECT(h, commands[7].mesh_desc.vertex_count == 3U); PP_EXPECT(h, commands[7].kind == RecordedRenderCommandKind::bind_texture);
PP_EXPECT(h, commands[8].kind == RecordedRenderCommandKind::draw); PP_EXPECT(h, commands[7].texture_slot == 1U);
PP_EXPECT(h, commands[9].kind == RecordedRenderCommandKind::end_render_pass); PP_EXPECT(h, commands[7].texture_desc.extent.height == 32U);
PP_EXPECT(h, recorded_render_command_kind_name(commands[8].kind) == std::string_view("draw")); PP_EXPECT(h, recorded_render_command_kind_name(commands[7].kind) == std::string_view("bind_texture"));
PP_EXPECT(h, commands[8].kind == RecordedRenderCommandKind::bind_mesh);
PP_EXPECT(h, commands[8].mesh_desc.vertex_count == 3U);
PP_EXPECT(h, commands[9].kind == RecordedRenderCommandKind::draw);
PP_EXPECT(h, commands[10].kind == RecordedRenderCommandKind::end_render_pass);
PP_EXPECT(h, recorded_render_command_kind_name(commands[9].kind) == std::string_view("draw"));
PP_EXPECT(h, context.upload_texture( PP_EXPECT(h, context.upload_texture(
texture, texture,
@@ -817,12 +877,12 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
upload_bytes) upload_bytes)
.ok()); .ok());
const auto commands_after_upload = device.commands(); const auto commands_after_upload = device.commands();
PP_EXPECT(h, commands_after_upload.size() == 11U); PP_EXPECT(h, commands_after_upload.size() == 12U);
PP_EXPECT(h, commands_after_upload[10].kind == RecordedRenderCommandKind::upload_texture); PP_EXPECT(h, commands_after_upload[11].kind == RecordedRenderCommandKind::upload_texture);
PP_EXPECT(h, commands_after_upload[10].texture_desc.extent.width == 64U); PP_EXPECT(h, commands_after_upload[11].texture_desc.extent.width == 64U);
PP_EXPECT(h, commands_after_upload[10].readback_region.x == 4U); PP_EXPECT(h, commands_after_upload[11].readback_region.x == 4U);
PP_EXPECT(h, commands_after_upload[10].upload_bytes == 96U); PP_EXPECT(h, commands_after_upload[11].upload_bytes == 96U);
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_upload[10].kind) == std::string_view("upload_texture")); PP_EXPECT(h, recorded_render_command_kind_name(commands_after_upload[11].kind) == std::string_view("upload_texture"));
PP_EXPECT(h, context.read_texture( PP_EXPECT(h, context.read_texture(
texture, texture,
@@ -830,22 +890,22 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
readback_buffer) readback_buffer)
.ok()); .ok());
const auto commands_after_readback = device.commands(); const auto commands_after_readback = device.commands();
PP_EXPECT(h, commands_after_readback.size() == 12U); PP_EXPECT(h, commands_after_readback.size() == 13U);
PP_EXPECT(h, commands_after_readback[11].kind == RecordedRenderCommandKind::read_texture); PP_EXPECT(h, commands_after_readback[12].kind == RecordedRenderCommandKind::read_texture);
PP_EXPECT(h, commands_after_readback[11].texture_desc.extent.width == 64U); PP_EXPECT(h, commands_after_readback[12].texture_desc.extent.width == 64U);
PP_EXPECT(h, commands_after_readback[11].readback_region.x == 4U); PP_EXPECT(h, commands_after_readback[12].readback_region.x == 4U);
PP_EXPECT(h, commands_after_readback[11].readback_region.height == 3U); PP_EXPECT(h, commands_after_readback[12].readback_region.height == 3U);
PP_EXPECT(h, commands_after_readback[11].readback_bytes == 96U); PP_EXPECT(h, commands_after_readback[12].readback_bytes == 96U);
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_readback[11].kind) == std::string_view("read_texture")); PP_EXPECT(h, recorded_render_command_kind_name(commands_after_readback[12].kind) == std::string_view("read_texture"));
PP_EXPECT(h, context.capture_frame(target, readback_buffer).ok()); PP_EXPECT(h, context.capture_frame(target, readback_buffer).ok());
const auto commands_after_capture = device.commands(); const auto commands_after_capture = device.commands();
PP_EXPECT(h, commands_after_capture.size() == 13U); PP_EXPECT(h, commands_after_capture.size() == 14U);
PP_EXPECT(h, commands_after_capture[12].kind == RecordedRenderCommandKind::capture_frame); PP_EXPECT(h, commands_after_capture[13].kind == RecordedRenderCommandKind::capture_frame);
PP_EXPECT(h, commands_after_capture[12].target_desc.extent.width == 64U); PP_EXPECT(h, commands_after_capture[13].target_desc.extent.width == 64U);
PP_EXPECT(h, commands_after_capture[12].target_desc.extent.height == 32U); PP_EXPECT(h, commands_after_capture[13].target_desc.extent.height == 32U);
PP_EXPECT(h, commands_after_capture[12].capture_bytes == 8192U); PP_EXPECT(h, commands_after_capture[13].capture_bytes == 8192U);
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_capture[12].kind) == std::string_view("capture_frame")); PP_EXPECT(h, recorded_render_command_kind_name(commands_after_capture[13].kind) == std::string_view("capture_frame"));
PP_EXPECT(h, context.blit_render_target( PP_EXPECT(h, context.blit_render_target(
target, target,
@@ -855,16 +915,16 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
BlitFilter::linear) BlitFilter::linear)
.ok()); .ok());
const auto commands_after_blit = device.commands(); const auto commands_after_blit = device.commands();
PP_EXPECT(h, commands_after_blit.size() == 14U); PP_EXPECT(h, commands_after_blit.size() == 15U);
PP_EXPECT(h, commands_after_blit[13].kind == RecordedRenderCommandKind::blit_render_target); PP_EXPECT(h, commands_after_blit[14].kind == RecordedRenderCommandKind::blit_render_target);
PP_EXPECT(h, commands_after_blit[13].source_desc.extent.width == 64U); PP_EXPECT(h, commands_after_blit[14].source_desc.extent.width == 64U);
PP_EXPECT(h, commands_after_blit[13].destination_desc.extent.height == 32U); PP_EXPECT(h, commands_after_blit[14].destination_desc.extent.height == 32U);
PP_EXPECT(h, commands_after_blit[13].source_region.width == 16U); PP_EXPECT(h, commands_after_blit[14].source_region.width == 16U);
PP_EXPECT(h, commands_after_blit[13].destination_region.x == 2U); PP_EXPECT(h, commands_after_blit[14].destination_region.x == 2U);
PP_EXPECT(h, commands_after_blit[13].blit_filter == BlitFilter::linear); PP_EXPECT(h, commands_after_blit[14].blit_filter == BlitFilter::linear);
PP_EXPECT(h, commands_after_blit[13].blit_source_bytes == 512U); PP_EXPECT(h, commands_after_blit[14].blit_source_bytes == 512U);
PP_EXPECT(h, commands_after_blit[13].blit_destination_bytes == 128U); PP_EXPECT(h, commands_after_blit[14].blit_destination_bytes == 128U);
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_blit[13].kind) == std::string_view("blit_render_target")); PP_EXPECT(h, recorded_render_command_kind_name(commands_after_blit[14].kind) == std::string_view("blit_render_target"));
device.clear(); device.clear();
PP_EXPECT(h, device.commands().empty()); PP_EXPECT(h, device.commands().empty());
@@ -914,6 +974,10 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
PP_EXPECT(h, !scissor_before_begin.ok()); PP_EXPECT(h, !scissor_before_begin.ok());
PP_EXPECT(h, scissor_before_begin.code == StatusCode::invalid_argument); PP_EXPECT(h, scissor_before_begin.code == StatusCode::invalid_argument);
const auto depth_before_begin = context.set_depth_state(DepthState {});
PP_EXPECT(h, !depth_before_begin.ok());
PP_EXPECT(h, depth_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, ClearColor {});
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);
@@ -982,6 +1046,19 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
PP_EXPECT(h, !invalid_scissor.ok()); PP_EXPECT(h, !invalid_scissor.ok());
PP_EXPECT(h, invalid_scissor.code == StatusCode::out_of_range); PP_EXPECT(h, invalid_scissor.code == StatusCode::out_of_range);
auto invalid_depth = DepthState {};
invalid_depth.compare = static_cast<CompareOp>(255);
const auto invalid_depth_state = context.set_depth_state(invalid_depth);
PP_EXPECT(h, !invalid_depth_state.ok());
PP_EXPECT(h, invalid_depth_state.code == StatusCode::invalid_argument);
PP_EXPECT(h, context.set_depth_state(DepthState {
.test_enabled = true,
.write_enabled = true,
.compare = CompareOp::less_or_equal,
})
.ok());
const auto draw_without_bindings = context.draw(); const auto draw_without_bindings = context.draw();
PP_EXPECT(h, !draw_without_bindings.ok()); PP_EXPECT(h, !draw_without_bindings.ok());
PP_EXPECT(h, draw_without_bindings.code == StatusCode::invalid_argument); PP_EXPECT(h, draw_without_bindings.code == StatusCode::invalid_argument);
@@ -1016,6 +1093,10 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
PP_EXPECT(h, !scissor_after_end.ok()); PP_EXPECT(h, !scissor_after_end.ok());
PP_EXPECT(h, scissor_after_end.code == StatusCode::invalid_argument); PP_EXPECT(h, scissor_after_end.code == StatusCode::invalid_argument);
const auto depth_after_end = context.set_depth_state(DepthState {});
PP_EXPECT(h, !depth_after_end.ok());
PP_EXPECT(h, depth_after_end.code == StatusCode::invalid_argument);
const auto bind_texture_after_end = context.bind_texture(0, texture); const auto bind_texture_after_end = context.bind_texture(0, texture);
PP_EXPECT(h, !bind_texture_after_end.ok()); PP_EXPECT(h, !bind_texture_after_end.ok());
PP_EXPECT(h, bind_texture_after_end.code == StatusCode::invalid_argument); PP_EXPECT(h, bind_texture_after_end.code == StatusCode::invalid_argument);
@@ -1105,6 +1186,7 @@ int main()
harness.run("computes_frame_capture_byte_sizes", computes_frame_capture_byte_sizes); harness.run("computes_frame_capture_byte_sizes", computes_frame_capture_byte_sizes);
harness.run("validates_blit_contract", validates_blit_contract); harness.run("validates_blit_contract", validates_blit_contract);
harness.run("validates_blend_contract", validates_blend_contract); harness.run("validates_blend_contract", validates_blend_contract);
harness.run("validates_depth_contract", validates_depth_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_shader_program_descriptors", validates_shader_program_descriptors); harness.run("validates_shader_program_descriptors", validates_shader_program_descriptors);
harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog); harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog);

View File

@@ -2292,6 +2292,11 @@ int record_render(int argc, char** argv)
.source_alpha = pp::renderer::BlendFactor::one, .source_alpha = pp::renderer::BlendFactor::one,
.destination_alpha = pp::renderer::BlendFactor::one_minus_source_alpha, .destination_alpha = pp::renderer::BlendFactor::one_minus_source_alpha,
}); });
const auto depth_status = context.set_depth_state(pp::renderer::DepthState {
.test_enabled = true,
.write_enabled = true,
.compare = pp::renderer::CompareOp::less_or_equal,
});
const auto bind_texture_status = context.bind_texture(0, texture); const auto bind_texture_status = context.bind_texture(0, texture);
const auto mesh_status = context.bind_mesh(mesh); const auto mesh_status = context.bind_mesh(mesh);
const auto draw_status = context.draw(); const auto draw_status = context.draw();
@@ -2305,6 +2310,10 @@ int record_render(int argc, char** argv)
print_error("record-render", blend_status.message); print_error("record-render", blend_status.message);
return 2; return 2;
} }
if (!depth_status.ok()) {
print_error("record-render", depth_status.message);
return 2;
}
if (!bind_texture_status.ok()) { if (!bind_texture_status.ok()) {
print_error("record-render", bind_texture_status.message); print_error("record-render", bind_texture_status.message);
return 2; return 2;
@@ -2362,6 +2371,7 @@ int record_render(int argc, char** argv)
std::size_t draw_commands = 0; std::size_t draw_commands = 0;
std::size_t scissor_commands = 0; std::size_t scissor_commands = 0;
std::size_t blend_commands = 0; std::size_t blend_commands = 0;
std::size_t depth_commands = 0;
std::size_t bind_texture_commands = 0; std::size_t bind_texture_commands = 0;
std::size_t upload_commands = 0; std::size_t upload_commands = 0;
std::size_t readback_commands = 0; std::size_t readback_commands = 0;
@@ -2382,6 +2392,8 @@ int record_render(int argc, char** argv)
++scissor_commands; ++scissor_commands;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::set_blend_state) { } else if (command.kind == pp::renderer::RecordedRenderCommandKind::set_blend_state) {
++blend_commands; ++blend_commands;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::set_depth_state) {
++depth_commands;
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::bind_texture) { } else if (command.kind == pp::renderer::RecordedRenderCommandKind::bind_texture) {
++bind_texture_commands; ++bind_texture_commands;
const auto bound_bytes = pp::renderer::texture_byte_size(command.texture_desc); const auto bound_bytes = pp::renderer::texture_byte_size(command.texture_desc);
@@ -2415,6 +2427,7 @@ int record_render(int argc, char** argv)
<< ",\"drawCommands\":" << draw_commands << ",\"drawCommands\":" << draw_commands
<< ",\"scissorCommands\":" << scissor_commands << ",\"scissorCommands\":" << scissor_commands
<< ",\"blendCommands\":" << blend_commands << ",\"blendCommands\":" << blend_commands
<< ",\"depthCommands\":" << depth_commands
<< ",\"bindTextureCommands\":" << bind_texture_commands << ",\"bindTextureCommands\":" << bind_texture_commands
<< ",\"boundTextureBytes\":" << bound_texture_bytes << ",\"boundTextureBytes\":" << bound_texture_bytes
<< ",\"uploadCommands\":" << upload_commands << ",\"uploadCommands\":" << upload_commands