Share recorded document upload reporting

This commit is contained in:
2026-06-05 18:46:15 +02:00
parent 81898a5dcc
commit 693923b7bd
8 changed files with 141 additions and 63 deletions

View File

@@ -252,7 +252,8 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
rectangles into full renderer-sized RGBA buffers with layer visibility, rectangles into full renderer-sized RGBA buffers with layer visibility,
opacity, and blend mode applied in document order, then uploading those six opacity, and blend mode applied in document order, then uploading those six
faces through the renderer-neutral `IRenderDevice` texture API using the faces through the renderer-neutral `IRenderDevice` texture API using the
recording backend. recording backend. It also covers the shared recorded-upload report helper
consumed by CLI and live export-readiness bridges.
- `pano_cli simulate-document-export` exposes the same pure document-to-PPI - `pano_cli simulate-document-export` exposes the same pure document-to-PPI
export, asset-level decode, and document reimport path through JSON export, asset-level decode, and document reimport path through JSON
automation and is covered by `pano_cli_simulate_document_export_smoke`. automation and is covered by `pano_cli_simulate_document_export_smoke`.
@@ -270,16 +271,16 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
exports through the pure `pp_document` PPI writer and reports generated byte exports through the pure `pp_document` PPI writer and reports generated byte
counts plus decoded dirty-face counts in `ppiExport` JSON. Payload-complete counts plus decoded dirty-face counts in `ppiExport` JSON. Payload-complete
snapshots also feed the active frame through the `pp_paint_renderer` snapshots also feed the active frame through the `pp_paint_renderer`
document-frame compositor and renderer-neutral recording upload path, document-frame compositor and renderer-neutral recorded-upload report helper,
reporting texture, transition, command, byte, and active-frame payload counts reporting texture, transition, command, byte, and active-frame payload counts
in `rendererUpload` JSON. It is covered by in `rendererUpload` JSON. It is covered by
`pano_cli_plan_canvas_document_snapshot_smoke` plus the payload-bearing `pano_cli_plan_canvas_document_snapshot_smoke` plus the payload-bearing
snapshot smoke. snapshot smoke.
- Live equirectangular, layer, animation-frame, and cube-face export adapters - Live equirectangular, layer, animation-frame, and cube-face export adapters
now prepare and log the same payload-bearing canvas document snapshot plus now prepare and log the same payload-bearing canvas document snapshot plus
renderer-neutral active-frame upload report before delegating to retained shared renderer-neutral active-frame upload report before delegating to
`Canvas` export execution. Depth and video export remain on the older retained retained `Canvas` export execution. Depth and video export remain on the older
path. retained path.
- `pano_cli save-document-project` writes that pure document export to a PPI - `pano_cli save-document-project` writes that pure document export to a PPI
file and is covered by `pano_cli_save_document_project_roundtrip_smoke`, file and is covered by `pano_cli_save_document_project_roundtrip_smoke`,
which inspects and loads the generated file. which inspects and loads the generated file.

File diff suppressed because one or more lines are too long

View File

