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.compatibilityMode =
67 backend == wgpu::BackendType::OpenGL || backend == wgpu::BackendType::OpenGLES;
68 options.nextInChain = &togglesDesc;
69 std::vector<dawn::native::Adapter> adapters = sInstance->EnumerateAdapters(&options);
70 SkASSERT(!adapters.empty());
71 // Sort adapters by adapterType(DiscreteGPU, IntegratedGPU, CPU) and
72 // backendType(WebGPU, D3D11, D3D12, Metal, Vulkan, OpenGL, OpenGLES).
73 std::sort(
74 adapters.begin(), adapters.end(), [](dawn::native::Adapter a, dawn::native::Adapter b) {
75 wgpu::Adapter wgpuA = a.Get();
76 wgpu::Adapter wgpuB = b.Get();
77 wgpu::AdapterInfo infoA;
78 wgpu::AdapterInfo infoB;
79 wgpuA.GetInfo(&infoA);
80 wgpuB.GetInfo(&infoB);
81 return std::tuple(infoA.adapterType, infoA.backendType) <
82 std::tuple(infoB.adapterType, infoB.backendType);
83 });
84
85 for (const auto& adapter : adapters) {
86 wgpu::Adapter wgpuAdapter = adapter.Get();
87 wgpu::AdapterInfo props;
88 wgpuAdapter.GetInfo(&props);
89 if (backend == props.backendType) {
90 matchedAdaptor = adapter;
91 break;
92 }
93 }
94
95 if (!matchedAdaptor) {
96 return nullptr;
97 }
98
99 #if LOG_ADAPTER
100 wgpu::AdapterInfo info;
101 sAdapter.GetInfo(&info);
102 SkDebugf("GPU: %s\nDriver: %s\n", info.device, info.description);
103 #endif
104
105 std::vector<wgpu::FeatureName> features;
106 wgpu::Adapter adapter = matchedAdaptor.Get();
107 if (adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) {
108 features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled);
109 }
110 if (adapter.HasFeature(wgpu::FeatureName::TransientAttachments)) {
111 features.push_back(wgpu::FeatureName::TransientAttachments);
112 }
113 if (adapter.HasFeature(wgpu::FeatureName::Unorm16TextureFormats)) {
114 features.push_back(wgpu::FeatureName::Unorm16TextureFormats);
115 }
116 if (adapter.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
117 features.push_back(wgpu::FeatureName::DualSourceBlending);
118 }
119 if (adapter.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
120 features.push_back(wgpu::FeatureName::FramebufferFetch);
121 }
122 if (adapter.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages)) {
123 features.push_back(wgpu::FeatureName::BufferMapExtendedUsages);
124 }
125 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
126 features.push_back(wgpu::FeatureName::TextureCompressionETC2);
127 }
128 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
129 features.push_back(wgpu::FeatureName::TextureCompressionBC);
130 }
131 if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
132 features.push_back(wgpu::FeatureName::R8UnormStorage);
133 }
134 if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
135 features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
136 }
137 if (adapter.HasFeature(wgpu::FeatureName::DawnPartialLoadResolveTexture)) {
138 features.push_back(wgpu::FeatureName::DawnPartialLoadResolveTexture);
139 }
140 if (adapter.HasFeature(wgpu::FeatureName::TimestampQuery)) {
141 features.push_back(wgpu::FeatureName::TimestampQuery);
142 }
143 if (adapter.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
144 features.push_back(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment);
145 }
146
147 wgpu::DeviceDescriptor desc;
148 desc.requiredFeatureCount = features.size();
149 desc.requiredFeatures = features.data();
150 desc.nextInChain = &togglesDesc;
151 desc.SetDeviceLostCallback(
152 wgpu::CallbackMode::AllowSpontaneous,
153 [](const wgpu::Device&, wgpu::DeviceLostReason reason, const char* message) {
154 if (reason != wgpu::DeviceLostReason::Destroyed) {
155 SK_ABORT("Device lost: %s\n", message);
156 }
157 });
158 desc.SetUncapturedErrorCallback([](const wgpu::Device&, wgpu::ErrorType, const char* message) {
159 SkDebugf("Device error: %s\n", message);
160 });
161
162 wgpu::Device device = wgpu::Device::Acquire(matchedAdaptor.CreateDevice(&desc));
163 SkASSERT(device);
164
165 skgpu::graphite::DawnBackendContext backendContext;
166 backendContext.fInstance = wgpu::Instance(sInstance->Get());
167 backendContext.fDevice = device;
168 backendContext.fQueue = device.GetQueue();
169 return std::unique_ptr<GraphiteTestContext>(new DawnTestContext(backendContext));
170 }
171
contextType()172 skgpu::ContextType DawnTestContext::contextType() {
173 wgpu::AdapterInfo info;
174 fBackendContext.fDevice.GetAdapter().GetInfo(&info);
175 switch (info.backendType) {
176 case wgpu::BackendType::D3D11:
177 return skgpu::ContextType::kDawn_D3D11;
178
179 case wgpu::BackendType::D3D12:
180 return skgpu::ContextType::kDawn_D3D12;
181
182 case wgpu::BackendType::Metal:
183 return skgpu::ContextType::kDawn_Metal;
184
185 case wgpu::BackendType::Vulkan:
186 return skgpu::ContextType::kDawn_Vulkan;
187
188 case wgpu::BackendType::OpenGL:
189 return skgpu::ContextType::kDawn_OpenGL;
190
191 case wgpu::BackendType::OpenGLES:
192 return skgpu::ContextType::kDawn_OpenGLES;
193 default:
194 SK_ABORT("unexpected Dawn backend");
195 return skgpu::ContextType::kMock;
196 }
197 }
198
makeContext(const TestOptions & options)199 std::unique_ptr<skgpu::graphite::Context> DawnTestContext::makeContext(const TestOptions& options) {
200 skgpu::graphite::ContextOptions revisedContextOptions(options.fContextOptions);
201 skgpu::graphite::ContextOptionsPriv contextOptionsPriv;
202 if (!options.fContextOptions.fOptionsPriv) {
203 revisedContextOptions.fOptionsPriv = &contextOptionsPriv;
204 }
205 // Needed to make synchronous readPixels work
206 revisedContextOptions.fOptionsPriv->fStoreContextRefInRecorder = true;
207
208 auto backendContext = fBackendContext;
209 if (options.fNeverYieldToWebGPU) {
210 backendContext.fTick = nullptr;
211 }
212
213 return skgpu::graphite::ContextFactory::MakeDawn(backendContext, revisedContextOptions);
214 }
215
tick()216 void DawnTestContext::tick() { fBackendContext.fTick(fBackendContext.fInstance); }
217
218 } // namespace skiatest::graphite
219