SLProject  4.3.020
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
AppDemoWebGPU.cpp
Go to the documentation of this file.
1 /**
2  * \file AppDemoWebGPU.cpp
3  * \brief Small WebGPU test application
4  * \details WebGPU is an API provided by a web browser that enables
5  * webpage scripts to efficiently utilize a device's graphics
6  * processing unit (GPU). This is achieved with the underlying
7  * Vulkan, Metal, or Direct3D 12 system APIs. On relevant devices,
8  * WebGPU is intended to supersede the older WebGL standard.
9  * \date Summer 2023
10  * \authors Marino von Wattenwyl
11  * \copyright http://opensource.org/licenses/GPL-3.0
12  * \remarks Please use clangformat to format the code. See more code style on
13  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
14 */
15 
16 //////////////////////////////////////////////////////////////////////////////
17 // Please read the documentation for the WebGPUDemoApp struct further below //
18 //////////////////////////////////////////////////////////////////////////////
19 
20 #if defined(_WIN32)
21 # define SYSTEM_WINDOWS
22 # define GLFW_EXPOSE_NATIVE_WIN32
23 # define WIN32_LEAN_AND_MEAN
24 # include <windows.h>
25 #elif defined(__linux__)
26 # define SYSTEM_LINUX
27 # define GLFW_EXPOSE_NATIVE_X11
28 #elif defined(__APPLE__)
29 # ifndef SYSTEM_DARWIN
30 # define SYSTEM_DARWIN
31 # endif
32 # define GLFW_EXPOSE_NATIVE_COCOA
33 #endif
34 //-----------------------------------------------------------------------------
35 #include <webgpu.h>
36 #include <GLFW/glfw3.h>
37 #include <GLFW/glfw3native.h>
38 #include <opencv2/imgcodecs.hpp>
39 #include <opencv2/imgproc.hpp>
40 #include <SLMat4.h>
41 #include <SLVec4.h>
42 
43 #include <iostream>
44 #include <vector>
45 #include <cstdlib>
46 #include <cstring>
47 
48 //-----------------------------------------------------------------------------
49 #ifdef SYSTEM_DARWIN
50 extern "C" void* createMetalLayer(void* window);
51 #endif
52 //-----------------------------------------------------------------------------
53 #define WEBGPU_DEMO_LOG(msg) std::cout << (msg) << std::endl
54 //-----------------------------------------------------------------------------
55 #define WEBGPU_DEMO_CHECK(condition, errorMsg) \
56  if (!(condition)) \
57  { \
58  std::cerr << (errorMsg) << std::endl; \
59  std::exit(1); \
60  }
61 //-----------------------------------------------------------------------------
62 //! Application struct WebGPUDemoApp with all global variables
64 {
65  GLFWwindow* window = nullptr;
66  int surfaceWidth = 0;
67  int surfaceHeight = 0;
68 
69  WGPUInstance instance = nullptr;
70  WGPUAdapter adapter = nullptr;
71  WGPUDevice device = nullptr;
72  WGPUQueue queue = nullptr;
73  WGPUSurface surface = nullptr;
74  WGPUBuffer vertexBuffer = nullptr;
75  WGPUBuffer indexBuffer = nullptr;
76  WGPUBuffer uniformBuffer = nullptr;
77  WGPUTexture depthTexture = nullptr;
78  WGPUTextureView depthTextureView = nullptr;
79  WGPUTexture texture = nullptr;
80  WGPUTextureView textureView = nullptr;
81  WGPUSampler sampler = nullptr;
82  WGPUShaderModule shaderModule = nullptr;
83  WGPUBindGroupLayout bindGroupLayout = nullptr;
84  WGPUBindGroup bindGroup = nullptr;
85  WGPUPipelineLayout pipelineLayout = nullptr;
86  WGPURenderPipeline pipeline = nullptr;
87 
88  WGPUSurfaceConfiguration surfaceConfig;
89  WGPUTextureFormat depthTextureFormat;
90  WGPUTextureDescriptor depthTextureDesc;
91  WGPUTextureViewDescriptor depthTextureViewDesc;
92 
93  unsigned vertexDataSize;
94  unsigned indexCount;
95  unsigned indexDataSize;
96 
97  float camRotX = 0.0f;
98  float camRotY = 0.0f;
99  float camZ = 2.0f;
100 };
101 //-----------------------------------------------------------------------------
103 {
104  float positionX;
105  float positionY;
106  float positionZ;
107  float normalX;
108  float normalY;
109  float normalZ;
110  float uvX;
111  float uvY;
112 };
113 //-----------------------------------------------------------------------------
114 struct alignas(16) ShaderUniformData
115 {
116  float projectionMatrix[16];
117  float viewMatrix[16];
118  float modelMatrix[16];
119 };
120 //-----------------------------------------------------------------------------
121 static_assert(sizeof(ShaderUniformData) % 16 == 0, "uniform data size must be a multiple of 16");
122 //-----------------------------------------------------------------------------
124 {
125  // Get the window size from the GLFW window.
126  glfwGetWindowSize(app.window, &app.surfaceWidth, &app.surfaceHeight);
127 
128  // The surface size might be zero if the window is minimized.
129  if (app.surfaceWidth == 0 || app.surfaceHeight == 0)
130  return;
131 
132  WEBGPU_DEMO_LOG("[WebGPU] Re-configuring surface");
133  app.surfaceConfig.width = app.surfaceWidth;
134  app.surfaceConfig.height = app.surfaceHeight;
135  wgpuSurfaceConfigure(app.surface, &app.surfaceConfig);
136 
137  // Recreate the depth texture.
138 
139  wgpuTextureViewRelease(app.depthTextureView);
140  wgpuTextureDestroy(app.depthTexture);
141  wgpuTextureRelease(app.depthTexture);
142 
143  app.depthTextureDesc.size.width = app.surfaceWidth;
144  app.depthTextureDesc.size.height = app.surfaceHeight;
145 
146  app.depthTexture = wgpuDeviceCreateTexture(app.device, &app.depthTextureDesc);
147  WEBGPU_DEMO_CHECK(app.depthTexture, "[WebGPU] Failed to re-create depth texture");
148  WEBGPU_DEMO_LOG("[WebGPU] Depth texture re-created");
149 
150  app.depthTextureView = wgpuTextureCreateView(app.depthTexture, &app.depthTextureViewDesc);
151  WEBGPU_DEMO_CHECK(app.depthTextureView, "[WebGPU] Failed to re-create depth texture view");
152  WEBGPU_DEMO_LOG("[WebGPU] Depth texture view re-created");
153 }
154 //-----------------------------------------------------------------------------
156 {
157  if (app.surfaceWidth == 0 || app.surfaceHeight == 0)
158  return;
159 
160  // Get a texture from the surface to render into.
161  WGPUSurfaceTexture surfaceTexture;
162  wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture);
163 
164  // The surface might change over time.
165  // For example, the window might be resized or minimized.
166  // We have to check the status and adapt to it.
167  switch (surfaceTexture.status)
168  {
169  case WGPUSurfaceGetCurrentTextureStatus_Success:
170  // Everything is ok, but still check for a suboptimal texture and re-configure it if needed.
171  if (surfaceTexture.suboptimal)
172  {
173  WEBGPU_DEMO_LOG("[WebGPU] Re-configuring currently suboptimal surface");
174  reconfigureSurface(app);
175  wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture);
176  }
177 
178  break;
179 
180  case WGPUSurfaceGetCurrentTextureStatus_Timeout:
181  case WGPUSurfaceGetCurrentTextureStatus_Outdated:
182  case WGPUSurfaceGetCurrentTextureStatus_Lost:
183  // The surface needs to be re-configured.
184  reconfigureSurface(app);
185  wgpuSurfaceGetCurrentTexture(app.surface, &surfaceTexture);
186  break;
187 
188  case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory:
189  case WGPUSurfaceGetCurrentTextureStatus_DeviceLost:
190  // An error occured.
191  WEBGPU_DEMO_CHECK(false, "[WebGPU] Failed to acquire current surface texture");
192  break;
193 
194  case WGPUSurfaceGetCurrentTextureStatus_Force32:
195  break;
196  }
197 
198  // === Create a WebGPU texture view ===
199  // The texture view is where we render our image into.
200 
201  // Create a view into the texture to specify where and how to modify the texture.
202  WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, nullptr);
203 
204  // === Prepare uniform data ===
205  float aspectRatio = static_cast<float>(app.surfaceWidth) / static_cast<float>(app.surfaceHeight);
206 
207  SLMat4f modelMatrix;
208 
209  SLMat4f projectionMatrix;
210  projectionMatrix.perspective(70.0f, aspectRatio, 0.1, 1000.0f);
211 
212  SLMat4f viewMatrix;
213  viewMatrix.rotate(app.camRotY, SLVec3f::AXISY);
214  viewMatrix.rotate(app.camRotX, SLVec3f::AXISX);
215  viewMatrix.translate(0.0f, 0.0f, 2.0f);
216  viewMatrix.invert();
217 
218  // === Update uniforms ===
219  ShaderUniformData uniformData = {};
220  std::memcpy(uniformData.projectionMatrix, projectionMatrix.m(), sizeof(uniformData.projectionMatrix));
221  std::memcpy(uniformData.viewMatrix, viewMatrix.m(), sizeof(uniformData.viewMatrix));
222  std::memcpy(uniformData.modelMatrix, modelMatrix.m(), sizeof(uniformData.modelMatrix));
223  wgpuQueueWriteBuffer(app.queue, app.uniformBuffer, 0, &uniformData, sizeof(ShaderUniformData));
224 
225  // === Create a WebGPU command encoder ===
226  // The encoder encodes the commands for the GPU into a command buffer.
227 
228  WGPUCommandEncoderDescriptor cmdEncoderDesc = {};
229  cmdEncoderDesc.label = "Demo Command Encoder";
230 
231  WGPUCommandEncoder cmdEncoder = wgpuDeviceCreateCommandEncoder(app.device, &cmdEncoderDesc);
232  WEBGPU_DEMO_CHECK(cmdEncoder, "[WebGPU] Failed to create command encoder");
233 
234  // === Create a WebGPU render pass ===
235  // The render pass specifies what attachments to use while rendering.
236  // A color attachment specifies what view to render into and what to do with the texture before and after
237  // rendering. We clear the texture before rendering and store the results after rendering.
238  // The depth attachment specifies what depth texture to use.
239 
240  WGPURenderPassColorAttachment colorAttachment = {};
241  colorAttachment.view = view;
242  colorAttachment.loadOp = WGPULoadOp_Clear;
243  colorAttachment.storeOp = WGPUStoreOp_Store;
244  colorAttachment.clearValue.r = 0.3;
245  colorAttachment.clearValue.g = 0.0;
246  colorAttachment.clearValue.b = 0.2;
247  colorAttachment.clearValue.a = 1.0;
248 
249  WGPURenderPassDepthStencilAttachment depthStencilAttachment = {};
250  depthStencilAttachment.view = app.depthTextureView;
251  depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
252  depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store;
253  depthStencilAttachment.depthClearValue = 1.0f;
254  depthStencilAttachment.depthReadOnly = false;
255  depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
256  depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store;
257  depthStencilAttachment.stencilClearValue = 0.0f;
258  depthStencilAttachment.stencilReadOnly = true;
259 
260  WGPURenderPassDescriptor renderPassDesc = {};
261  renderPassDesc.label = "Demo Render Pass";
262  renderPassDesc.colorAttachmentCount = 1;
263  renderPassDesc.colorAttachments = &colorAttachment;
264  renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
265 
266  // === Encode the commands ===
267  // The commands to begin a render pass, bind a pipeline, draw the triangle and end the render pass
268  // are encoded into a buffer.
269 
270  WGPURenderPassEncoder renderPassEncoder = wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc);
271  wgpuRenderPassEncoderSetPipeline(renderPassEncoder, app.pipeline);
272  wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, app.vertexBuffer, 0, app.vertexDataSize);
273  wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, app.indexBuffer, WGPUIndexFormat_Uint16, 0, app.indexDataSize);
274  wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, app.bindGroup, 0, nullptr);
275  wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, app.indexCount, 1, 0, 0, 0);
276  wgpuRenderPassEncoderEnd(renderPassEncoder);
277 
278  // === Get the command buffer ===
279  // The command encoder is finished to get the commands for the GPU to execute in a command buffer.
280 
281  WGPUCommandBufferDescriptor cmdBufferDesc = {};
282  cmdBufferDesc.label = "Demo Command Buffer";
283 
284  WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(cmdEncoder, &cmdBufferDesc);
285 
286  // === Submit the command buffer to the GPU ===
287  // The work for the GPU is submitted through the queue and executed.
288  wgpuQueueSubmit(app.queue, 1, &cmdBuffer);
289 
290  // === Present the surface ===
291  // This presents our rendered texture to the screen.
292  wgpuSurfacePresent(app.surface);
293 
294  // === Clean up resources ===
295  wgpuCommandBufferRelease(cmdBuffer);
296  wgpuRenderPassEncoderRelease(renderPassEncoder);
297  wgpuCommandEncoderRelease(cmdEncoder);
298  wgpuTextureViewRelease(view);
299  wgpuTextureRelease(surfaceTexture.texture);
300 }
301 //-----------------------------------------------------------------------------
302 void onResize(GLFWwindow* window, int width, int height)
303 {
304  WebGPUDemoApp& app = *((WebGPUDemoApp*)glfwGetWindowUserPointer(window));
305  reconfigureSurface(app);
306  onPaint(app);
307 }
308 //-----------------------------------------------------------------------------
310 {
311  // === Initialize GLFW ===
312 
313  WEBGPU_DEMO_CHECK(glfwInit(), "[GLFW] Failed to initialize");
314  WEBGPU_DEMO_LOG("[GLFW] Initialized");
315 
316  // === Create the GLFW window ===
317 
318  // Prevent GLFW from creating an OpenGL context as the underlying graphics API probably won't be OpenGL.
319  glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
320 
321  app.window = glfwCreateWindow(1280, 720, "WebGPU Demo", nullptr, nullptr);
322  WEBGPU_DEMO_CHECK(app.window, "[GLFW] Failed to create window");
323  WEBGPU_DEMO_LOG("[GLFW] Window created");
324 
325  glfwSetWindowUserPointer(app.window, &app);
326  glfwSetFramebufferSizeCallback(app.window, onResize);
327 }
328 //-----------------------------------------------------------------------------
329 void handleAdapterRequest(WGPURequestAdapterStatus status,
330  WGPUAdapter adapter,
331  char const* message,
332  void* userdata)
333 {
334  WEBGPU_DEMO_CHECK(status == WGPURequestAdapterStatus_Success,
335  "[WebGPU] Failed to acquire adapter: " + std::string(message));
336  WEBGPU_DEMO_LOG("[WebGPU] Adapter acquired");
337 
338  WGPUAdapter* outAdapter = (WGPUAdapter*)userdata;
339  *outAdapter = adapter;
340 }
341 //-----------------------------------------------------------------------------
342 void handleDeviceRequest(WGPURequestDeviceStatus status,
343  WGPUDevice device,
344  char const* message,
345  void* userdata)
346 {
347  WEBGPU_DEMO_CHECK(status == WGPURequestDeviceStatus_Success,
348  "[WebGPU] Failed to acquire device: " + std::string(message));
349  WEBGPU_DEMO_LOG("[WebGPU] Device acquired");
350 
351  WGPUDevice* outDevice = (WGPUDevice*)userdata;
352  *outDevice = device;
353 }
354 //-----------------------------------------------------------------------------
356 {
357  // === Create a WebGPU instance ===
358  // The instance is the root interface to WebGPU through which we create
359  // all other WebGPU resources.
360 
361  app.instance = wgpuCreateInstance(nullptr);
362  WEBGPU_DEMO_CHECK(app.instance, "[WebGPU] Failed to create instance");
363  WEBGPU_DEMO_LOG("[WebGPU] Instance created");
364 
365  // === Acquire a WebGPU adapter ===
366  // An adapter provides information about the capabilities of the GPU.
367 
368  WGPURequestAdapterOptions adapterOptions = {};
369 
370  wgpuInstanceRequestAdapter(app.instance,
371  &adapterOptions,
373  &app.adapter);
374  WEBGPU_DEMO_CHECK(app.adapter, "[WebGPU] Failed to create adapter");
375  WEBGPU_DEMO_LOG("[WebGPU] Adapter created");
376 
377  WGPUSupportedLimits adapterLimits = {};
378  wgpuAdapterGetLimits(app.adapter, &adapterLimits);
379 
380  // === Acquire a WebGPU device ===
381  // A device provides access to a GPU and is created from an adapter.
382  // We specify the capabilities that we require our device to have in requiredLimits.
383  // We cannot access more resources than specified in the required limits,
384  // which is how WebGPU prevents code from working on one machine and not on another.
385 
386  WGPURequiredLimits requiredLimits = {};
387  requiredLimits.limits.maxVertexAttributes = 3u;
388  requiredLimits.limits.maxVertexBuffers = 1u;
389  requiredLimits.limits.maxBufferSize = 2048ull;
390  requiredLimits.limits.maxVertexBufferArrayStride = sizeof(VertexData);
391  requiredLimits.limits.maxInterStageShaderComponents = 5u;
392  requiredLimits.limits.maxBindGroups = 1u;
393  requiredLimits.limits.maxBindingsPerBindGroup = 3u;
394  requiredLimits.limits.maxUniformBuffersPerShaderStage = 1u;
395  requiredLimits.limits.maxUniformBufferBindingSize = 512ull;
396  requiredLimits.limits.maxSampledTexturesPerShaderStage = 1u;
397  requiredLimits.limits.maxSamplersPerShaderStage = 1u;
398  requiredLimits.limits.maxTextureDimension1D = 4096;
399  requiredLimits.limits.maxTextureDimension2D = 4096;
400  requiredLimits.limits.maxTextureArrayLayers = 1;
401  requiredLimits.limits.minStorageBufferOffsetAlignment = adapterLimits.limits.minStorageBufferOffsetAlignment;
402  requiredLimits.limits.minUniformBufferOffsetAlignment = adapterLimits.limits.minUniformBufferOffsetAlignment;
403 
404  WGPUDeviceDescriptor deviceDesc = {};
405  deviceDesc.label = "Demo Device";
406  deviceDesc.requiredLimits = &requiredLimits;
407  deviceDesc.defaultQueue.label = "Demo Queue";
408 
409  wgpuAdapterRequestDevice(app.adapter,
410  &deviceDesc,
412  &app.device);
413 
414  // === Acquire a WebGPU queue ===
415  // The queue is where the commands for the GPU are submitted to.
416 
417  app.queue = wgpuDeviceGetQueue(app.device);
418  WEBGPU_DEMO_CHECK(app.queue, "[WebGPU] Failed to acquire queue");
419  WEBGPU_DEMO_LOG("[WebGPU] Queue acquired");
420 
421  // === Create a WebGPU surface ===
422  // The surface is where our rendered images will be presented to.
423  // It is created from window handles on most platforms and from a canvas in the browser.
424 
425 #if defined(SYSTEM_WINDOWS)
426  WGPUSurfaceDescriptorFromWindowsHWND nativeSurfaceDesc = {};
427  nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND;
428  nativeSurfaceDesc.hinstance = GetModuleHandle(nullptr);
429  nativeSurfaceDesc.hwnd = glfwGetWin32Window(app.window);
430 #elif defined(SYSTEM_LINUX)
431  WGPUSurfaceDescriptorFromXlibWindow nativeSurfaceDesc = {};
432  nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromXlibWindow;
433  nativeSurfaceDesc.display = glfwGetX11Display();
434  nativeSurfaceDesc.window = glfwGetX11Window(app.window);
435 #elif defined(SYSTEM_DARWIN)
436  WGPUSurfaceDescriptorFromMetalLayer nativeSurfaceDesc = {};
437  nativeSurfaceDesc.chain.sType = WGPUSType_SurfaceDescriptorFromMetalLayer;
438  nativeSurfaceDesc.layer = createMetalLayer(glfwGetCocoaWindow(app.window));
439 #endif
440 
441  WGPUSurfaceDescriptor surfaceDesc = {};
442  surfaceDesc.label = "Demo Surface";
443  surfaceDesc.nextInChain = (const WGPUChainedStruct*)&nativeSurfaceDesc;
444 
445  app.surface = wgpuInstanceCreateSurface(app.instance, &surfaceDesc);
446  WEBGPU_DEMO_CHECK(app.surface, "[WebGPU] Failed to create surface");
447  WEBGPU_DEMO_LOG("[WebGPU] Surface created");
448 
449  // === Configure the surface ===
450  // The surface needs to be configured before images can be presented.
451 
452  // Query the surface capabilities from the adapter.
453  WGPUSurfaceCapabilities surfaceCapabilities;
454  wgpuSurfaceGetCapabilities(app.surface,
455  app.adapter,
456  &surfaceCapabilities);
457 
458  // Get the window size from the GLFW window.
459  glfwGetFramebufferSize(app.window,
460  &app.surfaceWidth,
461  &app.surfaceHeight);
462 
463  app.surfaceConfig = {};
464  app.surfaceConfig.device = app.device;
465  app.surfaceConfig.usage = WGPUTextureUsage_RenderAttachment;
466  app.surfaceConfig.format = surfaceCapabilities.formats[0];
467  app.surfaceConfig.presentMode = WGPUPresentMode_Fifo;
468  app.surfaceConfig.alphaMode = surfaceCapabilities.alphaModes[0];
469  app.surfaceConfig.width = app.surfaceWidth;
470  app.surfaceConfig.height = app.surfaceHeight;
471  wgpuSurfaceConfigure(app.surface, &app.surfaceConfig);
472  WEBGPU_DEMO_LOG("[WebGPU] Surface configured");
473 
474  // === Create the vertex buffer ===
475  // The vertex buffer contains the input data for the shader.
476 
477  // clang-format off
478  std::vector<VertexData> vertexData =
479  {
480  // left
481  {-0.5, 0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
482  {-0.5, -0.5, -0.5, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
483  {-0.5, 0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f},
484  {-0.5, -0.5, 0.5, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f},
485 
486  // right
487  { 0.5, 0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
488  { 0.5, -0.5, 0.5, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
489  { 0.5, 0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f},
490  { 0.5, -0.5, -0.5, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f},
491 
492  // bottom
493  {-0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f},
494  {-0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f},
495  { 0.5, -0.5, 0.5, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f},
496  { 0.5, -0.5, -0.5, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f},
497 
498  // top
499  {-0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
500  {-0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f},
501  { 0.5, 0.5, -0.5, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f},
502  { 0.5, 0.5, 0.5, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f},
503 
504  // back
505  { 0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
506  { 0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f},
507  {-0.5, 0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f},
508  {-0.5, -0.5, -0.5, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f},
509 
510  // front
511  {-0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
512  {-0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
513  { 0.5, 0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f},
514  { 0.5, -0.5, 0.5, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f},
515  };
516  // clang-format on
517 
518  unsigned vertexCount = vertexData.size();
519  app.vertexDataSize = vertexData.size() * sizeof(VertexData);
520 
521  WGPUBufferDescriptor vertexBufferDesc = {};
522  vertexBufferDesc.label = "Demo Vertex Buffer";
523  vertexBufferDesc.size = app.vertexDataSize;
524  vertexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex;
525 
526  app.vertexBuffer = wgpuDeviceCreateBuffer(app.device, &vertexBufferDesc);
527  WEBGPU_DEMO_CHECK(app.vertexBuffer, "[WebGPU] Failed to create vertex buffer");
528  WEBGPU_DEMO_LOG("[WebGPU] Vertex buffer created");
529 
530  // Upload the data to the GPU.
531  wgpuQueueWriteBuffer(app.queue,
532  app.vertexBuffer,
533  0,
534  vertexData.data(),
535  app.vertexDataSize);
536 
537  // === Create the index buffer ===
538 
539  // clang-format off
540  std::vector<std::uint16_t> indexData =
541  {
542  0, 1, 2, 2, 1, 3, // left
543  4, 5, 6, 6, 5, 7, // right
544  8, 9, 10, 10, 9, 11, // bottom
545  12, 13, 14, 14, 13, 15, // top
546  16, 17, 18, 18, 17, 19, // back
547  20, 21, 22, 22, 21, 23, // front
548  };
549  // clang-format on
550 
551  app.indexCount = indexData.size();
552  app.indexDataSize = indexData.size() * sizeof(std::uint16_t);
553 
554  WGPUBufferDescriptor indexBufferDesc = {};
555  indexBufferDesc.label = "Demo Index Buffer";
556  indexBufferDesc.size = app.indexDataSize;
557  indexBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index;
558 
559  app.indexBuffer = wgpuDeviceCreateBuffer(app.device, &indexBufferDesc);
560  WEBGPU_DEMO_CHECK(app.indexBuffer, "[WebGPU] Failed to create index buffer");
561  WEBGPU_DEMO_LOG("[WebGPU] Index buffer created");
562 
563  wgpuQueueWriteBuffer(app.queue,
564  app.indexBuffer,
565  0,
566  indexData.data(),
567  app.indexDataSize);
568 
569  // === Create the uniform buffer ===
570 
571  WGPUBufferDescriptor uniformBufferDesc = {};
572  uniformBufferDesc.label = "Demo Uniform Buffer";
573  uniformBufferDesc.size = sizeof(ShaderUniformData);
574  uniformBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
575 
576  app.uniformBuffer = wgpuDeviceCreateBuffer(app.device, &uniformBufferDesc);
577  WEBGPU_DEMO_CHECK(app.indexBuffer, "[WebGPU] Failed to create uniform buffer");
578  WEBGPU_DEMO_LOG("[WebGPU] Uniform buffer created");
579 
580  ShaderUniformData uniformData = {};
581  wgpuQueueWriteBuffer(app.queue,
582  app.uniformBuffer,
583  0,
584  &uniformData,
585  sizeof(ShaderUniformData));
586 
587  // === Create the depth texture ===
588 
589  app.depthTextureFormat = WGPUTextureFormat_Depth24Plus;
590 
591  app.depthTextureDesc = {};
592  app.depthTextureDesc.dimension = WGPUTextureDimension_2D;
593  app.depthTextureDesc.format = app.depthTextureFormat;
594  app.depthTextureDesc.mipLevelCount = 1;
595  app.depthTextureDesc.sampleCount = 1;
596  app.depthTextureDesc.size.width = app.surfaceWidth;
597  app.depthTextureDesc.size.height = app.surfaceHeight;
598  app.depthTextureDesc.size.depthOrArrayLayers = 1;
599  app.depthTextureDesc.usage = WGPUTextureUsage_RenderAttachment;
600  app.depthTextureDesc.viewFormatCount = 1;
601  app.depthTextureDesc.viewFormats = &app.depthTextureFormat;
602 
603  app.depthTexture = wgpuDeviceCreateTexture(app.device, &app.depthTextureDesc);
604  WEBGPU_DEMO_CHECK(app.depthTexture, "[WebGPU] Failed to create depth texture");
605  WEBGPU_DEMO_LOG("[WebGPU] Depth texture created");
606 
607  app.depthTextureViewDesc = {};
608  app.depthTextureViewDesc.aspect = WGPUTextureAspect_DepthOnly;
609  app.depthTextureViewDesc.baseArrayLayer = 0;
610  app.depthTextureViewDesc.arrayLayerCount = 1;
611  app.depthTextureViewDesc.baseMipLevel = 0;
612  app.depthTextureViewDesc.mipLevelCount = 1;
613  app.depthTextureViewDesc.dimension = WGPUTextureViewDimension_2D;
614  app.depthTextureViewDesc.format = app.depthTextureFormat;
615 
616  app.depthTextureView = wgpuTextureCreateView(app.depthTexture, &app.depthTextureViewDesc);
617  WEBGPU_DEMO_CHECK(app.depthTextureView, "[WebGPU] Failed to create depth texture view");
618  WEBGPU_DEMO_LOG("[WebGPU] Depth texture view created");
619 
620  // === Create the texture ===
621 
622  cv::Mat image = cv::imread(std::string(SL_PROJECT_ROOT) + "/data/images/textures/brickwall0512_C.jpg");
623  cv::cvtColor(image, image, cv::COLOR_BGR2RGBA);
624 
625  unsigned imageWidth = image.cols;
626  unsigned imageHeight = image.rows;
627  unsigned pixelDataSize = 4 * imageWidth * imageHeight;
628 
629  WGPUTextureDescriptor textureDesc = {};
630  textureDesc.label = "Demo Texture";
631  textureDesc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
632  textureDesc.dimension = WGPUTextureDimension_2D;
633  textureDesc.size.width = imageWidth;
634  textureDesc.size.height = imageHeight;
635  textureDesc.size.depthOrArrayLayers = 1;
636  textureDesc.format = WGPUTextureFormat_RGBA8UnormSrgb;
637  textureDesc.mipLevelCount = 4;
638  textureDesc.sampleCount = 1;
639 
640  app.texture = wgpuDeviceCreateTexture(app.device, &textureDesc);
641  WEBGPU_DEMO_CHECK(app.texture, "[WebGPU] Failed to create texture");
642  WEBGPU_DEMO_LOG("[WebGPU] Texture created");
643 
644  // Where do we copy the pixel data to?
645  WGPUImageCopyTexture destination = {};
646  destination.texture = app.texture;
647  destination.mipLevel = 0;
648  destination.origin.x = 0;
649  destination.origin.y = 0;
650  destination.origin.z = 0;
651  destination.aspect = WGPUTextureAspect_All;
652 
653  // Where do we copy the pixel data from?
654  WGPUTextureDataLayout pixelDataLayout = {};
655  pixelDataLayout.offset = 0;
656  pixelDataLayout.bytesPerRow = 4 * textureDesc.size.width;
657  pixelDataLayout.rowsPerImage = textureDesc.size.height;
658 
659  // Generate mip levels.
660 
661  WGPUExtent3D mipLevelSize;
662  mipLevelSize.width = textureDesc.size.width;
663  mipLevelSize.height = textureDesc.size.height;
664  mipLevelSize.depthOrArrayLayers = 1;
665 
666  for (unsigned mipLevel = 0; mipLevel < textureDesc.mipLevelCount; mipLevel++)
667  {
668  cv::Mat mipLevelImage;
669  cv::Size cvSize(static_cast<int>(mipLevelSize.width),
670  static_cast<int>(mipLevelSize.height));
671  cv::resize(image, mipLevelImage, cvSize);
672 
673  std::size_t mipLevelBytes = 4ull * mipLevelSize.width * mipLevelSize.height;
674 
675  destination.mipLevel = mipLevel;
676  pixelDataLayout.bytesPerRow = 4 * mipLevelSize.width;
677  pixelDataLayout.rowsPerImage = mipLevelSize.height;
678 
679  // Upload the data to the GPU.
680  wgpuQueueWriteTexture(app.queue,
681  &destination,
682  mipLevelImage.data,
683  mipLevelBytes,
684  &pixelDataLayout,
685  &mipLevelSize);
686 
687  // Scale the image down for the next mip level.
688  mipLevelSize.width /= 2;
689  mipLevelSize.height /= 2;
690  }
691 
692  // === Create a texture view into the texture ===
693  WGPUTextureViewDescriptor textureViewDesc = {};
694  textureViewDesc.aspect = WGPUTextureAspect_All;
695  textureViewDesc.baseArrayLayer = 0;
696  textureViewDesc.arrayLayerCount = 1;
697  textureViewDesc.baseMipLevel = 0;
698  textureViewDesc.mipLevelCount = textureDesc.mipLevelCount;
699  textureViewDesc.dimension = WGPUTextureViewDimension_2D;
700  textureViewDesc.format = textureDesc.format;
701 
702  app.textureView = wgpuTextureCreateView(app.texture, &textureViewDesc);
703  WEBGPU_DEMO_CHECK(app.textureView, "[WebGPU] Failed to create texture view");
704  WEBGPU_DEMO_LOG("[WebGPU] Texture view created");
705 
706  // === Create the texture sampler ===
707  // The sampler is used to look up values of the texture in the shader.
708  // We could look up values directly without a sampler, but with a sampler we
709  // get access to features like interpolation and mipmapping.
710 
711  WGPUSamplerDescriptor samplerDesc = {};
712  samplerDesc.addressModeU = WGPUAddressMode_ClampToEdge;
713  samplerDesc.addressModeV = WGPUAddressMode_ClampToEdge;
714  samplerDesc.addressModeW = WGPUAddressMode_ClampToEdge;
715  samplerDesc.magFilter = WGPUFilterMode_Linear;
716  samplerDesc.minFilter = WGPUFilterMode_Linear;
717  samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
718  samplerDesc.lodMinClamp = 0.0f;
719  samplerDesc.lodMaxClamp = static_cast<float>(textureDesc.mipLevelCount);
720  samplerDesc.compare = WGPUCompareFunction_Undefined;
721  samplerDesc.maxAnisotropy = 1.0f;
722 
723  app.sampler = wgpuDeviceCreateSampler(app.device, &samplerDesc);
724  WEBGPU_DEMO_CHECK(app.sampler, "[WebGPU] Failed to create sampler");
725  WEBGPU_DEMO_LOG("[WebGPU] Sampler created");
726 
727  // === Create the shader module ===
728  // Create a shader module for use in our render pipeline.
729  // Shaders are written in a WebGPU-specific language called WGSL (WebGPU shader language).
730  // Shader modules can be used in multiple pipeline stages by specifying different entry points.
731 
732  const char* shaderSource = R"(
733  struct VertexInput {
734  @location(0) position: vec3f,
735  @location(1) normal: vec3f,
736  @location(2) uv: vec2f,
737  };
738 
739  struct Uniforms {
740  projectionMatrix: mat4x4f,
741  viewMatrix: mat4x4f,
742  modelMatrix: mat4x4f,
743  };
744 
745  struct VertexOutput {
746  @builtin(position) position: vec4f,
747  @location(0) worldNormal: vec3f,
748  @location(1) uv: vec2f,
749  }
750 
751  const LIGHT_DIR = vec3f(4.0, -8.0, 1.0);
752 
753  @group(0) @binding(0) var<uniform> uniforms: Uniforms;
754  @group(0) @binding(1) var texture: texture_2d<f32>;
755  @group(0) @binding(2) var textureSampler: sampler;
756 
757  @vertex
758  fn vs_main(in: VertexInput) -> VertexOutput {
759  var localPos = vec4f(in.position, 1.0);
760  var worldPos = uniforms.modelMatrix * localPos;
761 
762  var out: VertexOutput;
763  out.position = uniforms.projectionMatrix * uniforms.viewMatrix * worldPos;
764  out.worldNormal = in.normal;
765  out.uv = in.uv;
766  return out;
767  }
768 
769  @fragment
770  fn fs_main(in: VertexOutput) -> @location(0) vec4f {
771  var baseColor = textureSample(texture, textureSampler, in.uv);
772 
773  var diffuse = dot(-normalize(in.worldNormal), normalize(LIGHT_DIR));
774  diffuse = diffuse * 0.5 + 0.5;
775 
776  return vec4(baseColor.rgb * diffuse, baseColor.a);
777  }
778  )";
779 
780  WGPUShaderModuleWGSLDescriptor shaderModuleWGSLDesc = {};
781  shaderModuleWGSLDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
782  shaderModuleWGSLDesc.code = shaderSource;
783 
784  WGPUShaderModuleDescriptor shaderModuleDesc = {};
785  shaderModuleDesc.label = "Demo Shader";
786  shaderModuleDesc.nextInChain = (const WGPUChainedStruct*)&shaderModuleWGSLDesc;
787 
788  app.shaderModule = wgpuDeviceCreateShaderModule(app.device, &shaderModuleDesc);
789  WEBGPU_DEMO_CHECK(app.shaderModule, "[WebGPU] Failed to create shader module");
790  WEBGPU_DEMO_LOG("[WebGPU] Shader module created");
791 
792  // === Create the bind group layout ===
793  // Bind groups contain binding layouts that describe how uniforms and textures are passed to shaders.
794 
795  // Entry for the uniform
796  WGPUBindGroupLayoutEntry uniformBindLayout = {};
797  uniformBindLayout.binding = 0;
798  uniformBindLayout.visibility = WGPUShaderStage_Vertex;
799  uniformBindLayout.buffer.type = WGPUBufferBindingType_Uniform;
800  uniformBindLayout.buffer.minBindingSize = sizeof(ShaderUniformData);
801 
802  // Entry for the texture
803  WGPUBindGroupLayoutEntry textureBindLayout = {};
804  textureBindLayout.binding = 1;
805  textureBindLayout.visibility = WGPUShaderStage_Fragment;
806  textureBindLayout.texture.sampleType = WGPUTextureSampleType_Float;
807  textureBindLayout.texture.viewDimension = WGPUTextureViewDimension_2D;
808 
809  // Entry for the sampler
810  WGPUBindGroupLayoutEntry samplerBindLayout = {};
811  samplerBindLayout.binding = 2;
812  samplerBindLayout.visibility = WGPUShaderStage_Fragment;
813  samplerBindLayout.sampler.type = WGPUSamplerBindingType_Filtering;
814 
815  std::vector<WGPUBindGroupLayoutEntry> bindGroupLayoutEntries = {uniformBindLayout,
816  textureBindLayout,
817  samplerBindLayout};
818 
819  WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc = {};
820  bindGroupLayoutDesc.label = "Demo Bind Group Layout";
821  bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size();
822  bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data();
823 
824  app.bindGroupLayout = wgpuDeviceCreateBindGroupLayout(app.device,
825  &bindGroupLayoutDesc);
826  WEBGPU_DEMO_CHECK(app.bindGroupLayout, "[WebGPU] Failed to create bind group layout");
827  WEBGPU_DEMO_LOG("[WebGPU] Bind group layout created");
828 
829  // === Create the bind group ===
830  // The bind group actually binds the buffer to uniforms and textures.
831 
832  WGPUBindGroupEntry uniformBinding = {};
833  uniformBinding.binding = 0;
834  uniformBinding.buffer = app.uniformBuffer;
835  uniformBinding.offset = 0;
836  uniformBinding.size = sizeof(ShaderUniformData);
837 
838  WGPUBindGroupEntry textureBinding = {};
839  textureBinding.binding = 1;
840  textureBinding.textureView = app.textureView;
841 
842  WGPUBindGroupEntry samplerBinding = {};
843  samplerBinding.binding = 2;
844  samplerBinding.sampler = app.sampler;
845 
846  std::vector<WGPUBindGroupEntry> bindGroupEntries = {uniformBinding,
847  textureBinding,
848  samplerBinding};
849 
850  WGPUBindGroupDescriptor bindGroupDesc = {};
851  bindGroupDesc.layout = app.bindGroupLayout;
852  bindGroupDesc.entryCount = bindGroupEntries.size();
853  bindGroupDesc.entries = bindGroupEntries.data();
854  app.bindGroup = wgpuDeviceCreateBindGroup(app.device, &bindGroupDesc);
855  WEBGPU_DEMO_CHECK(app.bindGroup, "[WebGPU] Failed to create bind group");
856  WEBGPU_DEMO_LOG("[WebGPU] Bind group created");
857 
858  // === Create the pipeline layout ===
859  // The pipeline layout specifies the bind groups the pipeline uses.
860 
861  WGPUPipelineLayoutDescriptor pipelineLayoutDesc = {};
862  pipelineLayoutDesc.label = "Demo Pipeline Layout";
863  pipelineLayoutDesc.bindGroupLayoutCount = 1;
864  pipelineLayoutDesc.bindGroupLayouts = &app.bindGroupLayout;
865 
866  app.pipelineLayout = wgpuDeviceCreatePipelineLayout(app.device, &pipelineLayoutDesc);
867  WEBGPU_DEMO_CHECK(app.pipelineLayout, "[WebGPU] Failed to create pipeline layout");
868  WEBGPU_DEMO_LOG("[WebGPU] Pipeline layout created");
869 
870  // === Create the render pipeline ===
871  // The render pipeline specifies the configuration for the fixed-function stages as well as
872  // the shaders for the programmable stages of the hardware pipeline.
873 
874  // Description of the vertex attributes for the vertex buffer layout
875  WGPUVertexAttribute positionAttribute = {};
876  positionAttribute.format = WGPUVertexFormat_Float32x3;
877  positionAttribute.offset = 0;
878  positionAttribute.shaderLocation = 0;
879 
880  WGPUVertexAttribute normalAttribute = {};
881  normalAttribute.format = WGPUVertexFormat_Float32x3;
882  normalAttribute.offset = 3 * sizeof(float);
883  normalAttribute.shaderLocation = 1;
884 
885  WGPUVertexAttribute uvAttribute = {};
886  uvAttribute.format = WGPUVertexFormat_Float32x2;
887  uvAttribute.offset = 6 * sizeof(float);
888  uvAttribute.shaderLocation = 2;
889 
890  std::vector<WGPUVertexAttribute> vertexAttributes = {positionAttribute,
891  normalAttribute,
892  uvAttribute};
893 
894  // Description of the vertex buffer layout for the vertex shader stage
895  WGPUVertexBufferLayout vertexBufferLayout = {};
896  vertexBufferLayout.arrayStride = sizeof(VertexData);
897  vertexBufferLayout.stepMode = WGPUVertexStepMode_Vertex;
898  vertexBufferLayout.attributeCount = vertexAttributes.size();
899  vertexBufferLayout.attributes = vertexAttributes.data();
900 
901  // Configuration for the vertex shader stage
902  WGPUVertexState vertexState = {};
903  vertexState.module = app.shaderModule;
904  vertexState.entryPoint = "vs_main";
905  vertexState.bufferCount = 1;
906  vertexState.buffers = &vertexBufferLayout;
907 
908  // Configuration for the primitive assembly and rasterization stages
909  WGPUPrimitiveState primitiveState = {};
910  primitiveState.topology = WGPUPrimitiveTopology_TriangleList;
911  primitiveState.stripIndexFormat = WGPUIndexFormat_Undefined;
912  primitiveState.frontFace = WGPUFrontFace_CCW;
913  primitiveState.cullMode = WGPUCullMode_Back;
914 
915  // Configuration for depth testing and stencil buffer
916  WGPUDepthStencilState depthStencilState = {};
917  depthStencilState.format = app.depthTextureFormat;
918  depthStencilState.depthWriteEnabled = true;
919  depthStencilState.depthCompare = WGPUCompareFunction_Less;
920  depthStencilState.stencilReadMask = 0;
921  depthStencilState.stencilWriteMask = 0;
922  depthStencilState.stencilFront.compare = WGPUCompareFunction_Always;
923  depthStencilState.stencilFront.failOp = WGPUStencilOperation_Keep;
924  depthStencilState.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
925  depthStencilState.stencilFront.passOp = WGPUStencilOperation_Keep;
926  depthStencilState.stencilBack.compare = WGPUCompareFunction_Always;
927  depthStencilState.stencilBack.failOp = WGPUStencilOperation_Keep;
928  depthStencilState.stencilBack.depthFailOp = WGPUStencilOperation_Keep;
929  depthStencilState.stencilBack.passOp = WGPUStencilOperation_Keep;
930 
931  // Configuration for multisampling
932  WGPUMultisampleState multisampleState = {};
933  multisampleState.count = 1;
934  multisampleState.mask = WGPUColorWriteMask_All;
935 
936  // Configuration for the fragment shader stage
937  WGPUColorTargetState colorTargetState = {};
938  colorTargetState.format = surfaceCapabilities.formats[0];
939  colorTargetState.writeMask = WGPUColorWriteMask_All;
940  WGPUFragmentState fragmentState = {};
941  fragmentState.module = app.shaderModule;
942  fragmentState.entryPoint = "fs_main";
943  fragmentState.targetCount = 1;
944  fragmentState.targets = &colorTargetState;
945 
946  // Configuration for the entire pipeline
947  WGPURenderPipelineDescriptor pipelineDesc = {};
948  pipelineDesc.label = "Demo Pipeline";
949  pipelineDesc.layout = app.pipelineLayout;
950  pipelineDesc.vertex = vertexState;
951  pipelineDesc.primitive = primitiveState;
952  pipelineDesc.depthStencil = &depthStencilState;
953  pipelineDesc.multisample = multisampleState;
954  pipelineDesc.fragment = &fragmentState;
955 
956  app.pipeline = wgpuDeviceCreateRenderPipeline(app.device, &pipelineDesc);
957  WEBGPU_DEMO_CHECK(app.pipeline, "[WebGPU] Failed to create render pipeline");
958  WEBGPU_DEMO_LOG("[WebGPU] Render pipeline created");
959 }
960 //-----------------------------------------------------------------------------
962 {
963  // === Release all WebGPU resources ===
964 
965  wgpuRenderPipelineRelease(app.pipeline);
966  wgpuPipelineLayoutRelease(app.pipelineLayout);
967  wgpuBindGroupRelease(app.bindGroup);
968  wgpuBindGroupLayoutRelease(app.bindGroupLayout);
969  wgpuShaderModuleRelease(app.shaderModule);
970  wgpuSamplerRelease(app.sampler);
971  wgpuTextureViewRelease(app.textureView);
972  wgpuTextureDestroy(app.texture);
973  wgpuTextureRelease(app.texture);
974  wgpuTextureViewRelease(app.depthTextureView);
975  wgpuTextureDestroy(app.depthTexture);
976  wgpuTextureRelease(app.depthTexture);
977  wgpuBufferDestroy(app.uniformBuffer);
978  wgpuBufferRelease(app.uniformBuffer);
979  wgpuBufferDestroy(app.indexBuffer);
980  wgpuBufferRelease(app.indexBuffer);
981  wgpuBufferDestroy(app.vertexBuffer);
982  wgpuBufferRelease(app.vertexBuffer);
983  wgpuSurfaceRelease(app.surface);
984  wgpuQueueRelease(app.queue);
985  wgpuDeviceRelease(app.device);
986  wgpuAdapterRelease(app.adapter);
987  wgpuInstanceRelease(app.instance);
988 
989  WEBGPU_DEMO_LOG("[WebGPU] Resources released");
990 }
991 //-----------------------------------------------------------------------------
993 {
994  // === Destroy the window and terminate GLFW ===
995 
996  glfwDestroyWindow(app.window);
997  glfwTerminate();
998  WEBGPU_DEMO_LOG("[GLFW] Window closed and terminated");
999 }
1000 //-----------------------------------------------------------------------------
1001 int main(int argc, const char* argv[])
1002 {
1003  WebGPUDemoApp app;
1004 
1005  initGLFW(app);
1006  initWebGPU(app);
1007 
1008  double lastCursorX = 0.0f;
1009  double lastCursorY = 0.0f;
1010 
1011  // === Render loop ===
1012 
1013  while (!glfwWindowShouldClose(app.window))
1014  {
1015  // === Update cube model matrix ===
1016 
1017  double cursorX;
1018  double cursorY;
1019  glfwGetCursorPos(app.window, &cursorX, &cursorY);
1020 
1021  if (glfwGetMouseButton(app.window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS)
1022  {
1023  app.camRotX -= 0.25f * static_cast<float>(cursorY - lastCursorY);
1024  app.camRotY -= 0.25f * static_cast<float>(cursorX - lastCursorX);
1025  }
1026 
1027  lastCursorX = cursorX;
1028  lastCursorY = cursorY;
1029 
1030  onPaint(app);
1031 
1032  glfwPollEvents();
1033  }
1034 
1035  deinitWebGPU(app);
1036  deinitGLFW(app);
1037 
1038  return 0;
1039 }
1040 //-----------------------------------------------------------------------------
void deinitGLFW(WebGPUDemoApp &app)
void initGLFW(WebGPUDemoApp &app)
#define WEBGPU_DEMO_LOG(msg)
void handleDeviceRequest(WGPURequestDeviceStatus status, WGPUDevice device, char const *message, void *userdata)
void onResize(GLFWwindow *window, int width, int height)
void handleAdapterRequest(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const *message, void *userdata)
void onPaint(WebGPUDemoApp &app)
void deinitWebGPU(WebGPUDemoApp &app)
void reconfigureSurface(WebGPUDemoApp &app)
int main(int argc, const char *argv[])
#define WEBGPU_DEMO_CHECK(condition, errorMsg)
void initWebGPU(WebGPUDemoApp &app)
static GLFWwindow * window
The global glfw window handle.
Definition: AppGLFW.cpp:35
void invert()
Inverts the matrix.
Definition: SLMat4.h:1364
void m(int i, T val)
Definition: SLMat4.h:93
void rotate(T degAng, T axisx, T axisy, T axisz)
Definition: SLMat4.h:656
void perspective(T fov, T aspect, T n, T f)
Defines a perspective projection matrix with a field of view angle.
Definition: SLMat4.h:874
void translate(T tx, T ty, T tz=0)
Definition: SLMat4.h:601
static SLVec3 AXISY
Definition: SLVec3.h:298
static SLVec3 AXISX
Definition: SLVec3.h:297
id createMetalLayer(NSWindow *window)
Definition: metal_layer.mm:4
float projectionMatrix[16]
Application struct WebGPUDemoApp with all global variables.
unsigned vertexDataSize
unsigned indexDataSize
WGPUSurface surface
WGPUBuffer indexBuffer
WGPUBindGroupLayout bindGroupLayout
WGPUBindGroup bindGroup
WGPUTexture depthTexture
WGPUDevice device
WGPUAdapter adapter
WGPUPipelineLayout pipelineLayout
unsigned indexCount
WGPUTextureView textureView
WGPUSurfaceConfiguration surfaceConfig
WGPUTextureFormat depthTextureFormat
WGPURenderPipeline pipeline
WGPUTextureView depthTextureView
WGPUTexture texture
GLFWwindow * window
WGPUSampler sampler
WGPUQueue queue
WGPUBuffer uniformBuffer
WGPUTextureViewDescriptor depthTextureViewDesc
WGPUBuffer vertexBuffer
WGPUTextureDescriptor depthTextureDesc
WGPUInstance instance
WGPUShaderModule shaderModule