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 "include/private/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)33 std::unique_ptr<GraphiteTestContext> DawnTestContext::Make(wgpu::BackendType backend) {
34 static std::unique_ptr<dawn::native::Instance> sInstance;
35 static SkOnce sOnce;
36
37 static constexpr const char* kToggles[] = {
38 "allow_unsafe_apis", // Needed for dual-source blending.
39 "use_user_defined_labels_in_backend",
40 };
41 wgpu::DawnTogglesDescriptor togglesDesc;
42 togglesDesc.enabledToggleCount = std::size(kToggles);
43 togglesDesc.enabledToggles = kToggles;
44
45 // Creation of Instance is cheap but calling EnumerateAdapters can be expensive the first time,
46 // but then the results are cached on the Instance object. So save the Instance here so we can
47 // avoid the overhead of EnumerateAdapters on every test.
48 sOnce([&]{
49 DawnProcTable backendProcs = dawn::native::GetProcs();
50 dawnProcSetProcs(&backendProcs);
51 WGPUInstanceDescriptor desc{};
52 // need for WaitAny with timeout > 0
53 desc.features.timedWaitAnyEnable = true;
54 sInstance = std::make_unique<dawn::native::Instance>(&desc);
55 });
56
57 dawn::native::Adapter matchedAdaptor;
58
59 wgpu::RequestAdapterOptions options;
60 options.nextInChain = &togglesDesc;
61 std::vector<dawn::native::Adapter> adapters = sInstance->EnumerateAdapters(&options);
62 SkASSERT(!adapters.empty());
63 // Sort adapters by adapterType(DiscreteGPU, IntegratedGPU, CPU) and
64 // backendType(WebGPU, D3D11, D3D12, Metal, Vulkan, OpenGL, OpenGLES).
65 std::sort(adapters.begin(),
66 adapters.end(),
67 [](dawn::native::Adapter a, dawn::native::Adapter b) {
68 wgpu::AdapterProperties propA;
69 wgpu::AdapterProperties propB;
70 a.GetProperties(&propA);
71 b.GetProperties(&propB);
72 return std::tuple(propA.adapterType, propA.backendType) <
73 std::tuple(propB.adapterType, propB.backendType);
74 });
75
76 for (const auto& adapter : adapters) {
77 wgpu::AdapterProperties props;
78 adapter.GetProperties(&props);
79 if (backend == props.backendType) {
80 matchedAdaptor = adapter;
81 break;
82 }
83 }
84
85 if (!matchedAdaptor) {
86 return nullptr;
87 }
88
89 #if LOG_ADAPTER
90 wgpu::AdapterProperties properties;
91 sAdapter.GetProperties(&properties);
92 SkDebugf("GPU: %s\nDriver: %s\n", properties.name, properties.driverDescription);
93 #endif
94
95 std::vector<wgpu::FeatureName> features;
96 wgpu::Adapter adapter = matchedAdaptor.Get();
97 if (adapter.HasFeature(wgpu::FeatureName::MSAARenderToSingleSampled)) {
98 features.push_back(wgpu::FeatureName::MSAARenderToSingleSampled);
99 }
100 if (adapter.HasFeature(wgpu::FeatureName::TransientAttachments)) {
101 features.push_back(wgpu::FeatureName::TransientAttachments);
102 }
103 if (adapter.HasFeature(wgpu::FeatureName::Unorm16TextureFormats)) {
104 features.push_back(wgpu::FeatureName::Unorm16TextureFormats);
105 }
106 if (adapter.HasFeature(wgpu::FeatureName::DualSourceBlending)) {
107 features.push_back(wgpu::FeatureName::DualSourceBlending);
108 }
109 if (adapter.HasFeature(wgpu::FeatureName::FramebufferFetch)) {
110 features.push_back(wgpu::FeatureName::FramebufferFetch);
111 }
112 if (adapter.HasFeature(wgpu::FeatureName::BufferMapExtendedUsages)) {
113 features.push_back(wgpu::FeatureName::BufferMapExtendedUsages);
114 }
115 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionETC2)) {
116 features.push_back(wgpu::FeatureName::TextureCompressionETC2);
117 }
118 if (adapter.HasFeature(wgpu::FeatureName::TextureCompressionBC)) {
119 features.push_back(wgpu::FeatureName::TextureCompressionBC);
120 }
121 if (adapter.HasFeature(wgpu::FeatureName::R8UnormStorage)) {
122 features.push_back(wgpu::FeatureName::R8UnormStorage);
123 }
124 if (adapter.HasFeature(wgpu::FeatureName::DawnLoadResolveTexture)) {
125 features.push_back(wgpu::FeatureName::DawnLoadResolveTexture);
126 }
127
128 wgpu::DeviceDescriptor desc;
129 desc.requiredFeatureCount = features.size();
130 desc.requiredFeatures = features.data();
131 desc.nextInChain = &togglesDesc;
132 desc.deviceLostCallbackInfo.callback =
133 [](WGPUDeviceImpl *const *, WGPUDeviceLostReason reason, const char* message, void*) {
134 if (reason != WGPUDeviceLostReason_Destroyed) {
135 SK_ABORT("Device lost: %s\n", message);
136 }
137 };
138
139 wgpu::Device device = wgpu::Device::Acquire(matchedAdaptor.CreateDevice(&desc));
140 SkASSERT(device);
141 device.SetUncapturedErrorCallback(
142 [](WGPUErrorType type, const char* message, void*) {
143 SkDebugf("Device error: %s\n", message);
144 },
145 /*userdata=*/nullptr);
146
147 skgpu::graphite::DawnBackendContext backendContext;
148 backendContext.fInstance = wgpu::Instance(sInstance->Get());
149 backendContext.fDevice = device;
150 backendContext.fQueue = device.GetQueue();
151 return std::unique_ptr<GraphiteTestContext>(new DawnTestContext(backendContext));
152 }
153
contextType()154 skgpu::ContextType DawnTestContext::contextType() {
155 wgpu::AdapterProperties props;
156 fBackendContext.fDevice.GetAdapter().GetProperties(&props);
157 switch (props.backendType) {
158 case wgpu::BackendType::D3D11:
159 return skgpu::ContextType::kDawn_D3D11;
160
161 case wgpu::BackendType::D3D12:
162 return skgpu::ContextType::kDawn_D3D12;
163
164 case wgpu::BackendType::Metal:
165 return skgpu::ContextType::kDawn_Metal;
166
167 case wgpu::BackendType::Vulkan:
168 return skgpu::ContextType::kDawn_Vulkan;
169
170 case wgpu::BackendType::OpenGL:
171 return skgpu::ContextType::kDawn_OpenGL;
172
173 case wgpu::BackendType::OpenGLES:
174 return skgpu::ContextType::kDawn_OpenGLES;
175 default:
176 SK_ABORT("unexpected Dawn backend");
177 return skgpu::ContextType::kMock;
178 }
179 }
180
makeContext(const TestOptions & options)181 std::unique_ptr<skgpu::graphite::Context> DawnTestContext::makeContext(const TestOptions& options) {
182 skgpu::graphite::ContextOptions revisedContextOptions(options.fContextOptions);
183 skgpu::graphite::ContextOptionsPriv contextOptionsPriv;
184 if (!options.fContextOptions.fOptionsPriv) {
185 revisedContextOptions.fOptionsPriv = &contextOptionsPriv;
186 }
187 // Needed to make synchronous readPixels work
188 revisedContextOptions.fOptionsPriv->fStoreContextRefInRecorder = true;
189
190 auto backendContext = fBackendContext;
191 if (options.fNeverYieldToWebGPU) {
192 backendContext.fTick = nullptr;
193 }
194
195 return skgpu::graphite::ContextFactory::MakeDawn(backendContext, revisedContextOptions);
196 }
197
tick()198 void DawnTestContext::tick() { fBackendContext.fTick(fBackendContext.fInstance); }
199
200 } // namespace skiatest::graphite
201