blob: 71bc905633c422319adc14d97f188ec862f8d544 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_LIB_ESCHER_PROFILING_TIMESTAMP_PROFILER_H_
#define SRC_UI_LIB_ESCHER_PROFILING_TIMESTAMP_PROFILER_H_
#include <vector>
#include "src/lib/fxl/memory/ref_counted.h"
#include "src/ui/lib/escher/forward_declarations.h"
#include "src/ui/lib/escher/third_party/granite/vk/command_buffer.h"
#include "src/ui/lib/escher/vk/vulkan_context.h"
namespace escher {
class TimestampProfiler : public fxl::RefCountedThreadSafe<TimestampProfiler> {
public:
TimestampProfiler(vk::Device device, float timestamp_period);
~TimestampProfiler();
// Add a Vulkan timestamp-query that will mark the time that all previous
// commands in |cmd_buf| have finished the pipeline stage specified by
// |flags|. For example, use eVertexShader to mark the time that vertex
// shaders have been applied to all vertices from previous draw-calls.
//
// |name| needs to be a global constant. The trace engine that ultimately
// consumes Result structs caches it, so if the contents were overwritten the
// records would be inaccurate.
//
// NOTE: you should understand the caveats in the Vulkan spec regarding the
// accuracy of these timestamps. For example, many implementations will treat
// any set of flags as equivalent to eBottomOfPipe.
void AddTimestamp(CommandBufferPtr cmd_buf, vk::PipelineStageFlagBits flags, const char* name);
struct Result {
uint64_t raw_nanoseconds; // nanoseconds according to some timebase.
uint64_t time; // microseconds elapsed since the first timestamp.
uint64_t elapsed; // microseconds elapsed since the previous timestamp.
const char* name;
};
struct TraceEvent {
uint64_t start_elapsed_ticks; // ticks elapsed from the first timestamp to start
uint64_t end_elapsed_ticks; // ticks elapsed from the first timestamp to end
std::vector<const char*> names; // all trace event names for the time range
};
// GetQueryResults() returns the raw events generated by a series of
// AddTimeStamp() calls.
std::vector<Result> GetQueryResults();
// Transforms a vector of Results into a vector of TraceEvents more suitable
// for the trace system to use.
//
// NOTE: Currently only supported on Fuchsia.
static std::vector<TraceEvent> ProcessTraceEvents(const std::vector<Result>& timestamps);
// Uses VTHREAD trace macros to register all GPU work represented in
// |trace_events|. |trace_events| should be returned from a call to
// ProcessTraceEvents() |trace_literal| is the name that the event will take
// and should be a string literal.
static void TraceGpuQueryResults(const std::vector<TraceEvent>& trace_events,
uint64_t frame_number, uint64_t escher_frame_number,
const char* trace_literal, const char* gpu_vthread_literal,
uint64_t gpu_vthread_id);
static void LogGpuQueryResults(uint64_t escher_frame_number,
const std::vector<Result>& timestamps);
private:
// Each QueryRange keeps track of current usage of a separate vk::QueryPool.
// A new pool (and hence a new QueryRange) is used whenever:
// - the capacity of the previous pool is reached.
// - a different CommandBuffer is passed to AddTimestamp().
struct QueryRange {
vk::QueryPool pool;
vk::CommandBuffer command_buffer;
uint32_t start_index; // within the pool
uint32_t count;
};
QueryRange* ObtainRange(CommandBufferPtr cmd_buf);
QueryRange* CreateRange(CommandBufferPtr cmd_buf);
QueryRange* CreateRangeAndPool(CommandBufferPtr cmd_buf);
std::vector<QueryRange> ranges_;
std::vector<vk::QueryPool> pools_;
// Remembers timestamp names, and will eventually be filled in with timestamp
// values before returning results.
std::vector<Result> results_;
uint32_t query_count_ = 0;
uint32_t current_pool_index_ = 0;
const vk::Device device_;
const float timestamp_period_;
};
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_PROFILING_TIMESTAMP_PROFILER_H_