• 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/unittests/validation/ValidationTest.h"
16 
17 #include "common/Assert.h"
18 #include "common/SystemUtils.h"
19 #include "dawn/dawn_proc.h"
20 #include "dawn/webgpu.h"
21 #include "dawn_native/NullBackend.h"
22 #include "tests/ToggleParser.h"
23 #include "utils/WireHelper.h"
24 
25 #include <algorithm>
26 
27 namespace {
28 
29     bool gUseWire = false;
30     std::string gWireTraceDir = "";
31     std::unique_ptr<ToggleParser> gToggleParser = nullptr;
32 
33 }  // namespace
34 
InitDawnValidationTestEnvironment(int argc,char ** argv)35 void InitDawnValidationTestEnvironment(int argc, char** argv) {
36     gToggleParser = std::make_unique<ToggleParser>();
37 
38     for (int i = 1; i < argc; ++i) {
39         if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
40             gUseWire = true;
41             continue;
42         }
43 
44         constexpr const char kWireTraceDirArg[] = "--wire-trace-dir=";
45         size_t argLen = sizeof(kWireTraceDirArg) - 1;
46         if (strncmp(argv[i], kWireTraceDirArg, argLen) == 0) {
47             gWireTraceDir = argv[i] + argLen;
48             continue;
49         }
50 
51         if (gToggleParser->ParseEnabledToggles(argv[i])) {
52             continue;
53         }
54 
55         if (gToggleParser->ParseDisabledToggles(argv[i])) {
56             continue;
57         }
58 
59         if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
60             dawn::InfoLog()
61                 << "\n\nUsage: " << argv[0]
62                 << " [GTEST_FLAGS...] [-w]\n"
63                    "    [--enable-toggles=toggles] [--disable-toggles=toggles]\n"
64                    "  -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"
65                    "  --enable-toggles: Comma-delimited list of Dawn toggles to enable.\n"
66                    "    ex.) skip_validation,disable_robustness,turn_off_vsync\n"
67                    "  --disable-toggles: Comma-delimited list of Dawn toggles to disable\n";
68             continue;
69         }
70 
71         // Skip over args that look like they're for Googletest.
72         constexpr const char kGtestArgPrefix[] = "--gtest_";
73         if (strncmp(kGtestArgPrefix, argv[i], sizeof(kGtestArgPrefix) - 1) == 0) {
74             continue;
75         }
76 
77         dawn::WarningLog() << " Unused argument: " << argv[i];
78     }
79 }
80 
ValidationTest()81 ValidationTest::ValidationTest()
82     : mWireHelper(utils::CreateWireHelper(gUseWire, gWireTraceDir.c_str())) {
83 }
84 
SetUp()85 void ValidationTest::SetUp() {
86     instance = std::make_unique<dawn_native::Instance>();
87     instance->DiscoverDefaultAdapters();
88 
89     std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
90 
91     // Validation tests run against the null backend, find the corresponding adapter
92     bool foundNullAdapter = false;
93     for (auto& currentAdapter : adapters) {
94         wgpu::AdapterProperties adapterProperties;
95         currentAdapter.GetProperties(&adapterProperties);
96 
97         if (adapterProperties.backendType == wgpu::BackendType::Null) {
98             adapter = currentAdapter;
99             foundNullAdapter = true;
100             break;
101         }
102     }
103 
104     ASSERT(foundNullAdapter);
105 
106     std::tie(device, backendDevice) = mWireHelper->RegisterDevice(CreateTestDevice());
107     device.SetUncapturedErrorCallback(ValidationTest::OnDeviceError, this);
108 
109     std::string traceName =
110         std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
111         "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
112     mWireHelper->BeginWireTrace(traceName.c_str());
113 }
114 
~ValidationTest()115 ValidationTest::~ValidationTest() {
116     // We need to destroy Dawn objects before setting the procs to null otherwise the dawn*Release
117     // will call a nullptr
118     device = wgpu::Device();
119     mWireHelper.reset();
120 }
121 
TearDown()122 void ValidationTest::TearDown() {
123     FlushWire();
124     ASSERT_FALSE(mExpectError);
125 
126     if (device) {
127         EXPECT_EQ(mLastWarningCount,
128                   dawn_native::GetDeprecationWarningCountForTesting(backendDevice));
129     }
130 }
131 
StartExpectDeviceError(testing::Matcher<std::string> errorMatcher)132 void ValidationTest::StartExpectDeviceError(testing::Matcher<std::string> errorMatcher) {
133     mExpectError = true;
134     mError = false;
135     mErrorMatcher = errorMatcher;
136 }
137 
StartExpectDeviceError()138 void ValidationTest::StartExpectDeviceError() {
139     StartExpectDeviceError(testing::_);
140 }
141 
EndExpectDeviceError()142 bool ValidationTest::EndExpectDeviceError() {
143     mExpectError = false;
144     mErrorMatcher = testing::_;
145     return mError;
146 }
GetLastDeviceErrorMessage() const147 std::string ValidationTest::GetLastDeviceErrorMessage() const {
148     return mDeviceErrorMessage;
149 }
150 
RegisterDevice(WGPUDevice backendDevice)151 wgpu::Device ValidationTest::RegisterDevice(WGPUDevice backendDevice) {
152     return mWireHelper->RegisterDevice(backendDevice).first;
153 }
154 
UsesWire() const155 bool ValidationTest::UsesWire() const {
156     return gUseWire;
157 }
158 
FlushWire()159 void ValidationTest::FlushWire() {
160     EXPECT_TRUE(mWireHelper->FlushClient());
161     EXPECT_TRUE(mWireHelper->FlushServer());
162 }
163 
WaitForAllOperations(const wgpu::Device & device)164 void ValidationTest::WaitForAllOperations(const wgpu::Device& device) {
165     bool done = false;
166     device.GetQueue().OnSubmittedWorkDone(
167         0u, [](WGPUQueueWorkDoneStatus, void* userdata) { *static_cast<bool*>(userdata) = true; },
168         &done);
169 
170     // Force the currently submitted operations to completed.
171     while (!done) {
172         device.Tick();
173         FlushWire();
174     }
175 
176     // TODO(cwallez@chromium.org): It's not clear why we need this additional tick. Investigate it
177     // once WebGPU has defined the ordering of callbacks firing.
178     device.Tick();
179     FlushWire();
180 }
181 
HasToggleEnabled(const char * toggle) const182 bool ValidationTest::HasToggleEnabled(const char* toggle) const {
183     auto toggles = dawn_native::GetTogglesUsed(backendDevice);
184     return std::find_if(toggles.begin(), toggles.end(), [toggle](const char* name) {
185                return strcmp(toggle, name) == 0;
186            }) != toggles.end();
187 }
188 
GetSupportedLimits()189 wgpu::SupportedLimits ValidationTest::GetSupportedLimits() {
190     WGPUSupportedLimits supportedLimits;
191     supportedLimits.nextInChain = nullptr;
192     dawn_native::GetProcs().deviceGetLimits(backendDevice, &supportedLimits);
193     return *reinterpret_cast<wgpu::SupportedLimits*>(&supportedLimits);
194 }
195 
CreateTestDevice()196 WGPUDevice ValidationTest::CreateTestDevice() {
197     // Disabled disallowing unsafe APIs so we can test them.
198     dawn_native::DawnDeviceDescriptor deviceDescriptor;
199     deviceDescriptor.forceDisabledToggles.push_back("disallow_unsafe_apis");
200 
201     for (const std::string& toggle : gToggleParser->GetEnabledToggles()) {
202         deviceDescriptor.forceEnabledToggles.push_back(toggle.c_str());
203     }
204 
205     for (const std::string& toggle : gToggleParser->GetDisabledToggles()) {
206         deviceDescriptor.forceDisabledToggles.push_back(toggle.c_str());
207     }
208 
209     return adapter.CreateDevice(&deviceDescriptor);
210 }
211 
212 // static
OnDeviceError(WGPUErrorType type,const char * message,void * userdata)213 void ValidationTest::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) {
214     ASSERT(type != WGPUErrorType_NoError);
215     auto self = static_cast<ValidationTest*>(userdata);
216     self->mDeviceErrorMessage = message;
217 
218     ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message;
219     ASSERT_FALSE(self->mError) << "Got two errors in expect block";
220     if (self->mExpectError) {
221         ASSERT_THAT(message, self->mErrorMatcher);
222     }
223     self->mError = true;
224 }
225 
DummyRenderPass(const wgpu::Device & device)226 ValidationTest::DummyRenderPass::DummyRenderPass(const wgpu::Device& device)
227     : attachmentFormat(wgpu::TextureFormat::RGBA8Unorm), width(400), height(400) {
228     wgpu::TextureDescriptor descriptor;
229     descriptor.dimension = wgpu::TextureDimension::e2D;
230     descriptor.size.width = width;
231     descriptor.size.height = height;
232     descriptor.size.depthOrArrayLayers = 1;
233     descriptor.sampleCount = 1;
234     descriptor.format = attachmentFormat;
235     descriptor.mipLevelCount = 1;
236     descriptor.usage = wgpu::TextureUsage::RenderAttachment;
237     attachment = device.CreateTexture(&descriptor);
238 
239     wgpu::TextureView view = attachment.CreateView();
240     mColorAttachment.view = view;
241     mColorAttachment.resolveTarget = nullptr;
242     mColorAttachment.clearColor = {0.0f, 0.0f, 0.0f, 0.0f};
243     mColorAttachment.loadOp = wgpu::LoadOp::Clear;
244     mColorAttachment.storeOp = wgpu::StoreOp::Store;
245 
246     colorAttachmentCount = 1;
247     colorAttachments = &mColorAttachment;
248     depthStencilAttachment = nullptr;
249 }
250