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' 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": {