| // 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 "src/dawn/wire/client/Instance.h" | |
| #include <algorithm> | |
| #include <limits> | |
| #include <memory> | |
| #include <span> | |
| #include <string> | |
| #include <utility> | |
| #include "dawn/wire/client/ApiObjects_autogen.h" | |
| #include "dawn/wire/client/webgpu.h" | |
| #include "partition_alloc/pointers/raw_ptr.h" | |
| #include "src/dawn/common/StringViewUtils.h" | |
| #include "src/dawn/common/WGSLFeatureMapping.h" | |
| #include "src/dawn/wire/client/Client.h" | |
| #include "src/dawn/wire/client/EventManager.h" | |
| #include "src/utils/compiler.h" | |
| #include "src/utils/log.h" | |
| #include "tint/tint.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, | |
| wgpu::RequestAdapterStatus status, | |
| StringView message, | |
| const AdapterInfo* info, | |
| const Limits* limits, | |
| Span<const wgpu::FeatureName> features) { | |
| DAWN_ASSERT(mAdapter != nullptr); | |
| mStatus = status; | |
| mMessage = message; | |
| if (status == wgpu::RequestAdapterStatus::Success) { | |
| mAdapter->SetInfo(info); | |
| mAdapter->SetLimits(limits); | |
| mAdapter->SetFeatures(features); | |
| } | |
| return WireResult::Success; | |
| } | |
| private: | |
| void CompleteImpl(FutureID futureID, EventCompletionType completionType) override { | |
| if (completionType == EventCompletionType::Shutdown) { | |
| mStatus = wgpu::RequestAdapterStatus::CallbackCancelled; | |
| mMessage = "A valid external Instance reference no longer exists."; | |
| } | |
| void* userdata1 = mUserdata1.ExtractAsDangling(); | |
| void* userdata2 = mUserdata2.ExtractAsDangling(); | |
| if (mCallback) { | |
| mCallback(ToAPI(mStatus), | |
| mStatus == wgpu::RequestAdapterStatus::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. | |
| wgpu::RequestAdapterStatus mStatus = wgpu::RequestAdapterStatus::Success; | |
| 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; | |
| }; | |
| wgpu::WGSLLanguageFeatureName ToWGPUWGSLLanguageFeature(tint::wgsl::LanguageFeature f) { | |
| switch (f) { | |
| #define CASE(WgslName, WgpuName) \ | |
| case tint::wgsl::LanguageFeature::WgslName: \ | |
| return wgpu::WGSLLanguageFeatureName::WgpuName; | |
| DAWN_FOREACH_WGSL_FEATURE(CASE) | |
| #undef CASE | |
| case tint::wgsl::LanguageFeature::kUndefined: | |
| DAWN_UNREACHABLE(); | |
| } | |
| } | |
| } // anonymous namespace | |
| static constexpr auto kSupportedFeatures = | |
| std::array<wgpu::InstanceFeatureName, 1>{wgpu::InstanceFeatureName::TimedWaitAny}; | |
| // Instance | |
| Instance::Instance(const ObjectBaseParams& params) | |
| : ObjectBase(params), mEventManager(std::make_unique<EventManager>(0)) {} | |
| ObjectType Instance::GetObjectType() const { | |
| return ObjectType::Instance; | |
| } | |
| EventManager& Instance::GetEventManager() const { | |
| DAWN_ASSERT(mEventManager != nullptr); | |
| return *mEventManager; | |
| } | |
| WireResult Instance::Initialize(const InstanceDescriptor* descriptor) { | |
| size_t timedWaitAnyMaxCount = 0; | |
| if (descriptor != nullptr) { | |
| bool enabledTimedWaitAny = false; | |
| for (auto feature : descriptor->requiredFeatures) { | |
| if (std::find(kSupportedFeatures.begin(), kSupportedFeatures.end(), feature) == | |
| kSupportedFeatures.end()) { | |
| dawn::ErrorLog() << "Wire client doesn't support WGPUInstanceFeatureName(" | |
| << ToAPI(feature) << ")"; | |
| return WireResult::FatalError; | |
| } | |
| if (feature == wgpu::InstanceFeatureName::TimedWaitAny) { | |
| enabledTimedWaitAny = true; | |
| } | |
| } | |
| if (enabledTimedWaitAny) { | |
| if (descriptor->requiredLimits) { | |
| timedWaitAnyMaxCount = descriptor->requiredLimits->timedWaitAnyMaxCount; | |
| } | |
| timedWaitAnyMaxCount = std::max(timedWaitAnyMaxCount, kTimedWaitAnyMaxCountDefault); | |
| } | |
| 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 DawnWireWGSLControl* wgslControl = nullptr; | |
| const DawnWGSLBlocklist* wgslBlocklist = nullptr; | |
| for (const auto* chain = descriptor->nextInChain; chain; chain = chain->nextInChain) { | |
| switch (chain->sType) { | |
| case wgpu::SType::DawnWireWGSLControl: | |
| wgslControl = reinterpret_cast<const DawnWireWGSLControl*>(chain); | |
| break; | |
| case wgpu::SType::DawnWGSLBlocklist: | |
| wgslBlocklist = reinterpret_cast<const DawnWGSLBlocklist*>(chain); | |
| break; | |
| default: | |
| // TODO(https://crbug.com/526537254): Use the cpp_print helpers once we | |
| // deduplicate some of the autogen code w.r.t native. | |
| dawn::ErrorLog() << "Wire client instance doesn't support InstanceDescriptor " | |
| "extension structure with sType (" | |
| << static_cast<uint32_t>(chain->sType) << ")"; | |
| return WireResult::FatalError; | |
| } | |
| } | |
| GatherWGSLFeatures(wgslControl, wgslBlocklist); | |
| } | |
| mEventManager = std::make_unique<EventManager>(timedWaitAnyMaxCount); | |
| return WireResult::Success; | |
| } | |
| Future Instance::APIRequestAdapter(const RequestAdapterOptions* options, | |
| const WGPURequestAdapterCallbackInfo& callbackInfo) { | |
| Client* client = GetClient(); | |
| Ref<Adapter> adapter = client->Make<Adapter>(this); | |
| auto [futureIDInternal, tracked] = | |
| GetEventManager().TrackEvent(AcquireRef(new RequestAdapterEvent(callbackInfo, adapter))); | |
| if (!tracked) { | |
| return {futureIDInternal}; | |
| } | |
| InstanceRequestAdapterCmd cmd; | |
| cmd.instanceId = GetWireHandle(client).id; | |
| cmd.future = {futureIDInternal}; | |
| cmd.adapterObjectHandle = adapter->GetWireHandle(client); | |
| cmd.options = ToWireCmd(options); | |
| client->SerializeCommand(std::move(cmd)); | |
| return {futureIDInternal}; | |
| } | |
| WireResult Client::DoInstanceRequestAdapterCallback(ObjectId instanceId, | |
| Future future, | |
| wgpu::RequestAdapterStatus status, | |
| StringView message, | |
| const AdapterInfo* info, | |
| const Limits* limits, | |
| Span<const wgpu::FeatureName> features) { | |
| return SetFutureReady<RequestAdapterEvent>(instanceId, future.id, status, message, info, limits, | |
| features); | |
| } | |
| void Instance::APIProcessEvents() { | |
| GetEventManager().ProcessPollEvents(); | |
| } | |
| wgpu::WaitStatus Instance::APIWaitAny(Span<FutureWaitInfo> infos, uint64_t timeoutNS) { | |
| return GetEventManager().WaitAny(infos, timeoutNS); | |
| } | |
| void Instance::GatherWGSLFeatures(const DawnWireWGSLControl* wgslControl, | |
| const DawnWGSLBlocklist* wgslBlocklist) { | |
| DawnWireWGSLControl 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 (const char* name : wgslBlocklist->blocklistedFeatures) { | |
| tint::wgsl::LanguageFeature tintFeature = tint::wgsl::ParseLanguageFeature(name); | |
| if (tintFeature == tint::wgsl::LanguageFeature::kUndefined) { | |
| // Ignore unknown features in the blocklist. | |
| continue; | |
| } | |
| if (tint::wgsl::GetLanguageFeatureStatus(tintFeature) != | |
| tint::wgsl::FeatureStatus::kShipped) { | |
| mWGSLFeatures.erase(ToWGPUWGSLLanguageFeature(tintFeature)); | |
| } | |
| } | |
| } | |
| } | |
| bool Instance::APIHasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName feature) const { | |
| return mWGSLFeatures.contains(feature); | |
| } | |
| void Instance::APIGetWGSLLanguageFeatures(SupportedWGSLLanguageFeatures* features) const { | |
| DAWN_ASSERT(features != nullptr); | |
| features->features = HeapArrayFrom(mWGSLFeatures).MoveToSpan(); | |
| } | |
| Surface* Instance::APICreateSurface(const SurfaceDescriptor* desc) const { | |
| dawn::ErrorLog() << "Instance::CreateSurface is not supported in the wire. Use " | |
| "dawn::wire::client::WireClient::InjectSurface instead."; | |
| return nullptr; | |
| } | |
| void APISupportedWGSLLanguageFeaturesFreeMembers( | |
| WGPUSupportedWGSLLanguageFeatures supportedFeatures) { | |
| DeleteAllocationFromHeapArray(supportedFeatures.features); | |
| } | |
| void APISupportedInstanceFeaturesFreeMembers(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(), | |
| dawn::wire::client::FromAPI(feature)) != | |
| dawn::wire::client::kSupportedFeatures.end(); | |
| } | |
| DAWN_WIRE_EXPORT void wgpuDawnWireClientGetInstanceFeatures( | |
| WGPUSupportedInstanceFeatures* features) { | |
| DAWN_ASSERT(features != nullptr); | |
| dawn::wire::client::FromAPI(features)->features = dawn::wire::client::kSupportedFeatures; | |
| } | |
| DAWN_WIRE_EXPORT WGPUInstance | |
| wgpuDawnWireClientCreateInstance(WGPUInstanceDescriptor const* descriptor) { | |
| // Not implemented. Wire currently must be created from an existing server side instance. | |
| DAWN_UNREACHABLE(); | |
| return nullptr; | |
| } |