dEQPのqpa結果ファイルを集計する

/ Python

OpenGL/Vulkanの互換性テストプログラム (VK-GL-CTS)を実行すると、結果としてTestResults.qpaというファイルが得られるがこのファイルのサマリを抽出するプログラムがすぐに見当たらなかった。公式ウェブサイトでは、Cherryというツールが挙げられているのだが、すぐにqpaファイルの内容を調べたい場合にはやや不便。

TestResults.qpaはどうもbase64テキストでエンコードされたpng画像を含んでいるらしく、deqp-vkなどを長時間流した際にできてくるTestResults.qpaはギガバイト単位になる。OpenGLはそれほどではないが、qpaが生成される点は同じ。エディタで中身を開いてみると下記のようなbeginTestCaseResultとendTestCaseResultで囲まれたXMLファイルの繰り返しである。

#beginTestCaseResult dEQP-EGL.info.version
<?xml version="1.0" encoding="UTF-8"?>
<TestCaseResult CasePath="dEQP-EGL.info.version" Version="0.3.4" CaseType="SelfValidate">
 <Text>1.4</Text>
 <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">13877</Number>
 <Result StatusCode="Pass">Pass</Result>
</TestCaseResult>

#endTestCaseResult

であるなら、やることは下記のような手動パースである。このプログラムはUbuntu24.04の特に何も追加していない状態のpython3で動くことを確認している。

#!/usr/bin/env python3

import sys
import re
import xml.etree.ElementTree as ET

def process(f):    
    caseresult = ''
    with open(f) as qpafile:
        for line in qpafile:
            if line.startswith('#beginTestCaseResult'):
                caseresult = ''
                continue
            if line.startswith('#endTestCaseResult'):
                et = ET.fromstring(caseresult)
                name = et.attrib['CasePath']
                ok = et.find('Result').attrib['StatusCode']
                print(name, ok)
                continue
            caseresult += line

if __name__ == '__main__':
    for f in sys.argv[1:]:
        process(f)

これをgl-vk-parse-qpa.pyとして保存しておき実行権限を与えておく。外側から下記のbashプログラムでさらに処理をする。

#!/usr/bin/env bash

if [[ ! -f qpa-summary.txt ]]; then
    ./gl-vk-parse-qpa.py *.qpa > qpa-summary.txt
fi

echo $(cat qpa-summary.txt | wc -l) "test results"

if [[ ! -f qpa-summary-caselist.txt ]]; then
    cat qpa-summary.txt | cut -d\  -f1 | sort | uniq > qpa-summary-caselist.txt
    cat qpa-summary.txt | cut -d\  -f2 | sort | uniq > qpa-summary-statuscode.txt
fi

echo $(cat qpa-summary-caselist.txt | wc -l) "test cases"

for l in 1 2 3 4; do
    if [[ ! -f qpa-summary-caselist-l${l}.txt ]]; then
        cat qpa-summary-caselist.txt | cut -d\. -f1-$l | uniq > qpa-summary-caselist-l${l}.txt
    fi
    echo $(cat qpa-summary-caselist-l${l}.txt | wc -l) "test group at l$l"
done

for l in 1 2 3; do
    while read -r line; do
        T=$(grep ^$line qpa-summary-caselist.txt | wc -l)
        P=$(grep ^$line.*\ Pass$ qpa-summary.txt | wc -l)
        N=$(grep ^$line.*\ Fail$ qpa-summary.txt | wc -l)
        F=$(grep ^$line.*\ NotSupported$ qpa-summary.txt | wc -l)
        Q=$(grep ^$line.*\ QualityWarning$ qpa-summary.txt | wc -l)
        printf "%-40s Total=%7d Pass=%7d NotSupported=%6d Fail=%6d QualityWarning=%6d\n" $line $T $P $N $F $Q
    done < qpa-summary-caselist-l${l}.txt
done

こちらも同じくgl-vk-cts-summary.shとして保存し、実行権限を与えておく。適当にdeqp-vkcts-runnerを実行して得られたqpaファイルをカレントディレクトリに配置した状態で実行すると、下記のような表示が得られる。

