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

@@ -5,6 +5,7 @@
#include <array>
#include <cstddef>
#include <limits>
#include <memory>
#include <new>
#include <string_view>
@@ -40,6 +41,7 @@ using pp::renderer::RecordingRenderDevice;
using pp::renderer::RecordingRenderTarget;
using pp::renderer::RecordingShaderProgram;
using pp::renderer::RecordingTexture2D;
using pp::renderer::RenderPassDesc;
using pp::renderer::SamplerAddressMode;
using pp::renderer::sampler_address_mode_name;
using pp::renderer::SamplerDesc;
@@ -72,6 +74,7 @@ using pp::renderer::validate_compare_op;
using pp::renderer::validate_depth_state;
using pp::renderer::validate_mesh_desc;
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_desc;
using pp::renderer::validate_sampler_filter;
@@ -192,9 +195,14 @@ class FakeCommandContext final : public ICommandContext {
public:
[[nodiscard]] pp::foundation::Status begin_render_pass(
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;
last_render_pass_desc = desc;
return validate_extent(target.color_desc().extent);
}
@@ -405,6 +413,7 @@ public:
}
bool in_render_pass = false;
RenderPassDesc last_render_pass_desc {};
const char* shader_name = nullptr;
const char* last_uniform_name = nullptr;
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);
}
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)
{
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"));
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_scissor(ScissorRect { .enabled = true, .x = 4, .y = 5, .width = 16, .height = 8 }).ok());
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.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_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.write_enabled);
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.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);
PP_EXPECT(h, !before_shader.ok());
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");
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_scissor(ScissorRect { .enabled = true, .x = 4, .y = 6, .width = 16, .height = 8 }).ok());
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[1].kind == RecordedRenderCommandKind::begin_render_pass);
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_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].viewport.height == 32U);
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.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.code == StatusCode::invalid_argument);
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(
texture,
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.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.code == StatusCode::invalid_argument);
@@ -1577,6 +1659,7 @@ int main()
harness.run("validates_depth_contract", validates_depth_contract);
harness.run("validates_sampler_contract", validates_sampler_contract);
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_uniform_writes", validates_shader_uniform_writes);
harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog);