1 module main;
2
3 import core.stdc.stdlib;
4 import std.stdio;
5 import std.conv;
6 import std.math;
7 import std.random;
8 import std..string;
9 import std.file: readText;
10
11 import bindbc.wgpu;
12 import bindbc.sdl;
13 import loader = bindbc.loader.sharedlib;
14
15 void quit(string message = "")
16 {
17 if (message.length)
18 writeln(message);
19 core.stdc.stdlib.exit(1);
20 }
21
22 void main(string[] args)
23 {
24 auto wgpuSupport = loadWGPU();
25 writeln("wgpuSupport: ", wgpuSupport);
26
27 auto sdlSupport = loadSDL();
28 writeln("sdlSupport: ", sdlSupport);
29
30 if (loader.errors.length)
31 {
32 writeln("Loader errors:");
33 foreach(info; loader.errors)
34 {
35 writeln(to!string(info.error), ": ", to!string(info.message));
36 }
37 }
38
39 version(OSX)
40 {
41 SDL_SetHint(SDL_HINT_RENDER_DRIVER, toStringz("metal"));
42 }
43
44 if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
45 quit("Error: failed to init SDL: " ~ to!string(SDL_GetError()));
46
47 uint winWidth = 1280;
48 uint winHeight = 720;
49 SDL_Window* sdlWindow = SDL_CreateWindow(toStringz("WGPU"),
50 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
51 winWidth, winHeight,
52 SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
53
54 wgpuSetLogLevel(WGPULogLevel.Debug);
55
56 SDL_SysWMinfo wmInfo;
57 SDL_GetWindowWMInfo(sdlWindow, &wmInfo);
58 writeln("Subsystem: ", wmInfo.subsystem);
59 WGPUSurface surface = createSurface(wmInfo);
60
61 WGPUAdapter adapter;
62 WGPURequestAdapterOptions adapterOpts = {
63 nextInChain: null,
64 compatibleSurface: surface
65 };
66 wgpuInstanceRequestAdapter(null, &adapterOpts, &requestAdapterCallback, cast(void*)&adapter);
67 writeln("Adapter OK");
68
69 WGPUDevice device;
70 WGPUDeviceExtras deviceExtras = {
71 chain: {
72 next: null,
73 sType: cast(WGPUSType)WGPUNativeSType.DeviceExtras
74 },
75 nativeFeatures: WGPUNativeFeature.TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
76 label: "Device",
77 tracePath: null,
78 };
79 WGPURequiredLimits limits = {
80 nextInChain: null,
81 limits: {
82 maxBindGroups: 1
83 }
84 };
85 WGPUDeviceDescriptor deviceDesc = {
86 nextInChain: cast(const(WGPUChainedStruct)*)&deviceExtras,
87 requiredFeaturesCount: 0,
88 requiredFeatures: null,
89 requiredLimits: &limits
90 };
91
92 wgpuAdapterRequestDevice(adapter, &deviceDesc, &requestDeviceCallback, cast(void*)&device);
93 writeln("Device OK");
94
95 const(char)* shaderText = readText("data/shader.wgsl").toStringz;
96 WGPUShaderModuleWGSLDescriptor wgslDescriptor = {
97 chain: {
98 next: null,
99 sType: WGPUSType.ShaderModuleWGSLDescriptor
100 },
101 source: shaderText
102 };
103 WGPUShaderModuleDescriptor shaderSource = {
104 nextInChain: cast(const(WGPUChainedStruct)*)&wgslDescriptor,
105 label: toStringz("shader.wgsl")
106 };
107 WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(device, &shaderSource);
108 writeln("Shader OK");
109
110 WGPUBindGroupLayoutDescriptor bglDesc = {
111 label: "bind group layout",
112 entries: null,
113 entryCount: 0
114 };
115 WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bglDesc);
116 WGPUBindGroupDescriptor bgDesc = {
117 label: "bind group",
118 layout: bindGroupLayout,
119 entries: null,
120 entryCount: 0
121 };
122 WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bgDesc);
123 WGPUBindGroupLayout[1] bindGroupLayouts = [ bindGroupLayout ];
124 writeln("Bind group OK");
125
126 WGPUPipelineLayoutDescriptor plDesc = {
127 bindGroupLayouts: bindGroupLayouts.ptr,
128 bindGroupLayoutCount: bindGroupLayouts.length
129 };
130 WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &plDesc);
131 writeln("Pipeline layout OK");
132
133 WGPUTextureFormat swapChainFormat = wgpuSurfaceGetPreferredFormat(surface, adapter);
134 writeln(swapChainFormat);
135
136 WGPUBlendState blend = {
137 color: {
138 srcFactor: WGPUBlendFactor.One,
139 dstFactor: WGPUBlendFactor.Zero,
140 operation: WGPUBlendOperation.Add
141 },
142 alpha: {
143 srcFactor: WGPUBlendFactor.One,
144 dstFactor: WGPUBlendFactor.Zero,
145 operation: WGPUBlendOperation.Add
146 }
147 };
148 WGPUColorTargetState cts = {
149 format: swapChainFormat,
150 blend: &blend,
151 writeMask: WGPUColorWriteMask.All
152 };
153 WGPUFragmentState fs = {
154 modul: shaderModule,
155 entryPoint: "fs_main",
156 targetCount: 1,
157 targets: &cts
158 };
159 WGPURenderPipelineDescriptor rpDesc = {
160 label: "Render pipeline",
161 layout: pipelineLayout,
162 vertex: {
163 modul: shaderModule,
164 entryPoint: "vs_main",
165 bufferCount: 0,
166 buffers: null
167 },
168 primitive: {
169 topology: WGPUPrimitiveTopology.TriangleList,
170 stripIndexFormat: WGPUIndexFormat.Undefined,
171 frontFace: WGPUFrontFace.CCW,
172 cullMode: WGPUCullMode.None
173 },
174 multisample: {
175 count: 1,
176 mask: ~0,
177 alphaToCoverageEnabled: false
178 },
179 fragment: &fs,
180 depthStencil: null
181 };
182 WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &rpDesc);
183 writeln("Render pipeline OK");
184
185 WGPUSwapChain createSwapChain(uint w, uint h) {
186 WGPUSwapChainDescriptor swcDesc = {
187 usage: WGPUTextureUsage.RenderAttachment,
188 format: swapChainFormat,
189 width: w,
190 height: h,
191 presentMode: WGPUPresentMode.Fifo
192 };
193 return wgpuDeviceCreateSwapChain(device, surface, &swcDesc);
194 }
195
196 WGPUSwapChain swapChain = createSwapChain(winWidth, winHeight);
197
198 bool running = true;
199 while(running)
200 {
201 SDL_Event event;
202 while(SDL_PollEvent(&event))
203 {
204 switch (event.type)
205 {
206 case SDL_WINDOWEVENT:
207 if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
208 {
209 winWidth = event.window.data1;
210 winHeight = event.window.data2;
211 writeln(winWidth, "x", winHeight);
212 swapChain = createSwapChain(winWidth, winHeight);
213 }
214 break;
215 case SDL_KEYUP:
216 const key = event.key.keysym.scancode;
217 if (key == 41) // Esc
218 {
219 running = false;
220 }
221 break;
222 case SDL_QUIT:
223 running = false;
224 break;
225 default:
226 break;
227 }
228 }
229
230 WGPUTextureView nextTextureView = wgpuSwapChainGetCurrentTextureView(swapChain);
231 if (!nextTextureView)
232 continue;
233
234 WGPUCommandEncoderDescriptor ceDesc = {
235 label: "Command Encoder"
236 };
237 WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &ceDesc);
238
239 WGPURenderPassColorAttachment colorAttachment = {
240 view: nextTextureView,
241 resolveTarget: null,
242 loadOp: WGPULoadOp.Clear,
243 storeOp: WGPUStoreOp.Store,
244 clearColor: WGPUColor(0.5, 0.5, 0.5, 1.0)
245 };
246 WGPURenderPassDescriptor passDesc = {
247 colorAttachments: &colorAttachment,
248 colorAttachmentCount: 1,
249 depthStencilAttachment: null
250 };
251 WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &passDesc);
252
253 wgpuRenderPassEncoderSetPipeline(renderPass, pipeline);
254 wgpuRenderPassEncoderSetBindGroup(renderPass, 0, bindGroup, 0, null);
255 wgpuRenderPassEncoderDraw(renderPass, 3, 1, 0, 0);
256 wgpuRenderPassEncoderEndPass(renderPass);
257
258 WGPUQueue queue = wgpuDeviceGetQueue(device);
259 WGPUCommandBufferDescriptor cmdbufDesc = { label: null };
260 WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(encoder, &cmdbufDesc);
261 wgpuQueueSubmit(queue, 1, &cmdBuffer);
262 wgpuSwapChainPresent(swapChain);
263 }
264
265 SDL_Quit();
266 }
267
268 WGPUSurface createSurface(SDL_SysWMinfo wmInfo)
269 {
270 WGPUSurface surface;
271 version(Windows)
272 {
273 if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
274 {
275 auto win_hwnd = wmInfo.info.win.window;
276 auto win_hinstance = wmInfo.info.win.hinstance;
277 WGPUSurfaceDescriptorFromWindowsHWND sfdHwnd = {
278 chain: {
279 next: null,
280 sType: WGPUSType.SurfaceDescriptorFromWindowsHWND
281 },
282 hinstance: win_hinstance,
283 hwnd: win_hwnd
284 };
285 WGPUSurfaceDescriptor sfd = {
286 label: null,
287 nextInChain: cast(const(WGPUChainedStruct)*)&sfdHwnd
288 };
289 surface = wgpuInstanceCreateSurface(null, &sfd);
290 }
291 else
292 {
293 quit("Unsupported subsystem, sorry");
294 }
295 }
296 else version(linux)
297 {
298 // Needs test!
299 if (wmInfo.subsystem == SDL_SYSWM_X11)
300 {
301 auto x11_display = wmInfo.info.x11.display;
302 auto x11_window = wmInfo.info.x11.window;
303 WGPUSurfaceDescriptorFromXlib sfdX11 = {
304 chain: {
305 next: null,
306 sType: WGPUSType.SurfaceDescriptorFromXlib
307 },
308 display: x11_display,
309 window: x11_window
310 };
311 WGPUSurfaceDescriptor sfd = {
312 label: null,
313 nextInChain: cast(const(WGPUChainedStruct)*)&sfdX11
314 };
315 surface = wgpuInstanceCreateSurface(null, &sfd);
316 }
317 else
318 {
319 quit("Unsupported subsystem, sorry");
320 }
321 }
322 else version(OSX)
323 {
324 // Needs test!
325 SDL_Renderer* renderer = SDL_CreateRenderer(window.sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC);
326 auto m_layer = SDL_RenderGetMetalLayer(renderer);
327
328 WGPUSurfaceDescriptorFromMetalLayer sfdMetal = {
329 chain: {
330 next: null,
331 sType: WGPUSType.SurfaceDescriptorFromMetalLayer
332 },
333 layer: m_layer
334 };
335 WGPUSurfaceDescriptor sfd = {
336 label: null,
337 nextInChain: cast(const(WGPUChainedStruct)*)&sfdMetal
338 };
339 surface = wgpuInstanceCreateSurface(null, &sfd);
340
341 SDL_DestroyRenderer(renderer);
342 }
343 return surface;
344 }
345
346 extern(C)
347 {
348 void requestAdapterCallback(WGPURequestAdapterStatus status, WGPUAdapter adapter, const(char)* message, void* userdata)
349 {
350 if (status == WGPURequestAdapterStatus.Success)
351 *cast(WGPUAdapter*)userdata = adapter;
352 else
353 {
354 writeln(status);
355 writeln(to!string(message));
356 }
357 }
358
359 void requestDeviceCallback(WGPURequestDeviceStatus status, WGPUDevice device, const(char)* message, void* userdata)
360 {
361 if (status == WGPURequestDeviceStatus.Success)
362 *cast(WGPUDevice*)userdata = device;
363 else
364 {
365 writeln(status);
366 writeln(to!string(message));
367 }
368 }
369 }