• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/DawnTest.h"
16 
17 #include "common/Assert.h"
18 #include "common/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, &copySize);
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