• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "common/Assert.h"
18 #include "common/GPUInfo.h"
19 #include "common/Log.h"
20 #include "common/Math.h"
21 #include "common/Platform.h"
22 #include "common/SystemUtils.h"
23 #include "dawn/dawn_proc.h"
24 #include "dawn_wire/WireClient.h"
25 #include "dawn_wire/WireServer.h"
26 #include "utils/ComboRenderPipelineDescriptor.h"
27 #include "utils/PlatformDebugLogger.h"
28 #include "utils/SystemUtils.h"
29 #include "utils/TerribleCommandBuffer.h"
30 #include "utils/TestUtils.h"
31 #include "utils/WGPUHelpers.h"
32 #include "utils/WireHelper.h"
33 
34 #include <algorithm>
35 #include <fstream>
36 #include <iomanip>
37 #include <regex>
38 #include <sstream>
39 #include <unordered_map>
40 
41 #if defined(DAWN_ENABLE_BACKEND_OPENGL)
42 #    include "GLFW/glfw3.h"
43 #    include "dawn_native/OpenGLBackend.h"
44 #endif  // DAWN_ENABLE_BACKEND_OPENGL
45 
46 namespace {
47 
ParamName(wgpu::BackendType type)48     std::string ParamName(wgpu::BackendType type) {
49         switch (type) {
50             case wgpu::BackendType::D3D12:
51                 return "D3D12";
52             case wgpu::BackendType::Metal:
53                 return "Metal";
54             case wgpu::BackendType::Null:
55                 return "Null";
56             case wgpu::BackendType::OpenGL:
57                 return "OpenGL";
58             case wgpu::BackendType::OpenGLES:
59                 return "OpenGLES";
60             case wgpu::BackendType::Vulkan:
61                 return "Vulkan";
62             default:
63                 UNREACHABLE();
64         }
65     }
66 
AdapterTypeName(wgpu::AdapterType type)67     const char* AdapterTypeName(wgpu::AdapterType type) {
68         switch (type) {
69             case wgpu::AdapterType::DiscreteGPU:
70                 return "Discrete GPU";
71             case wgpu::AdapterType::IntegratedGPU:
72                 return "Integrated GPU";
73             case wgpu::AdapterType::CPU:
74                 return "CPU";
75             case wgpu::AdapterType::Unknown:
76                 return "Unknown";
77             default:
78                 UNREACHABLE();
79         }
80     }
81 
82     struct MapReadUserdata {
83         DawnTestBase* test;
84         size_t slot;
85     };
86 
87     DawnTestEnvironment* gTestEnv = nullptr;
88 
89     template <typename T>
printBuffer(testing::AssertionResult & result,const T * buffer,const size_t count)90     void printBuffer(testing::AssertionResult& result, const T* buffer, const size_t count) {
91         static constexpr unsigned int kBytes = sizeof(T);
92 
93         for (size_t index = 0; index < count; ++index) {
94             auto byteView = reinterpret_cast<const uint8_t*>(buffer + index);
95             for (unsigned int b = 0; b < kBytes; ++b) {
96                 char buf[4];
97                 sprintf(buf, "%02X ", byteView[b]);
98                 result << buf;
99             }
100         }
101         result << std::endl;
102     }
103 
104 }  // anonymous namespace
105 
106 const RGBA8 RGBA8::kZero = RGBA8(0, 0, 0, 0);
107 const RGBA8 RGBA8::kBlack = RGBA8(0, 0, 0, 255);
108 const RGBA8 RGBA8::kRed = RGBA8(255, 0, 0, 255);
109 const RGBA8 RGBA8::kGreen = RGBA8(0, 255, 0, 255);
110 const RGBA8 RGBA8::kBlue = RGBA8(0, 0, 255, 255);
111 const RGBA8 RGBA8::kYellow = RGBA8(255, 255, 0, 255);
112 const RGBA8 RGBA8::kWhite = RGBA8(255, 255, 255, 255);
113 
BackendTestConfig(wgpu::BackendType backendType,std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)114 BackendTestConfig::BackendTestConfig(wgpu::BackendType backendType,
115                                      std::initializer_list<const char*> forceEnabledWorkarounds,
116                                      std::initializer_list<const char*> forceDisabledWorkarounds)
117     : backendType(backendType),
118       forceEnabledWorkarounds(forceEnabledWorkarounds),
119       forceDisabledWorkarounds(forceDisabledWorkarounds) {
120 }
121 
D3D12Backend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)122 BackendTestConfig D3D12Backend(std::initializer_list<const char*> forceEnabledWorkarounds,
123                                std::initializer_list<const char*> forceDisabledWorkarounds) {
124     return BackendTestConfig(wgpu::BackendType::D3D12, forceEnabledWorkarounds,
125                              forceDisabledWorkarounds);
126 }
127 
MetalBackend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)128 BackendTestConfig MetalBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
129                                std::initializer_list<const char*> forceDisabledWorkarounds) {
130     return BackendTestConfig(wgpu::BackendType::Metal, forceEnabledWorkarounds,
131                              forceDisabledWorkarounds);
132 }
133 
NullBackend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)134 BackendTestConfig NullBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
135                               std::initializer_list<const char*> forceDisabledWorkarounds) {
136     return BackendTestConfig(wgpu::BackendType::Null, forceEnabledWorkarounds,
137                              forceDisabledWorkarounds);
138 }
139 
OpenGLBackend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)140 BackendTestConfig OpenGLBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
141                                 std::initializer_list<const char*> forceDisabledWorkarounds) {
142     return BackendTestConfig(wgpu::BackendType::OpenGL, forceEnabledWorkarounds,
143                              forceDisabledWorkarounds);
144 }
145 
OpenGLESBackend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)146 BackendTestConfig OpenGLESBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
147                                   std::initializer_list<const char*> forceDisabledWorkarounds) {
148     return BackendTestConfig(wgpu::BackendType::OpenGLES, forceEnabledWorkarounds,
149                              forceDisabledWorkarounds);
150 }
151 
VulkanBackend(std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)152 BackendTestConfig VulkanBackend(std::initializer_list<const char*> forceEnabledWorkarounds,
153                                 std::initializer_list<const char*> forceDisabledWorkarounds) {
154     return BackendTestConfig(wgpu::BackendType::Vulkan, forceEnabledWorkarounds,
155                              forceDisabledWorkarounds);
156 }
157 
TestAdapterProperties(const wgpu::AdapterProperties & properties,bool selected)158 TestAdapterProperties::TestAdapterProperties(const wgpu::AdapterProperties& properties,
159                                              bool selected)
160     : wgpu::AdapterProperties(properties), adapterName(properties.name), selected(selected) {
161 }
162 
AdapterTestParam(const BackendTestConfig & config,const TestAdapterProperties & adapterProperties)163 AdapterTestParam::AdapterTestParam(const BackendTestConfig& config,
164                                    const TestAdapterProperties& adapterProperties)
165     : adapterProperties(adapterProperties),
166       forceEnabledWorkarounds(config.forceEnabledWorkarounds),
167       forceDisabledWorkarounds(config.forceDisabledWorkarounds) {
168 }
169 
operator <<(std::ostream & os,const AdapterTestParam & param)170 std::ostream& operator<<(std::ostream& os, const AdapterTestParam& param) {
171     os << ParamName(param.adapterProperties.backendType) << " "
172        << param.adapterProperties.adapterName;
173 
174     // In a Windows Remote Desktop session there are two adapters named "Microsoft Basic Render
175     // Driver" with different adapter types. We must differentiate them to avoid any tests using the
176     // same name.
177     if (param.adapterProperties.deviceID == 0x008C) {
178         std::string adapterType = AdapterTypeName(param.adapterProperties.adapterType);
179         os << " " << adapterType;
180     }
181 
182     for (const char* forceEnabledWorkaround : param.forceEnabledWorkarounds) {
183         os << "; e:" << forceEnabledWorkaround;
184     }
185     for (const char* forceDisabledWorkaround : param.forceDisabledWorkarounds) {
186         os << "; d:" << forceDisabledWorkaround;
187     }
188     return os;
189 }
190 
PrintToStringParamName(const char * test)191 DawnTestBase::PrintToStringParamName::PrintToStringParamName(const char* test) : mTest(test) {
192 }
193 
SanitizeParamName(std::string paramName,size_t index) const194 std::string DawnTestBase::PrintToStringParamName::SanitizeParamName(std::string paramName,
195                                                                     size_t index) const {
196     // Sanitize the adapter name for GoogleTest
197     std::string sanitizedName = std::regex_replace(paramName, std::regex("[^a-zA-Z0-9]+"), "_");
198 
199     // Strip trailing underscores, if any.
200     while (sanitizedName.back() == '_') {
201         sanitizedName.resize(sanitizedName.length() - 1);
202     }
203 
204     // We don't know the the test name at this point, but the format usually looks like
205     // this.
206     std::string prefix = mTest + ".TheTestNameUsuallyGoesHere/";
207     std::string testFormat = prefix + sanitizedName;
208     if (testFormat.length() > 220) {
209         // The bots don't support test names longer than 256. Shorten the name and append a unique
210         // index if we're close. The failure log will still print the full param name.
211         std::string suffix = std::string("__") + std::to_string(index);
212         size_t targetLength = sanitizedName.length();
213         targetLength -= testFormat.length() - 220;
214         targetLength -= suffix.length();
215         sanitizedName.resize(targetLength);
216         sanitizedName = sanitizedName + suffix;
217     }
218     return sanitizedName;
219 }
220 
221 // Implementation of DawnTestEnvironment
222 
InitDawnEnd2EndTestEnvironment(int argc,char ** argv)223 void InitDawnEnd2EndTestEnvironment(int argc, char** argv) {
224     gTestEnv = new DawnTestEnvironment(argc, argv);
225     testing::AddGlobalTestEnvironment(gTestEnv);
226 }
227 
228 // static
SetEnvironment(DawnTestEnvironment * env)229 void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) {
230     gTestEnv = env;
231 }
232 
DawnTestEnvironment(int argc,char ** argv)233 DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) {
234     ParseArgs(argc, argv);
235 
236     if (mBackendValidationLevel != dawn_native::BackendValidationLevel::Disabled) {
237         mPlatformDebugLogger =
238             std::unique_ptr<utils::PlatformDebugLogger>(utils::CreatePlatformDebugLogger());
239     }
240 
241     // Create a temporary instance to select available and preferred adapters. This is done before
242     // test instantiation so GetAvailableAdapterTestParamsForBackends can generate test
243     // parameterizations all selected adapters. We drop the instance at the end of this function
244     // because the Vulkan validation layers use static global mutexes which behave badly when
245     // Chromium's test launcher forks the test process. The instance will be recreated on test
246     // environment setup.
247     std::unique_ptr<dawn_native::Instance> instance = CreateInstanceAndDiscoverAdapters();
248     ASSERT(instance);
249 
250     SelectPreferredAdapterProperties(instance.get());
251     PrintTestConfigurationAndAdapterInfo(instance.get());
252 }
253 
254 DawnTestEnvironment::~DawnTestEnvironment() = default;
255 
ParseArgs(int argc,char ** argv)256 void DawnTestEnvironment::ParseArgs(int argc, char** argv) {
257     size_t argLen = 0;  // Set when parsing --arg=X arguments
258     for (int i = 1; i < argc; ++i) {
259         if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
260             mUseWire = true;
261             continue;
262         }
263 
264         if (strcmp("--run-suppressed-tests", argv[i]) == 0) {
265             mRunSuppressedTests = true;
266             continue;
267         }
268 
269         constexpr const char kEnableBackendValidationSwitch[] = "--enable-backend-validation";
270         argLen = sizeof(kEnableBackendValidationSwitch) - 1;
271         if (strncmp(argv[i], kEnableBackendValidationSwitch, argLen) == 0) {
272             const char* level = argv[i] + argLen;
273             if (level[0] != '\0') {
274                 if (strcmp(level, "=full") == 0) {
275                     mBackendValidationLevel = dawn_native::BackendValidationLevel::Full;
276                 } else if (strcmp(level, "=partial") == 0) {
277                     mBackendValidationLevel = dawn_native::BackendValidationLevel::Partial;
278                 } else if (strcmp(level, "=disabled") == 0) {
279                     mBackendValidationLevel = dawn_native::BackendValidationLevel::Disabled;
280                 } else {
281                     dawn::ErrorLog() << "Invalid backend validation level" << level;
282                     UNREACHABLE();
283                 }
284             } else {
285                 mBackendValidationLevel = dawn_native::BackendValidationLevel::Partial;
286             }
287             continue;
288         }
289 
290         if (strcmp("-c", argv[i]) == 0 || strcmp("--begin-capture-on-startup", argv[i]) == 0) {
291             mBeginCaptureOnStartup = true;
292             continue;
293         }
294 
295         if (mToggleParser.ParseEnabledToggles(argv[i])) {
296             continue;
297         }
298 
299         if (mToggleParser.ParseDisabledToggles(argv[i])) {
300             continue;
301         }
302 
303         constexpr const char kVendorIdFilterArg[] = "--adapter-vendor-id=";
304         argLen = sizeof(kVendorIdFilterArg) - 1;
305         if (strncmp(argv[i], kVendorIdFilterArg, argLen) == 0) {
306             const char* vendorIdFilter = argv[i] + argLen;
307             if (vendorIdFilter[0] != '\0') {
308                 mVendorIdFilter = strtoul(vendorIdFilter, nullptr, 16);
309                 // Set filter flag if vendor id is non-zero.
310                 mHasVendorIdFilter = mVendorIdFilter != 0;
311             }
312             continue;
313         }
314 
315         constexpr const char kExclusiveDeviceTypePreferenceArg[] =
316             "--exclusive-device-type-preference=";
317         argLen = sizeof(kExclusiveDeviceTypePreferenceArg) - 1;
318         if (strncmp(argv[i], kExclusiveDeviceTypePreferenceArg, argLen) == 0) {
319             const char* preference = argv[i] + argLen;
320             if (preference[0] != '\0') {
321                 std::istringstream ss(preference);
322                 std::string type;
323                 while (std::getline(ss, type, ',')) {
324                     if (strcmp(type.c_str(), "discrete") == 0) {
325                         mDevicePreferences.push_back(dawn_native::DeviceType::DiscreteGPU);
326                     } else if (strcmp(type.c_str(), "integrated") == 0) {
327                         mDevicePreferences.push_back(dawn_native::DeviceType::IntegratedGPU);
328                     } else if (strcmp(type.c_str(), "cpu") == 0) {
329                         mDevicePreferences.push_back(dawn_native::DeviceType::CPU);
330                     } else {
331                         dawn::ErrorLog() << "Invalid device type preference: " << type;
332                         UNREACHABLE();
333                     }
334                 }
335             }
336             continue;
337         }
338 
339         constexpr const char kWireTraceDirArg[] = "--wire-trace-dir=";
340         argLen = sizeof(kWireTraceDirArg) - 1;
341         if (strncmp(argv[i], kWireTraceDirArg, argLen) == 0) {
342             mWireTraceDir = argv[i] + argLen;
343             continue;
344         }
345 
346         constexpr const char kBackendArg[] = "--backend=";
347         argLen = sizeof(kBackendArg) - 1;
348         if (strncmp(argv[i], kBackendArg, argLen) == 0) {
349             const char* param = argv[i] + argLen;
350             if (strcmp("d3d12", param) == 0) {
351                 mBackendTypeFilter = wgpu::BackendType::D3D12;
352             } else if (strcmp("metal", param) == 0) {
353                 mBackendTypeFilter = wgpu::BackendType::Metal;
354             } else if (strcmp("null", param) == 0) {
355                 mBackendTypeFilter = wgpu::BackendType::Null;
356             } else if (strcmp("opengl", param) == 0) {
357                 mBackendTypeFilter = wgpu::BackendType::OpenGL;
358             } else if (strcmp("opengles", param) == 0) {
359                 mBackendTypeFilter = wgpu::BackendType::OpenGLES;
360             } else if (strcmp("vulkan", param) == 0) {
361                 mBackendTypeFilter = wgpu::BackendType::Vulkan;
362             } else {
363                 dawn::ErrorLog()
364                     << "Invalid backend \"" << param
365                     << "\". Valid backends are: d3d12, metal, null, opengl, opengles, vulkan.";
366                 UNREACHABLE();
367             }
368             mHasBackendTypeFilter = true;
369             continue;
370         }
371         if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
372             dawn::InfoLog()
373                 << "\n\nUsage: " << argv[0]
374                 << " [GTEST_FLAGS...] [-w] [-c]\n"
375                    "    [--enable-toggles=toggles] [--disable-toggles=toggles]\n"
376                    "    [--backend=x]\n"
377                    "    [--adapter-vendor-id=x] "
378                    "[--enable-backend-validation[=full,partial,disabled]]\n"
379                    "    [--exclusive-device-type-preference=integrated,cpu,discrete]\n\n"
380                    "  -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"
381                    "  -c, --begin-capture-on-startup: Begin debug capture on startup "
382                    "(defaults to no capture)\n"
383                    "  --enable-backend-validation: Enables backend validation. Defaults to \n"
384                    "    'partial' to enable only minimum backend validation. Set to 'full' to\n"
385                    "    enable all available backend validation with less performance overhead.\n"
386                    "    Set to 'disabled' to run with no validation (same as no flag).\n"
387                    "  --enable-toggles: Comma-delimited list of Dawn toggles to enable.\n"
388                    "    ex.) skip_validation,disable_robustness,turn_off_vsync\n"
389                    "  --disable-toggles: Comma-delimited list of Dawn toggles to disable\n"
390                    "  --adapter-vendor-id: Select adapter by vendor id to run end2end tests"
391                    "on multi-GPU systems \n"
392                    "  --backend: Select adapter by backend type. Valid backends are: d3d12, metal, "
393                    "null, opengl, opengles, vulkan\n"
394                    "  --exclusive-device-type-preference: Comma-delimited list of preferred device "
395                    "types. For each backend, tests will run only on adapters that match the first "
396                    "available device type\n"
397                    "  --run-suppressed-tests: Run all the tests that will be skipped by the macro "
398                    "DAWN_SUPPRESS_TEST_IF()\n";
399             continue;
400         }
401 
402         // Skip over args that look like they're for Googletest.
403         constexpr const char kGtestArgPrefix[] = "--gtest_";
404         if (strncmp(kGtestArgPrefix, argv[i], sizeof(kGtestArgPrefix) - 1) == 0) {
405             continue;
406         }
407 
408         dawn::WarningLog() << " Unused argument: " << argv[i];
409     }
410 }
411 
CreateInstanceAndDiscoverAdapters()412 std::unique_ptr<dawn_native::Instance> DawnTestEnvironment::CreateInstanceAndDiscoverAdapters() {
413     auto instance = std::make_unique<dawn_native::Instance>();
414     instance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup);
415     instance->SetBackendValidationLevel(mBackendValidationLevel);
416     instance->DiscoverDefaultAdapters();
417 
418 #ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
419     if (!glfwInit()) {
420         return instance;
421     }
422     glfwDefaultWindowHints();
423     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
424     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
425     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
426     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
427     glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
428 
429     mOpenGLWindow = glfwCreateWindow(400, 400, "Dawn OpenGL test window", nullptr, nullptr);
430 
431     glfwMakeContextCurrent(mOpenGLWindow);
432     dawn_native::opengl::AdapterDiscoveryOptions adapterOptions;
433     adapterOptions.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
434     instance->DiscoverAdapters(&adapterOptions);
435 #endif  // DAWN_ENABLE_BACKEND_DESKTOP_GL
436 
437 #ifdef DAWN_ENABLE_BACKEND_OPENGLES
438 
439     ScopedEnvironmentVar angleDefaultPlatform;
440     if (GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) {
441         angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", "swiftshader");
442     }
443 
444     if (!glfwInit()) {
445         return instance;
446     }
447     glfwDefaultWindowHints();
448     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
449     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
450     glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
451     glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
452     glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
453 
454     mOpenGLESWindow = glfwCreateWindow(400, 400, "Dawn OpenGLES test window", nullptr, nullptr);
455 
456     glfwMakeContextCurrent(mOpenGLESWindow);
457     dawn_native::opengl::AdapterDiscoveryOptionsES adapterOptionsES;
458     adapterOptionsES.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
459     instance->DiscoverAdapters(&adapterOptionsES);
460     glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
461 #endif  // DAWN_ENABLE_BACKEND_OPENGLES
462 
463     return instance;
464 }
465 
GetOpenGLWindow() const466 GLFWwindow* DawnTestEnvironment::GetOpenGLWindow() const {
467     return mOpenGLWindow;
468 }
469 
GetOpenGLESWindow() const470 GLFWwindow* DawnTestEnvironment::GetOpenGLESWindow() const {
471     return mOpenGLESWindow;
472 }
473 
SelectPreferredAdapterProperties(const dawn_native::Instance * instance)474 void DawnTestEnvironment::SelectPreferredAdapterProperties(const dawn_native::Instance* instance) {
475     // Get the first available preferred device type.
476     dawn_native::DeviceType preferredDeviceType = static_cast<dawn_native::DeviceType>(-1);
477     bool hasDevicePreference = false;
478     for (dawn_native::DeviceType devicePreference : mDevicePreferences) {
479         for (const dawn_native::Adapter& adapter : instance->GetAdapters()) {
480             wgpu::AdapterProperties properties;
481             adapter.GetProperties(&properties);
482 
483             if (adapter.GetDeviceType() == devicePreference) {
484                 preferredDeviceType = devicePreference;
485                 hasDevicePreference = true;
486                 break;
487             }
488         }
489         if (hasDevicePreference) {
490             break;
491         }
492     }
493 
494     std::set<std::pair<wgpu::BackendType, std::string>> adapterNameSet;
495     for (const dawn_native::Adapter& adapter : instance->GetAdapters()) {
496         wgpu::AdapterProperties properties;
497         adapter.GetProperties(&properties);
498 
499         // All adapters are selected by default.
500         bool selected = true;
501         // The adapter is deselected if:
502         if (mHasBackendTypeFilter) {
503             // It doesn't match the backend type, if present.
504             selected &= properties.backendType == mBackendTypeFilter;
505         }
506         if (mHasVendorIdFilter) {
507             // It doesn't match the vendor id, if present.
508             selected &= mVendorIdFilter == properties.vendorID;
509 
510             if (!mDevicePreferences.empty()) {
511                 dawn::WarningLog() << "Vendor ID filter provided. Ignoring device type preference.";
512             }
513         }
514         if (hasDevicePreference) {
515             // There is a device preference and:
516             selected &=
517                 // The device type doesn't match the first available preferred type for that
518                 // backend, if present.
519                 (adapter.GetDeviceType() == preferredDeviceType) ||
520                 // Always select Unknown OpenGL adapters if we don't want a CPU adapter.
521                 // OpenGL will usually be unknown because we can't query the device type.
522                 // If we ever have Swiftshader GL (unlikely), we could set the DeviceType properly.
523                 (preferredDeviceType != dawn_native::DeviceType::CPU &&
524                  adapter.GetDeviceType() == dawn_native::DeviceType::Unknown &&
525                  (properties.backendType == wgpu::BackendType::OpenGL ||
526                   properties.backendType == wgpu::BackendType::OpenGLES)) ||
527                 // Always select the Null backend. There are few tests on this backend, and they run
528                 // quickly. This is temporary as to not lose coverage. We can group it with
529                 // Swiftshader as a CPU adapter when we have Swiftshader tests.
530                 (properties.backendType == wgpu::BackendType::Null);
531         }
532 
533         // In Windows Remote Desktop sessions we may be able to discover multiple adapters that
534         // have the same name and backend type. We will just choose one adapter from them in our
535         // tests.
536         const auto adapterTypeAndName =
537             std::make_pair(properties.backendType, std::string(properties.name));
538         if (adapterNameSet.find(adapterTypeAndName) == adapterNameSet.end()) {
539             adapterNameSet.insert(adapterTypeAndName);
540             mAdapterProperties.emplace_back(properties, selected);
541         }
542     }
543 }
544 
GetAvailableAdapterTestParamsForBackends(const BackendTestConfig * params,size_t numParams)545 std::vector<AdapterTestParam> DawnTestEnvironment::GetAvailableAdapterTestParamsForBackends(
546     const BackendTestConfig* params,
547     size_t numParams) {
548     std::vector<AdapterTestParam> testParams;
549     for (size_t i = 0; i < numParams; ++i) {
550         for (const auto& adapterProperties : mAdapterProperties) {
551             if (params[i].backendType == adapterProperties.backendType &&
552                 adapterProperties.selected) {
553                 testParams.push_back(AdapterTestParam(params[i], adapterProperties));
554             }
555         }
556     }
557     return testParams;
558 }
559 
PrintTestConfigurationAndAdapterInfo(dawn_native::Instance * instance) const560 void DawnTestEnvironment::PrintTestConfigurationAndAdapterInfo(
561     dawn_native::Instance* instance) const {
562     dawn::LogMessage log = dawn::InfoLog();
563     log << "Testing configuration\n"
564            "---------------------\n"
565            "UseWire: "
566         << (mUseWire ? "true" : "false")
567         << "\n"
568            "Run suppressed tests: "
569         << (mRunSuppressedTests ? "true" : "false")
570         << "\n"
571            "BackendValidation: ";
572 
573     switch (mBackendValidationLevel) {
574         case dawn_native::BackendValidationLevel::Full:
575             log << "full";
576             break;
577         case dawn_native::BackendValidationLevel::Partial:
578             log << "partial";
579             break;
580         case dawn_native::BackendValidationLevel::Disabled:
581             log << "disabled";
582             break;
583         default:
584             UNREACHABLE();
585     }
586 
587     if (GetEnabledToggles().size() > 0) {
588         log << "\n"
589                "Enabled Toggles\n";
590         for (const std::string& toggle : GetEnabledToggles()) {
591             const dawn_native::ToggleInfo* info = instance->GetToggleInfo(toggle.c_str());
592             ASSERT(info != nullptr);
593             log << " - " << info->name << ": " << info->description << "\n";
594         }
595     }
596 
597     if (GetDisabledToggles().size() > 0) {
598         log << "\n"
599                "Disabled Toggles\n";
600         for (const std::string& toggle : GetDisabledToggles()) {
601             const dawn_native::ToggleInfo* info = instance->GetToggleInfo(toggle.c_str());
602             ASSERT(info != nullptr);
603             log << " - " << info->name << ": " << info->description << "\n";
604         }
605     }
606 
607     log << "\n"
608            "BeginCaptureOnStartup: "
609         << (mBeginCaptureOnStartup ? "true" : "false")
610         << "\n"
611            "\n"
612         << "System adapters: \n";
613 
614     for (const TestAdapterProperties& properties : mAdapterProperties) {
615         std::ostringstream vendorId;
616         std::ostringstream deviceId;
617         vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
618                  << properties.vendorID;
619         deviceId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
620                  << properties.deviceID;
621 
622         // Preparing for outputting hex numbers
623         log << std::showbase << std::hex << std::setfill('0') << std::setw(4)
624 
625             << " - \"" << properties.adapterName << "\" - \"" << properties.driverDescription
626             << "\"\n"
627             << "   type: " << AdapterTypeName(properties.adapterType)
628             << ", backend: " << ParamName(properties.backendType) << "\n"
629             << "   vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str()
630             << (properties.selected ? " [Selected]" : "") << "\n";
631     }
632 }
633 
SetUp()634 void DawnTestEnvironment::SetUp() {
635     mInstance = CreateInstanceAndDiscoverAdapters();
636     ASSERT(mInstance);
637 }
638 
TearDown()639 void DawnTestEnvironment::TearDown() {
640     // When Vulkan validation layers are enabled, it's unsafe to call Vulkan APIs in the destructor
641     // of a static/global variable, so the instance must be manually released beforehand.
642     mInstance.reset();
643 }
644 
UsesWire() const645 bool DawnTestEnvironment::UsesWire() const {
646     return mUseWire;
647 }
648 
RunSuppressedTests() const649 bool DawnTestEnvironment::RunSuppressedTests() const {
650     return mRunSuppressedTests;
651 }
652 
GetBackendValidationLevel() const653 dawn_native::BackendValidationLevel DawnTestEnvironment::GetBackendValidationLevel() const {
654     return mBackendValidationLevel;
655 }
656 
GetInstance() const657 dawn_native::Instance* DawnTestEnvironment::GetInstance() const {
658     return mInstance.get();
659 }
660 
HasVendorIdFilter() const661 bool DawnTestEnvironment::HasVendorIdFilter() const {
662     return mHasVendorIdFilter;
663 }
664 
GetVendorIdFilter() const665 uint32_t DawnTestEnvironment::GetVendorIdFilter() const {
666     return mVendorIdFilter;
667 }
668 
HasBackendTypeFilter() const669 bool DawnTestEnvironment::HasBackendTypeFilter() const {
670     return mHasBackendTypeFilter;
671 }
672 
GetBackendTypeFilter() const673 wgpu::BackendType DawnTestEnvironment::GetBackendTypeFilter() const {
674     return mBackendTypeFilter;
675 }
676 
GetWireTraceDir() const677 const char* DawnTestEnvironment::GetWireTraceDir() const {
678     if (mWireTraceDir.length() == 0) {
679         return nullptr;
680     }
681     return mWireTraceDir.c_str();
682 }
683 
GetEnabledToggles() const684 const std::vector<std::string>& DawnTestEnvironment::GetEnabledToggles() const {
685     return mToggleParser.GetEnabledToggles();
686 }
687 
GetDisabledToggles() const688 const std::vector<std::string>& DawnTestEnvironment::GetDisabledToggles() const {
689     return mToggleParser.GetDisabledToggles();
690 }
691 
692 // Implementation of DawnTest
693 
DawnTestBase(const AdapterTestParam & param)694 DawnTestBase::DawnTestBase(const AdapterTestParam& param)
695     : mParam(param),
696       mWireHelper(utils::CreateWireHelper(gTestEnv->UsesWire(), gTestEnv->GetWireTraceDir())) {
697 }
698 
~DawnTestBase()699 DawnTestBase::~DawnTestBase() {
700     // We need to destroy child objects before the Device
701     mReadbackSlots.clear();
702     queue = wgpu::Queue();
703     device = wgpu::Device();
704 
705     // D3D12's GPU-based validation will accumulate objects over time if the backend device is not
706     // destroyed and recreated, so we reset it here.
707     if (IsD3D12() && IsBackendValidationEnabled()) {
708         mBackendAdapter.ResetInternalDeviceForTesting();
709     }
710     mWireHelper.reset();
711 }
712 
IsD3D12() const713 bool DawnTestBase::IsD3D12() const {
714     return mParam.adapterProperties.backendType == wgpu::BackendType::D3D12;
715 }
716 
IsMetal() const717 bool DawnTestBase::IsMetal() const {
718     return mParam.adapterProperties.backendType == wgpu::BackendType::Metal;
719 }
720 
IsNull() const721 bool DawnTestBase::IsNull() const {
722     return mParam.adapterProperties.backendType == wgpu::BackendType::Null;
723 }
724 
IsOpenGL() const725 bool DawnTestBase::IsOpenGL() const {
726     return mParam.adapterProperties.backendType == wgpu::BackendType::OpenGL;
727 }
728 
IsOpenGLES() const729 bool DawnTestBase::IsOpenGLES() const {
730     return mParam.adapterProperties.backendType == wgpu::BackendType::OpenGLES;
731 }
732 
IsVulkan() const733 bool DawnTestBase::IsVulkan() const {
734     return mParam.adapterProperties.backendType == wgpu::BackendType::Vulkan;
735 }
736 
IsAMD() const737 bool DawnTestBase::IsAMD() const {
738     return gpu_info::IsAMD(mParam.adapterProperties.vendorID);
739 }
740 
IsARM() const741 bool DawnTestBase::IsARM() const {
742     return gpu_info::IsARM(mParam.adapterProperties.vendorID);
743 }
744 
IsImgTec() const745 bool DawnTestBase::IsImgTec() const {
746     return gpu_info::IsImgTec(mParam.adapterProperties.vendorID);
747 }
748 
IsIntel() const749 bool DawnTestBase::IsIntel() const {
750     return gpu_info::IsIntel(mParam.adapterProperties.vendorID);
751 }
752 
IsNvidia() const753 bool DawnTestBase::IsNvidia() const {
754     return gpu_info::IsNvidia(mParam.adapterProperties.vendorID);
755 }
756 
IsQualcomm() const757 bool DawnTestBase::IsQualcomm() const {
758     return gpu_info::IsQualcomm(mParam.adapterProperties.vendorID);
759 }
760 
IsSwiftshader() const761 bool DawnTestBase::IsSwiftshader() const {
762     return gpu_info::IsSwiftshader(mParam.adapterProperties.vendorID,
763                                    mParam.adapterProperties.deviceID);
764 }
765 
IsANGLE() const766 bool DawnTestBase::IsANGLE() const {
767     return !mParam.adapterProperties.adapterName.find("ANGLE");
768 }
769 
IsWARP() const770 bool DawnTestBase::IsWARP() const {
771     return gpu_info::IsWARP(mParam.adapterProperties.vendorID, mParam.adapterProperties.deviceID);
772 }
773 
IsWindows() const774 bool DawnTestBase::IsWindows() const {
775 #ifdef DAWN_PLATFORM_WINDOWS
776     return true;
777 #else
778     return false;
779 #endif
780 }
781 
IsLinux() const782 bool DawnTestBase::IsLinux() const {
783 #ifdef DAWN_PLATFORM_LINUX
784     return true;
785 #else
786     return false;
787 #endif
788 }
789 
IsMacOS(int32_t majorVersion,int32_t minorVersion) const790 bool DawnTestBase::IsMacOS(int32_t majorVersion, int32_t minorVersion) const {
791 #ifdef DAWN_PLATFORM_MACOS
792     if (majorVersion == -1 && minorVersion == -1) {
793         return true;
794     }
795     int32_t majorVersionOut, minorVersionOut = 0;
796     GetMacOSVersion(&majorVersionOut, &minorVersionOut);
797     return (majorVersion != -1 && majorVersion == majorVersionOut) &&
798            (minorVersion != -1 && minorVersion == minorVersionOut);
799 #else
800     return false;
801 #endif
802 }
803 
UsesWire() const804 bool DawnTestBase::UsesWire() const {
805     return gTestEnv->UsesWire();
806 }
807 
IsBackendValidationEnabled() const808 bool DawnTestBase::IsBackendValidationEnabled() const {
809     return gTestEnv->GetBackendValidationLevel() != dawn_native::BackendValidationLevel::Disabled;
810 }
811 
RunSuppressedTests() const812 bool DawnTestBase::RunSuppressedTests() const {
813     return gTestEnv->RunSuppressedTests();
814 }
815 
IsDXC() const816 bool DawnTestBase::IsDXC() const {
817     return HasToggleEnabled("use_dxc");
818 }
819 
IsAsan() const820 bool DawnTestBase::IsAsan() const {
821 #if defined(ADDRESS_SANITIZER)
822     return true;
823 #else
824     return false;
825 #endif
826 }
827 
HasToggleEnabled(const char * toggle) const828 bool DawnTestBase::HasToggleEnabled(const char* toggle) const {
829     auto toggles = dawn_native::GetTogglesUsed(backendDevice);
830     return std::find_if(toggles.begin(), toggles.end(), [toggle](const char* name) {
831                return strcmp(toggle, name) == 0;
832            }) != toggles.end();
833 }
834 
HasVendorIdFilter() const835 bool DawnTestBase::HasVendorIdFilter() const {
836     return gTestEnv->HasVendorIdFilter();
837 }
838 
GetVendorIdFilter() const839 uint32_t DawnTestBase::GetVendorIdFilter() const {
840     return gTestEnv->GetVendorIdFilter();
841 }
842 
HasBackendTypeFilter() const843 bool DawnTestBase::HasBackendTypeFilter() const {
844     return gTestEnv->HasBackendTypeFilter();
845 }
846 
GetBackendTypeFilter() const847 wgpu::BackendType DawnTestBase::GetBackendTypeFilter() const {
848     return gTestEnv->GetBackendTypeFilter();
849 }
850 
GetInstance() const851 wgpu::Instance DawnTestBase::GetInstance() const {
852     return gTestEnv->GetInstance()->Get();
853 }
854 
GetAdapter() const855 dawn_native::Adapter DawnTestBase::GetAdapter() const {
856     return mBackendAdapter;
857 }
858 
GetRequiredFeatures()859 std::vector<const char*> DawnTestBase::GetRequiredFeatures() {
860     return {};
861 }
862 
GetRequiredLimits(const wgpu::SupportedLimits &)863 wgpu::RequiredLimits DawnTestBase::GetRequiredLimits(const wgpu::SupportedLimits&) {
864     return {};
865 }
866 
GetAdapterProperties() const867 const wgpu::AdapterProperties& DawnTestBase::GetAdapterProperties() const {
868     return mParam.adapterProperties;
869 }
870 
GetSupportedLimits()871 wgpu::SupportedLimits DawnTestBase::GetSupportedLimits() {
872     WGPUSupportedLimits supportedLimits;
873     supportedLimits.nextInChain = nullptr;
874     dawn_native::GetProcs().deviceGetLimits(backendDevice, &supportedLimits);
875     return *reinterpret_cast<wgpu::SupportedLimits*>(&supportedLimits);
876 }
877 
SupportsFeatures(const std::vector<const char * > & features)878 bool DawnTestBase::SupportsFeatures(const std::vector<const char*>& features) {
879     ASSERT(mBackendAdapter);
880     std::set<std::string> supportedFeaturesSet;
881     for (const char* supportedFeatureName : mBackendAdapter.GetSupportedFeatures()) {
882         supportedFeaturesSet.insert(supportedFeatureName);
883     }
884 
885     for (const char* featureName : features) {
886         if (supportedFeaturesSet.find(featureName) == supportedFeaturesSet.end()) {
887             return false;
888         }
889     }
890 
891     return true;
892 }
893 
SetUp()894 void DawnTestBase::SetUp() {
895     {
896         // Find the adapter that exactly matches our adapter properties.
897         const auto& adapters = gTestEnv->GetInstance()->GetAdapters();
898         const auto& it = std::find_if(
899             adapters.begin(), adapters.end(), [&](const dawn_native::Adapter& adapter) {
900                 wgpu::AdapterProperties properties;
901                 adapter.GetProperties(&properties);
902 
903                 return (mParam.adapterProperties.selected &&
904                         properties.deviceID == mParam.adapterProperties.deviceID &&
905                         properties.vendorID == mParam.adapterProperties.vendorID &&
906                         properties.adapterType == mParam.adapterProperties.adapterType &&
907                         properties.backendType == mParam.adapterProperties.backendType &&
908                         strcmp(properties.name, mParam.adapterProperties.adapterName.c_str()) == 0);
909             });
910         ASSERT(it != adapters.end());
911         mBackendAdapter = *it;
912     }
913 
914     // Setup the per-test platform. Tests can provide one by overloading CreateTestPlatform.
915     mTestPlatform = CreateTestPlatform();
916     gTestEnv->GetInstance()->SetPlatform(mTestPlatform.get());
917 
918     // Create the device from the adapter
919     for (const char* forceEnabledWorkaround : mParam.forceEnabledWorkarounds) {
920         ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceEnabledWorkaround) != nullptr);
921     }
922     for (const char* forceDisabledWorkaround : mParam.forceDisabledWorkarounds) {
923         ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceDisabledWorkaround) != nullptr);
924     }
925     dawn_native::DawnDeviceDescriptor deviceDescriptor = {};
926     deviceDescriptor.forceEnabledToggles = mParam.forceEnabledWorkarounds;
927     deviceDescriptor.forceDisabledToggles = mParam.forceDisabledWorkarounds;
928     deviceDescriptor.requiredFeatures = GetRequiredFeatures();
929 
930     wgpu::SupportedLimits supportedLimits;
931     mBackendAdapter.GetLimits(reinterpret_cast<WGPUSupportedLimits*>(&supportedLimits));
932     wgpu::RequiredLimits requiredLimits = GetRequiredLimits(supportedLimits);
933     deviceDescriptor.requiredLimits = reinterpret_cast<WGPURequiredLimits*>(&requiredLimits);
934 
935     // Disabled disallowing unsafe APIs so we can test them.
936     deviceDescriptor.forceDisabledToggles.push_back("disallow_unsafe_apis");
937 
938     for (const std::string& toggle : gTestEnv->GetEnabledToggles()) {
939         const dawn_native::ToggleInfo* info =
940             gTestEnv->GetInstance()->GetToggleInfo(toggle.c_str());
941         ASSERT(info != nullptr);
942         deviceDescriptor.forceEnabledToggles.push_back(info->name);
943     }
944 
945     for (const std::string& toggle : gTestEnv->GetDisabledToggles()) {
946         const dawn_native::ToggleInfo* info =
947             gTestEnv->GetInstance()->GetToggleInfo(toggle.c_str());
948         ASSERT(info != nullptr);
949         deviceDescriptor.forceDisabledToggles.push_back(info->name);
950     }
951 
952     std::tie(device, backendDevice) =
953         mWireHelper->RegisterDevice(mBackendAdapter.CreateDevice(&deviceDescriptor));
954     ASSERT_NE(nullptr, backendDevice);
955 
956     std::string traceName =
957         std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
958         "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
959     mWireHelper->BeginWireTrace(traceName.c_str());
960 
961     queue = device.GetQueue();
962 
963     device.SetUncapturedErrorCallback(OnDeviceError, this);
964     device.SetDeviceLostCallback(OnDeviceLost, this);
965 #if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
966     if (IsOpenGL()) {
967         glfwMakeContextCurrent(gTestEnv->GetOpenGLWindow());
968     }
969 #endif  // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
970 #if defined(DAWN_ENABLE_BACKEND_OPENGLES)
971     if (IsOpenGLES()) {
972         glfwMakeContextCurrent(gTestEnv->GetOpenGLESWindow());
973     }
974 #endif  // defined(DAWN_ENABLE_BACKEND_OPENGLES)
975 
976     device.SetLoggingCallback(
977         [](WGPULoggingType type, char const* message, void*) {
978             switch (type) {
979                 case WGPULoggingType_Verbose:
980                     dawn::DebugLog() << message;
981                     break;
982                 case WGPULoggingType_Warning:
983                     dawn::WarningLog() << message;
984                     break;
985                 case WGPULoggingType_Error:
986                     dawn::ErrorLog() << message;
987                     break;
988                 default:
989                     dawn::InfoLog() << message;
990                     break;
991             }
992         },
993         nullptr);
994 }
995 
TearDown()996 void DawnTestBase::TearDown() {
997     FlushWire();
998 
999     MapSlotsSynchronously();
1000     ResolveExpectations();
1001 
1002     for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
1003         mReadbackSlots[i].buffer.Unmap();
1004     }
1005 
1006     if (!UsesWire()) {
1007         EXPECT_EQ(mLastWarningCount,
1008                   dawn_native::GetDeprecationWarningCountForTesting(device.Get()));
1009     }
1010 
1011     // The device will be destroyed soon after, so we want to set the expectation.
1012     ExpectDeviceDestruction();
1013 }
1014 
StartExpectDeviceError(testing::Matcher<std::string> errorMatcher)1015 void DawnTestBase::StartExpectDeviceError(testing::Matcher<std::string> errorMatcher) {
1016     mExpectError = true;
1017     mError = false;
1018     mErrorMatcher = errorMatcher;
1019 }
1020 
EndExpectDeviceError()1021 bool DawnTestBase::EndExpectDeviceError() {
1022     mExpectError = false;
1023     mErrorMatcher = testing::_;
1024     return mError;
1025 }
1026 
ExpectDeviceDestruction()1027 void DawnTestBase::ExpectDeviceDestruction() {
1028     mExpectDestruction = true;
1029 }
1030 
1031 // static
OnDeviceError(WGPUErrorType type,const char * message,void * userdata)1032 void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
1033     ASSERT(type != WGPUErrorType_NoError);
1034     DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
1035 
1036     ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
1037     ASSERT_FALSE(self->mError) << "Got two errors in expect block";
1038     if (self->mExpectError) {
1039         ASSERT_THAT(message, self->mErrorMatcher);
1040     }
1041     self->mError = true;
1042 }
1043 
OnDeviceLost(WGPUDeviceLostReason reason,const char * message,void * userdata)1044 void DawnTestBase::OnDeviceLost(WGPUDeviceLostReason reason, const char* message, void* userdata) {
1045     DawnTestBase* self = static_cast<DawnTestBase*>(userdata);
1046     if (self->mExpectDestruction) {
1047         EXPECT_EQ(reason, WGPUDeviceLostReason_Destroyed);
1048         return;
1049     }
1050     // Using ADD_FAILURE + ASSERT instead of FAIL to prevent the current test from continuing with a
1051     // corrupt state.
1052     ADD_FAILURE() << "Device lost during test: " << message;
1053     ASSERT(false);
1054 }
1055 
AddBufferExpectation(const char * file,int line,const wgpu::Buffer & buffer,uint64_t offset,uint64_t size,detail::Expectation * expectation)1056 std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file,
1057                                                        int line,
1058                                                        const wgpu::Buffer& buffer,
1059                                                        uint64_t offset,
1060                                                        uint64_t size,
1061                                                        detail::Expectation* expectation) {
1062     auto readback = ReserveReadback(size);
1063 
1064     // We need to enqueue the copy immediately because by the time we resolve the expectation,
1065     // the buffer might have been modified.
1066     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1067     encoder.CopyBufferToBuffer(buffer, offset, readback.buffer, readback.offset, size);
1068 
1069     wgpu::CommandBuffer commands = encoder.Finish();
1070     queue.Submit(1, &commands);
1071 
1072     DeferredExpectation deferred;
1073     deferred.file = file;
1074     deferred.line = line;
1075     deferred.readbackSlot = readback.slot;
1076     deferred.readbackOffset = readback.offset;
1077     deferred.size = size;
1078     deferred.rowBytes = size;
1079     deferred.bytesPerRow = size;
1080     deferred.expectation.reset(expectation);
1081 
1082     mDeferredExpectations.push_back(std::move(deferred));
1083     mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
1084     return *(mDeferredExpectations.back().message.get());
1085 }
1086 
AddTextureExpectationImpl(const char * file,int line,detail::Expectation * expectation,const wgpu::Texture & texture,wgpu::Origin3D origin,wgpu::Extent3D extent,uint32_t level,wgpu::TextureAspect aspect,uint32_t dataSize,uint32_t bytesPerRow)1087 std::ostringstream& DawnTestBase::AddTextureExpectationImpl(const char* file,
1088                                                             int line,
1089                                                             detail::Expectation* expectation,
1090                                                             const wgpu::Texture& texture,
1091                                                             wgpu::Origin3D origin,
1092                                                             wgpu::Extent3D extent,
1093                                                             uint32_t level,
1094                                                             wgpu::TextureAspect aspect,
1095                                                             uint32_t dataSize,
1096                                                             uint32_t bytesPerRow) {
1097     if (bytesPerRow == 0) {
1098         bytesPerRow = Align(extent.width * dataSize, kTextureBytesPerRowAlignment);
1099     } else {
1100         ASSERT(bytesPerRow >= extent.width * dataSize);
1101         ASSERT(bytesPerRow == Align(bytesPerRow, kTextureBytesPerRowAlignment));
1102     }
1103 
1104     uint32_t rowsPerImage = extent.height;
1105     uint32_t size = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent.width,
1106                                                extent.height, extent.depthOrArrayLayers, dataSize);
1107 
1108     auto readback = ReserveReadback(Align(size, 4));
1109 
1110     // We need to enqueue the copy immediately because by the time we resolve the expectation,
1111     // the texture might have been modified.
1112     wgpu::ImageCopyTexture imageCopyTexture =
1113         utils::CreateImageCopyTexture(texture, level, origin, aspect);
1114     wgpu::ImageCopyBuffer imageCopyBuffer =
1115         utils::CreateImageCopyBuffer(readback.buffer, readback.offset, bytesPerRow, rowsPerImage);
1116 
1117     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1118     encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &extent);
1119 
1120     wgpu::CommandBuffer commands = encoder.Finish();
1121     queue.Submit(1, &commands);
1122 
1123     DeferredExpectation deferred;
1124     deferred.file = file;
1125     deferred.line = line;
1126     deferred.readbackSlot = readback.slot;
1127     deferred.readbackOffset = readback.offset;
1128     deferred.size = size;
1129     deferred.rowBytes = extent.width * dataSize;
1130     deferred.bytesPerRow = bytesPerRow;
1131     deferred.expectation.reset(expectation);
1132 
1133     mDeferredExpectations.push_back(std::move(deferred));
1134     mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
1135     return *(mDeferredExpectations.back().message.get());
1136 }
1137 
ExpectSampledFloatDataImpl(wgpu::TextureView textureView,const char * wgslTextureType,uint32_t width,uint32_t height,uint32_t componentCount,uint32_t sampleCount,detail::Expectation * expectation)1138 std::ostringstream& DawnTestBase::ExpectSampledFloatDataImpl(wgpu::TextureView textureView,
1139                                                              const char* wgslTextureType,
1140                                                              uint32_t width,
1141                                                              uint32_t height,
1142                                                              uint32_t componentCount,
1143                                                              uint32_t sampleCount,
1144                                                              detail::Expectation* expectation) {
1145     std::ostringstream shaderSource;
1146     shaderSource << "let width : u32 = " << width << "u;\n";
1147     shaderSource << "[[group(0), binding(0)]] var tex : " << wgslTextureType << ";\n";
1148     shaderSource << R"(
1149         [[block]] struct Result {
1150             values : array<f32>;
1151         };
1152         [[group(0), binding(1)]] var<storage, read_write> result : Result;
1153     )";
1154     shaderSource << "let componentCount : u32 = " << componentCount << "u;\n";
1155     shaderSource << "let sampleCount : u32 = " << sampleCount << "u;\n";
1156 
1157     shaderSource << "fn doTextureLoad(t: " << wgslTextureType
1158                  << ", coord: vec2<i32>, sample: u32, component: u32) -> f32";
1159     if (sampleCount > 1) {
1160         shaderSource << R"({
1161             return textureLoad(tex, coord, i32(sample))[component];
1162         })";
1163     } else {
1164         if (strcmp(wgslTextureType, "texture_depth_2d") == 0) {
1165             ASSERT(componentCount == 1);
1166             shaderSource << R"({
1167                 return textureLoad(tex, coord, 0);
1168             })";
1169         } else {
1170             shaderSource << R"({
1171                 return textureLoad(tex, coord, 0)[component];
1172             })";
1173         }
1174     }
1175     shaderSource << R"(
1176         [[stage(compute), workgroup_size(1)]] fn main(
1177             [[builtin(global_invocation_id)]] GlobalInvocationId : vec3<u32>
1178         ) {
1179             let baseOutIndex = GlobalInvocationId.y * width + GlobalInvocationId.x;
1180             for (var s = 0u; s < sampleCount; s = s + 1u) {
1181                 for (var c = 0u; c < componentCount; c = c + 1u) {
1182                     result.values[
1183                         baseOutIndex * sampleCount * componentCount +
1184                         s * componentCount +
1185                         c
1186                     ] = doTextureLoad(tex, vec2<i32>(GlobalInvocationId.xy), s, c);
1187                 }
1188             }
1189         }
1190     )";
1191 
1192     wgpu::ShaderModule csModule = utils::CreateShaderModule(device, shaderSource.str().c_str());
1193 
1194     wgpu::ComputePipelineDescriptor pipelineDescriptor;
1195     pipelineDescriptor.compute.module = csModule;
1196     pipelineDescriptor.compute.entryPoint = "main";
1197 
1198     wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor);
1199 
1200     // Create and initialize the slot buffer so that it won't unexpectedly affect the count of
1201     // resources lazily cleared.
1202     const std::vector<float> initialBufferData(width * height * componentCount * sampleCount, 0.f);
1203     wgpu::Buffer readbackBuffer = utils::CreateBufferFromData(
1204         device, initialBufferData.data(), sizeof(float) * initialBufferData.size(),
1205         wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage);
1206 
1207     wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
1208                                                      {{0, textureView}, {1, readbackBuffer}});
1209 
1210     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1211     wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
1212     pass.SetPipeline(pipeline);
1213     pass.SetBindGroup(0, bindGroup);
1214     pass.Dispatch(width, height);
1215     pass.EndPass();
1216     wgpu::CommandBuffer commands = commandEncoder.Finish();
1217     queue.Submit(1, &commands);
1218 
1219     return EXPECT_BUFFER(readbackBuffer, 0, initialBufferData.size() * sizeof(float), expectation);
1220 }
1221 
ExpectSampledFloatData(wgpu::Texture texture,uint32_t width,uint32_t height,uint32_t componentCount,uint32_t arrayLayer,uint32_t mipLevel,detail::Expectation * expectation)1222 std::ostringstream& DawnTestBase::ExpectSampledFloatData(wgpu::Texture texture,
1223                                                          uint32_t width,
1224                                                          uint32_t height,
1225                                                          uint32_t componentCount,
1226                                                          uint32_t arrayLayer,
1227                                                          uint32_t mipLevel,
1228                                                          detail::Expectation* expectation) {
1229     wgpu::TextureViewDescriptor viewDesc = {};
1230     viewDesc.dimension = wgpu::TextureViewDimension::e2D;
1231     viewDesc.baseMipLevel = mipLevel;
1232     viewDesc.mipLevelCount = 1;
1233     viewDesc.baseArrayLayer = arrayLayer;
1234     viewDesc.arrayLayerCount = 1;
1235 
1236     return ExpectSampledFloatDataImpl(texture.CreateView(&viewDesc), "texture_2d<f32>", width,
1237                                       height, componentCount, 1, expectation);
1238 }
1239 
ExpectMultisampledFloatData(wgpu::Texture texture,uint32_t width,uint32_t height,uint32_t componentCount,uint32_t sampleCount,uint32_t arrayLayer,uint32_t mipLevel,detail::Expectation * expectation)1240 std::ostringstream& DawnTestBase::ExpectMultisampledFloatData(wgpu::Texture texture,
1241                                                               uint32_t width,
1242                                                               uint32_t height,
1243                                                               uint32_t componentCount,
1244                                                               uint32_t sampleCount,
1245                                                               uint32_t arrayLayer,
1246                                                               uint32_t mipLevel,
1247                                                               detail::Expectation* expectation) {
1248     wgpu::TextureViewDescriptor viewDesc = {};
1249     viewDesc.dimension = wgpu::TextureViewDimension::e2D;
1250     viewDesc.baseMipLevel = mipLevel;
1251     viewDesc.mipLevelCount = 1;
1252     viewDesc.baseArrayLayer = arrayLayer;
1253     viewDesc.arrayLayerCount = 1;
1254 
1255     return ExpectSampledFloatDataImpl(texture.CreateView(&viewDesc), "texture_multisampled_2d<f32>",
1256                                       width, height, componentCount, sampleCount, expectation);
1257 }
1258 
ExpectSampledDepthData(wgpu::Texture texture,uint32_t width,uint32_t height,uint32_t arrayLayer,uint32_t mipLevel,detail::Expectation * expectation)1259 std::ostringstream& DawnTestBase::ExpectSampledDepthData(wgpu::Texture texture,
1260                                                          uint32_t width,
1261                                                          uint32_t height,
1262                                                          uint32_t arrayLayer,
1263                                                          uint32_t mipLevel,
1264                                                          detail::Expectation* expectation) {
1265     wgpu::TextureViewDescriptor viewDesc = {};
1266     viewDesc.aspect = wgpu::TextureAspect::DepthOnly;
1267     viewDesc.dimension = wgpu::TextureViewDimension::e2D;
1268     viewDesc.baseMipLevel = mipLevel;
1269     viewDesc.mipLevelCount = 1;
1270     viewDesc.baseArrayLayer = arrayLayer;
1271     viewDesc.arrayLayerCount = 1;
1272 
1273     return ExpectSampledFloatDataImpl(texture.CreateView(&viewDesc), "texture_depth_2d", width,
1274                                       height, 1, 1, expectation);
1275 }
1276 
ExpectAttachmentDepthStencilTestData(wgpu::Texture texture,wgpu::TextureFormat format,uint32_t width,uint32_t height,uint32_t arrayLayer,uint32_t mipLevel,std::vector<float> expectedDepth,uint8_t * expectedStencil)1277 std::ostringstream& DawnTestBase::ExpectAttachmentDepthStencilTestData(
1278     wgpu::Texture texture,
1279     wgpu::TextureFormat format,
1280     uint32_t width,
1281     uint32_t height,
1282     uint32_t arrayLayer,
1283     uint32_t mipLevel,
1284     std::vector<float> expectedDepth,
1285     uint8_t* expectedStencil) {
1286     wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
1287 
1288     // Make the color attachment that we'll use to read back.
1289     wgpu::TextureDescriptor colorTexDesc = {};
1290     colorTexDesc.size = {width, height, 1};
1291     colorTexDesc.format = wgpu::TextureFormat::R32Uint;
1292     colorTexDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
1293     wgpu::Texture colorTexture = device.CreateTexture(&colorTexDesc);
1294 
1295     wgpu::Texture depthDataTexture = nullptr;
1296     if (expectedDepth.size() > 0) {
1297         // Make a sampleable texture to store the depth data. We'll sample this in the
1298         // shader to output depth.
1299         wgpu::TextureDescriptor depthDataDesc = {};
1300         depthDataDesc.size = {width, height, 1};
1301         depthDataDesc.format = wgpu::TextureFormat::R32Float;
1302         depthDataDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
1303         depthDataTexture = device.CreateTexture(&depthDataDesc);
1304 
1305         // Upload the depth data.
1306         wgpu::ImageCopyTexture imageCopyTexture =
1307             utils::CreateImageCopyTexture(depthDataTexture, 0, {0, 0, 0});
1308         wgpu::TextureDataLayout textureDataLayout =
1309             utils::CreateTextureDataLayout(0, sizeof(float) * width);
1310         wgpu::Extent3D copyExtent = {width, height, 1};
1311 
1312         queue.WriteTexture(&imageCopyTexture, expectedDepth.data(),
1313                            sizeof(float) * expectedDepth.size(), &textureDataLayout, &copyExtent);
1314     }
1315 
1316     // Pipeline for a full screen quad.
1317     utils::ComboRenderPipelineDescriptor pipelineDescriptor;
1318 
1319     pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
1320         [[stage(vertex)]]
1321         fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
1322             var pos = array<vec2<f32>, 3>(
1323                 vec2<f32>(-1.0, -1.0),
1324                 vec2<f32>( 3.0, -1.0),
1325                 vec2<f32>(-1.0,  3.0));
1326             return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
1327         })");
1328 
1329     if (depthDataTexture) {
1330         // Sample the input texture and write out depth. |result| will only be set to 1 if we
1331         // pass the depth test.
1332         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
1333             [[group(0), binding(0)]] var texture0 : texture_2d<f32>;
1334 
1335             struct FragmentOut {
1336                 [[location(0)]] result : u32;
1337                 [[builtin(frag_depth)]] fragDepth : f32;
1338             };
1339 
1340             [[stage(fragment)]]
1341             fn main([[builtin(position)]] FragCoord : vec4<f32>) -> FragmentOut {
1342                 var output : FragmentOut;
1343                 output.result = 1u;
1344                 output.fragDepth = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0)[0];
1345                 return output;
1346             })");
1347     } else {
1348         pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, R"(
1349             [[stage(fragment)]]
1350             fn main() -> [[location(0)]] u32 {
1351                 return 1u;
1352             })");
1353     }
1354 
1355     wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil(format);
1356     if (depthDataTexture) {
1357         // Pass the depth test only if the depth is equal.
1358         depthStencil->depthCompare = wgpu::CompareFunction::Equal;
1359 
1360         // TODO(jiawei.shao@intel.com): The Intel Mesa Vulkan driver can't set gl_FragDepth unless
1361         // depthWriteEnabled == true. This either needs to be fixed in the driver or restricted by
1362         // the WebGPU API.
1363         depthStencil->depthWriteEnabled = true;
1364     }
1365 
1366     if (expectedStencil != nullptr) {
1367         // Pass the stencil test only if the stencil is equal.
1368         depthStencil->stencilFront.compare = wgpu::CompareFunction::Equal;
1369     }
1370 
1371     pipelineDescriptor.cTargets[0].format = colorTexDesc.format;
1372 
1373     wgpu::TextureViewDescriptor viewDesc = {};
1374     viewDesc.baseMipLevel = mipLevel;
1375     viewDesc.mipLevelCount = 1;
1376     viewDesc.baseArrayLayer = arrayLayer;
1377     viewDesc.arrayLayerCount = 1;
1378 
1379     utils::ComboRenderPassDescriptor passDescriptor({colorTexture.CreateView()},
1380                                                     texture.CreateView(&viewDesc));
1381     passDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
1382     passDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
1383 
1384     wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
1385 
1386     wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
1387     if (expectedStencil != nullptr) {
1388         pass.SetStencilReference(*expectedStencil);
1389     }
1390     pass.SetPipeline(pipeline);
1391     if (depthDataTexture) {
1392         // Bind the depth data texture.
1393         pass.SetBindGroup(0, utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
1394                                                   {{0, depthDataTexture.CreateView()}}));
1395     }
1396     pass.Draw(3);
1397     pass.EndPass();
1398 
1399     wgpu::CommandBuffer commands = commandEncoder.Finish();
1400     queue.Submit(1, &commands);
1401 
1402     std::vector<uint32_t> colorData(width * height, 1u);
1403     return EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, {0, 0}, {width, height});
1404 }
1405 
WaitABit()1406 void DawnTestBase::WaitABit() {
1407     device.Tick();
1408     FlushWire();
1409 
1410     utils::USleep(100);
1411 }
1412 
FlushWire()1413 void DawnTestBase::FlushWire() {
1414     if (gTestEnv->UsesWire()) {
1415         bool C2SFlushed = mWireHelper->FlushClient();
1416         bool S2CFlushed = mWireHelper->FlushServer();
1417         ASSERT(C2SFlushed);
1418         ASSERT(S2CFlushed);
1419     }
1420 }
1421 
WaitForAllOperations()1422 void DawnTestBase::WaitForAllOperations() {
1423     bool done = false;
1424     device.GetQueue().OnSubmittedWorkDone(
1425         0u, [](WGPUQueueWorkDoneStatus, void* userdata) { *static_cast<bool*>(userdata) = true; },
1426         &done);
1427     while (!done) {
1428         WaitABit();
1429     }
1430 }
1431 
ReserveReadback(uint64_t readbackSize)1432 DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(uint64_t readbackSize) {
1433     ReadbackSlot slot;
1434     slot.bufferSize = readbackSize;
1435 
1436     // Create and initialize the slot buffer so that it won't unexpectedly affect the count of
1437     // resource lazy clear in the tests.
1438     const std::vector<uint8_t> initialBufferData(readbackSize, 0u);
1439     slot.buffer =
1440         utils::CreateBufferFromData(device, initialBufferData.data(), readbackSize,
1441                                     wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst);
1442 
1443     ReadbackReservation reservation;
1444     reservation.buffer = slot.buffer;
1445     reservation.slot = mReadbackSlots.size();
1446     reservation.offset = 0;
1447 
1448     mReadbackSlots.push_back(std::move(slot));
1449     return reservation;
1450 }
1451 
MapSlotsSynchronously()1452 void DawnTestBase::MapSlotsSynchronously() {
1453     // Initialize numPendingMapOperations before mapping, just in case the callback is called
1454     // immediately.
1455     mNumPendingMapOperations = mReadbackSlots.size();
1456 
1457     // Map all readback slots
1458     for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
1459         MapReadUserdata* userdata = new MapReadUserdata{this, i};
1460 
1461         const ReadbackSlot& slot = mReadbackSlots[i];
1462         slot.buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize, SlotMapCallback,
1463                              userdata);
1464     }
1465 
1466     // Busy wait until all map operations are done.
1467     while (mNumPendingMapOperations != 0) {
1468         WaitABit();
1469     }
1470 }
1471 
1472 // static
SlotMapCallback(WGPUBufferMapAsyncStatus status,void * userdata_)1473 void DawnTestBase::SlotMapCallback(WGPUBufferMapAsyncStatus status, void* userdata_) {
1474     DAWN_ASSERT(status == WGPUBufferMapAsyncStatus_Success);
1475 
1476     std::unique_ptr<MapReadUserdata> userdata(static_cast<MapReadUserdata*>(userdata_));
1477     DawnTestBase* test = userdata->test;
1478     ReadbackSlot* slot = &test->mReadbackSlots[userdata->slot];
1479 
1480     slot->mappedData = slot->buffer.GetConstMappedRange();
1481     test->mNumPendingMapOperations--;
1482 }
1483 
ResolveExpectations()1484 void DawnTestBase::ResolveExpectations() {
1485     for (const auto& expectation : mDeferredExpectations) {
1486         DAWN_ASSERT(mReadbackSlots[expectation.readbackSlot].mappedData != nullptr);
1487 
1488         // Get a pointer to the mapped copy of the data for the expectation.
1489         const char* data =
1490             static_cast<const char*>(mReadbackSlots[expectation.readbackSlot].mappedData);
1491         data += expectation.readbackOffset;
1492 
1493         uint32_t size;
1494         std::vector<char> packedData;
1495         if (expectation.rowBytes != expectation.bytesPerRow) {
1496             DAWN_ASSERT(expectation.bytesPerRow > expectation.rowBytes);
1497             uint32_t rowCount =
1498                 (expectation.size + expectation.bytesPerRow - 1) / expectation.bytesPerRow;
1499             uint32_t packedSize = rowCount * expectation.rowBytes;
1500             packedData.resize(packedSize);
1501             for (uint32_t r = 0; r < rowCount; ++r) {
1502                 for (uint32_t i = 0; i < expectation.rowBytes; ++i) {
1503                     packedData[i + r * expectation.rowBytes] =
1504                         data[i + r * expectation.bytesPerRow];
1505                 }
1506             }
1507             data = packedData.data();
1508             size = packedSize;
1509         } else {
1510             size = expectation.size;
1511         }
1512 
1513         // Get the result for the expectation and add context to failures
1514         testing::AssertionResult result = expectation.expectation->Check(data, size);
1515         if (!result) {
1516             result << " Expectation created at " << expectation.file << ":" << expectation.line
1517                    << std::endl;
1518             result << expectation.message->str();
1519         }
1520 
1521         EXPECT_TRUE(result);
1522     }
1523 }
1524 
CreateTestPlatform()1525 std::unique_ptr<dawn_platform::Platform> DawnTestBase::CreateTestPlatform() {
1526     return nullptr;
1527 }
1528 
operator ==(const RGBA8 & other) const1529 bool RGBA8::operator==(const RGBA8& other) const {
1530     return r == other.r && g == other.g && b == other.b && a == other.a;
1531 }
1532 
operator !=(const RGBA8 & other) const1533 bool RGBA8::operator!=(const RGBA8& other) const {
1534     return !(*this == other);
1535 }
1536 
operator <=(const RGBA8 & other) const1537 bool RGBA8::operator<=(const RGBA8& other) const {
1538     return (r <= other.r && g <= other.g && b <= other.b && a <= other.a);
1539 }
1540 
operator >=(const RGBA8 & other) const1541 bool RGBA8::operator>=(const RGBA8& other) const {
1542     return (r >= other.r && g >= other.g && b >= other.b && a >= other.a);
1543 }
1544 
operator <<(std::ostream & stream,const RGBA8 & color)1545 std::ostream& operator<<(std::ostream& stream, const RGBA8& color) {
1546     return stream << "RGBA8(" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g)
1547                   << ", " << static_cast<int>(color.b) << ", " << static_cast<int>(color.a) << ")";
1548 }
1549 
1550 namespace detail {
GetAvailableAdapterTestParamsForBackends(const BackendTestConfig * params,size_t numParams)1551     std::vector<AdapterTestParam> GetAvailableAdapterTestParamsForBackends(
1552         const BackendTestConfig* params,
1553         size_t numParams) {
1554         ASSERT(gTestEnv != nullptr);
1555         return gTestEnv->GetAvailableAdapterTestParamsForBackends(params, numParams);
1556     }
1557 
1558     // Helper classes to set expectations
1559 
1560     template <typename T, typename U>
ExpectEq(T singleValue,T tolerance)1561     ExpectEq<T, U>::ExpectEq(T singleValue, T tolerance) : mTolerance(tolerance) {
1562         mExpected.push_back(singleValue);
1563     }
1564 
1565     template <typename T, typename U>
ExpectEq(const T * values,const unsigned int count,T tolerance)1566     ExpectEq<T, U>::ExpectEq(const T* values, const unsigned int count, T tolerance)
1567         : mTolerance(tolerance) {
1568         mExpected.assign(values, values + count);
1569     }
1570 
1571     namespace {
1572 
1573         template <typename T, typename U = T>
CheckImpl(const T & expected,const U & actual,const T & tolerance)1574         testing::AssertionResult CheckImpl(const T& expected, const U& actual, const T& tolerance) {
1575             ASSERT(tolerance == T{});
1576             if (expected != actual) {
1577                 return testing::AssertionFailure() << expected << ", actual " << actual;
1578             }
1579             return testing::AssertionSuccess();
1580         }
1581 
1582         template <>
CheckImpl(const float & expected,const float & actual,const float & tolerance)1583         testing::AssertionResult CheckImpl<float>(const float& expected,
1584                                                   const float& actual,
1585                                                   const float& tolerance) {
1586             if (abs(expected - actual) > tolerance) {
1587                 return tolerance == 0.0
1588                            ? testing::AssertionFailure() << expected << ", actual " << actual
1589                            : testing::AssertionFailure() << "within " << tolerance << " of "
1590                                                          << expected << ", actual " << actual;
1591             }
1592             return testing::AssertionSuccess();
1593         }
1594 
1595         // Interpret uint16_t as float16
1596         // This is mostly for reading float16 output from textures
1597         template <>
CheckImpl(const float & expected,const uint16_t & actual,const float & tolerance)1598         testing::AssertionResult CheckImpl<float, uint16_t>(const float& expected,
1599                                                             const uint16_t& actual,
1600                                                             const float& tolerance) {
1601             float actualF32 = Float16ToFloat32(actual);
1602             if (abs(expected - actualF32) > tolerance) {
1603                 return tolerance == 0.0
1604                            ? testing::AssertionFailure() << expected << ", actual " << actualF32
1605                            : testing::AssertionFailure() << "within " << tolerance << " of "
1606                                                          << expected << ", actual " << actualF32;
1607             }
1608             return testing::AssertionSuccess();
1609         }
1610 
1611     }  // namespace
1612 
1613     template <typename T, typename U>
Check(const void * data,size_t size)1614     testing::AssertionResult ExpectEq<T, U>::Check(const void* data, size_t size) {
1615         DAWN_ASSERT(size == sizeof(U) * mExpected.size());
1616         const U* actual = static_cast<const U*>(data);
1617 
1618         for (size_t i = 0; i < mExpected.size(); ++i) {
1619             testing::AssertionResult check = CheckImpl(mExpected[i], actual[i], mTolerance);
1620             if (!check) {
1621                 testing::AssertionResult result = testing::AssertionFailure()
1622                                                   << "Expected data[" << i << "] to be "
1623                                                   << check.message() << std::endl;
1624 
1625                 if (mExpected.size() <= 1024) {
1626                     result << "Expected:" << std::endl;
1627                     printBuffer(result, mExpected.data(), mExpected.size());
1628 
1629                     result << "Actual:" << std::endl;
1630                     printBuffer(result, actual, mExpected.size());
1631                 }
1632 
1633                 return result;
1634             }
1635         }
1636         return testing::AssertionSuccess();
1637     }
1638 
1639     template class ExpectEq<uint8_t>;
1640     template class ExpectEq<uint16_t>;
1641     template class ExpectEq<uint32_t>;
1642     template class ExpectEq<uint64_t>;
1643     template class ExpectEq<RGBA8>;
1644     template class ExpectEq<float>;
1645     template class ExpectEq<float, uint16_t>;
1646 
1647     template <typename T>
ExpectBetweenColors(T value0,T value1)1648     ExpectBetweenColors<T>::ExpectBetweenColors(T value0, T value1) {
1649         T l, h;
1650         l.r = std::min(value0.r, value1.r);
1651         l.g = std::min(value0.g, value1.g);
1652         l.b = std::min(value0.b, value1.b);
1653         l.a = std::min(value0.a, value1.a);
1654 
1655         h.r = std::max(value0.r, value1.r);
1656         h.g = std::max(value0.g, value1.g);
1657         h.b = std::max(value0.b, value1.b);
1658         h.a = std::max(value0.a, value1.a);
1659 
1660         mLowerColorChannels.push_back(l);
1661         mHigherColorChannels.push_back(h);
1662 
1663         mValues0.push_back(value0);
1664         mValues1.push_back(value1);
1665     }
1666 
1667     template <typename T>
Check(const void * data,size_t size)1668     testing::AssertionResult ExpectBetweenColors<T>::Check(const void* data, size_t size) {
1669         DAWN_ASSERT(size == sizeof(T) * mLowerColorChannels.size());
1670         DAWN_ASSERT(mHigherColorChannels.size() == mLowerColorChannels.size());
1671         DAWN_ASSERT(mValues0.size() == mValues1.size());
1672         DAWN_ASSERT(mValues0.size() == mLowerColorChannels.size());
1673 
1674         const T* actual = static_cast<const T*>(data);
1675 
1676         for (size_t i = 0; i < mLowerColorChannels.size(); ++i) {
1677             if (!(actual[i] >= mLowerColorChannels[i] && actual[i] <= mHigherColorChannels[i])) {
1678                 testing::AssertionResult result = testing::AssertionFailure()
1679                                                   << "Expected data[" << i << "] to be between "
1680                                                   << mValues0[i] << " and " << mValues1[i]
1681                                                   << ", actual " << actual[i] << std::endl;
1682 
1683                 if (mLowerColorChannels.size() <= 1024) {
1684                     result << "Expected between:" << std::endl;
1685                     printBuffer(result, mValues0.data(), mLowerColorChannels.size());
1686                     result << "and" << std::endl;
1687                     printBuffer(result, mValues1.data(), mLowerColorChannels.size());
1688 
1689                     result << "Actual:" << std::endl;
1690                     printBuffer(result, actual, mLowerColorChannels.size());
1691                 }
1692 
1693                 return result;
1694             }
1695         }
1696 
1697         return testing::AssertionSuccess();
1698     }
1699 
1700     template class ExpectBetweenColors<RGBA8>;
1701 }  // namespace detail
1702