| // |
| // 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 |