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         maxBindGroups: 1,
76         label: "Device",
77         tracePath: null,
78     };
79     WGPUDeviceDescriptor deviceDesc = {
80         nextInChain: cast(const(WGPUChainedStruct)*)&deviceExtras,
81     };
82     wgpuAdapterRequestDevice(adapter, &deviceDesc, &requestDeviceCallback, cast(void*)&device);
83     writeln("Device OK");
84     
85     const(char)* shaderText = readText("data/shader.wgsl").toStringz;
86     WGPUShaderModuleWGSLDescriptor wgslDescriptor = {
87         chain: {
88             next: null,
89             sType: WGPUSType.ShaderModuleWGSLDescriptor
90         },
91         source: shaderText
92     };
93     WGPUShaderModuleDescriptor shaderSource = {
94         nextInChain: cast(const(WGPUChainedStruct)*)&wgslDescriptor,
95         label: toStringz("shader.wgsl")
96     };
97     WGPUShaderModule shaderModule = wgpuDeviceCreateShaderModule(device, &shaderSource);
98     writeln("Shader OK");
99     
100     WGPUBindGroupLayoutDescriptor bglDesc = {
101         label: "bind group layout",
102         entries: null,
103         entryCount: 0
104     };
105     WGPUBindGroupLayout bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bglDesc);
106     WGPUBindGroupDescriptor bgDesc = {
107         label: "bind group",
108         layout: bindGroupLayout,
109         entries: null,
110         entryCount: 0
111     };
112     WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bgDesc);
113     WGPUBindGroupLayout[1] bindGroupLayouts = [ bindGroupLayout ];
114     writeln("Bind group OK");
115     
116     WGPUPipelineLayoutDescriptor plDesc = {
117         bindGroupLayouts: bindGroupLayouts.ptr,
118         bindGroupLayoutCount: bindGroupLayouts.length
119     };
120     WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &plDesc);
121     writeln("Pipeline layout OK");
122     
123     WGPUTextureFormat swapChainFormat;
124     wgpuSurfaceGetPreferredFormat(surface, adapter, &preferredTextureCallback, &swapChainFormat);
125     writeln(swapChainFormat);
126     
127     WGPUBlendState blend = {
128         color: {
129             srcFactor: WGPUBlendFactor.One,
130             dstFactor: WGPUBlendFactor.Zero,
131             operation: WGPUBlendOperation.Add
132         },
133         alpha: {
134             srcFactor: WGPUBlendFactor.One,
135             dstFactor: WGPUBlendFactor.Zero,
136             operation: WGPUBlendOperation.Add
137         }
138     };
139     WGPUColorTargetState cts = {
140         format: swapChainFormat,
141         blend: &blend,
142         writeMask: WGPUColorWriteMask.All
143     };
144     WGPUFragmentState fs = {
145         modul: shaderModule,
146         entryPoint: "fs_main",
147         targetCount: 1,
148         targets: &cts
149     };
150     WGPURenderPipelineDescriptor rpDesc = {
151         label: "Render pipeline",
152         layout: pipelineLayout,
153         vertex: {
154             modul: shaderModule,
155             entryPoint: "vs_main",
156             bufferCount: 0,
157             buffers: null
158         },
159         primitive: {
160             topology: WGPUPrimitiveTopology.TriangleList,
161             stripIndexFormat: WGPUIndexFormat.Undefined,
162             frontFace: WGPUFrontFace.CCW,
163             cullMode: WGPUCullMode.None
164         },
165         multisample: {
166             count: 1, 
167             mask: ~0,
168             alphaToCoverageEnabled: false
169         },
170         fragment: &fs,
171         depthStencil: null
172     };
173     WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &rpDesc);
174     writeln("Render pipeline OK");
175     
176     WGPUSwapChain createSwapChain(uint w, uint h) {
177         WGPUSwapChainDescriptor swcDesc = {
178             usage: WGPUTextureUsage.RenderAttachment,
179             format: swapChainFormat,
180             width: w,
181             height: h,
182             presentMode: WGPUPresentMode.Fifo
183         };
184         return wgpuDeviceCreateSwapChain(device, surface, &swcDesc);
185     }
186 
187     WGPUSwapChain swapChain = createSwapChain(winWidth, winHeight);
188     
189     bool running = true;
190     while(running)
191     {
192         SDL_Event event;
193         while(SDL_PollEvent(&event))
194         {
195             switch (event.type)
196             {
197                 case SDL_WINDOWEVENT:
198                     if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
199                     {
200                         winWidth = event.window.data1;
201                         winHeight = event.window.data2;
202                         writeln(winWidth, "x", winHeight);
203                         swapChain = createSwapChain(winWidth, winHeight);
204                     }
205                     break;
206                 case SDL_KEYUP:
207                     const key = event.key.keysym.scancode;
208                     if (key == 41) // Esc
209                     {
210                         running = false;
211                     }
212                     break;
213                 case SDL_QUIT:
214                     running = false;
215                     break;
216                 default:
217                     break;
218             }
219         }
220         
221         WGPUTextureView nextTextureView = wgpuSwapChainGetCurrentTextureView(swapChain);
222         if (!nextTextureView)
223             continue;
224         
225         WGPUCommandEncoderDescriptor ceDesc = {
226             label: "Command Encoder"
227         };
228         WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &ceDesc);
229         
230         WGPURenderPassColorAttachment colorAttachment = {
231             view: nextTextureView,
232             resolveTarget: null,
233             loadOp: WGPULoadOp.Clear,
234             storeOp: WGPUStoreOp.Store,
235             clearColor: WGPUColor(0.5, 0.5, 0.5, 1.0)
236         };
237         WGPURenderPassDescriptor passDesc = {
238             colorAttachments: &colorAttachment,
239             colorAttachmentCount: 1,
240             depthStencilAttachment: null
241         };
242         WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &passDesc);
243         
244         wgpuRenderPassEncoderSetPipeline(renderPass, pipeline);
245         wgpuRenderPassEncoderSetBindGroup(renderPass, 0, bindGroup, 0, null);
246         wgpuRenderPassEncoderDraw(renderPass, 3, 1, 0, 0);
247         wgpuRenderPassEncoderEndPass(renderPass);
248         
249         WGPUQueue queue = wgpuDeviceGetQueue(device);
250         WGPUCommandBufferDescriptor cmdbufDesc = { label: null };
251         WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(encoder, &cmdbufDesc);
252         wgpuQueueSubmit(queue, 1, &cmdBuffer);
253         wgpuSwapChainPresent(swapChain);
254     }
255     
256     SDL_Quit();
257 }
258 
259 WGPUSurface createSurface(SDL_SysWMinfo wmInfo)
260 {
261     WGPUSurface surface;
262     version(Windows)
263     {
264         if (wmInfo.subsystem == SDL_SYSWM_WINDOWS)
265         {
266             auto win_hwnd = wmInfo.info.win.window;
267             auto win_hinstance = wmInfo.info.win.hinstance;
268             WGPUSurfaceDescriptorFromWindowsHWND sfdHwnd = {
269                 chain: {
270                     next: null,
271                     sType: WGPUSType.SurfaceDescriptorFromWindowsHWND
272                 },
273                 hinstance: win_hinstance,
274                 hwnd: win_hwnd
275             };
276             WGPUSurfaceDescriptor sfd = {
277                 label: null,
278                 nextInChain: cast(const(WGPUChainedStruct)*)&sfdHwnd
279             };
280             surface = wgpuInstanceCreateSurface(null, &sfd);
281         }
282         else
283         {
284             quit("Unsupported subsystem, sorry");
285         }
286     }
287     else version(linux)
288     {
289         // Needs test!
290         if (wmInfo.subsystem == SDL_SYSWM_X11)
291         {
292             auto x11_display = wmInfo.info.x11.display;
293             auto x11_window = wmInfo.info.x11.window;
294             WGPUSurfaceDescriptorFromXlib sfdX11 = {
295                 chain: {
296                     next: null,
297                     sType: WGPUSType.SurfaceDescriptorFromXlib
298                 },
299                 display: x11_display,
300                 window: x11_window
301             };
302             WGPUSurfaceDescriptor sfd = {
303                 label: null,
304                 nextInChain: cast(const(WGPUChainedStruct)*)&sfdX11
305             };
306             surface = wgpuInstanceCreateSurface(null, &sfd);
307         }
308         else
309         {
310             quit("Unsupported subsystem, sorry");
311         }
312     }
313     else version(OSX)
314     {
315         // Needs test!
316         SDL_Renderer* renderer = SDL_CreateRenderer(window.sdlWindow, -1, SDL_RENDERER_PRESENTVSYNC);
317         auto m_layer = SDL_RenderGetMetalLayer(renderer);
318         
319         WGPUSurfaceDescriptorFromMetalLayer sfdMetal = {
320             chain: {
321                 next: null,
322                 sType: WGPUSType.SurfaceDescriptorFromMetalLayer
323             },
324             layer: m_layer
325         };
326         WGPUSurfaceDescriptor sfd = {
327             label: null,
328             nextInChain: cast(const(WGPUChainedStruct)*)&sfdMetal
329         };
330         surface = wgpuInstanceCreateSurface(null, &sfd);
331         
332         SDL_DestroyRenderer(renderer);
333     }
334     return surface;
335 }
336 
337 extern(C)
338 {
339     void requestAdapterCallback(WGPUAdapter result, void* userdata)
340     {
341         *cast(WGPUAdapter*)userdata = result;
342     }
343 
344     extern(C) void requestDeviceCallback(WGPUDevice result, void* userdata)
345     {
346         *cast(WGPUDevice*)userdata = result;
347     }
348     
349     extern(C) void preferredTextureCallback(WGPUTextureFormat format, void* userdata)
350     {
351         *cast(WGPUTextureFormat*)userdata = format;
352     }
353 }