#include "render_3D/Point_Scene.h" #include #include #include #include #include #include #include #include #include namespace renderive::render_3d { namespace { struct Test_Point_Scene { std::shared_ptr visual; std::unique_ptr scene; }; Test_Point_Scene make_test_scene(Scene_Options options) { std::vector points{ {{-0.72F, -0.38F, 0.15F}, {245, 70, 78, 255}, 42.0F}, {{0.00F, 0.48F, -0.05F}, {65, 220, 132, 255}, 48.0F}, {{0.72F, 0.0F, 0.30F}, {75, 135, 255, 255}, 54.0F}, {{-0.35F, 0.10F, -0.45F}, {250, 205, 75, 255}, 30.0F}, {{0.38F, -0.12F, -0.25F}, {205, 95, 245, 255}, 34.0F}, }; Point_State state; state.style = {{12, 16, 24, 255}, 2.0F, Point_Aspect::Outline}; auto visual = Point_Visual::Builder{state}.build(std::move(points)); auto scene = std::make_unique(options, visual); return {std::move(visual), std::move(scene)}; } struct Event_Point { float x{}; float y{}; }; std::size_t colored_pixel_count(const Pixel_Frame& frame) { if (frame.rgba8.size() < 4) return 0; const auto* bytes = reinterpret_cast(frame.rgba8.data()); const std::array background{ bytes[0], bytes[1], bytes[2], bytes[3]}; std::size_t result{}; for (std::size_t offset = 0; offset + 3 < frame.rgba8.size(); offset += 4) { result += bytes[offset] != background[0] || bytes[offset + 1] != background[1] || bytes[offset + 2] != background[2] || bytes[offset + 3] != background[3]; } return result; } TEST(PointRenderIntegration, RendersRealRgbaAndResizes) { try { auto demo = make_test_scene(Scene_Options{.viewport = {320, 200}}); ASSERT_TRUE(demo.scene->request_frame()); auto first = demo.scene->latest_frame(); ASSERT_NE(first, nullptr); EXPECT_EQ(first->extent, (Extent{320, 200})); EXPECT_EQ(first->rgba8.size(), 320U * 200U * 4U); EXPECT_GT(colored_pixel_count(*first), 100U); demo.scene->resize({480, 270}); ASSERT_TRUE(demo.scene->request_frame()); auto resized = demo.scene->latest_frame(); ASSERT_NE(resized, nullptr); EXPECT_EQ(resized->extent, (Extent{480, 270})); EXPECT_EQ(resized->rgba8.size(), 480U * 270U * 4U); EXPECT_GT(colored_pixel_count(*resized), 100U); } catch (const std::exception& error) { GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); } } TEST(PointRenderIntegration, KernelEventsReachDatovizArcballOnItsDomain) { try { auto demo = make_test_scene(Scene_Options{.viewport = {320, 200}}); ASSERT_TRUE(demo.scene->request_frame()); const auto before = demo.scene->latest_frame()->rgba8; ::renderive::Basic_Pointer_Event press( ::renderive::Event_Type::Pointer_Press); press.position = {120.0F, 90.0F}; press.button = ::renderive::Mouse_Button::Left; demo.scene->dispatch(press); ::renderive::Basic_Pointer_Event move( ::renderive::Event_Type::Pointer_Move); move.position = {210.0F, 125.0F}; move.buttons = 1; demo.scene->dispatch(move); move.position = {245.0F, 145.0F}; demo.scene->dispatch(move); ::renderive::Basic_Pointer_Event release( ::renderive::Event_Type::Pointer_Release); release.position = move.position; release.button = ::renderive::Mouse_Button::Left; demo.scene->dispatch(release); ASSERT_TRUE(demo.scene->request_frame()); const auto after_drag = demo.scene->latest_frame(); ASSERT_NE(after_drag, nullptr); EXPECT_NE(after_drag->rgba8, before); ::renderive::Basic_Wheel_Event wheel; wheel.position = {160.0F, 100.0F}; wheel.pixel_delta_y = 180.0; wheel.angle_delta_y = -216.0; demo.scene->dispatch(wheel); ASSERT_TRUE(demo.scene->request_frame()); const auto after_wheel = demo.scene->latest_frame(); ASSERT_NE(after_wheel, nullptr); EXPECT_GT(colored_pixel_count(*after_wheel), 100U); EXPECT_NE(after_wheel->rgba8, after_drag->rgba8); } catch (const std::exception& error) { GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); } } TEST(PointRenderIntegration, CapturesExternalGpuLifetimeAndSeparatedBackendPhases) { try { auto demo = make_test_scene(Scene_Options{.viewport = {320, 200}}); auto& kernel_scene = demo.scene->render_scene(); const auto request = kernel_scene.capture_next_frame(); ASSERT_TRUE(request); const Capture_Session_Id capture = request.session_id; ASSERT_TRUE(demo.scene->request_frame()); const auto session = kernel_scene.capture_session(capture); ASSERT_TRUE(session); ASSERT_EQ(session->captured_count(), 1U); const auto& snapshot = *session->frames.front().snapshot; const auto plan = kernel_scene.find_render_plan( snapshot.render_plan_version); ASSERT_TRUE(plan); const auto render_node = std::find_if( plan->graph.nodes.begin(), plan->graph.nodes.end(), [](const Render_Node& node) { return node.kind == Render_Node_Kind::render; }); ASSERT_NE(render_node, plan->graph.nodes.end()); const auto& execution = snapshot.node_executions.at( render_node->execution_index); EXPECT_EQ(execution.status, Node_Execution_Status::complete); EXPECT_GT(execution.external_start_time_ns, 0U); EXPECT_GE(execution.external_end_time_ns, execution.external_start_time_ns); EXPECT_GE(execution.duration_ns(), execution.cpu_duration_ns()); EXPECT_TRUE(execution.metrics.contains( Node_Metric_Kind::queue_wait_ns)); EXPECT_TRUE(execution.metrics.contains( Node_Metric_Kind::backend_execute_duration_ns)); EXPECT_TRUE(execution.metrics.contains( Node_Metric_Kind::gpu_fence_wait_ns)); EXPECT_TRUE(execution.metrics.contains( Node_Metric_Kind::readback_duration_ns)); const auto artifact_bytes = execution.metrics.get( Node_Metric_Kind::backend_artifact_json_bytes); EXPECT_GT(artifact_bytes, 0U); const auto artifact = std::find_if( execution.attachments.begin(), execution.attachments.end(), [](const Node_Diagnostic_Attachment& attachment) { return attachment.type == "datoviz.drp2.artifact.json"; }); ASSERT_NE(artifact, execution.attachments.end()); EXPECT_EQ(artifact->content.size(), artifact_bytes); EXPECT_FALSE(artifact->content.empty()); const bool gpu_timing = execution.metrics.contains( Node_Metric_Kind::gpu_render_duration_ns); EXPECT_EQ(gpu_timing, execution.metrics.contains( Node_Metric_Kind::gpu_transition_duration_ns)); EXPECT_EQ(gpu_timing, execution.metrics.contains( Node_Metric_Kind::gpu_copy_duration_ns)); EXPECT_EQ(gpu_timing, execution.metrics.contains( Node_Metric_Kind::gpu_total_duration_ns)); if (gpu_timing) { const auto total = execution.metrics.get( Node_Metric_Kind::gpu_total_duration_ns); EXPECT_GE(total, execution.metrics.get( Node_Metric_Kind::gpu_render_duration_ns)); EXPECT_GE(total, execution.metrics.get( Node_Metric_Kind::gpu_transition_duration_ns)); EXPECT_GE(total, execution.metrics.get( Node_Metric_Kind::gpu_copy_duration_ns)); } } catch (const std::exception& error) { GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what(); } } } // namespace } // namespace renderive::render_3d