summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-08 08:33:55 +0100
committerskal <pascal.massimino@gmail.com>2026-02-08 08:33:55 +0100
commit16c2cdce6ad1d89d3c537f2c2cff743449925125 (patch)
tree44792b3f8909dba5622143179c95787fd4de5149 /src
parentd19aec6690bdf515435f4052275828b061c3f71f (diff)
feat(platform): Centralize platform-specific WebGPU code and improve shader composition
Diffstat (limited to 'src')
-rw-r--r--src/3d/renderer.cc250
-rw-r--r--src/gpu/effect.cc6
-rw-r--r--src/gpu/effects/shader_composer.cc10
-rw-r--r--src/gpu/effects/shader_composer.h5
-rw-r--r--src/gpu/gpu.cc111
-rw-r--r--src/gpu/texture_manager.cc17
-rw-r--r--src/platform/platform.cc87
-rw-r--r--src/platform/platform.h84
-rw-r--r--src/util/mesh_utils.h13
9 files changed, 456 insertions, 127 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index e08a0d0..0bc8812 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -5,6 +5,7 @@
#include "generated/assets.h"
#include "gpu/effects/shader_composer.h"
#include "util/asset_manager.h"
+#include "util/mesh_utils.h"
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -124,7 +125,254 @@ void Renderer3D::create_default_resources() {
WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, nullptr)
.buffer;
}
+void Renderer3D::create_pipeline() {
+ // Shader Modules
+ std::string sdf_shader_source =
+ ShaderComposer::Get().Compose(std::vector<std::string>{}, AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH);
+ WGPUShaderModuleDescriptor sdf_shader_desc =
+ platform_create_shader_module_descriptor(sdf_shader_source.c_str());
+ WGPUShaderModule sdf_shader_module =
+ wgpuDeviceCreateShaderModule(device_, &sdf_shader_desc);
+ std::string sdf_no_bvh_shader_source =
+ ShaderComposer::Get().Compose(std::vector<std::string>{}, AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR);
+ WGPUShaderModuleDescriptor sdf_no_bvh_shader_desc =
+ platform_create_shader_module_descriptor(
+ sdf_no_bvh_shader_source.c_str());
+ WGPUShaderModule sdf_no_bvh_shader_module =
+ wgpuDeviceCreateShaderModule(device_, &sdf_no_bvh_shader_desc);
+
+ // Mesh Shader Module
+ std::string mesh_shader_source =
+ ShaderComposer::Get().Compose(std::vector<std::string>{}, AssetId::ASSET_SHADER_RENDER_SHADOWS); // Use SHADOWS shader
+ WGPUShaderModuleDescriptor mesh_shader_desc =
+ platform_create_shader_module_descriptor(mesh_shader_source.c_str());
+ WGPUShaderModule mesh_shader_module =
+ wgpuDeviceCreateShaderModule(device_, &mesh_shader_desc);
+
+ // Bind Group Layouts and Pipelines (SDF)
+ std::vector<WGPUBindGroupLayoutEntry> bgl_entries;
+ // Uniforms
+ bgl_entries.push_back({.binding = 0,
+ .visibility = WGPUShaderStage_Vertex |
+ WGPUShaderStage_Fragment,
+ .buffer = {.type = WGPUBufferBindingType_Uniform,
+ .hasDynamicOffset = false,
+ .minBindingSize = sizeof(GlobalUniforms)}});
+ // Object Storage
+ bgl_entries.push_back(
+ {.binding = 1,
+ .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment,
+ .buffer = {.type = WGPUBufferBindingType_ReadOnlyStorage,
+ .hasDynamicOffset = false,
+ .minBindingSize = sizeof(ObjectData) * kMaxObjects}});
+
+ // BVH Storage (Conditional for BVH pipeline)
+ bgl_entries.push_back(
+ {.binding = 2,
+ .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment,
+ .buffer = {.type = WGPUBufferBindingType_ReadOnlyStorage,
+ .hasDynamicOffset = false,
+ .minBindingSize = sizeof(BVHNode) * kMaxObjects * 2}});
+
+ // Noise Texture
+ bgl_entries.push_back({.binding = 3,
+ .visibility = WGPUShaderStage_Fragment,
+ .texture = {.sampleType = WGPUTextureSampleType_Float,
+ .viewDimension = WGPUTextureViewDimension_2D,
+ .multisampled = false}});
+ // Default Sampler
+ bgl_entries.push_back({.binding = 4,
+ .visibility = WGPUShaderStage_Fragment,
+ .sampler = {.type = WGPUSamplerBindingType_Filtering}});
+ // Skybox Texture
+ bgl_entries.push_back({.binding = 5,
+ .visibility = WGPUShaderStage_Fragment,
+ .texture = {.sampleType = WGPUTextureSampleType_Float,
+ .viewDimension = WGPUTextureViewDimension_2D,
+ .multisampled = false}});
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = (uint32_t)bgl_entries.size();
+ bgl_desc.entries = bgl_entries.data();
+
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bind_group_layout;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ // Depth Stencil State
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_True;
+ depth_stencil.depthCompare = WGPUCompareFunction_Less;
+
+ // Configure common color target state
+ WGPUColorTargetState color_target = {};
+ color_target.format = format_;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ // Alpha blending for meshes/particles, but not SDFs
+ color_target.blend = nullptr; // Explicitly set to nullptr
+
+ WGPUFragmentState fragment_state = {};
+ fragment_state.module = sdf_shader_module;
+ fragment_state.entryPoint = str_view("fs_main");
+ fragment_state.targetCount = 1;
+ fragment_state.targets = &color_target;
+
+ // Create SDF pipeline (with BVH)
+ WGPURenderPipelineDescriptor pipeline_desc = {};
+ pipeline_desc.layout = pipeline_layout;
+ pipeline_desc.vertex.module = sdf_shader_module;
+ pipeline_desc.vertex.entryPoint = str_view("vs_main");
+ pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ pipeline_desc.multisample.count = 1;
+ pipeline_desc.multisample.mask = 0xFFFFFFFF;
+ pipeline_desc.fragment = &fragment_state;
+ PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, &depth_stencil);
+ pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_desc);
+
+ // Create SDF pipeline (without BVH)
+ fragment_state.module = sdf_no_bvh_shader_module; // Use linear shader
+ WGPURenderPipelineDescriptor pipeline_no_bvh_desc = pipeline_desc;
+ pipeline_no_bvh_desc.vertex.module = sdf_no_bvh_shader_module;
+ pipeline_no_bvh_desc.fragment = &fragment_state;
+ PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_no_bvh_desc, &depth_stencil);
+ pipeline_no_bvh_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_no_bvh_desc);
+
+ // Configure blend state for mesh/particle pipeline
+ WGPUBlendState mesh_blend_state = {};
+ mesh_blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha;
+ mesh_blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
+ mesh_blend_state.color.operation = WGPUBlendOperation_Add;
+ mesh_blend_state.alpha.srcFactor = WGPUBlendFactor_One;
+ mesh_blend_state.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
+ mesh_blend_state.alpha.operation = WGPUBlendOperation_Add;
+ color_target.blend = &mesh_blend_state; // Enable blending for meshes
+
+ // Create Mesh pipeline
+ WGPURenderPipelineDescriptor mesh_pipeline_desc = {};
+ mesh_pipeline_desc.layout = pipeline_layout;
+ mesh_pipeline_desc.vertex.module = mesh_shader_module;
+ mesh_pipeline_desc.vertex.entryPoint = str_view("vs_main");
+ mesh_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ mesh_pipeline_desc.multisample.count = 1;
+ mesh_pipeline_desc.multisample.mask = 0xFFFFFFFF;
+ mesh_pipeline_desc.fragment = &fragment_state; // Reuse fragment state
+ // Override fragment module to mesh_shader_module
+ fragment_state.module = mesh_shader_module;
+ mesh_pipeline_desc.fragment = &fragment_state;
+ PLATFORM_SET_PIPELINE_DEPTH_STENCIL(mesh_pipeline_desc, &depth_stencil);
+
+ // Vertex attributes for mesh
+ WGPUVertexAttribute vert_attrs[3] = {};
+ // Position
+ vert_attrs[0].shaderLocation = 0;
+ vert_attrs[0].offset = 0;
+ vert_attrs[0].format = WGPUVertexFormat_Float32x3;
+ // Normal
+ vert_attrs[1].shaderLocation = 1;
+ vert_attrs[1].offset = sizeof(vec3);
+ vert_attrs[1].format = WGPUVertexFormat_Float32x3;
+ // UV
+ vert_attrs[2].shaderLocation = 2;
+ vert_attrs[2].offset = sizeof(vec3) * 2;
+ vert_attrs[2].format = WGPUVertexFormat_Float32x2;
+
+ WGPUVertexBufferLayout vert_buffer_layout = {};
+ vert_buffer_layout.arrayStride = sizeof(MeshVertex);
+ vert_buffer_layout.stepMode = WGPUVertexStepMode_Vertex;
+ vert_buffer_layout.attributeCount = 3;
+ vert_buffer_layout.attributes = vert_attrs;
+
+ mesh_pipeline_desc.vertex.bufferCount = 1;
+ mesh_pipeline_desc.vertex.buffers = &vert_buffer_layout;
+
+ mesh_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &mesh_pipeline_desc);
+
+ // Release shader modules as they are now part of the pipelines
+ wgpuShaderModuleRelease(sdf_shader_module);
+ wgpuShaderModuleRelease(sdf_no_bvh_shader_module);
+ wgpuShaderModuleRelease(mesh_shader_module);
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+}
+
+void Renderer3D::create_skybox_pipeline() {
+ std::string skybox_shader_source =
+ ShaderComposer::Get().Compose(std::vector<std::string>{}, AssetId::ASSET_SHADER_RENDER_SHADOWS); // Use SHADOWS shader
+ WGPUShaderModuleDescriptor skybox_shader_desc =
+ platform_create_shader_module_descriptor(skybox_shader_source.c_str());
+ WGPUShaderModule skybox_shader_module =
+ wgpuDeviceCreateShaderModule(device_, &skybox_shader_desc);
+
+ WGPUBindGroupLayoutEntry bgl_entries[3] = {};
+ // Skybox Texture
+ bgl_entries[0].binding = 0;
+ bgl_entries[0].visibility = WGPUShaderStage_Fragment;
+ bgl_entries[0].texture = {.sampleType = WGPUTextureSampleType_Float,
+ .viewDimension = WGPUTextureViewDimension_2D,
+ .multisampled = false};
+ // Sampler
+ bgl_entries[1].binding = 1;
+ bgl_entries[1].visibility = WGPUShaderStage_Fragment;
+ bgl_entries[1].sampler = {.type = WGPUSamplerBindingType_Filtering};
+ // Global Uniforms
+ bgl_entries[2].binding = 2;
+ bgl_entries[2].visibility = WGPUShaderStage_Vertex;
+ bgl_entries[2].buffer = {.type = WGPUBufferBindingType_Uniform,
+ .hasDynamicOffset = false,
+ .minBindingSize = sizeof(GlobalUniforms)};
+
+ WGPUBindGroupLayoutDescriptor bgl_desc = {};
+ bgl_desc.entryCount = 3;
+ bgl_desc.entries = bgl_entries;
+ WGPUBindGroupLayout bind_group_layout =
+ wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
+
+ WGPUPipelineLayoutDescriptor pl_desc = {};
+ pl_desc.bindGroupLayoutCount = 1;
+ pl_desc.bindGroupLayouts = &bind_group_layout;
+ WGPUPipelineLayout pipeline_layout =
+ wgpuDeviceCreatePipelineLayout(device_, &pl_desc);
+
+ WGPUColorTargetState color_target = {};
+ color_target.format = format_;
+ color_target.writeMask = WGPUColorWriteMask_All;
+ color_target.blend = nullptr; // No blending for skybox
+
+ WGPUFragmentState fragment_state = {};
+ fragment_state.module = skybox_shader_module;
+ fragment_state.entryPoint = str_view("fs_main");
+ fragment_state.targetCount = 1;
+ fragment_state.targets = &color_target;
+
+ WGPURenderPipelineDescriptor pipeline_desc = {};
+ pipeline_desc.layout = pipeline_layout;
+ pipeline_desc.vertex.module = skybox_shader_module;
+ pipeline_desc.vertex.entryPoint = str_view("vs_main");
+ pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ pipeline_desc.multisample.count = 1;
+ pipeline_desc.multisample.mask = 0xFFFFFFFF;
+ pipeline_desc.fragment = &fragment_state;
+
+ // Skybox depth state (clear and write depth for objects)
+ WGPUDepthStencilState depth_stencil = {};
+ depth_stencil.format = WGPUTextureFormat_Depth24Plus;
+ depth_stencil.depthWriteEnabled = WGPUOptionalBool_False;
+ depth_stencil.depthCompare = WGPUCompareFunction_Always;
+ PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, &depth_stencil);
+
+ skybox_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_desc);
+
+ wgpuShaderModuleRelease(skybox_shader_module);
+ wgpuPipelineLayoutRelease(pipeline_layout);
+ wgpuBindGroupLayoutRelease(bind_group_layout);
+}
void Renderer3D::set_noise_texture(WGPUTextureView noise_view) {
noise_texture_view_ = noise_view;
}
@@ -450,4 +698,4 @@ void Renderer3D::render(const Scene& scene, const Camera& camera, float time,
wgpuCommandBufferRelease(commands);
wgpuCommandEncoderRelease(encoder);
-} \ No newline at end of file
+}
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc
index a1b45f1..9e769eb 100644
--- a/src/gpu/effect.cc
+++ b/src/gpu/effect.cc
@@ -260,9 +260,7 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
scene_attachment.loadOp = WGPULoadOp_Clear;
scene_attachment.storeOp = WGPUStoreOp_Store;
scene_attachment.clearValue = {0, 0, 0, 1};
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
scene_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
WGPURenderPassDepthStencilAttachment depth_attachment = {};
depth_attachment.view = depth_view_;
@@ -302,9 +300,7 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
final_attachment.resolveTarget = nullptr;
final_attachment.loadOp = WGPULoadOp_Load;
final_attachment.storeOp = WGPUStoreOp_Store;
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
final_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
WGPURenderPassDescriptor final_desc = {
.colorAttachmentCount = 1, .colorAttachments = &final_attachment};
WGPURenderPassEncoder final_pass =
@@ -338,9 +334,7 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
pp_attachment.resolveTarget = nullptr;
pp_attachment.loadOp = WGPULoadOp_Load;
pp_attachment.storeOp = WGPUStoreOp_Store;
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
pp_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
WGPURenderPassDescriptor pp_desc = {.colorAttachmentCount = 1,
.colorAttachments = &pp_attachment};
WGPURenderPassEncoder pp_pass =
diff --git a/src/gpu/effects/shader_composer.cc b/src/gpu/effects/shader_composer.cc
index b746f8b..5cd8699 100644
--- a/src/gpu/effects/shader_composer.cc
+++ b/src/gpu/effects/shader_composer.cc
@@ -2,6 +2,7 @@
// It implements the ShaderComposer class.
#include "gpu/effects/shader_composer.h"
+#include "util/asset_manager.h"
#include <set>
#include <sstream>
@@ -86,3 +87,12 @@ ShaderComposer::Compose(const std::vector<std::string>& dependencies,
return ss.str();
}
+
+std::string
+ShaderComposer::Compose(const std::vector<std::string>& dependencies,
+ AssetId main_code_asset_id,
+ const CompositionMap& substitutions) {
+ size_t size;
+ const char* main_code = reinterpret_cast<const char*>(GetAsset(main_code_asset_id, &size));
+ return Compose(dependencies, std::string(main_code, size), substitutions);
+}
diff --git a/src/gpu/effects/shader_composer.h b/src/gpu/effects/shader_composer.h
index 9eb43f4..961ffcb 100644
--- a/src/gpu/effects/shader_composer.h
+++ b/src/gpu/effects/shader_composer.h
@@ -3,6 +3,7 @@
#pragma once
+#include "generated/assets.h" // For AssetId
#include <map>
#include <set>
#include <string>
@@ -23,6 +24,10 @@ class ShaderComposer {
std::string Compose(const std::vector<std::string>& dependencies,
const std::string& main_code,
const CompositionMap& substitutions = {});
+
+ std::string Compose(const std::vector<std::string>& dependencies,
+ AssetId main_code_asset_id,
+ const CompositionMap& substitutions = {});
private:
ShaderComposer() = default;
diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc
index 9776eac..c61c605 100644
--- a/src/gpu/gpu.cc
+++ b/src/gpu/gpu.cc
@@ -57,11 +57,7 @@ RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format,
RenderPass pass = {};
// Create Shader Module
- WGPUShaderSourceWGSL wgsl_src = {};
- wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_src.code = str_view(shader_code);
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = &wgsl_src.chain;
+ WGPUShaderModuleDescriptor shader_desc = platform_create_shader_module_descriptor(shader_code);
WGPUShaderModule shader_module =
wgpuDeviceCreateShaderModule(device, &shader_desc);
@@ -145,7 +141,7 @@ RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format,
depth_stencil.format = WGPUTextureFormat_Depth24Plus;
depth_stencil.depthWriteEnabled = WGPUOptionalBool_False;
depth_stencil.depthCompare = WGPUCompareFunction_Always;
- pipeline_desc.depthStencil = &depth_stencil;
+ PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, &depth_stencil);
pass.pipeline = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
@@ -157,11 +153,7 @@ ComputePass gpu_create_compute_pass(WGPUDevice device, const char* shader_code,
int num_bindings) {
ComputePass pass = {};
- WGPUShaderSourceWGSL wgsl_src = {};
- wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL;
- wgsl_src.code = str_view(shader_code);
- WGPUShaderModuleDescriptor shader_desc = {};
- shader_desc.nextInChain = &wgsl_src.chain;
+ WGPUShaderModuleDescriptor shader_desc = platform_create_shader_module_descriptor(shader_code);
WGPUShaderModule shader_module =
wgpuDeviceCreateShaderModule(device, &shader_desc);
@@ -214,49 +206,11 @@ ComputePass gpu_create_compute_pass(WGPUDevice device, const char* shader_code,
#if !defined(STRIP_ALL)
#if defined(DEMO_CROSS_COMPILE_WIN32)
-static void handle_request_adapter(WGPURequestAdapterStatus status,
- WGPUAdapter adapter, const char* message,
- void* userdata) {
- if (status == WGPURequestAdapterStatus_Success) {
- *((WGPUAdapter*)userdata) = adapter;
- } else {
- printf("Request adapter failed: %s\n", message ? message : "Unknown");
- }
-}
-static void handle_request_device(WGPURequestDeviceStatus status,
- WGPUDevice device, const char* message,
- void* userdata) {
- if (status == WGPURequestDeviceStatus_Success) {
- *((WGPUDevice*)userdata) = device;
- } else {
- printf("Request device failed: %s\n", message ? message : "Unknown");
- }
-}
static void handle_device_error(WGPUErrorType type, const char* message,
void* userdata) {
printf("WebGPU Error: %s\n", message ? message : "Unknown");
}
#else
-static void handle_request_adapter(WGPURequestAdapterStatus status,
- WGPUAdapter adapter, WGPUStringView message,
- void* userdata, void* userdata2) {
- (void)userdata2;
- if (status == WGPURequestAdapterStatus_Success) {
- *((WGPUAdapter*)userdata) = adapter;
- } else {
- printf("Request adapter failed: %.*s\n", (int)message.length, message.data);
- }
-}
-static void handle_request_device(WGPURequestDeviceStatus status,
- WGPUDevice device, WGPUStringView message,
- void* userdata, void* userdata2) {
- (void)userdata2;
- if (status == WGPURequestDeviceStatus_Success) {
- *((WGPUDevice*)userdata) = device;
- } else {
- printf("Request device failed: %.*s\n", (int)message.length, message.data);
- }
-}
static void handle_device_error(const WGPUDevice* device, WGPUErrorType type,
WGPUStringView message, void* userdata,
void* userdata2) {
@@ -266,41 +220,6 @@ static void handle_device_error(const WGPUDevice* device, WGPUErrorType type,
printf("WebGPU Error: %.*s\n", (int)message.length, message.data);
}
#endif /* defined(DEMO_CROSS_COMPILE_WIN32) */
-#else
-// STRIP_ALL versions
-#if defined(DEMO_CROSS_COMPILE_WIN32)
-static void handle_request_adapter(WGPURequestAdapterStatus status,
- WGPUAdapter adapter, const char* message,
- void* userdata) {
- if (status == WGPURequestAdapterStatus_Success) {
- *((WGPUAdapter*)userdata) = adapter;
- }
-}
-static void handle_request_device(WGPURequestDeviceStatus status,
- WGPUDevice device, const char* message,
- void* userdata) {
- if (status == WGPURequestDeviceStatus_Success) {
- *((WGPUDevice*)userdata) = device;
- }
-}
-#else
-static void handle_request_adapter(WGPURequestAdapterStatus status,
- WGPUAdapter adapter, WGPUStringView message,
- void* userdata, void* userdata2) {
- (void)userdata2;
- if (status == WGPURequestAdapterStatus_Success) {
- *((WGPUAdapter*)userdata) = adapter;
- }
-}
-static void handle_request_device(WGPURequestDeviceStatus status,
- WGPUDevice device, WGPUStringView message,
- void* userdata, void* userdata2) {
- (void)userdata2;
- if (status == WGPURequestDeviceStatus_Success) {
- *((WGPUDevice*)userdata) = device;
- }
-}
-#endif /* defined(DEMO_CROSS_COMPILE_WIN32) */
#endif /* !defined(STRIP_ALL) */
void gpu_init(PlatformState* platform_state) {
@@ -311,16 +230,8 @@ void gpu_init(PlatformState* platform_state) {
adapter_opts.compatibleSurface = g_surface;
adapter_opts.powerPreference = WGPUPowerPreference_HighPerformance;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- wgpuInstanceRequestAdapter(g_instance, &adapter_opts, handle_request_adapter,
- &g_adapter);
-#else
- WGPURequestAdapterCallbackInfo adapter_cb = {};
- adapter_cb.mode = WGPUCallbackMode_WaitAnyOnly;
- adapter_cb.callback = handle_request_adapter;
- adapter_cb.userdata1 = &g_adapter;
- wgpuInstanceRequestAdapter(g_instance, &adapter_opts, adapter_cb);
-#endif /* defined(DEMO_CROSS_COMPILE_WIN32) */
+ platform_wgpu_request_adapter(g_instance, &adapter_opts, &g_adapter);
+
while (!g_adapter)
platform_wgpu_wait_any(g_instance);
@@ -331,16 +242,8 @@ void gpu_init(PlatformState* platform_state) {
#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
#endif /* !defined(STRIP_ALL) */
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- wgpuAdapterRequestDevice(g_adapter, &device_desc, handle_request_device,
- &g_device);
-#else
- WGPURequestDeviceCallbackInfo device_cb = {};
- device_cb.mode = WGPUCallbackMode_WaitAnyOnly;
- device_cb.callback = handle_request_device;
- device_cb.userdata1 = &g_device;
- wgpuAdapterRequestDevice(g_adapter, &device_desc, device_cb);
-#endif /* defined(DEMO_CROSS_COMPILE_WIN32) */
+ platform_wgpu_request_device(g_adapter, &device_desc, &g_device);
+
while (!g_device)
platform_wgpu_wait_any(g_instance);
diff --git a/src/gpu/texture_manager.cc b/src/gpu/texture_manager.cc
index 0c30c94..e69425f 100644
--- a/src/gpu/texture_manager.cc
+++ b/src/gpu/texture_manager.cc
@@ -2,19 +2,10 @@
// It implements the TextureManager.
#include "gpu/texture_manager.h"
+#include "platform/platform.h" // Include for WGPU_TEX_COPY_INFO and WGPU_TEX_DATA_LAYOUT
#include <cstdio>
#include <vector>
-#if defined(DEMO_CROSS_COMPILE_WIN32)
-// Old API
-#define WGPU_TEX_COPY_INFO WGPUImageCopyTexture
-#define WGPU_TEX_DATA_LAYOUT WGPUTextureDataLayout
-#else
-// New API
-#define WGPU_TEX_COPY_INFO WGPUTexelCopyTextureInfo
-#define WGPU_TEX_DATA_LAYOUT WGPUTexelCopyBufferLayout
-#endif
-
void TextureManager::init(WGPUDevice device, WGPUQueue queue) {
device_ = device;
queue_ = queue;
@@ -60,11 +51,7 @@ void TextureManager::create_texture(const std::string& name, int width,
tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
tex_desc.mipLevelCount = 1;
tex_desc.sampleCount = 1;
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- tex_desc.label = nullptr;
-#else
- tex_desc.label = {nullptr, 0};
-#endif
+ tex_desc.label = label_view(name.c_str());
WGPUTexture texture = wgpuDeviceCreateTexture(device_, &tex_desc);
diff --git a/src/platform/platform.cc b/src/platform/platform.cc
index 29b3af3..2b8529a 100644
--- a/src/platform/platform.cc
+++ b/src/platform/platform.cc
@@ -5,6 +5,7 @@
#include "platform/platform.h"
#include "glfw3webgpu.h"
#include <GLFW/glfw3.h>
+#include <cstdio>
// --- Callbacks ---
@@ -128,4 +129,88 @@ WGPUSurface platform_create_wgpu_surface(WGPUInstance instance,
double platform_get_time() {
return glfwGetTime();
-} \ No newline at end of file
+}
+
+// --- WebGPU Request Helpers ---
+
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+static void _platform_adapter_cb_win32(WGPURequestAdapterStatus status,
+ WGPUAdapter adapter, const char* message,
+ void* userdata) {
+ if (status == WGPURequestAdapterStatus_Success) {
+ *((WGPUAdapter*)userdata) = adapter;
+ } else {
+ printf("Request adapter failed: %s\n", message ? message : "Unknown");
+ }
+}
+
+void platform_wgpu_request_adapter(WGPUInstance instance,
+ const WGPURequestAdapterOptions* options,
+ WGPUAdapter* out_adapter) {
+ wgpuInstanceRequestAdapter(instance, options, _platform_adapter_cb_win32,
+ out_adapter);
+}
+
+static void _platform_device_cb_win32(WGPURequestDeviceStatus status,
+ WGPUDevice device, const char* message,
+ void* userdata) {
+ if (status == WGPURequestDeviceStatus_Success) {
+ *((WGPUDevice*)userdata) = device;
+ } else {
+ printf("Request device failed: %s\n", message ? message : "Unknown");
+ }
+}
+
+void platform_wgpu_request_device(WGPUAdapter adapter,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPUDevice* out_device) {
+ wgpuAdapterRequestDevice(adapter, descriptor, _platform_device_cb_win32,
+ out_device);
+}
+
+#else
+
+static void _platform_adapter_cb_native(WGPURequestAdapterStatus status,
+ WGPUAdapter adapter,
+ WGPUStringView message, void* userdata,
+ void* userdata2) {
+ (void)userdata2;
+ if (status == WGPURequestAdapterStatus_Success) {
+ *((WGPUAdapter*)userdata) = adapter;
+ } else {
+ printf("Request adapter failed: %.*s\n", (int)message.length, message.data);
+ }
+}
+
+void platform_wgpu_request_adapter(WGPUInstance instance,
+ const WGPURequestAdapterOptions* options,
+ WGPUAdapter* out_adapter) {
+ WGPURequestAdapterCallbackInfo cb = {};
+ cb.mode = WGPUCallbackMode_WaitAnyOnly;
+ cb.callback = _platform_adapter_cb_native;
+ cb.userdata1 = out_adapter;
+ wgpuInstanceRequestAdapter(instance, options, cb);
+}
+
+static void _platform_device_cb_native(WGPURequestDeviceStatus status,
+ WGPUDevice device,
+ WGPUStringView message, void* userdata,
+ void* userdata2) {
+ (void)userdata2;
+ if (status == WGPURequestDeviceStatus_Success) {
+ *((WGPUDevice*)userdata) = device;
+ } else {
+ printf("Request device failed: %.*s\n", (int)message.length, message.data);
+ }
+}
+
+void platform_wgpu_request_device(WGPUAdapter adapter,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPUDevice* out_device) {
+ WGPURequestDeviceCallbackInfo cb = {};
+ cb.mode = WGPUCallbackMode_WaitAnyOnly;
+ cb.callback = _platform_device_cb_native;
+ cb.userdata1 = out_device;
+ wgpuAdapterRequestDevice(adapter, descriptor, cb);
+}
+#endif
diff --git a/src/platform/platform.h b/src/platform/platform.h
index 0a98850..ddea7af 100644
--- a/src/platform/platform.h
+++ b/src/platform/platform.h
@@ -22,6 +22,9 @@
#define WGPUCallbackMode_WaitAnyOnly 0
typedef WGPUShaderModuleWGSLDescriptor WGPUShaderSourceWGSL;
+// Texture compatibility typedefs
+typedef WGPUImageCopyTexture WGPU_TEX_COPY_INFO;
+typedef WGPUTextureDataLayout WGPU_TEX_DATA_LAYOUT;
static inline const char* str_view(const char* str) {
return str;
@@ -39,10 +42,44 @@ platform_wgpu_set_error_callback(WGPUDevice device,
wgpuDeviceSetUncapturedErrorCallback(device, callback, nullptr);
}
+// Helper to create a WGPUShaderModuleDescriptor with platform-specific chain
+static inline WGPUShaderModuleDescriptor platform_create_shader_module_descriptor(
+ const char* shader_code) {
+ WGPUShaderSourceWGSL* wgsl_src = new WGPUShaderSourceWGSL();
+ wgsl_src->chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_src->code = str_view(shader_code);
+
+ WGPUShaderModuleDescriptor shader_desc = {};
+ shader_desc.nextInChain = &wgsl_src->chain;
+ shader_desc.label = label_view(nullptr); // Use label_view for cross-platform compatibility
+
+ return shader_desc;
+}
+
+// Macro to set the depth stencil state for render pipelines
+#define PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, depth_stencil_state_ptr) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wuninitialized\"") \
+ _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") \
+ do { \
+ if (depth_stencil_state_ptr) { \
+ if (defined(DEMO_CROSS_COMPILE_WIN32)) { \
+ (pipeline_desc).depthStencilState = *(depth_stencil_state_ptr); \
+ } else { \
+ (pipeline_desc).depthStencil = (depth_stencil_state_ptr); \
+ } \
+ } \
+ } while (0) \
+ _Pragma("clang diagnostic pop")
+
#else
#include <webgpu.h>
#include <wgpu.h>
+// Texture compatibility typedefs
+typedef WGPUTexelCopyTextureInfo WGPU_TEX_COPY_INFO;
+typedef WGPUTexelCopyBufferLayout WGPU_TEX_DATA_LAYOUT;
+
static inline WGPUStringView str_view(const char* str) {
if (!str)
return {nullptr, 0};
@@ -69,8 +106,55 @@ platform_wgpu_set_error_callback(WGPUDevice device,
// Handled in descriptor for new API, but provided for compatibility if needed
// elsewhere
}
+
+// Helper to create a WGPUShaderModuleDescriptor with platform-specific chain
+static inline WGPUShaderModuleDescriptor platform_create_shader_module_descriptor(
+ const char* shader_code) {
+ WGPUShaderModuleDescriptor shader_desc = {};
+ // Need to dynamically allocate wgsl_src, as it needs to persist for the lifetime of shader_desc.
+ WGPUShaderSourceWGSL* wgsl_src = new WGPUShaderSourceWGSL();
+ wgsl_src->chain.sType = WGPUSType_ShaderSourceWGSL;
+ wgsl_src->code = str_view(shader_code);
+
+ shader_desc.nextInChain = &wgsl_src->chain;
+ shader_desc.label = label_view(nullptr);
+ return shader_desc;
+}
+
+// Macro to set the depth stencil state for render pipelines
+#define PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, depth_stencil_state_ptr) \
+ do { \
+ (pipeline_desc).depthStencil = (depth_stencil_state_ptr); \
+ } while (0)
#endif
+// Helper to extract error message from platform-specific string type
+static inline const char* platform_wgpu_get_error_message(
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ const char* message
+#else
+ WGPUStringView message
+#endif
+) {
+#if defined(DEMO_CROSS_COMPILE_WIN32)
+ return message ? message : "Unknown";
+#else
+ // Warning: Returns raw pointer to view data, typically not null-terminated!
+ // Use '%.*s' printf format with message.length if possible.
+ // This helper is best effort.
+ return message.data;
+#endif
+}
+
+// Platform-agnostic WebGPU request helpers
+void platform_wgpu_request_adapter(WGPUInstance instance,
+ const WGPURequestAdapterOptions* options,
+ WGPUAdapter* out_adapter);
+
+void platform_wgpu_request_device(WGPUAdapter adapter,
+ const WGPUDeviceDescriptor* descriptor,
+ WGPUDevice* out_device);
+
// Forward declare GLFWwindow to avoid including the full header here.
struct GLFWwindow;
diff --git a/src/util/mesh_utils.h b/src/util/mesh_utils.h
new file mode 100644
index 0000000..df203ed
--- /dev/null
+++ b/src/util/mesh_utils.h
@@ -0,0 +1,13 @@
+// This file is part of the 64k demo project.
+// It defines utility structures and functions for mesh processing.
+
+#pragma once
+
+#include "util/mini_math.h"
+
+// Vertex structure for meshes
+struct MeshVertex {
+ vec3 position;
+ vec3 normal;
+ vec2 uv;
+};