@@ -677,13 +677,14 @@ also has a tested pure PPI export helper, and
snapshots and reports generated byte/dirty-face summaries. The same automation snapshots and reports generated byte/dirty-face summaries. The same automation
now feeds payload-complete snapshots through the `pp_paint_renderer` now feeds payload-complete snapshots through the `pp_paint_renderer`
document-frame compositor and renderer-neutral recording upload path, reporting document-frame compositor and renderer-neutral recording upload path, reporting
texture, transition, byte, payload, and command counts. Live save writer texture, transition, byte, payload, and command counts through the shared
replacement, export adoption, and renderer-owned readback remain under `record_document_frame_upload` report helper. Live save writer replacement,
export adoption, and renderer-owned readback remain under
`DEBT-0010`/`DEBT-0013`/`DEBT-0036`. `DEBT-0010`/`DEBT-0013`/`DEBT-0036`.
Live equirectangular, layer, animation-frame, and cube-face export adapters now Live equirectangular, layer, animation-frame, and cube-face export adapters now
prepare the same payload-bearing document snapshot and renderer-neutral upload prepare the same payload-bearing document snapshot and renderer-neutral upload
report before delegating to retained `Canvas` export execution, so export report helper before delegating to retained `Canvas` export execution, so export
workflows consume the boundary without changing file output yet. workflows consume the shared renderer boundary without changing file output yet.
`pano_cli plan-image-import` exposes app-core planning for File > Import image `pano_cli plan-image-import` exposes app-core planning for File > Import image
route decisions, including wide equirectangular images, legacy vertical cube route decisions, including wide equirectangular images, legacy vertical cube
strips, regular transform-placement images, and invalid image dimensions; live strips, regular transform-placement images, and invalid image dimensions; live
@@ -2515,12 +2516,13 @@ Results:
- Payload-complete canvas snapshot automation also crosses the renderer - Payload-complete canvas snapshot automation also crosses the renderer
boundary now: `pano_cli plan-canvas-document-snapshot` records the same boundary now: `pano_cli plan-canvas-document-snapshot` records the same
snapshot through the pure document-frame compositor and renderer-neutral snapshot through the pure document-frame compositor and renderer-neutral
texture upload stream, so agents can validate document/canvas payloads moving texture upload stream through `pp_paint_renderer::record_document_frame_upload`,
into renderer commands before live canvas export/save writer replacement. so agents can validate document/canvas payloads moving into renderer commands
before live canvas export/save writer replacement.
- Live image/collection/cube export adapters now prepare and log the same - Live image/collection/cube export adapters now prepare and log the same
document/canvas plus renderer-upload readiness before retained `Canvas` document/canvas plus shared renderer-upload readiness report before retained
export calls. Depth and video export remain on their prior retained paths; `Canvas` export calls. Depth and video export remain on their prior retained
actual image/cube writer replacement remains tracked under export debt. paths; actual image/cube writer replacement remains tracked under export debt.
- Snapshot creation now rejects invalid embedded RGBA8 face payloads before - Snapshot creation now rejects invalid embedded RGBA8 face payloads before
document export or history can persist malformed state. document export or history can persist malformed state.
- Package-smoke wrappers validate the Windows CMake app executable/runtime - Package-smoke wrappers validate the Windows CMake app executable/runtime

View File

@@ -5,7 +5,6 @@
#include "app.h" #include "app.h"
#include "legacy_document_canvas_services.h" #include "legacy_document_canvas_services.h"
#include "paint_renderer/compositor.h" #include "paint_renderer/compositor.h"
#include "renderer_api/recording_renderer.h"
#include <string> #include <string>
#include <thread> #include <thread>
@@ -46,41 +45,28 @@ pp::foundation::Status prepare_legacy_document_export_snapshot(App& app, const c
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }
pp::renderer::RecordingRenderDevice render_device; const auto recorded_upload = pp::paint_renderer::record_document_frame_upload(
const auto uploaded = pp::paint_renderer::upload_document_frame_faces(
render_device,
pp::paint_renderer::DocumentFrameUploadRequest { pp::paint_renderer::DocumentFrameUploadRequest {
.document = &snapshot.value().document, .document = &snapshot.value().document,
.frame_index = snapshot.value().document.active_frame_index(), .frame_index = snapshot.value().document.active_frame_index(),
.clear_color = {}, .clear_color = {},
}); });
if (!uploaded) { if (!recorded_upload) {
LOG("%s document export renderer upload failed: %s", context, uploaded.status().message); LOG("%s document export renderer upload failed: %s", context, recorded_upload.status().message);
return uploaded.status(); return recorded_upload.status();
}
std::size_t upload_commands = 0;
std::size_t transition_commands = 0;
const auto commands = render_device.commands();
for (const auto& command : commands) {
if (command.kind == pp::renderer::RecordedRenderCommandKind::upload_texture) {
++upload_commands;
}
if (command.kind == pp::renderer::RecordedRenderCommandKind::transition_texture) {
++transition_commands;
}
} }
const auto& uploaded = recorded_upload.value().upload;
LOG( LOG(
"%s document export renderer upload: textures=%zu bytes=%llu transitions=%zu facePayloads=%zu commands=%zu uploadCommands=%zu transitionCommands=%zu", "%s document export renderer upload: textures=%zu bytes=%llu transitions=%zu facePayloads=%zu commands=%zu uploadCommands=%zu transitionCommands=%zu",
context, context,
uploaded.value().texture_count, uploaded.texture_count,
static_cast<unsigned long long>(uploaded.value().uploaded_bytes), static_cast<unsigned long long>(uploaded.uploaded_bytes),
uploaded.value().transition_count, uploaded.transition_count,
uploaded.value().composite.face_payload_count, uploaded.composite.face_payload_count,
commands.size(), recorded_upload.value().command_count,
upload_commands, recorded_upload.value().upload_command_count,
transition_commands); recorded_upload.value().transition_command_count);
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }

View File