1328765 test results
1324037 test cases
7 test group at l1
30 test group at l2
189 test group at l3
1650 test group at l4
CTS-Configs                              Total=     15 Pass=     15 NotSupported=     0 Fail=     0 QualityWarning=     0
dEQP-EGL                                 Total=   3783 Pass=   1001 NotSupported=     0 Fail=  2781 QualityWarning=     1
dEQP-GLES2                               Total=  17153 Pass=  17036 NotSupported=     6 Fail=   111 QualityWarning=     0
dEQP-GLES3                               Total=  14227 Pass=  14227 NotSupported=     0 Fail=     0 QualityWarning=     0
dEQP-VK                                  Total=1288385 Pass= 319294 NotSupported=    51 Fail=970632 QualityWarning=     4
KHR-GLES2                                Total=    473 Pass=   3360 NotSupported=     0 Fail=   245 QualityWarning=     0
KHR-NoContext                            Total=      1 Pass=      1 NotSupported=     0 Fail=     0 QualityWarning=     0
...

これは前半のみを抽出したもの。少しprint部分を改造すればmarkdownの表もあっという間に作ることができる。


testcase Total Pass NotSupported Fail QualityWarning
CTS-Configs 15 15 0 0 0
dEQP-EGL 3783 1001 0 2781 1
dEQP-GLES2 17153 17036 6 111 0
dEQP-GLES3 14227 14227 0 0 0
dEQP-VK 1288385 319294 51 970632 4
KHR-GLES2 473 3360 0 245 0
KHR-NoContext 1 1 0 0 0
CTS-Configs.es2 1 1 0 0 0
CTS-Configs.es3 3 3 0 0 0
CTS-Configs.es31 1 1 0 0 0
CTS-Configs.es32 1 1 0 0 0
CTS-Configs.gl30 1 1 0 0 0
CTS-Configs.gl31 1 1 0 0 0
CTS-Configs.gl32 1 1 0 0 0
CTS-Configs.gl33 1 1 0 0 0
CTS-Configs.gl40 1 1 0 0 0
CTS-Configs.gl41 1 1 0 0 0
CTS-Configs.gl42 1 1 0 0 0
CTS-Configs.gl43 1 1 0 0 0
CTS-Configs.gl44 1 1 0 0 0
CTS-Configs.gl45 1 1 0 0 0
CTS-Configs.gl46 1 1 0 0 0
dEQP-EGL.functional 3778 996 0 2781 1
dEQP-EGL.info 5 5 0 0 0
dEQP-GLES2.functional 17147 17030 6 111 0
dEQP-GLES2.info 6 6 0 0 0
dEQP-GLES3.functional 14221 14221 0 0 0
dEQP-GLES3.info 6 6 0 0 0
dEQP-VK.api 297213 95525 51 203210 4
dEQP-VK.binding_model 140947 59475 0 81472 0
dEQP-VK.info 19 34 0 4 0
dEQP-VK.memory 5588 2641 0 2947 0
dEQP-VK.pipeline 844618 161619 0 682999 0
KHR-GLES2.core 61 392 0 35 0
KHR-GLES2.shaders 15 120 0 0 0
KHR-GLES2.texture_3d 397 2848 0 210 0
KHR-NoContext.es2 1 1 0 0 0
CTS-Configs.es2 1 1 0 0 0
CTS-Configs.es3 3 3 0 0 0
CTS-Configs.es31 1 1 0 0 0
CTS-Configs.es32 1 1 0 0 0
CTS-Configs.gl30 1 1 0 0 0
CTS-Configs.gl31 1 1 0 0 0
CTS-Configs.gl32 1 1 0 0 0
CTS-Configs.gl33 1 1 0 0 0
CTS-Configs.gl40 1 1 0 0 0
CTS-Configs.gl41 1 1 0 0 0
CTS-Configs.gl42 1 1 0 0 0
CTS-Configs.gl43 1 1 0 0 0
CTS-Configs.gl44 1 1 0 0 0
CTS-Configs.gl45 1 1 0 0 0
CTS-Configs.gl46 1 1 0 0 0
dEQP-EGL.functional.buffer_age 168 84 0 84 0
dEQP-EGL.functional.choose_config 67 65 0 2 0
dEQP-EGL.functional.client_extensions 3 3 0 0 0
dEQP-EGL.functional.color_clears 363 78 0 285 0
dEQP-EGL.functional.create_context_ext 400 150 0 250 0
dEQP-EGL.functional.create_context 422 167 0 255 0
dEQP-EGL.functional.create_surface 147 16 0 131 0
dEQP-EGL.functional.fence_sync 28 28 0 0 0
dEQP-EGL.functional.get_frame_timestamps 21 0 0 21 0
dEQP-EGL.functional.get_proc_address 203 202 0 0 1
dEQP-EGL.functional.hdr_metadata 2 0 0 2 0
dEQP-EGL.functional.image 127 69 0 58 0
dEQP-EGL.functional.multicontext 6 0 0 6 0
dEQP-EGL.functional.multithread 8 4 0 4 0
dEQP-EGL.functional.mutable_render_buffer 3 0 0 3 0
dEQP-EGL.functional.native_color_mapping 126 0 0 126 0
dEQP-EGL.functional.native_coord_mapping 126 0 0 126 0
dEQP-EGL.functional.negative_api 26 25 0 1 0
dEQP-EGL.functional.negative_partial_update 7 0 0 7 0
dEQP-EGL.functional.partial_update 29 0 0 29 0
dEQP-EGL.functional.preserve_swap 30 12 0 18 0
dEQP-EGL.functional.query_config 32 31 0 1 0
dEQP-EGL.functional.query_context 65 25 0 40 0
dEQP-EGL.functional.query_surface 126 32 0 94 0
dEQP-EGL.functional.render 128 48 0 80 0
dEQP-EGL.functional.resize 12 4 0 8 0
dEQP-EGL.functional.reusable_sync 23 23 0 0 0
dEQP-EGL.functional.robustness 58 2 0 56 0
dEQP-EGL.functional.sharing 1272 18 0 1254 0
dEQP-EGL.functional.surfaceless_context 21 16 0 5 0
dEQP-EGL.functional.swap_buffers 75 36 0 39 0
dEQP-EGL.functional.swap_buffers_with_damage 54 36 0 18 0
dEQP-EGL.functional.thread_cleanup 4 0 0 4 0
dEQP-EGL.functional.wide_color 50 8 0 42 0
dEQP-EGL.info.client_apis 1 1 0 0 0
dEQP-EGL.info.configs 1 1 0 0 0
dEQP-EGL.info.extensions 1 1 0 0 0
dEQP-EGL.info.vendor 1 1 0 0 0
dEQP-EGL.info.version 1 1 0 0 0
dEQP-GLES2.functional.attribute_location 57 57 0 0 0
dEQP-GLES2.functional.buffer 49 49 0 0 0
dEQP-GLES2.functional.clip_control 8 8 0 0 0
dEQP-GLES2.functional.clipping 602 602 0 0 0
dEQP-GLES2.functional.color_clear 19 19 0 0 0
dEQP-GLES2.functional.debug_marker 3 0 0 3 0
dEQP-GLES2.functional.default_vertex_attrib 40 40 0 0 0
dEQP-GLES2.functional.depth_range 28 28 0 0 0
dEQP-GLES2.functional.depth_stencil_clear 11 11 0 0 0
dEQP-GLES2.functional.dither 20 20 0 0 0
dEQP-GLES2.functional.draw 101 101 0 0 0
dEQP-GLES2.functional.fbo 709 612 6 91 0
dEQP-GLES2.functional.flush_finish 5 5 0 0 0
dEQP-GLES2.functional.fragment_ops 1923 1923 0 0 0
dEQP-GLES2.functional.implementation_limits 16 16 0 0 0
dEQP-GLES2.functional.lifetime 33 33 0 0 0
dEQP-GLES2.functional.light_amount 19 19 0 0 0
dEQP-GLES2.functional.multisample 17 0 0 17 0
dEQP-GLES2.functional.multisampled_render_to_texture 1 0 0 1 0
dEQP-GLES2.functional.multisample 17 0 0 17 0
dEQP-GLES2.functional.negative_api 246 246 0 0 0
dEQP-GLES2.functional.polygon_offset 8 8 0 0 0
dEQP-GLES2.functional.prerequisite 3 3 0 0 0
dEQP-GLES2.functional.rasterization 52 52 0 0 0
dEQP-GLES2.functional.read_pixels 8 8 0 0 0
dEQP-GLES2.functional.shader_api 38 38 0 0 0
dEQP-GLES2.functional.shaders 10101 10101 0 0 0
dEQP-GLES2.functional.state_query 396 396 0 0 0
dEQP-GLES2.functional.texture 807 807 0 0 0
dEQP-GLES2.functional.uniform_api 1257 1257 0 0 0
dEQP-GLES2.functional.vertex_arrays 571 571 0 0 0
dEQP-GLES2.info.extensions 1 1 0 0 0
dEQP-GLES2.info.renderer 1 1 0 0 0
dEQP-GLES2.info.render_target 1 1 0 0 0
dEQP-GLES2.info.shading_language_version 1 1 0 0 0
dEQP-GLES2.info.vendor 1 1 0 0 0
dEQP-GLES2.info.version 1 1 0 0 0
dEQP-GLES3.functional.buffer 695 695 0 0 0
dEQP-GLES3.functional.color_clear 19 19 0 0 0
dEQP-GLES3.functional.depth_stencil_clear 11 11 0 0 0
dEQP-GLES3.functional.implementation_limits 49 49 0 0 0
dEQP-GLES3.functional.prerequisite 3 3 0 0 0
dEQP-GLES3.functional.shaders 13444 13444 0 0 0
dEQP-GLES3.info.extensions 1 1 0 0 0
dEQP-GLES3.info.renderer 1 1 0 0 0
dEQP-GLES3.info.render_target 1 1 0 0 0
dEQP-GLES3.info.shading_language_version 1 1 0 0 0
dEQP-GLES3.info.vendor 1 1 0 0 0
dEQP-GLES3.info.version 1 1 0 0 0
dEQP-VK.api.buffer 4446 1358 0 3088 0
dEQP-VK.api.buffer_marker 156 0 0 156 0
dEQP-VK.api.buffer_memory_requirements 240 24 0 216 0
dEQP-VK.api.buffer 4446 1358 0 3088 0
dEQP-VK.api.buffer_view 974 304 0 670 0
dEQP-VK.api.command_buffers 124 54 0 70 0
dEQP-VK.api.copy_and_blit 241072 62661 0 178411 0
dEQP-VK.api.descriptor_pool 6 6 0 0 0
dEQP-VK.api.descriptor_set 6 6 0 0 0
dEQP-VK.api.device_drm_properties 1 1 0 0 0
dEQP-VK.api.device_init 217 205 0 12 0
dEQP-VK.api.driver_properties 5 8 2 0 0
dEQP-VK.api.extension_duplicates 4 4 0 0 0
dEQP-VK.api.external 199 39 0 160 0
dEQP-VK.api.fill_and_update_buffer 72 48 0 24 0
dEQP-VK.api.format_feature_flags2 184 184 0 0 0
dEQP-VK.api.fragment_shader_output 86 86 0 0 0
dEQP-VK.api.frame_boundary 18 0 0 18 0
dEQP-VK.api.get_device_proc_addr 1 1 0 0 0
dEQP-VK.api.get_memory_commitment 2 0 0 2 0
dEQP-VK.api.granularity 650 238 0 412 0
dEQP-VK.api.image_clearing 44754 24636 48 20070 0
dEQP-VK.api.image_compression_control 916 0 0 916 0
dEQP-VK.api.info 3888 5439 1 11 0
dEQP-VK.api.invariance 2 2 0 0 0
dEQP-VK.api.maintenance3_check 46 46 0 0 0
dEQP-VK.api.maintenance5 10 0 0 10 0
dEQP-VK.api.maintenance6_check 1 0 0 1 0
dEQP-VK.api.maintenance7 2 0 0 2 0
dEQP-VK.api.null_handle 24 23 0 1 0
dEQP-VK.api.object_management 457 453 0 0 4
dEQP-VK.api.pipeline 9 9 0 0 0
dEQP-VK.api.smoke 6 12 0 0 0
dEQP-VK.api.tooling_info 2 2 0 0 0
dEQP-VK.api.version_check 3 4 0 2 0
dEQP-VK.binding_model.buffer_device_address 4714 3370 0 1344 0
dEQP-VK.binding_model.descriptor_copy 285 281 0 4 0
dEQP-VK.binding_model.descriptorset_random 35148 4206 0 30942 0
dEQP-VK.binding_model.descriptor_update 80 20 0 60 0
dEQP-VK.binding_model.dynamic_offset 2 2 0 0 0
dEQP-VK.binding_model.mutable_descriptor 14306 8390 0 5916 0
dEQP-VK.binding_model.shader_access 86412 43206 0 43206 0
dEQP-VK.info.build 1 2 0 0 0
dEQP-VK.info.device 11 18 0 4 0
dEQP-VK.info.device_extensions 1 2 0 0 0
dEQP-VK.info.device_features 1 2 0 0 0
dEQP-VK.info.device_group_peer_memory_features 1 0 0 2 0
dEQP-VK.info.device_layers 1 2 0 0 0
dEQP-VK.info.device_mandatory_features 1 2 0 0 0
dEQP-VK.info.device_memory_budget 1 0 0 2 0
dEQP-VK.info.device_memory_properties 1 2 0 0 0
dEQP-VK.info.device_no_khx_extensions 1 2 0 0 0
dEQP-VK.info.device_properties 1 2 0 0 0
dEQP-VK.info.device_queue_family_properties 1 2 0 0 0
dEQP-VK.info.instance_extension_device_functions 1 2 0 0 0
dEQP-VK.info.instance_extensions 1 2 0 0 0
dEQP-VK.info.instance_layers 1 2 0 0 0
dEQP-VK.info.memory_limits 1 2 0 0 0
dEQP-VK.info.physical_device_groups 1 2 0 0 0
dEQP-VK.info.physical_devices 1 2 0 0 0
dEQP-VK.info.platform 1 2 0 0 0
dEQP-VK.memory.address_binding_report 41 0 0 41 0
dEQP-VK.memory.allocation 202 202 0 0 0
dEQP-VK.memory.binding 252 42 0 210 0
dEQP-VK.memory.device_group_allocation 202 0 0 202 0
dEQP-VK.memory.device_memory_report 42 35 0 7 0
dEQP-VK.memory.mapping 4466 2233 0 2233 0
dEQP-VK.memory.pageable_allocation 202 0 0 202 0
dEQP-VK.memory.pipeline_barrier 104 104 0 0 0
dEQP-VK.memory.requirements 77 25 0 52 0
dEQP-VK.pipeline.fast_linked_library 115628 0 0 115628 0
dEQP-VK.pipeline.monolithic 267708 161618 0 106090 0
dEQP-VK.pipeline.pipeline_library 115653 1 0 115652 0
dEQP-VK.pipeline.shader_object_linked_binary 35572 0 0 35572 0
dEQP-VK.pipeline.shader_object_linked_spirv 35572 0 0 35572 0
dEQP-VK.pipeline.shader_object_unlinked_binary 35572 0 0 35572 0
dEQP-VK.pipeline.shader_object_unlinked_spirv 238913 0 0 238913 0
KHR-GLES2.core.internalformat 61 392 0 35 0
KHR-GLES2.shaders.aggressive_optimizations 12 96 0 0 0
KHR-GLES2.shaders.negative 3 24 0 0 0
KHR-GLES2.texture_3d.compressed_texture 30 0 0 210 0
KHR-GLES2.texture_3d.copy_sub_image 2 14 0 0 0
KHR-GLES2.texture_3d.filtering 361 2806 0 0 0
KHR-GLES2.texture_3d.framebuffer_texture 2 14 0 0 0
KHR-GLES2.texture_3d.sub_image 2 14 0 0 0
KHR-NoContext.es2.no_error 1 1 0 0 0

