blob: 4e4a9bc9aadd1fcd35a65b64f55f793bd335193c [file]
// 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/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/wire/client/webgpu.h"
#include "partition_alloc/pointers/raw_ptr.h"
#include "src/dawn/common/StringViewUtils.h"
#include "src/dawn/wire/client/Client.h"
#include "src/utils/compiler.h"
#include "src/utils/log.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.userdata1),
mUserdata2(callbackInfo.userdata2),
mDevice(std::move(device)) {}
EventType GetType() override { return kType; }
WireResult ReadyHook(FutureID futureID,
wgpu::RequestDeviceStatus status,
StringView message,
const Limits* limits,
Span<const wgpu::FeatureName> features) {
DAWN_ASSERT(mDevice != nullptr);
mStatus = status;
mMessage = message;
if (status == wgpu::RequestDeviceStatus::Success) {
mDevice->SetLimits(limits);
mDevice->SetFeatures(features);
}
return WireResult::Success;
}
private:
void CompleteImpl(FutureID futureID, EventCompletionType completionType) override {
if (completionType == EventCompletionType::Shutdown) {
mStatus = wgpu::RequestDeviceStatus::CallbackCancelled;
mMessage = "A valid external Instance reference no longer exists.";
}
// Callback needs to happen before device lost handling to ensure resolution order.
void* userdata1 = mUserdata1.ExtractAsDangling();
void* userdata2 = mUserdata2.ExtractAsDangling();
if (mCallback) {
Ref<Device> device = mDevice;
mCallback(ToAPI(mStatus),
mStatus == wgpu::RequestDeviceStatus::Success ? ReturnToAPI(std::move(device))
: nullptr,
ToOutputStringView(mMessage), userdata1, userdata2);
}
if (mStatus != wgpu::RequestDeviceStatus::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 == wgpu::RequestDeviceStatus::CallbackCancelled) {
mDevice->HandleDeviceLost(wgpu::DeviceLostReason::CallbackCancelled,
"A valid external Instance reference no longer exists.");
} else {
mDevice->HandleDeviceLost(wgpu::DeviceLostReason::FailedCreation,
"Device failed at creation.");
}
}
}
WGPURequestDeviceCallback 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::RequestDeviceStatus mStatus = wgpu::RequestDeviceStatus::Success;
std::string mMessage;
// The device is created when we call RequestDevice. 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;
}
wgpu::Status Adapter::APIGetLimits(Limits* limits) const {
return mLimitsAndFeatures.GetLimits(limits);
}
bool Adapter::APIHasFeature(wgpu::FeatureName feature) const {
return mLimitsAndFeatures.HasFeature(feature);
}
void Adapter::APIGetFeatures(SupportedFeatures* features) const {
mLimitsAndFeatures.ToSupportedFeatures(features);
}
void Adapter::SetLimits(const Limits* limits) {
return mLimitsAndFeatures.SetLimits(limits);
}
void Adapter::SetFeatures(Span<const wgpu::FeatureName> features) {
return mLimitsAndFeatures.SetFeatures(features);
}
void Adapter::SetInfo(const AdapterInfo* info) {
mInfo.backendType = info->backendType;
mInfo.adapterType = info->adapterType;
mInfo.vendorID = info->vendorID;
mInfo.deviceID = info->deviceID;
mInfo.subgroupMinSize = info->subgroupMinSize;
mInfo.subgroupMaxSize = info->subgroupMaxSize;
// Deep copy the string pointed out by info. StringViews are all explicitly sized by the wire.
mVendor = info->vendor;
mArchitecture = info->architecture;
mDeviceName = info->device;
mDescription = info->description;
mInfo.nextInChain = nullptr;
// Loop through the chained struct.
for (const auto* chain = info->nextInChain; chain; chain = chain->nextInChain) {
switch (chain->sType) {
case wgpu::SType::AdapterPropertiesMemoryHeaps: {
// Make a copy of the heap info in `mMemoryHeapInfo`.
const auto* memoryHeapProperties =
reinterpret_cast<const AdapterPropertiesMemoryHeaps*>(chain);
mMemoryHeapInfo.assign(memoryHeapProperties->heapInfo.begin(),
memoryHeapProperties->heapInfo.end());
break;
}
case wgpu::SType::AdapterPropertiesD3D: {
const auto* d3dProperties = reinterpret_cast<const AdapterPropertiesD3D*>(chain);
mD3DProperties.shaderModel = d3dProperties->shaderModel;
mD3DProperties.adapterLUIDLowPart = d3dProperties->adapterLUIDLowPart;
mD3DProperties.adapterLUIDHighPart = d3dProperties->adapterLUIDHighPart;
break;
}
case wgpu::SType::AdapterPropertiesVk: {
const auto* vkProperties = reinterpret_cast<const AdapterPropertiesVk*>(chain);
mVkProperties.driverVersion = vkProperties->driverVersion;
break;
}
case wgpu::SType::AdapterPropertiesSubgroupMatrixConfigs: {
// Make a copy of the heap info in `mSubgroupMatrixConfigs`.
const auto* subgroupMatrixConfigs =
reinterpret_cast<const AdapterPropertiesSubgroupMatrixConfigs*>(chain);
mSubgroupMatrixConfigs.assign(subgroupMatrixConfigs->configs.begin(),
subgroupMatrixConfigs->configs.end());
break;
}
case wgpu::SType::DawnAdapterPropertiesPowerPreference: {
const auto* powerProperties =
reinterpret_cast<const DawnAdapterPropertiesPowerPreference*>(chain);
mPowerProperties.powerPreference = powerProperties->powerPreference;
break;
}
default:
DAWN_UNREACHABLE();
break;
}
}
}
wgpu::Status Adapter::APIGetInfo(AdapterInfo* info) const {
// Loop through the chained struct.
for (auto* chain = info->nextInChain; chain; chain = chain->nextInChain) {
switch (chain->sType) {
case wgpu::SType::AdapterPropertiesMemoryHeaps: {
// Copy `mMemoryHeapInfo` into a new allocation.
auto* memoryHeapProperties = reinterpret_cast<AdapterPropertiesMemoryHeaps*>(chain);
memoryHeapProperties->heapInfo = HeapArrayFrom(mMemoryHeapInfo).MoveToSpan();
break;
}
case wgpu::SType::AdapterPropertiesD3D: {
auto* d3dProperties = reinterpret_cast<AdapterPropertiesD3D*>(chain);
d3dProperties->shaderModel = mD3DProperties.shaderModel;
d3dProperties->adapterLUIDLowPart = mD3DProperties.adapterLUIDLowPart;
d3dProperties->adapterLUIDHighPart = mD3DProperties.adapterLUIDHighPart;
break;
}
case wgpu::SType::AdapterPropertiesVk: {
auto* vkProperties = reinterpret_cast<AdapterPropertiesVk*>(chain);
vkProperties->driverVersion = mVkProperties.driverVersion;
break;
}
case wgpu::SType::AdapterPropertiesSubgroupMatrixConfigs: {
if (!APIHasFeature(wgpu::FeatureName::ChromiumExperimentalSubgroupMatrix)) {
return wgpu::Status::Error;
}
// Copy `mSubgroupMatrixConfigs` into a new allocation.
auto* subgroupMatrixConfigs =
reinterpret_cast<AdapterPropertiesSubgroupMatrixConfigs*>(chain);
subgroupMatrixConfigs->configs = HeapArrayFrom(mSubgroupMatrixConfigs).MoveToSpan();
break;
}
case wgpu::SType::DawnAdapterPropertiesPowerPreference: {
auto* powerProperties =
reinterpret_cast<DawnAdapterPropertiesPowerPreference*>(chain);
powerProperties->powerPreference = mPowerProperties.powerPreference;
break;
}
default:
break;
}
}
info->backendType = mInfo.backendType;
info->adapterType = mInfo.adapterType;
info->vendorID = mInfo.vendorID;
info->deviceID = mInfo.deviceID;
info->subgroupMinSize = mInfo.subgroupMinSize;
info->subgroupMaxSize = mInfo.subgroupMaxSize;
// Allocate space for all strings.
size_t allocSize =
mVendor.length() + mArchitecture.length() + mDeviceName.length() + mDescription.length();
// SAFETY: The data in the buffer will be initialized by AddString below.
Span<char> outBuffer = DAWN_UNSAFE_BUFFERS(HeapArray<char>::Uninit(allocSize).MoveToSpan());
auto AddString = [&](const std::string& in, StringView* out) {
DAWN_ASSERT(in.length() <= outBuffer.size());
outBuffer.CopyPrefixFrom(in);
*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 wgpu::Status::Success;
}
Future Adapter::APIRequestDevice(const DeviceDescriptor* descriptor,
const WGPURequestDeviceCallbackInfo& callbackInfo) {
Client* client = GetClient();
Ref<Device> device = client->Make<Device>(GetInstance(), this, descriptor);
auto [futureIDInternal, tracked] =
GetEventManager().TrackEvent(AcquireRef(new RequestDeviceEvent(callbackInfo, device)));
if (!tracked) {
return {futureIDInternal};
}
// Ensure callbacks are not serialized as part of the command, as they cannot be passed between
// processes.
DeviceDescriptor wireDescriptor = {};
if (descriptor) {
wireDescriptor = *descriptor;
wireDescriptor.deviceLostCallbackInfo = {};
wireDescriptor.uncapturedErrorCallbackInfo = {};
}
AdapterRequestDeviceCmd cmd;
cmd.adapterId = GetWireHandle(client).id;
cmd.instanceId = GetInstance()->GetWireHandle(client).id;
cmd.future = {futureIDInternal};
cmd.deviceObjectHandle = device->GetWireHandle(client);
cmd.deviceLostFuture = {device->APIGetLostFuture().id};
cmd.descriptor = ToWireCmd(&wireDescriptor);
client->SerializeCommand(std::move(cmd));
return {futureIDInternal};
}
WireResult Client::DoAdapterRequestDeviceCallback(ObjectId instanceId,
Future future,
wgpu::RequestDeviceStatus status,
StringView message,
const Limits* limits,
Span<const wgpu::FeatureName> features) {
return SetFutureReady<RequestDeviceEvent>(instanceId, future.id, status, message, limits,
features);
}
Instance* Adapter::APIGetInstance() const {
Ref<Instance> instance = GetInstance();
return ReturnToAPI2(std::move(instance));
}
Device* Adapter::APICreateDevice(const DeviceDescriptor*) {
dawn::ErrorLog() << "adapter.CreateDevice not supported with dawn_wire.";
return nullptr;
}
wgpu::Status Adapter::APIGetFormatCapabilities(wgpu::TextureFormat format,
DawnFormatCapabilities* capabilities) {
dawn::ErrorLog() << "adapter.GetFormatCapabilities not supported with dawn_wire.";
return wgpu::Status::Error;
}
void APIAdapterInfoFreeMembers(WGPUAdapterInfo info) {
// This single delete is enough because everything is a single allocation.
DeleteAllocationFromHeapArray(info.vendor.data);
}
void APIAdapterPropertiesMemoryHeapsFreeMembers(
WGPUAdapterPropertiesMemoryHeaps memoryHeapProperties) {
DeleteAllocationFromHeapArray(memoryHeapProperties.heapInfo);
}
void APIDawnDrmFormatCapabilitiesFreeMembers(WGPUDawnDrmFormatCapabilities capabilities) {
DeleteAllocationFromHeapArray(capabilities.properties);
}
void APISupportedFeaturesFreeMembers(WGPUSupportedFeatures supportedFeatures) {
DeleteAllocationFromHeapArray(supportedFeatures.features);
}
void APIAdapterPropertiesSubgroupMatrixConfigsFreeMembers(
WGPUAdapterPropertiesSubgroupMatrixConfigs subgroupMatrixConfigs) {
DeleteAllocationFromHeapArray(subgroupMatrixConfigs.configs);
}
} // namespace dawn::wire::client