// Include WebGPU headers first to define WEBGPU_H_ #if MLN_WEBGPU_IMPL_DAWN #include #elif MLN_WEBGPU_IMPL_WGPU #include #endif #include #include #include #include #include #include #include #include #include // for strlen #include // for std::getenv namespace mbgl { namespace webgpu { class RenderPass::Impl { public: WGPURenderPassEncoder encoder = nullptr; WGPUCommandEncoder commandEncoder = nullptr; const gfx::UniformBufferArray* globalUniformBuffers = nullptr; // Required here because it must life as long as the render pass wgpu::TextureView colorView; // Required here because it must life as long as the render pass wgpu::TextureView depthStencilView; wgpu::TextureFormat previousColorFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat previousDepthStencilFormat = wgpu::TextureFormat::Undefined; bool colorFormatUpdated = false; bool depthFormatUpdated = false; }; RenderPass::RenderPass(CommandEncoder& commandEncoder_, const char* name, const gfx::RenderPassDescriptor& descriptor_) : descriptor(descriptor_), commandEncoder(commandEncoder_), debugGroup(commandEncoder_.createDebugGroup(name)), impl(std::make_unique()) { // Get the WebGPU command encoder from our CommandEncoder impl->commandEncoder = commandEncoder_.getEncoder(); if (!impl->commandEncoder) { mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Command encoder unavailable"); return; } // Access the renderable resource associated with this pass // Check if resource exists before accessing it if (!descriptor.renderable.hasResource()) { mbgl::Log::Error(mbgl::Event::Render, "WebGPU: No renderable resource available"); return; } auto& renderableResource = descriptor.renderable.getResource(); renderableResource.bind(); // Get the backend to access device and surface fallbacks auto& context = commandEncoder_.getContext(); auto& backend = static_cast(context.getBackend()); impl->previousColorFormat = backend.getColorFormat(); impl->previousDepthStencilFormat = backend.getDepthStencilFormat(); // Resolve the color attachment view, preferring the renderable's texture view WGPUTextureView colorViewHandle = renderableResource.getColorTextureView(); if (!colorViewHandle) { if (void* textureViewPtr = backend.getCurrentTextureView()) { colorViewHandle = reinterpret_cast(textureViewPtr); } } if (!colorViewHandle) { mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Failed to acquire color attachment view"); return; } wgpuTextureViewAddRef(colorViewHandle); #if MLN_WEBGPU_IMPL_DAWN impl->colorView = wgpu::TextureView::Acquire(colorViewHandle); #elif MLN_WEBGPU_IMPL_WGPU impl->colorView = wgpu::TextureView(colorViewHandle); #endif if (auto colorFormat = renderableResource.getColorTextureFormat()) { backend.setColorFormat(*colorFormat); impl->colorFormatUpdated = true; } WGPURenderPassColorAttachment colorAttachment = {}; #if MLN_WEBGPU_IMPL_DAWN colorAttachment.view = impl->colorView.Get(); #elif MLN_WEBGPU_IMPL_WGPU colorAttachment.view = static_cast(impl->colorView); #endif colorAttachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; colorAttachment.resolveTarget = nullptr; colorAttachment.storeOp = WGPUStoreOp_Store; if (descriptor.clearColor) { const auto& clearColor = descriptor.clearColor.value(); const WGPUColor value{static_cast(clearColor.r), static_cast(clearColor.g), static_cast(clearColor.b), static_cast(clearColor.a)}; colorAttachment.loadOp = WGPULoadOp_Clear; colorAttachment.clearValue = value; } else { colorAttachment.loadOp = WGPULoadOp_Load; #if MLN_WEBGPU_IMPL_WGPU // wgpu-native requires clearValue to be set even when using LoadOp_Load const WGPUColor defaultClearValue{0.0, 0.0, 0.0, 0.0}; colorAttachment.clearValue = defaultClearValue; #endif } WGPURenderPassDepthStencilAttachment depthAttachment = {}; depthAttachment.depthLoadOp = descriptor.clearDepth ? WGPULoadOp_Clear : WGPULoadOp_Load; depthAttachment.depthStoreOp = WGPUStoreOp_Store; depthAttachment.depthClearValue = descriptor.clearDepth ? descriptor.clearDepth.value() : 1.0f; depthAttachment.depthReadOnly = false; if (descriptor.clearStencil) { depthAttachment.stencilLoadOp = WGPULoadOp_Clear; depthAttachment.stencilStoreOp = WGPUStoreOp_Store; depthAttachment.stencilClearValue = static_cast(descriptor.clearStencil.value()); } else { depthAttachment.stencilLoadOp = WGPULoadOp_Undefined; depthAttachment.stencilStoreOp = WGPUStoreOp_Undefined; depthAttachment.stencilClearValue = 0u; } depthAttachment.stencilReadOnly = false; depthAttachment.view = nullptr; WGPURenderPassDepthStencilAttachment* depthAttachmentPtr = nullptr; WGPUTextureView depthViewHandle = renderableResource.getDepthStencilTextureView(); if (!depthViewHandle) { if (void* depthStencilViewPtr = backend.getDepthStencilView()) { depthViewHandle = reinterpret_cast(depthStencilViewPtr); } } if (depthViewHandle) { wgpuTextureViewAddRef(depthViewHandle); #if MLN_WEBGPU_IMPL_DAWN impl->depthStencilView = wgpu::TextureView::Acquire(depthViewHandle); #elif MLN_WEBGPU_IMPL_WGPU impl->depthStencilView = wgpu::TextureView(depthViewHandle); #endif if (impl->depthStencilView) { #if MLN_WEBGPU_IMPL_DAWN depthAttachment.view = impl->depthStencilView.Get(); #elif MLN_WEBGPU_IMPL_WGPU depthAttachment.view = static_cast(impl->depthStencilView); #endif depthAttachmentPtr = &depthAttachment; } } if (depthAttachmentPtr) { if (auto depthFormat = renderableResource.getDepthStencilTextureFormat()) { backend.setDepthStencilFormat(*depthFormat); impl->depthFormatUpdated = true; } } WGPURenderPassDescriptor renderPassDesc = {}; const WGPUStringView passLabel{name, name ? strlen(name) : 0}; renderPassDesc.label = passLabel; renderPassDesc.colorAttachmentCount = 1; renderPassDesc.colorAttachments = &colorAttachment; renderPassDesc.depthStencilAttachment = depthAttachmentPtr; impl->encoder = wgpuCommandEncoderBeginRenderPass(impl->commandEncoder, &renderPassDesc); if (impl->encoder) { auto size = descriptor.renderable.getSize(); wgpuRenderPassEncoderSetViewport( impl->encoder, 0.0f, 0.0f, static_cast(size.width), static_cast(size.height), 0.0f, 1.0f); wgpuRenderPassEncoderSetScissorRect(impl->encoder, 0, 0, size.width, size.height); } else { mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Failed to begin render pass"); #if MLN_WEBGPU_IMPL_WGPU wgpuTextureViewRelease(impl->colorView); wgpuTextureViewRelease(impl->depthStencilView); #endif impl->colorView = nullptr; impl->depthStencilView = nullptr; } } RenderPass::~RenderPass() { if (impl->encoder) { // End the render pass try { wgpuRenderPassEncoderEnd(impl->encoder); } catch (...) { mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Failed to end render pass"); } wgpuRenderPassEncoderRelease(impl->encoder); } #if MLN_WEBGPU_IMPL_WGPU if (impl->colorView) { wgpuTextureViewRelease(impl->colorView); } if (impl->depthStencilView) { wgpuTextureViewRelease(impl->depthStencilView); } #endif auto& backend = static_cast(commandEncoder.getContext().getBackend()); if (impl->colorFormatUpdated) { backend.setColorFormat(impl->previousColorFormat); } if (impl->depthFormatUpdated) { backend.setDepthStencilFormat(impl->previousDepthStencilFormat); } } void RenderPass::pushDebugGroup(const char* name) { if (impl->encoder) { // Debug groups on render pass encoders can cause issues in some Dawn versions // Enable with environment variable if needed for debugging static bool enableDebugGroups = std::getenv("WEBGPU_ENABLE_DEBUG_GROUPS") != nullptr; if (enableDebugGroups) { WGPUStringView label = {name, name ? strlen(name) : 0}; wgpuRenderPassEncoderPushDebugGroup(impl->encoder, label); } } } void RenderPass::popDebugGroup() { if (impl->encoder) { // Debug groups on render pass encoders can cause issues in some Dawn versions // Enable with environment variable if needed for debugging static bool enableDebugGroups = std::getenv("WEBGPU_ENABLE_DEBUG_GROUPS") != nullptr; if (enableDebugGroups) { wgpuRenderPassEncoderPopDebugGroup(impl->encoder); } } } void RenderPass::addDebugSignpost(const char* name) { if (impl->encoder) { WGPUStringView label = {name, name ? strlen(name) : 0}; wgpuRenderPassEncoderInsertDebugMarker(impl->encoder, label); } } WGPURenderPassEncoder RenderPass::getEncoder() const { return impl->encoder; } void RenderPass::setGlobalUniformBuffers(const gfx::UniformBufferArray* buffers) { impl->globalUniformBuffers = buffers; } const gfx::UniformBufferArray* RenderPass::getGlobalUniformBuffers() const { return impl->globalUniformBuffers; } } // namespace webgpu } // namespace mbgl