表示に関してはこれで十分だが、bashのスクリプトの集計ロジックはまだまだ改良が必要。例えば、同じテストケースを何度も実行した場合にはその回数だけPassならPassが増えていくので全部足してもTotalにならない。ちなみにこのmarkdown表を作ったときの出力部分は下記

printf "|%-40s |%7s |%7s |%6s |%6s |%6s|\n" testcase Total Pass NotSupported Fail QualityWarning
printf "|---|---:|---:|---:|---:|---:|\n"
for l in 1 2 3; do
    while read -r line; do
        T=$(grep ^$line qpa-summary-caselist.txt | wc -l)
        P=$(grep ^$line.*\ Pass$ qpa-summary.txt | wc -l)
        N=$(grep ^$line.*\ Fail$ qpa-summary.txt | wc -l)
        F=$(grep ^$line.*\ NotSupported$ qpa-summary.txt | wc -l)
        Q=$(grep ^$line.*\ QualityWarning$ qpa-summary.txt | wc -l)
        printf "|%-40s |%7d |%7d |%6d |%6d |%6d|\n" $line $T $P $N $F $Q
    done < qpa-summary-caselist-l${l}.txt
done

本当に、bashのprintとmarkdownの表は相性がいい。にしても、この形の集計ツールくらいCTSの一部としてありそうなものなのだが。。