|
115 | 115 | var primarySet = new HashSet<string>(primary); |
116 | 116 |
|
117 | 117 | // Find fallback gold from other platforms (matching test framework behavior) |
118 | | - var seen = new HashSet<string>(primarySet); |
119 | | - var fallbacks = new List<object>(); |
| 118 | + // Prefer: same API + same renderer class > same API > same class > any |
| 119 | + var requestedApi = parts[0]; |
| 120 | + var requestedIsSw = IsSoftwareRenderer(parts[1]); |
| 121 | + var fallbackBest = new Dictionary<string, (string platform, int score)>(); |
120 | 122 | var suiteDir = Path.Combine(testsDir, suite); |
121 | 123 | if (Directory.Exists(suiteDir)) |
122 | 124 | { |
|
126 | 128 | if (pName == "local") continue; |
127 | 129 | foreach (var dDir in Directory.GetDirectories(pDir)) |
128 | 130 | { |
129 | | - var fallbackPlatform = $"{pName}/{Path.GetFileName(dDir)}"; |
| 131 | + var device = Path.GetFileName(dDir); |
| 132 | + var fallbackPlatform = $"{pName}/{device}"; |
130 | 133 | if (fallbackPlatform == platform) continue; |
| 134 | + int score = 0; |
| 135 | + if (pName == requestedApi) score += 2; |
| 136 | + if (IsSoftwareRenderer(device) == requestedIsSw) score += 1; |
131 | 137 | foreach (var f in Directory.GetFiles(dDir, "*.png")) |
132 | 138 | { |
133 | 139 | var name = Path.GetFileName(f); |
134 | | - if (seen.Add(name)) |
135 | | - fallbacks.Add(new { Name = name, FallbackPlatform = fallbackPlatform }); |
| 140 | + if (primarySet.Contains(name)) continue; |
| 141 | + if (!fallbackBest.TryGetValue(name, out var existing) || score > existing.score) |
| 142 | + fallbackBest[name] = (fallbackPlatform, score); |
136 | 143 | } |
137 | 144 | } |
138 | 145 | } |
139 | 146 | } |
| 147 | + var fallbacks = fallbackBest.Select(kv => (object)new { Name = kv.Key, FallbackPlatform = kv.Value.platform }).ToList(); |
140 | 148 |
|
141 | 149 | return Results.Ok(new |
142 | 150 | { |
|
153 | 161 | var filePath = Path.Combine(testsDir, suite, parts[0], parts[1], name); |
154 | 162 | if (File.Exists(filePath)) return Results.File(filePath, "image/png"); |
155 | 163 |
|
156 | | - // Fallback: search all platforms in this suite |
| 164 | + // Fallback: search all platforms in this suite, preferring closest match |
157 | 165 | var suiteDir = Path.Combine(testsDir, suite); |
158 | 166 | if (Directory.Exists(suiteDir)) |
| 167 | + { |
| 168 | + var requestedIsSw = IsSoftwareRenderer(parts[1]); |
| 169 | + var requestedApi = parts[0]; // e.g. "Windows.Direct3D11" |
| 170 | + string? bestPath = null; |
| 171 | + int bestScore = -1; |
159 | 172 | foreach (var pDir in Directory.GetDirectories(suiteDir)) |
| 173 | + { |
| 174 | + var pName = Path.GetFileName(pDir); |
| 175 | + if (pName == "local") continue; |
160 | 176 | foreach (var dDir in Directory.GetDirectories(pDir)) |
161 | 177 | { |
162 | 178 | var candidate = Path.Combine(dDir, name); |
163 | | - if (File.Exists(candidate)) return Results.File(candidate, "image/png"); |
| 179 | + if (!File.Exists(candidate)) continue; |
| 180 | + var device = Path.GetFileName(dDir); |
| 181 | + int score = 0; |
| 182 | + if (pName == requestedApi) score += 2; // same API |
| 183 | + if (IsSoftwareRenderer(device) == requestedIsSw) score += 1; // same renderer class |
| 184 | + if (score > bestScore) { bestScore = score; bestPath = candidate; } |
164 | 185 | } |
| 186 | + } |
| 187 | + if (bestPath != null) return Results.File(bestPath, "image/png"); |
| 188 | + } |
165 | 189 | return Results.NotFound(); |
166 | 190 | }); |
167 | 191 |
|
@@ -434,6 +458,12 @@ static List<object> ListPngs(string dir) |
434 | 458 | } |
435 | 459 |
|
436 | 460 |
|
| 461 | +static bool IsSoftwareRenderer(string device) |
| 462 | +{ |
| 463 | + var d = device.ToLowerInvariant(); |
| 464 | + return d.Contains("warp") || d.Contains("swiftshader"); |
| 465 | +} |
| 466 | + |
437 | 467 | static IResult ServeImage(string baseDir, string suite, string platform, string name) |
438 | 468 | { |
439 | 469 | var parts = platform.Split('/', 2); |
|
0 commit comments