Expose recording renderer through pano cli
This commit is contained in:
@@ -244,6 +244,8 @@ Known local toolchain state:
|
|||||||
- `pp_renderer_api` exposes a headless `RecordingRenderDevice` that validates
|
- `pp_renderer_api` exposes a headless `RecordingRenderDevice` that validates
|
||||||
command order, records render commands, and records trace markers without a
|
command order, records render commands, and records trace markers without a
|
||||||
window or GL context.
|
window or GL context.
|
||||||
|
- `pano_cli record-render` exposes the recording renderer through JSON
|
||||||
|
automation and is covered by `pano_cli_record_render_smoke`.
|
||||||
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
||||||
through the vcpkg preset; default and Android validation still use the
|
through the vcpkg preset; default and Android validation still use the
|
||||||
retained vendored fallback tracked by DEBT-0012.
|
retained vendored fallback tracked by DEBT-0012.
|
||||||
|
|||||||
@@ -743,6 +743,9 @@ Results:
|
|||||||
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
|
- `pp_renderer_api` now includes a headless `RecordingRenderDevice` with strict
|
||||||
command-order validation and command/trace capture, giving automation a
|
command-order validation and command/trace capture, giving automation a
|
||||||
backend-neutral render path that does not require a window or GL context.
|
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, and trace/draw summary
|
||||||
|
for agent automation.
|
||||||
- PowerShell package-smoke wrapper validates the Windows CMake app executable
|
- PowerShell package-smoke wrapper validates the Windows CMake app executable
|
||||||
and runtime `data/` copy.
|
and runtime `data/` copy.
|
||||||
- Android arm64 configured with NDK 29.0.14206865 through the platform-build
|
- Android arm64 configured with NDK 29.0.14206865 through the platform-build
|
||||||
|
|||||||
@@ -279,6 +279,12 @@ if(TARGET pano_cli)
|
|||||||
set_tests_properties(pano_cli_parse_layout_smoke PROPERTIES
|
set_tests_properties(pano_cli_parse_layout_smoke PROPERTIES
|
||||||
LABELS "ui;integration;desktop-fast")
|
LABELS "ui;integration;desktop-fast")
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_record_render_smoke
|
||||||
|
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\":7.*\"drawCommands\":1")
|
||||||
|
|
||||||
add_test(NAME pano_cli_simulate_stroke_smoke
|
add_test(NAME pano_cli_simulate_stroke_smoke
|
||||||
COMMAND pano_cli simulate-stroke --x1 0 --y1 0 --x2 10 --y2 0 --spacing 2)
|
COMMAND pano_cli simulate-stroke --x1 0 --y1 0 --x2 10 --y2 0 --spacing 2)
|
||||||
set_tests_properties(pano_cli_simulate_stroke_smoke PROPERTIES
|
set_tests_properties(pano_cli_simulate_stroke_smoke PROPERTIES
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ target_link_libraries(pano_cli PRIVATE
|
|||||||
pp_foundation
|
pp_foundation
|
||||||
pp_assets
|
pp_assets
|
||||||
pp_document
|
pp_document
|
||||||
|
pp_renderer_api
|
||||||
pp_ui_core)
|
pp_ui_core)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "foundation/result.h"
|
#include "foundation/result.h"
|
||||||
#include "paint/stroke.h"
|
#include "paint/stroke.h"
|
||||||
#include "paint/stroke_script.h"
|
#include "paint/stroke_script.h"
|
||||||
|
#include "renderer_api/recording_renderer.h"
|
||||||
#include "ui_core/layout_xml.h"
|
#include "ui_core/layout_xml.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -52,6 +53,11 @@ struct SimulateStrokeScriptArgs {
|
|||||||
std::string path;
|
std::string path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RecordRenderArgs {
|
||||||
|
std::uint32_t width = 64;
|
||||||
|
std::uint32_t height = 32;
|
||||||
|
};
|
||||||
|
|
||||||
void print_error(std::string_view command, std::string_view message)
|
void print_error(std::string_view command, std::string_view message)
|
||||||
{
|
{
|
||||||
std::cout << "{\"ok\":false,\"command\":\"" << command
|
std::cout << "{\"ok\":false,\"command\":\"" << command
|
||||||
@@ -109,6 +115,7 @@ void print_help()
|
|||||||
<< " inspect-project --path FILE\n"
|
<< " inspect-project --path FILE\n"
|
||||||
<< " load-project --path FILE\n"
|
<< " load-project --path FILE\n"
|
||||||
<< " parse-layout --path FILE\n"
|
<< " parse-layout --path FILE\n"
|
||||||
|
<< " record-render [--width N] [--height N]\n"
|
||||||
<< " simulate-stroke --x1 N --y1 N --x2 N --y2 N [--spacing N]\n"
|
<< " simulate-stroke --x1 N --y1 N --x2 N --y2 N [--spacing N]\n"
|
||||||
<< " simulate-stroke-script --path FILE\n"
|
<< " simulate-stroke-script --path FILE\n"
|
||||||
<< " --help\n";
|
<< " --help\n";
|
||||||
@@ -624,6 +631,121 @@ int simulate_stroke_script(int argc, char** argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status parse_record_render_args(int argc, char** argv, RecordRenderArgs& args)
|
||||||
|
{
|
||||||
|
for (int i = 2; i < argc; ++i) {
|
||||||
|
const std::string_view key(argv[i]);
|
||||||
|
if (key == "--width" || key == "--height") {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto value = pp::foundation::parse_u32(argv[++i]);
|
||||||
|
if (!value) {
|
||||||
|
return value.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == "--width") {
|
||||||
|
args.width = value.value();
|
||||||
|
} else {
|
||||||
|
args.height = value.value();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return pp::foundation::Status::invalid_argument("unknown option");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.width == 0 || args.height == 0) {
|
||||||
|
return pp::foundation::Status::invalid_argument("width and height must be greater than zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int record_render(int argc, char** argv)
|
||||||
|
{
|
||||||
|
RecordRenderArgs args;
|
||||||
|
const auto status = parse_record_render_args(argc, argv, args);
|
||||||
|
if (!status.ok()) {
|
||||||
|
print_error("record-render", status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::renderer::RecordingRenderDevice device;
|
||||||
|
pp::renderer::RecordingRenderTarget target(pp::renderer::TextureDesc {
|
||||||
|
.extent = pp::renderer::Extent2D { .width = args.width, .height = args.height },
|
||||||
|
.format = pp::renderer::TextureFormat::rgba8,
|
||||||
|
.render_target = true,
|
||||||
|
});
|
||||||
|
pp::renderer::RecordingShaderProgram shader("pano-cli-record-render");
|
||||||
|
pp::renderer::RecordingMesh mesh(pp::renderer::MeshDesc {
|
||||||
|
.vertex_count = 3,
|
||||||
|
.index_count = 0,
|
||||||
|
.topology = pp::renderer::PrimitiveTopology::triangles,
|
||||||
|
});
|
||||||
|
|
||||||
|
device.trace()->marker("renderer", "pano_cli_record_render");
|
||||||
|
auto& context = device.immediate_context();
|
||||||
|
const auto begin_status = context.begin_render_pass(
|
||||||
|
target,
|
||||||
|
pp::renderer::ClearColor { .r = 0.0F, .g = 0.0F, .b = 0.0F, .a = 1.0F });
|
||||||
|
if (!begin_status.ok()) {
|
||||||
|
print_error("record-render", begin_status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto viewport_status = context.set_viewport(
|
||||||
|
pp::renderer::Viewport { .x = 0, .y = 0, .width = args.width, .height = args.height });
|
||||||
|
if (!viewport_status.ok()) {
|
||||||
|
print_error("record-render", viewport_status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto shader_status = context.bind_shader(shader);
|
||||||
|
const auto mesh_status = context.bind_mesh(mesh);
|
||||||
|
const auto draw_status = context.draw();
|
||||||
|
context.end_render_pass();
|
||||||
|
|
||||||
|
if (!shader_status.ok()) {
|
||||||
|
print_error("record-render", shader_status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (!mesh_status.ok()) {
|
||||||
|
print_error("record-render", mesh_status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (!draw_status.ok()) {
|
||||||
|
print_error("record-render", draw_status.message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t draw_commands = 0;
|
||||||
|
std::size_t trace_markers = 0;
|
||||||
|
const auto commands = device.commands();
|
||||||
|
for (const auto& command : commands) {
|
||||||
|
if (command.kind == pp::renderer::RecordedRenderCommandKind::draw) {
|
||||||
|
++draw_commands;
|
||||||
|
} else if (command.kind == pp::renderer::RecordedRenderCommandKind::trace_marker) {
|
||||||
|
++trace_markers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "{\"ok\":true,\"command\":\"record-render\""
|
||||||
|
<< ",\"backend\":\"" << device.backend_name() << "\""
|
||||||
|
<< ",\"target\":{\"width\":" << args.width
|
||||||
|
<< ",\"height\":" << args.height
|
||||||
|
<< ",\"format\":\"rgba8\"}"
|
||||||
|
<< ",\"commands\":" << commands.size()
|
||||||
|
<< ",\"drawCommands\":" << draw_commands
|
||||||
|
<< ",\"traceMarkers\":" << trace_markers
|
||||||
|
<< ",\"first\":\""
|
||||||
|
<< pp::renderer::recorded_render_command_kind_name(commands.front().kind)
|
||||||
|
<< "\",\"last\":\""
|
||||||
|
<< pp::renderer::recorded_render_command_kind_name(commands.back().kind)
|
||||||
|
<< "\"}\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pp::foundation::Status parse_layout_args(int argc, char** argv, ParseLayoutArgs& args)
|
pp::foundation::Status parse_layout_args(int argc, char** argv, ParseLayoutArgs& args)
|
||||||
{
|
{
|
||||||
for (int i = 2; i < argc; ++i) {
|
for (int i = 2; i < argc; ++i) {
|
||||||
@@ -721,6 +843,10 @@ int main(int argc, char** argv)
|
|||||||
return parse_layout(argc, argv);
|
return parse_layout(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command == "record-render") {
|
||||||
|
return record_render(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
print_error(command, "unknown command");
|
print_error(command, "unknown command");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user