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 }