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