// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkWebGPUConfiguration.h" #include "Private/vtkWebGPUBufferInternals.h" #include "Private/vtkWebGPUConfigurationInternals.h" #include "Private/vtkWebGPUTextureInternals.h" #include "vtkObjectFactory.h" #include "vtkStringFormatter.h" #include "vtkWebGPUHelpers.h" #include "vtkWebGPURenderWindow.h" #include "vtksys/SystemInformation.hxx" #include "vtksys/SystemTools.hxx" #include #include #ifdef __EMSCRIPTEN__ #include #endif VTK_ABI_NAMESPACE_BEGIN namespace { // https://pcisig.com/membership/member-companies const std::uint32_t AMD_PCI_VENDOR_ID = 0x1002; const std::uint32_t APPLE_PCI_VENDOR_ID = 0x106b; const std::uint32_t ARM_PCI_VENDOR_ID = 0x13b5; const std::uint32_t BROADCOM_PCI_VENDOR_ID = 0x14e4; // Used on low power devices like Raspberry-Pi const std::uint32_t INTEL_PCI_VENDOR_ID = 0x8086; const std::uint32_t MESA_PCI_VENDOR_ID = 0x10005; const std::uint32_t MICROSOFT_PCI_VENDOR_ID = 0x1414; // used in Microsoft WSL const std::uint32_t NVIDIA_PCI_VENDOR_ID = 0x10de; const std::uint32_t SAMSUNG_PCI_VENDOR_ID = 0x144d; template std::basic_ostream& operator<<( std::basic_ostream& o, wgpu::StringView value) { o << std::string_view(value); return o; } ostream& operator<<(ostream& os, const vtkWebGPUConfiguration::BackendType& backend) { switch (backend) { case vtkWebGPUConfiguration::BackendType::Null: os << "Null"; break; case vtkWebGPUConfiguration::BackendType::WebGPU: os << "WebGPU"; break; case vtkWebGPUConfiguration::BackendType::D3D11: os << "D3D11"; break; case vtkWebGPUConfiguration::BackendType::D3D12: os << "D3D12"; break; case vtkWebGPUConfiguration::BackendType::Metal: os << "Metal"; break; case vtkWebGPUConfiguration::BackendType::Vulkan: os << "Vulkan"; break; case vtkWebGPUConfiguration::BackendType::OpenGL: os << "OpenGL"; break; case vtkWebGPUConfiguration::BackendType::OpenGLES: os << "OpenGLES"; break; case vtkWebGPUConfiguration::BackendType::Undefined: default: os << "Undefined"; break; } return os; } ostream& operator<<(ostream& os, const wgpu::BackendType& backend) { switch (backend) { case wgpu::BackendType::Null: os << "Null"; break; case wgpu::BackendType::WebGPU: os << "WebGPU"; break; case wgpu::BackendType::D3D11: os << "D3D11"; break; case wgpu::BackendType::D3D12: os << "D3D12"; break; case wgpu::BackendType::Metal: os << "Metal"; break; case wgpu::BackendType::Vulkan: os << "Vulkan"; break; case wgpu::BackendType::OpenGL: os << "OpenGL"; break; case wgpu::BackendType::OpenGLES: os << "OpenGLES"; break; case wgpu::BackendType::Undefined: default: os << "Undefined"; break; } return os; } ostream& operator<<(ostream& os, const vtkWebGPUConfiguration::PowerPreferenceType& power) { switch (power) { case vtkWebGPUConfiguration::PowerPreferenceType::HighPerformance: os << "HighPerformance"; break; case vtkWebGPUConfiguration::PowerPreferenceType::LowPower: os << "LowPower"; break; case vtkWebGPUConfiguration::PowerPreferenceType::Undefined: default: os << "Undefined"; break; } return os; } ostream& operator<<(ostream& os, const wgpu::AdapterType& type) { switch (type) { case wgpu::AdapterType::DiscreteGPU: os << "discrete GPU"; break; case wgpu::AdapterType::IntegratedGPU: os << "integrated GPU"; break; case wgpu::AdapterType::CPU: os << "CPU"; break; case wgpu::AdapterType::Unknown: os << "unknown"; break; } return os; } std::string AsHex(uint32_t val) { std::stringstream hex; hex << "0x" << std::uppercase << std::setfill('0') << std::setw(4) << std::hex << val; return hex.str(); } std::string FormatNumber(uint64_t num) { auto s = vtk::to_string(num); std::stringstream ret; auto remainder = s.length() % 3; ret << s.substr(0, remainder); for (size_t i = remainder; i < s.length(); i += 3) { if (i > 0) { ret << ","; } ret << s.substr(i, 3); } return ret.str(); } void PrintLimits(ostream& os, vtkIndent indent, const wgpu::Limits& limits) { os << indent << "maxTextureDimension1D: " << FormatNumber(limits.maxTextureDimension1D) << '\n'; os << indent << "maxTextureDimension2D: " << FormatNumber(limits.maxTextureDimension2D) << '\n'; os << indent << "maxTextureDimension3D: " << FormatNumber(limits.maxTextureDimension3D) << '\n'; os << indent << "maxTextureArrayLayers: " << FormatNumber(limits.maxTextureArrayLayers) << '\n'; os << indent << "maxBindGroups: " << FormatNumber(limits.maxBindGroups) << '\n'; os << indent << "maxBindGroupsPlusVertexBuffers: " << FormatNumber(limits.maxBindGroupsPlusVertexBuffers) << '\n'; os << indent << "maxBindingsPerBindGroup: " << FormatNumber(limits.maxBindingsPerBindGroup) << '\n'; os << indent << "maxDynamicUniformBuffersPerPipelineLayout: " << FormatNumber(limits.maxDynamicUniformBuffersPerPipelineLayout) << '\n'; os << indent << "maxDynamicStorageBuffersPerPipelineLayout: " << FormatNumber(limits.maxDynamicStorageBuffersPerPipelineLayout) << '\n'; os << indent << "maxSampledTexturesPerShaderStage: " << FormatNumber(limits.maxSampledTexturesPerShaderStage) << '\n'; os << indent << "maxSamplersPerShaderStage: " << FormatNumber(limits.maxSamplersPerShaderStage) << '\n'; os << indent << "maxStorageBuffersPerShaderStage: " << FormatNumber(limits.maxStorageBuffersPerShaderStage) << '\n'; os << indent << "maxStorageTexturesPerShaderStage: " << FormatNumber(limits.maxStorageTexturesPerShaderStage) << '\n'; os << indent << "maxUniformBuffersPerShaderStage: " << FormatNumber(limits.maxUniformBuffersPerShaderStage) << '\n'; os << indent << "maxUniformBufferBindingSize: " << FormatNumber(limits.maxUniformBufferBindingSize) << '\n'; os << indent << "maxStorageBufferBindingSize: " << FormatNumber(limits.maxStorageBufferBindingSize) << '\n'; os << indent << "minUniformBufferOffsetAlignment: " << FormatNumber(limits.minUniformBufferOffsetAlignment) << '\n'; os << indent << "minStorageBufferOffsetAlignment: " << FormatNumber(limits.minStorageBufferOffsetAlignment) << '\n'; os << indent << "maxVertexBuffers: " << FormatNumber(limits.maxVertexBuffers) << '\n'; os << indent << "maxBufferSize: " << FormatNumber(limits.maxBufferSize) << '\n'; os << indent << "maxVertexAttributes: " << FormatNumber(limits.maxVertexAttributes) << '\n'; os << indent << "maxVertexBufferArrayStride: " << FormatNumber(limits.maxVertexBufferArrayStride) << '\n'; os << indent << "maxInterStageShaderVariables: " << FormatNumber(limits.maxInterStageShaderVariables) << '\n'; os << indent << "maxColorAttachments: " << FormatNumber(limits.maxColorAttachments) << '\n'; os << indent << "maxColorAttachmentBytesPerSample: " << FormatNumber(limits.maxColorAttachmentBytesPerSample) << '\n'; os << indent << "maxComputeWorkgroupStorageSize: " << FormatNumber(limits.maxComputeWorkgroupStorageSize) << '\n'; os << indent << "maxComputeInvocationsPerWorkgroup: " << FormatNumber(limits.maxComputeInvocationsPerWorkgroup) << '\n'; os << indent << "maxComputeWorkgroupSizeX: " << FormatNumber(limits.maxComputeWorkgroupSizeX) << '\n'; os << indent << "maxComputeWorkgroupSizeY: " << FormatNumber(limits.maxComputeWorkgroupSizeY) << '\n'; os << indent << "maxComputeWorkgroupSizeZ: " << FormatNumber(limits.maxComputeWorkgroupSizeZ) << '\n'; os << indent << "maxComputeWorkgroupsPerDimension: " << FormatNumber(limits.maxComputeWorkgroupsPerDimension) << '\n'; } void PrintAdapterInfo(ostream& os, vtkIndent indent, const wgpu::Adapter& adapter) { wgpu::AdapterInfo info{}; #if VTK_USE_DAWN_WEBGPU wgpu::DawnAdapterPropertiesPowerPreference power_props{}; info.nextInChain = &power_props; #endif adapter.GetInfo(&info); os << indent << "VendorID: " << AsHex(info.vendorID) << '\n'; os << indent << "Vendor: " << info.vendor << '\n'; os << indent << "Architecture: " << info.architecture << '\n'; os << indent << "DeviceID: " << AsHex(info.deviceID) << '\n'; os << indent << "Name: " << info.device << '\n'; os << indent << "Driver description: " << info.description << '\n'; os << indent << "Adapter Type: " << info.adapterType << '\n'; os << indent << "Backend Type: " << info.backendType << '\n'; os << indent << "Power: "; #if VTK_USE_DAWN_WEBGPU switch (power_props.powerPreference) { case wgpu::PowerPreference::LowPower: os << "low power\n"; break; case wgpu::PowerPreference::HighPerformance: os << "high performance\n"; break; case wgpu::PowerPreference::Undefined: os << "\n"; break; } #else os << "Unknown\n"; #endif } void PrintAdapterFeatures(ostream& os, vtkIndent indent, const wgpu::Adapter& adapter) { wgpu::SupportedFeatures supportedFeatures = {}; adapter.GetFeatures(&supportedFeatures); os << indent << "Features\n"; os << indent << "========\n"; for (std::size_t i = 0; i < supportedFeatures.featureCount; ++i) { const auto feature = supportedFeatures.features[i]; #if VTK_USE_DAWN_WEBGPU auto info = dawn::native::GetFeatureInfo(feature); os << indent << " * " << info->name << '\n'; os << indent << info->description << '\n'; os << indent << " " << info->url << '\n'; #elif defined(__EMSCRIPTEN__) // Look up the list of feature strings in `WebGPU.FeatureName` const auto featureIdx = static_cast::type>(feature); // clang-format off #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" char *featureNameCStr = (char*)EM_ASM_PTR({ let jsString = WebGPU.FeatureName[$0]; if (jsString === undefined) { jsString = "undefined"; } return stringToNewUTF8(jsString); }, featureIdx); #pragma clang diagnostic pop // clang-format on os << indent << indent << featureNameCStr << '\n'; free(featureNameCStr); #endif } } void PrintAdapterLimits(ostream& os, vtkIndent indent, const wgpu::Adapter& adapter) { wgpu::Limits adapterLimits; if (adapter.GetLimits(&adapterLimits)) { os << indent << '\n'; os << indent << "Adapter Limits\n"; os << indent << "==============\n"; PrintLimits(os, indent.GetNextIndent(), adapterLimits); } } void PrintAdapter(ostream& os, vtkIndent indent, const wgpu::Adapter& adapter) { os << indent << "Adapter\n"; os << indent << "=======\n"; PrintAdapterInfo(os, indent.GetNextIndent(), adapter); PrintAdapterFeatures(os, indent.GetNextIndent(), adapter); PrintAdapterLimits(os, indent.GetNextIndent(), adapter); } #if VTK_USE_DAWN_WEBGPU /** * Implement Dawn's MemoryDump interface. */ class DawnMemoryDump : public dawn::native::MemoryDump { public: void AddScalar(const char* name, const char* key, const char* units, uint64_t value) override { if (key == MemoryDump::kNameSize && units == MemoryDump::kUnitsBytes) { TotalSize += value; } else if (key == MemoryDump::kNameObjectCount && units == MemoryDump::kUnitsObjects) { TotalObjects += value; } auto it = this->WebGPUObjects.find(name); if (it == this->WebGPUObjects.end()) { MemoryInformation info; info.Size = value; this->WebGPUObjects[name] = info; } else { it->second.Size = value; } } void AddString(const char* name, const char* key, const std::string& value) override { auto it = this->WebGPUObjects.find(name); if (it == this->WebGPUObjects.end()) { MemoryInformation info; info.Properties[key] = value; this->WebGPUObjects[name] = info; } else { it->second.Properties[key] = value; } } uint64_t GetTotalSize() const { return TotalSize; } uint64_t GetTotalNumberOfObjects() const { return TotalObjects; } void PrintSelf(ostream& os, vtkIndent indent) { os << indent << "TotalSize: " << this->TotalSize << '\n'; os << indent << "TotalObjects: " << this->TotalObjects << '\n'; for (auto& object : this->WebGPUObjects) { os << indent << indent << "-Name: " << object.first << '\n'; os << indent << indent << " Size: " << object.second.Size << '\n'; for (auto& property : object.second.Properties) { os << indent << indent << " " << property.first << "=" << property.second << '\n'; } } } struct MemoryInformation { std::uint64_t Size; std::map Properties; }; private: uint64_t TotalSize = 0; uint64_t TotalObjects = 0; std::unordered_map WebGPUObjects; }; #endif } // end anon namespace //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkWebGPUConfiguration); //------------------------------------------------------------------------------ vtkWebGPUConfiguration::vtkWebGPUConfiguration() : Internals(new vtkWebGPUConfigurationInternals()) { vtksys::SystemInformation info; if (info.GetOSIsApple()) { this->Backend = BackendType::Metal; } else if (info.GetOSIsWindows()) { this->Backend = BackendType::D3D12; } else { this->Backend = BackendType::Vulkan; } this->Timeout = vtkWebGPUConfigurationInternals::DefaultTimeout; } //------------------------------------------------------------------------------ vtkWebGPUConfiguration::~vtkWebGPUConfiguration() { this->Finalize(); } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::PrintSelf(ostream& os, vtkIndent indent) { os << "Backend: " << this->Backend << '\n'; os << "BackendInUse: " << this->GetBackendInUseAsString() << '\n'; os << "PowerPreference: " << this->PowerPreference << '\n'; os << "DeviceReady: " << (this->Internals->DeviceReady ? "yes\n" : "no\n"); os << "Timeout:" << this->Timeout << "ms\n"; os << "Instance: " << vtkWebGPUConfigurationInternals::Instance.Get() << '\n'; os << "Adapter: " << this->Internals->Adapter.Get() << '\n'; os << "Device: " << this->Internals->Device.Get() << '\n'; this->Superclass::PrintSelf(os, indent.GetNextIndent()); } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::SetDefaultTimeout(double t) { vtkWebGPUConfigurationInternals::DefaultTimeout = t; } //------------------------------------------------------------------------------ wgpu::Adapter vtkWebGPUConfiguration::GetAdapter() { return this->Internals->Adapter; } //------------------------------------------------------------------------------ wgpu::Device vtkWebGPUConfiguration::GetDevice() { return this->Internals->Device; } //------------------------------------------------------------------------------ wgpu::Instance vtkWebGPUConfiguration::GetInstance() { return vtkWebGPUConfigurationInternals::Instance; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::Initialize() { vtkDebugMacro(<< __func__); auto& internals = (*this->Internals); if (internals.DeviceReady) { vtkDebugMacro(<< "Device is already initialized."); return true; } vtkWebGPUConfigurationInternals::AddInstanceRef(); wgpu::RequestAdapterOptions adapterOptions = {}; adapterOptions.backendType = internals.ToWGPUBackendType(this->Backend); adapterOptions.powerPreference = internals.ToWGPUPowerPreferenceType(this->PowerPreference); std::uint64_t timeoutNS = UINT64_MAX; internals.Timedout = false; auto waitStatus = vtkWebGPUConfigurationInternals::Instance.WaitAny( vtkWebGPUConfigurationInternals::Instance.RequestAdapter( &adapterOptions, wgpu::CallbackMode::WaitAnyOnly, [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, const char* message, vtkWebGPUConfigurationInternals* internalsData) { if (status != wgpu::RequestAdapterStatus::Success) { vtkGenericWarningMacro("Failed to get an adapter:" << message); return; } internalsData->Adapter = std::move(adapter); }, this->Internals.get()), timeoutNS); if (waitStatus == wgpu::WaitStatus::TimedOut) { vtkWarningMacro(<< "Request adapter timed out!"); return internals.DeviceReady; } internals.Timedout = false; // Create device descriptor with callbacks and toggles wgpu::DeviceDescriptor deviceDescriptor = {}; deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, [](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message) { const char* reasonName = ""; switch (reason) { case wgpu::DeviceLostReason::Unknown: reasonName = "Unknown"; break; case wgpu::DeviceLostReason::Destroyed: reasonName = "Destroyed"; break; case wgpu::DeviceLostReason::CallbackCancelled: reasonName = "CallbackCancelled"; break; case wgpu::DeviceLostReason::FailedCreation: reasonName = "FailedCreation"; break; default: break; } vtkLog(INFO, << "Device lost, reason=" << reasonName << ". " << vtkWebGPUHelpers::StringViewToStdString(message)); }); deviceDescriptor.SetUncapturedErrorCallback( [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message) { const char* errorTypeName = ""; switch (type) { case wgpu::ErrorType::Validation: errorTypeName = "Validation"; break; case wgpu::ErrorType::OutOfMemory: errorTypeName = "Out of memory"; break; case wgpu::ErrorType::Unknown: errorTypeName = "Unknown"; break; case wgpu::ErrorType::Internal: errorTypeName = "Internal"; break; default: break; } vtkGenericWarningMacro(<< errorTypeName << " error: " << vtkWebGPUHelpers::StringViewToStdString(message)); }); // Populating limits of the device internals.PopulateRequiredLimits(internals.Adapter); deviceDescriptor.requiredLimits = &internals.RequiredLimits; // Populating required features of the device internals.PopulateRequiredFeatures(); deviceDescriptor.requiredFeatureCount = internals.RequiredFeatures.size(); deviceDescriptor.requiredFeatures = internals.RequiredFeatures.data(); // Synchronously create the device internals.Timedout = false; waitStatus = vtkWebGPUConfigurationInternals::Instance.WaitAny( internals.Adapter.RequestDevice( &deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly, [](wgpu::RequestDeviceStatus status, wgpu::Device device, const char* message, vtkWebGPUConfigurationInternals* internalsData) { if (status != wgpu::RequestDeviceStatus::Success) { vtkGenericWarningMacro("Failed to get a device:" << message); return; } internalsData->Device = std::move(device); // internalsData->Queue = internals->Device.GetQueue(); }, this->Internals.get()), UINT64_MAX); if (waitStatus == wgpu::WaitStatus::TimedOut) { vtkWarningMacro(<< "Request device timed out!"); return internals.DeviceReady; } if (internals.Device != nullptr) { internals.DeviceReady = true; } return internals.DeviceReady; } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::Finalize() { auto& internals = (*this->Internals); if (!internals.DeviceReady) { return; } internals.Adapter = nullptr; internals.Device = nullptr; internals.DeviceReady = false; vtkWebGPUConfigurationInternals::ReleaseInstanceRef(); } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::ProcessEvents() { #if defined(__EMSCRIPTEN__) vtkWebGPUConfigurationInternals::Instance.ProcessEvents(); if (emscripten_has_asyncify()) { // gives a chance for webgpu callback code to execute emscripten_sleep(1); } else { vtkErrorMacro(<< "This build of VTK cannot run asynchronous javascript code synchronously." "Please compile VTK with ASYNCIFY or JSPI."); } #else vtkWebGPUConfigurationInternals::Instance.ProcessEvents(); #endif } //------------------------------------------------------------------------------ vtkWebGPUConfiguration::BackendType vtkWebGPUConfiguration::GetBackendInUse() { auto& internals = (*this->Internals); if (!internals.DeviceReady) { return vtkWebGPUConfiguration::BackendType::Undefined; } wgpu::AdapterInfo info{}; internals.Adapter.GetInfo(&info); return internals.FromWGPUBackendType(info.backendType); } //------------------------------------------------------------------------------ std::string vtkWebGPUConfiguration::GetBackendInUseAsString() { auto& internals = (*this->Internals); if (internals.DeviceReady) { wgpu::AdapterInfo info{}; internals.Adapter.GetInfo(&info); switch (info.backendType) { case wgpu::BackendType::Null: return "Null"; case wgpu::BackendType::WebGPU: return "WebGPU"; case wgpu::BackendType::D3D11: return "D3D11"; case wgpu::BackendType::D3D12: return "D3D12"; case wgpu::BackendType::Metal: return "Metal"; case wgpu::BackendType::Vulkan: return "Vulkan"; case wgpu::BackendType::OpenGL: return "OpenGL"; case wgpu::BackendType::OpenGLES: return "OpenGL ES"; case wgpu::BackendType::Undefined: default: return "Undefined"; } } else { return "Undefined"; } } //------------------------------------------------------------------------------ std::size_t vtkWebGPUConfiguration::Align(std::size_t value, std::size_t alignment) { // This is equivalent to std::ceil(value / (float)alignment) * alignment. // This implementation is more efficient because it avoids floating point operations, ceil with // the use of bitmasks. return // This step ensures that any remainder when value is divided by alignment is handled correctly // by rounding up to the next multiple of alignment. (value + alignment - 1) // clear the lower bits (using mask) that are less than the alignment boundary. & ~(alignment - 1); } //------------------------------------------------------------------------------ std::string vtkWebGPUConfiguration::DeviceNotReadyMessage() { return "Device not ready\n"; } //------------------------------------------------------------------------------ std::string vtkWebGPUConfiguration::ReportCapabilities() { std::ostringstream os; if (this->Internals->DeviceReady) { ::PrintAdapter(os, vtkIndent(), this->Internals->Adapter); return os.str(); } else { return DeviceNotReadyMessage(); } } //------------------------------------------------------------------------------ std::uint32_t vtkWebGPUConfiguration::GetAdapterVendorID() { if (this->Internals->DeviceReady) { wgpu::AdapterInfo info{}; this->Internals->Adapter.GetInfo(&info); return info.vendorID; } else { return 0; } } //------------------------------------------------------------------------------ std::uint32_t vtkWebGPUConfiguration::GetAdapterDeviceID() { if (this->Internals->DeviceReady) { wgpu::AdapterInfo info{}; this->Internals->Adapter.GetInfo(&info); return info.deviceID; } else { return 0; } } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsAMDGPUInUse() { return this->GetAdapterVendorID() == ::AMD_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsAppleGPUInUse() { return this->GetAdapterVendorID() == ::APPLE_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsARMGPUInUse() { return this->GetAdapterVendorID() == ::ARM_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsBroadcomGPUInUse() { return this->GetAdapterVendorID() == ::BROADCOM_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsIntelGPUInUse() { return this->GetAdapterVendorID() == ::INTEL_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsMesaGPUInUse() { return this->GetAdapterVendorID() == ::MESA_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsMicrosoftGPUInUse() { return this->GetAdapterVendorID() == ::MICROSOFT_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsNVIDIAGPUInUse() { return this->GetAdapterVendorID() == ::NVIDIA_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ bool vtkWebGPUConfiguration::IsSamsungGPUInUse() { return this->GetAdapterVendorID() == ::SAMSUNG_PCI_VENDOR_ID; } //------------------------------------------------------------------------------ wgpu::Buffer vtkWebGPUConfiguration::CreateBuffer(unsigned long sizeBytes, wgpu::BufferUsage usage, bool mappedAtCreation /*=false*/, const char* label /*=nullptr*/) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot create buffer because device is not ready."); return nullptr; } wgpu::BufferDescriptor bufferDescriptor; bufferDescriptor.label = label == nullptr ? "(nolabel)" : label; bufferDescriptor.size = sizeBytes; bufferDescriptor.usage = usage; bufferDescriptor.mappedAtCreation = mappedAtCreation; return this->CreateBuffer(bufferDescriptor); } //------------------------------------------------------------------------------ wgpu::Buffer vtkWebGPUConfiguration::CreateBuffer(const wgpu::BufferDescriptor& bufferDescriptor) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot create buffer because device is not ready."); return nullptr; } const auto label = vtkWebGPUHelpers::StringViewToStdString(bufferDescriptor.label); if (!vtkWebGPUBufferInternals::CheckBufferSize(internals.Device, bufferDescriptor.size)) { wgpu::Limits supportedDeviceLimits; internals.Device.GetLimits(&supportedDeviceLimits); vtkLog(ERROR, "The current WebGPU Device cannot create buffers larger than: " << supportedDeviceLimits.maxStorageBufferBindingSize << " bytes but the buffer with label " << label << " is " << bufferDescriptor.size << " bytes big."); return nullptr; } vtkVLog(this->GetGPUMemoryLogVerbosity(), "Create buffer {label: \"" << label << "\", size: " << bufferDescriptor.size << "}"); wgpu::Buffer buffer = internals.Device.CreateBuffer(&bufferDescriptor); return buffer; } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::WriteBuffer(const wgpu::Buffer& buffer, unsigned long offset, const void* data, unsigned long sizeBytes, const char* description /*= nullptr*/) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot write data into buffer because device is not ready."); return; } vtkVLog(this->GetGPUMemoryLogVerbosity(), "Write buffer {description: \"" << (description ? description : "null") << "\", offset: " << offset << ", size: " << sizeBytes << "}"); internals.Device.GetQueue().WriteBuffer(buffer, offset, data, sizeBytes); } //------------------------------------------------------------------------------ wgpu::Texture vtkWebGPUConfiguration::CreateTexture(wgpu::Extent3D extents, wgpu::TextureDimension dimension, wgpu::TextureFormat format, wgpu::TextureUsage usage, int mipLevelCount, const char* label /*=nullptr*/) { wgpu::TextureDescriptor textureDescriptor; textureDescriptor.dimension = dimension; textureDescriptor.format = format; textureDescriptor.size = extents; textureDescriptor.mipLevelCount = mipLevelCount; textureDescriptor.nextInChain = nullptr; textureDescriptor.sampleCount = 1; textureDescriptor.usage = usage; textureDescriptor.viewFormatCount = 0; textureDescriptor.viewFormats = nullptr; textureDescriptor.label = label; return this->CreateTexture(textureDescriptor); } //------------------------------------------------------------------------------ wgpu::Texture vtkWebGPUConfiguration::CreateTexture( const wgpu::TextureDescriptor& textureDescriptor) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot create texture because device is not ready."); return nullptr; } const auto label = vtkWebGPUHelpers::StringViewToStdString(textureDescriptor.label); vtkVLog(this->GetGPUMemoryLogVerbosity(), "Create texture {label: \"" << label << "\", size: [" << textureDescriptor.size.width << ',' << textureDescriptor.size.height << ',' << textureDescriptor.size.depthOrArrayLayers << "]}"); return internals.Device.CreateTexture(&textureDescriptor); } //------------------------------------------------------------------------------ wgpu::TextureView vtkWebGPUConfiguration::CreateView(wgpu::Texture texture, wgpu::TextureViewDimension dimension, wgpu::TextureAspect aspect, wgpu::TextureFormat format, int baseMipLevel, int mipLevelCount, const char* label /*=nullptr*/) { // Creating a "full" view of the texture wgpu::TextureViewDescriptor textureViewDescriptor; textureViewDescriptor.arrayLayerCount = 1; textureViewDescriptor.aspect = aspect; textureViewDescriptor.baseArrayLayer = 0; textureViewDescriptor.baseMipLevel = baseMipLevel; textureViewDescriptor.dimension = dimension; textureViewDescriptor.format = format; textureViewDescriptor.label = label; textureViewDescriptor.mipLevelCount = mipLevelCount; textureViewDescriptor.nextInChain = nullptr; return this->CreateView(texture, textureViewDescriptor); } //------------------------------------------------------------------------------ wgpu::TextureView vtkWebGPUConfiguration::CreateView( wgpu::Texture texture, const wgpu::TextureViewDescriptor& viewDescriptor) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot create texture because device is not ready."); return nullptr; } return texture.CreateView(&viewDescriptor); } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::WriteTexture(wgpu::Texture texture, uint32_t bytesPerRow, uint32_t sizeBytes, const void* data, uint32_t srcOffset /*=0*/, wgpu::Origin3D dstOffset /*={0, 0, 0}*/, uint32_t dstMipLevel /*= 0*/, const char* description /*= nullptr*/) { auto& internals = (*this->Internals); if (!internals.DeviceReady) { vtkWarningMacro(<< "Cannot write data into texture because device is not ready."); return; } const auto copyTexture = vtkWebGPUTextureInternals::GetTexelCopyTextureInfo(texture, dstOffset, dstMipLevel); const auto textureDataLayout = vtkWebGPUTextureInternals::GetDataLayout(texture, bytesPerRow, srcOffset); wgpu::Extent3D textureExtents = { texture.GetWidth(), texture.GetHeight(), texture.GetDepthOrArrayLayers() }; vtkVLog(this->GetGPUMemoryLogVerbosity(), "Write texture {description: \"" << (description ? description : "null") << "\", size: " << sizeBytes << "}"); internals.Device.GetQueue().WriteTexture( ©Texture, data, sizeBytes, &textureDataLayout, &textureExtents); } //------------------------------------------------------------------------------ void vtkWebGPUConfiguration::SetGPUMemoryLogVerbosity(vtkLogger::Verbosity verbosity) { this->GPUMemoryLogVerbosity = verbosity; } //------------------------------------------------------------------------------ vtkLogger::Verbosity vtkWebGPUConfiguration::GetGPUMemoryLogVerbosity() { if (this->GPUMemoryLogVerbosity == vtkLogger::VERBOSITY_INVALID) { this->GPUMemoryLogVerbosity = vtkLogger::VERBOSITY_TRACE; // Find an environment variable that specifies logger verbosity const char* verbosityKey = "VTK_WEBGPU_MEMORY_LOG_VERBOSITY"; if (vtksys::SystemTools::HasEnv(verbosityKey)) { const char* verbosityCStr = vtksys::SystemTools::GetEnv(verbosityKey); const auto verbosity = vtkLogger::ConvertToVerbosity(verbosityCStr); if (verbosity > vtkLogger::VERBOSITY_INVALID) { this->GPUMemoryLogVerbosity = verbosity; } } } return this->GPUMemoryLogVerbosity; } void vtkWebGPUConfiguration::DumpMemoryStatistics() { #if VTK_USE_DAWN_WEBGPU auto* memoryDump = new DawnMemoryDump(); dawn::native::DumpMemoryStatistics(this->GetDevice().Get(), memoryDump); std::ostringstream os; memoryDump->PrintSelf(os, vtkIndent()); vtkVLog(this->GetGPUMemoryLogVerbosity(), << os.str()); delete memoryDump; #else // Cannot do anything here because we don't know if the textures/buffers // created through `this->CreateTexture` or `this->CreateBuffer` are still alive. vtkVLog(this->GetGPUMemoryLogVerbosity(), "Cannot determine memory statistics for allocated webgpu objects in this webgpu " "implementation"); #endif } VTK_ABI_NAMESPACE_END