diff --git a/.github/actions/graphics-sdk/action.yml b/.github/actions/graphics-sdk/action.yml index d15abc04..8a4336a0 100644 --- a/.github/actions/graphics-sdk/action.yml +++ b/.github/actions/graphics-sdk/action.yml @@ -20,7 +20,7 @@ runs: name: Resolve graphics SDK cache identity shell: bash env: - SDK_INPUTS: ${{ hashFiles('eng/graphics/dependencies.lock.json', 'eng/graphics/windows-runtime.json', 'eng/graphics/build.py', 'eng/graphics/DawnSymbolBoundary.cmake', 'eng/graphics/dawn_exports.py', 'eng/graphics/dawn_native_device.cpp', 'eng/graphics/dawn_native_device_internal.h', 'experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h', 'eng/graphics/install-windows-sdk.ps1', '.github/actions/graphics-sdk/action.yml') }} + SDK_INPUTS: ${{ hashFiles('eng/graphics/dependencies.lock.json', 'eng/graphics/windows-runtime.json', 'eng/graphics/build.py', 'eng/graphics/DawnSymbolBoundary.cmake', 'eng/graphics/dawn_exports.py', 'eng/graphics/dawn_native_device.cpp', 'eng/graphics/dawn_native_device_internal.h', 'experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h', 'experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_linux_external_provider.h', 'eng/graphics/install-windows-sdk.ps1', '.github/actions/graphics-sdk/action.yml') }} run: | set -euo pipefail { diff --git a/docs/validation/linux-exact-vulkan-instance-query-649.md b/docs/validation/linux-exact-vulkan-instance-query-649.md index 650fc870..75ad81cf 100644 --- a/docs/validation/linux-exact-vulkan-instance-query-649.md +++ b/docs/validation/linux-exact-vulkan-instance-query-649.md @@ -31,11 +31,13 @@ lifetime. The host supplies the X11 `Display` and visual ID and must call `vkGetPhysicalDeviceXlibPresentationSupportKHR` through the returned resolver before it creates an Xlib surface. -`bind_dawn_linux_external_device` consumes v2 and copies the public -`wgpu::Device` into the existing native owner. That reference retains the Dawn -backend and every borrowed Vulkan identity. Allocator identity checks now -include the instance, resolver and presentation capability mask. No Vulkan -object returned by this API may be destroyed by the consumer, and no second +Issue #651 subsequently advances `bind_dawn_linux_external_device` to v3 so it +can require balanced access to Dawn's exact queue; direct v2 consumers remain +ABI compatible. The binding copies the public `wgpu::Device` into the existing +native owner. That reference retains the Dawn backend and every borrowed Vulkan +identity. Allocator identity checks include the instance, resolver, +presentation capability mask and v3 queue-access capability. No Vulkan object +returned by this API may be destroyed by the consumer, and no second instance/device or CPU transfer path is permitted. ## SDK and build contracts diff --git a/docs/validation/linux-external-image-interop-28.md b/docs/validation/linux-external-image-interop-28.md index 4e073ce1..c3174766 100644 --- a/docs/validation/linux-external-image-interop-28.md +++ b/docs/validation/linux-external-image-interop-28.md @@ -90,6 +90,12 @@ The X11 host must still call unsupported displays fail before surface creation and cannot select a second Vulkan instance or a CPU presentation route. +Issue #651 adds v3 without changing v1 or v2. V3 preserves the exact native +tuple and adds a balanced queue-access capability. Its acquire/release pair +holds Dawn's own device synchronization guard around external Graphite submit +and native present work, so those calls cannot race Dawn's submissions to the +same `VkQueue`. + The SDK now exposes `dawn_linux_external_device_factory`. An AppScene host with an exact-device Dawn integration creates the WebGPU instance/adapter/device and native allocator atomically, and returns one diff --git a/docs/validation/linux-vulkan-queue-access-651-28.md b/docs/validation/linux-vulkan-queue-access-651-28.md new file mode 100644 index 00000000..251ce40d --- /dev/null +++ b/docs/validation/linux-vulkan-queue-access-651-28.md @@ -0,0 +1,61 @@ +# Exact Dawn Vulkan queue access + +Issue #651 is a prerequisite of AppScene #265/#264 under WebScene #28. It +extends the Linux-only native-device bridge at Dawn revision +`2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378` without changing the v1 or v2 ABI. + +## Dawn synchronization seam + +The pinned Dawn source enables `Feature::ImplicitDeviceSynchronization` in +`PhysicalDevice.cpp`. Device initialization consequently creates the +ref-counted recursive `DeviceMutex`. Generated native API entry points acquire +`DeviceBase::GetGuard()` before queue operations. Vulkan +`Queue::SubmitPendingCommandsImpl` reaches `vkQueueSubmit` inside that guard, +and `Surface::APIPresent` explicitly holds the same guard through +`SwapChain::Present` and `vkQueuePresentKHR`. + +The bridge therefore needs no private queue replacement and no patch to +`QueueVk`. `websceneDawnAcquireVulkanQueueV3` retains both an external +`WGPUDevice` reference and an internal `DeviceBase` reference, then holds the +exact `DeviceGuard`. `websceneDawnReleaseVulkanQueueV3` drops the guard before +either reference. External work in that interval is serialized with all Dawn +submit and present work on the returned exact queue. + +## ABI and lifecycle + +`websceneDawnQueryVulkanDeviceV3` preserves the v2 instance, physical device, +device, queue, resolver, Xlib capability, queue family and UUID identity. It +also requires Dawn's device-guard capability. Query and acquire validate the +public size/version and authoritative live registry before private device +memory is dereferenced. Foreign and stale tokens fail at lookup; non-Vulkan, +lost, disconnecting, disconnected and destroyed devices fail before access is +published. + +Access scopes do not nest and acquire/release must run on the same thread. A +live-token registry rejects random, stale and double-release tokens without +dereferencing caller memory. Release removes the token from that registry +before unlocking, and unlocking occurs without the registry mutex held so Dawn +deferred work cannot invert the lock order. The C++ wrapper is non-copyable and +non-movable and keeps the complete public Linux device lifetime until its +same-thread destructor releases the token. Native operations inside the scope +must not call WebGPU or invoke user callbacks. + +An active token prevents last-reference teardown. Explicit destroy or device +loss on another thread waits for the bounded external operation, then observes +the released guard; an access request already waiting on the guard rechecks the +device state and fails if teardown won. The release path remains available +after loss so the mutex and retained device references cannot be stranded. + +## SDK boundary and remaining acceptance + +The Linux symbol allowlist requires v1, v2, v3, acquire and release together. +The installed public header, provenance bridge sources, export inventory and +SDK manifest remain content-hashed, and their inputs participate in the exact +graphics SDK cache key. No private Dawn header enters the SDK. + +Only `git diff --check` was run. Builds and authored tests remain unexecuted +under the directed fast policy. Physical Linux acceptance still requires the +AppScene external-memory and semaphore path to hold one access scope across +Graphite insertion/submission and native present, retire frames from private +completion primitives, and prove loss/teardown bounds and race-free +interleaving with Dawn submissions under validation layers. diff --git a/eng/graphics/DawnSymbolBoundary.cmake b/eng/graphics/DawnSymbolBoundary.cmake index 5ae67328..42c1adfd 100644 --- a/eng/graphics/DawnSymbolBoundary.cmake +++ b/eng/graphics/DawnSymbolBoundary.cmake @@ -27,7 +27,7 @@ function(webscene_isolate_dawn) target_link_options(webgpu_dawn PRIVATE "LINKER:-exported_symbols_list,${CMAKE_BINARY_DIR}/webscene-dawn.exports") elseif(UNIX) file(WRITE "${CMAKE_BINARY_DIR}/webscene-dawn.exports" - "{ global: wgpu*; websceneDawnQueryVulkanDeviceV1; websceneDawnQueryVulkanDeviceV2; local: *; };\n") + "{ global: wgpu*; websceneDawnQueryVulkanDeviceV1; websceneDawnQueryVulkanDeviceV2; websceneDawnQueryVulkanDeviceV3; websceneDawnAcquireVulkanQueueV3; websceneDawnReleaseVulkanQueueV3; local: *; };\n") target_link_options(webgpu_dawn PRIVATE "LINKER:--version-script=${CMAKE_BINARY_DIR}/webscene-dawn.exports") endif() # Windows exports only functions decorated by WGPU_SHARED_LIBRARY; diff --git a/eng/graphics/README.md b/eng/graphics/README.md index f8e5ec10..38d9b636 100644 --- a/eng/graphics/README.md +++ b/eng/graphics/README.md @@ -25,6 +25,13 @@ configuration controls its runtime. Windows build/link and runtime dependency validation (including D3D shader compiler packaging) remain hardware-runner gates. Never exchange CRT-owned allocations between these libraries. +Linux also exports the additive v3 query and balanced +`websceneDawnAcquireVulkanQueueV3`/`websceneDawnReleaseVulkanQueueV3` pair. An +active access retains the exact public device and Dawn object and holds the +same `DeviceGuard` used by generated Dawn API entry points and Vulkan surface +presentation. It serializes external submit/present work with Dawn without +publishing a second queue or any private Dawn type. + `GraphicsDependencies.cmake` verifies the SDK before importing it, including revision, RID, lock, settings, complete installed file inventory and content hashes. CMake cannot silently choose another `Dawn_DIR`. A dependency roll requires rebuilding headers and libraries together. Package manifests prove integrity and provenance, not API conformance or hardware execution. Dependency checkouts explicitly set both `core.autocrlf=false` and `core.eol=lf`: upstream `text=auto` attributes otherwise permit Windows-native CRLF license bytes. The optional Dawn C++ module wrapper is disabled; WebScene uses generated C/C++ headers and does not require compiler module scanning. This avoids a hosted GCC configuration that passed the language feature test but lacked CMake import-graph discovery support. diff --git a/eng/graphics/dawn_exports.py b/eng/graphics/dawn_exports.py index b403d9bc..1d1ff2e0 100644 --- a/eng/graphics/dawn_exports.py +++ b/eng/graphics/dawn_exports.py @@ -20,7 +20,10 @@ def inspect_exports(library, rid): symbol = match.group(1) symbols.append(symbol[1:] if rid.startswith("osx-") and symbol.startswith("_") else symbol) allowed_bridge = ({"websceneDawnQueryVulkanDeviceV1", - "websceneDawnQueryVulkanDeviceV2"} + "websceneDawnQueryVulkanDeviceV2", + "websceneDawnQueryVulkanDeviceV3", + "websceneDawnAcquireVulkanQueueV3", + "websceneDawnReleaseVulkanQueueV3"} if rid.startswith("linux-") else set()) unexpected = [name for name in symbols if not re.fullmatch(r"wgpu[A-Z][A-Za-z0-9]*", name) diff --git a/eng/graphics/dawn_native_device.cpp b/eng/graphics/dawn_native_device.cpp index c357c7fc..4bbc1b8e 100644 --- a/eng/graphics/dawn_native_device.cpp +++ b/eng/graphics/dawn_native_device.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "src/dawn/common/Ref.h" #include "src/dawn/native/Adapter.h" @@ -15,6 +17,8 @@ #include "src/dawn/native/vulkan/QueueVk.h" namespace { +struct queue_access_token; + struct live_devices { std::mutex mutex; struct entry { @@ -23,8 +27,44 @@ struct live_devices { entry* next; }; entry* first{}; + queue_access_token* first_access{}; +}; + +class external_device_reference final { + public: + explicit external_device_reference(dawn::native::DeviceBase* device) noexcept + : device_(device) { + device_->APIAddRef(); + } + ~external_device_reference() { device_->APIRelease(); } + external_device_reference(const external_device_reference&)=delete; + external_device_reference& operator=(const external_device_reference&)=delete; + + private: + dawn::native::DeviceBase* device_; +}; + +struct queue_access_token final { + queue_access_token(dawn::Ref device, + dawn::native::DeviceGuard guard,VkQueue queue, + uint32_t family) noexcept + : device(std::move(device)),external_reference(this->device.Get()), + guard(std::move(guard)),queue(queue),family(family), + owner(std::this_thread::get_id()) {} + + // Destruction order is significant: the guard unlocks before the external + // and internal device references are released. + dawn::Ref device; + external_device_reference external_reference; + dawn::native::DeviceGuard guard; + VkQueue queue; + uint32_t family; + std::thread::id owner; + queue_access_token* next{}; }; +thread_local queue_access_token* current_queue_access{}; + live_devices& devices() { // Construct in static storage without registering a destructor. Dawn // devices may outlive ordinary process statics during loader teardown. @@ -48,6 +88,25 @@ bool nonzero_uuid(const uint8_t* value) noexcept { for(size_t index=0;indexGetGlobalInfo(); + if(global.HasExt(dawn::native::vulkan::InstanceExt::Surface)) + capabilities|=WEBSCENE_DAWN_VULKAN_SURFACE_EXTENSION_ENABLED_V2; +#if defined(DAWN_USE_X11) + if(global.HasExt(dawn::native::vulkan::InstanceExt::XlibSurface)) + capabilities|=WEBSCENE_DAWN_VULKAN_XLIB_SURFACE_EXTENSION_ENABLED_V2; + if(device->fn.DestroySurfaceKHR&&device->fn.GetPhysicalDeviceSurfaceSupportKHR&& + device->fn.GetPhysicalDeviceSurfaceCapabilitiesKHR&& + device->fn.GetPhysicalDeviceSurfaceFormatsKHR&& + device->fn.GetPhysicalDeviceSurfacePresentModesKHR&& + device->fn.CreateXlibSurfaceKHR&& + device->fn.GetPhysicalDeviceXlibPresentationSupportKHR) + capabilities|=WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_PROCS_AVAILABLE_V2; +#endif + return capabilities; +} } namespace webscene::dawn_bridge { @@ -160,21 +219,7 @@ extern "C" webscene_dawn_native_device_status_v2 websceneDawnQueryVulkanDeviceV2 if(!nonzero_uuid(ids.deviceUUID)||!nonzero_uuid(ids.driverUUID)) return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_IDENTITY_V2; - uint32_t capabilities=0; - const auto& global=device->GetGlobalInfo(); - if(global.HasExt(dawn::native::vulkan::InstanceExt::Surface)) - capabilities|=WEBSCENE_DAWN_VULKAN_SURFACE_EXTENSION_ENABLED_V2; -#if defined(DAWN_USE_X11) - if(global.HasExt(dawn::native::vulkan::InstanceExt::XlibSurface)) - capabilities|=WEBSCENE_DAWN_VULKAN_XLIB_SURFACE_EXTENSION_ENABLED_V2; - if(device->fn.DestroySurfaceKHR&&device->fn.GetPhysicalDeviceSurfaceSupportKHR&& - device->fn.GetPhysicalDeviceSurfaceCapabilitiesKHR&& - device->fn.GetPhysicalDeviceSurfaceFormatsKHR&& - device->fn.GetPhysicalDeviceSurfacePresentModesKHR&& - device->fn.CreateXlibSurfaceKHR&& - device->fn.GetPhysicalDeviceXlibPresentationSupportKHR) - capabilities|=WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_PROCS_AVAILABLE_V2; -#endif + const auto capabilities=instance_capabilities(device); if((capabilities&WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2)!= WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2) return WEBSCENE_DAWN_NATIVE_DEVICE_XLIB_PRESENTATION_UNAVAILABLE_V2; @@ -197,3 +242,140 @@ extern "C" webscene_dawn_native_device_status_v2 websceneDawnQueryVulkanDeviceV2 *result=value; return WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V2; } + +extern "C" webscene_dawn_native_device_status_v3 websceneDawnQueryVulkanDeviceV3( + WGPUDevice token,webscene_dawn_native_device_v3* result) { + if(!token||!result)return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3; + if(result->struct_sizeversion!=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3) + return WEBSCENE_DAWN_NATIVE_DEVICE_INCOMPATIBLE_ABI_V3; + auto base=find_device(token); + if(!base)return WEBSCENE_DAWN_NATIVE_DEVICE_FOREIGN_DEVICE_V3; + auto guard=base->GetGuard(); + if(base->GetState()!=dawn::native::DeviceBase::State::Alive||!base->GetQueue()) + return WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3; + if(base->GetPhysicalDevice()->GetBackendType()!=wgpu::BackendType::Vulkan) + return WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V3; + if(!base->HasFeature(dawn::native::Feature::ImplicitDeviceSynchronization)) + return WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3; + + auto* device=dawn::native::vulkan::ToBackend(base.Get()); + auto* physical=dawn::native::vulkan::ToBackend(base->GetPhysicalDevice()); + auto* queue=dawn::native::vulkan::ToBackend(base->GetQueue()); + const auto instance=device->GetVkInstance(); + const auto resolver=device->fn.GetInstanceProcAddr; + if(instance==VK_NULL_HANDLE||!resolver||queue->GetVkQueue()==VK_NULL_HANDLE|| + device->GetGraphicsQueueFamily()==UINT32_MAX) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_IDENTITY_V3; + VkPhysicalDeviceIDProperties ids{}; + ids.sType=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; + VkPhysicalDeviceProperties2 properties{}; + properties.sType=VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + properties.pNext=&ids; + device->fn.GetPhysicalDeviceProperties2(physical->GetVkPhysicalDevice(),&properties); + if(!nonzero_uuid(ids.deviceUUID)||!nonzero_uuid(ids.driverUUID)) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_IDENTITY_V3; + const auto capabilities=instance_capabilities(device); + if((capabilities&WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2)!= + WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2) + return WEBSCENE_DAWN_NATIVE_DEVICE_XLIB_PRESENTATION_UNAVAILABLE_V3; + + webscene_dawn_native_device_v3 value{}; + value.struct_size=sizeof(value); + value.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + value.adapter=dawn::native::ToAPI(base->GetAdapter()); + value.device=token; + value.vk_instance=reinterpret_cast(instance); + value.vk_physical_device=reinterpret_cast(physical->GetVkPhysicalDevice()); + value.vk_device=reinterpret_cast(device->GetVkDevice()); + value.vk_queue=reinterpret_cast(queue->GetVkQueue()); + value.vk_get_instance_proc_addr= + reinterpret_cast(resolver); + value.queue_family=device->GetGraphicsQueueFamily(); + value.instance_capabilities=capabilities; + value.queue_access_capabilities= + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_DEVICE_GUARD_V3; + std::memcpy(value.device_uuid,ids.deviceUUID,VK_UUID_SIZE); + std::memcpy(value.driver_uuid,ids.driverUUID,VK_UUID_SIZE); + *result=value; + return WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3; +} + +extern "C" webscene_dawn_native_device_status_v3 websceneDawnAcquireVulkanQueueV3( + WGPUDevice public_device,webscene_dawn_vulkan_queue_access_v3* result) { + if(!public_device||!result) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3; + if(result->struct_sizeversion!=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3) + return WEBSCENE_DAWN_NATIVE_DEVICE_INCOMPATIBLE_ABI_V3; + if(result->access_token) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3; + if(current_queue_access) + return WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_NESTED_V3; + auto base=find_device(public_device); + if(!base)return WEBSCENE_DAWN_NATIVE_DEVICE_FOREIGN_DEVICE_V3; + auto guard=base->GetGuard(); + if(base->GetState()!=dawn::native::DeviceBase::State::Alive||!base->GetQueue()) + return WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3; + if(base->GetPhysicalDevice()->GetBackendType()!=wgpu::BackendType::Vulkan) + return WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V3; + if(!base->HasFeature(dawn::native::Feature::ImplicitDeviceSynchronization)) + return WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3; + auto* device=dawn::native::vulkan::ToBackend(base.Get()); + auto* queue=dawn::native::vulkan::ToBackend(base->GetQueue()); + const auto queue_handle=queue->GetVkQueue(); + const auto queue_family=device->GetGraphicsQueueFamily(); + if(queue_handle==VK_NULL_HANDLE||queue_family==UINT32_MAX) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_IDENTITY_V3; + auto* access=new(std::nothrow) queue_access_token( + std::move(base),std::move(guard),queue_handle,queue_family); + if(!access)return WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3; + { + auto& registry=devices(); + std::lock_guard lock(registry.mutex); + access->next=registry.first_access; + registry.first_access=access; + } + current_queue_access=access; + webscene_dawn_vulkan_queue_access_v3 value{}; + value.struct_size=sizeof(value); + value.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + value.device=public_device; + value.vk_queue=reinterpret_cast(access->queue); + value.queue_family=access->family; + value.access_token=access; + *result=value; + return WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3; +} + +extern "C" webscene_dawn_native_device_status_v3 websceneDawnReleaseVulkanQueueV3( + webscene_dawn_vulkan_queue_access_v3* value) { + if(!value) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3; + if(value->struct_sizeversion!=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3) + return WEBSCENE_DAWN_NATIVE_DEVICE_INCOMPATIBLE_ABI_V3; + if(!value->access_token) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3; + auto& registry=devices(); + queue_access_token* access{}; + { + std::lock_guard lock(registry.mutex); + auto** link=®istry.first_access; + while(*link&&static_cast(*link)!=value->access_token) + link=&(*link)->next; + if(!*link)return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_QUEUE_ACCESS_V3; + access=*link; + if(access->owner!=std::this_thread::get_id()) + return WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_WRONG_THREAD_V3; + if(current_queue_access!=access) + return WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_QUEUE_ACCESS_V3; + *link=access->next; + } + const bool lost=access->device->GetState()!=dawn::native::DeviceBase::State::Alive; + current_queue_access=nullptr; + *value={}; + delete access; + return lost ? WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3 + : WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3; +} diff --git a/eng/graphics/tests/test_dawn_exports.py b/eng/graphics/tests/test_dawn_exports.py index 66a6ffbd..9c552a56 100644 --- a/eng/graphics/tests/test_dawn_exports.py +++ b/eng/graphics/tests/test_dawn_exports.py @@ -19,7 +19,10 @@ def test_native_symbol_formats(self): ("osx-arm64", "00001000 T _wgpuCreateInstance\n00001010 T _wgpuGetProcAddress\n"), ("linux-x64", "00001000 T wgpuCreateInstance\n00001010 T wgpuGetProcAddress\n" "00001020 T websceneDawnQueryVulkanDeviceV1\n" - "00001030 T websceneDawnQueryVulkanDeviceV2\n"), + "00001030 T websceneDawnQueryVulkanDeviceV2\n" + "00001040 T websceneDawnQueryVulkanDeviceV3\n" + "00001050 T websceneDawnAcquireVulkanQueueV3\n" + "00001060 T websceneDawnReleaseVulkanQueueV3\n"), ("win-x64", " 1 0 00001000 wgpuCreateInstance\n 2 1 00001010 wgpuGetProcAddress\n")]: with self.subTest(rid=rid): self.assertEqual(self.inspect(text, rid)["status"], "passed") @@ -35,6 +38,10 @@ def test_linux_bridge_is_required_and_exact(self): "1020 T websceneDawnQueryVulkanDeviceV1\n", "1000 T wgpuCreateInstance\n1010 T wgpuGetProcAddress\n" "1020 T websceneDawnQueryVulkanDeviceV2\n", + "1000 T wgpuCreateInstance\n1010 T wgpuGetProcAddress\n" + "1020 T websceneDawnQueryVulkanDeviceV1\n" + "1030 T websceneDawnQueryVulkanDeviceV2\n" + "1040 T websceneDawnQueryVulkanDeviceV3\n", ]: with self.subTest(text=text), self.assertRaises(ValueError): self.inspect(text, "linux-x64") diff --git a/eng/graphics/tests/test_dawn_native_device_bridge.py b/eng/graphics/tests/test_dawn_native_device_bridge.py index 7e1fa463..9374e91a 100644 --- a/eng/graphics/tests/test_dawn_native_device_bridge.py +++ b/eng/graphics/tests/test_dawn_native_device_bridge.py @@ -14,13 +14,57 @@ def test_v2_validates_public_inputs_before_live_registry_lookup(self): self.assertLess(query.index("result->version"), query.index("find_device(token)")) self.assertLess(query.index("find_device(token)"), query.index("base->GetGuard()")) + def test_v3_query_and_acquire_validate_before_private_dereference(self): + source = (ROOT / "eng/graphics/dawn_native_device.cpp").read_text() + for signature in ("websceneDawnQueryVulkanDeviceV3(", + "websceneDawnAcquireVulkanQueueV3("): + body = source.split(signature, 1)[1] + self.assertLess(body.index("!"), body.index("find_device(")) + self.assertLess(body.index("struct_size"), body.index("find_device(")) + self.assertLess(body.index("version"), body.index("find_device(")) + self.assertLess(body.index("find_device("), body.index("base->GetGuard()")) + + def test_v3_holds_dawns_device_guard_and_balanced_device_references(self): + source = (ROOT / "eng/graphics/dawn_native_device.cpp").read_text() + for token in ( + "dawn::native::DeviceGuard guard", + "device_->APIAddRef()", + "device_->APIRelease()", + "thread_local queue_access_token* current_queue_access", + "QUEUE_ACCESS_NESTED_V3", + "QUEUE_ACCESS_WRONG_THREAD_V3", + "delete access", + ): + self.assertIn(token, source) + + def test_cpp_wrapper_is_lexical_and_sdk_targets_publish_v3(self): + wrapper = (ROOT / "experiments/WebScene.NativeEngine.Probe/native/graphics/" + "dawn_linux_external_provider.h").read_text() + for token in ( + "class dawn_linux_external_queue_access final", + "with_dawn_linux_external_queue_access", + "websceneDawnAcquireVulkanQueueV3", + "websceneDawnReleaseVulkanQueueV3", + "dawn_linux_external_queue_access&&)=delete", + ): + self.assertIn(token, wrapper) + for path in ( + "src/WebScene.Sdk/cmake/LinuxSDK.cmake", + "src/WebScene.Sdk/cmake/WebSceneLinuxSDK.cmake", + "src/WebScene.Sdk/cmake/WebSceneLinuxConfig.cmake", + ): + self.assertIn( + "WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3=3", + (ROOT / path).read_text(), + ) + def test_registry_is_compatible_with_dawns_no_exception_build(self): source = (ROOT / "eng/graphics/dawn_native_device.cpp").read_text() self.assertIn("new(std::nothrow) live_devices::entry", source) self.assertNotIn("catch(", source.replace(" ", "")) self.assertNotIn("std::unordered_map", source) - def test_v1_and_v2_are_staged_hashed_and_exported_together(self): + def test_v1_v2_and_v3_are_staged_hashed_and_exported_together(self): header = (ROOT / "experiments/WebScene.NativeEngine.Probe/native/graphics/" "webscene/dawn_native_device.h").read_text() symbols = (ROOT / "eng/graphics/DawnSymbolBoundary.cmake").read_text() @@ -28,11 +72,16 @@ def test_v1_and_v2_are_staged_hashed_and_exported_together(self): builder = (ROOT / "eng/graphics/build.py").read_text() verifier = (ROOT / "eng/graphics/verify-sdk.py").read_text() cache = (ROOT / ".github/actions/graphics-sdk/action.yml").read_text() - for version in ("V1", "V2"): + for version in ("V1", "V2", "V3"): name = f"websceneDawnQueryVulkanDevice{version}" self.assertIn(name, header) self.assertIn(name, symbols) self.assertIn(name, exports) + for name in ("websceneDawnAcquireVulkanQueueV3", + "websceneDawnReleaseVulkanQueueV3"): + self.assertIn(name, header) + self.assertIn(name, symbols) + self.assertIn(name, exports) for name in ("dawn_native_device.h", "dawn_native_device.cpp", "dawn_native_device_internal.h"): self.assertIn(name, builder) diff --git a/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt b/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt index c4dcc97b..060a1349 100644 --- a/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt +++ b/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt @@ -382,7 +382,8 @@ if(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) target_compile_definitions(webscene_graphics_dawn_linux_external_provider_contract_tests PRIVATE WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION=1 WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION=1 - WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2) + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2 + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3=3) target_include_directories(webscene_graphics_dawn_linux_external_provider_contract_tests PRIVATE native) target_link_libraries(webscene_graphics_dawn_linux_external_provider_contract_tests PRIVATE dawn::webgpu_dawn) diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_linux_external_provider.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_linux_external_provider.h index 719de7fd..65c437d4 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_linux_external_provider.h +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_linux_external_provider.h @@ -3,7 +3,9 @@ #include "linux_external_image.h" #include "webscene/dawn_native_device.h" #include +#include #include +#include namespace webscene::graphics { #ifndef WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION @@ -33,7 +35,11 @@ enum class dawn_linux_external_status : uint32_t { invalid_device_factory, native_device_query_failed, device_lost, - xlib_presentation_unavailable + xlib_presentation_unavailable, + queue_access_unavailable, + queue_access_nested, + invalid_queue_access, + queue_access_wrong_thread }; struct dawn_linux_external_device_lifetime { @@ -45,8 +51,10 @@ struct dawn_linux_external_device_lifetime { const void* vk_physical_device{}; const void* vk_device{}; const void* vk_queue{}; - webscene_dawn_vk_get_instance_proc_addr_v2 vk_get_instance_proc_addr{}; - webscene_dawn_vulkan_instance_capabilities_v2 instance_capabilities{}; + webscene_dawn_vk_get_instance_proc_addr_v3 vk_get_instance_proc_addr{}; + webscene_dawn_vulkan_instance_capabilities_v3 instance_capabilities{}; + webscene_dawn_vulkan_queue_access_capabilities_v3 + queue_access_capabilities{}; std::array device_uuid{}; std::array driver_uuid{}; uint32_t dawn_queue_family{UINT32_MAX}; @@ -73,17 +81,19 @@ inline dawn_linux_external_status bind_dawn_linux_external_device( return dawn_linux_external_status::invalid_argument; if(device_lost->load(std::memory_order_acquire)) return dawn_linux_external_status::device_lost; - webscene_dawn_native_device_v2 native{}; + webscene_dawn_native_device_v3 native{}; native.struct_size=sizeof(native); - native.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2; - const auto status=websceneDawnQueryVulkanDeviceV2(device.Get(),&native); - if(status==WEBSCENE_DAWN_NATIVE_DEVICE_LOST_V2) + native.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + const auto status=websceneDawnQueryVulkanDeviceV3(device.Get(),&native); + if(status==WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3) return dawn_linux_external_status::device_lost; - if(status==WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V2) + if(status==WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V3) return dawn_linux_external_status::not_vulkan; - if(status==WEBSCENE_DAWN_NATIVE_DEVICE_XLIB_PRESENTATION_UNAVAILABLE_V2) + if(status==WEBSCENE_DAWN_NATIVE_DEVICE_XLIB_PRESENTATION_UNAVAILABLE_V3) return dawn_linux_external_status::xlib_presentation_unavailable; - if(status!=WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V2) + if(status==WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3) + return dawn_linux_external_status::queue_access_unavailable; + if(status!=WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3) return dawn_linux_external_status::native_device_query_failed; if(native.adapter!=adapter.Get()||native.device!=device.Get()|| !native.vk_instance||!native.vk_physical_device||!native.vk_device|| @@ -91,7 +101,10 @@ inline dawn_linux_external_status bind_dawn_linux_external_device( native.queue_family==UINT32_MAX|| (native.instance_capabilities& WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2)!= - WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2) + WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2|| + (native.queue_access_capabilities& + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3)!= + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3) return dawn_linux_external_status::native_device_query_failed; auto lifetime=std::make_shared(); lifetime->dawn_adapter_token=native.adapter; @@ -102,6 +115,7 @@ inline dawn_linux_external_status bind_dawn_linux_external_device( lifetime->vk_queue=native.vk_queue; lifetime->vk_get_instance_proc_addr=native.vk_get_instance_proc_addr; lifetime->instance_capabilities=native.instance_capabilities; + lifetime->queue_access_capabilities=native.queue_access_capabilities; for(size_t index=0;indexdevice_uuid.size();++index) { lifetime->device_uuid[index]=native.device_uuid[index]; lifetime->driver_uuid[index]=native.driver_uuid[index]; @@ -227,6 +241,9 @@ inline bool same_dawn_linux_external_identity( (expected->instance_capabilities& WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2)== WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2&& + (expected->queue_access_capabilities& + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3)== + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3&& nonzero_uuid(expected->device_uuid)&&nonzero_uuid(expected->driver_uuid)&& expected->dawn_queue_family!=UINT32_MAX; } @@ -237,6 +254,93 @@ inline bool dawn_linux_external_device_is_lost( value->device_lost->load(std::memory_order_acquire); } +inline dawn_linux_external_status dawn_linux_queue_access_status( + webscene_dawn_native_device_status_v3 value) noexcept { + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3) + return dawn_linux_external_status::success; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3) + return dawn_linux_external_status::device_lost; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_NESTED_V3) + return dawn_linux_external_status::queue_access_nested; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_QUEUE_ACCESS_V3) + return dawn_linux_external_status::invalid_queue_access; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_WRONG_THREAD_V3) + return dawn_linux_external_status::queue_access_wrong_thread; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3) + return dawn_linux_external_status::queue_access_unavailable; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3) + return dawn_linux_external_status::invalid_argument; + if(value==WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V3) + return dawn_linux_external_status::not_vulkan; + return dawn_linux_external_status::native_device_query_failed; +} + +// Lexically scoped access to Dawn's exact queue. The scope is neither movable +// nor copyable, so ordinary use acquires and releases on one thread. Calling a +// WebGPU entry point, nesting access, or transferring the scope is forbidden. +class dawn_linux_external_queue_access final { + std::shared_ptr lifetime_; + webscene_dawn_vulkan_queue_access_v3 access_{}; + bool active_{}; +public: + dawn_linux_external_queue_access( + std::shared_ptr lifetime, + dawn_linux_external_status& status) noexcept + :lifetime_(std::move(lifetime)) { + status=dawn_linux_external_status::invalid_argument; + if(!lifetime_||!lifetime_->native_owner||!lifetime_->dawn_device_token|| + dawn_linux_external_device_is_lost(lifetime_)) { + if(lifetime_&&dawn_linux_external_device_is_lost(lifetime_)) + status=dawn_linux_external_status::device_lost; + return; + } + access_.struct_size=sizeof(access_); + access_.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + const auto acquired=websceneDawnAcquireVulkanQueueV3( + lifetime_->dawn_device_token,&access_); + status=dawn_linux_queue_access_status(acquired); + if(acquired!=WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3)return; + if(access_.device!=lifetime_->dawn_device_token|| + access_.vk_queue!=lifetime_->vk_queue|| + access_.queue_family!=lifetime_->dawn_queue_family) { + const auto released=websceneDawnReleaseVulkanQueueV3(&access_); + if(released!=WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3&& + released!=WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3) + std::terminate(); + status=dawn_linux_external_status::device_mismatch; + return; + } + active_=true; + } + ~dawn_linux_external_queue_access() { + if(!active_)return; + const auto released=websceneDawnReleaseVulkanQueueV3(&access_); + active_=false; + if(released!=WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3&& + released!=WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3) + std::terminate(); + } + dawn_linux_external_queue_access(const dawn_linux_external_queue_access&)=delete; + dawn_linux_external_queue_access& operator=( + const dawn_linux_external_queue_access&)=delete; + dawn_linux_external_queue_access(dawn_linux_external_queue_access&&)=delete; + dawn_linux_external_queue_access& operator=( + dawn_linux_external_queue_access&&)=delete; + explicit operator bool() const noexcept{return active_;} + const void* vk_queue() const noexcept{return access_.vk_queue;} + uint32_t queue_family() const noexcept{return access_.queue_family;} +}; + +template +dawn_linux_external_status with_dawn_linux_external_queue_access( + std::shared_ptr lifetime, + Operation&& operation) { + dawn_linux_external_status status{}; + dawn_linux_external_queue_access access(std::move(lifetime),status); + if(!access)return status; + return std::forward(operation)(access); +} + inline bool dawn_linux_snapshot_matches_device( const linux_external_image_snapshot& snapshot, const dawn_linux_external_device_lifetime& device) noexcept { diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h index 80503bd3..d073cd47 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webscene/dawn_native_device.h @@ -12,6 +12,9 @@ extern "C" { #ifndef WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2 #define WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2 2U #endif +#ifndef WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3 +#define WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3 3U +#endif /* The original name remains the v1 value for source compatibility. New code * selects the explicit versioned constant matching the query it calls. */ #ifndef WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION @@ -94,6 +97,69 @@ typedef struct webscene_dawn_native_device_v2 { uint8_t driver_uuid[16]; } webscene_dawn_native_device_v2; +typedef uint32_t webscene_dawn_native_device_status_v3; +enum { + WEBSCENE_DAWN_NATIVE_DEVICE_SUCCESS_V3 = 0, + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3 = 1, + WEBSCENE_DAWN_NATIVE_DEVICE_INCOMPATIBLE_ABI_V3 = 2, + WEBSCENE_DAWN_NATIVE_DEVICE_FOREIGN_DEVICE_V3 = 3, + WEBSCENE_DAWN_NATIVE_DEVICE_NOT_VULKAN_V3 = 4, + WEBSCENE_DAWN_NATIVE_DEVICE_LOST_OR_CLOSING_V3 = 5, + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_IDENTITY_V3 = 6, + WEBSCENE_DAWN_NATIVE_DEVICE_XLIB_PRESENTATION_UNAVAILABLE_V3 = 7, + WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_UNAVAILABLE_V3 = 8, + WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_NESTED_V3 = 9, + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_QUEUE_ACCESS_V3 = 10, + WEBSCENE_DAWN_NATIVE_DEVICE_QUEUE_ACCESS_WRONG_THREAD_V3 = 11 +}; + +typedef webscene_dawn_vk_proc_v2 webscene_dawn_vk_proc_v3; +typedef webscene_dawn_vk_get_instance_proc_addr_v2 + webscene_dawn_vk_get_instance_proc_addr_v3; +typedef webscene_dawn_vulkan_instance_capabilities_v2 + webscene_dawn_vulkan_instance_capabilities_v3; + +typedef uint32_t webscene_dawn_vulkan_queue_access_capabilities_v3; +enum { + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_DEVICE_GUARD_V3 = 1U << 0, + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3 = + WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_DEVICE_GUARD_V3 +}; + +/* V3 preserves the v2 native tuple and proves that the exported balanced + * access functions serialize with Dawn submissions and presentation. */ +typedef struct webscene_dawn_native_device_v3 { + uint32_t struct_size; + uint32_t version; + WGPUAdapter adapter; + WGPUDevice device; + void* vk_instance; + void* vk_physical_device; + void* vk_device; + void* vk_queue; + webscene_dawn_vk_get_instance_proc_addr_v3 vk_get_instance_proc_addr; + uint32_t queue_family; + webscene_dawn_vulkan_instance_capabilities_v3 instance_capabilities; + webscene_dawn_vulkan_queue_access_capabilities_v3 queue_access_capabilities; + uint32_t reserved; + uint8_t device_uuid[16]; + uint8_t driver_uuid[16]; +} webscene_dawn_native_device_v3; + +/* Opaque balanced access to the exact queue reported by the v3 query. One + * access may be active per thread. Acquire and release must occur on the same + * thread, and access scopes must not nest. The caller must not copy or modify + * an active value. */ +typedef struct webscene_dawn_vulkan_queue_access_v3 { + uint32_t struct_size; + uint32_t version; + WGPUDevice device; + void* vk_queue; + uint32_t queue_family; + uint32_t reserved; + void* access_token; +} webscene_dawn_vulkan_queue_access_v3; + #if defined(_WIN32) #define WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT __declspec(dllimport) #elif defined(__GNUC__) @@ -118,6 +184,22 @@ WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT webscene_dawn_native_device_status_v2 websceneDawnQueryVulkanDeviceV2( WGPUDevice device, webscene_dawn_native_device_v2* result); +/* V3 succeeds only when Dawn's device-wide synchronization guard protects the + * exact Vulkan queue. Failure leaves result unchanged. */ +WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT webscene_dawn_native_device_status_v3 +websceneDawnQueryVulkanDeviceV3( + WGPUDevice device, webscene_dawn_native_device_v3* result); + +/* Acquire retains the WGPUDevice and Dawn's internal device until release. + * It fails for foreign, stale, lost or closing devices before private state is + * used. Release always ends a valid same-thread scope, including after loss. */ +WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT webscene_dawn_native_device_status_v3 +websceneDawnAcquireVulkanQueueV3( + WGPUDevice device, webscene_dawn_vulkan_queue_access_v3* result); +WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT webscene_dawn_native_device_status_v3 +websceneDawnReleaseVulkanQueueV3( + webscene_dawn_vulkan_queue_access_v3* access); + #undef WEBSCENE_DAWN_NATIVE_DEVICE_EXPORT #ifdef __cplusplus diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_linux_external_provider_contract_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_linux_external_provider_contract_tests.cpp index b3e084d7..e2fd3469 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_linux_external_provider_contract_tests.cpp +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_linux_external_provider_contract_tests.cpp @@ -11,6 +11,7 @@ int main(){ static_assert(WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION==1); static_assert(WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V1==1); static_assert(WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2==2); + static_assert(WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3==3); webscene_dawn_native_device_v1 query{}; query.struct_size=sizeof(query); query.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION; @@ -39,6 +40,22 @@ int main(){ query_v2.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V1; require(websceneDawnQueryVulkanDeviceV2(first,&query_v2)== WEBSCENE_DAWN_NATIVE_DEVICE_INCOMPATIBLE_ABI_V2); + webscene_dawn_native_device_v3 query_v3{}; + query_v3.struct_size=sizeof(query_v3); + query_v3.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + require(websceneDawnQueryVulkanDeviceV3(nullptr,&query_v3)== + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3); + require(websceneDawnQueryVulkanDeviceV3(first,&query_v3)== + WEBSCENE_DAWN_NATIVE_DEVICE_FOREIGN_DEVICE_V3); + webscene_dawn_vulkan_queue_access_v3 access{}; + access.struct_size=sizeof(access); + access.version=WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3; + require(websceneDawnAcquireVulkanQueueV3(nullptr,&access)== + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3); + require(websceneDawnAcquireVulkanQueueV3(first,&access)== + WEBSCENE_DAWN_NATIVE_DEVICE_FOREIGN_DEVICE_V3); + require(websceneDawnReleaseVulkanQueueV3(&access)== + WEBSCENE_DAWN_NATIVE_DEVICE_INVALID_ARGUMENT_V3); require(valid_dawn_linux_external_binding(first,first,first)); require(!valid_dawn_linux_external_binding(nullptr,first,first)); require(!valid_dawn_linux_external_binding(first,second,first)); @@ -54,6 +71,7 @@ int main(){ device->vk_queue=reinterpret_cast(uintptr_t{5}); device->vk_get_instance_proc_addr=&fake_instance_resolver; device->instance_capabilities=WEBSCENE_DAWN_VULKAN_XLIB_PRESENTATION_REQUIRED_V2; + device->queue_access_capabilities=WEBSCENE_DAWN_VULKAN_QUEUE_ACCESS_REQUIRED_V3; device->device_uuid[0]=1; device->driver_uuid[0]=2;device->dawn_queue_family=4; device->device_lost=std::make_shared>(false); @@ -63,6 +81,10 @@ int main(){ device->instance_capabilities=0; require(!same_dawn_linux_external_identity(device,device)); device->instance_capabilities=capabilities; + const auto queue_access_capabilities=device->queue_access_capabilities; + device->queue_access_capabilities=0; + require(!same_dawn_linux_external_identity(device,device)); + device->queue_access_capabilities=queue_access_capabilities; const auto resolver=device->vk_get_instance_proc_addr; device->vk_get_instance_proc_addr=nullptr; require(!same_dawn_linux_external_identity(device,device)); diff --git a/src/WebScene.Sdk/cmake/LinuxSDK.cmake b/src/WebScene.Sdk/cmake/LinuxSDK.cmake index 59825174..d1bfdc00 100644 --- a/src/WebScene.Sdk/cmake/LinuxSDK.cmake +++ b/src/WebScene.Sdk/cmake/LinuxSDK.cmake @@ -38,7 +38,8 @@ if(WEBSCENE_SDK_ENABLE_WEBGPU) target_compile_definitions(webscene_sdk_webgpu INTERFACE WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION=1 WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION=1 - WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2) + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2 + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3=3) target_link_libraries(webscene_sdk_webgpu INTERFACE webscene_core dawn::webgpu_dawn) add_library(WebScene::WebGPU ALIAS webscene_sdk_webgpu) install(DIRECTORY "${WEBSCENE_GRAPHICS_SDK_ROOT}/dawn/include/" DESTINATION include) diff --git a/src/WebScene.Sdk/cmake/WebSceneLinuxConfig.cmake b/src/WebScene.Sdk/cmake/WebSceneLinuxConfig.cmake index bf052860..b75689bc 100644 --- a/src/WebScene.Sdk/cmake/WebSceneLinuxConfig.cmake +++ b/src/WebScene.Sdk/cmake/WebSceneLinuxConfig.cmake @@ -41,7 +41,7 @@ if(NOT TARGET WebScene::Core) set_target_properties(WebScene::WebGPU PROPERTIES INTERFACE_LINK_LIBRARIES "WebScene::Core;WebScene::_Dawn;Threads::Threads;${CMAKE_DL_LIBS}" INTERFACE_INCLUDE_DIRECTORIES "${WebScene_SDK_ROOT}/include/graphics" - INTERFACE_COMPILE_DEFINITIONS "WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION=1;WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION=1;WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2") + INTERFACE_COMPILE_DEFINITIONS "WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION=1;WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION=1;WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2;WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3=3") endif() add_executable(WebScene::Compiler IMPORTED GLOBAL) set_property(TARGET WebScene::Compiler PROPERTY IMPORTED_LOCATION "${WebScene_SDK_ROOT}/bin/webscene-uic") diff --git a/src/WebScene.Sdk/cmake/WebSceneLinuxSDK.cmake b/src/WebScene.Sdk/cmake/WebSceneLinuxSDK.cmake index b31e2b58..3a3816b4 100644 --- a/src/WebScene.Sdk/cmake/WebSceneLinuxSDK.cmake +++ b/src/WebScene.Sdk/cmake/WebSceneLinuxSDK.cmake @@ -43,7 +43,8 @@ if(WEBSCENE_SDK_WEBGPU) target_compile_definitions(webscene_sdk_webgpu INTERFACE WEBSCENE_DAWN_LINUX_EXTERNAL_FACTORY_VERSION=1 WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION=1 - WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2) + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V2=2 + WEBSCENE_DAWN_NATIVE_DEVICE_ABI_VERSION_V3=3) target_link_libraries(webscene_sdk_webgpu INTERFACE webscene_core dawn::webgpu_dawn Threads::Threads) add_library(WebScene::WebGPU ALIAS webscene_sdk_webgpu) install(DIRECTORY "${WEBSCENE_GRAPHICS_SDK_ROOT}/dawn/include/" DESTINATION include)