1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "SampleUtils.h"
16
17 #include "common/Assert.h"
18 #include "common/Log.h"
19 #include "common/Platform.h"
20 #include "common/SystemUtils.h"
21 #include "utils/BackendBinding.h"
22 #include "utils/GLFWUtils.h"
23 #include "utils/TerribleCommandBuffer.h"
24
25 #include <dawn/dawn_proc.h>
26 #include <dawn/dawn_wsi.h>
27 #include <dawn_native/DawnNative.h>
28 #include <dawn_wire/WireClient.h>
29 #include <dawn_wire/WireServer.h>
30 #include "GLFW/glfw3.h"
31
32 #include <algorithm>
33 #include <cstring>
34
PrintDeviceError(WGPUErrorType errorType,const char * message,void *)35 void PrintDeviceError(WGPUErrorType errorType, const char* message, void*) {
36 const char* errorTypeName = "";
37 switch (errorType) {
38 case WGPUErrorType_Validation:
39 errorTypeName = "Validation";
40 break;
41 case WGPUErrorType_OutOfMemory:
42 errorTypeName = "Out of memory";
43 break;
44 case WGPUErrorType_Unknown:
45 errorTypeName = "Unknown";
46 break;
47 case WGPUErrorType_DeviceLost:
48 errorTypeName = "Device lost";
49 break;
50 default:
51 UNREACHABLE();
52 return;
53 }
54 dawn::ErrorLog() << errorTypeName << " error: " << message;
55 }
56
PrintGLFWError(int code,const char * message)57 void PrintGLFWError(int code, const char* message) {
58 dawn::ErrorLog() << "GLFW error: " << code << " - " << message;
59 }
60
61 enum class CmdBufType {
62 None,
63 Terrible,
64 // TODO(cwallez@chromium.org): double terrible cmdbuf
65 };
66
67 // Default to D3D12, Metal, Vulkan, OpenGL in that order as D3D12 and Metal are the preferred on
68 // their respective platforms, and Vulkan is preferred to OpenGL
69 #if defined(DAWN_ENABLE_BACKEND_D3D12)
70 static wgpu::BackendType backendType = wgpu::BackendType::D3D12;
71 #elif defined(DAWN_ENABLE_BACKEND_METAL)
72 static wgpu::BackendType backendType = wgpu::BackendType::Metal;
73 #elif defined(DAWN_ENABLE_BACKEND_VULKAN)
74 static wgpu::BackendType backendType = wgpu::BackendType::Vulkan;
75 #elif defined(DAWN_ENABLE_BACKEND_OPENGLES)
76 static wgpu::BackendType backendType = wgpu::BackendType::OpenGLES;
77 #elif defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
78 static wgpu::BackendType backendType = wgpu::BackendType::OpenGL;
79 #else
80 # error
81 #endif
82
83 static CmdBufType cmdBufType = CmdBufType::Terrible;
84 static std::unique_ptr<dawn_native::Instance> instance;
85 static utils::BackendBinding* binding = nullptr;
86
87 static GLFWwindow* window = nullptr;
88
89 static dawn_wire::WireServer* wireServer = nullptr;
90 static dawn_wire::WireClient* wireClient = nullptr;
91 static utils::TerribleCommandBuffer* c2sBuf = nullptr;
92 static utils::TerribleCommandBuffer* s2cBuf = nullptr;
93
CreateCppDawnDevice()94 wgpu::Device CreateCppDawnDevice() {
95 ScopedEnvironmentVar angleDefaultPlatform;
96 if (GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) {
97 angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", "swiftshader");
98 }
99
100 glfwSetErrorCallback(PrintGLFWError);
101 if (!glfwInit()) {
102 return wgpu::Device();
103 }
104
105 // Create the test window and discover adapters using it (esp. for OpenGL)
106 utils::SetupGLFWWindowHintsForBackend(backendType);
107 glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
108 window = glfwCreateWindow(640, 480, "Dawn window", nullptr, nullptr);
109 if (!window) {
110 return wgpu::Device();
111 }
112
113 instance = std::make_unique<dawn_native::Instance>();
114 utils::DiscoverAdapter(instance.get(), window, backendType);
115
116 // Get an adapter for the backend to use, and create the device.
117 dawn_native::Adapter backendAdapter;
118 {
119 std::vector<dawn_native::Adapter> adapters = instance->GetAdapters();
120 auto adapterIt = std::find_if(adapters.begin(), adapters.end(),
121 [](const dawn_native::Adapter adapter) -> bool {
122 wgpu::AdapterProperties properties;
123 adapter.GetProperties(&properties);
124 return properties.backendType == backendType;
125 });
126 ASSERT(adapterIt != adapters.end());
127 backendAdapter = *adapterIt;
128 }
129
130 WGPUDevice backendDevice = backendAdapter.CreateDevice();
131 DawnProcTable backendProcs = dawn_native::GetProcs();
132
133 binding = utils::CreateBinding(backendType, window, backendDevice);
134 if (binding == nullptr) {
135 return wgpu::Device();
136 }
137
138 // Choose whether to use the backend procs and devices directly, or set up the wire.
139 WGPUDevice cDevice = nullptr;
140 DawnProcTable procs;
141
142 switch (cmdBufType) {
143 case CmdBufType::None:
144 procs = backendProcs;
145 cDevice = backendDevice;
146 break;
147
148 case CmdBufType::Terrible: {
149 c2sBuf = new utils::TerribleCommandBuffer();
150 s2cBuf = new utils::TerribleCommandBuffer();
151
152 dawn_wire::WireServerDescriptor serverDesc = {};
153 serverDesc.procs = &backendProcs;
154 serverDesc.serializer = s2cBuf;
155
156 wireServer = new dawn_wire::WireServer(serverDesc);
157 c2sBuf->SetHandler(wireServer);
158
159 dawn_wire::WireClientDescriptor clientDesc = {};
160 clientDesc.serializer = c2sBuf;
161
162 wireClient = new dawn_wire::WireClient(clientDesc);
163 procs = dawn_wire::client::GetProcs();
164 s2cBuf->SetHandler(wireClient);
165
166 auto deviceReservation = wireClient->ReserveDevice();
167 wireServer->InjectDevice(backendDevice, deviceReservation.id,
168 deviceReservation.generation);
169
170 cDevice = deviceReservation.device;
171 } break;
172 }
173
174 dawnProcSetProcs(&procs);
175 procs.deviceSetUncapturedErrorCallback(cDevice, PrintDeviceError, nullptr);
176 return wgpu::Device::Acquire(cDevice);
177 }
178
GetSwapChainImplementation()179 uint64_t GetSwapChainImplementation() {
180 return binding->GetSwapChainImplementation();
181 }
182
GetPreferredSwapChainTextureFormat()183 wgpu::TextureFormat GetPreferredSwapChainTextureFormat() {
184 DoFlush();
185 return static_cast<wgpu::TextureFormat>(binding->GetPreferredSwapChainTextureFormat());
186 }
187
GetSwapChain(const wgpu::Device & device)188 wgpu::SwapChain GetSwapChain(const wgpu::Device& device) {
189 wgpu::SwapChainDescriptor swapChainDesc;
190 swapChainDesc.implementation = GetSwapChainImplementation();
191 return device.CreateSwapChain(nullptr, &swapChainDesc);
192 }
193
CreateDefaultDepthStencilView(const wgpu::Device & device)194 wgpu::TextureView CreateDefaultDepthStencilView(const wgpu::Device& device) {
195 wgpu::TextureDescriptor descriptor;
196 descriptor.dimension = wgpu::TextureDimension::e2D;
197 descriptor.size.width = 640;
198 descriptor.size.height = 480;
199 descriptor.size.depthOrArrayLayers = 1;
200 descriptor.sampleCount = 1;
201 descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
202 descriptor.mipLevelCount = 1;
203 descriptor.usage = wgpu::TextureUsage::RenderAttachment;
204 auto depthStencilTexture = device.CreateTexture(&descriptor);
205 return depthStencilTexture.CreateView();
206 }
207
InitSample(int argc,const char ** argv)208 bool InitSample(int argc, const char** argv) {
209 for (int i = 1; i < argc; i++) {
210 if (std::string("-b") == argv[i] || std::string("--backend") == argv[i]) {
211 i++;
212 if (i < argc && std::string("d3d12") == argv[i]) {
213 backendType = wgpu::BackendType::D3D12;
214 continue;
215 }
216 if (i < argc && std::string("metal") == argv[i]) {
217 backendType = wgpu::BackendType::Metal;
218 continue;
219 }
220 if (i < argc && std::string("null") == argv[i]) {
221 backendType = wgpu::BackendType::Null;
222 continue;
223 }
224 if (i < argc && std::string("opengl") == argv[i]) {
225 backendType = wgpu::BackendType::OpenGL;
226 continue;
227 }
228 if (i < argc && std::string("opengles") == argv[i]) {
229 backendType = wgpu::BackendType::OpenGLES;
230 continue;
231 }
232 if (i < argc && std::string("vulkan") == argv[i]) {
233 backendType = wgpu::BackendType::Vulkan;
234 continue;
235 }
236 fprintf(stderr,
237 "--backend expects a backend name (opengl, opengles, metal, d3d12, null, "
238 "vulkan)\n");
239 return false;
240 }
241 if (std::string("-c") == argv[i] || std::string("--command-buffer") == argv[i]) {
242 i++;
243 if (i < argc && std::string("none") == argv[i]) {
244 cmdBufType = CmdBufType::None;
245 continue;
246 }
247 if (i < argc && std::string("terrible") == argv[i]) {
248 cmdBufType = CmdBufType::Terrible;
249 continue;
250 }
251 fprintf(stderr, "--command-buffer expects a command buffer name (none, terrible)\n");
252 return false;
253 }
254 if (std::string("-h") == argv[i] || std::string("--help") == argv[i]) {
255 printf("Usage: %s [-b BACKEND] [-c COMMAND_BUFFER]\n", argv[0]);
256 printf(" BACKEND is one of: d3d12, metal, null, opengl, opengles, vulkan\n");
257 printf(" COMMAND_BUFFER is one of: none, terrible\n");
258 return false;
259 }
260 }
261 return true;
262 }
263
DoFlush()264 void DoFlush() {
265 if (cmdBufType == CmdBufType::Terrible) {
266 bool c2sSuccess = c2sBuf->Flush();
267 bool s2cSuccess = s2cBuf->Flush();
268
269 ASSERT(c2sSuccess && s2cSuccess);
270 }
271 glfwPollEvents();
272 }
273
ShouldQuit()274 bool ShouldQuit() {
275 return glfwWindowShouldClose(window);
276 }
277
GetGLFWWindow()278 GLFWwindow* GetGLFWWindow() {
279 return window;
280 }
281