trace_processor: Route hw_queue GpuUserAnnotationEvent to the queue track When a GpuUserAnnotationEvent carries a hw_queue_iid, intern it onto that hardware queue's render stage track (reusing the render stage blueprint) so it appears alongside the events on the queue. Without a resolvable specification it falls back to the stream-scoped gpu_user_annotation track.
diff --git a/src/trace_processor/importers/proto/gpu_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc index 8e721a9..5f339c7 100644 --- a/src/trace_processor/importers/proto/gpu_event_parser.cc +++ b/src/trace_processor/importers/proto/gpu_event_parser.cc
@@ -1029,13 +1029,35 @@ } auto ugpu = context_->gpu_tracker->GetOrCreateGpu(gpu_id); - TrackId track_id = context_->track_compressor->InternScoped( - kUserAnnotationBlueprint, tracks::Dimensions(ugpu.value, gpu_id, upid), - ts, static_cast<int64_t>(event.duration()), tracks::DynamicName(name_id), - [](ArgsTracker::BoundInserter&) {}); + std::optional<TrackId> track_id; + if (event.has_hw_queue_iid()) { + // With a hardware queue, place the annotation on that queue's render stage + // track, next to the events on the queue. + auto* spec = sequence_state->LookupInternedMessage< + protos::pbzero::InternedData::kGpuSpecificationsFieldNumber, + protos::pbzero::InternedGpuRenderStageSpecification>( + event.hw_queue_iid()); + if (spec) { + StringId hwqueue_name = context_->storage->InternString(spec->name()); + track_id = context_->track_compressor->InternScoped( + kRenderStageBlueprint, + tracks::Dimensions(ugpu.value, gpu_id, base::StringView("iid"), + static_cast<uint32_t>(event.hw_queue_iid()), + hwqueue_name), + ts, static_cast<int64_t>(event.duration()), + tracks::DynamicName(hwqueue_name), + [](ArgsTracker::BoundInserter&) {}); + } + } + if (!track_id) { + track_id = context_->track_compressor->InternScoped( + kUserAnnotationBlueprint, tracks::Dimensions(ugpu.value, gpu_id, upid), + ts, static_cast<int64_t>(event.duration()), + tracks::DynamicName(name_id), [](ArgsTracker::BoundInserter&) {}); + } auto opt_slice_id = context_->slice_tracker->Scoped( - ts, track_id, kNullStringId, name_id, + ts, *track_id, kNullStringId, name_id, static_cast<int64_t>(event.duration()), [&](ArgsTracker::BoundInserter* inserter) { inserter->AddArg(context_id_id_,
diff --git a/test/trace_processor/diff_tests/parser/graphics/tests_gpu_trace.py b/test/trace_processor/diff_tests/parser/graphics/tests_gpu_trace.py index dbc1029..980e56b 100644 --- a/test/trace_processor/diff_tests/parser/graphics/tests_gpu_trace.py +++ b/test/trace_processor/diff_tests/parser/graphics/tests_gpu_trace.py
@@ -416,6 +416,61 @@ "HostSubmit","annotation1" """)) + def test_gpu_user_annotation_hw_queue(self): + return DiffTestBlueprint( + trace=TextProto(r""" + packet { + timestamp: 0 + incremental_state_cleared: true + trusted_packet_sequence_id: 1 + interned_data { + graphics_contexts { iid: 1 pid: 100 api: CUDA } + gpu_specifications { iid: 1 name: "queue0" } + } + } + packet { + timestamp: 1000 + trusted_packet_sequence_id: 1 + gpu_render_stage_event { + event_id: 1 + duration: 100 + hw_queue_iid: 1 + gpu_id: 0 + context: 1 + name: "kernelA" + } + } + packet { + timestamp: 1200 + trusted_packet_sequence_id: 1 + gpu_user_annotation_event { + name: "annotation1" + duration: 800 + hw_queue_iid: 1 + gpu_id: 0 + context: 1 + args { name: "device" int_value: 0 } + args { name: "stream" uint_value: 7 } + } + } + """), + query=""" + SELECT + COUNT(DISTINCT s.track_id) AS distinct_tracks, + SUM(t.type = 'gpu_render_stage') AS render_stage_slices, + MAX(CASE + WHEN s.name = 'annotation1' + THEN extract_arg(s.arg_set_id, 'stream') + END) AS annotation_stream + FROM gpu_slice s + JOIN track t ON t.id = s.track_id + WHERE s.name IN ('kernelA', 'annotation1'); + """, + out=Csv(""" + "distinct_tracks","render_stage_slices","annotation_stream" + 1,2,7 + """)) + def test_vulkan_api_events(self): return DiffTestBlueprint( trace=Path('vulkan_api_events.py'),