Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions impeller/renderer/backend/vulkan/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ impeller_component("vulkan") {
"fence_waiter_vk.h",
"formats_vk.cc",
"formats_vk.h",
"gpu_tracer_vk.cc",
"gpu_tracer_vk.h",
"limits_vk.h",
"pass_bindings_cache.cc",
"pass_bindings_cache.h",
Expand Down
3 changes: 3 additions & 0 deletions impeller/renderer/backend/vulkan/command_buffer_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const std::shared_ptr<CommandEncoderVK>& CommandBufferVK::GetEncoder() {
}

bool CommandBufferVK::OnSubmitCommands(CompletionCallback callback) {
if (!encoder_) {
encoder_ = encoder_factory_->Create();
}
if (!callback) {
return encoder_->Submit();
}
Expand Down
9 changes: 9 additions & 0 deletions impeller/renderer/backend/vulkan/context_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "impeller/renderer/backend/vulkan/command_pool_vk.h"
#include "impeller/renderer/backend/vulkan/debug_report_vk.h"
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
#include "impeller/renderer/backend/vulkan/resource_manager_vk.h"
#include "impeller/renderer/backend/vulkan/surface_context_vk.h"
#include "impeller/renderer/capabilities.h"
Expand Down Expand Up @@ -430,6 +431,10 @@ void ContextVK::Setup(Settings settings) {
device_name_ = std::string(physical_device_properties.deviceName);
is_valid_ = true;

// Create the GPU Tracer later because it depends on state from
// the ContextVK.
gpu_tracer_ = std::make_shared<GPUTracerVK>(shared_from_this());

//----------------------------------------------------------------------------
/// Label all the relevant objects. This happens after setup so that the
/// debug messengers have had a chance to be set up.
Expand Down Expand Up @@ -532,4 +537,8 @@ ContextVK::CreateGraphicsCommandEncoderFactory() const {
return std::make_unique<CommandEncoderFactoryVK>(weak_from_this());
}

std::shared_ptr<GPUTracerVK> ContextVK::GetGPUTracer() const {
return gpu_tracer_;
}

} // namespace impeller
7 changes: 7 additions & 0 deletions impeller/renderer/backend/vulkan/context_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class DebugReportVK;
class FenceWaiterVK;
class ResourceManagerVK;
class SurfaceContextVK;
class GPUTracerVK;

class ContextVK final : public Context,
public BackendCast<ContextVK, Context>,
Expand Down Expand Up @@ -144,6 +145,10 @@ class ContextVK final : public Context,

std::shared_ptr<CommandPoolRecyclerVK> GetCommandPoolRecycler() const;

std::shared_ptr<GPUTracerVK> GetGPUTracer() const;

void RecordFrameEndTime() const;

private:
struct DeviceHolderImpl : public DeviceHolder {
// |DeviceHolder|
Expand Down Expand Up @@ -171,6 +176,8 @@ class ContextVK final : public Context,
std::shared_ptr<CommandPoolRecyclerVK> command_pool_recycler_;
std::string device_name_;
std::shared_ptr<fml::ConcurrentMessageLoop> raster_message_loop_;
std::shared_ptr<GPUTracerVK> gpu_tracer_;

bool sync_presentation_ = false;
const uint64_t hash_;

Expand Down
107 changes: 107 additions & 0 deletions impeller/renderer/backend/vulkan/gpu_tracer_vk.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"

#include <utility>
#include "fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/command_buffer.h"

namespace impeller {

static constexpr uint32_t kPoolSize = 128u;

GPUTracerVK::GPUTracerVK(const std::shared_ptr<ContextVK>& context)
: context_(context) {
timestamp_period_ =
context_->GetPhysicalDevice().getProperties().limits.timestampPeriod;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to check if this is actually supported right? Otherwise the value might be meaningless.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the cmd itself is in 1.0, but I need to check that the queue supports timestamp queries. I think this will always be supported on graphics queues though, just might not have as much resolution as we'd like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some tests failed due to queue contruction issues so I added some logs to see what this value ends up as ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, this can be zero. Good call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


vk::QueryPoolCreateInfo info;
info.queryCount = kPoolSize;
info.queryType = vk::QueryType::eTimestamp;

auto [status, pool] = context_->GetDevice().createQueryPoolUnique(info);
if (status != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to create query pool.";
return;
}
query_pool_ = std::move(pool);
valid_ = true;
}

void GPUTracerVK::RecordStartFrameTime() {
if (!valid_) {
return;
}
auto buffer = context_->CreateCommandBuffer();
auto vk_trace_cmd_buffer =
CommandBufferVK::Cast(*buffer).GetEncoder()->GetCommandBuffer();
// The two commands below are executed in order, such that writeTimeStamp is
// guaranteed to occur after resetQueryPool has finished. The validation
// layer seem particularly strict, and efforts to reset the entire pool
// were met with validation errors (though seemingly correct measurements).
// To work around this, the tracer only resets the query that will be
// used next.
vk_trace_cmd_buffer.resetQueryPool(query_pool_.get(), current_index_, 1);
vk_trace_cmd_buffer.writeTimestamp(vk::PipelineStageFlagBits::eTopOfPipe,
query_pool_.get(), current_index_);

if (!buffer->SubmitCommands()) {
VALIDATION_LOG << "GPUTracerVK: Failed to record start time.";
}

// The logic in RecordEndFrameTime requires us to have recorded a pair of
// tracing events. If this method failed for any reason we need to be sure we
// don't attempt to record and read back a second value, or we will get values
// that span multiple frames.
started_frame_ = true;
}

void GPUTracerVK::RecordEndFrameTime() {
if (!valid_ || !started_frame_) {
return;
}
started_frame_ = false;
auto last_query = current_index_;
current_index_ += 1;

auto buffer = context_->CreateCommandBuffer();
auto vk_trace_cmd_buffer =
CommandBufferVK::Cast(*buffer).GetEncoder()->GetCommandBuffer();
vk_trace_cmd_buffer.resetQueryPool(query_pool_.get(), current_index_, 1);
vk_trace_cmd_buffer.writeTimestamp(vk::PipelineStageFlagBits::eBottomOfPipe,
query_pool_.get(), current_index_);

// On completion of the second time stamp recording, we read back this value
// and the previous value. The difference is approximately the frame time.
if (!buffer->SubmitCommands([&, last_query](CommandBuffer::Status status) {
uint64_t bits[2] = {0, 0};
auto result = context_->GetDevice().getQueryPoolResults(
query_pool_.get(), last_query, 2, sizeof(bits), &bits,
sizeof(int64_t), vk::QueryResultFlagBits::e64);

if (result == vk::Result::eSuccess) {
// This value should probably be available in some form besides a
// timeline event but that is a job for a future Jonah.
auto gpu_ms = (((bits[1] - bits[0]) * timestamp_period_) / 1000000);
FML_TRACE_COUNTER("flutter", "GPUTracer",
1234, // Trace Counter ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably just be reinterpret_cast<int64_t>(this), or at least a named constant somewhere.

"FrameTimeMS", gpu_ms);
}
})) {
if (!buffer->SubmitCommands()) {
VALIDATION_LOG << "GPUTracerVK failed to record frame end time.";
}
}

if (current_index_ == kPoolSize - 1) {
current_index_ = 0u;
}
}

} // namespace impeller
40 changes: 40 additions & 0 deletions impeller/renderer/backend/vulkan/gpu_tracer_vk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/vk.h"

namespace impeller {

/// @brief A class that uses timestamp queries to record the approximate GPU
/// execution time.
class GPUTracerVK {
public:
explicit GPUTracerVK(const std::shared_ptr<ContextVK>& context);

~GPUTracerVK() = default;

/// @brief Record the approximate start time of the GPU workload for the
/// current frame.
void RecordStartFrameTime();

/// @brief Record the approximate end time of the GPU workload for the current
/// frame.
void RecordEndFrameTime();

private:
void ResetQueryPool(size_t pool);

const std::shared_ptr<ContextVK> context_;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a smart C++ guy. Is the ContextVK owning a shared_ptr to the GPUTracerVK and the GPUTracerVK also having a shared_ptr to the ContextVK going to cause problems? Feels a bit like a cycle. Could use weakptrs...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

vk::UniqueQueryPool query_pool_ = {};

size_t current_index_ = 0u;
// The number of nanoseconds for each timestamp unit.
float timestamp_period_ = 1;
bool started_frame_ = false;
bool valid_ = false;
};

} // namespace impeller
2 changes: 2 additions & 0 deletions impeller/renderer/backend/vulkan/queue_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class QueueVK {

void InsertDebugMarker(const char* label) const;

size_t GetTimestampBits() const;

private:
mutable Mutex queue_mutex_;

Expand Down
9 changes: 9 additions & 0 deletions impeller/renderer/backend/vulkan/swapchain_impl_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

#include "impeller/renderer/backend/vulkan/swapchain_impl_vk.h"

#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/formats_vk.h"
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
#include "impeller/renderer/backend/vulkan/surface_vk.h"
#include "impeller/renderer/backend/vulkan/swapchain_image_vk.h"
#include "impeller/renderer/context.h"
#include "vulkan/vulkan_structs.hpp"

namespace impeller {
Expand Down Expand Up @@ -375,6 +378,9 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() {
nullptr // fence
);

/// Record the approximate start of the GPU workload.
context.GetGPUTracer()->RecordStartFrameTime();

switch (acq_result) {
case vk::Result::eSuccess:
// Keep going.
Expand Down Expand Up @@ -450,6 +456,9 @@ bool SwapchainImplVK::Present(const std::shared_ptr<SwapchainImageVK>& image,
}
}

/// Record the approximate end of the GPU workload.
context.GetGPUTracer()->RecordEndFrameTime();

//----------------------------------------------------------------------------
/// Signal that the presentation semaphore is ready.
///
Expand Down