@@ -1,5 +1,7 @@
#include "paint_renderer/compositor.h" #include "paint_renderer/compositor.h"
#include "renderer_api/recording_renderer.h"
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <utility> #include <utility>
@@ -412,6 +414,31 @@ pp::foundation::Result<DocumentFrameUploadResult> upload_document_frame_faces(
return pp::foundation::Result<DocumentFrameUploadResult>::success(std::move(result)); return pp::foundation::Result<DocumentFrameUploadResult>::success(std::move(result));
} }
pp::foundation::Result<RecordedDocumentFrameUploadResult> record_document_frame_upload(
DocumentFrameUploadRequest request)
{
pp::renderer::RecordingRenderDevice render_device;
auto uploaded = upload_document_frame_faces(render_device, request);
if (!uploaded) {
return pp::foundation::Result<RecordedDocumentFrameUploadResult>::failure(uploaded.status());
}
RecordedDocumentFrameUploadResult result;
result.upload = std::move(uploaded.value());
const auto commands = render_device.commands();
result.command_count = commands.size();
for (const auto& command : commands) {
if (command.kind == pp::renderer::RecordedRenderCommandKind::upload_texture) {
++result.upload_command_count;
}
if (command.kind == pp::renderer::RecordedRenderCommandKind::transition_texture) {
++result.transition_command_count;
}
}
return pp::foundation::Result<RecordedDocumentFrameUploadResult>::success(std::move(result));
}
bool stroke_composite_requires_feedback( bool stroke_composite_requires_feedback(
pp::paint::BlendMode layer_blend_mode, pp::paint::BlendMode layer_blend_mode,
pp::paint::StrokeBlendMode stroke_blend_mode, pp::paint::StrokeBlendMode stroke_blend_mode,

View File

@@ -132,6 +132,13 @@ struct DocumentFrameUploadResult {
std::uint64_t uploaded_bytes = 0; std::uint64_t uploaded_bytes = 0;
}; };
struct RecordedDocumentFrameUploadResult {
DocumentFrameUploadResult upload {};
std::size_t command_count = 0;
std::size_t upload_command_count = 0;
std::size_t transition_command_count = 0;
};
[[nodiscard]] pp::foundation::Status composite_layer( [[nodiscard]] pp::foundation::Status composite_layer(
std::span<pp::paint::Rgba> destination, std::span<pp::paint::Rgba> destination,
pp::renderer::Extent2D extent, pp::renderer::Extent2D extent,
@@ -147,6 +154,9 @@ struct DocumentFrameUploadResult {
pp::renderer::IRenderDevice& device, pp::renderer::IRenderDevice& device,
DocumentFrameUploadRequest request); DocumentFrameUploadRequest request);
[[nodiscard]] pp::foundation::Result<RecordedDocumentFrameUploadResult> record_document_frame_upload(
DocumentFrameUploadRequest request);
[[nodiscard]] bool stroke_composite_requires_feedback( [[nodiscard]] bool stroke_composite_requires_feedback(
pp::paint::BlendMode layer_blend_mode, pp::paint::BlendMode layer_blend_mode,
pp::paint::StrokeBlendMode stroke_blend_mode, pp::paint::StrokeBlendMode stroke_blend_mode,

View File

@@ -541,6 +541,62 @@ void uploads_document_frame_faces_to_renderer_api(pp::tests::Harness& h)
PP_EXPECT(h, transition_count == pp::document::cube_face_count); PP_EXPECT(h, transition_count == pp::document::cube_face_count);
} }
void records_document_frame_upload_report(pp::tests::Harness& h)
{
const AnimationFrame root_frames[] {
{ .duration_ms = 100, .face_pixels = {} },
};
const AnimationFrame layer_frames[] {
{
.duration_ms = 100,
.face_pixels = {
LayerFacePixels {
.face_index = 1,
.x = 0,
.y = 0,
.width = 1,
.height = 1,
.rgba8 = { 0, 0, 255, 255 },
},
},
},
};
const DocumentLayerConfig layers[] {
{
.name = "Paint",
.frames = std::span<const AnimationFrame>(layer_frames, 1),
},
};
const auto document = CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
.width = 1,
.height = 1,
.layers = std::span<const DocumentLayerConfig>(layers, 1),
.frames = std::span<const AnimationFrame>(root_frames, 1),
.selection_masks = {},
});
PP_EXPECT(h, document);
if (!document) {
return;
}
const auto report = pp::paint_renderer::record_document_frame_upload(
pp::paint_renderer::DocumentFrameUploadRequest {
.document = &document.value(),
.frame_index = 0,
});
PP_EXPECT(h, report);
if (report) {
PP_EXPECT(h, report.value().upload.texture_count == pp::document::cube_face_count);
PP_EXPECT(h, report.value().upload.transition_count == pp::document::cube_face_count);
PP_EXPECT(h, report.value().upload.uploaded_bytes == 24U);
PP_EXPECT(h, report.value().upload.composite.face_payload_count == 1U);
PP_EXPECT(h, report.value().command_count == 12U);
PP_EXPECT(h, report.value().upload_command_count == pp::document::cube_face_count);
PP_EXPECT(h, report.value().transition_command_count == pp::document::cube_face_count);
}
}
void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h) void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
{ {
RecordingRenderDevice device; RecordingRenderDevice device;
@@ -922,6 +978,7 @@ int main()
harness.run("composites_document_frame_cube_faces", composites_document_frame_cube_faces); harness.run("composites_document_frame_cube_faces", composites_document_frame_cube_faces);
harness.run("document_frame_composite_rejects_invalid_requests", document_frame_composite_rejects_invalid_requests); harness.run("document_frame_composite_rejects_invalid_requests", document_frame_composite_rejects_invalid_requests);
harness.run("uploads_document_frame_faces_to_renderer_api", uploads_document_frame_faces_to_renderer_api); harness.run("uploads_document_frame_faces_to_renderer_api", uploads_document_frame_faces_to_renderer_api);
harness.run("records_document_frame_upload_report", records_document_frame_upload_report);
harness.run("document_frame_upload_rejects_invalid_requests", document_frame_upload_rejects_invalid_requests); harness.run("document_frame_upload_rejects_invalid_requests", document_frame_upload_rejects_invalid_requests);
harness.run("detects_feedback_requirements", detects_feedback_requirements); harness.run("detects_feedback_requirements", detects_feedback_requirements);
harness.run("plans_stroke_composite_paths", plans_stroke_composite_paths); harness.run("plans_stroke_composite_paths", plans_stroke_composite_paths);

View File

@@ -6024,36 +6024,26 @@ int plan_canvas_document_snapshot(int argc, char** argv)
ppi_export_bytes = exported.value().bytes.size(); ppi_export_bytes = exported.value().bytes.size();
ppi_export_dirty_faces = decoded.value().project.body.summary.dirty_face_count; ppi_export_dirty_faces = decoded.value().project.body.summary.dirty_face_count;
constexpr pp::paint::Rgba clear_color {}; const auto recorded_upload = pp::paint_renderer::record_document_frame_upload(
pp::renderer::RecordingRenderDevice render_device;
const auto uploaded = pp::paint_renderer::upload_document_frame_faces(
render_device,
pp::paint_renderer::DocumentFrameUploadRequest { pp::paint_renderer::DocumentFrameUploadRequest {
.document = &document, .document = &document,
.frame_index = document.active_frame_index(), .frame_index = document.active_frame_index(),
.clear_color = clear_color, .clear_color = {},
}); });
if (!uploaded) { if (!recorded_upload) {
print_error("plan-canvas-document-snapshot", uploaded.status().message); print_error("plan-canvas-document-snapshot", recorded_upload.status().message);
return 2; return 2;
} }
renderer_upload_ready = true; renderer_upload_ready = true;
renderer_texture_count = uploaded.value().texture_count; renderer_texture_count = recorded_upload.value().upload.texture_count;
renderer_transition_count = uploaded.value().transition_count; renderer_transition_count = recorded_upload.value().upload.transition_count;
renderer_uploaded_bytes = uploaded.value().uploaded_bytes; renderer_uploaded_bytes = recorded_upload.value().upload.uploaded_bytes;
renderer_face_payloads = uploaded.value().composite.face_payload_count; renderer_face_payloads = recorded_upload.value().upload.composite.face_payload_count;
renderer_composited_layer_faces = uploaded.value().composite.composited_layer_face_count; renderer_composited_layer_faces = recorded_upload.value().upload.composite.composited_layer_face_count;
const auto commands = render_device.commands(); renderer_command_count = recorded_upload.value().command_count;
renderer_command_count = commands.size(); renderer_upload_command_count = recorded_upload.value().upload_command_count;
for (const auto& command : commands) { renderer_transition_command_count = recorded_upload.value().transition_command_count;
if (command.kind == pp::renderer::RecordedRenderCommandKind::upload_texture) {
++renderer_upload_command_count;
}
if (command.kind == pp::renderer::RecordedRenderCommandKind::transition_texture) {
++renderer_transition_command_count;
}
}
} }
std::cout << "{\"ok\":true,\"command\":\"plan-canvas-document-snapshot\"" std::cout << "{\"ok\":true,\"command\":\"plan-canvas-document-snapshot\""