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 }