| // 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. | |
| #ifdef UNSAFE_BUFFERS_BUILD | |
| // TODO(crbug.com/439062058): Remove this and convert code to safer constructs. | |
| #pragma allow_unsafe_buffers | |
| #endif | |
| #include "dawn/wire/client/Instance.h" | |
| #include <limits> | |
| #include <memory> | |
| #include <span> | |
| #include <string> | |
| #include <utility> | |
| #include "dawn/common/Log.h" | |
| #include "dawn/common/StringViewUtils.h" | |
| #include "dawn/common/WGSLFeatureMapping.h" | |
| #include "dawn/wire/client/ApiObjects_autogen.h" | |
| #include "dawn/wire/client/Client.h" | |
| #include "dawn/wire/client/EventManager.h" | |
| #include "dawn/wire/client/webgpu.h" | |
| #include "partition_alloc/pointers/raw_ptr.h" | |
| #include "tint/lang/wgsl/enums.h" | |
| #include "tint/lang/wgsl/feature_status.h" | |
| namespace dawn::wire::client { | |
| namespace { | |
| class RequestAdapterEvent : public TrackedEvent { | |
| public: | |
| static constexpr EventType kType = EventType::RequestAdapter; | |
| RequestAdapterEvent(const WGPURequestAdapterCallbackInfo& callbackInfo, Ref<Adapter> adapter) | |
| : TrackedEvent(callbackInfo.mode), | |
| mCallback(callbackInfo.callback), | |
| mUserdata1(callbackInfo.userdata1), | |
| mUserdata2(callbackInfo.userdata2), | |
| mAdapter(std::move(adapter)) {} | |
| EventType GetType() override { return kType; } | |
| WireResult ReadyHook(FutureID futureID, | |
| WGPURequestAdapterStatus status, | |
| WGPUStringView message, | |
| const WGPUAdapterInfo* info, | |
| const WGPULimits* limits, | |
| uint32_t featuresCount, | |
| const WGPUFeatureName* features) { | |
| DAWN_ASSERT(mAdapter != nullptr); | |
| mStatus = status; | |
| mMessage = ToString(message); | |
| if (status == WGPURequestAdapterStatus_Success) { | |
| mAdapter->SetInfo(info); | |
| mAdapter->SetLimits(limits); | |
| mAdapter->SetFeatures(features, featuresCount); | |
| } | |
| return WireResult::Success; | |
| } | |
| private: | |
| void CompleteImpl(FutureID futureID, EventCompletionType completionType) override { | |
| if (completionType == EventCompletionType::Shutdown) { | |
| mStatus = WGPURequestAdapterStatus_CallbackCancelled; | |
| mMessage = "A valid external Instance reference no longer exists."; | |
| } | |
| void* userdata1 = mUserdata1.ExtractAsDangling(); | |
| void* userdata2 = mUserdata2.ExtractAsDangling(); | |
| if (mCallback) { | |
| mCallback(mStatus, | |
| mStatus == WGPURequestAdapterStatus_Success ? ReturnToAPI(std::move(mAdapter)) | |
| : nullptr, | |
| ToOutputStringView(mMessage), userdata1, userdata2); | |
| } | |
| } | |
| WGPURequestAdapterCallback mCallback = 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. | |
| WGPURequestAdapterStatus mStatus; | |
| std::string mMessage; | |
| // The adapter is created when we call RequestAdapter(F). It is guaranteed to be alive | |
| // throughout the duration of a RequestAdapterEvent 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 adapter to the user who then takes ownership as the Event goes away. | |
| Ref<Adapter> mAdapter; | |
| }; | |
| WGPUWGSLLanguageFeatureName ToWGPUWGSLLanguageFeature(tint::wgsl::LanguageFeature f) { | |
| switch (f) { | |
| #define CASE(WgslName, WgpuName) \ | |
| case tint::wgsl::LanguageFeature::WgslName: \ | |
| return WGPUWGSLLanguageFeatureName_##WgpuName; | |
| DAWN_FOREACH_WGSL_FEATURE(CASE) | |
| #undef CASE | |
| case tint::wgsl::LanguageFeature::kUndefined: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| } // anonymous namespace | |
| static constexpr auto kSupportedFeatures = | |
| std::array<WGPUInstanceFeatureName, 1>{WGPUInstanceFeatureName_TimedWaitAny}; | |
| // Instance | |
| Instance::Instance(const ObjectBaseParams& params) | |
| : RefCountedWithExternalCount<ObjectWithEventsBase>(params, params.handle) {} | |
| void Instance::WillDropLastExternalRef() { | |
| if (IsRegistered()) { | |
| GetEventManager().TransitionTo(EventManager::State::InstanceDropped); | |
| } | |
| Unregister(); | |
| } | |
| ObjectType Instance::GetObjectType() const { | |
| return ObjectType::Instance; | |
| } | |
| WireResult Instance::Initialize(const WGPUInstanceDescriptor* descriptor) { | |
| if (descriptor == nullptr) { | |
| return WireResult::Success; | |
| } | |
| bool enabledTimedWaitAny = false; | |
| for (auto feature : std::span(descriptor->requiredFeatures, descriptor->requiredFeatureCount)) { | |
| if (std::find(kSupportedFeatures.begin(), kSupportedFeatures.end(), feature) == | |
| kSupportedFeatures.end()) { | |
| dawn::ErrorLog() << "Wire client doesn't support WGPUInstanceFeatureName(" << feature | |
| << ")"; | |
| return WireResult::FatalError; | |
| } | |
| if (feature == WGPUInstanceFeatureName_TimedWaitAny) { | |
| enabledTimedWaitAny = true; | |
| } | |
| } | |
| if (descriptor->requiredLimits) { | |
| if (descriptor->requiredLimits->nextInChain != nullptr) { | |
| dawn::ErrorLog() << "Wire client doesn't support any WGPUInstanceLimits extensions"; | |
| return WireResult::FatalError; | |
| } | |
| if (!enabledTimedWaitAny && descriptor->requiredLimits->timedWaitAnyMaxCount > 0) { | |
| dawn::ErrorLog() << "Wire client doesn't support non-zero timedWaitAnyMaxCount if " | |
| "WGPUInstanceFeatureName_TimedWaitAny is not enabled."; | |
| return WireResult::FatalError; | |
| } | |
| } | |
| const WGPUDawnWireWGSLControl* wgslControl = nullptr; | |
| const WGPUDawnWGSLBlocklist* wgslBlocklist = nullptr; | |
| for (const WGPUChainedStruct* chain = descriptor->nextInChain; chain != nullptr; | |
| chain = chain->next) { | |
| switch (chain->sType) { | |
| case WGPUSType_DawnWireWGSLControl: | |
| wgslControl = reinterpret_cast<const WGPUDawnWireWGSLControl*>(chain); | |
| break; | |
| case WGPUSType_DawnWGSLBlocklist: | |
| wgslBlocklist = reinterpret_cast<const WGPUDawnWGSLBlocklist*>(chain); | |
| break; | |
| default: | |
| dawn::ErrorLog() << "Wire client instance doesn't support InstanceDescriptor " | |
| "extension structure with sType (" | |
| << chain->sType << ")"; | |
| return WireResult::FatalError; | |
| } | |
| } | |
| GatherWGSLFeatures(wgslControl, wgslBlocklist); | |
| return WireResult::Success; | |
| } | |
| WGPUFuture Instance::APIRequestAdapter(const WGPURequestAdapterOptions* options, | |
| const WGPURequestAdapterCallbackInfo& callbackInfo) { | |
| Client* client = GetClient(); | |
| Ref<Adapter> adapter = client->Make<Adapter>(GetEventManagerHandle()); | |
| auto [futureIDInternal, tracked] = | |
| GetEventManager().TrackEvent(AcquireRef(new RequestAdapterEvent(callbackInfo, adapter))); | |
| if (!tracked) { | |
| return {futureIDInternal}; | |
| } | |
| InstanceRequestAdapterCmd cmd; | |
| cmd.instanceId = GetWireHandle(client).id; | |
| cmd.eventManagerHandle = GetEventManagerHandle(); | |
| cmd.future = {futureIDInternal}; | |
| cmd.adapterObjectHandle = adapter->GetWireHandle(client); | |
| cmd.options = options; | |
| client->SerializeCommand(cmd); | |
| return {futureIDInternal}; | |
| } | |
| WireResult Client::DoInstanceRequestAdapterCallback(ObjectHandle eventManager, | |
| WGPUFuture future, | |
| WGPURequestAdapterStatus status, | |
| WGPUStringView message, | |
| const WGPUAdapterInfo* info, | |
| const WGPULimits* limits, | |
| uint32_t featuresCount, | |
| const WGPUFeatureName* features) { | |
| return SetFutureReady<RequestAdapterEvent>(eventManager, future.id, status, message, info, | |
| limits, featuresCount, features); | |
| } | |
| void Instance::APIProcessEvents() { | |
| GetEventManager().ProcessPollEvents(); | |
| } | |
| WGPUWaitStatus Instance::APIWaitAny(size_t count, WGPUFutureWaitInfo* infos, uint64_t timeoutNS) { | |
| return GetEventManager().WaitAny(count, infos, timeoutNS); | |
| } | |
| void Instance::GatherWGSLFeatures(const WGPUDawnWireWGSLControl* wgslControl, | |
| const WGPUDawnWGSLBlocklist* wgslBlocklist) { | |
| WGPUDawnWireWGSLControl defaultWgslControl{}; | |
| if (wgslControl == nullptr) { | |
| wgslControl = &defaultWgslControl; | |
| } | |
| for (auto wgslFeature : tint::wgsl::kAllLanguageFeatures) { | |
| // Skip over testing features if we don't have the toggle to expose them. | |
| if (!wgslControl->enableTesting) { | |
| switch (wgslFeature) { | |
| case tint::wgsl::LanguageFeature::kChromiumTestingUnimplemented: | |
| case tint::wgsl::LanguageFeature::kChromiumTestingUnsafeExperimental: | |
| case tint::wgsl::LanguageFeature::kChromiumTestingExperimental: | |
| case tint::wgsl::LanguageFeature::kChromiumTestingShippedWithKillswitch: | |
| case tint::wgsl::LanguageFeature::kChromiumTestingShipped: | |
| continue; | |
| default: | |
| break; | |
| } | |
| } | |
| // Expose the feature depending on its status and wgslControl. | |
| bool enable = false; | |
| switch (tint::wgsl::GetLanguageFeatureStatus(wgslFeature)) { | |
| case tint::wgsl::FeatureStatus::kUnknown: | |
| case tint::wgsl::FeatureStatus::kUnimplemented: | |
| enable = false; | |
| break; | |
| case tint::wgsl::FeatureStatus::kUnsafeExperimental: | |
| enable = wgpu::Bool(wgslControl->enableUnsafe); | |
| break; | |
| case tint::wgsl::FeatureStatus::kExperimental: | |
| enable = wgpu::Bool(wgslControl->enableExperimental); | |
| break; | |
| case tint::wgsl::FeatureStatus::kShippedWithKillswitch: | |
| case tint::wgsl::FeatureStatus::kShipped: | |
| enable = true; | |
| break; | |
| } | |
| if (enable && wgslFeature != tint::wgsl::LanguageFeature::kUndefined) { | |
| mWGSLFeatures.emplace(ToWGPUWGSLLanguageFeature(wgslFeature)); | |
| } | |
| } | |
| // Remove blocklisted features. | |
| if (wgslBlocklist != nullptr) { | |
| for (size_t i = 0; i < wgslBlocklist->blocklistedFeatureCount; i++) { | |
| const char* name = wgslBlocklist->blocklistedFeatures[i]; | |
| tint::wgsl::LanguageFeature tintFeature = tint::wgsl::ParseLanguageFeature(name); | |
| if (tintFeature == tint::wgsl::LanguageFeature::kUndefined) { | |
| // Ignore unknown features in the blocklist. | |
| continue; | |
| } | |
| mWGSLFeatures.erase(ToWGPUWGSLLanguageFeature(tintFeature)); | |
| } | |
| } | |
| } | |
| bool Instance::APIHasWGSLLanguageFeature(WGPUWGSLLanguageFeatureName feature) const { | |
| return mWGSLFeatures.contains(feature); | |
| } | |
| void Instance::APIGetWGSLLanguageFeatures(WGPUSupportedWGSLLanguageFeatures* features) const { | |
| DAWN_ASSERT(features != nullptr); | |
| size_t featureCount = mWGSLFeatures.size(); | |
| WGPUWGSLLanguageFeatureName* wgslFeatures = new WGPUWGSLLanguageFeatureName[featureCount]; | |
| uint32_t index = 0; | |
| for (WGPUWGSLLanguageFeatureName feature : mWGSLFeatures) { | |
| wgslFeatures[index++] = feature; | |
| } | |
| DAWN_ASSERT(index == featureCount); | |
| features->featureCount = featureCount; | |
| features->features = wgslFeatures; | |
| } | |
| WGPUSurface Instance::APICreateSurface(const WGPUSurfaceDescriptor* desc) const { | |
| dawn::ErrorLog() << "Instance::CreateSurface is not supported in the wire. Use " | |
| "dawn::wire::client::WireClient::InjectSurface instead."; | |
| return nullptr; | |
| } | |
| void APIFreeMembers(WGPUSupportedWGSLLanguageFeatures supportedFeatures) { | |
| delete[] supportedFeatures.features; | |
| } | |
| void APIFreeMembers(WGPUSupportedInstanceFeatures supportedFeatures) { | |
| // Nothing to do, supportedFeatures.features is statically allocated. | |
| } | |
| } // namespace dawn::wire::client | |
| // Free-standing API functions | |
| DAWN_WIRE_EXPORT WGPUStatus wgpuDawnWireClientGetInstanceLimits(WGPUInstanceLimits* limits) { | |
| DAWN_ASSERT(limits != nullptr); | |
| if (limits->nextInChain != nullptr) { | |
| dawn::ErrorLog() << "Wire client doesn't support any WGPUInstanceLimits extensions"; | |
| return WGPUStatus_Error; | |
| } | |
| limits->timedWaitAnyMaxCount = std::numeric_limits<size_t>::max(); | |
| return WGPUStatus_Success; | |
| } | |
| DAWN_WIRE_EXPORT WGPUBool wgpuDawnWireClientHasInstanceFeature(WGPUInstanceFeatureName feature) { | |
| return std::find(dawn::wire::client::kSupportedFeatures.begin(), | |
| dawn::wire::client::kSupportedFeatures.end(), | |
| feature) != dawn::wire::client::kSupportedFeatures.end(); | |
| } | |
| DAWN_WIRE_EXPORT void wgpuDawnWireClientGetInstanceFeatures( | |
| WGPUSupportedInstanceFeatures* features) { | |
| DAWN_ASSERT(features != nullptr); | |
| features->featureCount = dawn::wire::client::kSupportedFeatures.size(); | |
| features->features = dawn::wire::client::kSupportedFeatures.data(); | |
| } | |
| DAWN_WIRE_EXPORT WGPUInstance | |
| wgpuDawnWireClientCreateInstance(WGPUInstanceDescriptor const* descriptor) { | |
| // Not implemented. Wire currently must be created from an existing server side instance. | |
| DAWN_CHECK(false); | |
| return nullptr; | |
| } |