From 5b428d40a9fc69f1073a7ee47d63c6dedbe14053 Mon Sep 17 00:00:00 2001 From: Dandielo Date: Wed, 12 Nov 2025 22:11:18 +0900 Subject: [PATCH 1/2] Update Emscripten to (4.18) Overview: With the recent releases, Emscripten dropped their own WebGPU implementation in favor of Dawns (Googles) implementation. With this, the render module did require quite some changes, but mostly just renaming types. Outside of WebGPU nothing else required additional attention. --- .../private/webgpu_commands.cxx | 17 ++-- .../webgpu_renderer/private/webgpu_device.cxx | 97 +++++++++---------- .../webgpu_renderer/private/webgpu_driver.cxx | 81 +++++++++++----- .../webgpu_renderer/private/webgpu_queue.cxx | 8 +- .../private/webgpu_resources.hxx | 8 +- .../webgpu_renderer/private/webgpu_shader.hxx | 2 +- .../private/webgpu_surface.hxx | 3 + .../private/webgpu_swapchain.cxx | 41 ++++++-- .../private/webgpu_swapchain.hxx | 5 +- .../webgpu_renderer/private/webgpu_utils.hxx | 5 + .../webgpu_renderer/webgpu_renderer.bff | 7 -- source/configs.bff | 25 ++++- source/fbuild.bff | 4 +- tools/settings.json | 2 +- 14 files changed, 194 insertions(+), 111 deletions(-) diff --git a/source/code/modules/webgpu_renderer/private/webgpu_commands.cxx b/source/code/modules/webgpu_renderer/private/webgpu_commands.cxx index d3739370..741a3e2d 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_commands.cxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_commands.cxx @@ -80,8 +80,8 @@ namespace ice::render::webgpu // attachment_count += 1; // } - WGPURenderPassDescriptor descriptor{}; - descriptor.label = "Default Renderpass"; + WGPURenderPassDescriptor descriptor = WGPU_RENDER_PASS_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Default Renderpass"); descriptor.colorAttachmentCount = attachment_count; descriptor.colorAttachments = attachments; descriptor.depthStencilAttachment = nullptr; @@ -140,8 +140,8 @@ namespace ice::render::webgpu ICE_ASSERT_CORE(subpass.depth_stencil_attachment.layout != ImageLayout::DepthStencil); - WGPURenderPassDescriptor descriptor{}; - descriptor.label = "Default Renderpass"; + WGPURenderPassDescriptor descriptor = WGPU_RENDER_PASS_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Default Renderpass"); descriptor.colorAttachmentCount = attachment_count; descriptor.colorAttachments = attachments; descriptor.depthStencilAttachment = nullptr; @@ -296,8 +296,8 @@ namespace ice::render::webgpu //wgpuCommandEncoderInsertDebugMarker(webgpu_cmds->command_encoder, "End"); - WGPUCommandBufferDescriptor descriptor{}; - descriptor.label = "Command Buffer"; + WGPUCommandBufferDescriptor descriptor = WGPU_COMMAND_BUFFER_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Command Buffer"); webgpu_cmds->command_buffer = wgpuCommandEncoderFinish(webgpu_cmds->command_encoder, &descriptor); } @@ -321,14 +321,13 @@ namespace ice::render::webgpu bytes_per_row ); - WGPUImageCopyBuffer source{}; + WGPUTexelCopyBufferInfo source = WGPU_TEXEL_COPY_BUFFER_INFO_INIT; source.buffer = WebGPUBuffer::native(image_contents)->wgpu_buffer; - source.layout.offset = 0; source.layout.bytesPerRow = bytes_per_row; source.layout.rowsPerImage = extents.y; - WGPUImageCopyTexture destination{}; + WGPUTexelCopyTextureInfo destination = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT; destination.aspect = WGPUTextureAspect_Undefined; destination.mipLevel = 0; destination.origin = { 0, 0, 0 }; diff --git a/source/code/modules/webgpu_renderer/private/webgpu_device.cxx b/source/code/modules/webgpu_renderer/private/webgpu_device.cxx index 531114e7..2e6e64cf 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_device.cxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_device.cxx @@ -41,21 +41,22 @@ namespace ice::render::webgpu ice::render::RenderSurface* surface ) noexcept -> ice::render::RenderSwapchain* { - WebGPURenderSurface* webgpu_surface = static_cast(surface); - ice::platform::RenderSurface* render_surface = reinterpret_cast(webgpu_surface->_surface_info.webgpu.internal); - ice::vec2u surface_dimensions = render_surface->get_dimensions(); - - WGPUSwapChainDescriptor descriptor{}; - descriptor.label = "Default Swapchain"; - descriptor.presentMode = WGPUPresentMode_Fifo; - descriptor.usage = WGPUTextureUsage_RenderAttachment; - descriptor.width = surface_dimensions.x; - descriptor.height = surface_dimensions.y; - descriptor.format = webgpu_surface->_wgpu_surface_format; - WGPUSwapChain swapchain = wgpuDeviceCreateSwapChain(_wgpu_device, webgpu_surface->_wgpu_surface, &descriptor); + WebGPURenderSurface* const webgpu_surface = static_cast(surface); + ice::platform::RenderSurface* const render_surface = reinterpret_cast(webgpu_surface->_surface_info.webgpu.internal); + ice::vec2u const surface_dimensions = render_surface->get_dimensions(); + + WGPUSurfaceConfiguration config = WGPU_SURFACE_CONFIGURATION_INIT; + config.device = _wgpu_device; + config.format = webgpu_surface->_wgpu_surface_format; + config.width = surface_dimensions.x; + config.height = surface_dimensions.y; + config.usage = WGPUTextureUsage_RenderAttachment; + config.presentMode = webgpu_surface->_wgpu_present_mode; + config.alphaMode = WGPUCompositeAlphaMode_Auto; + wgpuSurfaceConfigure(webgpu_surface->_wgpu_surface, &config); ICE_ASSERT(surface != nullptr, "Invalid render surface object!"); - return _allocator.create(swapchain, webgpu_surface->_wgpu_surface_format, surface_dimensions); + return _allocator.create(webgpu_surface->_wgpu_surface, webgpu_surface->_wgpu_surface_format, surface_dimensions); } void WebGPUDevice::destroy_swapchain( @@ -85,8 +86,8 @@ namespace ice::render::webgpu { WGPUBindGroupLayoutEntry entries[16]{}; - WGPUBindGroupLayoutDescriptor descriptor{}; - descriptor.label = "Resource Set Layout"; + WGPUBindGroupLayoutDescriptor descriptor = WGPU_BIND_GROUP_LAYOUT_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Resource Set Layout"); descriptor.entryCount = ice::count(bindings); descriptor.entries = entries; ICE_ASSERT_CORE(descriptor.entryCount <= 16); @@ -166,8 +167,8 @@ namespace ice::render::webgpu if (current_set != set_info.resource_set) { WebGPUResourceSet* native = WebGPUResourceSet::native(current_set); - WGPUBindGroupDescriptor descriptor{}; - descriptor.label = "Resource Set"; + WGPUBindGroupDescriptor descriptor = WGPU_BIND_GROUP_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Resource Set"); descriptor.layout = native->_wgpu_group_layout; descriptor.entries = entries; descriptor.entryCount = idx; @@ -186,7 +187,7 @@ namespace ice::render::webgpu for (ResourceUpdateInfo const& info : set_info.resources) { WGPUBindGroupEntry& entry = entries[idx]; - entry = WGPUBindGroupEntry{}; + entry = WGPU_BIND_GROUP_ENTRY_INIT; entry.binding = set_info.binding_index; switch(set_info.resource_type) @@ -216,8 +217,8 @@ namespace ice::render::webgpu if (idx > 0) { WebGPUResourceSet* native = WebGPUResourceSet::native(current_set); - WGPUBindGroupDescriptor descriptor{}; - descriptor.label = "Resource Set"; + WGPUBindGroupDescriptor descriptor = WGPU_BIND_GROUP_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Resource Set"); descriptor.layout = native->_wgpu_group_layout; descriptor.entries = entries; descriptor.entryCount = idx; @@ -262,8 +263,8 @@ namespace ice::render::webgpu count += 1; } - WGPUPipelineLayoutDescriptor descriptor{}; - descriptor.label = "Pipeline Layout"; + WGPUPipelineLayoutDescriptor descriptor = WGPU_PIPELINE_LAYOUT_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Pipeline Layout"); descriptor.bindGroupLayoutCount = count; descriptor.bindGroupLayouts = layouts; @@ -285,13 +286,11 @@ namespace ice::render::webgpu char const* code = reinterpret_cast(shader_info.shader_data.location); ICE_ASSERT_CORE(code[shader_info.shader_data.size.value - 1] == '\0'); - WGPUShaderModuleWGSLDescriptor wgsl_descriptor{}; - wgsl_descriptor.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - wgsl_descriptor.chain.next = nullptr; - wgsl_descriptor.code = code; + WGPUShaderSourceWGSL wgsl_descriptor = WGPU_SHADER_SOURCE_WGSL_INIT; + wgsl_descriptor.code = wgpu_string(code); - WGPUShaderModuleDescriptor descriptor{}; - descriptor.label = "Shader"; + WGPUShaderModuleDescriptor descriptor = WGPU_SHADER_MODULE_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Shader"); descriptor.nextInChain = &wgsl_descriptor.chain; WGPUShaderModule shader = wgpuDeviceCreateShaderModule(_wgpu_device, &descriptor); @@ -339,12 +338,12 @@ namespace ice::render::webgpu binding_count += 1; } - WGPUVertexState vertex{}; - vertex.entryPoint = "main"; + WGPUVertexState vertex = WGPU_VERTEX_STATE_INIT; + vertex.entryPoint = wgpu_string("main"); vertex.bufferCount = binding_count; vertex.buffers = bindings; - WGPUBlendState blend{}; + WGPUBlendState blend = WGPU_BLEND_STATE_INIT; blend.color.srcFactor = WGPUBlendFactor_SrcAlpha; blend.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; blend.color.operation = WGPUBlendOperation_Add; @@ -353,8 +352,8 @@ namespace ice::render::webgpu blend.alpha.operation = WGPUBlendOperation_Add; WGPUColorTargetState targets[4]{}; - WGPUFragmentState fragment{}; - fragment.entryPoint = "main"; + WGPUFragmentState fragment = WGPU_FRAGMENT_STATE_INIT; + fragment.entryPoint = wgpu_string("main"); fragment.targetCount = 0; fragment.targets = targets; @@ -375,11 +374,11 @@ namespace ice::render::webgpu { case ShaderStageFlags::VertexStage: vertex.module = WebGPUShader::native(program.shader)->_wgpu_shader; - vertex.entryPoint = ice::string::begin(program.entry_point); + vertex.entryPoint = wgpu_string(ice::string::begin(program.entry_point)); break; case ShaderStageFlags::FragmentStage: fragment.module = WebGPUShader::native(program.shader)->_wgpu_shader; - fragment.entryPoint = ice::string::begin(program.entry_point); + fragment.entryPoint = wgpu_string(ice::string::begin(program.entry_point)); break; default: ICE_ASSERT_CORE(false); @@ -406,8 +405,8 @@ namespace ice::render::webgpu // depthstencil.back.writeMask = 0; // depthstencil.front = depthstencil.back; - WGPURenderPipelineDescriptor descriptor{}; - descriptor.label = "Render Pipeline"; + WGPURenderPipelineDescriptor descriptor = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Render Pipeline"); descriptor.layout = WebGPUPipeline::native(info.layout); descriptor.multisample.alphaToCoverageEnabled = false; descriptor.multisample.count = 1; @@ -435,7 +434,7 @@ namespace ice::render::webgpu ice::u32 buffer_size ) noexcept -> ice::render::Buffer { - WGPUBufferUsageFlags flags = WGPUBufferUsage_CopyDst; + WGPUBufferUsage flags = WGPUBufferUsage_CopyDst; switch(buffer_type) { case BufferType::Index: @@ -452,8 +451,8 @@ namespace ice::render::webgpu break; } - WGPUBufferDescriptor descriptor{}; - descriptor.label = "Buffer"; + WGPUBufferDescriptor descriptor = WGPU_BUFFER_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Buffer"); descriptor.mappedAtCreation = false; descriptor.size = buffer_size; descriptor.usage = flags; @@ -520,8 +519,8 @@ namespace ice::render::webgpu { ICE_ASSERT_CORE(image_info.type == ImageType::Image2D); - WGPUTextureDescriptor descriptor{}; - descriptor.label = "Texture"; + WGPUTextureDescriptor descriptor = WGPU_TEXTURE_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("Texture"); descriptor.dimension = WGPUTextureDimension_2D; descriptor.usage = native_usage(image_info.usage); descriptor.format = native_format(image_info.format); @@ -535,13 +534,13 @@ namespace ice::render::webgpu if (data.location != nullptr && data.size > 0_B) { - WGPUImageCopyTexture copy_info{}; + WGPUTexelCopyTextureInfo copy_info{}; copy_info.mipLevel = 0; copy_info.texture = texture; copy_info.origin = { 0, 0, 0 }; copy_info.aspect = WGPUTextureAspect_All; - WGPUTextureDataLayout layout{}; + WGPUTexelCopyBufferLayout layout{}; layout.offset = 0; layout.bytesPerRow = image_info.width * 4; layout.rowsPerImage = image_info.height; @@ -549,8 +548,8 @@ namespace ice::render::webgpu wgpuQueueWriteTexture(_wgpu_queue, ©_info, data.location, data.size.value, &layout, &descriptor.size); } - WGPUTextureViewDescriptor view_descriptor{}; - view_descriptor.label = ""; + WGPUTextureViewDescriptor view_descriptor = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT; + view_descriptor.label = wgpu_string(""); view_descriptor.dimension = WGPUTextureViewDimension_2D; view_descriptor.format = descriptor.format; view_descriptor.mipLevelCount = descriptor.mipLevelCount; @@ -578,11 +577,11 @@ namespace ice::render::webgpu ice::render::SamplerInfo const& sampler_info ) noexcept -> ice::render::Sampler { - WGPUSamplerDescriptor descriptor{}; + WGPUSamplerDescriptor descriptor = WGPU_SAMPLER_DESCRIPTOR_INIT; descriptor.addressModeU = native_address_mode(sampler_info.address_mode.u); descriptor.addressModeV = native_address_mode(sampler_info.address_mode.v); descriptor.addressModeW = native_address_mode(sampler_info.address_mode.w); - descriptor.label = "Sampler"; + descriptor.label = wgpu_string("Sampler"); descriptor.magFilter = native_filter(sampler_info.mag_filter); descriptor.minFilter = native_filter(sampler_info.min_filter); descriptor.mipmapFilter = native_mipmap_mode(sampler_info.mip_map_mode); @@ -610,7 +609,7 @@ namespace ice::render::webgpu ) const noexcept -> ice::render::RenderQueue* { WGPUQueue queue = wgpuDeviceGetQueue(_wgpu_device); - wgpuQueueReference(queue); + wgpuQueueAddRef(queue); return _allocator.create(_allocator, _wgpu_device, queue); } diff --git a/source/code/modules/webgpu_renderer/private/webgpu_driver.cxx b/source/code/modules/webgpu_renderer/private/webgpu_driver.cxx index 1222155b..b3fcd6f2 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_driver.cxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_driver.cxx @@ -9,8 +9,15 @@ namespace ice::render::webgpu { - void wgpu_device_callback(WGPURequestDeviceStatus status, WGPUDevice device, char const* message, void* userdata) + void wgpu_device_callback(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata, void*) { + ICE_LOG_IF( + status != WGPURequestDeviceStatus::WGPURequestDeviceStatus_Success, + LogSeverity::Error, LogTag_WebGPU, + "Failed creation of WebGPU device with error: {}", + ice::String{ message.data, message.length } + ); + WebGPUDriver* driver = reinterpret_cast(userdata); if (status == WGPURequestDeviceStatus_Success) { @@ -18,8 +25,15 @@ namespace ice::render::webgpu } } - void wgpu_adapter_callback(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message, void* userdata) + void wgpu_adapter_callback(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata, void*) { + ICE_LOG_IF( + status != WGPURequestAdapterStatus_Success, + LogSeverity::Error, LogTag_WebGPU, + "Failed to create WebGPU Adapter with error: {}", + ice::String{ message.data, message.length } + ); + WebGPUDriver* driver = reinterpret_cast(userdata); if (status == WGPURequestAdapterStatus_Success) { @@ -30,15 +44,15 @@ namespace ice::render::webgpu // limits.limits.minUniformBufferOffsetAlignment = 256; // limits.limits.minStorageBufferOffsetAlignment = 256; - WGPUDeviceDescriptor descriptor{}; - descriptor.label = "IceShard-WebGPU"; - descriptor.defaultQueue.label = "Default Queue"; - descriptor.deviceLostCallback = nullptr; - descriptor.deviceLostUserdata = nullptr; - descriptor.requiredFeatureCount = 0; - descriptor.requiredFeatures = nullptr; - descriptor.requiredLimits = nullptr; //&limits; - wgpuAdapterRequestDevice(adapter, &descriptor, wgpu_device_callback, driver); + WGPUDeviceDescriptor descriptor = WGPU_DEVICE_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("IceShard-WebGPU"); + descriptor.defaultQueue.label = wgpu_string("Default Queue"); + + WGPURequestDeviceCallbackInfo callback_info = WGPU_REQUEST_DEVICE_CALLBACK_INFO_INIT; + callback_info.callback = wgpu_device_callback; + callback_info.userdata1 = driver; + callback_info.mode = WGPUCallbackMode_AllowSpontaneous; + wgpuAdapterRequestDevice(adapter, &descriptor, callback_info); } } @@ -52,7 +66,15 @@ namespace ice::render::webgpu , _wgpu_adapter{ nullptr } , _wgpu_device{ nullptr } { + WGPURequestAdapterOptions adapter_options = WGPU_REQUEST_ADAPTER_OPTIONS_INIT; + adapter_options.backendType = WGPUBackendType::WGPUBackendType_WebGPU; + adapter_options.powerPreference = WGPUPowerPreference_HighPerformance; + WGPURequestAdapterCallbackInfo adapter_callback = WGPU_REQUEST_ADAPTER_CALLBACK_INFO_INIT; + adapter_callback.callback = wgpu_adapter_callback; + adapter_callback.userdata1 = this; + adapter_callback.mode = WGPUCallbackMode_AllowSpontaneous; + wgpuInstanceRequestAdapter(_wgpu_instance, &adapter_options, adapter_callback); } WebGPUDriver::~WebGPUDriver() noexcept @@ -90,24 +112,33 @@ namespace ice::render::webgpu ice::render::SurfaceInfo const& surface_info ) noexcept -> ice::render::RenderSurface* { - WGPUSurfaceDescriptorFromCanvasHTMLSelector canvas{ - .chain = { .sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector }, - .selector = surface_info.webgpu.selector - }; - WGPUSurfaceDescriptor descriptor{}; - descriptor.label = "Default Surface"; + WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvas = WGPU_EMSCRIPTEN_SURFACE_SOURCE_CANVAS_HTML_SELECTOR_INIT; + canvas.selector = wgpu_string(surface_info.webgpu.selector); + + WGPUSurfaceDescriptor descriptor = WGPU_SURFACE_DESCRIPTOR_INIT; + descriptor.label = wgpu_string("HTML Canvas Surface"); descriptor.nextInChain = &canvas.chain; - WGPUSurface surface = wgpuInstanceCreateSurface(_wgpu_instance, &descriptor); - WGPUTextureFormat surface_format = wgpuSurfaceGetPreferredFormat(surface, _wgpu_adapter); + WGPUSurface const surface = wgpuInstanceCreateSurface(_wgpu_instance, &descriptor); - WGPURequestAdapterOptions adapter_options{}; - adapter_options.backendType = WGPUBackendType::WGPUBackendType_WebGPU; - adapter_options.powerPreference = WGPUPowerPreference_HighPerformance; - adapter_options.compatibleSurface = surface; - wgpuInstanceRequestAdapter(_wgpu_instance, &adapter_options, wgpu_adapter_callback, this); + WGPUSurfaceCapabilities capabilities = WGPU_SURFACE_CAPABILITIES_INIT; + wgpuSurfaceGetCapabilities(surface, _wgpu_adapter, &capabilities); + ICE_ASSERT(capabilities.formatCount > 0, "Failed to fetch surface capabilities!"); + + bool supportsMailbox = false; + for (size_t i = 0; i < capabilities.presentModeCount; i++) + { + if (capabilities.presentModes[i] == WGPUPresentMode_Mailbox) + { + supportsMailbox = true; + } + } + + WGPUPresentMode const present_mode = supportsMailbox ? WGPUPresentMode_Mailbox : WGPUPresentMode_Fifo; + WGPUTextureFormat const surface_format = capabilities.formats[0]; + wgpuSurfaceCapabilitiesFreeMembers(capabilities); ICE_ASSERT(surface_info.type == SurfaceType::HTML5_DOMCanvas, "Only 'HTML5 Canvas' surfaces are allowed!"); - return _allocator.create(surface, surface_format, surface_info); + return _allocator.create(surface, surface_format, present_mode, surface_info); } void WebGPUDriver::destroy_surface( diff --git a/source/code/modules/webgpu_renderer/private/webgpu_queue.cxx b/source/code/modules/webgpu_renderer/private/webgpu_queue.cxx index 7ab9a2e6..8e33538d 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_queue.cxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_queue.cxx @@ -49,7 +49,7 @@ namespace ice::render::webgpu webgpu_cmds->type = type; WGPUCommandEncoderDescriptor descriptor{}; - descriptor.label = type == CommandBufferType::Primary ? "Primary Command Encoded" : "Secondary Command Encoder"; + descriptor.label = wgpu_string(type == CommandBufferType::Primary ? "Primary Command Encoded" : "Secondary Command Encoder"); webgpu_cmds->command_encoder = wgpuDeviceCreateCommandEncoder(_wgpu_device, &descriptor); out_buffer = WebGPUCommandBuffer::handle(webgpu_cmds); @@ -73,7 +73,7 @@ namespace ice::render::webgpu wgpuCommandEncoderRelease(webgpu_cmds->command_encoder); WGPUCommandEncoderDescriptor descriptor{}; - descriptor.label = type == CommandBufferType::Primary ? "Primary Command Encoded" : "Secondary Command Encoder"; + descriptor.label = wgpu_string(type == CommandBufferType::Primary ? "Primary Command Encoded" : "Secondary Command Encoder"); webgpu_cmds->command_encoder = wgpuDeviceCreateCommandEncoder(_wgpu_device, &descriptor); auto it = ice::multi_hashmap::find_first(_wgpu_command_buffers, pool_index); @@ -120,11 +120,11 @@ namespace ice::render::webgpu WGPUCommandEncoderDescriptor descriptor{}; if (it.value()->type == CommandBufferType::Secondary) { - descriptor.label = "Primary Command Encoded"; + descriptor.label = wgpu_string("Primary Command Encoded"); } else { - descriptor.label = "Secondary Command Encoded"; + descriptor.label = wgpu_string("Secondary Command Encoded"); } it.value()->command_encoder = wgpuDeviceCreateCommandEncoder(_wgpu_device, &descriptor); diff --git a/source/code/modules/webgpu_renderer/private/webgpu_resources.hxx b/source/code/modules/webgpu_renderer/private/webgpu_resources.hxx index cf8dd1ec..e646a52b 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_resources.hxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_resources.hxx @@ -36,19 +36,19 @@ namespace ice::render::webgpu } }; - inline auto native_shader_stages(ice::render::ShaderStageFlags flags) noexcept -> WGPUShaderStageFlags + inline auto native_shader_stages(ice::render::ShaderStageFlags flags) noexcept -> WGPUShaderStage { bool const unsupported_flags = ice::has_any(flags, ~(ShaderStageFlags::FragmentStage | ShaderStageFlags::VertexStage)); ICE_ASSERT(unsupported_flags == false, "WebGPU rendered only supports vertex, fragment and compute stages."); - WGPUShaderStageFlags result = WGPUShaderStage_None; + WGPUShaderStage result = WGPUShaderStage_None; if (ice::has_all(flags, ShaderStageFlags::VertexStage)) { - result = WGPUShaderStageFlags(result | WGPUShaderStage_Vertex); + result = WGPUShaderStage(result | WGPUShaderStage_Vertex); } if (ice::has_all(flags, ShaderStageFlags::FragmentStage)) { - result = WGPUShaderStageFlags(result | WGPUShaderStage_Fragment); + result = WGPUShaderStage(result | WGPUShaderStage_Fragment); } return result; } diff --git a/source/code/modules/webgpu_renderer/private/webgpu_shader.hxx b/source/code/modules/webgpu_renderer/private/webgpu_shader.hxx index e3390a40..50fbbb6c 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_shader.hxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_shader.hxx @@ -48,7 +48,7 @@ namespace ice::render::webgpu case ShaderAttribType::Vec3f: return WGPUVertexFormat_Float32x3; case ShaderAttribType::Vec4f: return WGPUVertexFormat_Float32x4; case ShaderAttribType::Vec4f_Unorm8: return WGPUVertexFormat_Unorm8x4; - default: return WGPUVertexFormat_Undefined; + default: return WGPUVertexFormat_Force32; } } diff --git a/source/code/modules/webgpu_renderer/private/webgpu_surface.hxx b/source/code/modules/webgpu_renderer/private/webgpu_surface.hxx index 5fa26bf8..2f7c28aa 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_surface.hxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_surface.hxx @@ -15,10 +15,12 @@ namespace ice::render::webgpu WebGPURenderSurface( WGPUSurface wgpu_surface, WGPUTextureFormat wgpu_surface_format, + WGPUPresentMode wgpu_present_mode, ice::render::SurfaceInfo surface_info ) noexcept : _wgpu_surface{ wgpu_surface } , _wgpu_surface_format{ wgpu_surface_format } + , _wgpu_present_mode{ wgpu_present_mode } , _surface_info{ surface_info } { } @@ -30,6 +32,7 @@ namespace ice::render::webgpu WGPUSurface const _wgpu_surface; WGPUTextureFormat _wgpu_surface_format; + WGPUPresentMode _wgpu_present_mode; ice::render::SurfaceInfo const _surface_info; }; diff --git a/source/code/modules/webgpu_renderer/private/webgpu_swapchain.cxx b/source/code/modules/webgpu_renderer/private/webgpu_swapchain.cxx index 1936e48d..3da1a9d7 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_swapchain.cxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_swapchain.cxx @@ -8,24 +8,36 @@ namespace ice::render::webgpu { WebGPUSwapchain::WebGPUSwapchain( - WGPUSwapChain wgpu_swapchain, + WGPUSurface wgpu_surface, WGPUTextureFormat wgpu_format, ice::vec2u extent ) noexcept - : _wgpu_swapchain{ wgpu_swapchain } + : _wgpu_surface{ wgpu_surface } , _wgpu_format{ wgpu_format } , _extent{ extent } , _images{ { }, { } } , _current_index{ 1 } { + wgpuSurfaceGetCurrentTexture(_wgpu_surface, &_texture); + ICE_ASSERT_CORE(_texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal + || _texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal); // We aquire the first image so we don't need to handle a special case in 'aquire_image' - _images[_current_index].wgpu_texture_view = wgpuSwapChainGetCurrentTextureView(_wgpu_swapchain); + _images[_current_index].wgpu_texture = _texture.texture; + + WGPUTextureViewDescriptor view_descriptor = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT; + view_descriptor.arrayLayerCount = 1; + view_descriptor.baseArrayLayer = 0; + view_descriptor.mipLevelCount = 1; + view_descriptor.baseMipLevel = 0; + view_descriptor.format = _wgpu_format; + view_descriptor.aspect = WGPUTextureAspect_All; + view_descriptor.usage = WGPUTextureUsage_RenderAttachment; + _images[_current_index].wgpu_texture_view = wgpuTextureCreateView(_texture.texture, &view_descriptor); } WebGPUSwapchain::~WebGPUSwapchain() noexcept { - wgpuTextureRelease(_images[_current_index].wgpu_texture); - wgpuSwapChainRelease(_wgpu_swapchain); + wgpuSurfaceUnconfigure(_wgpu_surface); } auto WebGPUSwapchain::extent() const noexcept -> ice::vec2u @@ -51,10 +63,27 @@ namespace ice::render::webgpu auto WebGPUSwapchain::aquire_image() noexcept -> ice::u32 { // We release the previous textures since it's no longer needed + wgpuTextureRelease(_images[_current_index].wgpu_texture); wgpuTextureViewRelease(_images[_current_index].wgpu_texture_view); + _current_index = (_current_index + 1) % image_count(); // And allocate the new texture for the current frame - _images[_current_index].wgpu_texture_view = wgpuSwapChainGetCurrentTextureView(_wgpu_swapchain); + wgpuSurfaceGetCurrentTexture(_wgpu_surface, &_texture); + ICE_ASSERT_CORE(_texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal + || _texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal); + // We aquire the first image so we don't need to handle a special case in 'aquire_image' + _images[_current_index].wgpu_texture = _texture.texture; + + WGPUTextureViewDescriptor view_descriptor = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT; + view_descriptor.arrayLayerCount = 1; + view_descriptor.baseArrayLayer = 0; + view_descriptor.mipLevelCount = 1; + view_descriptor.baseMipLevel = 0; + view_descriptor.format = _wgpu_format; + view_descriptor.aspect = WGPUTextureAspect_All; + view_descriptor.usage = WGPUTextureUsage_RenderAttachment; + _images[_current_index].wgpu_texture_view = wgpuTextureCreateView(_texture.texture, &view_descriptor); + return _current_index; } diff --git a/source/code/modules/webgpu_renderer/private/webgpu_swapchain.hxx b/source/code/modules/webgpu_renderer/private/webgpu_swapchain.hxx index f46c8bb9..2613a493 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_swapchain.hxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_swapchain.hxx @@ -13,7 +13,7 @@ namespace ice::render::webgpu { public: WebGPUSwapchain( - WGPUSwapChain wgpu_swapchain, + WGPUSurface wgpu_surface, WGPUTextureFormat wgpu_format, ice::vec2u extent ) noexcept; @@ -31,10 +31,11 @@ namespace ice::render::webgpu auto current_image_index() const noexcept -> ice::u32 override; - WGPUSwapChain const _wgpu_swapchain; + WGPUSurface const _wgpu_surface; private: WGPUTextureFormat const _wgpu_format; ice::vec2u const _extent; + WGPUSurfaceTexture _texture; WebGPUImage _images[2]; uint32_t _current_index; }; diff --git a/source/code/modules/webgpu_renderer/private/webgpu_utils.hxx b/source/code/modules/webgpu_renderer/private/webgpu_utils.hxx index 60216801..fd4f3b6b 100644 --- a/source/code/modules/webgpu_renderer/private/webgpu_utils.hxx +++ b/source/code/modules/webgpu_renderer/private/webgpu_utils.hxx @@ -13,6 +13,11 @@ namespace ice::render::webgpu constexpr ice::LogTagDefinition LogTag_WebGPU = ice::create_log_tag(ice::LogTag::Module, "WebGPU"); + constexpr auto wgpu_string(ice::String value) noexcept -> WGPUStringView + { + return WGPUStringView{ .data = value._data, .length = value._size }; + } + #define ICE_LOG_WGPU(severity, message, ...) \ ICE_LOG(severity, ice::render::webgpu::LogTag_WebGPU, message, __VA_ARGS__) diff --git a/source/code/modules/webgpu_renderer/webgpu_renderer.bff b/source/code/modules/webgpu_renderer/webgpu_renderer.bff index feca0759..e7f8abdb 100644 --- a/source/code/modules/webgpu_renderer/webgpu_renderer.bff +++ b/source/code/modules/webgpu_renderer/webgpu_renderer.bff @@ -17,12 +17,5 @@ 'render_system' } ] - - .Public = - [ - .Libs = { - 'webgpu' - } - ] ] .Projects + .Project diff --git a/source/configs.bff b/source/configs.bff index 02d8a31e..01abce75 100644 --- a/source/configs.bff +++ b/source/configs.bff @@ -216,6 +216,8 @@ // Requires options for multithreading '-pthread' '-sSHARED_MEMORY=1' + // WebGPU (Dawn) + '--use-port=emdawnwebgpu' } .LinkLinkerOptions = { '-pthread' @@ -228,9 +230,10 @@ // '-sINITIAL_MEMORY=1073741824' // 1GiB '-sMALLOC=mimalloc' '-sABORTING_MALLOC=1' - '-sUSE_WEBGPU=1' '-sUSE_GLFW=3' '-sEXIT_RUNTIME' + // WebGPU (Dawn) + '--use-port=emdawnwebgpu' } ] @@ -285,6 +288,24 @@ } ] +.Config_Profile_WebAsm = +[ + .Name = 'Config-Profile-WebAsm' + .Requires = { 'Profile', 'SDK-WebAsm', .Kind_WindowedApp } + .LinkLinkerOptions = { + // '--closure=1' (Fix access to Node from Emscpripted) + } +] + +.Config_Release_WebAsm = +[ + .Name = 'Config-Release-WebAsm' + .Requires = { 'Release', 'SDK-WebAsm', .Kind_WindowedApp } + .LinkLinkerOptions = { + // '--closure=1' (Fix access to Node from Emscpripted) + } +] + .Config_Develop_MSVC = [ .Name = 'Config-Develop-MSVC' @@ -380,7 +401,9 @@ .Config_Develop_MSVC .Config_Profile .Config_Profile_MSVC + .Config_Profile_WebAsm .Config_Release .Config_Release_MSVC + .Config_Release_WebAsm .Config_Vulkan } diff --git a/source/fbuild.bff b/source/fbuild.bff index 7db40030..a6e4ab69 100644 --- a/source/fbuild.bff +++ b/source/fbuild.bff @@ -103,7 +103,7 @@ Settings Using( .Piepline_WebAsm ) // Might need to be updated whenever latest EmScripten is downloaded - .EMSDKVersion = '4.0.9' + .EMSDKVersion = '4.0.18' .PipelineName = 'WebAsm' .PipelineAllowUnityBuilds = true @@ -111,7 +111,7 @@ Settings .PipelineTags = { 'Monolythic' } .PipelineConanProfile = 'WebAsm' - .PipelineToolchain = 'em4-clang-21.0.0' + .PipelineToolchain = 'em4-clang-22.0.0' // Disable explicitly (we dont want VS configs for WebAssembly for now) .PipelineVSInfo = diff --git a/tools/settings.json b/tools/settings.json index 414ba3a7..9442a907 100644 --- a/tools/settings.json +++ b/tools/settings.json @@ -35,7 +35,7 @@ "webasm": { "emscripten": { "location": "build/webasm", - "version": "4.0.9" + "version": "4.0.18" } }, "doxy": { From 4f92d95f663393ee154c5d2546a687b2f55b2164 Mon Sep 17 00:00:00 2001 From: Dandielo Date: Wed, 12 Nov 2025 22:17:50 +0900 Subject: [PATCH 2/2] [actions] Update clang version param used by build script. (Emscripten) --- .github/workflows/build-validate-emscripten.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-validate-emscripten.yaml b/.github/workflows/build-validate-emscripten.yaml index 451b7582..279abeb6 100644 --- a/.github/workflows/build-validate-emscripten.yaml +++ b/.github/workflows/build-validate-emscripten.yaml @@ -23,4 +23,4 @@ jobs: host-platform: 'windows-latest' target-platform: 'web' targets: "${{ matrix.project }}-${{ matrix.pipeline }}-${{ matrix.config }}" - clang-version: '21' + clang-version: '22'