blob: b7271732a525645a40a39fcbbd3833b754967420 [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.
//
// DisplayWgpu.cpp:
// Implements the class methods for DisplayWgpu.
//
#include "libANGLE/renderer/wgpu/DisplayWgpu.h"
#include <dawn/dawn_proc.h>
#include "common/debug.h"
#include "common/platform.h"
#include "libANGLE/Display.h"
#include "libANGLE/renderer/wgpu/ContextWgpu.h"
#include "libANGLE/renderer/wgpu/DeviceWgpu.h"
#include "libANGLE/renderer/wgpu/DisplayWgpu_api.h"
#include "libANGLE/renderer/wgpu/ImageWgpu.h"
#include "libANGLE/renderer/wgpu/SurfaceWgpu.h"
#include "libANGLE/renderer/wgpu/wgpu_proc_utils.h"
#if defined(ANGLE_PLATFORM_LINUX)
# if defined(ANGLE_USE_X11)
# define ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE 1
# define ANGLE_WEBGPU_WINDOW_SYSTEM angle::NativeWindowSystem::X11
# elif defined(ANGLE_USE_WAYLAND)
# define ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE 1
# define ANGLE_WEBGPU_WINDOW_SYSTEM angle::NativeWindowSystem::Wayland
# else
# define ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE 0
# define ANGLE_WEBGPU_WINDOW_SYSTEM angle::NativeWindowSystem::Other
# endif
#else
# define ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE 1
# define ANGLE_WEBGPU_WINDOW_SYSTEM angle::NativeWindowSystem::Other
#endif
namespace rx
{
#if !ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE
WindowSurfaceWgpu *CreateWgpuWindowSurface(const egl::SurfaceState &surfaceState,
EGLNativeWindowType window)
{
UNIMPLEMENTED();
return nullptr;
}
#endif
DisplayWgpu::DisplayWgpu(const egl::DisplayState &state) : DisplayImpl(state) {}
DisplayWgpu::~DisplayWgpu() {}
egl::Error DisplayWgpu::initialize(egl::Display *display)
{
const egl::AttributeMap &attribs = display->getAttributeMap();
mProcTable = *reinterpret_cast<DawnProcTable *>(
attribs.get(EGL_PLATFORM_ANGLE_DAWN_PROC_TABLE_ANGLE,
reinterpret_cast<EGLAttrib>(&webgpu::GetDefaultProcTable())));
WGPUInstance providedInstance =
reinterpret_cast<WGPUInstance>(attribs.get(EGL_PLATFORM_ANGLE_WEBGPU_INSTANCE_ANGLE, 0));
if (providedInstance)
{
mProcTable.instanceAddRef(providedInstance);
mInstance = webgpu::InstanceHandle::Acquire(&mProcTable, providedInstance);
}
WGPUDevice providedDevice =
reinterpret_cast<WGPUDevice>(attribs.get(EGL_PLATFORM_ANGLE_WEBGPU_DEVICE_ANGLE, 0));
if (providedDevice)
{
mProcTable.deviceAddRef(providedDevice);
mDevice = webgpu::DeviceHandle::Acquire(&mProcTable, providedDevice);
mAdapter =
webgpu::AdapterHandle::Acquire(&mProcTable, mProcTable.deviceGetAdapter(mDevice.get()));
if (!mInstance)
{
mInstance = webgpu::InstanceHandle::Acquire(
&mProcTable, mProcTable.adapterGetInstance(mAdapter.get()));
}
}
else
{
ANGLE_TRY(createWgpuDevice());
}
mQueue = webgpu::QueueHandle::Acquire(&mProcTable, mProcTable.deviceGetQueue(mDevice.get()));
mFormatTable.initialize();
mLimitsWgpu = WGPU_LIMITS_INIT;
mProcTable.deviceGetLimits(mDevice.get(), &mLimitsWgpu);
initializeFeatures();
webgpu::GenerateCaps(mLimitsWgpu, &mGLCaps, &mGLTextureCaps, &mGLExtensions, &mGLLimitations,
&mEGLCaps, &mEGLExtensions, &mMaxSupportedClientVersion);
return egl::NoError();
}
void DisplayWgpu::terminate()
{
mAdapter = nullptr;
mInstance = nullptr;
mDevice = nullptr;
mQueue = nullptr;
}
egl::Error DisplayWgpu::makeCurrent(egl::Display *display,
egl::Surface *drawSurface,
egl::Surface *readSurface,
gl::Context *context)
{
// Ensure that the correct global DebugAnnotator is installed when the end2end tests change
// the ANGLE back-end (done frequently).
display->setGlobalDebugAnnotator();
return egl::NoError();
}
egl::ConfigSet DisplayWgpu::generateConfigs()
{
egl::Config config;
config.renderTargetFormat = GL_BGRA8_EXT;
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
config.bufferSize = 32;
config.redSize = 8;
config.greenSize = 8;
config.blueSize = 8;
config.alphaSize = 8;
config.alphaMaskSize = 0;
config.bindToTextureRGB = EGL_FALSE;
config.bindToTextureRGBA = EGL_FALSE;
config.colorBufferType = EGL_RGB_BUFFER;
config.configCaveat = EGL_NONE;
config.conformant = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
config.depthSize = 24;
config.level = 0;
config.matchNativePixmap = EGL_NONE;
config.maxPBufferWidth = 0;
config.maxPBufferHeight = 0;
config.maxPBufferPixels = 0;
config.maxSwapInterval = 1;
config.minSwapInterval = 1;
config.nativeRenderable = EGL_TRUE;
config.nativeVisualID = 0;
config.nativeVisualType = EGL_NONE;
config.renderableType = EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
config.sampleBuffers = 0;
config.samples = 0;
config.stencilSize = 8;
config.surfaceType = EGL_PBUFFER_BIT;
#if ANGLE_WEBGPU_HAS_WINDOW_SURFACE_TYPE
config.surfaceType |= EGL_WINDOW_BIT;
#endif
config.optimalOrientation = 0;
config.transparentType = EGL_NONE;
config.transparentRedValue = 0;
config.transparentGreenValue = 0;
config.transparentBlueValue = 0;
egl::ConfigSet configSet;
configSet.add(config);
return configSet;
}
bool DisplayWgpu::testDeviceLost()
{
return false;
}
egl::Error DisplayWgpu::restoreLostDevice(const egl::Display *display)
{
return egl::NoError();
}
egl::Error DisplayWgpu::validateClientBuffer(const egl::Config *configuration,
EGLenum buftype,
EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs) const
{
switch (buftype)
{
case EGL_WEBGPU_TEXTURE_ANGLE:
return validateExternalWebGPUTexture(clientBuffer, attribs);
default:
return DisplayImpl::validateClientBuffer(configuration, buftype, clientBuffer, attribs);
}
}
egl::Error DisplayWgpu::validateImageClientBuffer(const gl::Context *context,
EGLenum target,
EGLClientBuffer clientBuffer,
const egl::AttributeMap &attribs) const
{
switch (target)
{
case EGL_WEBGPU_TEXTURE_ANGLE:
return validateExternalWebGPUTexture(clientBuffer, attribs);
default:
return DisplayImpl::validateImageClientBuffer(context, target, clientBuffer, attribs);
}
}
egl::Error DisplayWgpu::validateExternalWebGPUTexture(EGLClientBuffer buffer,
const egl::AttributeMap &attribs) const
{
WGPUTexture externalTexture = reinterpret_cast<WGPUTexture>(buffer);
if (externalTexture == nullptr)
{
return egl::Error(EGL_BAD_PARAMETER, "NULL Buffer");
}
WGPUTextureFormat externalTextureFormat = mProcTable.textureGetFormat(externalTexture);
const webgpu::Format *webgpuFormat =
getFormatForImportedTexture(attribs, externalTextureFormat);
if (webgpuFormat == nullptr)
{
return egl::Error(EGL_BAD_PARAMETER, "Invalid format.");
}
return egl::NoError();
}
bool DisplayWgpu::isValidNativeWindow(EGLNativeWindowType window) const
{
return true;
}
std::string DisplayWgpu::getRendererDescription()
{
return "WebGPU";
}
std::string DisplayWgpu::getVendorString()
{
return "WebGPU";
}
std::string DisplayWgpu::getVersionString(bool includeFullVersion)
{
return std::string();
}
DeviceImpl *DisplayWgpu::createDevice()
{
return new DeviceWgpu();
}
egl::Error DisplayWgpu::waitClient(const gl::Context *context)
{
return egl::NoError();
}
egl::Error DisplayWgpu::waitNative(const gl::Context *context, EGLint engine)
{
return egl::NoError();
}
gl::Version DisplayWgpu::getMaxSupportedESVersion() const
{
return mMaxSupportedClientVersion;
}
gl::Version DisplayWgpu::getMaxConformantESVersion() const
{
return mMaxSupportedClientVersion;
}
SurfaceImpl *DisplayWgpu::createWindowSurface(const egl::SurfaceState &state,
EGLNativeWindowType window,
const egl::AttributeMap &attribs)
{
return CreateWgpuWindowSurface(state, window);
}
SurfaceImpl *DisplayWgpu::createPbufferSurface(const egl::SurfaceState &state,
const egl::AttributeMap &attribs)
{
return new OffscreenSurfaceWgpu(state, EGL_NONE, nullptr);
}
SurfaceImpl *DisplayWgpu::createPbufferFromClientBuffer(const egl::SurfaceState &state,
EGLenum buftype,
EGLClientBuffer buffer,
const egl::AttributeMap &attribs)
{
return new OffscreenSurfaceWgpu(state, buftype, buffer);
}
SurfaceImpl *DisplayWgpu::createPixmapSurface(const egl::SurfaceState &state,
NativePixmapType nativePixmap,
const egl::AttributeMap &attribs)
{
UNIMPLEMENTED();
return nullptr;
}
ImageImpl *DisplayWgpu::createImage(const egl::ImageState &state,
const gl::Context *context,
EGLenum target,
const egl::AttributeMap &attribs)
{
return new ImageWgpu(state, context);
}
ExternalImageSiblingImpl *DisplayWgpu::createExternalImageSibling(const gl::Context *context,
EGLenum target,
EGLClientBuffer buffer,
const egl::AttributeMap &attribs)
{
switch (target)
{
case EGL_WEBGPU_TEXTURE_ANGLE:
return new WebGPUTextureImageSiblingWgpu(buffer, attribs);
default:
return DisplayImpl::createExternalImageSibling(context, target, buffer, attribs);
}
}
rx::ContextImpl *DisplayWgpu::createContext(const gl::State &state,
gl::ErrorSet *errorSet,
const egl::Config *configuration,
const gl::Context *shareContext,
const egl::AttributeMap &attribs)
{
return new ContextWgpu(state, errorSet, this);
}
StreamProducerImpl *DisplayWgpu::createStreamProducerD3DTexture(
egl::Stream::ConsumerType consumerType,
const egl::AttributeMap &attribs)
{
UNIMPLEMENTED();
return nullptr;
}
ShareGroupImpl *DisplayWgpu::createShareGroup(const egl::ShareGroupState &state)
{
return new ShareGroupWgpu(state);
}
void DisplayWgpu::populateFeatureList(angle::FeatureList *features)
{
mFeatures.populateFeatureList(features);
}
angle::NativeWindowSystem DisplayWgpu::getWindowSystem() const
{
return ANGLE_WEBGPU_WINDOW_SYSTEM;
}
const webgpu::Format *DisplayWgpu::getFormatForImportedTexture(const egl::AttributeMap &attribs,
WGPUTextureFormat wgpuFormat) const
{
GLenum requestedGLFormat = attribs.getAsInt(EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_NONE);
GLenum requestedGLType = attribs.getAsInt(EGL_TEXTURE_TYPE_ANGLE, GL_NONE);
if (requestedGLFormat != GL_NONE)
{
const gl::InternalFormat &internalFormat =
gl::GetInternalFormatInfo(requestedGLFormat, requestedGLType);
if (internalFormat.internalFormat == GL_NONE)
{
return nullptr;
}
const webgpu::Format &format = mFormatTable[internalFormat.sizedInternalFormat];
if (format.getActualWgpuTextureFormat() != wgpuFormat)
{
return nullptr;
}
return &format;
}
else
{
return mFormatTable.findClosestTextureFormat(wgpuFormat);
}
}
void DisplayWgpu::generateExtensions(egl::DisplayExtensions *outExtensions) const
{
*outExtensions = mEGLExtensions;
}
void DisplayWgpu::generateCaps(egl::Caps *outCaps) const
{
*outCaps = mEGLCaps;
}
void DisplayWgpu::initializeFeatures()
{
ApplyFeatureOverrides(&mFeatures, getState().featureOverrides);
if (mState.featureOverrides.allDisabled)
{
return;
}
// Disabled by default. Gets explicitly enabled by ANGLE embedders.
ANGLE_FEATURE_CONDITION((&mFeatures), avoidWaitAny, false);
}
egl::Error DisplayWgpu::createWgpuDevice()
{
if (!mInstance)
{
WGPUInstanceDescriptor instanceDescriptor = WGPU_INSTANCE_DESCRIPTOR_INIT;
static constexpr auto kTimedWaitAny = WGPUInstanceFeatureName_TimedWaitAny;
instanceDescriptor.requiredFeatureCount = 1;
instanceDescriptor.requiredFeatures = &kTimedWaitAny;
mInstance = webgpu::InstanceHandle::Acquire(&mProcTable,
mProcTable.createInstance(&instanceDescriptor));
}
struct RequestAdapterResult
{
WGPURequestAdapterStatus status;
webgpu::AdapterHandle adapter;
std::string message;
};
RequestAdapterResult adapterResult;
WGPURequestAdapterOptions requestAdapterOptions = WGPU_REQUEST_ADAPTER_OPTIONS_INIT;
WGPURequestAdapterCallbackInfo requestAdapterCallback = WGPU_REQUEST_ADAPTER_CALLBACK_INFO_INIT;
requestAdapterCallback.mode = WGPUCallbackMode_WaitAnyOnly;
requestAdapterCallback.callback = [](WGPURequestAdapterStatus status, WGPUAdapter adapter,
struct WGPUStringView message, void *userdata1,
void *userdata2) {
RequestAdapterResult *result = reinterpret_cast<RequestAdapterResult *>(userdata1);
const DawnProcTable *wgpu = reinterpret_cast<const DawnProcTable *>(userdata2);
result->status = status;
result->adapter = webgpu::AdapterHandle::Acquire(wgpu, adapter);
result->message = std::string(message.data, message.length);
};
requestAdapterCallback.userdata1 = &adapterResult;
requestAdapterCallback.userdata2 = &mProcTable;
WGPUFutureWaitInfo futureWaitInfo;
futureWaitInfo.future = mProcTable.instanceRequestAdapter(
mInstance.get(), &requestAdapterOptions, requestAdapterCallback);
WGPUWaitStatus status = mProcTable.instanceWaitAny(mInstance.get(), 1, &futureWaitInfo, -1);
if (webgpu::IsWgpuError(status))
{
std::ostringstream err;
err << "Failed to get WebGPU adapter: " << adapterResult.message;
return egl::Error(EGL_BAD_ALLOC, err.str());
}
mAdapter = adapterResult.adapter;
std::vector<WGPUFeatureName> requiredFeatures; // empty for now
WGPUDeviceDescriptor deviceDesc = WGPU_DEVICE_DESCRIPTOR_INIT;
deviceDesc.requiredFeatureCount = requiredFeatures.size();
deviceDesc.requiredFeatures = requiredFeatures.data();
deviceDesc.uncapturedErrorCallbackInfo.callback =
[](WGPUDevice const *device, WGPUErrorType type, struct WGPUStringView message,
void *userdata1, void *userdata2) {
ASSERT(userdata1 == nullptr);
ASSERT(userdata2 == nullptr);
ERR() << "Error: " << static_cast<std::underlying_type<WGPUErrorType>::type>(type)
<< " - message: " << std::string(message.data, message.length);
};
struct RequestDeviceResult
{
WGPURequestDeviceStatus status;
webgpu::DeviceHandle device;
std::string message;
};
RequestDeviceResult deviceResult;
WGPURequestDeviceCallbackInfo requestDeviceCallback = WGPU_REQUEST_DEVICE_CALLBACK_INFO_INIT;
requestDeviceCallback.mode = WGPUCallbackMode_WaitAnyOnly;
requestDeviceCallback.callback = [](WGPURequestDeviceStatus status, WGPUDevice device,
struct WGPUStringView message, void *userdata1,
void *userdata2) {
RequestDeviceResult *result = reinterpret_cast<RequestDeviceResult *>(userdata1);
const DawnProcTable *wgpu = reinterpret_cast<const DawnProcTable *>(userdata2);
result->status = status;
result->device = webgpu::DeviceHandle::Acquire(wgpu, device);
result->message = std::string(message.data, message.length);
};
requestDeviceCallback.userdata1 = &deviceResult;
requestDeviceCallback.userdata2 = &mProcTable;
futureWaitInfo.future =
mProcTable.adapterRequestDevice(mAdapter.get(), &deviceDesc, requestDeviceCallback);
status = mProcTable.instanceWaitAny(mInstance.get(), 1, &futureWaitInfo, -1);
if (webgpu::IsWgpuError(status))
{
std::ostringstream err;
err << "Failed to get WebGPU device: " << deviceResult.message;
return egl::Error(EGL_BAD_ALLOC, err.str());
}
mDevice = deviceResult.device;
return egl::NoError();
}
DisplayImpl *CreateWgpuDisplay(const egl::DisplayState &state)
{
return new DisplayWgpu(state);
}
} // namespace rx