From f758e324e17b52116075bb9175a3dd03d223a424 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Sun, 2 Feb 2025 11:26:23 +0200 Subject: kms++util: New template based conversion & testpat code New templated conversion and test pattern code. Signed-off-by: Tomi Valkeinen --- kms++util/src/testpat.cpp | 301 ++++++++++++++++++++++++++++++---------------- 1 file changed, 197 insertions(+), 104 deletions(-) (limited to 'kms++util/src/testpat.cpp') diff --git a/kms++util/src/testpat.cpp b/kms++util/src/testpat.cpp index 029684a..05efeab 100644 --- a/kms++util/src/testpat.cpp +++ b/kms++util/src/testpat.cpp @@ -1,8 +1,11 @@ -//#define DRAW_PERF_PRINT - #include -#include +#include +#include +#include +#include +#include +#include #ifdef HAS_PTHREAD #include @@ -11,15 +14,14 @@ #include #include +#include "conv.h" + using namespace std; namespace kms { -static RGB get_test_pattern_pixel(IFramebuffer& fb, unsigned x, unsigned y) +static RGB16 get_test_pattern_pixel_16(size_t w, size_t h, size_t x, size_t y) { - const unsigned w = fb.width(); - const unsigned h = fb.height(); - const unsigned mw = 20; const unsigned xm1 = mw; @@ -29,36 +31,36 @@ static RGB get_test_pattern_pixel(IFramebuffer& fb, unsigned x, unsigned y) // white margin lines if (x == xm1 || x == xm2 || y == ym1 || y == ym2) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); // white box in top left corner else if (x < xm1 && y < ym1) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); // white box outlines to corners else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); // white box outlines to corners else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); // blue bar on the left else if (x < xm1 && (y > ym1 && y < ym2)) - return RGB(0, 0, 255); + return RGB16::from_8(0, 0, 255); // blue bar on the top else if (y < ym1 && (x > xm1 && x < xm2)) - return RGB(0, 0, 255); + return RGB16::from_8(0, 0, 255); // red bar on the right else if (x > xm2 && (y > ym1 && y < ym2)) - return RGB(255, 0, 0); + return RGB16::from_8(255, 0, 0); // red bar on the bottom else if (y > ym2 && (x > xm1 && x < xm2)) - return RGB(255, 0, 0); + return RGB16::from_8(255, 0, 0); // inside the margins else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { // diagonal line if (x == y || w - x == h - y) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); // diagonal line else if (w - x - 1 == y || x == h - y - 1) - return RGB(255, 255, 255); + return RGB16::from_8(255, 255, 255); else { int t = (x - xm1 - 1) * 8 / (xm2 - xm1 - 1); unsigned r = 0, g = 0, b = 0; @@ -91,108 +93,203 @@ static RGB get_test_pattern_pixel(IFramebuffer& fb, unsigned x, unsigned y) break; } - return RGB(r, g, b); + return RGB16::from_8(r, g, b); } } else { // black corners - return RGB(0, 0, 0); + return RGB16::from_8(0, 0, 0); } } -static void draw_test_pattern_part(IFramebuffer& fb, unsigned start_y, unsigned end_y, YUVType yuvt) +static void get_test_pattern_line(size_t w, size_t h, size_t row, std::span buf) { - unsigned x, y; - unsigned w = fb.width(); - - const PixelFormatInfo& format_info = get_pixel_format_info(fb.format()); - const PixelFormatPlaneInfo& plane_info = format_info.planes[format_info.num_planes - 1]; - - switch (format_info.type) { - case PixelColorType::RGB: - for (y = start_y; y < end_y; y++) { - for (x = 0; x < w; x++) { - RGB pixel = get_test_pattern_pixel(fb, x, y); - draw_rgb_pixel(fb, x, y, pixel); - } - } + for (size_t x = 0; x < w; ++x) + buf[x] = get_test_pattern_pixel_16(w, h, x, row); +} + +static void get_test_pattern_line_yuv(size_t w, size_t h, size_t row, + std::span buf, + const TestPatternOptions& options) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = get_test_pattern_pixel_16(w, h, x, row) + .to_yuv(options.rec, options.range); +} + +static void get_plain_line_rgb(size_t w, const RGB16& color, std::span buf) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = color; +} + +static void get_plain_line_yuv(size_t w, const YUV16& color, std::span buf) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = color; +} + +static void draw_test_pattern_part(IFramebuffer& fb, size_t start_y, size_t end_y, + const TestPatternOptions& options) +{ + std::optional solid; + + if (options.pattern == "red") + solid = RGB16(0xffff, 0, 0); + else if (options.pattern == "green") + solid = RGB16(0, 0xffff, 0); + else if (options.pattern == "blue") + solid = RGB16(0, 0, 0xffff); + else if (options.pattern == "white") + solid = RGB16(0xffff, 0xffff, 0xffff); + else if (options.pattern == "black") + solid = RGB16(0, 0, 0); + + std::function span)> generate_line_rgb; + std::function span)> generate_line_yuv; + + if (solid.has_value()) { + generate_line_rgb = [&fb, rgb = solid.value()](size_t y, + std::span span) { + get_plain_line_rgb(fb.width(), rgb, span); + }; + + generate_line_yuv = [&fb, rgb = solid.value(), + &options](size_t y, std::span span) { + get_plain_line_yuv(fb.width(), + rgb.to_yuv(options.rec, options.range), span); + }; + } else { + generate_line_rgb = [&fb](size_t y, std::span span) { + get_test_pattern_line(fb.width(), fb.height(), y, span); + }; + + generate_line_yuv = [&fb, &options](size_t y, std::span span) { + get_test_pattern_line_yuv(fb.width(), fb.height(), y, span, + options); + }; + } + +#define CASE_ARGB(x) \ + case PixelFormat::x: \ + ARGB_Writer::write_pattern(fb, start_y, end_y, \ + generate_line_rgb); \ break; - case PixelColorType::YUV: - switch (plane_info.hsub + plane_info.vsub) { - case 2: - for (y = start_y; y < end_y; y++) { - for (x = 0; x < w; x++) { - RGB pixel = get_test_pattern_pixel(fb, x, y); - draw_yuv444_pixel(fb, x, y, pixel.yuv(yuvt)); - } - } - break; - - case 3: - for (y = start_y; y < end_y; y++) { - for (x = 0; x < w; x += 2) { - RGB pixel1 = get_test_pattern_pixel(fb, x, y); - RGB pixel2 = get_test_pattern_pixel(fb, x + 1, y); - draw_yuv422_macropixel(fb, x, y, pixel1.yuv(yuvt), pixel2.yuv(yuvt)); - } - } - break; - - case 4: - for (y = start_y; y < end_y; y += 2) { - for (x = 0; x < w; x += 2) { - RGB pixel00 = get_test_pattern_pixel(fb, x, y); - RGB pixel10 = get_test_pattern_pixel(fb, x + 1, y); - RGB pixel01 = get_test_pattern_pixel(fb, x, y + 1); - RGB pixel11 = get_test_pattern_pixel(fb, x + 1, y + 1); - draw_yuv420_macropixel(fb, x, y, - pixel00.yuv(yuvt), pixel10.yuv(yuvt), - pixel01.yuv(yuvt), pixel11.yuv(yuvt)); - } - } - break; +#define CASE_YUV(x) \ + case PixelFormat::x: \ + YUV_Writer::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; - default: - throw invalid_argument("unsupported number of pixel format planes"); - } +#define CASE_YUV_PACKED(x) \ + case PixelFormat::x: \ + YUVPackedWriter::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; +#define CASE_YUV_SEMI(x) \ + case PixelFormat::x: \ + YUVSemiPlanarWriter::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ break; +#define CASE_YUV_PLANAR(x) \ + case PixelFormat::x: \ + YUVPlanarWriter::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + + switch (fb.format()) { + CASE_YUV_SEMI(XV20); + CASE_YUV_SEMI(XV15); + CASE_YUV_SEMI(NV12); + CASE_YUV_SEMI(NV21); + CASE_YUV_SEMI(NV16); + CASE_YUV_SEMI(NV61); + + CASE_ARGB(RGB565); + CASE_ARGB(BGR565); + + CASE_ARGB(XRGB1555); + CASE_ARGB(ARGB1555); + CASE_ARGB(XRGB4444); + CASE_ARGB(ARGB4444); + + CASE_ARGB(RGB888); + CASE_ARGB(BGR888); + + CASE_ARGB(XRGB8888); + CASE_ARGB(ARGB8888); + CASE_ARGB(XBGR8888); + CASE_ARGB(ABGR8888); + CASE_ARGB(RGBX8888); + CASE_ARGB(RGBA8888); + CASE_ARGB(BGRX8888); + CASE_ARGB(BGRA8888); + CASE_ARGB(XRGB2101010); + CASE_ARGB(ARGB2101010); + CASE_ARGB(XBGR2101010); + CASE_ARGB(ABGR2101010); + CASE_ARGB(RGBX1010102); + CASE_ARGB(RGBA1010102); + CASE_ARGB(BGRX1010102); + CASE_ARGB(BGRA1010102); + + CASE_YUV_PACKED(YUYV); + CASE_YUV_PACKED(YVYU); + CASE_YUV_PACKED(UYVY); + CASE_YUV_PACKED(VYUY); + + CASE_YUV(XVUY2101010); + + CASE_YUV_PLANAR(YUV444); + CASE_YUV_PLANAR(YVU444); + CASE_YUV_PLANAR(YUV422); + CASE_YUV_PLANAR(YVU422); + CASE_YUV_PLANAR(YUV420); + CASE_YUV_PLANAR(YVU420); + default: - throw invalid_argument("unsupported pixel format"); + break; } } -static void draw_test_pattern_impl(IFramebuffer& fb, YUVType yuvt) +void draw_test_pattern_multi(IFramebuffer& fb, const TestPatternOptions& options) { -#ifdef HAS_PTHREAD - if (fb.height() < 20) { - draw_test_pattern_part(fb, 0, fb.height(), yuvt); - return; - } + auto& info = get_pixel_format_info(fb.format()); + uint8_t v_sub = 0; + for (size_t p = 0; p < info.num_planes; ++p) + v_sub = max(v_sub, info.planes[p].vsub); + + if (fb.height() % v_sub) + throw invalid_argument("FB height must be divisible with vsub"); // Create the mmaps before starting the threads - for (unsigned i = 0; i < fb.num_planes(); ++i) + for (size_t i = 0; i < fb.num_planes(); ++i) fb.map(i); - unsigned num_threads = thread::hardware_concurrency(); - vector workers; + size_t num_threads = thread::hardware_concurrency(); + + size_t part_height = fb.height() / num_threads; - unsigned part = (fb.height() / num_threads) & ~1; + // round up to v_sub + part_height = (part_height + v_sub - 1) / v_sub * v_sub; + + vector workers; std::vector errors(num_threads); - for (unsigned n = 0; n < num_threads; ++n) { - unsigned start = n * part; - unsigned end = start + part; + for (size_t n = 0; n < num_threads; ++n) { + size_t start = n * part_height; + size_t end = start + part_height - 1; if (n == num_threads - 1) - end = fb.height(); + end = fb.height() - 1; - workers.push_back(thread([&fb, start, end, yuvt, &error = errors[n]]() { + workers.push_back(thread([&fb, start, end, &options, &error = errors[n]]() { try { - draw_test_pattern_part(fb, start, end, yuvt); - } catch(...) { + draw_test_pattern_part(fb, start, end, options); + } catch (...) { error = std::current_exception(); } })); @@ -201,27 +298,23 @@ static void draw_test_pattern_impl(IFramebuffer& fb, YUVType yuvt) for (thread& t : workers) t.join(); - auto i = std::find_if(errors.begin(), errors.end(), [](auto& e) { return e != nullptr; }); + auto i = std::find_if(errors.begin(), errors.end(), + [](auto& e) { return e != nullptr; }); if (i != errors.end()) std::rethrow_exception(*i); - -#else - draw_test_pattern_part(fb, 0, fb.height(), yuvt); -#endif } -void draw_test_pattern(IFramebuffer& fb, YUVType yuvt) +void draw_test_pattern_single(IFramebuffer& fb, const TestPatternOptions& options) { -#ifdef DRAW_PERF_PRINT - Stopwatch sw; - sw.start(); -#endif - - draw_test_pattern_impl(fb, yuvt); + draw_test_pattern_part(fb, 0, fb.height() - 1, options); +} -#ifdef DRAW_PERF_PRINT - double us = sw.elapsed_us(); - printf("draw took %u us\n", (unsigned)us); +void draw_test_pattern(IFramebuffer& fb, const TestPatternOptions& options) +{ +#ifdef HAS_PTHREAD + draw_test_pattern_multi(fb, options); +#else + draw_test_pattern_single(fb, options); #endif } -- cgit v1.2.3