blob: 61f385349ebf8f33455c98017a29e9943f4070cd [file] [log] [blame] [edit]
//
// Copyright 2024 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// SurfaceWgpu.cpp:
// Implements the class methods for SurfaceWgpu.
//
#ifdef UNSAFE_BUFFERS_BUILD
# pragma allow_unsafe_buffers
#endif
#include "libANGLE/renderer/wgpu/SurfaceWgpu.h"
#include "common/debug.h"
#include "libANGLE/Display.h"
#include "libANGLE/Surface.h"
#include "libANGLE/renderer/wgpu/DisplayWgpu.h"
#include "libANGLE/renderer/wgpu/FramebufferWgpu.h"
namespace rx
{
constexpr WGPUTextureUsage kSurfaceTextureUsage =
WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc |
WGPUTextureUsage_CopyDst;
SurfaceWgpu::SurfaceWgpu(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
SurfaceWgpu::~SurfaceWgpu() {}
angle::Result SurfaceWgpu::createDepthStencilAttachment(const egl::Display *display,
uint32_t width,
uint32_t height,
const webgpu::Format &webgpuFormat,
webgpu::DeviceHandle device,
AttachmentImage *outDepthStencilAttachment)
{
const DisplayWgpu *displayWgpu = webgpu::GetImpl(display);
const DawnProcTable *wgpu = displayWgpu->getProcs();
WGPUTextureDescriptor desc = outDepthStencilAttachment->texture.createTextureDescriptor(
kSurfaceTextureUsage, WGPUTextureDimension_2D, {width, height, 1},
webgpuFormat.getActualWgpuTextureFormat(), 1, 1);
constexpr uint32_t level = 0;
constexpr uint32_t layer = 0;
ANGLE_TRY(outDepthStencilAttachment->texture.initImage(wgpu, webgpuFormat.getIntendedFormatID(),
webgpuFormat.getActualImageFormatID(),
device, gl::LevelIndex(level), desc));
webgpu::TextureViewHandle view;
ANGLE_TRY(outDepthStencilAttachment->texture.createTextureViewSingleLevel(gl::LevelIndex(level),
layer, view));
outDepthStencilAttachment->renderTarget.set(
&outDepthStencilAttachment->texture, view, webgpu::LevelIndex(level), layer,
outDepthStencilAttachment->texture.toWgpuTextureFormat());
return angle::Result::Continue;
}
OffscreenSurfaceWgpu::OffscreenSurfaceWgpu(const egl::SurfaceState &surfaceState,
EGLenum clientBufferType,
EGLClientBuffer clientBuffer)
: SurfaceWgpu(surfaceState),
mWidth(surfaceState.attributes.getAsInt(EGL_WIDTH, 0)),
mHeight(surfaceState.attributes.getAsInt(EGL_HEIGHT, 0)),
mClientBufferType(clientBufferType),
mClientBuffer(clientBuffer)
{}
OffscreenSurfaceWgpu::~OffscreenSurfaceWgpu() {}
egl::Error OffscreenSurfaceWgpu::initialize(const egl::Display *display)
{
return angle::ResultToEGL(initializeImpl(display));
}
egl::Error OffscreenSurfaceWgpu::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
{
UNREACHABLE();
return egl::NoError();
}
egl::Error OffscreenSurfaceWgpu::bindTexImage(const gl::Context *context,
gl::Texture *texture,
EGLint buffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
egl::Error OffscreenSurfaceWgpu::releaseTexImage(const gl::Context *context, EGLint buffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
void OffscreenSurfaceWgpu::setSwapInterval(const egl::Display *display, EGLint interval) {}
gl::Extents OffscreenSurfaceWgpu::getSize() const
{
return gl::Extents(mWidth, mHeight, 1);
}
EGLint OffscreenSurfaceWgpu::getSwapBehavior() const
{
return EGL_BUFFER_DESTROYED;
}
angle::Result OffscreenSurfaceWgpu::initializeContents(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
egl::Error OffscreenSurfaceWgpu::attachToFramebuffer(const gl::Context *context,
gl::Framebuffer *framebuffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
egl::Error OffscreenSurfaceWgpu::detachFromFramebuffer(const gl::Context *context,
gl::Framebuffer *framebuffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
angle::Result OffscreenSurfaceWgpu::getAttachmentRenderTarget(
const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut)
{
if (binding == GL_BACK)
{
*rtOut = &mColorAttachment.renderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
*rtOut = &mDepthStencilAttachment.renderTarget;
}
return angle::Result::Continue;
}
angle::Result OffscreenSurfaceWgpu::initializeImpl(const egl::Display *display)
{
DisplayWgpu *displayWgpu = webgpu::GetImpl(display);
const DawnProcTable *wgpu = displayWgpu->getProcs();
webgpu::DeviceHandle device = displayWgpu->getDevice();
if (mClientBufferType == EGL_WEBGPU_TEXTURE_ANGLE)
{
webgpu::TextureHandle externalTexture =
webgpu::TextureHandle::Acquire(wgpu, reinterpret_cast<WGPUTexture>(mClientBuffer));
ASSERT(externalTexture != nullptr);
// Explicitly add-ref to hold on to the external reference
wgpu->textureAddRef(externalTexture.get());
const webgpu::Format *webgpuFormat = displayWgpu->getFormatForImportedTexture(
mState.attributes, wgpu->textureGetFormat(externalTexture.get()));
ASSERT(webgpuFormat);
ANGLE_TRY(mColorAttachment.texture.initExternal(wgpu, webgpuFormat->getIntendedFormatID(),
webgpuFormat->getActualImageFormatID(),
externalTexture));
webgpu::TextureViewHandle view;
ANGLE_TRY(
mColorAttachment.texture.createTextureViewSingleLevel(gl::LevelIndex(0), 0, view));
mColorAttachment.renderTarget.set(&mColorAttachment.texture, view, webgpu::LevelIndex(0), 0,
mColorAttachment.texture.toWgpuTextureFormat());
mWidth = mColorAttachment.texture.getSize().width;
mHeight = mColorAttachment.texture.getSize().height;
}
else
{
const egl::Config *config = mState.config;
if (config->renderTargetFormat != GL_NONE)
{
const webgpu::Format &webgpuFormat = displayWgpu->getFormat(config->renderTargetFormat);
WGPUTextureDescriptor desc = mColorAttachment.texture.createTextureDescriptor(
kSurfaceTextureUsage, WGPUTextureDimension_2D,
{static_cast<uint32_t>(mWidth), static_cast<uint32_t>(mHeight), 1},
webgpuFormat.getActualWgpuTextureFormat(), 1, 1);
constexpr uint32_t level = 0;
constexpr uint32_t layer = 0;
ANGLE_TRY(mColorAttachment.texture.initImage(wgpu, webgpuFormat.getIntendedFormatID(),
webgpuFormat.getActualImageFormatID(),
device, gl::LevelIndex(level), desc));
webgpu::TextureViewHandle view;
ANGLE_TRY(mColorAttachment.texture.createTextureViewSingleLevel(gl::LevelIndex(level),
layer, view));
mColorAttachment.renderTarget.set(&mColorAttachment.texture, view,
webgpu::LevelIndex(level), layer,
mColorAttachment.texture.toWgpuTextureFormat());
}
if (config->depthStencilFormat != GL_NONE)
{
const webgpu::Format &webgpuFormat = displayWgpu->getFormat(config->depthStencilFormat);
ANGLE_TRY(createDepthStencilAttachment(display, static_cast<uint32_t>(mWidth),
static_cast<uint32_t>(mHeight), webgpuFormat,
device, &mDepthStencilAttachment));
}
}
return angle::Result::Continue;
}
WindowSurfaceWgpu::WindowSurfaceWgpu(const egl::SurfaceState &surfaceState,
EGLNativeWindowType window)
: SurfaceWgpu(surfaceState), mNativeWindow(window)
{}
WindowSurfaceWgpu::~WindowSurfaceWgpu() {}
egl::Error WindowSurfaceWgpu::initialize(const egl::Display *display)
{
return angle::ResultToEGL(initializeImpl(display));
}
void WindowSurfaceWgpu::destroy(const egl::Display *display)
{
mSurface = nullptr;
mColorAttachment.renderTarget.reset();
mColorAttachment.texture.resetImage();
mDepthStencilAttachment.renderTarget.reset();
mDepthStencilAttachment.texture.resetImage();
}
egl::Error WindowSurfaceWgpu::swap(const gl::Context *context, SurfaceSwapFeedback *feedback)
{
return angle::ResultToEGL(swapImpl(context));
}
egl::Error WindowSurfaceWgpu::bindTexImage(const gl::Context *context,
gl::Texture *texture,
EGLint buffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
egl::Error WindowSurfaceWgpu::releaseTexImage(const gl::Context *context, EGLint buffer)
{
UNIMPLEMENTED();
return egl::NoError();
}
void WindowSurfaceWgpu::setSwapInterval(const egl::Display *display, EGLint interval)
{
UNIMPLEMENTED();
}
gl::Extents WindowSurfaceWgpu::getSize() const
{
return mCurrentSurfaceSize;
}
EGLint WindowSurfaceWgpu::getSwapBehavior() const
{
UNIMPLEMENTED();
return 0;
}
angle::Result WindowSurfaceWgpu::initializeContents(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
egl::Error WindowSurfaceWgpu::attachToFramebuffer(const gl::Context *context,
gl::Framebuffer *framebuffer)
{
FramebufferWgpu *framebufferWgpu = GetImplAs<FramebufferWgpu>(framebuffer);
framebufferWgpu->setFlipY(true);
return egl::NoError();
}
egl::Error WindowSurfaceWgpu::detachFromFramebuffer(const gl::Context *context,
gl::Framebuffer *framebuffer)
{
FramebufferWgpu *framebufferWgpu = GetImplAs<FramebufferWgpu>(framebuffer);
framebufferWgpu->setFlipY(false);
return egl::NoError();
}
angle::Result WindowSurfaceWgpu::getAttachmentRenderTarget(
const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut)
{
if (binding == GL_BACK)
{
*rtOut = &mColorAttachment.renderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
*rtOut = &mDepthStencilAttachment.renderTarget;
}
return angle::Result::Continue;
}
angle::Result WindowSurfaceWgpu::initializeImpl(const egl::Display *display)
{
DisplayWgpu *displayWgpu = webgpu::GetImpl(display);
const DawnProcTable *wgpu = displayWgpu->getProcs();
webgpu::AdapterHandle adapter = displayWgpu->getAdapter();
ANGLE_TRY(createWgpuSurface(display, &mSurface));
gl::Extents size;
ANGLE_TRY(getCurrentWindowSize(display, &size));
WGPUSurfaceCapabilities surfaceCapabilities = WGPU_SURFACE_CAPABILITIES_INIT;
WGPUStatus getCapabilitiesStatus =
wgpu->surfaceGetCapabilities(mSurface.get(), adapter.get(), &surfaceCapabilities);
if (getCapabilitiesStatus != WGPUStatus_Success)
{
ERR() << "wgpuSurfaceGetCapabilities failed: "
<< gl::FmtHex(static_cast<uint32_t>(getCapabilitiesStatus));
return angle::Result::Stop;
}
const egl::Config *config = mState.config;
ASSERT(config->renderTargetFormat != GL_NONE);
mSurfaceTextureFormat = &displayWgpu->getFormat(config->renderTargetFormat);
ASSERT(std::find(surfaceCapabilities.formats,
surfaceCapabilities.formats + surfaceCapabilities.formatCount,
mSurfaceTextureFormat->getActualWgpuTextureFormat()) !=
(surfaceCapabilities.formats + surfaceCapabilities.formatCount));
mSurfaceTextureUsage =
WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst;
ASSERT((surfaceCapabilities.usages & mSurfaceTextureUsage) == mSurfaceTextureUsage);
// Default to the always supported Fifo present mode. Use Mailbox if it's available.
mPresentMode = WGPUPresentMode_Fifo;
for (size_t i = 0; i < surfaceCapabilities.presentModeCount; i++)
{
if (surfaceCapabilities.presentModes[i] == WGPUPresentMode_Mailbox)
{
mPresentMode = WGPUPresentMode_Mailbox;
}
}
if (config->depthStencilFormat != GL_NONE)
{
mDepthStencilFormat = &displayWgpu->getFormat(config->depthStencilFormat);
}
else
{
mDepthStencilFormat = nullptr;
}
ANGLE_TRY(configureSurface(display, size));
ANGLE_TRY(updateCurrentTexture(display));
return angle::Result::Continue;
}
angle::Result WindowSurfaceWgpu::swapImpl(const gl::Context *context)
{
const egl::Display *display = context->getDisplay();
ContextWgpu *contextWgpu = webgpu::GetImpl(context);
const DawnProcTable *wgpu = webgpu::GetProcs(contextWgpu);
ANGLE_TRY(contextWgpu->flush(webgpu::RenderPassClosureReason::EGLSwapBuffers));
wgpu->surfacePresent(mSurface.get());
gl::Extents size;
ANGLE_TRY(getCurrentWindowSize(display, &size));
if (size != mCurrentSurfaceSize)
{
ANGLE_TRY(configureSurface(display, size));
}
ANGLE_TRY(updateCurrentTexture(display));
return angle::Result::Continue;
}
angle::Result WindowSurfaceWgpu::configureSurface(const egl::Display *display,
const gl::Extents &size)
{
DisplayWgpu *displayWgpu = webgpu::GetImpl(display);
const DawnProcTable *wgpu = displayWgpu->getProcs();
webgpu::DeviceHandle device = displayWgpu->getDevice();
ASSERT(mSurfaceTextureFormat != nullptr);
WGPUSurfaceConfiguration surfaceConfig = WGPU_SURFACE_CONFIGURATION_INIT;
surfaceConfig.device = device.get();
surfaceConfig.format = mSurfaceTextureFormat->getActualWgpuTextureFormat();
surfaceConfig.usage = mSurfaceTextureUsage;
surfaceConfig.width = size.width;
surfaceConfig.height = size.height;
surfaceConfig.presentMode = mPresentMode;
wgpu->surfaceConfigure(mSurface.get(), &surfaceConfig);
if (mDepthStencilFormat)
{
ANGLE_TRY(createDepthStencilAttachment(
display, static_cast<uint32_t>(size.width), static_cast<uint32_t>(size.height),
*mDepthStencilFormat, device, &mDepthStencilAttachment));
}
mCurrentSurfaceSize = size;
return angle::Result::Continue;
}
angle::Result WindowSurfaceWgpu::updateCurrentTexture(const egl::Display *display)
{
DisplayWgpu *displayWgpu = webgpu::GetImpl(display);
const DawnProcTable *wgpu = displayWgpu->getProcs();
WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT;
wgpu->surfaceGetCurrentTexture(mSurface.get(), &surfaceTexture);
webgpu::TextureHandle texture = webgpu::TextureHandle::Acquire(wgpu, surfaceTexture.texture);
if (surfaceTexture.status != WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal &&
surfaceTexture.status != WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal)
{
ERR() << "wgpuSurfaceGetCurrentTexture failed: "
<< gl::FmtHex(static_cast<uint32_t>(surfaceTexture.status));
return angle::Result::Stop;
}
WGPUTextureFormat wgpuFormat = wgpu->textureGetFormat(texture.get());
angle::FormatID angleFormat = webgpu::GetFormatIDFromWgpuTextureFormat(wgpuFormat);
ANGLE_TRY(mColorAttachment.texture.initExternal(wgpu, angleFormat, angleFormat, texture));
webgpu::TextureViewHandle view;
ANGLE_TRY(mColorAttachment.texture.createTextureViewSingleLevel(gl::LevelIndex(0), 0, view));
mColorAttachment.renderTarget.set(&mColorAttachment.texture, view, webgpu::LevelIndex(0), 0,
wgpuFormat);
return angle::Result::Continue;
}
} // namespace rx