| SkQP Render Test Algorithm |
| ========================== |
| |
| The following is a description of the render test validation algorithm that |
| will be used by the version of SkQP that will be released for Android Q-release. |
| |
| There is a global macro constant: `SK_SKQP_GLOBAL_ERROR_TOLERANCE`, which |
| reflects the `gn` variable `skia_skqp_global_error_tolerance`. This is usually |
| set to 8. |
| |
| First, look for a file named `skqp/rendertests.txt` in the |
| `platform_tools/android/apps/skqp/src/main/assets` directory. The format of |
| this file is: each line contains one render test name, followed by a comma, |
| followed by an integer. The integer is the `passing_threshold` for that test. |
| |
| For each test, we have a `max_image` and a `min_image`. These are PNG-encoded |
| images stored in SkQP's APK's asset directory (in the paths `gmkb/${TEST}/min.png` |
| and `gmkb/${TEST}/max.png`). |
| |
| The test input is a rendered image. This will be produced by running one of |
| the render tests against the either the `vk` (Vulkan) or `gles` (OpenGL ES) |
| Skia backend. |
| |
| Here is psuedocode for the error calculation: |
| |
| function calculate_pixel_error(pixel_value, pixel_max, pixel_min): |
| pixel_error = 0 |
| |
| for color_channel in { red, green, blue, alpha }: |
| value = get_color(pixel_value, color_channel) |
| v_max = get_color(pixel_max, color_channel) |
| v_min = get_color(pixel_min, color_channel) |
| |
| if value > v_max: |
| channel_error = value - v_max |
| elif value < v_min: |
| channel_error = v_min - value |
| else: |
| channel_error = 0 |
| pixel_error = max(pixel_error, channel_error) |
| |
| return max(0, pixel_error - SK_SKQP_GLOBAL_ERROR_TOLERANCE); |
| |
| function get_error(rendered_image, max_image, min_image): |
| assert(dimensions(rendered_image) == dimensions(max_image)) |
| assert(dimensions(rendered_image) == dimensions(min_image)) |
| |
| max_error = 0 |
| bad_pixels = 0 |
| total_error = 0 |
| |
| error_image = allocate_bitmap(dimensions(rendered_image)) |
| |
| for xy in list_all_pixel_coordinates(rendered_image): |
| pixel_error = calculate_pixel_error(rendered_image(xy), |
| max_image(xy), |
| min_image(xy)) |
| if pixel_error > 0: |
| for neighboring_xy in find_neighbors(xy): |
| if not inside(neighboring_xy, dimensions(rendered_image)): |
| continue |
| pixel_error = min(pixel_error, |
| calculate_pixel_error(rendered_image(xy), |
| max_image(neighboring_xy), |
| min_image(neighboring_xy))) |
| |
| if pixel_error > 0: |
| max_error = max(max_error, pixel_error) |
| bad_pixels += 1 |
| total_error += pixel_error |
| |
| error_image(xy) = linear_interpolation(black, red, pixel_error) |
| else: |
| error_image(xy) = white |
| |
| return ((total_error, max_error, bad_pixels), error_image) |
| |
| For each render test, there is a threshold value for `total_error`, : |
| `passing_threshold`. |
| |
| If `passing_threshold >= 0 && total_error > passing_threshold`, then the test |
| is a failure and is included in the report. if `passing_threshold == -1`, then |
| the test always passes, but we do execute the test to verify that the driver |
| does not crash. |
| |
| We generate a report with the following information for each test: |
| |
| backend_name,render_test_name,max_error,bad_pixels,total_error |
| |
| in CSV format in the file `out.csv`. A HTML report of just the failing tests |
| is written to the file `report.html`. This version includes four images for |
| each test: `rendered_image`, `max_image`, `min_image`, and `error_image`, as |
| well as the three metrics: `max_error`, `bad_pixels`, and `total_error`. |
| |
| |
| |