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 }