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, ©Extent);
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