odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

bl_test_context_jit.cpp (11797B)


      1 // This file is part of Blend2D project <https://blend2d.com>
      2 //
      3 // See blend2d.h or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #include <blend2d.h>
      7 #include <cmath>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 
     12 #include "bl_test_context_baseapp.h"
     13 #include "bl_test_context_utilities.h"
     14 
     15 #include "../commons/imagediff.h"
     16 
     17 namespace ContextTests {
     18 
     19 #if defined(_M_X64) || defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(_M_IX86) || defined(__i386) || defined(__i386__)
     20   #define BL_JIT_ARCH_X86
     21 #elif defined(_M_ARM64) || defined(__ARM64__) || defined(__aarch64__)
     22   #define BL_JIT_ARCH_A64
     23 #endif
     24 
     25 class JITTestApp : public BaseTestApp {
     26 public:
     27   BLString _cpu_features_string;
     28 
     29   bool iterate_all_jit_features = false;
     30   uint32_t selected_cpu_features {};
     31   uint32_t maximum_pixel_difference = 0xFFFFFFFFu;
     32 
     33   uint32_t failed_count {};
     34   uint32_t passed_count {};
     35 
     36   JITTestApp()
     37     : BaseTestApp() {}
     38 
     39   int help() {
     40     using StringUtils::bool_to_string;
     41 
     42     printf("Usage:\n");
     43     printf("  bl_test_context_jit [options] [--help for help]\n");
     44     printf("\n");
     45 
     46     printf("Purpose:\n");
     47     printf("  JIT rendering context tester is designed to verify whether JIT-compiled\n");
     48     printf("  pipelines and reference pipelines yield pixel identical output when used\n");
     49     printf("  with the same input data. In addition, JIT rendering context tester verifies\n");
     50     printf("  whether all JIT compiled pipelines used by tests are actually compiled\n");
     51     printf("  successfully.\n");
     52     printf("\n");
     53     printf("  Blend2D's JIT compiler provides optimizations for various SIMD levels of the\n");
     54     printf("  supported architectures. For example X86 SIMD level could vary from SSE2 to\n");
     55     printf("  AVX-512+VBMI. The purpose of the tester is not just testing a single SIMD\n");
     56     printf("  level, but to offer to possibly testing ALL of them via command line options.\n");
     57     printf("\n");
     58     printf("Remarks:\n");
     59     printf("  Blend2D tries to use FMA when available, which means that rendering\n");
     60     printf("  styles that rely on floating point can end up different thanks to\n");
     61     printf("  rounding (FMA operation rounds only once, mul+add twice). Tests are\n");
     62     printf("  written in a way to make the difference minimal, but it's still there.\n");
     63     printf("\n");
     64     printf("  To counter it, use --max-diff=2 when testing the rendering of radial\n");
     65     printf("  and conical gradients. Other styles don't use floating point calculations\n");
     66     printf("  during fetching so they must yield identical results.\n");
     67     printf("\n");
     68 
     69     print_common_options(default_options);
     70 
     71     printf("JIT options:\n");
     72     printf("  --max-diff=<value>      - Maximum pixel difference allowed  [default=auto]\n");
     73     printf("  --simd-level=<name>     - SIMD level                        [default=all]\n");
     74     printf("\n");
     75 
     76 #if defined(BL_JIT_ARCH_X86)
     77     printf("JIT SIMD levels (X86 and X86_64):\n");
     78     printf("  sse2                    - Enables SSE2      (x86 baseline)  [128-bit SIMD]\n");
     79     printf("  sse3                    - Enables SSE3      (if available)  [128-bit SIMD]\n");
     80     printf("  ssse3                   - Enables SSSE3     (if available)  [128-bit SIMD]\n");
     81     printf("  sse4.1                  - Enables SSE4.1    (if available)  [128-bit SIMD]\n");
     82     printf("  sse4.2                  - Enables SSE4.2    (if available)  [128-bit SIMD]\n");
     83     printf("  avx                     - Enables AVX       (if available)  [128-bit SIMD]\n");
     84     printf("  avx2                    - Enables AVX2      (if available)  [256-bit SIMD]\n");
     85     printf("  avx512                  - Enables AVX512    (F|CD|BW|DQ|VL) [512-bit SIMD]\n");
     86 #elif defined(BL_JIT_ARCH_A64)
     87     printf("JIT SIMD levels (AArch64):\n");
     88     printf("  asimd                   - Enables ASIMD     (a64 baseline)  [128-bit SIMD]\n");
     89 #else
     90     printf("JIT SIMD levels (unknown architecture!):\n");
     91 #endif
     92     printf("  native                  - Use a native SIMD level as detected by Blend2D\n");
     93     printf("  all                     - Executes all available SIMD levels\n");
     94     printf("\n");
     95 
     96     print_commands();
     97     print_formats();
     98     print_comp_ops();
     99     print_opacity_ops();
    100     print_style_ids();
    101     print_style_ops();
    102 
    103     fflush(stdout);
    104     return 0;
    105   }
    106 
    107   void reset_counters() {
    108     mismatch_count = 0;
    109   }
    110 
    111   bool parse_jit_options(CmdLine cmd_line) {
    112     const char* max_diff_value = cmd_line.value_of("--max-diff", "auto");
    113     if (strcmp(max_diff_value, "auto") == 0) {
    114       maximum_pixel_difference = 0xFFFFFFFFu;
    115     }
    116     else {
    117       maximum_pixel_difference = cmd_line.value_as_uint("--max-diff", 0);
    118     }
    119 
    120     const char* simd_level = cmd_line.value_of("--simd-level", "all");
    121     if (simd_level) {
    122       if (StringUtils::strieq(simd_level, "native")) {
    123         // Nothing to do if configured to auto-detect.
    124       }
    125       else if (StringUtils::strieq(simd_level, "all")) {
    126         iterate_all_jit_features = true;
    127       }
    128 #if defined(BL_JIT_ARCH_X86)
    129       else if (StringUtils::strieq(simd_level, "sse2")) {
    130         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_SSE2;
    131       }
    132       else if (StringUtils::strieq(simd_level, "sse3")) {
    133         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_SSE3;
    134       }
    135       else if (StringUtils::strieq(simd_level, "ssse3")) {
    136         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_SSSE3;
    137       }
    138       else if (StringUtils::strieq(simd_level, "sse4.1")) {
    139         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_SSE4_1;
    140       }
    141       else if (StringUtils::strieq(simd_level, "sse4.2")) {
    142         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_SSE4_2;
    143       }
    144       else if (StringUtils::strieq(simd_level, "avx")) {
    145         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_AVX;
    146       }
    147       else if (StringUtils::strieq(simd_level, "avx2")) {
    148         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_AVX2;
    149       }
    150       else if (StringUtils::strieq(simd_level, "avx512")) {
    151         selected_cpu_features = BL_RUNTIME_CPU_FEATURE_X86_AVX512;
    152       }
    153 #elif defined(BL_JIT_ARCH_A64)
    154       else if (strcmp(simd_level, "asimd") == 0) {
    155         // Currently the default...
    156       }
    157 #endif
    158       else {
    159         printf("Failed to process command line arguments:\n");
    160         printf("  Unknown simd-level '%s' - please use --help to list all available simd levels\n", simd_level);
    161         return false;
    162       }
    163     }
    164 
    165     return true;
    166   }
    167 
    168   void stringify_feature_id(BLString& out, uint32_t cpu_features) noexcept {
    169     if (cpu_features)
    170       out.assign(StringUtils::cpu_x86_feature_to_string(BLRuntimeCpuFeatures(cpu_features)));
    171     else
    172       out.assign("native");
    173   }
    174 
    175   bool run_with_features(BLFormat format, uint32_t cpu_features) {
    176     reset_counters();
    177     stringify_feature_id(_cpu_features_string, cpu_features);
    178 
    179     ContextTester a_tester(test_cases, "ref");
    180     ContextTester b_tester(test_cases, "jit");
    181 
    182     a_tester.set_font_data(font_data);
    183     b_tester.set_font_data(font_data);
    184 
    185     a_tester.set_flush_sync(options.flush_sync);
    186     b_tester.set_flush_sync(options.flush_sync);
    187 
    188     BLContextCreateInfo a_create_info {};
    189     BLContextCreateInfo b_create_info {};
    190 
    191     a_create_info.flags = BL_CONTEXT_CREATE_FLAG_DISABLE_JIT;
    192 
    193     if (cpu_features) {
    194       b_create_info.flags = BL_CONTEXT_CREATE_FLAG_ISOLATED_JIT_RUNTIME |
    195                           BL_CONTEXT_CREATE_FLAG_OVERRIDE_CPU_FEATURES;
    196       b_create_info.cpu_features = cpu_features;
    197     }
    198 
    199     if (a_tester.init(int(options.width), int(options.height), format, a_create_info) != BL_SUCCESS ||
    200         b_tester.init(int(options.width), int(options.height), format, b_create_info) != BL_SUCCESS) {
    201       printf("Failed to initialize rendering contexts\n");
    202       return 1;
    203     }
    204 
    205     TestInfo info;
    206     dispatch_runs([&](CommandId command_id, StyleId style_id, StyleOp style_op, CompOp comp_op, OpacityOp opacity_op) {
    207       BLString s0;
    208       s0.append_format("%s/%s",
    209         StringUtils::style_id_to_string(style_id),
    210         StringUtils::style_op_to_string(style_op));
    211 
    212       BLString s1;
    213       s1.append_format("%s/%s",
    214         StringUtils::comp_op_to_string(comp_op),
    215         StringUtils::opacity_op_to_string(opacity_op));
    216 
    217       info.name.assign_format(
    218           "%-21s | fmt=%-7s| style+api=%-30s| comp+op=%-20s| simd-level=%-9s",
    219           StringUtils::command_id_to_string(command_id),
    220           StringUtils::format_to_string(format),
    221           s0.data(),
    222           s1.data(),
    223           _cpu_features_string.data());
    224 
    225       info.id.assign_format("ctx-jit-%s-%s-%s-%s-%s-%s-%s",
    226         StringUtils::format_to_string(format),
    227         StringUtils::command_id_to_string(command_id),
    228         StringUtils::style_id_to_string(style_id),
    229         StringUtils::style_op_to_string(style_op),
    230         StringUtils::comp_op_to_string(comp_op),
    231         StringUtils::opacity_op_to_string(opacity_op),
    232         _cpu_features_string.data());
    233 
    234       if (!options.quiet) {
    235         printf("Running [%s]\n", info.name.data());
    236       }
    237 
    238       a_tester.set_options(comp_op, opacity_op, style_id, style_op);
    239       b_tester.set_options(comp_op, opacity_op, style_id, style_op);
    240 
    241       uint32_t adjusted_max_diff = maximum_pixel_difference;
    242       if (adjusted_max_diff == 0xFFFFFFFFu) {
    243         adjusted_max_diff = maximum_pixel_difference_of(style_id);
    244       }
    245 
    246       if (run_multiple(command_id, info, a_tester, b_tester, adjusted_max_diff))
    247         passed_count++;
    248       else
    249         failed_count++;
    250     });
    251 
    252     a_tester.reset();
    253     b_tester.reset();
    254 
    255     if (mismatch_count)
    256       printf("Found %llu mismatches!\n\n", (unsigned long long)mismatch_count);
    257     else if (!options.quiet)
    258       printf("\n");
    259 
    260     return !mismatch_count;
    261   }
    262 
    263   int run(CmdLine cmd_line) {
    264     print_app_info("Blend2D JIT Rendering Context Tester", cmd_line.has_arg("--quiet"));
    265 
    266     if (cmd_line.has_arg("--help"))
    267       return help();
    268 
    269     if (!parse_common_options(cmd_line) || !parse_jit_options(cmd_line))
    270       return 1;
    271 
    272     if (iterate_all_jit_features) {
    273 #if defined(BL_JIT_ARCH_X86)
    274       static constexpr uint32_t x86_features_list[] = {
    275         BL_RUNTIME_CPU_FEATURE_X86_SSE2,
    276         BL_RUNTIME_CPU_FEATURE_X86_SSE3,
    277         BL_RUNTIME_CPU_FEATURE_X86_SSSE3,
    278         BL_RUNTIME_CPU_FEATURE_X86_SSE4_1,
    279         BL_RUNTIME_CPU_FEATURE_X86_SSE4_2,
    280         BL_RUNTIME_CPU_FEATURE_X86_AVX,
    281         BL_RUNTIME_CPU_FEATURE_X86_AVX2,
    282         BL_RUNTIME_CPU_FEATURE_X86_AVX512
    283       };
    284 
    285       BLRuntimeSystemInfo system_info {};
    286       BLRuntime::query_system_info(&system_info);
    287 
    288       for (const uint32_t& feature : x86_features_list) {
    289         if (!(system_info.cpu_features & feature))
    290           break;
    291 
    292         if (options.quiet) {
    293           stringify_feature_id(_cpu_features_string, feature);
    294           printf("Testing [%s] (quiet mode)\n", _cpu_features_string.data());
    295         }
    296 
    297         for (BLFormat format : test_cases.format_ids) {
    298           run_with_features(format, feature);
    299         }
    300       }
    301 #endif
    302 
    303       // Now run with all features if everything above has passed.
    304       for (BLFormat format : test_cases.format_ids) {
    305         run_with_features(format, 0);
    306       }
    307     }
    308     else {
    309       for (BLFormat format : test_cases.format_ids) {
    310         run_with_features(format, selected_cpu_features);
    311       }
    312     }
    313 
    314     if (failed_count) {
    315       printf("[FAILED] %u tests out of %u failed\n", failed_count, passed_count + failed_count);
    316       return 1;
    317     }
    318     else {
    319       printf("[PASSED] %u tests passed\n", passed_count);
    320       return 0;
    321     }
    322   }
    323 };
    324 
    325 } // {ContextTests}
    326 
    327 int main(int argc, char* argv[]) {
    328   BLRuntimeScope rt_scope;
    329   ContextTests::JITTestApp app;
    330 
    331   return app.run(CmdLine(argc, argv));
    332 }