Add renderer blit command contract
This commit is contained in:
@@ -286,12 +286,12 @@ Known local toolchain state:
|
||||
legacy OpenGL implementation files.
|
||||
- `pp_renderer_api` exposes a headless `RecordingRenderDevice` that validates
|
||||
command order, texture-upload byte counts, readback bounds, frame-capture
|
||||
sources, and destination buffer sizes, records render/upload/readback/
|
||||
frame-capture commands, and records trace markers without a window or GL
|
||||
context.
|
||||
sources, destination buffer sizes, and render-target blit regions, records
|
||||
render/upload/readback/frame-capture/blit commands, and records trace markers
|
||||
without a window or GL context.
|
||||
- `pano_cli record-render` exposes the recording renderer through JSON
|
||||
automation, including upload/readback/frame-capture command and byte totals,
|
||||
and is covered by `pano_cli_record_render_smoke` plus
|
||||
automation, including upload/readback/frame-capture/blit command and byte
|
||||
totals, and is covered by `pano_cli_record_render_smoke` plus
|
||||
`pano_cli_record_render_rejects_oversized_target`.
|
||||
- `pano_cli simulate-document-history` exposes `pp_document::DocumentHistory`
|
||||
apply/undo/redo state through JSON automation and is covered by
|
||||
|
||||
@@ -417,9 +417,10 @@ Status: started. `pp_renderer_api` exists as a headless renderer-neutral target
|
||||
with texture descriptor, byte-size, viewport, mesh, readback bounds, command
|
||||
context, render device, shader program descriptor, mesh, render target,
|
||||
readback byte-size helpers, texture-upload/readback command validation,
|
||||
frame-capture byte-size helpers, frame-capture command validation, trace
|
||||
interface validation, and the canonical PanoPainter shader catalog now consumed
|
||||
by the legacy OpenGL app initialization path.
|
||||
frame-capture byte-size helpers, frame-capture command validation,
|
||||
render-target blit validation, trace interface validation, and the canonical
|
||||
PanoPainter shader catalog now consumed by the legacy OpenGL app initialization
|
||||
path.
|
||||
`pp_renderer_gl` now exists as the first OpenGL backend library and owns pure
|
||||
OpenGL capability detection for framebuffer fetch, map-buffer alignment, and
|
||||
float texture support. It also owns the OpenGL texture upload-type mapping used
|
||||
@@ -720,8 +721,9 @@ Results:
|
||||
- `pp_renderer_api_tests` passed, including shader descriptor validation,
|
||||
PanoPainter shader catalog validation, readback byte-size and command-order
|
||||
validation, texture-upload byte-count validation, frame-capture byte-size and
|
||||
command-order validation, recording upload/readback/frame-capture command
|
||||
capture, and invalid catalog rejection.
|
||||
command-order validation, render-target blit validation, recording
|
||||
upload/readback/frame-capture/blit command capture, and invalid catalog
|
||||
rejection.
|
||||
- `pp_paint_renderer_compositor_tests` passed.
|
||||
- `pp_ui_core_color_tests` passed.
|
||||
- `pp_ui_core_layout_value_tests` passed.
|
||||
@@ -815,13 +817,13 @@ Results:
|
||||
reintroduces raw `GL_*`/`WGL_*` constants outside the allowed legacy OpenGL
|
||||
implementation files.
|
||||
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
|
||||
command-order/texture-upload/readback/frame-capture validation; it records
|
||||
commands, trace markers, texture uploads/readbacks, and frame captures, giving
|
||||
automation a backend-neutral render path that does not require a window or GL
|
||||
context.
|
||||
command-order/texture-upload/readback/frame-capture/blit validation; it
|
||||
records commands, trace markers, texture uploads/readbacks, frame captures,
|
||||
and render-target blits, giving automation 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
|
||||
JSON command counts, target dimensions, backend name, trace/draw summary, and
|
||||
texture-upload/readback/frame-capture command/byte totals for agent
|
||||
texture-upload/readback/frame-capture/blit command/byte totals for agent
|
||||
automation, with an expected-failure smoke for oversized render/readback
|
||||
targets.
|
||||
- `pano_cli simulate-document-history` exercises pure document history
|
||||
|
||||
@@ -256,6 +256,52 @@ pp::foundation::Status RecordingCommandContext::capture_frame(
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status RecordingCommandContext::blit_render_target(
|
||||
IRenderTarget& source,
|
||||
ReadbackRegion source_region,
|
||||
IRenderTarget& destination,
|
||||
ReadbackRegion destination_region,
|
||||
BlitFilter filter) noexcept
|
||||
{
|
||||
if (in_render_pass_) {
|
||||
return pp::foundation::Status::invalid_argument("render target blit must be outside a render pass");
|
||||
}
|
||||
|
||||
const auto source_desc = source.color_desc();
|
||||
const auto destination_desc = destination.color_desc();
|
||||
const auto desc_status = validate_blit_descs(source_desc, destination_desc);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
|
||||
const auto filter_status = validate_blit_filter(filter);
|
||||
if (!filter_status.ok()) {
|
||||
return filter_status;
|
||||
}
|
||||
|
||||
const auto source_bytes = readback_byte_size(source_desc, source_region);
|
||||
if (!source_bytes) {
|
||||
return source_bytes.status();
|
||||
}
|
||||
|
||||
const auto destination_bytes = readback_byte_size(destination_desc, destination_region);
|
||||
if (!destination_bytes) {
|
||||
return destination_bytes.status();
|
||||
}
|
||||
|
||||
push_command(commands_, RecordedRenderCommand {
|
||||
.kind = RecordedRenderCommandKind::blit_render_target,
|
||||
.source_desc = source_desc,
|
||||
.destination_desc = destination_desc,
|
||||
.source_region = source_region,
|
||||
.destination_region = destination_region,
|
||||
.blit_filter = filter,
|
||||
.blit_source_bytes = source_bytes.value(),
|
||||
.blit_destination_bytes = destination_bytes.value(),
|
||||
});
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
void RecordingCommandContext::end_render_pass() noexcept
|
||||
{
|
||||
if (!in_render_pass_) {
|
||||
@@ -339,6 +385,8 @@ const char* recorded_render_command_kind_name(RecordedRenderCommandKind kind) no
|
||||
return "read_texture";
|
||||
case RecordedRenderCommandKind::capture_frame:
|
||||
return "capture_frame";
|
||||
case RecordedRenderCommandKind::blit_render_target:
|
||||
return "blit_render_target";
|
||||
case RecordedRenderCommandKind::end_render_pass:
|
||||
return "end_render_pass";
|
||||
case RecordedRenderCommandKind::trace_marker:
|
||||
|
||||
@@ -16,6 +16,7 @@ enum class RecordedRenderCommandKind : std::uint8_t {
|
||||
upload_texture,
|
||||
read_texture,
|
||||
capture_frame,
|
||||
blit_render_target,
|
||||
end_render_pass,
|
||||
trace_marker,
|
||||
};
|
||||
@@ -27,10 +28,17 @@ struct RecordedRenderCommand {
|
||||
Viewport viewport {};
|
||||
MeshDesc mesh_desc {};
|
||||
TextureDesc texture_desc {};
|
||||
TextureDesc source_desc {};
|
||||
TextureDesc destination_desc {};
|
||||
ReadbackRegion readback_region {};
|
||||
ReadbackRegion source_region {};
|
||||
ReadbackRegion destination_region {};
|
||||
BlitFilter blit_filter = BlitFilter::nearest;
|
||||
std::uint64_t upload_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;
|
||||
const char* component = "";
|
||||
const char* name = "";
|
||||
};
|
||||
@@ -102,6 +110,12 @@ public:
|
||||
[[nodiscard]] pp::foundation::Status capture_frame(
|
||||
IRenderTarget& target,
|
||||
IReadbackBuffer& destination) noexcept override;
|
||||
[[nodiscard]] pp::foundation::Status blit_render_target(
|
||||
IRenderTarget& source,
|
||||
ReadbackRegion source_region,
|
||||
IRenderTarget& destination,
|
||||
ReadbackRegion destination_region,
|
||||
BlitFilter filter) noexcept override;
|
||||
void end_render_pass() noexcept override;
|
||||
|
||||
[[nodiscard]] bool in_render_pass() const noexcept;
|
||||
|
||||
@@ -241,6 +241,40 @@ pp::foundation::Result<std::uint64_t> frame_capture_byte_size(TextureDesc desc)
|
||||
return texture_byte_size(desc);
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept
|
||||
{
|
||||
switch (filter) {
|
||||
case BlitFilter::nearest:
|
||||
case BlitFilter::linear:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("blit filter is not supported");
|
||||
}
|
||||
|
||||
pp::foundation::Status validate_blit_descs(TextureDesc source, TextureDesc destination) noexcept
|
||||
{
|
||||
if (!source.render_target || !destination.render_target) {
|
||||
return pp::foundation::Status::invalid_argument("blit endpoints must be render targets");
|
||||
}
|
||||
|
||||
if (source.format != destination.format) {
|
||||
return pp::foundation::Status::invalid_argument("blit endpoints must use matching texture formats");
|
||||
}
|
||||
|
||||
const auto source_status = texture_byte_size(source);
|
||||
if (!source_status.ok()) {
|
||||
return source_status.status();
|
||||
}
|
||||
|
||||
const auto destination_status = texture_byte_size(destination);
|
||||
if (!destination_status.ok()) {
|
||||
return destination_status.status();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
const char* texture_format_name(TextureFormat format) noexcept
|
||||
{
|
||||
switch (format) {
|
||||
@@ -269,4 +303,16 @@ const char* primitive_topology_name(PrimitiveTopology topology) noexcept
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char* blit_filter_name(BlitFilter filter) noexcept
|
||||
{
|
||||
switch (filter) {
|
||||
case BlitFilter::nearest:
|
||||
return "nearest";
|
||||
case BlitFilter::linear:
|
||||
return "linear";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ enum class PrimitiveTopology : std::uint8_t {
|
||||
lines,
|
||||
};
|
||||
|
||||
enum class BlitFilter : std::uint8_t {
|
||||
nearest,
|
||||
linear,
|
||||
};
|
||||
|
||||
struct MeshDesc {
|
||||
std::uint32_t vertex_count = 0;
|
||||
std::uint32_t index_count = 0;
|
||||
@@ -134,6 +139,12 @@ public:
|
||||
[[nodiscard]] virtual pp::foundation::Status capture_frame(
|
||||
IRenderTarget& target,
|
||||
IReadbackBuffer& destination) noexcept = 0;
|
||||
[[nodiscard]] virtual pp::foundation::Status blit_render_target(
|
||||
IRenderTarget& source,
|
||||
ReadbackRegion source_region,
|
||||
IRenderTarget& destination,
|
||||
ReadbackRegion destination_region,
|
||||
BlitFilter filter) noexcept = 0;
|
||||
virtual void end_render_pass() noexcept = 0;
|
||||
};
|
||||
|
||||
@@ -156,7 +167,12 @@ public:
|
||||
ReadbackRegion region) noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<std::uint64_t> frame_capture_byte_size(TextureDesc desc) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_readback_region(TextureDesc desc, ReadbackRegion region) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_filter(BlitFilter filter) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status validate_blit_descs(
|
||||
TextureDesc source,
|
||||
TextureDesc destination) noexcept;
|
||||
[[nodiscard]] const char* texture_format_name(TextureFormat format) noexcept;
|
||||
[[nodiscard]] const char* primitive_topology_name(PrimitiveTopology topology) noexcept;
|
||||
[[nodiscard]] const char* blit_filter_name(BlitFilter filter) noexcept;
|
||||
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ if(TARGET pano_cli)
|
||||
COMMAND pano_cli record-render --width 32 --height 16)
|
||||
set_tests_properties(pano_cli_record_render_smoke PROPERTIES
|
||||
LABELS "renderer;integration;desktop-fast"
|
||||
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"commands\":10.*\"drawCommands\":1.*\"uploadCommands\":1.*\"uploadBytes\":4.*\"readbackCommands\":1.*\"readbackBytes\":2048.*\"captureCommands\":1.*\"captureBytes\":2048")
|
||||
PASS_REGULAR_EXPRESSION "\"backend\":\"recording\".*\"width\":32.*\"height\":16.*\"commands\":11.*\"drawCommands\":1.*\"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
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <string_view>
|
||||
|
||||
using pp::foundation::StatusCode;
|
||||
using pp::renderer::BlitFilter;
|
||||
using pp::renderer::blit_filter_name;
|
||||
using pp::renderer::ClearColor;
|
||||
using pp::renderer::Extent2D;
|
||||
using pp::renderer::frame_capture_byte_size;
|
||||
@@ -42,6 +44,8 @@ using pp::renderer::ShaderCatalogEntry;
|
||||
using pp::renderer::texture_byte_size;
|
||||
using pp::renderer::texture_format_name;
|
||||
using pp::renderer::validate_extent;
|
||||
using pp::renderer::validate_blit_descs;
|
||||
using pp::renderer::validate_blit_filter;
|
||||
using pp::renderer::validate_mesh_desc;
|
||||
using pp::renderer::validate_readback_region;
|
||||
using pp::renderer::validate_shader_catalog;
|
||||
@@ -200,6 +204,38 @@ public:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status blit_render_target(
|
||||
IRenderTarget& source,
|
||||
ReadbackRegion source_region,
|
||||
IRenderTarget& destination,
|
||||
ReadbackRegion destination_region,
|
||||
BlitFilter filter) noexcept override
|
||||
{
|
||||
const auto source_desc = source.color_desc();
|
||||
const auto destination_desc = destination.color_desc();
|
||||
const auto desc_status = validate_blit_descs(source_desc, destination_desc);
|
||||
if (!desc_status.ok()) {
|
||||
return desc_status;
|
||||
}
|
||||
const auto filter_status = validate_blit_filter(filter);
|
||||
if (!filter_status.ok()) {
|
||||
return filter_status;
|
||||
}
|
||||
const auto source_bytes = readback_byte_size(source_desc, source_region);
|
||||
if (!source_bytes) {
|
||||
return source_bytes.status();
|
||||
}
|
||||
const auto destination_bytes = readback_byte_size(destination_desc, destination_region);
|
||||
if (!destination_bytes) {
|
||||
return destination_bytes.status();
|
||||
}
|
||||
|
||||
last_blit_source_bytes = source_bytes.value();
|
||||
last_blit_destination_bytes = destination_bytes.value();
|
||||
last_blit_filter = filter;
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
void end_render_pass() noexcept override
|
||||
{
|
||||
in_render_pass = false;
|
||||
@@ -210,6 +246,9 @@ public:
|
||||
std::uint64_t last_upload_bytes = 0;
|
||||
std::uint64_t last_readback_bytes = 0;
|
||||
std::uint64_t last_capture_bytes = 0;
|
||||
std::uint64_t last_blit_source_bytes = 0;
|
||||
std::uint64_t last_blit_destination_bytes = 0;
|
||||
BlitFilter last_blit_filter = BlitFilter::nearest;
|
||||
};
|
||||
|
||||
class FakeRenderDevice final : public IRenderDevice {
|
||||
@@ -338,6 +377,42 @@ void computes_frame_capture_byte_sizes(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, non_target.status().code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void validates_blit_contract(pp::tests::Harness& h)
|
||||
{
|
||||
const TextureDesc target_desc {
|
||||
.extent = Extent2D { .width = 16, .height = 8 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.render_target = true,
|
||||
};
|
||||
const TextureDesc r8_target_desc {
|
||||
.extent = Extent2D { .width = 16, .height = 8 },
|
||||
.format = TextureFormat::r8,
|
||||
.render_target = true,
|
||||
};
|
||||
const TextureDesc texture_desc {
|
||||
.extent = Extent2D { .width = 16, .height = 8 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.render_target = false,
|
||||
};
|
||||
|
||||
PP_EXPECT(h, validate_blit_descs(target_desc, target_desc).ok());
|
||||
PP_EXPECT(h, validate_blit_filter(BlitFilter::nearest).ok());
|
||||
PP_EXPECT(h, validate_blit_filter(BlitFilter::linear).ok());
|
||||
PP_EXPECT(h, blit_filter_name(BlitFilter::linear) == std::string_view("linear"));
|
||||
|
||||
const auto non_target = validate_blit_descs(texture_desc, target_desc);
|
||||
const auto mismatched_format = validate_blit_descs(target_desc, r8_target_desc);
|
||||
const auto bad_filter = validate_blit_filter(static_cast<BlitFilter>(255));
|
||||
|
||||
PP_EXPECT(h, !non_target.ok());
|
||||
PP_EXPECT(h, non_target.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !mismatched_format.ok());
|
||||
PP_EXPECT(h, mismatched_format.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !bad_filter.ok());
|
||||
PP_EXPECT(h, bad_filter.code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, blit_filter_name(static_cast<BlitFilter>(255)) == std::string_view("unknown"));
|
||||
}
|
||||
|
||||
void validates_viewports_and_mesh_descriptors(pp::tests::Harness& h)
|
||||
{
|
||||
const Extent2D target { .width = 64, .height = 32 };
|
||||
@@ -512,10 +587,20 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h)
|
||||
readback_buffer)
|
||||
.ok());
|
||||
PP_EXPECT(h, context.capture_frame(target, readback_buffer).ok());
|
||||
PP_EXPECT(h, context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 2, .y = 3, .width = 4, .height = 5 },
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 8, .height = 10 },
|
||||
BlitFilter::linear)
|
||||
.ok());
|
||||
PP_EXPECT(h, device.context.shader_name == std::string_view("fake-shader"));
|
||||
PP_EXPECT(h, device.context.last_upload_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_readback_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_capture_bytes == 8192U);
|
||||
PP_EXPECT(h, device.context.last_blit_source_bytes == 80U);
|
||||
PP_EXPECT(h, device.context.last_blit_destination_bytes == 320U);
|
||||
PP_EXPECT(h, device.context.last_blit_filter == BlitFilter::linear);
|
||||
}
|
||||
|
||||
void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
|
||||
@@ -533,6 +618,11 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
|
||||
.format = TextureFormat::rgba8,
|
||||
.render_target = true,
|
||||
});
|
||||
RecordingRenderTarget blit_target(TextureDesc {
|
||||
.extent = Extent2D { .width = 64, .height = 32 },
|
||||
.format = TextureFormat::rgba8,
|
||||
.render_target = true,
|
||||
});
|
||||
RecordingShaderProgram shader("recorded-shader");
|
||||
RecordingMesh mesh(MeshDesc { .vertex_count = 3, .index_count = 0, .topology = PrimitiveTopology::triangles });
|
||||
|
||||
@@ -601,6 +691,25 @@ void recording_renderer_records_valid_command_sequences(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, commands_after_capture[9].capture_bytes == 8192U);
|
||||
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_capture[9].kind) == std::string_view("capture_frame"));
|
||||
|
||||
PP_EXPECT(h, context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 16, .height = 8 },
|
||||
blit_target,
|
||||
ReadbackRegion { .x = 2, .y = 3, .width = 8, .height = 4 },
|
||||
BlitFilter::linear)
|
||||
.ok());
|
||||
const auto commands_after_blit = device.commands();
|
||||
PP_EXPECT(h, commands_after_blit.size() == 11U);
|
||||
PP_EXPECT(h, commands_after_blit[10].kind == RecordedRenderCommandKind::blit_render_target);
|
||||
PP_EXPECT(h, commands_after_blit[10].source_desc.extent.width == 64U);
|
||||
PP_EXPECT(h, commands_after_blit[10].destination_desc.extent.height == 32U);
|
||||
PP_EXPECT(h, commands_after_blit[10].source_region.width == 16U);
|
||||
PP_EXPECT(h, commands_after_blit[10].destination_region.x == 2U);
|
||||
PP_EXPECT(h, commands_after_blit[10].blit_filter == BlitFilter::linear);
|
||||
PP_EXPECT(h, commands_after_blit[10].blit_source_bytes == 512U);
|
||||
PP_EXPECT(h, commands_after_blit[10].blit_destination_bytes == 128U);
|
||||
PP_EXPECT(h, recorded_render_command_kind_name(commands_after_blit[10].kind) == std::string_view("blit_render_target"));
|
||||
|
||||
device.clear();
|
||||
PP_EXPECT(h, device.commands().empty());
|
||||
}
|
||||
@@ -618,6 +727,11 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
|
||||
.format = TextureFormat::rgba8,
|
||||
.render_target = false,
|
||||
});
|
||||
RecordingRenderTarget r8_target(TextureDesc {
|
||||
.extent = Extent2D { .width = 32, .height = 16 },
|
||||
.format = TextureFormat::r8,
|
||||
.render_target = true,
|
||||
});
|
||||
RecordingTexture2D texture(TextureDesc {
|
||||
.extent = Extent2D { .width = 32, .height = 16 },
|
||||
.format = TextureFormat::rgba8,
|
||||
@@ -660,6 +774,15 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
|
||||
PP_EXPECT(h, !capture_during_render_pass.ok());
|
||||
PP_EXPECT(h, capture_during_render_pass.code == StatusCode::invalid_argument);
|
||||
|
||||
const auto blit_during_render_pass = context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
BlitFilter::nearest);
|
||||
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 {});
|
||||
PP_EXPECT(h, !nested_begin.ok());
|
||||
PP_EXPECT(h, nested_begin.code == StatusCode::invalid_argument);
|
||||
@@ -720,6 +843,42 @@ void recording_renderer_rejects_invalid_command_order_and_targets(pp::tests::Har
|
||||
const auto capture_non_target = context.capture_frame(non_render_target, full_readback_buffer);
|
||||
PP_EXPECT(h, !capture_non_target.ok());
|
||||
PP_EXPECT(h, capture_non_target.code == StatusCode::invalid_argument);
|
||||
|
||||
const auto blit_non_target = context.blit_render_target(
|
||||
non_render_target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
BlitFilter::nearest);
|
||||
PP_EXPECT(h, !blit_non_target.ok());
|
||||
PP_EXPECT(h, blit_non_target.code == StatusCode::invalid_argument);
|
||||
|
||||
const auto blit_mismatched_format = context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
r8_target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
BlitFilter::nearest);
|
||||
PP_EXPECT(h, !blit_mismatched_format.ok());
|
||||
PP_EXPECT(h, blit_mismatched_format.code == StatusCode::invalid_argument);
|
||||
|
||||
const auto blit_outside_bounds = context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 31, .y = 15, .width = 2, .height = 1 },
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
BlitFilter::nearest);
|
||||
PP_EXPECT(h, !blit_outside_bounds.ok());
|
||||
PP_EXPECT(h, blit_outside_bounds.code == StatusCode::out_of_range);
|
||||
|
||||
const auto blit_bad_filter = context.blit_render_target(
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
target,
|
||||
ReadbackRegion { .x = 0, .y = 0, .width = 1, .height = 1 },
|
||||
static_cast<BlitFilter>(255));
|
||||
PP_EXPECT(h, !blit_bad_filter.ok());
|
||||
PP_EXPECT(h, blit_bad_filter.code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -732,6 +891,7 @@ int main()
|
||||
harness.run("validates_readback_bounds", validates_readback_bounds);
|
||||
harness.run("computes_readback_byte_sizes", computes_readback_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_viewports_and_mesh_descriptors", validates_viewports_and_mesh_descriptors);
|
||||
harness.run("validates_shader_program_descriptors", validates_shader_program_descriptors);
|
||||
harness.run("validates_panopainter_shader_catalog", validates_panopainter_shader_catalog);
|
||||
|
||||
@@ -2221,6 +2221,11 @@ int record_render(int argc, char** argv)
|
||||
.format = pp::renderer::TextureFormat::rgba8,
|
||||
.render_target = true,
|
||||
});
|
||||
pp::renderer::RecordingRenderTarget blit_target(pp::renderer::TextureDesc {
|
||||
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
|
||||
.format = pp::renderer::TextureFormat::rgba8,
|
||||
.render_target = true,
|
||||
});
|
||||
pp::renderer::RecordingReadbackBuffer readback_buffer(
|
||||
static_cast<std::uint64_t>(args.width) * args.height * 4U);
|
||||
const std::array<std::byte, 4> upload_pixel {
|
||||
@@ -2305,14 +2310,38 @@ int record_render(int argc, char** argv)
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto blit_status = context.blit_render_target(
|
||||
target,
|
||||
pp::renderer::ReadbackRegion {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = args.width,
|
||||
.height = args.height,
|
||||
},
|
||||
blit_target,
|
||||
pp::renderer::ReadbackRegion {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = args.width,
|
||||
.height = args.height,
|
||||
},
|
||||
pp::renderer::BlitFilter::nearest);
|
||||
if (!blit_status.ok()) {
|
||||
print_error("record-render", blit_status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::size_t draw_commands = 0;
|
||||
std::size_t upload_commands = 0;
|
||||
std::size_t readback_commands = 0;
|
||||
std::size_t capture_commands = 0;
|
||||
std::size_t blit_commands = 0;
|
||||
std::size_t trace_markers = 0;
|
||||
std::uint64_t upload_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;
|
||||
const auto commands = device.commands();
|
||||
for (const auto& command : commands) {
|
||||
if (command.kind == pp::renderer::RecordedRenderCommandKind::draw) {
|
||||
@@ -2326,6 +2355,10 @@ int record_render(int argc, char** argv)
|
||||
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::capture_frame) {
|
||||
++capture_commands;
|
||||
capture_bytes += command.capture_bytes;
|
||||
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::blit_render_target) {
|
||||
++blit_commands;
|
||||
blit_source_bytes += command.blit_source_bytes;
|
||||
blit_destination_bytes += command.blit_destination_bytes;
|
||||
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::trace_marker) {
|
||||
++trace_markers;
|
||||
}
|
||||
@@ -2344,6 +2377,9 @@ int record_render(int argc, char** argv)
|
||||
<< ",\"readbackBytes\":" << readback_bytes
|
||||
<< ",\"captureCommands\":" << capture_commands
|
||||
<< ",\"captureBytes\":" << capture_bytes
|
||||
<< ",\"blitCommands\":" << blit_commands
|
||||
<< ",\"blitSourceBytes\":" << blit_source_bytes
|
||||
<< ",\"blitDestinationBytes\":" << blit_destination_bytes
|
||||
<< ",\"traceMarkers\":" << trace_markers
|
||||
<< ",\"first\":\""
|
||||
<< pp::renderer::recorded_render_command_kind_name(commands.front().kind)
|
||||
|
||||
Reference in New Issue
Block a user