275 lines
7.1 KiB
C++
275 lines
7.1 KiB
C++
/* Copyright (c) 2018-2025, Arm Limited and Contributors
|
|
* Copyright (c) 2020-2025, Broadcom Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 "hwcpipe_stats_provider.h"
|
|
|
|
namespace
|
|
{
|
|
const char *get_product_family_name(hwcpipe::device::gpu_family f)
|
|
{
|
|
using gpu_family = hwcpipe::device::gpu_family;
|
|
|
|
switch (f)
|
|
{
|
|
case gpu_family::bifrost:
|
|
return "Bifrost";
|
|
case gpu_family::midgard:
|
|
return "Midgard";
|
|
case gpu_family::valhall:
|
|
return "Valhall";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace vkb
|
|
{
|
|
HWCPipeStatsProvider::HWCPipeStatsProvider(std::set<StatIndex> &requested_stats)
|
|
{
|
|
// Mapping of stats to their hwcpipe availability
|
|
// clang-format off
|
|
StatDataMap hwcpipe_stats = {
|
|
{StatIndex::gpu_cycles, {hwcpipe_counter::MaliGPUActiveCy}},
|
|
{StatIndex::gpu_vertex_cycles, {hwcpipe_counter::MaliNonFragQueueActiveCy, {MaliNonFragActiveCy, MaliBinningQueueActiveCy} }},
|
|
{StatIndex::gpu_load_store_cycles, {hwcpipe_counter::MaliLSIssueCy}},
|
|
{StatIndex::gpu_tiles, {hwcpipe_counter::MaliFragTile}},
|
|
{StatIndex::gpu_killed_tiles, {hwcpipe_counter::MaliFragTileKill}},
|
|
{StatIndex::gpu_fragment_cycles, {hwcpipe_counter::MaliFragQueueActiveCy, {MaliFragActiveCy, MaliMainQueueActiveCy}}},
|
|
{StatIndex::gpu_fragment_jobs, {hwcpipe_counter::MaliFragQueueJob, {MaliMainQueueJob}}},
|
|
{StatIndex::gpu_ext_reads, {hwcpipe_counter::MaliExtBusRdBt}},
|
|
{StatIndex::gpu_ext_writes, {hwcpipe_counter::MaliExtBusWrBt}},
|
|
{StatIndex::gpu_ext_read_stalls, {hwcpipe_counter::MaliExtBusRdStallCy}},
|
|
{StatIndex::gpu_ext_write_stalls, {hwcpipe_counter::MaliExtBusWrStallCy}},
|
|
{StatIndex::gpu_ext_read_bytes, {hwcpipe_counter::MaliExtBusRdBy}},
|
|
{StatIndex::gpu_ext_write_bytes, {hwcpipe_counter::MaliExtBusWrBy}},
|
|
{StatIndex::gpu_tex_cycles, {hwcpipe_counter::MaliTexIssueCy}}};
|
|
// clang-format on
|
|
|
|
// Detect all GPUs & print some info
|
|
for (const auto &gpu : hwcpipe::find_gpus())
|
|
{
|
|
LOGI("HWCPipe: ------------------------------------------------------------");
|
|
LOGI("HWCPipe: GPU Device {}:", gpu.get_device_number());
|
|
LOGI("HWCPipe: ------------------------------------------------------------");
|
|
LOGI("HWCPipe: Product Family: {}", get_product_family_name(gpu.get_gpu_family()));
|
|
LOGI("HWCPipe: Number of Cores: {}", gpu.num_shader_cores());
|
|
LOGI("HWCPipe: Bus Width: {}", gpu.bus_width());
|
|
}
|
|
|
|
// Probe device 0 (i.e. /dev/mali0)
|
|
auto gpu = hwcpipe::gpu(0);
|
|
if (!gpu)
|
|
{
|
|
LOGE("HWCPipe: Mali GPU device 0 is missing");
|
|
}
|
|
|
|
auto config = hwcpipe::sampler_config(gpu);
|
|
auto counter_db = hwcpipe::counter_database{};
|
|
|
|
std::error_code ec;
|
|
for (const auto &stat : requested_stats)
|
|
{
|
|
auto it = hwcpipe_stats.find(stat);
|
|
if (it != hwcpipe_stats.end())
|
|
{
|
|
hwcpipe::counter_metadata meta;
|
|
|
|
auto ec = counter_db.describe_counter(it->second.counter, meta);
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: unknown counter");
|
|
}
|
|
|
|
ec = config.add_counter(it->second.counter);
|
|
if (ec)
|
|
{
|
|
// Some counters have changed in recent devices
|
|
auto &fallback_list = it->second.fallback_list;
|
|
if (!fallback_list.empty())
|
|
{
|
|
for (const auto &fallback : fallback_list)
|
|
{
|
|
ec = counter_db.describe_counter(fallback, meta);
|
|
ec = config.add_counter(fallback);
|
|
|
|
if (!ec)
|
|
{
|
|
// Replace counter with available alternative
|
|
it->second.counter = fallback;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: '{}' counter not supported by this GPU.", meta.name);
|
|
}
|
|
else
|
|
{
|
|
stat_data[stat] = hwcpipe_stats[stat];
|
|
|
|
LOGI("HWCPipe: enabled '{}' counter", meta.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove any supported stats from the requested set.
|
|
// Subsequent providers will then only look for things that aren't already supported.
|
|
for (const auto &iter : stat_data)
|
|
{
|
|
requested_stats.erase(iter.first);
|
|
}
|
|
|
|
stat_data_count = stat_data.size();
|
|
|
|
sampler = std::make_unique<hwcpipe::sampler<>>(config);
|
|
|
|
if (stat_data_count > 0)
|
|
{
|
|
ec = sampler->start_sampling();
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: {}", ec.message());
|
|
}
|
|
}
|
|
}
|
|
|
|
HWCPipeStatsProvider::~HWCPipeStatsProvider()
|
|
{
|
|
std::error_code ec;
|
|
|
|
if (stat_data_count > 0)
|
|
{
|
|
ec = sampler->stop_sampling();
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: {}", ec.message());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool HWCPipeStatsProvider::is_available(StatIndex index) const
|
|
{
|
|
return stat_data.find(index) != stat_data.end();
|
|
}
|
|
|
|
const StatGraphData &HWCPipeStatsProvider::get_graph_data(StatIndex index) const
|
|
{
|
|
assert(is_available(index) && "HWCPipeStatsProvider::get_graph_data() called with invalid StatIndex");
|
|
|
|
static StatGraphData vertex_compute_cycles{"Vertex Compute Cycles", "{:4.1f} M/s", static_cast<float>(1e-6)};
|
|
|
|
// HWCPipe reports combined vertex/compute cycles (which is Arm specific)
|
|
// Ensure we report graph with the correct name when asked for vertex cycles
|
|
if (index == StatIndex::gpu_vertex_cycles)
|
|
{
|
|
return vertex_compute_cycles;
|
|
}
|
|
|
|
return default_graph_map[index];
|
|
}
|
|
|
|
static double get_gpu_counter_value(const hwcpipe::counter_sample &sample)
|
|
{
|
|
switch (sample.type)
|
|
{
|
|
case hwcpipe::counter_sample::type::uint64:
|
|
return sample.value.uint64;
|
|
case hwcpipe::counter_sample::type::float64:
|
|
return sample.value.float64;
|
|
default:
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
StatsProvider::Counters HWCPipeStatsProvider::sample(float delta_time)
|
|
{
|
|
Counters res;
|
|
|
|
if (stat_data_count < 1)
|
|
{
|
|
return res;
|
|
}
|
|
|
|
std::error_code ec;
|
|
|
|
ec = sampler->sample_now();
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: {}", ec.message());
|
|
}
|
|
else
|
|
{
|
|
// Map from hwcpipe measurement to our sample result for each counter
|
|
for (auto iter : stat_data)
|
|
{
|
|
StatIndex index = iter.first;
|
|
const StatData &data = iter.second;
|
|
|
|
hwcpipe::counter_sample sample;
|
|
|
|
ec = sampler->get_counter_value(data.counter, sample);
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: {}", ec.message());
|
|
continue;
|
|
}
|
|
|
|
auto d = get_gpu_counter_value(sample);
|
|
|
|
if (data.scaling == StatScaling::ByDeltaTime && delta_time != 0.0f)
|
|
{
|
|
d /= delta_time;
|
|
}
|
|
else if (data.scaling == StatScaling::ByCounter)
|
|
{
|
|
ec = sampler->get_counter_value(data.divisor, sample);
|
|
if (ec)
|
|
{
|
|
LOGE("HWCPipe: {}", ec.message());
|
|
continue;
|
|
}
|
|
|
|
double divisor = get_gpu_counter_value(sample);
|
|
if (divisor != 0.0)
|
|
{
|
|
d /= divisor;
|
|
}
|
|
else
|
|
{
|
|
d = 0.0;
|
|
}
|
|
}
|
|
|
|
res[index].result = d;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
StatsProvider::Counters HWCPipeStatsProvider::continuous_sample(float delta_time)
|
|
{
|
|
return sample(delta_time);
|
|
}
|
|
|
|
} // namespace vkb
|