1 /* 2 * Copyright © 2023 Google LLC 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 #include "perfetto.h" 25 26 #include "util/hash_table.h" 27 #include "util/perf/u_trace.h" 28 #include "util/ralloc.h" 29 30 using perfetto::DataSource; 31 template <typename DataSourceType, typename DataSourceTraits> 32 class MesaRenderpassDataSource 33 : public perfetto::DataSource<DataSourceType, DataSourceTraits> { 34 35 public: 36 typedef typename perfetto::DataSource<DataSourceType, 37 DataSourceTraits>::TraceContext 38 TraceContext; 39 OnSetup(const perfetto::DataSourceBase::SetupArgs &)40 void OnSetup(const perfetto::DataSourceBase::SetupArgs &) override 41 { 42 // Use this callback to apply any custom configuration to your data 43 // source based on the TraceConfig in SetupArgs. 44 debug_markers = NULL; 45 } 46 OnStart(const perfetto::DataSourceBase::StartArgs &)47 void OnStart(const perfetto::DataSourceBase::StartArgs &) override 48 { 49 debug_markers = _mesa_hash_table_create(NULL, _mesa_hash_string, 50 _mesa_key_string_equal); 51 // This notification can be used to initialize the GPU driver, enable 52 // counters, etc. StartArgs will contains the DataSourceDescriptor, 53 // which can be extended. 54 u_trace_perfetto_start(); 55 PERFETTO_LOG("Tracing started"); 56 } 57 OnStop(const perfetto::DataSourceBase::StopArgs &)58 void OnStop(const perfetto::DataSourceBase::StopArgs &) override 59 { 60 PERFETTO_LOG("Tracing stopped"); 61 62 // Undo any initialization done in OnStart. 63 u_trace_perfetto_stop(); 64 // TODO we should perhaps block until queued traces are flushed? 65 66 static_cast<DataSourceType *>(this)->Trace([](auto ctx) { 67 auto packet = ctx.NewTracePacket(); 68 packet->Finalize(); 69 ctx.Flush(); 70 }); 71 72 ralloc_free(debug_markers); 73 } 74 75 /* Emits a clock sync trace event. Perfetto uses periodic clock events 76 * like this to sync up our GPU render stages with the CPU on the same 77 * timeline, since clocks always drift over time. Note that perfetto 78 * relies on gpu_ts being monotonic, and will perform badly if it goes 79 * backwards -- see tu_perfetto.cc for an example implemntation of handling 80 * going backwards. 81 */ EmitClockSync(TraceContext & ctx,uint64_t cpu_ts,uint64_t gpu_ts,uint32_t gpu_clock_id)82 static void EmitClockSync(TraceContext &ctx, 83 uint64_t cpu_ts, 84 uint64_t gpu_ts, 85 uint32_t gpu_clock_id) 86 { 87 auto packet = ctx.NewTracePacket(); 88 89 packet->set_timestamp_clock_id( 90 perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); 91 packet->set_timestamp(cpu_ts); 92 93 auto event = packet->set_clock_snapshot(); 94 95 { 96 auto clock = event->add_clocks(); 97 98 clock->set_clock_id( 99 perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); 100 clock->set_timestamp(cpu_ts); 101 } 102 103 { 104 auto clock = event->add_clocks(); 105 106 clock->set_clock_id(gpu_clock_id); 107 clock->set_timestamp(gpu_ts); 108 } 109 } 110 111 /* Returns a stage iid to use for a command stream or queue annotation. 112 * 113 * Using a new stage lets the annotation string show up right on the track 114 * event in the UI, rather than needing to click into the event to find the 115 * name in the metadata. Intended for use with 116 * vkCmdBeginDebugUtilsLabelEXT() and glPushDebugGroup(). 117 * 118 * Note that SEQ_INCREMENTAL_STATE_CLEARED must have been set in the 119 * sequence before this is called. 120 */ debug_marker_stage(TraceContext & ctx,const char * name)121 uint64_t debug_marker_stage(TraceContext &ctx, const char *name) 122 { 123 struct hash_entry *entry = _mesa_hash_table_search(debug_markers, name); 124 const uint64_t dynamic_iid_base = 1ull << 32; 125 126 if (entry) { 127 return dynamic_iid_base + (uint32_t) (uintptr_t) entry->data; 128 } else { 129 uint64_t iid = dynamic_iid_base + debug_markers->entries; 130 131 auto packet = ctx.NewTracePacket(); 132 auto interned_data = packet->set_interned_data(); 133 134 auto desc = interned_data->add_gpu_specifications(); 135 desc->set_iid(iid); 136 desc->set_name(name); 137 138 /* We only track the entry count in entry->data, because the 139 * dynamic_iid_base would get lost on 32-bit builds. 140 */ 141 _mesa_hash_table_insert(debug_markers, 142 ralloc_strdup(debug_markers, name), 143 (void *) (uintptr_t) debug_markers->entries); 144 145 return iid; 146 } 147 } 148 149 private: 150 /* Hash table of application generated events (string -> iid) (use 151 * tctx.GetDataSourceLocked()->debug_marker_stage() to get a stage iid) 152 */ 153 struct hash_table *debug_markers; 154 }; 155 156 /* Begin the C API section. */ 157