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 nextTexture = wgpuSwapChainGetCurrentTextureView(swapChain); 222 if (!nextTexture) 223 continue; 224 225 WGPUCommandEncoderDescriptor ceDesc = { 226 label: "Command Encoder" 227 }; 228 WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &ceDesc); 229 230 WGPURenderPassColorAttachmentDescriptor caDesc = { 231 attachment: nextTexture, 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: &caDesc, 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 }