1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/graphite/dawn/GraphiteDawnTestContext.h"
9
10 #include "include/gpu/graphite/Context.h"
11 #include "include/gpu/graphite/ContextOptions.h"
12 #include "include/gpu/graphite/dawn/DawnTypes.h"
13 #include "include/gpu/graphite/dawn/DawnUtils.h"
14 #include "include/private/base/SkOnce.h"
15 #include "src/gpu/graphite/ContextOptionsPriv.h"
16 #include "tools/gpu/ContextType.h"
17 #include "tools/graphite/TestOptions.h"
18
19 #include "dawn/dawn_proc.h"
20
21 #define LOG_ADAPTER 0
22
23 namespace skiatest::graphite {
24
25 // TODO: http://crbug.com/dawn/2450 - Currently manually setting the device to null and calling
26 // tick/process events one last time to ensure that the device is lost accordingly at
27 // destruction. Once device lost is, by default, a spontaneous event, remove this.
~DawnTestContext()28 DawnTestContext::~DawnTestContext() {
29 fBackendContext.fDevice = nullptr;
30 tick();
31 }
32
Make(wgpu::BackendType backend,bool useTintIR)33 std::unique_ptr<GraphiteTestContext> DawnTestContext::Make(wgpu::BackendType backend,
34 bool useTintIR) {
35 static std::unique_ptr<dawn::native::Instance> sInstance;
36 static SkOnce sOnce;
37
38 static constexpr const char* kToggles[] = {
39 "allow_unsafe_apis", // Needed for dual-source blending.
40 "use_user_defined_labels_in_backend",
41 // Robustness impacts performance and is always disabled when running Graphite in Chrome,
42 // so this keeps Skia's tests operating closer to real-use behavior.
43 "disable_robustness",
44 // Must be last to correctly respond to `useTintIR` parameter.
45 "use_tint_ir",
46 };
47 wgpu::DawnTogglesDescriptor togglesDesc;
48 togglesDesc.enabledToggleCount = std::size(kToggles) - (useTintIR ? 0 : 1);
49 togglesDesc.enabledToggles = kToggles;
50
51 // Creation of Instance is cheap but calling EnumerateAdapters can be expensive the first time,
52 // but then the results are cached on the Instance object. So save the Instance here so we can
53 // avoid the overhead of EnumerateAdapters on every test.
54 sOnce([&]{
55 DawnProcTable backendProcs = dawn::native::GetProcs();
56 dawnProcSetProcs(&backendProcs);
57 WGPUInstanceDescriptor desc{};
58 // need for WaitAny with timeout > 0
59 desc.features.timedWaitAnyEnable = true;
60 sInstance = std::make_unique<dawn::native::Instance>(&desc);
61 });
62
63 dawn::native::Adapter matchedAdaptor;
64
65 wgpu::RequestAdapterOptions options;
66 options.featureLevel =
67 backend == wgpu::BackendType::OpenGL || backend == wgpu::BackendType::OpenGLES
68 ? wgpu::FeatureLevel::Compatibility
69 : wgpu::FeatureLevel::Core;
70 options.nextInChain = &togglesDesc;
71 std::vector<dawn::native::Adapter> adapters = sInstance->EnumerateAdapters(&options);
72 SkASSERT(!adapters.empty());
73 // Sort adapters by adapterType(DiscreteGPU, IntegratedGPU, CPU) and
74 // backendType(WebGPU, D3D11, D3D12, Metal, Vulkan, OpenGL, OpenGLES).
75 std::sort(
76 adapters.begin(), adapters.end(), [](dawn::native::Adapter a, dawn::native::Adapter b) {
77 wgpu::Adapter wgpuA = a.Get();
78 wgpu::Adapter wgpuB = b.Get();
79 wgpu::AdapterInfo infoA;
80 wgpu::AdapterInfo infoB;
81 wgpuA.GetInfo(&infoA);
82 wgpuB.GetInfo(&infoB);
83 return std::tuple(infoA.adapterType, infoA.backendType) <
84 std::tuple(infoB.adapterType, infoB.backendType);
85 });
86
87 for (const auto& adapter : adapters) {
88 wgpu::Adapter wgpuAdapter = adapter.Get();
89 wgpu::AdapterInfo props;
90 wgpuAdapter.GetInfo(&props);
91 if (backend == props.backendType) {
92 matchedAdaptor = adapter;
93 break;
94 }
95 }
96
97 if (!matchedAdaptor) {
98 return nullptr;
99 }
100
101 #if LOG_ADAPTER
102 wgpu::AdapterInfo info;
103 sAdapter.GetInfo(&info);
104 SkDebugf("GPU: %s\nDriver: %s\n", info.device, info.description);
105 #endif
106
107 std::vector<wgpu::FeatureName> features;
108 wgpu::Adapter adapter = matchedAdaptor.Get();
109 if (adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) {
110 features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled);
111 }
112 if (adapter.HasFeature(wgpu::FeatureName::TransientAttachments)) {
113 features.push_back(wgpu::FeatureName::TransientAttachments);
114 }
115 if (adapter.HasFeature(wgpu::FeatureName::Unorm16TextureFormats)) {
116 features.push_back(wgpu::FeatureName::Unorm16TextureFormats);
117 }
118 if (adapter.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
119 features.push_back(wgpu::FeatureName::DualSourceBlending);
120 }
121 if (adapter.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
122 features.push_back(wgpu::FeatureName::FramebufferFetch);
123 }
124 if (adapter.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages)) {
125 features.push_back(wgpu::FeatureName::BufferMapExtendedUsages);
126 }
127 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
128 features.push_back(wgpu::FeatureName::TextureCompressionETC2);
129 }
130 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
131 features.push_back(wgpu::FeatureName::TextureCompressionBC);
132 }
133 if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
134 features.push_back(wgpu::FeatureName::R8UnormStorage);
135 }
136 if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
137 features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
138 }
139 if (adapter.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture)) {
140 features.push_back(wgpu::FeatureName::DawnPartialLoadResolveTexture);
141 }
142 if (adapter.HasFeature(wgpu::FeatureName::TimestampQuery)) {
143 features.push_back(wgpu::FeatureName::TimestampQuery);
144 }
145 if (adapter.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
146 features.push_back(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment);
147 }
148
149 wgpu::DeviceDescriptor desc;
150 desc.requiredFeatureCount = features.size();
151 desc.requiredFeatures = features.data();
152 desc.nextInChain = &togglesDesc;
153 desc.SetDeviceLostCallback(
154 wgpu::CallbackMode::AllowSpontaneous,
155 [](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
156 if (reason != wgpu::DeviceLostReason::Destroyed) {
157 SK_ABORT("Device lost: %s\n", message);
158 }
159 });
160 desc.SetUncapturedErrorCallback([](const wgpu::Device&, wgpu::ErrorType, const char* message) {
161 SkDebugf("Device error: %s\n", message);
162 });
163
164 wgpu::Device device = wgpu::Device::Acquire(matchedAdaptor.CreateDevice(&desc));
165 SkASSERT(device);
166
167 skgpu::graphite::DawnBackendContext backendContext;
168 backendContext.fInstance = wgpu::Instance(sInstance->Get());
169 backendContext.fDevice = device;
170 backendContext.fQueue = device.GetQueue();
171 return std::unique_ptr<GraphiteTestContext>(new DawnTestContext(backendContext));
172 }
173
contextType()174 skgpu::ContextType DawnTestContext::contextType() {
175 wgpu::AdapterInfo info;
176 fBackendContext.fDevice.GetAdapter().GetInfo(&info);
177 switch (info.backendType) {
178 case wgpu::BackendType::D3D11:
179 return skgpu::ContextType::kDawn_D3D11;
180
181 case wgpu::BackendType::D3D12:
182 return skgpu::ContextType::kDawn_D3D12;
183
184 case wgpu::BackendType::Metal:
185 return skgpu::ContextType::kDawn_Metal;
186
187 case wgpu::BackendType::Vulkan:
188 return skgpu::ContextType::kDawn_Vulkan;
189
190 case wgpu::BackendType::OpenGL:
191 return skgpu::ContextType::kDawn_OpenGL;
192
193 case wgpu::BackendType::OpenGLES:
194 return skgpu::ContextType::kDawn_OpenGLES;
195 default:
196 SK_ABORT("unexpected Dawn backend");
197 return skgpu::ContextType::kMock;
198 }
199 }
200
makeContext(const TestOptions & options)201 std::unique_ptr<skgpu::graphite::Context> DawnTestContext::makeContext(const TestOptions& options) {
202 skgpu::graphite::ContextOptions revisedContextOptions(options.fContextOptions);
203 skgpu::graphite::ContextOptionsPriv contextOptionsPriv;
204 if (!options.fContextOptions.fOptionsPriv) {
205 revisedContextOptions.fOptionsPriv = &contextOptionsPriv;
206 }
207 // Needed to make synchronous readPixels work
208 revisedContextOptions.fOptionsPriv->fStoreContextRefInRecorder = true;
209
210 auto backendContext = fBackendContext;
211 if (options.fNeverYieldToWebGPU) {
212 backendContext.fTick = nullptr;
213 }
214
215 return skgpu::graphite::ContextFactory::MakeDawn(backendContext, revisedContextOptions);
216 }
217
tick()218 void DawnTestContext::tick() { fBackendContext.fTick(fBackendContext.fInstance); }
219
220 } // namespace skiatest::graphite
221