blob: 26afdc5a23faefae1e131526b85d44612c9d77f5 [file]
// 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