| // Copyright 2026 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "dawn/native/webgpu/SwapChainWGPU.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "dawn/common/StringViewUtils.h" |
| #include "dawn/native/Adapter.h" |
| #include "dawn/native/ChainUtils.h" |
| #include "dawn/native/Surface.h" |
| #include "dawn/native/webgpu/DeviceWGPU.h" |
| #include "dawn/native/webgpu/QueueWGPU.h" |
| #include "dawn/native/webgpu/TextureWGPU.h" |
| #include "dawn/native/webgpu/ToWGPU.h" |
| #include "dawn/native/webgpu/WebGPUError.h" |
| |
| namespace dawn::native::webgpu { |
| |
| ResultOrError<WGPUSurface> CreateWGPUSurface(const DawnProcTable& procs, |
| WGPUInstance instance, |
| const Surface* surface) { |
| WGPUSurfaceDescriptor descriptor = WGPU_SURFACE_DESCRIPTOR_INIT; |
| descriptor.label = ToOutputStringView(surface->GetLabel()); |
| |
| // Reconstruct the chained descriptor based on the surface type. |
| WGPUSurfaceSourceAndroidNativeWindow androidDesc = |
| WGPU_SURFACE_SOURCE_ANDROID_NATIVE_WINDOW_INIT; |
| WGPUSurfaceSourceMetalLayer metalDesc = WGPU_SURFACE_SOURCE_METAL_LAYER_INIT; |
| WGPUSurfaceSourceWindowsHWND hwndDesc = WGPU_SURFACE_SOURCE_WINDOWS_HWND_INIT; |
| WGPUSurfaceSourceWaylandSurface waylandDesc = WGPU_SURFACE_SOURCE_WAYLAND_SURFACE_INIT; |
| WGPUSurfaceSourceXlibWindow xlibDesc = WGPU_SURFACE_SOURCE_XLIB_WINDOW_INIT; |
| WGPUSurfaceDescriptorFromWindowsCoreWindow coreWindowDesc = |
| WGPU_SURFACE_DESCRIPTOR_FROM_WINDOWS_CORE_WINDOW_INIT; |
| WGPUSurfaceDescriptorFromWindowsUWPSwapChainPanel uwpDesc = |
| WGPU_SURFACE_DESCRIPTOR_FROM_WINDOWS_UWP_SWAP_CHAIN_PANEL_INIT; |
| WGPUSurfaceDescriptorFromWindowsWinUISwapChainPanel winuiDesc = |
| WGPU_SURFACE_DESCRIPTOR_FROM_WINDOWS_WINUI_SWAP_CHAIN_PANEL_INIT; |
| |
| switch (surface->GetType()) { |
| case Surface::Type::AndroidWindow: |
| androidDesc.chain.sType = WGPUSType_SurfaceSourceAndroidNativeWindow; |
| androidDesc.window = surface->GetAndroidNativeWindow(); |
| descriptor.nextInChain = &androidDesc.chain; |
| break; |
| case Surface::Type::MetalLayer: |
| metalDesc.chain.sType = WGPUSType_SurfaceSourceMetalLayer; |
| metalDesc.layer = surface->GetMetalLayer(); |
| descriptor.nextInChain = &metalDesc.chain; |
| break; |
| case Surface::Type::WindowsHWND: |
| hwndDesc.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; |
| hwndDesc.hinstance = surface->GetHInstance(); |
| hwndDesc.hwnd = surface->GetHWND(); |
| descriptor.nextInChain = &hwndDesc.chain; |
| break; |
| case Surface::Type::WindowsCoreWindow: |
| coreWindowDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsCoreWindow; |
| coreWindowDesc.coreWindow = surface->GetCoreWindow(); |
| descriptor.nextInChain = &coreWindowDesc.chain; |
| break; |
| case Surface::Type::WindowsUWPSwapChainPanel: |
| uwpDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsUWPSwapChainPanel; |
| uwpDesc.swapChainPanel = surface->GetUWPSwapChainPanel(); |
| descriptor.nextInChain = &uwpDesc.chain; |
| break; |
| case Surface::Type::WindowsWinUISwapChainPanel: |
| winuiDesc.chain.sType = WGPUSType_SurfaceDescriptorFromWindowsWinUISwapChainPanel; |
| winuiDesc.swapChainPanel = surface->GetWinUISwapChainPanel(); |
| descriptor.nextInChain = &winuiDesc.chain; |
| break; |
| case Surface::Type::WaylandSurface: |
| waylandDesc.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; |
| waylandDesc.display = surface->GetWaylandDisplay(); |
| waylandDesc.surface = surface->GetWaylandSurface(); |
| descriptor.nextInChain = &waylandDesc.chain; |
| break; |
| case Surface::Type::XlibWindow: |
| xlibDesc.chain.sType = WGPUSType_SurfaceSourceXlibWindow; |
| xlibDesc.display = surface->GetXDisplay(); |
| xlibDesc.window = surface->GetXWindow(); |
| descriptor.nextInChain = &xlibDesc.chain; |
| break; |
| default: |
| return DAWN_VALIDATION_ERROR("Unknown surface type %s.", surface->GetType()); |
| } |
| |
| WGPUSurface innerSurface = procs.instanceCreateSurface(instance, &descriptor); |
| DAWN_ASSERT(innerSurface); |
| return innerSurface; |
| } |
| |
| // static |
| ResultOrError<Ref<SwapChain>> SwapChain::Create(Device* device, |
| Surface* surface, |
| SwapChainBase* previousSwapChain, |
| const SurfaceConfiguration* config) { |
| WGPUSurface innerSurface = nullptr; |
| |
| if (previousSwapChain != nullptr) { |
| // Transfer the inner surface ownership. |
| SwapChain* backendSwapChain = ToBackend(previousSwapChain); |
| innerSurface = backendSwapChain->mInnerHandle; |
| backendSwapChain->mInnerHandle = nullptr; |
| } else { |
| DAWN_TRY_ASSIGN(innerSurface, |
| CreateWGPUSurface(*device->wgpu, device->GetInnerInstance(), surface)); |
| } |
| DAWN_ASSERT(innerSurface); |
| |
| WGPUSurfaceConfiguration innerConfig = {}; |
| innerConfig.device = device->GetInnerHandle(); |
| innerConfig.format = ToAPI(config->format); |
| innerConfig.usage = ToAPI(config->usage); |
| innerConfig.viewFormatCount = config->viewFormatCount; |
| std::vector<WGPUTextureFormat> viewFormats; |
| for (uint32_t i = 0; i < config->viewFormatCount; ++i) { |
| viewFormats.push_back(ToAPI(config->viewFormats[i])); |
| } |
| innerConfig.viewFormats = viewFormats.data(); |
| innerConfig.alphaMode = ToAPI(config->alphaMode); |
| innerConfig.width = config->width; |
| innerConfig.height = config->height; |
| innerConfig.presentMode = ToAPI(config->presentMode); |
| |
| device->wgpu->surfaceConfigure(innerSurface, &innerConfig); |
| |
| return AcquireRef(new SwapChain(device, surface, config, innerSurface)); |
| } |
| |
| SwapChain::SwapChain(Device* device, |
| Surface* surface, |
| const SurfaceConfiguration* config, |
| WGPUSurface innerSurface) |
| : SwapChainBase(device, surface, config), ObjectWGPU(device->wgpu->surfaceRelease) { |
| mInnerHandle = innerSurface; |
| } |
| |
| SwapChain::~SwapChain() = default; |
| |
| WGPUSurface SwapChain::GetInnerSurface() const { |
| DAWN_ASSERT(mInnerHandle); |
| return mInnerHandle; |
| } |
| |
| ResultOrError<SwapChainTextureInfo> SwapChain::GetCurrentTextureImpl() { |
| Device* device = ToBackend(GetDevice()); |
| WGPUSurfaceTexture innerSurfaceTexture = WGPU_SURFACE_TEXTURE_INIT; |
| device->wgpu->surfaceGetCurrentTexture(mInnerHandle, &innerSurfaceTexture); |
| |
| SwapChainTextureInfo info; |
| info.status = FromAPI(innerSurfaceTexture.status); |
| |
| if (info.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal && |
| info.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal) { |
| DAWN_ASSERT(innerSurfaceTexture.texture == nullptr); |
| return info; |
| } |
| |
| TextureDescriptor desc = GetSwapChainBaseTextureDescriptor(this); |
| |
| // Note: We're creating a Texture wrapper around the inner handle. |
| Ref<Texture> texture; |
| UnpackedPtr<TextureDescriptor> unpacked = Unpack(&desc); |
| texture = Texture::CreateFromSurfaceTexture(device, unpacked, innerSurfaceTexture); |
| |
| mCurrentTexture = texture; |
| info.texture = std::move(texture); |
| |
| return info; |
| } |
| |
| MaybeError SwapChain::PresentImpl() { |
| Device* device = ToBackend(GetDevice()); |
| |
| WGPUStatus status = device->wgpu->surfacePresent(mInnerHandle); |
| |
| DAWN_TRY(CheckWGPUSuccess(status, "surfacePresent")); |
| |
| DAWN_ASSERT(mCurrentTexture != nullptr); |
| mCurrentTexture->Destroy(); |
| mCurrentTexture = nullptr; |
| |
| return {}; |
| } |
| |
| void SwapChain::DetachFromSurfaceImpl() { |
| if (mCurrentTexture != nullptr) { |
| mCurrentTexture->Destroy(); |
| mCurrentTexture = nullptr; |
| } |
| } |
| |
| } // namespace dawn::native::webgpu |