From fb6eaa1960b7b2d7b70a608a4c4ef80a0d5e8cac Mon Sep 17 00:00:00 2001
From: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
Date: Mon, 28 Nov 2022 12:03:27 +0200
Subject: [PATCH] report-fossil: add anv support

---
 README.md            |   8 +-
 anv-report-fossil.py | 187 -------------------------------------------
 report-fossil.py     |  31 ++++++-
 3 files changed, 28 insertions(+), 198 deletions(-)
 delete mode 100755 anv-report-fossil.py

diff --git a/README.md b/README.md
index 85c3605..0933c37 100644
--- a/README.md
+++ b/README.md
@@ -124,13 +124,7 @@ Or you can use fossil_prune_replay.sh to replay a specific pipeline by hash:
 If you do not get a .csv file it likely means that a driver without
 `VK_KHR_pipeline_statistics`, such as a system installed driver.
 
-You can then compare two different csv files using the anv-report-fossil.py or
-radv-report-fossil.py scripts:
-
-    $ anv-report-fossil.py baseline.csv development.csv
-    $ radv-report-fossil.py baseline.csv development.csv
-
-Or the report-fossil.py script, which guesses the driver used:
+You can then compare two different csv files using the report-fossil.py script:
 
     $ report-fossil.py baseline.csv development.csv
 
