blob: 91a2ac71ed211856feef26070a435099f65a4bd2 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/proto/graphics_event_parser.h"
#include <inttypes.h>
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/field.h"
#include "src/trace_processor/args_tracker.h"
#include "src/trace_processor/event_tracker.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/track_tracker.h"
#include "src/trace_processor/types/trace_processor_context.h"
#include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
#include "protos/perfetto/trace/android/graphics_frame_event.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_counter_event.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_log.pbzero.h"
#include "protos/perfetto/trace/gpu/gpu_render_stage_event.pbzero.h"
#include "protos/perfetto/trace/gpu/vulkan_api_event.pbzero.h"
#include "protos/perfetto/trace/gpu/vulkan_memory_event.pbzero.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
namespace perfetto {
namespace trace_processor {
namespace {
// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkObjectType.html
typedef enum VkObjectType {
VK_OBJECT_TYPE_UNKNOWN = 0,
VK_OBJECT_TYPE_INSTANCE = 1,
VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2,
VK_OBJECT_TYPE_DEVICE = 3,
VK_OBJECT_TYPE_QUEUE = 4,
VK_OBJECT_TYPE_SEMAPHORE = 5,
VK_OBJECT_TYPE_COMMAND_BUFFER = 6,
VK_OBJECT_TYPE_FENCE = 7,
VK_OBJECT_TYPE_DEVICE_MEMORY = 8,
VK_OBJECT_TYPE_BUFFER = 9,
VK_OBJECT_TYPE_IMAGE = 10,
VK_OBJECT_TYPE_EVENT = 11,
VK_OBJECT_TYPE_QUERY_POOL = 12,
VK_OBJECT_TYPE_BUFFER_VIEW = 13,
VK_OBJECT_TYPE_IMAGE_VIEW = 14,
VK_OBJECT_TYPE_SHADER_MODULE = 15,
VK_OBJECT_TYPE_PIPELINE_CACHE = 16,
VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17,
VK_OBJECT_TYPE_RENDER_PASS = 18,
VK_OBJECT_TYPE_PIPELINE = 19,
VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20,
VK_OBJECT_TYPE_SAMPLER = 21,
VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22,
VK_OBJECT_TYPE_DESCRIPTOR_SET = 23,
VK_OBJECT_TYPE_FRAMEBUFFER = 24,
VK_OBJECT_TYPE_COMMAND_POOL = 25,
VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000,
VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000,
VK_OBJECT_TYPE_SURFACE_KHR = 1000000000,
VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000,
VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000,
VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001,
VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000,
VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000,
VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001,
VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000,
VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000,
VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000,
VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR =
VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE,
VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR =
VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION,
VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkObjectType;
} // anonymous namespace
GraphicsEventParser::GraphicsEventParser(TraceProcessorContext* context)
: context_(context),
vulkan_memory_tracker_(context),
description_id_(context->storage->InternString("description")),
gpu_render_stage_scope_id_(
context->storage->InternString("gpu_render_stage")),
graphics_event_scope_id_(
context->storage->InternString("graphics_frame_event")),
unknown_event_name_id_(context->storage->InternString("unknown_event")),
no_layer_name_name_id_(context->storage->InternString("no_layer_name")),
layer_name_key_id_(context->storage->InternString("layer_name")),
event_type_name_ids_{
{context->storage->InternString(
"unspecified_event") /* UNSPECIFIED */,
context->storage->InternString("Dequeue") /* DEQUEUE */,
context->storage->InternString("Queue") /* QUEUE */,
context->storage->InternString("Post") /* POST */,
context->storage->InternString(
"AcquireFenceSignaled") /* ACQUIRE_FENCE */,
context->storage->InternString("Latch") /* LATCH */,
context->storage->InternString(
"HWCCompositionQueued") /* HWC_COMPOSITION_QUEUED */,
context->storage->InternString(
"FallbackComposition") /* FALLBACK_COMPOSITION */,
context->storage->InternString(
"PresentFenceSignaled") /* PRESENT_FENCE */,
context->storage->InternString(
"ReleaseFenceSignaled") /* RELEASE_FENCE */,
context->storage->InternString("Modify") /* MODIFY */,
context->storage->InternString("Detach") /* DETACH */,
context->storage->InternString("Attach") /* ATTACH */,
context->storage->InternString("Cancel") /* CANCEL */}},
present_frame_name_(present_frame_buffer_,
base::ArraySize(present_frame_buffer_)),
present_frame_layer_name_(present_frame_layer_buffer_,
base::ArraySize(present_frame_layer_buffer_)),
present_frame_numbers_(present_frame_numbers_buffer_,
base::ArraySize(present_frame_numbers_buffer_)),
gpu_log_track_name_id_(context_->storage->InternString("GPU Log")),
gpu_log_scope_id_(context_->storage->InternString("gpu_log")),
tag_id_(context_->storage->InternString("tag")),
log_message_id_(context->storage->InternString("message")),
log_severity_ids_{{context_->storage->InternString("UNSPECIFIED"),
context_->storage->InternString("VERBOSE"),
context_->storage->InternString("DEBUG"),
context_->storage->InternString("INFO"),
context_->storage->InternString("WARNING"),
context_->storage->InternString("ERROR"),
context_->storage->InternString(
"UNKNOWN_SEVERITY") /* must be last */}},
vk_event_track_id_(context->storage->InternString("Vulkan Events")),
vk_event_scope_id_(context->storage->InternString("vulkan_events")),
vk_queue_submit_id_(context->storage->InternString("vkQueueSubmit")) {}
void GraphicsEventParser::ParseGpuCounterEvent(int64_t ts, ConstBytes blob) {
protos::pbzero::GpuCounterEvent::Decoder event(blob.data, blob.size);
protos::pbzero::GpuCounterDescriptor::Decoder descriptor(
event.counter_descriptor());
// Add counter spec to ID map.
for (auto it = descriptor.specs(); it; ++it) {
protos::pbzero::GpuCounterDescriptor_GpuCounterSpec::Decoder spec(*it);
if (!spec.has_counter_id()) {
PERFETTO_ELOG("Counter spec missing counter id");
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
continue;
}
if (!spec.has_name()) {
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
continue;
}
auto counter_id = spec.counter_id();
auto name = spec.name();
if (gpu_counter_track_ids_.find(counter_id) ==
gpu_counter_track_ids_.end()) {
auto desc = spec.description();
StringId unit_id = kNullStringId;
if (spec.has_numerator_units() || spec.has_denominator_units()) {
char buffer[1024];
base::StringWriter unit(buffer, sizeof(buffer));
for (auto numer = spec.numerator_units(); numer; ++numer) {
if (unit.pos()) {
unit.AppendChar(':');
}
unit.AppendInt(*numer);
}
char sep = '/';
for (auto denom = spec.denominator_units(); denom; ++denom) {
unit.AppendChar(sep);
unit.AppendInt(*denom);
sep = ':';
}
unit_id = context_->storage->InternString(unit.GetStringView());
}
auto name_id = context_->storage->InternString(name);
auto desc_id = context_->storage->InternString(desc);
auto track_id = context_->track_tracker->CreateGpuCounterTrack(
name_id, 0 /* gpu_id */, desc_id, unit_id);
gpu_counter_track_ids_.emplace(counter_id, track_id);
} else {
// Either counter spec was repeated or it came after counter data.
PERFETTO_ELOG("Duplicated counter spec found. (counter_id=%d, name=%s)",
counter_id, name.ToStdString().c_str());
context_->storage->IncrementStats(stats::gpu_counters_invalid_spec);
}
}
for (auto it = event.counters(); it; ++it) {
protos::pbzero::GpuCounterEvent_GpuCounter::Decoder counter(*it);
if (counter.has_counter_id() &&
(counter.has_int_value() || counter.has_double_value())) {
auto counter_id = counter.counter_id();
// Check missing counter_id
if (gpu_counter_track_ids_.find(counter_id) ==
gpu_counter_track_ids_.end()) {
char buffer[64];
base::StringWriter writer(buffer, sizeof(buffer));
writer.AppendString("gpu_counter(");
writer.AppendUnsignedInt(counter_id);
writer.AppendString(")");
auto name_id = context_->storage->InternString(writer.GetStringView());
TrackId track = context_->track_tracker->CreateGpuCounterTrack(
name_id, 0 /* gpu_id */);
gpu_counter_track_ids_.emplace(counter_id, track);
context_->storage->IncrementStats(stats::gpu_counters_missing_spec);
}
if (counter.has_int_value()) {
context_->event_tracker->PushCounter(
ts, counter.int_value(), gpu_counter_track_ids_[counter_id]);
} else {
context_->event_tracker->PushCounter(
ts, counter.double_value(), gpu_counter_track_ids_[counter_id]);
}
}
}
}
const StringId GraphicsEventParser::GetFullStageName(
const protos::pbzero::GpuRenderStageEvent_Decoder& event) const {
size_t stage_id = static_cast<size_t>(event.stage_id());
StringId stage_name;
if (stage_id < gpu_render_stage_ids_.size()) {
stage_name = gpu_render_stage_ids_[stage_id].first;
} else {
char buffer[64];
snprintf(buffer, sizeof(buffer), "render stage(%zu)", stage_id);
stage_name = context_->storage->InternString(buffer);
}
return stage_name;
}
/**
* Create a GPU render stage track based
* GpuRenderStageEvent.Specifications.Description.
*/
void GraphicsEventParser::InsertGpuTrack(
const protos::pbzero::
GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue) {
StringId track_name = context_->storage->InternString(hw_queue.name());
if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size() ||
!gpu_hw_queue_ids_[gpu_hw_queue_counter_].has_value()) {
tables::GpuTrackTable::Row track(track_name);
track.scope = gpu_render_stage_scope_id_;
track.description = context_->storage->InternString(hw_queue.description());
if (gpu_hw_queue_counter_ >= gpu_hw_queue_ids_.size()) {
gpu_hw_queue_ids_.emplace_back(
context_->track_tracker->InternGpuTrack(track));
} else {
// If a gpu_render_stage_event is received before the specification, it is
// possible that the slot has already been allocated.
gpu_hw_queue_ids_[gpu_hw_queue_counter_] =
context_->track_tracker->InternGpuTrack(track);
}
} else {
// If a gpu_render_stage_event is received before the specification, a track
// will be automatically generated. In that case, update the name and
// description.
auto track_id = gpu_hw_queue_ids_[gpu_hw_queue_counter_];
if (track_id.has_value()) {
auto row = context_->storage->mutable_gpu_track_table()
->id()
.IndexOf(track_id.value())
.value();
context_->storage->mutable_gpu_track_table()->mutable_name()->Set(
row, track_name);
context_->storage->mutable_gpu_track_table()->mutable_description()->Set(
row, context_->storage->InternString(hw_queue.description()));
} else {
tables::GpuTrackTable::Row track(track_name);
track.scope = gpu_render_stage_scope_id_;
track.description =
context_->storage->InternString(hw_queue.description());
}
}
++gpu_hw_queue_counter_;
}
base::Optional<std::string> GraphicsEventParser::FindDebugName(
int32_t vk_object_type,
uint64_t vk_handle) const {
auto map = debug_marker_names_.find(vk_object_type);
if (map == debug_marker_names_.end()) {
return base::nullopt;
}
auto name = map->second.find(vk_handle);
if (name == map->second.end()) {
return base::nullopt;
} else {
return name->second;
}
}
void GraphicsEventParser::ParseGpuRenderStageEvent(int64_t ts,
ConstBytes blob) {
protos::pbzero::GpuRenderStageEvent::Decoder event(blob.data, blob.size);
if (event.has_specifications()) {
protos::pbzero::GpuRenderStageEvent_Specifications::Decoder spec(
event.specifications().data, event.specifications().size);
for (auto it = spec.hw_queue(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
hw_queue(*it);
if (hw_queue.has_name()) {
InsertGpuTrack(hw_queue);
}
}
for (auto it = spec.stage(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_Specifications_Description::Decoder
stage(*it);
if (stage.has_name()) {
gpu_render_stage_ids_.emplace_back(std::make_pair(
context_->storage->InternString(stage.name()),
context_->storage->InternString(stage.description())));
}
}
}
auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
size_t stage_id = static_cast<size_t>(event.stage_id());
if (stage_id < gpu_render_stage_ids_.size()) {
auto description = gpu_render_stage_ids_[stage_id].second;
if (description != kNullStringId) {
inserter->AddArg(description_id_, Variadic::String(description));
}
}
for (auto it = event.extra_data(); it; ++it) {
protos::pbzero::GpuRenderStageEvent_ExtraData_Decoder datum(*it);
StringId name_id = context_->storage->InternString(datum.name());
StringId value = context_->storage->InternString(
datum.has_value() ? datum.value() : base::StringView());
inserter->AddArg(name_id, Variadic::String(value));
}
};
if (event.has_event_id()) {
TrackId track_id;
uint32_t hw_queue_id = static_cast<uint32_t>(event.hw_queue_id());
if (hw_queue_id < gpu_hw_queue_ids_.size() &&
gpu_hw_queue_ids_[hw_queue_id].has_value()) {
track_id = gpu_hw_queue_ids_[hw_queue_id].value();
} else {
// If the event has a hw_queue_id that does not have a Specification,
// create a new track for it.
char buf[128];
base::StringWriter writer(buf, sizeof(buf));
writer.AppendLiteral("Unknown GPU Queue ");
if (hw_queue_id > 1024) {
// We don't expect this to happen, but just in case there is a corrupt
// packet, make sure we don't allocate a ridiculous amount of memory.
hw_queue_id = 1024;
context_->storage->IncrementStats(
stats::gpu_render_stage_parser_errors);
PERFETTO_ELOG("Invalid hw_queue_id.");
} else {
writer.AppendInt(event.hw_queue_id());
}
StringId track_name =
context_->storage->InternString(writer.GetStringView());
tables::GpuTrackTable::Row track(track_name);
track.scope = gpu_render_stage_scope_id_;
track_id = context_->track_tracker->InternGpuTrack(track);
gpu_hw_queue_ids_.resize(hw_queue_id + 1);
gpu_hw_queue_ids_[hw_queue_id] = track_id;
}
auto render_target_name = FindDebugName(VK_OBJECT_TYPE_FRAMEBUFFER, event.render_target_handle());
auto render_target_name_id = render_target_name.has_value()
? context_->storage->InternString(
render_target_name.value().c_str())
: kNullStringId;
auto render_pass_name = FindDebugName(VK_OBJECT_TYPE_RENDER_PASS, event.render_pass_handle());
auto render_pass_name_id = render_pass_name.has_value()
? context_->storage->InternString(
render_pass_name.value().c_str())
: kNullStringId;
auto command_buffer_name = FindDebugName(VK_OBJECT_TYPE_COMMAND_BUFFER, event.command_buffer_handle());
auto command_buffer_name_id = command_buffer_name.has_value()
? context_->storage->InternString(
command_buffer_name.value().c_str())
: kNullStringId;
tables::GpuSliceTable::Row row;
row.ts = ts;
row.track_id = track_id;
row.name = GetFullStageName(event);
row.dur = static_cast<int64_t>(event.duration());
row.context_id = static_cast<int64_t>(event.context());
row.render_target = static_cast<int64_t>(event.render_target_handle());
row.render_target_name = render_target_name_id;
row.render_pass = static_cast<int64_t>(event.render_pass_handle());
row.render_pass_name = render_pass_name_id;
row.command_buffer = static_cast<int64_t>(event.command_buffer_handle());
row.command_buffer_name = command_buffer_name_id;
row.submission_id = event.submission_id();
row.hw_queue_id = hw_queue_id;
context_->slice_tracker->ScopedGpu(row, args_callback);
}
}
void GraphicsEventParser::ParseGraphicsFrameEvent(int64_t timestamp,
ConstBytes blob) {
using GraphicsFrameEvent = protos::pbzero::GraphicsFrameEvent;
protos::pbzero::GraphicsFrameEvent_Decoder frame_event(blob.data, blob.size);
if (!frame_event.has_buffer_event()) {
return;
}
ConstBytes bufferBlob = frame_event.buffer_event();
protos::pbzero::GraphicsFrameEvent_BufferEvent_Decoder event(bufferBlob.data,
bufferBlob.size);
if (!event.has_buffer_id()) {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing buffer id field.");
return;
}
StringId event_name_id = unknown_event_name_id_;
if (event.has_type()) {
const auto type = static_cast<size_t>(event.type());
if (type < event_type_name_ids_.size()) {
event_name_id = event_type_name_ids_[type];
graphics_frame_stats_map_[event.buffer_id()][type] = timestamp;
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with unknown type %zu.", type);
}
} else {
context_->storage->IncrementStats(
stats::graphics_frame_event_parser_errors);
PERFETTO_ELOG("GraphicsFrameEvent with missing type field.");
}
const uint32_t buffer_id = event.buffer_id();
StringId layer_name_id;
char buffer[4096];
const size_t layerNameMaxLength = 4000;
base::StringWriter track_name(buffer, sizeof(buffer));
if (event.has_layer_name()) {
const base::StringView layer_name(event.layer_name());
layer_name_id = context_->storage->InternString(layer_name);
track_name.AppendString(layer_name.substr(0, layerNameMaxLength));
} else {
layer_name_id = no_layer_name_name_id_;
track_name.AppendLiteral("unknown_layer");
}
track_name.AppendLiteral("[buffer:");
track_name.AppendUnsignedInt(buffer_id);
track_name.AppendChar(']');
const StringId track_name_id =
context_->storage->InternString(track_name.GetStringView());
const int64_t duration =
event.has_duration_ns() ? static_cast<int64_t>(event.duration_ns()) : 0;
const uint32_t frame_number =
event.has_frame_number() ? event.frame_number() : 0;
tables::GpuTrackTable::Row track(track_name_id);
track.scope = graphics_event_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
{
char frame_number_buffer[256];
base::StringWriter frame_numbers(frame_number_buffer,
base::ArraySize(frame_number_buffer));
frame_numbers.AppendUnsignedInt(frame_number);
tables::GraphicsFrameSliceTable::Row row;
row.ts = timestamp;
row.track_id = track_id;
row.name = event_name_id;
row.dur = duration;
row.frame_numbers =
context_->storage->InternString(frame_numbers.GetStringView());
row.layer_names = layer_name_id;
context_->slice_tracker->ScopedFrameEvent(row);
}
/* Displayed Frame track */
if (event.type() == GraphicsFrameEvent::PRESENT_FENCE) {
// Insert the frame stats for the buffer that was presented
auto acquire_ts =
graphics_frame_stats_map_[event.buffer_id()]
[GraphicsFrameEvent::ACQUIRE_FENCE];
auto queue_ts =
graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::QUEUE];
auto latch_ts =
graphics_frame_stats_map_[event.buffer_id()][GraphicsFrameEvent::LATCH];
tables::GraphicsFrameStatsTable::Row stats_row;
// AcquireFence can signal before Queue sometimes, so have 0 as a bound.
stats_row.queue_to_acquire_time =
std::max(acquire_ts - queue_ts, static_cast<int64_t>(0));
stats_row.acquire_to_latch_time = latch_ts - acquire_ts;
stats_row.latch_to_present_time = timestamp - latch_ts;
auto stats_row_id =
context_->storage->mutable_graphics_frame_stats_table()->Insert(
stats_row);
if (previous_timestamp_ == 0) {
const StringId present_track_name_id =
context_->storage->InternString("Displayed Frame");
tables::GpuTrackTable::Row present_track(present_track_name_id);
present_track.scope = graphics_event_scope_id_;
present_track_id_ =
context_->track_tracker->InternGpuTrack(present_track);
}
// The displayed frame is a slice from one present fence to another present
// fence. If multiple buffers have present fence at the same time, they all
// are shown on screen at the same time. So that particular displayed frame
// slice should include info from all those buffers in it.
// Since the events are parsed one by one, the following bookkeeping is
// needed to create the slice properly.
if (previous_timestamp_ == timestamp && previous_timestamp_ != 0) {
// Same timestamp as previous present fence. This buffer should also
// contribute to this slice.
present_frame_name_.AppendLiteral(", ");
present_frame_name_.AppendUnsignedInt(buffer_id);
// Append Layer names
present_frame_layer_name_.AppendLiteral(", ");
present_frame_layer_name_.AppendString(event.layer_name());
// Append Frame numbers
present_frame_numbers_.AppendLiteral(", ");
present_frame_numbers_.AppendUnsignedInt(frame_number);
// Add the current stats row to the list of stats that go with this frame
graphics_frame_stats_idx_.push_back(stats_row_id.row);
} else {
if (previous_timestamp_ != 0) {
StringId present_frame_layer_name_id = context_->storage->InternString(
present_frame_layer_name_.GetStringView());
// End the current slice that's being tracked.
const auto opt_slice_id = context_->slice_tracker->EndFrameEvent(
timestamp, present_track_id_);
if (opt_slice_id) {
// The slice could have had additional buffers in it, so we need to
// update the table.
auto* graphics_frame_slice_table =
context_->storage->mutable_graphics_frame_slice_table();
uint32_t row_idx =
*graphics_frame_slice_table->id().IndexOf(*opt_slice_id);
StringId frame_name_id = context_->storage->InternString(
present_frame_name_.GetStringView());
graphics_frame_slice_table->mutable_name()->Set(row_idx,
frame_name_id);
StringId present_frame_numbers_id = context_->storage->InternString(
present_frame_numbers_.GetStringView());
graphics_frame_slice_table->mutable_frame_numbers()->Set(
row_idx, present_frame_numbers_id);
graphics_frame_slice_table->mutable_layer_names()->Set(
row_idx, present_frame_layer_name_id);
// Set the slice_id for the frame_stats rows under this displayed
// frame
auto* slice_table = context_->storage->mutable_slice_table();
uint32_t slice_idx = *slice_table->id().IndexOf(*opt_slice_id);
for (uint32_t i = 0; i < graphics_frame_stats_idx_.size(); i++) {
context_->storage->mutable_graphics_frame_stats_table()
->mutable_slice_id()
->Set(graphics_frame_stats_idx_[i], slice_idx);
}
}
present_frame_layer_name_.reset();
present_frame_name_.reset();
present_frame_numbers_.reset();
graphics_frame_stats_idx_.clear();
}
// Start a new slice
present_frame_name_.AppendUnsignedInt(buffer_id);
previous_timestamp_ = timestamp;
present_frame_layer_name_.AppendString(event.layer_name());
present_frame_numbers_.AppendUnsignedInt(frame_number);
present_event_name_id_ =
context_->storage->InternString(present_frame_name_.GetStringView());
graphics_frame_stats_idx_.push_back(stats_row_id.row);
tables::GraphicsFrameSliceTable::Row row;
row.ts = timestamp;
row.track_id = present_track_id_;
row.name = present_event_name_id_;
context_->slice_tracker->BeginFrameEvent(row);
}
}
}
void GraphicsEventParser::UpdateVulkanMemoryAllocationCounters(
UniquePid upid,
const VulkanMemoryEvent::Decoder& event) {
StringId track_str_id = kNullStringId;
TrackId track = kInvalidTrackId;
auto allocation_scope = VulkanMemoryEvent::SCOPE_UNSPECIFIED;
uint32_t memory_type = std::numeric_limits<uint32_t>::max();
switch (event.source()) {
case VulkanMemoryEvent::SOURCE_DRIVER:
allocation_scope = static_cast<VulkanMemoryEvent::AllocationScope>(
event.allocation_scope());
if (allocation_scope == VulkanMemoryEvent::SCOPE_UNSPECIFIED)
return;
switch (event.operation()) {
case VulkanMemoryEvent::OP_CREATE:
vulkan_driver_memory_counters_[allocation_scope] +=
event.memory_size();
break;
case VulkanMemoryEvent::OP_DESTROY:
vulkan_driver_memory_counters_[allocation_scope] -=
event.memory_size();
break;
case VulkanMemoryEvent::OP_UNSPECIFIED:
case VulkanMemoryEvent::OP_BIND:
case VulkanMemoryEvent::OP_DESTROY_BOUND:
case VulkanMemoryEvent::OP_ANNOTATIONS:
return;
}
track_str_id = vulkan_memory_tracker_.FindAllocationScopeCounterString(
allocation_scope);
track = context_->track_tracker->InternProcessCounterTrack(track_str_id,
upid);
context_->event_tracker->PushCounter(
event.timestamp(), vulkan_driver_memory_counters_[allocation_scope],
track);
break;
case VulkanMemoryEvent::SOURCE_DEVICE_MEMORY:
memory_type = static_cast<uint32_t>(event.memory_type());
switch (event.operation()) {
case VulkanMemoryEvent::OP_CREATE:
vulkan_device_memory_counters_allocate_[memory_type] +=
event.memory_size();
break;
case VulkanMemoryEvent::OP_DESTROY:
vulkan_device_memory_counters_allocate_[memory_type] -=
event.memory_size();
break;
case VulkanMemoryEvent::OP_UNSPECIFIED:
case VulkanMemoryEvent::OP_BIND:
case VulkanMemoryEvent::OP_DESTROY_BOUND:
case VulkanMemoryEvent::OP_ANNOTATIONS:
return;
}
track_str_id = vulkan_memory_tracker_.FindMemoryTypeCounterString(
memory_type,
VulkanMemoryTracker::DeviceCounterType::kAllocationCounter);
track = context_->track_tracker->InternProcessCounterTrack(track_str_id,
upid);
context_->event_tracker->PushCounter(
event.timestamp(),
vulkan_device_memory_counters_allocate_[memory_type], track);
break;
case VulkanMemoryEvent::SOURCE_BUFFER:
case VulkanMemoryEvent::SOURCE_IMAGE:
memory_type = static_cast<uint32_t>(event.memory_type());
switch (event.operation()) {
case VulkanMemoryEvent::OP_BIND:
vulkan_device_memory_counters_bind_[memory_type] +=
event.memory_size();
break;
case VulkanMemoryEvent::OP_DESTROY_BOUND:
vulkan_device_memory_counters_bind_[memory_type] -=
event.memory_size();
break;
case VulkanMemoryEvent::OP_UNSPECIFIED:
case VulkanMemoryEvent::OP_CREATE:
case VulkanMemoryEvent::OP_DESTROY:
case VulkanMemoryEvent::OP_ANNOTATIONS:
return;
}
track_str_id = vulkan_memory_tracker_.FindMemoryTypeCounterString(
memory_type, VulkanMemoryTracker::DeviceCounterType::kBindCounter);
track = context_->track_tracker->InternProcessCounterTrack(track_str_id,
upid);
context_->event_tracker->PushCounter(
event.timestamp(), vulkan_device_memory_counters_bind_[memory_type],
track);
break;
case VulkanMemoryEvent::SOURCE_UNSPECIFIED:
case VulkanMemoryEvent::SOURCE_DEVICE:
return;
}
}
void GraphicsEventParser::ParseVulkanMemoryEvent(
PacketSequenceStateGeneration* sequence_state,
ConstBytes blob) {
using protos::pbzero::InternedData;
VulkanMemoryEvent::Decoder vulkan_memory_event(blob.data, blob.size);
tables::VulkanMemoryAllocationsTable::Row vulkan_memory_event_row;
vulkan_memory_event_row.source = vulkan_memory_tracker_.FindSourceString(
static_cast<VulkanMemoryEvent::Source>(vulkan_memory_event.source()));
vulkan_memory_event_row.operation =
vulkan_memory_tracker_.FindOperationString(
static_cast<VulkanMemoryEvent::Operation>(
vulkan_memory_event.operation()));
vulkan_memory_event_row.timestamp = vulkan_memory_event.timestamp();
vulkan_memory_event_row.upid =
context_->process_tracker->GetOrCreateProcess(vulkan_memory_event.pid());
if (vulkan_memory_event.has_device())
vulkan_memory_event_row.device =
static_cast<int64_t>(vulkan_memory_event.device());
if (vulkan_memory_event.has_device_memory())
vulkan_memory_event_row.device_memory =
static_cast<int64_t>(vulkan_memory_event.device_memory());
if (vulkan_memory_event.has_heap())
vulkan_memory_event_row.heap = vulkan_memory_event.heap();
if (vulkan_memory_event.has_memory_type())
vulkan_memory_event_row.memory_type = vulkan_memory_event.memory_type();
if (vulkan_memory_event.has_caller_iid()) {
vulkan_memory_event_row.function_name =
vulkan_memory_tracker_
.GetInternedString<InternedData::kFunctionNamesFieldNumber>(
sequence_state,
static_cast<uint64_t>(vulkan_memory_event.caller_iid()));
}
if (vulkan_memory_event.has_object_handle())
vulkan_memory_event_row.object_handle =
static_cast<int64_t>(vulkan_memory_event.object_handle());
if (vulkan_memory_event.has_memory_address())
vulkan_memory_event_row.memory_address =
static_cast<int64_t>(vulkan_memory_event.memory_address());
if (vulkan_memory_event.has_memory_size())
vulkan_memory_event_row.memory_size =
static_cast<int64_t>(vulkan_memory_event.memory_size());
if (vulkan_memory_event.has_allocation_scope())
vulkan_memory_event_row.scope =
vulkan_memory_tracker_.FindAllocationScopeString(
static_cast<VulkanMemoryEvent::AllocationScope>(
vulkan_memory_event.allocation_scope()));
UpdateVulkanMemoryAllocationCounters(vulkan_memory_event_row.upid.value(),
vulkan_memory_event);
auto* allocs = context_->storage->mutable_vulkan_memory_allocations_table();
VulkanAllocId id = allocs->Insert(vulkan_memory_event_row).id;
if (vulkan_memory_event.has_annotations()) {
auto inserter = context_->args_tracker->AddArgsTo(id);
for (auto it = vulkan_memory_event.annotations(); it; ++it) {
protos::pbzero::VulkanMemoryEventAnnotation::Decoder annotation(*it);
auto key_id =
vulkan_memory_tracker_
.GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
sequence_state, static_cast<uint64_t>(annotation.key_iid()));
if (annotation.has_int_value()) {
inserter.AddArg(key_id, Variadic::Integer(annotation.int_value()));
} else if (annotation.has_double_value()) {
inserter.AddArg(key_id, Variadic::Real(annotation.double_value()));
} else if (annotation.has_string_iid()) {
auto string_id =
vulkan_memory_tracker_
.GetInternedString<InternedData::kVulkanMemoryKeysFieldNumber>(
sequence_state,
static_cast<uint64_t>(annotation.string_iid()));
inserter.AddArg(key_id, Variadic::String(string_id));
}
}
}
}
void GraphicsEventParser::ParseGpuLog(int64_t ts, ConstBytes blob) {
protos::pbzero::GpuLog::Decoder event(blob.data, blob.size);
tables::GpuTrackTable::Row track(gpu_log_track_name_id_);
track.scope = gpu_log_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
if (event.has_tag()) {
inserter->AddArg(
tag_id_,
Variadic::String(context_->storage->InternString(event.tag())));
}
if (event.has_log_message()) {
inserter->AddArg(log_message_id_,
Variadic::String(context_->storage->InternString(
event.log_message())));
}
};
auto severity = static_cast<size_t>(event.severity());
StringId severity_id =
severity < log_severity_ids_.size()
? log_severity_ids_[static_cast<size_t>(event.severity())]
: log_severity_ids_[log_severity_ids_.size() - 1];
tables::GpuSliceTable::Row row;
row.ts = ts;
row.track_id = track_id;
row.name = severity_id;
row.dur = 0;
context_->slice_tracker->ScopedGpu(row, args_callback);
}
void GraphicsEventParser::ParseVulkanApiEvent(int64_t ts, ConstBytes blob) {
protos::pbzero::VulkanApiEvent::Decoder vk_event(blob.data, blob.size);
if (vk_event.has_vk_debug_utils_object_name()) {
protos::pbzero::VulkanApiEvent_VkDebugUtilsObjectName::Decoder event(
vk_event.vk_debug_utils_object_name());
debug_marker_names_[event.object_type()][event.object()] =
event.object_name().ToStdString();
}
if (vk_event.has_vk_queue_submit()) {
protos::pbzero::VulkanApiEvent_VkQueueSubmit::Decoder event(
vk_event.vk_queue_submit());
// Once flow table is implemented, we can create a nice UI that link the
// vkQueueSubmit to GpuRenderStageEvent. For now, just add it as in a GPU
// track so that they can appear close to the render stage slices.
tables::GpuTrackTable::Row track(vk_event_track_id_);
track.scope = vk_event_scope_id_;
TrackId track_id = context_->track_tracker->InternGpuTrack(track);
tables::GpuSliceTable::Row row;
row.ts = ts;
row.dur = static_cast<int64_t>(event.duration_ns());
row.track_id = track_id;
row.name = vk_queue_submit_id_;
if (event.has_vk_command_buffers()) {
row.command_buffer = static_cast<int64_t>(*event.vk_command_buffers());
}
row.submission_id = event.submission_id();
auto args_callback = [this, &event](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(context_->storage->InternString("pid"),
Variadic::Integer(event.pid()));
inserter->AddArg(context_->storage->InternString("tid"),
Variadic::Integer(event.tid()));
};
context_->slice_tracker->ScopedGpu(row, args_callback);
}
}
} // namespace trace_processor
} // namespace perfetto