diff options
author | Mikhail Burakov <mburakov@mailbox.org> | 2023-03-24 09:16:38 +0100 |
---|---|---|
committer | Mikhail Burakov <mburakov@mailbox.org> | 2023-03-24 09:16:38 +0100 |
commit | 60088b93999c1e93617a7221e571460c2e7d2883 (patch) | |
tree | 7472ebc65532b1c32f0516c875b97613f320fce4 | |
parent | 88573d1d0a4f89807bab33a45dea0658e37c1af0 (diff) |
More precise and detailed performance measurements
-rw-r--r-- | encode.c | 24 | ||||
-rw-r--r-- | encode.h | 5 | ||||
-rw-r--r-- | main.c | 84 | ||||
-rw-r--r-- | perf.c | 48 | ||||
-rw-r--r-- | perf.h | 35 |
5 files changed, 145 insertions, 51 deletions
@@ -31,6 +31,7 @@ #include <va/va_drmcommon.h> #include "gpu.h" +#include "perf.h" #include "util.h" #define AVBufferRefDestroy av_buffer_unref @@ -125,9 +126,10 @@ struct EncodeContext* EncodeContextCreate(struct GpuContext* gpu_context, return NULL; } - const AVCodec* codec = avcodec_find_encoder_by_name("h264_vaapi"); + static const char codec_name[] = "h264_vaapi"; + const AVCodec* codec = avcodec_find_encoder_by_name(codec_name); if (!codec) { - LOG("Failed to find h264_vaapi encoder"); + LOG("Failed to find %s encoder", codec_name); return NULL; } @@ -143,7 +145,7 @@ struct EncodeContext* EncodeContextCreate(struct GpuContext* gpu_context, encode_context->codec_context->pix_fmt = AV_PIX_FMT_VAAPI; encode_context->codec_context->max_b_frames = 0; encode_context->codec_context->refs = 1; - encode_context->codec_context->global_quality = 18; + encode_context->codec_context->global_quality = 32; encode_context->codec_context->colorspace = ConvertColorspace(colrospace); encode_context->codec_context->color_range = ConvertRange(range); @@ -250,7 +252,10 @@ static bool DrainPacket(const struct AVPacket* packet, int fd) { } } -bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd) { +bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd, + struct TimingStats* encode, + struct TimingStats* drain) { + unsigned long long before_send = MicrosNow(); GpuFrameDestroy(&encode_context->gpu_frame); AUTO(AVFrame)* hw_frame = RELEASE(encode_context->hw_frame); AUTO(AVPacket)* packet = av_packet_alloc(); @@ -265,13 +270,20 @@ bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd) { return false; } + unsigned long long total_send = MicrosNow() - before_send; + unsigned long long total_receive = 0; + unsigned long long total_drain = 0; for (;;) { + unsigned long long before_receive = MicrosNow(); err = avcodec_receive_packet(encode_context->codec_context, packet); switch (err) { case 0: break; case AVERROR(EAGAIN): case AVERROR_EOF: + total_receive += MicrosNow() - before_receive; + if (encode) TimingStatsRecord(encode, total_send + total_receive); + if (drain) TimingStatsRecord(drain, total_drain); return true; default: LOG("Failed to receive packet (%s)", av_err2str(err)); @@ -279,12 +291,16 @@ bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd) { } packet->stream_index = 0; + unsigned long long before_drain = MicrosNow(); bool result = DrainPacket(packet, fd); av_packet_unref(packet); if (!result) { LOG("Failed to write full packet (%s)", strerror(errno)); return false; } + + total_receive += before_drain - before_receive; + total_drain += MicrosNow() - before_drain; } } @@ -26,6 +26,7 @@ struct EncodeContext; struct GpuContext; struct GpuFrame; +struct TimingStats; struct EncodeContext* EncodeContextCreate(struct GpuContext* gpu_context, uint32_t width, uint32_t height, @@ -33,7 +34,9 @@ struct EncodeContext* EncodeContextCreate(struct GpuContext* gpu_context, enum YuvRange range); const struct GpuFrame* EncodeContextGetFrame( struct EncodeContext* encode_context); -bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd); +bool EncodeContextEncodeFrame(struct EncodeContext* encode_context, int fd, + struct TimingStats* encode, + struct TimingStats* drain); void EncodeContextDestroy(struct EncodeContext** encode_context); #endif // STREAMER_ENCODE_H_ @@ -16,18 +16,17 @@ */ #include <errno.h> -#include <limits.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> #include <unistd.h> #include "capture.h" #include "colorspace.h" #include "encode.h" #include "gpu.h" +#include "perf.h" #include "util.h" // TODO(mburakov): Currently zwp_linux_dmabuf_v1 has no way to provide @@ -40,32 +39,6 @@ static const enum YuvRange range = kNarrowRange; static volatile sig_atomic_t g_signal; static void OnSignal(int status) { g_signal = status; } -struct TimingStats { - unsigned long long min; - unsigned long long max; - unsigned long long sum; -}; - -static void TimingStatsRecord(struct TimingStats* timing_stats, - unsigned long long value) { - timing_stats->min = MIN(timing_stats->min, value); - timing_stats->max = MAX(timing_stats->max, value); - timing_stats->sum += value; -} - -static void TimingStatsLog(const struct TimingStats* timing_stats, - const char* name, unsigned long long counter) { - LOG("%s min/avg/max: %llu/%llu/%llu", name, timing_stats->min, - timing_stats->sum / counter, timing_stats->max); -} - -static unsigned long long MicrosNow(void) { - struct timespec ts = {.tv_sec = 0, .tv_nsec = 0}; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (unsigned long long)ts.tv_sec * 1000000ull + - (unsigned long long)ts.tv_nsec / 1000ull; -} - int main(int argc, char* argv[]) { (void)argc; (void)argv; @@ -92,12 +65,19 @@ int main(int argc, char* argv[]) { struct AUTO(EncodeContext)* encode_context = NULL; - struct TimingStats capture = {.min = ULLONG_MAX}; - struct TimingStats convert = {.min = ULLONG_MAX}; - struct TimingStats encode = {.min = ULLONG_MAX}; - struct TimingStats total = {.min = ULLONG_MAX}; + struct TimingStats capture; + struct TimingStats convert; + struct TimingStats encode; + struct TimingStats drain; + struct TimingStats total; + TimingStatsReset(&capture); + TimingStatsReset(&convert); + TimingStatsReset(&encode); + TimingStatsReset(&drain); + TimingStatsReset(&total); unsigned long long num_frames = 0; + unsigned long long recording_started = MicrosNow(); static const unsigned long long delta = 1000000ull / 60ull; for (unsigned long long next = MicrosNow() + delta; !g_signal; next += delta) { @@ -135,32 +115,44 @@ int main(int argc, char* argv[]) { GpuContextSync(gpu_context); unsigned long long before_encode = MicrosNow(); - if (!EncodeContextEncodeFrame(encode_context, STDOUT_FILENO)) { + if (!EncodeContextEncodeFrame(encode_context, STDOUT_FILENO, &encode, + &drain)) { LOG("Failed to encode frame"); return EXIT_FAILURE; } unsigned long long now = MicrosNow(); - if (num_frames++) { - // mburakov: Do not record stats for first (lazy) frame. - TimingStatsRecord(&capture, before_convert - before_capture); - TimingStatsRecord(&convert, before_encode - before_convert); - TimingStatsRecord(&encode, now - before_encode); - TimingStatsRecord(&total, now - before_capture); + TimingStatsRecord(&capture, before_convert - before_capture); + TimingStatsRecord(&convert, before_encode - before_convert); + TimingStatsRecord(&total, now - before_capture); + + unsigned long long period = now - recording_started; + static const unsigned long long second = 1000000; + if (period > 10 * second) { + LOG("---->8-------->8-------->8----"); + TimingStatsLog(&capture, "Capture", num_frames); + TimingStatsLog(&convert, "Convert", num_frames); + TimingStatsLog(&encode, "Encode", num_frames); + TimingStatsLog(&drain, "Drain", num_frames); + TimingStatsLog(&total, "Total", num_frames); + TimingStatsReset(&capture); + TimingStatsReset(&convert); + TimingStatsReset(&encode); + TimingStatsReset(&drain); + TimingStatsReset(&total); + recording_started = now; + num_frames = 0; } + + now = MicrosNow(); unsigned long long micros = now < next ? next - now : 0; if (micros) usleep((unsigned)micros); + num_frames++; } - if (!EncodeContextEncodeFrame(encode_context, STDOUT_FILENO)) { + if (!EncodeContextEncodeFrame(encode_context, STDOUT_FILENO, NULL, NULL)) { LOG("Failed to drain encoder"); return EXIT_FAILURE; } - - num_frames--; - TimingStatsLog(&capture, "Capture", num_frames); - TimingStatsLog(&convert, "Convert", num_frames); - TimingStatsLog(&encode, "Encode", num_frames); - TimingStatsLog(&total, "Total", num_frames); return EXIT_SUCCESS; } @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Mikhail Burakov. This file is part of streamer. + * + * streamer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * streamer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with streamer. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "perf.h" + +#include <limits.h> +#include <stdio.h> +#include <time.h> + +#include "util.h" + +unsigned long long MicrosNow(void) { + struct timespec ts = {.tv_sec = 0, .tv_nsec = 0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (unsigned long long)ts.tv_sec * 1000000ull + + (unsigned long long)ts.tv_nsec / 1000ull; +} + +void TimingStatsReset(struct TimingStats* timing_stats) { + *timing_stats = (struct TimingStats){.min = ULLONG_MAX}; +} + +void TimingStatsRecord(struct TimingStats* timing_stats, + unsigned long long value) { + timing_stats->min = MIN(timing_stats->min, value); + timing_stats->max = MAX(timing_stats->max, value); + timing_stats->sum += value; +} + +void TimingStatsLog(const struct TimingStats* timing_stats, const char* name, + unsigned long long counter) { + LOG("%s min/avg/max: %llu/%llu/%llu", name, timing_stats->min, + timing_stats->sum / counter, timing_stats->max); +} @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Mikhail Burakov. This file is part of streamer. + * + * streamer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * streamer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with streamer. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef STREAMER_PERF_H_ +#define STREAMER_PERF_H_ + +struct TimingStats { + unsigned long long min; + unsigned long long max; + unsigned long long sum; +}; + +unsigned long long MicrosNow(void); + +void TimingStatsReset(struct TimingStats* timing_stats); +void TimingStatsRecord(struct TimingStats* timing_stats, + unsigned long long value); +void TimingStatsLog(const struct TimingStats* timing_stats, const char* name, + unsigned long long counter); + +#endif // STREAMER_PERF_H_ |