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 }