| // Copyright 2021 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/wire/client/Adapter.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/types/span.h" // TODO(343500108): Use std::span when we have C++20. |
| #include "dawn/common/Log.h" |
| #include "dawn/common/StringViewUtils.h" |
| #include "dawn/wire/client/Client.h" |
| #include "dawn/wire/client/webgpu.h" |
| #include "partition_alloc/pointers/raw_ptr.h" |
| |
| namespace dawn::wire::client { |
| namespace { |
| |
| class RequestDeviceEvent : public TrackedEvent { |
| public: |
| static constexpr EventType kType = EventType::RequestDevice; |
| |
| RequestDeviceEvent(const WGPURequestDeviceCallbackInfo& callbackInfo, Ref<Device> device) |
| : TrackedEvent(callbackInfo.mode), |
| mCallback(callbackInfo.callback), |
| mUserdata1(callbackInfo.userdata), |
| mDevice(std::move(device)) {} |
| |
| RequestDeviceEvent(const WGPURequestDeviceCallbackInfo2& callbackInfo, Ref<Device> device) |
| : TrackedEvent(callbackInfo.mode), |
| mCallback2(callbackInfo.callback), |
| mUserdata1(callbackInfo.userdata1), |
| mUserdata2(callbackInfo.userdata2), |
| mDevice(std::move(device)) {} |
| |
| EventType GetType() override { return kType; } |
| |
| WireResult ReadyHook(FutureID futureID, |
| WGPURequestDeviceStatus status, |
| WGPUStringView message, |
| const WGPUSupportedLimits* limits, |
| uint32_t featuresCount, |
| const WGPUFeatureName* features) { |
| DAWN_ASSERT(mDevice != nullptr); |
| mStatus = status; |
| mMessage = ToString(message); |
| if (status == WGPURequestDeviceStatus_Success) { |
| mDevice->SetLimits(limits); |
| mDevice->SetFeatures(features, featuresCount); |
| } |
| return WireResult::Success; |
| } |
| |
| private: |
| void CompleteImpl(FutureID futureID, EventCompletionType completionType) override { |
| if (completionType == EventCompletionType::Shutdown) { |
| mStatus = WGPURequestDeviceStatus_InstanceDropped; |
| mMessage = "A valid external Instance reference no longer exists."; |
| } |
| |
| // Callback needs to happen before device lost handling to ensure resolution order. |
| if (mCallback) { |
| Ref<Device> device = mDevice; |
| mCallback(mStatus, |
| mStatus == WGPURequestDeviceStatus_Success ? ReturnToAPI(std::move(device)) |
| : nullptr, |
| ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling()); |
| } else if (mCallback2) { |
| Ref<Device> device = mDevice; |
| mCallback2(mStatus, |
| mStatus == WGPURequestDeviceStatus_Success ? ReturnToAPI(std::move(device)) |
| : nullptr, |
| ToOutputStringView(mMessage), mUserdata1.ExtractAsDangling(), |
| mUserdata2.ExtractAsDangling()); |
| } |
| |
| if (mStatus != WGPURequestDeviceStatus_Success) { |
| // If there was an error and we didn't return a device, we need to call the device lost |
| // callback and reclaim the device allocation. |
| if (mStatus == WGPURequestDeviceStatus_InstanceDropped) { |
| mDevice->HandleDeviceLost( |
| WGPUDeviceLostReason_InstanceDropped, |
| ToOutputStringView("A valid external Instance reference no longer exists.")); |
| } else { |
| mDevice->HandleDeviceLost(WGPUDeviceLostReason_FailedCreation, |
| ToOutputStringView("Device failed at creation.")); |
| } |
| } |
| |
| if (mCallback == nullptr && mCallback2 == nullptr) { |
| // If there's no callback, clean up the resources. |
| mUserdata1.ExtractAsDangling(); |
| mUserdata2.ExtractAsDangling(); |
| } |
| } |
| |
| WGPURequestDeviceCallback mCallback = nullptr; |
| WGPURequestDeviceCallback2 mCallback2 = nullptr; |
| raw_ptr<void> mUserdata1; |
| raw_ptr<void> mUserdata2; |
| |
| // Note that the message is optional because we want to return nullptr when it wasn't set |
| // instead of a pointer to an empty string. |
| WGPURequestDeviceStatus mStatus; |
| std::string mMessage; |
| |
| // The device is created when we call RequestDevice(F). It is guaranteed to be alive |
| // throughout the duration of a RequestDeviceEvent because the Event essentially takes |
| // ownership of it until either an error occurs at which point the Event cleans it up, or it |
| // returns the device to the user who then takes ownership as the Event goes away. |
| Ref<Device> mDevice; |
| }; |
| |
| } // anonymous namespace |
| |
| ObjectType Adapter::GetObjectType() const { |
| return ObjectType::Adapter; |
| } |
| |
| WGPUStatus Adapter::GetLimits(WGPUSupportedLimits* limits) const { |
| return mLimitsAndFeatures.GetLimits(limits); |
| } |
| |
| bool Adapter::HasFeature(WGPUFeatureName feature) const { |
| return mLimitsAndFeatures.HasFeature(feature); |
| } |
| |
| size_t Adapter::EnumerateFeatures(WGPUFeatureName* features) const { |
| return mLimitsAndFeatures.EnumerateFeatures(features); |
| } |
| |
| void Adapter::SetLimits(const WGPUSupportedLimits* limits) { |
| return mLimitsAndFeatures.SetLimits(limits); |
| } |
| |
| void Adapter::SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount) { |
| return mLimitsAndFeatures.SetFeatures(features, featuresCount); |
| } |
| |
| void Adapter::SetInfo(const WGPUAdapterInfo* info) { |
| mInfo = *info; |
| |
| // Deep copy the string pointed out by info. StringViews are all explicitly sized by the wire. |
| mVendor = ToString(info->vendor); |
| mInfo.vendor = ToOutputStringView(mVendor); |
| mArchitecture = ToString(info->architecture); |
| mInfo.architecture = ToOutputStringView(mArchitecture); |
| mDeviceName = ToString(info->device); |
| mInfo.device = ToOutputStringView(mDeviceName); |
| mDescription = ToString(info->description); |
| mInfo.description = ToOutputStringView(mDescription); |
| |
| mInfo.nextInChain = nullptr; |
| |
| // Loop through the chained struct. |
| WGPUChainedStructOut* chain = info->nextInChain; |
| while (chain != nullptr) { |
| switch (chain->sType) { |
| case WGPUSType_AdapterPropertiesMemoryHeaps: { |
| // Make a copy of the heap info in `mMemoryHeapInfo`. |
| const auto* memoryHeapProperties = |
| reinterpret_cast<const WGPUAdapterPropertiesMemoryHeaps*>(chain); |
| mMemoryHeapInfo = { |
| memoryHeapProperties->heapInfo, |
| memoryHeapProperties->heapInfo + memoryHeapProperties->heapCount}; |
| break; |
| } |
| case WGPUSType_AdapterPropertiesD3D: { |
| auto* d3dProperties = reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain); |
| mD3DProperties.shaderModel = d3dProperties->shaderModel; |
| break; |
| } |
| case WGPUSType_AdapterPropertiesVk: { |
| auto* vkProperties = reinterpret_cast<WGPUAdapterPropertiesVk*>(chain); |
| mVkProperties.driverVersion = vkProperties->driverVersion; |
| break; |
| } |
| default: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| chain = chain->next; |
| } |
| } |
| |
| WGPUStatus Adapter::GetInfo(WGPUAdapterInfo* info) const { |
| // Loop through the chained struct. |
| WGPUChainedStructOut* chain = info->nextInChain; |
| while (chain != nullptr) { |
| switch (chain->sType) { |
| case WGPUSType_AdapterPropertiesMemoryHeaps: { |
| // Copy `mMemoryHeapInfo` into a new allocation. |
| auto* memoryHeapProperties = |
| reinterpret_cast<WGPUAdapterPropertiesMemoryHeaps*>(chain); |
| size_t heapCount = mMemoryHeapInfo.size(); |
| auto* heapInfo = new WGPUMemoryHeapInfo[heapCount]; |
| memcpy(heapInfo, mMemoryHeapInfo.data(), sizeof(WGPUMemoryHeapInfo) * heapCount); |
| // Write out the pointer and count to the heap properties out-struct. |
| memoryHeapProperties->heapCount = heapCount; |
| memoryHeapProperties->heapInfo = heapInfo; |
| break; |
| } |
| case WGPUSType_AdapterPropertiesD3D: { |
| auto* d3dProperties = reinterpret_cast<WGPUAdapterPropertiesD3D*>(chain); |
| d3dProperties->shaderModel = mD3DProperties.shaderModel; |
| break; |
| } |
| case WGPUSType_AdapterPropertiesVk: { |
| auto* vkProperties = reinterpret_cast<WGPUAdapterPropertiesVk*>(chain); |
| vkProperties->driverVersion = mVkProperties.driverVersion; |
| break; |
| } |
| default: |
| break; |
| } |
| chain = chain->next; |
| } |
| |
| *info = mInfo; |
| |
| // Allocate space for all strings. |
| size_t allocSize = |
| mVendor.length() + mArchitecture.length() + mDeviceName.length() + mDescription.length(); |
| absl::Span<char> outBuffer{new char[allocSize], allocSize}; |
| |
| auto AddString = [&](const std::string& in, WGPUStringView* out) { |
| DAWN_ASSERT(in.length() <= outBuffer.length()); |
| memcpy(outBuffer.data(), in.data(), in.length()); |
| *out = {outBuffer.data(), in.length()}; |
| outBuffer = outBuffer.subspan(in.length()); |
| }; |
| |
| AddString(mVendor, &info->vendor); |
| AddString(mArchitecture, &info->architecture); |
| AddString(mDeviceName, &info->device); |
| AddString(mDescription, &info->description); |
| DAWN_ASSERT(outBuffer.empty()); |
| |
| return WGPUStatus_Success; |
| } |
| |
| void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor, |
| WGPURequestDeviceCallback callback, |
| void* userdata) { |
| WGPURequestDeviceCallbackInfo callbackInfo = {}; |
| callbackInfo.mode = WGPUCallbackMode_AllowSpontaneous; |
| callbackInfo.callback = callback; |
| callbackInfo.userdata = userdata; |
| RequestDeviceF(descriptor, callbackInfo); |
| } |
| |
| WGPUFuture Adapter::RequestDeviceF(const WGPUDeviceDescriptor* descriptor, |
| const WGPURequestDeviceCallbackInfo& callbackInfo) { |
| Client* client = GetClient(); |
| Ref<Device> device = client->Make<Device>(GetEventManagerHandle(), this, descriptor); |
| auto [futureIDInternal, tracked] = |
| GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(callbackInfo, device)); |
| if (!tracked) { |
| return {futureIDInternal}; |
| } |
| |
| // Ensure callbacks are not serialized as part of the command, as they cannot be passed between |
| // processes. |
| WGPUDeviceDescriptor wireDescriptor = {}; |
| if (descriptor) { |
| wireDescriptor = *descriptor; |
| wireDescriptor.deviceLostCallback = nullptr; |
| wireDescriptor.deviceLostUserdata = nullptr; |
| wireDescriptor.deviceLostCallbackInfo.callback = nullptr; |
| wireDescriptor.deviceLostCallbackInfo.userdata = nullptr; |
| wireDescriptor.uncapturedErrorCallbackInfo.callback = nullptr; |
| wireDescriptor.uncapturedErrorCallbackInfo.userdata = nullptr; |
| } |
| |
| AdapterRequestDeviceCmd cmd; |
| cmd.adapterId = GetWireId(); |
| cmd.eventManagerHandle = GetEventManagerHandle(); |
| cmd.future = {futureIDInternal}; |
| cmd.deviceObjectHandle = device->GetWireHandle(); |
| cmd.deviceLostFuture = device->GetDeviceLostFuture(); |
| cmd.descriptor = &wireDescriptor; |
| cmd.userdataCount = 1; |
| |
| client->SerializeCommand(cmd); |
| return {futureIDInternal}; |
| } |
| |
| WGPUFuture Adapter::RequestDevice2(const WGPUDeviceDescriptor* descriptor, |
| const WGPURequestDeviceCallbackInfo2& callbackInfo) { |
| Client* client = GetClient(); |
| Ref<Device> device = client->Make<Device>(GetEventManagerHandle(), this, descriptor); |
| auto [futureIDInternal, tracked] = |
| GetEventManager().TrackEvent(std::make_unique<RequestDeviceEvent>(callbackInfo, device)); |
| if (!tracked) { |
| return {futureIDInternal}; |
| } |
| |
| // Ensure callbacks are not serialized as part of the command, as they cannot be passed between |
| // processes. |
| WGPUDeviceDescriptor wireDescriptor = {}; |
| if (descriptor) { |
| wireDescriptor = *descriptor; |
| wireDescriptor.deviceLostCallback = nullptr; |
| wireDescriptor.deviceLostUserdata = nullptr; |
| wireDescriptor.deviceLostCallbackInfo.callback = nullptr; |
| wireDescriptor.deviceLostCallbackInfo.userdata = nullptr; |
| wireDescriptor.uncapturedErrorCallbackInfo.callback = nullptr; |
| wireDescriptor.uncapturedErrorCallbackInfo.userdata = nullptr; |
| } |
| |
| AdapterRequestDeviceCmd cmd; |
| cmd.adapterId = GetWireId(); |
| cmd.eventManagerHandle = GetEventManagerHandle(); |
| cmd.future = {futureIDInternal}; |
| cmd.deviceObjectHandle = device->GetWireHandle(); |
| cmd.deviceLostFuture = device->GetDeviceLostFuture(); |
| cmd.descriptor = &wireDescriptor; |
| cmd.userdataCount = 2; |
| |
| client->SerializeCommand(cmd); |
| return {futureIDInternal}; |
| } |
| |
| WireResult Client::DoAdapterRequestDeviceCallback(ObjectHandle eventManager, |
| WGPUFuture future, |
| WGPURequestDeviceStatus status, |
| WGPUStringView message, |
| const WGPUSupportedLimits* limits, |
| uint32_t featuresCount, |
| const WGPUFeatureName* features) { |
| return GetEventManager(eventManager) |
| .SetFutureReady<RequestDeviceEvent>(future.id, status, message, limits, featuresCount, |
| features); |
| } |
| |
| WGPUInstance Adapter::GetInstance() const { |
| dawn::ErrorLog() << "adapter.GetInstance not supported with dawn_wire."; |
| return nullptr; |
| } |
| |
| WGPUDevice Adapter::CreateDevice(const WGPUDeviceDescriptor*) { |
| dawn::ErrorLog() << "adapter.CreateDevice not supported with dawn_wire."; |
| return nullptr; |
| } |
| |
| WGPUStatus Adapter::GetFormatCapabilities(WGPUTextureFormat format, |
| WGPUFormatCapabilities* capabilities) { |
| dawn::ErrorLog() << "adapter.GetFormatCapabilities not supported with dawn_wire."; |
| return WGPUStatus_Error; |
| } |
| |
| } // namespace dawn::wire::client |
| |
| DAWN_WIRE_EXPORT void wgpuDawnWireClientAdapterInfoFreeMembers(WGPUAdapterInfo info) { |
| // This single delete is enough because everything is a single allocation. |
| delete[] info.vendor.data; |
| } |
| |
| DAWN_WIRE_EXPORT void wgpuDawnWireClientAdapterPropertiesMemoryHeapsFreeMembers( |
| WGPUAdapterPropertiesMemoryHeaps memoryHeapProperties) { |
| delete[] memoryHeapProperties.heapInfo; |
| } |
| |
| DAWN_WIRE_EXPORT void wgpuDawnWireClientDrmFormatCapabilitiesFreeMembers( |
| WGPUDrmFormatCapabilities capabilities) { |
| delete[] capabilities.properties; |
| } |