summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/expression/case_cache.ts
blob: 6bd4dc5b7e471ee7e221d92f6db492a22e7af4fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import { Cacheable, dataCache } from '../../../../common/framework/data_cache.js';
import { SerializedComparator, deserializeComparator } from '../../../util/compare.js';
import {
  Scalar,
  Vector,
  serializeValue,
  SerializedValue,
  deserializeValue,
} from '../../../util/conversion.js';
import {
  deserializeF32Interval,
  F32Interval,
  SerializedF32Interval,
  serializeF32Interval,
} from '../../../util/f32_interval.js';

import { Case, CaseList, Expectation } from './expression.js';

/**
 * SerializedExpectationValue holds the serialized form of an Expectation when
 * the Expectation is a Value
 * This form can be safely encoded to JSON.
 */
type SerializedExpectationValue = {
  kind: 'value';
  value: SerializedValue;
};

/**
 * SerializedExpectationValue holds the serialized form of an Expectation when
 * the Expectation is an Interval
 * This form can be safely encoded to JSON.
 */
type SerializedExpectationInterval = {
  kind: 'interval';
  value: SerializedF32Interval;
};

/**
 * SerializedExpectationValue holds the serialized form of an Expectation when
 * the Expectation is a list of Intervals
 * This form can be safely encoded to JSON.
 */
type SerializedExpectationIntervals = {
  kind: 'intervals';
  value: SerializedF32Interval[];
};

/**
 * SerializedExpectationValue holds the serialized form of an Expectation when
 * the Expectation is a Comparator
 * This form can be safely encoded to JSON.
 */
type SerializedExpectationComparator = {
  kind: 'comparator';
  value: SerializedComparator;
};

/**
 * SerializedExpectation holds the serialized form of an Expectation.
 * This form can be safely encoded to JSON.
 */
export type SerializedExpectation =
  | SerializedExpectationValue
  | SerializedExpectationInterval
  | SerializedExpectationIntervals
  | SerializedExpectationComparator;

/** serializeExpectation() converts an Expectation to a SerializedExpectation */
export function serializeExpectation(e: Expectation): SerializedExpectation {
  if (e instanceof Scalar || e instanceof Vector) {
    return { kind: 'value', value: serializeValue(e) };
  }
  if (e instanceof F32Interval) {
    return { kind: 'interval', value: serializeF32Interval(e) };
  }
  if (e instanceof Array) {
    return { kind: 'intervals', value: e.map(i => serializeF32Interval(i)) };
  }
  if (e instanceof Function) {
    const comp = (e as unknown) as SerializedComparator;
    if (comp !== undefined) {
      // if blocks used to refine the type of comp.kind, otherwise it is
      // actually the union of the string values
      if (comp.kind === 'anyOf') {
        return { kind: 'comparator', value: { kind: comp.kind, data: comp.data } };
      }
      if (comp.kind === 'skipUndefined') {
        return { kind: 'comparator', value: { kind: comp.kind, data: comp.data } };
      }
    }
    throw 'cannot serialize comparator';
  }
  throw 'cannot serialize expectation';
}

/** deserializeExpectation() converts a SerializedExpectation to a Expectation */
export function deserializeExpectation(data: SerializedExpectation): Expectation {
  switch (data.kind) {
    case 'value':
      return deserializeValue(data.value);
    case 'interval':
      return deserializeF32Interval(data.value);
    case 'intervals':
      return data.value.map(i => deserializeF32Interval(i));
    case 'comparator':
      return deserializeComparator(data.value);
  }
}

/**
 * SerializedCase holds the serialized form of a Case.
 * This form can be safely encoded to JSON.
 */
export type SerializedCase = {
  input: SerializedValue | SerializedValue[];
  expected: SerializedExpectation;
};

/** serializeCase() converts an Case to a SerializedCase */
export function serializeCase(c: Case): SerializedCase {
  return {
    input: c.input instanceof Array ? c.input.map(v => serializeValue(v)) : serializeValue(c.input),
    expected: serializeExpectation(c.expected),
  };
}

/** serializeCase() converts an SerializedCase to a Case */
export function deserializeCase(data: SerializedCase): Case {
  return {
    input:
      data.input instanceof Array
        ? data.input.map(v => deserializeValue(v))
        : deserializeValue(data.input),
    expected: deserializeExpectation(data.expected),
  };
}

/** CaseListBuilder is a function that builds a CaseList */
export type CaseListBuilder = () => CaseList;

/**
 * CaseCache is a cache of CaseList.
 * CaseCache implements the Cacheable interface, so the cases can be pre-built
 * and stored in the data cache, reducing computation costs at CTS runtime.
 */
export class CaseCache implements Cacheable<Record<string, CaseList>> {
  /**
   * Constructor
   * @param name the name of the cache. This must be globally unique.
   * @param builders a Record of case-list name to case-list builder.
   */
  constructor(name: string, builders: Record<string, CaseListBuilder>) {
    this.path = `webgpu/shader/execution/case-cache/${name}.json`;
    this.builders = builders;
  }

  /** get() returns the list of cases with the given name */
  public async get(name: string): Promise<CaseList> {
    const data = await dataCache.fetch(this);
    return data[name];
  }

  /**
   * build() implements the Cacheable.build interface.
   * @returns the data.
   */
  build(): Promise<Record<string, CaseList>> {
    const built: Record<string, CaseList> = {};
    for (const name in this.builders) {
      const cases = this.builders[name]();
      built[name] = cases;
    }
    return Promise.resolve(built);
  }

  /**
   * serialize() implements the Cacheable.serialize interface.
   * @returns the serialized data.
   */
  serialize(data: Record<string, CaseList>): string {
    const serialized: Record<string, SerializedCase[]> = {};
    for (const name in data) {
      serialized[name] = data[name].map(c => serializeCase(c));
    }
    return JSON.stringify(serialized);
  }

  /**
   * deserialize() implements the Cacheable.deserialize interface.
   * @returns the deserialize data.
   */
  deserialize(serialized: string): Record<string, CaseList> {
    const data = JSON.parse(serialized) as Record<string, SerializedCase[]>;
    const casesByName: Record<string, CaseList> = {};
    for (const name in data) {
      const cases = data[name].map(caseData => deserializeCase(caseData));
      casesByName[name] = cases;
    }
    return casesByName;
  }

  public readonly path: string;
  private readonly builders: Record<string, CaseListBuilder>;
}

export function makeCaseCache(name: string, builders: Record<string, CaseListBuilder>): CaseCache {
  return new CaseCache(name, builders);
}