diff --git a/anv-report-fossil.py b/anv-report-fossil.py
deleted file mode 100755
index 7996aaf..0000000
--- a/anv-report-fossil.py
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/usr/bin/env python3
-# Copyright © 2019 Intel Corporation
-
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import argparse
-import csv
-import pathlib
-import typing
-
-import attr
-
-if typing.TYPE_CHECKING:
-    import typing_extensions
-
-    class DiffProtocol(typing_extensions.Protocol):
-
-        old: int
-        new: int
-
-    V = typing.TypeVar('V', int, float, str)
-
-
-@attr.s(slots=True)
-class Result:
-
-    name: str = attr.ib()
-    instructions: typing.Optional[int] = attr.ib()
-    sends: typing.Optional[int] = attr.ib()
-    loops: typing.Optional[int] = attr.ib()
-    cycles: typing.Optional[int] = attr.ib()
-    spills: typing.Optional[int] = attr.ib()
-    fills: typing.Optional[int] = attr.ib()
-
-
-@attr.s(slots=True)
-class ResultFactory:
-
-    lookups: typing.Dict[str, int] = attr.ib()
-
-    def __call__(self, row: typing.List[str]) -> Result:
-        def get(name: str, type_: typing.Type['V']) -> typing.Optional['V']:
-            try:
-                v = row[self.lookups[name]]
-            except (KeyError, IndexError):
-                return None
-            return type_(v)
-
-        shader_name = '/'.join([
-            get('Database', str),
-            get('Pipeline hash', str),
-            get('Executable name', str),
-        ])
-
-        return Result(
-            name=shader_name,
-            instructions=get('Instruction Count', int),
-            sends=get('SEND Count', int),
-            loops=get('Loop Count', int),
-            cycles=get('Cycle Count', int),
-            spills=get('Spill Count', int),
-            fills=get('Fill Count', int),
-        )
-
-
-def calculate_percent(diff: 'DiffProtocol') -> str:
-    if diff.new and diff.old:
-        return '{:+.1%}'.format((diff.new / diff.old) - 1)
-    return '0.0%'
-
-
-@attr.s(slots=True)
-class ProgramDiff:
-
-    name: str = attr.ib()
-    old: int = attr.ib()
-    new: int = attr.ib()
-
-
-@attr.s(slots=True)
-class Diff:
-
-    name: str = attr.ib()
-    old: int = attr.ib(0)
-    new: int = attr.ib(0)
-    helped: typing.Dict[str, ProgramDiff] = attr.ib(factory=dict)
-    hurt: typing.Dict[str, ProgramDiff] = attr.ib(factory=dict)
-
-    def generate(self) -> str:
-        result: typing.List[str] = [
-            f'{self.name} in all programs: {self.old} -> {self.new} ({calculate_percent(self)})']
-        result.extend(
-            [f'{self.name} helped {n}: {h.old} -> {h.new} ({calculate_percent(h)})'
-             for n, h in self.helped.items()])
-        result.extend(
-            [f'{self.name} hurt {n}: {h.old} -> {h.new} ({calculate_percent(h)})'
-             for n, h in self.hurt.items()])
-
-        return '\n'.join(result)
-
-
-@attr.s(slots=True)
-class Report:
-
-    instructions: Diff = attr.ib(factory=lambda: Diff('Instructions'))
-    sends: Diff = attr.ib(factory=lambda: Diff('SENDs'))
-    loops: Diff = attr.ib(factory=lambda: Diff('Loops'))
-    cycles: Diff = attr.ib(factory=lambda: Diff('Cycles'))
-    spills: Diff = attr.ib(factory=lambda: Diff('Spills'))
-    fills: Diff = attr.ib(factory=lambda: Diff('Fills'))
-
-    def include(self, name: str, d0: Result, d1: Result) -> None:
-        for m in ['instructions', 'sends', 'loops', 'cycles', 'spills', 'fills']:
-            self._include(name, getattr(self, m), getattr(d0, m), getattr(d1, m))
-
-    def _include(self, name: str, member: Diff, d0: typing.Optional[int],
-                 d1: typing.Optional[int]) -> None:
-        if d0 > d1:
-            member.helped[name] = ProgramDiff(name, d0, d1)
-        elif d0 < d1:
-            member.hurt[name] = ProgramDiff(name, d0, d1)
-
-        member.old += d0
-        member.new += d1
-
-    def _include_cycles(self, name: str, d0: Result, d1: Result) -> None:
-        pass
-
-    def generate(self) -> str:
-        return '\n\n'.join(m.generate() for m in attr.astuple(self, recurse=False))
-
-
-def read_csv(csv_file: pathlib.Path) -> typing.Dict[str, Result]:
-    data: typing.Dict[str, Result] = {}
-    with csv_file.open('rt') as f:
-        reader = csv.reader(f)
-        for row in reader:
-            if 'Database' in row:
-                factory = ResultFactory({k: v for v, k in enumerate(row)})
-                continue
-            shader = factory(row)
-            data[shader.name] = shader
-    return data
-
-
-def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('csv', nargs=2, type=pathlib.Path, help='Path to CSV files')
-    args = parser.parse_args()
-
-    data0 = read_csv(args.csv[0])
-    data1 = read_csv(args.csv[1])
-
-    names = set(list(data0.keys()) + list(data1.keys()))
-
-    report = Report()
-    for name in names:
-        d0 = data0.get(name)
-        d1 = data1.get(name)
-        if not (d0 and d1):
-            # If a fossil is only in one run or another don't include it,
-            # otherwise we'll skew the overall results.
-            continue
-        report.include(name, d0, d1)
-
-    print(report.generate())
-
-
-if __name__ == "__main__":
-    main()
-
diff --git a/report-fossil.py b/report-fossil.py
index 025e728..e458025 100755
--- a/report-fossil.py
+++ b/report-fossil.py
@@ -124,6 +124,15 @@ statistics = [
     Statistic(internal_name='spills', csv_names=['TMU Spills'], display_name='Spills'),
     Statistic(internal_name='fills', csv_names=['TMU Fills'], display_name='Fills'),
     Statistic(internal_name='read_stalls', csv_names=['Read Stalls'], display_name='Read Stalls'),
+
+    # Anv statistics
+    Statistic(internal_name='subgroup_size', csv_names=['Subgroup size'], display_name='Subgroup size'),
+    Statistic(internal_name='send_count', csv_names=['SEND Count'], display_name='Send messages'),
+    Statistic(internal_name='loop_count', csv_names=['Loop Count'], display_name='Loop count'),
+    Statistic(internal_name='cycle_count', csv_names=['Cycle Count'], display_name='Cycle count'),
+    Statistic(internal_name='spill_count', csv_names=['Spill Count'], display_name='Spill count'),
+    Statistic(internal_name='fill_count', csv_names=['Fill Count'], display_name='Fill count'),
+    Statistic(internal_name='scratch_size', csv_names=['Scratch Memory Size'], display_name='Scratch Memory Size'),
 ]
 
 for n in range(8):
@@ -162,6 +171,24 @@ executables = {
     'FS (Render)' : 'fs',
     'FS (Binning)' : 'binning_fs',
     'CS (Render)' : 'cs',
+
+    # Anv executable names
+    'vertex' : 'vs',
+    'geometry' : 'gs',
+    'tessellation control': 'tcs',
+    'tessellation evaluation': 'tes',
+    'mesh': 'mesh',
+    'task': 'task',
+    'kernel': 'ks',
+    'SIMD8 fragment': 'fs',
+    'SIMD16 fragment': 'fs',
+    'SIMD32 fragment': 'fs',
+    'compute': 'cs',
+    'raygen' : 'rgen',
+    'any hit' : 'ahit',
+    'miss' : 'miss',
+    'closest hit' : 'chit',
+    'intersection' : 'intersection',
 }
 
 
@@ -587,10 +614,6 @@ def main():
 
     driver = next(iter(drivers))
 
-    if driver == 'anv':
-        importlib.import_module('anv-report-fossil').main()
-        return
-
     parser = argparse.ArgumentParser()
     parser.add_argument('csv', nargs='+', type=pathlib.Path, help='Path to CSV files')
     stat_list_arg = {'nargs':'*', 'default':None, 'type':str, 'metavar':'STAT',
-- 
GitLab