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         code: 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         // wgpu crashes when rendering to minimized window
231         auto winFlags = SDL_GetWindowFlags(sdlWindow);
232         auto isMinimized = winFlags & SDL_WINDOW_MINIMIZED;
233         if (isMinimized)
234             continue;
235         
236         WGPUTextureView nextTextureView = wgpuSwapChainGetCurrentTextureView(swapChain);
237         if (!nextTextureView)
238             continue;
239         
240         WGPUCommandEncoderDescriptor ceDesc = {
241             label: "Command Encoder"
242         };
243         WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &ceDesc);
244         
245         WGPURenderPassColorAttachment colorAttachment = {
246             view: nextTextureView,
247             resolveTarget: null,
248             loadOp: WGPULoadOp.Clear,
249             storeOp: WGPUStoreOp.Store,
250             clearColor: WGPUColor(0.5, 0.5, 0.5, 1.0)
251         };
252         WGPURenderPassDescriptor passDesc = {
253             colorAttachments: &colorAttachment,
254             colorAttachmentCount: 1,
255             depthStencilAttachment: null
256         };
257         WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &passDesc);
258         
259         wgpuRenderPassEncoderSetPipeline(renderPass, pipeline);
260         wgpuRenderPassEncoderSetBindGroup(renderPass, 0, bindGroup, 0, null);
261         wgpuRenderPassEncoderDraw(renderPass, 3, 1, 0, 0);
262         wgpuRenderPassEncoderEndPass(renderPass);
263         
264         WGPUQueue queue = wgpuDeviceGetQueue(device);
265         WGPUCommandBufferDescriptor cmdbufDesc = { label: null };
266         WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(encoder, &cmdbufDesc);
267         wgpuQueueSubmit(queue, 1, &cmdBuffer);
268         wgpuSwapChainPresent(swapChain);
269     }
270     
271     SDL_Quit();
272 }
273 
274 WGPUSurface createSurface(SDL_SysWMinfo wmInfo)
275 {
276     WGPUSurface surface;
277     version(Windows)
278     {
279         if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
280         {
281             auto win_hwnd = wmInfo.info.win.window;
282             auto win_hinstance = wmInfo.info.win.hinstance;
283             WGPUSurfaceDescriptorFromWindowsHWND sfdHwnd = {
284                 chain: {
285                     next: null,
286                     sType: WGPUSType.SurfaceDescriptorFromWindowsHWND
287                 },
288                 hinstance: win_hinstance,
289                 hwnd: win_hwnd
290             };
291             WGPUSurfaceDescriptor sfd = {
292                 label: null,
293                 nextInChain: cast(const(WGPUChainedStruct)*)&sfdHwnd
294             };
295             surface = wgpuInstanceCreateSurface(null, &sfd);
296         }
297         else
298         {
299             quit("Unsupported subsystem, sorry");
300         }
301     }
302     else version(linux)
303     {
304         // Needs test!
305         if (wmInfo.subsystem == SDL_SYSWM_X11)
306         {
307             auto x11_display = wmInfo.info.x11.display;
308             auto x11_window = wmInfo.info.x11.window;
309             WGPUSurfaceDescriptorFromXlib sfdX11 = {
310                 chain: {
311                     next: null,
312                     sType: WGPUSType.SurfaceDescriptorFromXlib
313                 },
314                 display: x11_display,
315                 window: x11_window
316             };
317             WGPUSurfaceDescriptor sfd = {
318                 label: null,
319                 nextInChain: cast(const(WGPUChainedStruct)*)&sfdX11
320             };
321             surface = wgpuInstanceCreateSurface(null, &sfd);
322         }
323         else
324         {
325             quit("Unsupported subsystem, sorry");
326         }
327     }
328     else version(OSX)
329     {
330         // Needs test!
331         SDL_Renderer* renderer = SDL_CreateRenderer(window.sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC);
332         auto m_layer = SDL_RenderGetMetalLayer(renderer);
333         
334         WGPUSurfaceDescriptorFromMetalLayer sfdMetal = {
335             chain: {
336                 next: null,
337                 sType: WGPUSType.SurfaceDescriptorFromMetalLayer
338             },
339             layer: m_layer
340         };
341         WGPUSurfaceDescriptor sfd = {
342             label: null,
343             nextInChain: cast(const(WGPUChainedStruct)*)&sfdMetal
344         };
345         surface = wgpuInstanceCreateSurface(null, &sfd);
346         
347         SDL_DestroyRenderer(renderer);
348     }
349     return surface;
350 }
351 
352 extern(C)
353 {
354     void requestAdapterCallback(WGPURequestAdapterStatus status, WGPUAdapter adapter, const(char)* message, void* userdata)
355     {
356         if (status == WGPURequestAdapterStatus.Success)
357             *cast(WGPUAdapter*)userdata = adapter;
358         else
359         {
360             writeln(status);
361             writeln(to!string(message));
362         }
363     }
364 
365     void requestDeviceCallback(WGPURequestDeviceStatus status, WGPUDevice device, const(char)* message, void* userdata)
366     {
367         if (status == WGPURequestDeviceStatus.Success)
368             *cast(WGPUDevice*)userdata = device;
369         else
370         {
371             writeln(status);
372             writeln(to!string(message));
373         }
374     }
375 }