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/Constants.h"
19 #include "common/Math.h"
20 #include "common/Platform.h"
21 #include "dawn_native/DawnNative.h"
22 #include "dawn_wire/WireClient.h"
23 #include "dawn_wire/WireServer.h"
24 #include "utils/BackendBinding.h"
25 #include "utils/DawnHelpers.h"
26 #include "utils/SystemUtils.h"
27 #include "utils/TerribleCommandBuffer.h"
28
29 #include <algorithm>
30 #include <iomanip>
31 #include <iostream>
32 #include <sstream>
33 #include <unordered_map>
34 #include "GLFW/glfw3.h"
35
36 namespace {
37
ParamName(dawn_native::BackendType type)38 std::string ParamName(dawn_native::BackendType type) {
39 switch (type) {
40 case dawn_native::BackendType::D3D12:
41 return "D3D12";
42 case dawn_native::BackendType::Metal:
43 return "Metal";
44 case dawn_native::BackendType::Null:
45 return "Null";
46 case dawn_native::BackendType::OpenGL:
47 return "OpenGL";
48 case dawn_native::BackendType::Vulkan:
49 return "Vulkan";
50 default:
51 UNREACHABLE();
52 }
53 }
54
DeviceTypeName(dawn_native::DeviceType type)55 const char* DeviceTypeName(dawn_native::DeviceType type) {
56 switch (type) {
57 case dawn_native::DeviceType::DiscreteGPU:
58 return "Discrete GPU";
59 case dawn_native::DeviceType::IntegratedGPU:
60 return "Integrated GPU";
61 case dawn_native::DeviceType::CPU:
62 return "CPU";
63 case dawn_native::DeviceType::Unknown:
64 return "Unknown";
65 default:
66 UNREACHABLE();
67 }
68 }
69
70 struct MapReadUserdata {
71 DawnTest* test;
72 size_t slot;
73 };
74
75 DawnTestEnvironment* gTestEnv = nullptr;
76
77 } // namespace
78
79 const DawnTestParam D3D12Backend(dawn_native::BackendType::D3D12);
80 const DawnTestParam MetalBackend(dawn_native::BackendType::Metal);
81 const DawnTestParam OpenGLBackend(dawn_native::BackendType::OpenGL);
82 const DawnTestParam VulkanBackend(dawn_native::BackendType::Vulkan);
83
ForceWorkarounds(const DawnTestParam & originParam,std::initializer_list<const char * > forceEnabledWorkarounds,std::initializer_list<const char * > forceDisabledWorkarounds)84 DawnTestParam ForceWorkarounds(const DawnTestParam& originParam,
85 std::initializer_list<const char*> forceEnabledWorkarounds,
86 std::initializer_list<const char*> forceDisabledWorkarounds) {
87 DawnTestParam newTestParam = originParam;
88 newTestParam.forceEnabledWorkarounds = forceEnabledWorkarounds;
89 newTestParam.forceDisabledWorkarounds = forceDisabledWorkarounds;
90 return newTestParam;
91 }
92
93 // Implementation of DawnTestEnvironment
94
InitDawnEnd2EndTestEnvironment(int argc,char ** argv)95 void InitDawnEnd2EndTestEnvironment(int argc, char** argv) {
96 gTestEnv = new DawnTestEnvironment(argc, argv);
97 testing::AddGlobalTestEnvironment(gTestEnv);
98 }
99
DawnTestEnvironment(int argc,char ** argv)100 DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) {
101 for (int i = 1; i < argc; ++i) {
102 if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
103 mUseWire = true;
104 continue;
105 }
106
107 if (strcmp("-d", argv[i]) == 0 || strcmp("--enable-backend-validation", argv[i]) == 0) {
108 mEnableBackendValidation = true;
109 continue;
110 }
111
112 if (strcmp("-c", argv[i]) == 0 || strcmp("--begin-capture-on-startup", argv[i]) == 0) {
113 mBeginCaptureOnStartup = true;
114 continue;
115 }
116
117 if (strstr(argv[i], "--adapter-vendor-id") != nullptr) {
118 const char* value = strchr(argv[i], '=');
119 if (value != nullptr) {
120 mVendorIdFilter = strtoul(value + 1, nullptr, 16);
121 // Set filter flag if vendor id is non-zero.
122 mHasVendorIdFilter = mVendorIdFilter != 0;
123 }
124 continue;
125 }
126
127 if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
128 std::cout << "\n\nUsage: " << argv[0]
129 << " [GTEST_FLAGS...] [-w] [-d] [-c] [--adapter-vendor-id=x]\n"
130 " -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"
131 " -d, --enable-backend-validation: Enable backend validation (defaults"
132 " to disabled)\n"
133 " -c, --begin-capture-on-startup: Begin debug capture on startup "
134 "(defaults to no capture)\n"
135 " --adapter-vendor-id: Select adapter by vendor id to run end2end tests"
136 "on multi-GPU systems \n"
137 << std::endl;
138 continue;
139 }
140 }
141 }
142
SetUp()143 void DawnTestEnvironment::SetUp() {
144 ASSERT_TRUE(glfwInit());
145
146 mInstance = std::make_unique<dawn_native::Instance>();
147 mInstance->EnableBackendValidation(mEnableBackendValidation);
148 mInstance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup);
149
150 static constexpr dawn_native::BackendType kAllBackends[] = {
151 dawn_native::BackendType::D3D12,
152 dawn_native::BackendType::Metal,
153 dawn_native::BackendType::OpenGL,
154 dawn_native::BackendType::Vulkan,
155 };
156
157 // Create a test window for each backend and discover an adapter using it.
158 for (dawn_native::BackendType backend : kAllBackends) {
159 if (detail::IsBackendAvailable(backend)) {
160 CreateBackendWindow(backend);
161 utils::DiscoverAdapter(mInstance.get(), mWindows[backend], backend);
162 }
163 }
164
165 std::cout << "Testing configuration\n"
166 "---------------------\n"
167 "UseWire: "
168 << (mUseWire ? "true" : "false")
169 << "\n"
170 "EnableBackendValidation: "
171 << (mEnableBackendValidation ? "true" : "false")
172 << "\n"
173 "BeginCaptureOnStartup: "
174 << (mBeginCaptureOnStartup ? "true" : "false")
175 << "\n"
176 "\n";
177
178 // Preparing for outputting hex numbers
179 std::cout << std::showbase << std::hex << std::setfill('0') << std::setw(4);
180
181 std::cout << "System adapters: \n";
182 for (const dawn_native::Adapter& adapter : mInstance->GetAdapters()) {
183 const dawn_native::PCIInfo& pci = adapter.GetPCIInfo();
184
185 std::ostringstream vendorId;
186 std::ostringstream deviceId;
187 vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
188 << pci.vendorId;
189 deviceId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4)
190 << pci.deviceId;
191
192 std::cout << " - \"" << pci.name << "\"\n";
193 std::cout << " type: " << DeviceTypeName(adapter.GetDeviceType())
194 << ", backend: " << ParamName(adapter.GetBackendType()) << "\n";
195 std::cout << " vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str()
196 << (mHasVendorIdFilter && mVendorIdFilter == pci.vendorId ? " [Selected]" : "")
197 << "\n";
198 }
199 std::cout << std::endl;
200 }
201
UsesWire() const202 bool DawnTestEnvironment::UsesWire() const {
203 return mUseWire;
204 }
205
IsBackendValidationEnabled() const206 bool DawnTestEnvironment::IsBackendValidationEnabled() const {
207 return mEnableBackendValidation;
208 }
209
GetInstance() const210 dawn_native::Instance* DawnTestEnvironment::GetInstance() const {
211 return mInstance.get();
212 }
213
GetWindowForBackend(dawn_native::BackendType type) const214 GLFWwindow* DawnTestEnvironment::GetWindowForBackend(dawn_native::BackendType type) const {
215 return mWindows.at(type);
216 }
217
HasVendorIdFilter() const218 bool DawnTestEnvironment::HasVendorIdFilter() const {
219 return mHasVendorIdFilter;
220 }
221
GetVendorIdFilter() const222 uint32_t DawnTestEnvironment::GetVendorIdFilter() const {
223 return mVendorIdFilter;
224 }
225
CreateBackendWindow(dawn_native::BackendType type)226 void DawnTestEnvironment::CreateBackendWindow(dawn_native::BackendType type) {
227 glfwDefaultWindowHints();
228 utils::SetupGLFWWindowHintsForBackend(type);
229
230 std::string windowName = "Dawn " + ParamName(type) + " test window";
231 GLFWwindow* window = glfwCreateWindow(400, 400, windowName.c_str(), nullptr, nullptr);
232
233 mWindows[type] = window;
234 }
235
236 // Implementation of DawnTest
237
238 DawnTest::DawnTest() = default;
239
~DawnTest()240 DawnTest::~DawnTest() {
241 // We need to destroy child objects before the Device
242 mReadbackSlots.clear();
243 queue = dawn::Queue();
244 swapchain = dawn::SwapChain();
245 device = dawn::Device();
246
247 mWireClient = nullptr;
248 mWireServer = nullptr;
249 if (gTestEnv->UsesWire()) {
250 backendProcs.deviceRelease(backendDevice);
251 }
252
253 dawnSetProcs(nullptr);
254 }
255
IsD3D12() const256 bool DawnTest::IsD3D12() const {
257 return GetParam().backendType == dawn_native::BackendType::D3D12;
258 }
259
IsMetal() const260 bool DawnTest::IsMetal() const {
261 return GetParam().backendType == dawn_native::BackendType::Metal;
262 }
263
IsOpenGL() const264 bool DawnTest::IsOpenGL() const {
265 return GetParam().backendType == dawn_native::BackendType::OpenGL;
266 }
267
IsVulkan() const268 bool DawnTest::IsVulkan() const {
269 return GetParam().backendType == dawn_native::BackendType::Vulkan;
270 }
271
IsAMD() const272 bool DawnTest::IsAMD() const {
273 return mPCIInfo.vendorId == kVendorID_AMD;
274 }
275
IsARM() const276 bool DawnTest::IsARM() const {
277 return mPCIInfo.vendorId == kVendorID_ARM;
278 }
279
IsImgTec() const280 bool DawnTest::IsImgTec() const {
281 return mPCIInfo.vendorId == kVendorID_ImgTec;
282 }
283
IsIntel() const284 bool DawnTest::IsIntel() const {
285 return mPCIInfo.vendorId == kVendorID_Intel;
286 }
287
IsNvidia() const288 bool DawnTest::IsNvidia() const {
289 return mPCIInfo.vendorId == kVendorID_Nvidia;
290 }
291
IsQualcomm() const292 bool DawnTest::IsQualcomm() const {
293 return mPCIInfo.vendorId == kVendorID_Qualcomm;
294 }
295
IsWindows() const296 bool DawnTest::IsWindows() const {
297 #ifdef DAWN_PLATFORM_WINDOWS
298 return true;
299 #else
300 return false;
301 #endif
302 }
303
IsLinux() const304 bool DawnTest::IsLinux() const {
305 #ifdef DAWN_PLATFORM_LINUX
306 return true;
307 #else
308 return false;
309 #endif
310 }
311
IsMacOS() const312 bool DawnTest::IsMacOS() const {
313 #ifdef DAWN_PLATFORM_APPLE
314 return true;
315 #else
316 return false;
317 #endif
318 }
319
UsesWire() const320 bool DawnTest::UsesWire() const {
321 return gTestEnv->UsesWire();
322 }
323
IsBackendValidationEnabled() const324 bool DawnTest::IsBackendValidationEnabled() const {
325 return gTestEnv->IsBackendValidationEnabled();
326 }
327
HasVendorIdFilter() const328 bool DawnTest::HasVendorIdFilter() const {
329 return gTestEnv->HasVendorIdFilter();
330 }
331
GetVendorIdFilter() const332 uint32_t DawnTest::GetVendorIdFilter() const {
333 return gTestEnv->GetVendorIdFilter();
334 }
335
SetUp()336 void DawnTest::SetUp() {
337 // Get an adapter for the backend to use, and create the device.
338 dawn_native::Adapter backendAdapter;
339 const dawn_native::BackendType backendType = GetParam().backendType;
340 {
341 dawn_native::Instance* instance = gTestEnv->GetInstance();
342 std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
343
344 for (const dawn_native::Adapter& adapter : adapters) {
345 if (adapter.GetBackendType() == backendType) {
346 if (HasVendorIdFilter()) {
347 if (adapter.GetPCIInfo().vendorId == GetVendorIdFilter()) {
348 backendAdapter = adapter;
349 break;
350 }
351 } else {
352 backendAdapter = adapter;
353
354 // On Metal, select the last adapter so that the discrete GPU is tested on
355 // multi-GPU systems.
356 // TODO(cwallez@chromium.org): Replace this with command line arguments
357 // requesting a specific device / vendor ID once the macOS 10.13 SDK is rolled
358 // and correct PCI info collection is implemented on Metal.
359 if (backendType != dawn_native::BackendType::Metal) {
360 break;
361 }
362 }
363 }
364 }
365
366 ASSERT(backendAdapter);
367 }
368
369 mPCIInfo = backendAdapter.GetPCIInfo();
370
371 for (const char* forceEnabledWorkaround : GetParam().forceEnabledWorkarounds) {
372 ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceEnabledWorkaround) != nullptr);
373 }
374 for (const char* forceDisabledWorkaround : GetParam().forceDisabledWorkarounds) {
375 ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceDisabledWorkaround) != nullptr);
376 }
377 dawn_native::DeviceDescriptor deviceDescriptor;
378 deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds;
379 deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds;
380 backendDevice = backendAdapter.CreateDevice(&deviceDescriptor);
381
382 backendProcs = dawn_native::GetProcs();
383
384 // Get the test window and create the device using it (esp. for OpenGL)
385 GLFWwindow* testWindow = gTestEnv->GetWindowForBackend(backendType);
386 DAWN_ASSERT(testWindow != nullptr);
387 mBinding.reset(utils::CreateBinding(backendType, testWindow, backendDevice));
388 DAWN_ASSERT(mBinding != nullptr);
389
390 // Choose whether to use the backend procs and devices directly, or set up the wire.
391 DawnDevice cDevice = nullptr;
392 DawnProcTable procs;
393
394 if (gTestEnv->UsesWire()) {
395 mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>();
396 mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
397
398 dawn_wire::WireServerDescriptor serverDesc = {};
399 serverDesc.device = backendDevice;
400 serverDesc.procs = &backendProcs;
401 serverDesc.serializer = mS2cBuf.get();
402
403 mWireServer.reset(new dawn_wire::WireServer(serverDesc));
404 mC2sBuf->SetHandler(mWireServer.get());
405
406 dawn_wire::WireClientDescriptor clientDesc = {};
407 clientDesc.serializer = mC2sBuf.get();
408
409 mWireClient.reset(new dawn_wire::WireClient(clientDesc));
410 DawnDevice clientDevice = mWireClient->GetDevice();
411 DawnProcTable clientProcs = mWireClient->GetProcs();
412 mS2cBuf->SetHandler(mWireClient.get());
413
414 procs = clientProcs;
415 cDevice = clientDevice;
416 } else {
417 procs = backendProcs;
418 cDevice = backendDevice;
419 }
420
421 // Set up the device and queue because all tests need them, and DawnTest needs them too for the
422 // deferred expectations.
423 dawnSetProcs(&procs);
424 device = dawn::Device::Acquire(cDevice);
425 queue = device.CreateQueue();
426
427 // The swapchain isn't used by tests but is useful when debugging with graphics debuggers that
428 // capture at frame boundaries.
429 dawn::SwapChainDescriptor swapChainDesc;
430 swapChainDesc.implementation = mBinding->GetSwapChainImplementation();
431 swapchain = device.CreateSwapChain(&swapChainDesc);
432 FlushWire();
433 swapchain.Configure(
434 static_cast<dawn::TextureFormat>(mBinding->GetPreferredSwapChainTextureFormat()),
435 dawn::TextureUsageBit::OutputAttachment, 400, 400);
436
437 device.SetErrorCallback(OnDeviceError, this);
438 }
439
TearDown()440 void DawnTest::TearDown() {
441 swapchain = dawn::SwapChain();
442 FlushWire();
443
444 MapSlotsSynchronously();
445 ResolveExpectations();
446
447 for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
448 mReadbackSlots[i].buffer.Unmap();
449 }
450 }
451
StartExpectDeviceError()452 void DawnTest::StartExpectDeviceError() {
453 mExpectError = true;
454 mError = false;
455 }
EndExpectDeviceError()456 bool DawnTest::EndExpectDeviceError() {
457 mExpectError = false;
458 return mError;
459 }
460
461 // static
OnDeviceError(const char * message,void * userdata)462 void DawnTest::OnDeviceError(const char* message, void* userdata) {
463 DawnTest* self = static_cast<DawnTest*>(userdata);
464
465 ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
466 ASSERT_FALSE(self->mError) << "Got two errors in expect block";
467 self->mError = true;
468 }
469
AddBufferExpectation(const char * file,int line,const dawn::Buffer & buffer,uint64_t offset,uint64_t size,detail::Expectation * expectation)470 std::ostringstream& DawnTest::AddBufferExpectation(const char* file,
471 int line,
472 const dawn::Buffer& buffer,
473 uint64_t offset,
474 uint64_t size,
475 detail::Expectation* expectation) {
476 auto readback = ReserveReadback(size);
477
478 // We need to enqueue the copy immediately because by the time we resolve the expectation,
479 // the buffer might have been modified.
480 dawn::CommandEncoder encoder = device.CreateCommandEncoder();
481 encoder.CopyBufferToBuffer(buffer, offset, readback.buffer, readback.offset, size);
482
483 dawn::CommandBuffer commands = encoder.Finish();
484 queue.Submit(1, &commands);
485
486 DeferredExpectation deferred;
487 deferred.file = file;
488 deferred.line = line;
489 deferred.readbackSlot = readback.slot;
490 deferred.readbackOffset = readback.offset;
491 deferred.size = size;
492 deferred.rowBytes = size;
493 deferred.rowPitch = size;
494 deferred.expectation.reset(expectation);
495
496 mDeferredExpectations.push_back(std::move(deferred));
497 mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
498 return *(mDeferredExpectations.back().message.get());
499 }
500
AddTextureExpectation(const char * file,int line,const dawn::Texture & texture,uint32_t x,uint32_t y,uint32_t width,uint32_t height,uint32_t level,uint32_t slice,uint32_t pixelSize,detail::Expectation * expectation)501 std::ostringstream& DawnTest::AddTextureExpectation(const char* file,
502 int line,
503 const dawn::Texture& texture,
504 uint32_t x,
505 uint32_t y,
506 uint32_t width,
507 uint32_t height,
508 uint32_t level,
509 uint32_t slice,
510 uint32_t pixelSize,
511 detail::Expectation* expectation) {
512 uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment);
513 uint32_t size = rowPitch * (height - 1) + width * pixelSize;
514
515 auto readback = ReserveReadback(size);
516
517 // We need to enqueue the copy immediately because by the time we resolve the expectation,
518 // the texture might have been modified.
519 dawn::TextureCopyView textureCopyView =
520 utils::CreateTextureCopyView(texture, level, slice, {x, y, 0});
521 dawn::BufferCopyView bufferCopyView =
522 utils::CreateBufferCopyView(readback.buffer, readback.offset, rowPitch, 0);
523 dawn::Extent3D copySize = {width, height, 1};
524
525 dawn::CommandEncoder encoder = device.CreateCommandEncoder();
526 encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size);
527
528 dawn::CommandBuffer commands = encoder.Finish();
529 queue.Submit(1, &commands);
530
531 DeferredExpectation deferred;
532 deferred.file = file;
533 deferred.line = line;
534 deferred.readbackSlot = readback.slot;
535 deferred.readbackOffset = readback.offset;
536 deferred.size = size;
537 deferred.rowBytes = width * pixelSize;
538 deferred.rowPitch = rowPitch;
539 deferred.expectation.reset(expectation);
540
541 mDeferredExpectations.push_back(std::move(deferred));
542 mDeferredExpectations.back().message = std::make_unique<std::ostringstream>();
543 return *(mDeferredExpectations.back().message.get());
544 }
545
WaitABit()546 void DawnTest::WaitABit() {
547 device.Tick();
548 FlushWire();
549
550 utils::USleep(100);
551 }
552
SwapBuffersForCapture()553 void DawnTest::SwapBuffersForCapture() {
554 // Insert a frame boundary for API capture tools.
555 dawn::Texture backBuffer = swapchain.GetNextTexture();
556 swapchain.Present(backBuffer);
557 }
558
FlushWire()559 void DawnTest::FlushWire() {
560 if (gTestEnv->UsesWire()) {
561 bool C2SFlushed = mC2sBuf->Flush();
562 bool S2CFlushed = mS2cBuf->Flush();
563 ASSERT(C2SFlushed);
564 ASSERT(S2CFlushed);
565 }
566 }
567
ReserveReadback(uint64_t readbackSize)568 DawnTest::ReadbackReservation DawnTest::ReserveReadback(uint64_t readbackSize) {
569 // For now create a new MapRead buffer for each readback
570 // TODO(cwallez@chromium.org): eventually make bigger buffers and allocate linearly?
571 dawn::BufferDescriptor descriptor;
572 descriptor.size = readbackSize;
573 descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::CopyDst;
574
575 ReadbackSlot slot;
576 slot.bufferSize = readbackSize;
577 slot.buffer = device.CreateBuffer(&descriptor);
578
579 ReadbackReservation reservation;
580 reservation.buffer = slot.buffer;
581 reservation.slot = mReadbackSlots.size();
582 reservation.offset = 0;
583
584 mReadbackSlots.push_back(std::move(slot));
585 return reservation;
586 }
587
MapSlotsSynchronously()588 void DawnTest::MapSlotsSynchronously() {
589 // Initialize numPendingMapOperations before mapping, just in case the callback is called
590 // immediately.
591 mNumPendingMapOperations = mReadbackSlots.size();
592
593 // Map all readback slots
594 for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
595 MapReadUserdata* userdata = new MapReadUserdata{this, i};
596
597 auto& slot = mReadbackSlots[i];
598 slot.buffer.MapReadAsync(SlotMapReadCallback, userdata);
599 }
600
601 // Busy wait until all map operations are done.
602 while (mNumPendingMapOperations != 0) {
603 WaitABit();
604 }
605 }
606
607 // static
SlotMapReadCallback(DawnBufferMapAsyncStatus status,const void * data,uint64_t,void * userdata_)608 void DawnTest::SlotMapReadCallback(DawnBufferMapAsyncStatus status,
609 const void* data,
610 uint64_t,
611 void* userdata_) {
612 DAWN_ASSERT(status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS);
613
614 auto userdata = static_cast<MapReadUserdata*>(userdata_);
615 userdata->test->mReadbackSlots[userdata->slot].mappedData = data;
616 userdata->test->mNumPendingMapOperations--;
617
618 delete userdata;
619 }
620
ResolveExpectations()621 void DawnTest::ResolveExpectations() {
622 for (const auto& expectation : mDeferredExpectations) {
623 DAWN_ASSERT(mReadbackSlots[expectation.readbackSlot].mappedData != nullptr);
624
625 // Get a pointer to the mapped copy of the data for the expectation.
626 const char* data =
627 static_cast<const char*>(mReadbackSlots[expectation.readbackSlot].mappedData);
628 data += expectation.readbackOffset;
629
630 uint32_t size;
631 std::vector<char> packedData;
632 if (expectation.rowBytes != expectation.rowPitch) {
633 DAWN_ASSERT(expectation.rowPitch > expectation.rowBytes);
634 uint32_t rowCount =
635 (expectation.size + expectation.rowPitch - 1) / expectation.rowPitch;
636 uint32_t packedSize = rowCount * expectation.rowBytes;
637 packedData.resize(packedSize);
638 for (uint32_t r = 0; r < rowCount; ++r) {
639 for (uint32_t i = 0; i < expectation.rowBytes; ++i) {
640 packedData[i + r * expectation.rowBytes] = data[i + r * expectation.rowPitch];
641 }
642 }
643 data = packedData.data();
644 size = packedSize;
645 } else {
646 size = expectation.size;
647 }
648
649 // Get the result for the expectation and add context to failures
650 testing::AssertionResult result = expectation.expectation->Check(data, size);
651 if (!result) {
652 result << " Expectation created at " << expectation.file << ":" << expectation.line
653 << std::endl;
654 result << expectation.message->str();
655 }
656
657 EXPECT_TRUE(result);
658 }
659 }
660
operator ==(const RGBA8 & other) const661 bool RGBA8::operator==(const RGBA8& other) const {
662 return r == other.r && g == other.g && b == other.b && a == other.a;
663 }
664
operator !=(const RGBA8 & other) const665 bool RGBA8::operator!=(const RGBA8& other) const {
666 return !(*this == other);
667 }
668
operator <<(std::ostream & stream,const RGBA8 & color)669 std::ostream& operator<<(std::ostream& stream, const RGBA8& color) {
670 return stream << "RGBA8(" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g)
671 << ", " << static_cast<int>(color.b) << ", " << static_cast<int>(color.a) << ")";
672 }
673
674 namespace detail {
IsBackendAvailable(dawn_native::BackendType type)675 bool IsBackendAvailable(dawn_native::BackendType type) {
676 switch (type) {
677 #if defined(DAWN_ENABLE_BACKEND_D3D12)
678 case dawn_native::BackendType::D3D12:
679 #endif
680 #if defined(DAWN_ENABLE_BACKEND_METAL)
681 case dawn_native::BackendType::Metal:
682 #endif
683 #if defined(DAWN_ENABLE_BACKEND_OPENGL)
684 case dawn_native::BackendType::OpenGL:
685 #endif
686 #if defined(DAWN_ENABLE_BACKEND_VULKAN)
687 case dawn_native::BackendType::Vulkan:
688 #endif
689 return true;
690
691 default:
692 return false;
693 }
694 }
695
FilterBackends(const DawnTestParam * params,size_t numParams)696 std::vector<DawnTestParam> FilterBackends(const DawnTestParam* params, size_t numParams) {
697 std::vector<DawnTestParam> backends;
698
699 for (size_t i = 0; i < numParams; ++i) {
700 if (IsBackendAvailable(params[i].backendType)) {
701 backends.push_back(params[i]);
702 }
703 }
704 return backends;
705 }
706
GetParamName(const testing::TestParamInfo<DawnTestParam> & info)707 std::string GetParamName(const testing::TestParamInfo<DawnTestParam>& info) {
708 std::ostringstream ostream;
709 ostream << ParamName(info.param.backendType);
710
711 for (const char* forceEnabledWorkaround : info.param.forceEnabledWorkarounds) {
712 ostream << "_" << forceEnabledWorkaround;
713 }
714
715 return ostream.str();
716 }
717
718 // Helper classes to set expectations
719
720 template <typename T>
ExpectEq(T singleValue)721 ExpectEq<T>::ExpectEq(T singleValue) {
722 mExpected.push_back(singleValue);
723 }
724
725 template <typename T>
ExpectEq(const T * values,const unsigned int count)726 ExpectEq<T>::ExpectEq(const T* values, const unsigned int count) {
727 mExpected.assign(values, values + count);
728 }
729
730 template <typename T>
Check(const void * data,size_t size)731 testing::AssertionResult ExpectEq<T>::Check(const void* data, size_t size) {
732 DAWN_ASSERT(size == sizeof(T) * mExpected.size());
733
734 const T* actual = static_cast<const T*>(data);
735
736 for (size_t i = 0; i < mExpected.size(); ++i) {
737 if (actual[i] != mExpected[i]) {
738 testing::AssertionResult result = testing::AssertionFailure()
739 << "Expected data[" << i << "] to be "
740 << mExpected[i] << ", actual " << actual[i]
741 << std::endl;
742
743 auto printBuffer = [&](const T* buffer) {
744 static constexpr unsigned int kBytes = sizeof(T);
745
746 for (size_t index = 0; index < mExpected.size(); ++index) {
747 auto byteView = reinterpret_cast<const uint8_t*>(buffer + index);
748 for (unsigned int b = 0; b < kBytes; ++b) {
749 char buf[4];
750 sprintf(buf, "%02X ", byteView[b]);
751 result << buf;
752 }
753 }
754 result << std::endl;
755 };
756
757 if (mExpected.size() <= 1024) {
758 result << "Expected:" << std::endl;
759 printBuffer(mExpected.data());
760
761 result << "Actual:" << std::endl;
762 printBuffer(actual);
763 }
764
765 return result;
766 }
767 }
768
769 return testing::AssertionSuccess();
770 }
771
772 template class ExpectEq<uint8_t>;
773 template class ExpectEq<uint32_t>;
774 template class ExpectEq<RGBA8>;
775 } // namespace detail
776