X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=futatabi%2Fjpeg_frame_view.cpp;h=c9b8090544b8313f7cf769812580fa4e11e0fd39;hb=fcae09355c1a00a68015b3d727339aebd2e52aa4;hp=059a3410586285ff230d6ebcc469846f428a4dc3;hpb=52336c086b8bc355b55e2046e3a055b1b4c70ef7;p=nageru diff --git a/futatabi/jpeg_frame_view.cpp b/futatabi/jpeg_frame_view.cpp index 059a341..c9b8090 100644 --- a/futatabi/jpeg_frame_view.cpp +++ b/futatabi/jpeg_frame_view.cpp @@ -4,6 +4,7 @@ #include "flags.h" #include "jpeg_destroyer.h" #include "jpeglib_error_wrapper.h" +#include "shared/metrics.h" #include "shared/post_to_main_thread.h" #include "video_stream.h" #include "ycbcr_converter.h" @@ -32,9 +33,8 @@ using namespace std; namespace { // Just an arbitrary order for std::map. -struct FrameOnDiskLexicalOrder -{ - bool operator() (const FrameOnDisk &a, const FrameOnDisk &b) const +struct FrameOnDiskLexicalOrder { + bool operator()(const FrameOnDisk &a, const FrameOnDisk &b) const { if (a.pts != b.pts) return a.pts < b.pts; @@ -72,6 +72,18 @@ struct PendingDecode { shared_ptr frame; }; +// There can be multiple JPEGFrameView instances, so make all the metrics static. +once_flag jpeg_metrics_inited; +atomic metric_jpeg_cache_used_bytes{ 0 }; // Same value as cache_bytes_used. +atomic metric_jpeg_cache_limit_bytes{ size_t(CACHE_SIZE_MB) * 1024 * 1024 }; +atomic metric_jpeg_cache_given_up_frames{ 0 }; +atomic metric_jpeg_cache_hit_frames{ 0 }; +atomic metric_jpeg_cache_miss_frames{ 0 }; +atomic metric_jpeg_software_decode_frames{ 0 }; +atomic metric_jpeg_software_fail_frames{ 0 }; +atomic metric_jpeg_vaapi_decode_frames{ 0 }; +atomic metric_jpeg_vaapi_fail_frames{ 0 }; + } // namespace thread JPEGFrameView::jpeg_decoder_thread; @@ -80,7 +92,7 @@ map cache; // Under cache_mu. size_t cache_bytes_used = 0; // Under cache_mu. condition_variable any_pending_decodes; deque pending_decodes; // Under cache_mu. -atomic event_counter{0}; +atomic event_counter{ 0 }; extern QGLWidget *global_share_widget; extern atomic should_quit; @@ -90,33 +102,35 @@ shared_ptr decode_jpeg(const string &jpeg) if (vaapi_jpeg_decoding_usable) { frame = decode_jpeg_vaapi(jpeg); if (frame != nullptr) { + ++metric_jpeg_vaapi_decode_frames; return frame; } fprintf(stderr, "VA-API hardware decoding failed; falling back to software.\n"); + ++metric_jpeg_vaapi_fail_frames; } frame.reset(new Frame); jpeg_decompress_struct dinfo; JPEGWrapErrorManager error_mgr(&dinfo); - if (!error_mgr.run([&dinfo]{ jpeg_create_decompress(&dinfo); })) { + if (!error_mgr.run([&dinfo] { jpeg_create_decompress(&dinfo); })) { return get_black_frame(); } JPEGDestroyer destroy_dinfo(&dinfo); - if (!error_mgr.run([&dinfo, &jpeg]{ - jpeg_mem_src(&dinfo, reinterpret_cast(jpeg.data()), jpeg.size()); - jpeg_read_header(&dinfo, true); - })) { + if (!error_mgr.run([&dinfo, &jpeg] { + jpeg_mem_src(&dinfo, reinterpret_cast(jpeg.data()), jpeg.size()); + jpeg_read_header(&dinfo, true); + })) { return get_black_frame(); } if (dinfo.num_components != 3) { fprintf(stderr, "Not a color JPEG. (%d components, Y=%dx%d, Cb=%dx%d, Cr=%dx%d)\n", - dinfo.num_components, - dinfo.comp_info[0].h_samp_factor, dinfo.comp_info[0].v_samp_factor, - dinfo.comp_info[1].h_samp_factor, dinfo.comp_info[1].v_samp_factor, - dinfo.comp_info[2].h_samp_factor, dinfo.comp_info[2].v_samp_factor); + dinfo.num_components, + dinfo.comp_info[0].h_samp_factor, dinfo.comp_info[0].v_samp_factor, + dinfo.comp_info[1].h_samp_factor, dinfo.comp_info[1].v_samp_factor, + dinfo.comp_info[2].h_samp_factor, dinfo.comp_info[2].v_samp_factor); return get_black_frame(); } if (dinfo.comp_info[0].h_samp_factor != dinfo.max_h_samp_factor || @@ -126,16 +140,16 @@ shared_ptr decode_jpeg(const string &jpeg) (dinfo.max_h_samp_factor % dinfo.comp_info[1].h_samp_factor) != 0 || (dinfo.max_v_samp_factor % dinfo.comp_info[1].v_samp_factor) != 0) { // No 2:3 subsampling or other weirdness. fprintf(stderr, "Unsupported subsampling scheme. (Y=%dx%d, Cb=%dx%d, Cr=%dx%d)\n", - dinfo.comp_info[0].h_samp_factor, dinfo.comp_info[0].v_samp_factor, - dinfo.comp_info[1].h_samp_factor, dinfo.comp_info[1].v_samp_factor, - dinfo.comp_info[2].h_samp_factor, dinfo.comp_info[2].v_samp_factor); + dinfo.comp_info[0].h_samp_factor, dinfo.comp_info[0].v_samp_factor, + dinfo.comp_info[1].h_samp_factor, dinfo.comp_info[1].v_samp_factor, + dinfo.comp_info[2].h_samp_factor, dinfo.comp_info[2].v_samp_factor); exit(1); } dinfo.raw_data_out = true; - if (!error_mgr.run([&dinfo]{ - jpeg_start_decompress(&dinfo); - })) { + if (!error_mgr.run([&dinfo] { + jpeg_start_decompress(&dinfo); + })) { return get_black_frame(); } @@ -162,24 +176,25 @@ shared_ptr decode_jpeg(const string &jpeg) frame->pitch_chroma = chroma_width_blocks * DCTSIZE; if (!error_mgr.run([&dinfo, &frame, v_mcu_size, mcu_height_blocks] { - JSAMPROW yptr[v_mcu_size], cbptr[v_mcu_size], crptr[v_mcu_size]; - JSAMPARRAY data[3] = { yptr, cbptr, crptr }; - for (unsigned y = 0; y < mcu_height_blocks; ++y) { - // NOTE: The last elements of cbptr/crptr will be unused for vertically subsampled chroma. - for (unsigned yy = 0; yy < v_mcu_size; ++yy) { - yptr[yy] = frame->y.get() + (y * DCTSIZE * dinfo.max_v_samp_factor + yy) * frame->pitch_y; - cbptr[yy] = frame->cb.get() + (y * DCTSIZE * dinfo.comp_info[1].v_samp_factor + yy) * frame->pitch_chroma; - crptr[yy] = frame->cr.get() + (y * DCTSIZE * dinfo.comp_info[1].v_samp_factor + yy) * frame->pitch_chroma; - } - - jpeg_read_raw_data(&dinfo, data, v_mcu_size); - } - - (void)jpeg_finish_decompress(&dinfo); - })) { + JSAMPROW yptr[v_mcu_size], cbptr[v_mcu_size], crptr[v_mcu_size]; + JSAMPARRAY data[3] = { yptr, cbptr, crptr }; + for (unsigned y = 0; y < mcu_height_blocks; ++y) { + // NOTE: The last elements of cbptr/crptr will be unused for vertically subsampled chroma. + for (unsigned yy = 0; yy < v_mcu_size; ++yy) { + yptr[yy] = frame->y.get() + (y * DCTSIZE * dinfo.max_v_samp_factor + yy) * frame->pitch_y; + cbptr[yy] = frame->cb.get() + (y * DCTSIZE * dinfo.comp_info[1].v_samp_factor + yy) * frame->pitch_chroma; + crptr[yy] = frame->cr.get() + (y * DCTSIZE * dinfo.comp_info[1].v_samp_factor + yy) * frame->pitch_chroma; + } + + jpeg_read_raw_data(&dinfo, data, v_mcu_size); + } + + (void)jpeg_finish_decompress(&dinfo); + })) { return get_black_frame(); } + ++metric_jpeg_software_decode_frames; return frame; } @@ -187,7 +202,8 @@ void prune_cache() { // Assumes cache_mu is held. int64_t bytes_still_to_remove = cache_bytes_used - (size_t(CACHE_SIZE_MB) * 1024 * 1024) * 9 / 10; - if (bytes_still_to_remove <= 0) return; + if (bytes_still_to_remove <= 0) + return; vector> lru_timestamps_and_size; for (const auto &key_and_value : cache) { @@ -202,12 +218,14 @@ void prune_cache() for (const pair &it : lru_timestamps_and_size) { lru_cutoff_point = it.first; bytes_still_to_remove -= it.second; - if (bytes_still_to_remove <= 0) break; + if (bytes_still_to_remove <= 0) + break; } - for (auto it = cache.begin(); it != cache.end(); ) { + for (auto it = cache.begin(); it != cache.end();) { if (it->second.last_used <= lru_cutoff_point) { cache_bytes_used -= frame_size(*it->second.frame); + metric_jpeg_cache_used_bytes = cache_bytes_used; it = cache.erase(it); } else { ++it; @@ -219,23 +237,28 @@ shared_ptr decode_jpeg_with_cache(FrameOnDisk frame_spec, CacheMissBehavi { *did_decode = false; { - unique_lock lock(cache_mu); + lock_guard lock(cache_mu); auto it = cache.find(frame_spec); if (it != cache.end()) { + ++metric_jpeg_cache_hit_frames; it->second.last_used = event_counter++; return it->second.frame; } } if (cache_miss_behavior == RETURN_NULLPTR_IF_NOT_IN_CACHE) { + ++metric_jpeg_cache_given_up_frames; return nullptr; } + ++metric_jpeg_cache_miss_frames; + *did_decode = true; shared_ptr frame = decode_jpeg(frame_reader->read_frame(frame_spec)); - unique_lock lock(cache_mu); + lock_guard lock(cache_mu); cache_bytes_used += frame_size(*frame); + metric_jpeg_cache_used_bytes = cache_bytes_used; cache[frame_spec] = LRUFrame{ frame, event_counter++ }; if (cache_bytes_used > size_t(CACHE_SIZE_MB) * 1024 * 1024) { @@ -301,7 +324,7 @@ void JPEGFrameView::jpeg_decoder_thread_func() ++num_decoded; if (num_decoded % 1000 == 0) { fprintf(stderr, "Decoded %zu images, dropped %zu (%.2f%% dropped)\n", - num_decoded, num_dropped, (100.0 * num_dropped) / (num_decoded + num_dropped)); + num_decoded, num_dropped, (100.0 * num_dropped) / (num_decoded + num_dropped)); } } if (subframe_idx == 0) { @@ -329,13 +352,24 @@ void JPEGFrameView::shutdown() JPEGFrameView::JPEGFrameView(QWidget *parent) : QGLWidget(parent, global_share_widget) { + call_once(jpeg_metrics_inited, [] { + global_metrics.add("jpeg_cache_used_bytes", &metric_jpeg_cache_used_bytes, Metrics::TYPE_GAUGE); + global_metrics.add("jpeg_cache_limit_bytes", &metric_jpeg_cache_limit_bytes, Metrics::TYPE_GAUGE); + global_metrics.add("jpeg_cache_frames", { { "action", "given_up" } }, &metric_jpeg_cache_given_up_frames); + global_metrics.add("jpeg_cache_frames", { { "action", "hit" } }, &metric_jpeg_cache_hit_frames); + global_metrics.add("jpeg_cache_frames", { { "action", "miss" } }, &metric_jpeg_cache_miss_frames); + global_metrics.add("jpeg_decode_frames", { { "decoder", "software" }, { "result", "decode" } }, &metric_jpeg_software_decode_frames); + global_metrics.add("jpeg_decode_frames", { { "decoder", "software" }, { "result", "fail" } }, &metric_jpeg_software_fail_frames); + global_metrics.add("jpeg_decode_frames", { { "decoder", "vaapi" }, { "result", "decode" } }, &metric_jpeg_vaapi_decode_frames); + global_metrics.add("jpeg_decode_frames", { { "decoder", "vaapi" }, { "result", "fail" } }, &metric_jpeg_vaapi_fail_frames); + }); } void JPEGFrameView::setFrame(unsigned stream_idx, FrameOnDisk frame, FrameOnDisk secondary_frame, float fade_alpha) { current_stream_idx = stream_idx; // TODO: Does this interact with fades? - unique_lock lock(cache_mu); + lock_guard lock(cache_mu); PendingDecode decode; decode.primary = frame; decode.secondary = secondary_frame; @@ -347,7 +381,7 @@ void JPEGFrameView::setFrame(unsigned stream_idx, FrameOnDisk frame, FrameOnDisk void JPEGFrameView::setFrame(shared_ptr frame) { - unique_lock lock(cache_mu); + lock_guard lock(cache_mu); PendingDecode decode; decode.frame = std::move(frame); decode.destination = this; @@ -485,5 +519,6 @@ shared_ptr get_black_frame() black_frame->pitch_y = global_flags.width; black_frame->pitch_chroma = global_flags.width / 2; }); + ++metric_jpeg_software_fail_frames; return black_frame; }