xref: /aosp_15_r20/external/skia/tools/graphite/dawn/GraphiteDawnTestContext.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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