{"id":15059851,"url":"https://github.com/malstraem/vulkan-interop-directx","last_synced_at":"2025-04-10T05:43:01.833Z","repository":{"id":167999490,"uuid":"608618515","full_name":"malstraem/vulkan-interop-directx","owner":"malstraem","description":"How to render into DirectX context using Vulkan","archived":false,"fork":false,"pushed_at":"2024-09-24T05:08:54.000Z","size":3438,"stargazers_count":23,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T06:54:41.489Z","etag":null,"topics":["directx","graphics","interop","interoperability","rendering","vulkan","winui","winui3","wpf"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/malstraem.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-02T11:51:04.000Z","updated_at":"2025-01-20T19:48:40.000Z","dependencies_parsed_at":"2023-12-16T13:00:20.786Z","dependency_job_id":"f9da9f61-0c2f-4865-af9a-169bda9218b4","html_url":"https://github.com/malstraem/vulkan-interop-directx","commit_stats":{"total_commits":45,"total_committers":2,"mean_commits":22.5,"dds":0.4666666666666667,"last_synced_commit":"99464151301f80505bae2b695cc3df7db781f74c"},"previous_names":["malstraem/vulkan-interop-directx"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malstraem%2Fvulkan-interop-directx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malstraem%2Fvulkan-interop-directx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malstraem%2Fvulkan-interop-directx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/malstraem%2Fvulkan-interop-directx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/malstraem","download_url":"https://codeload.github.com/malstraem/vulkan-interop-directx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166672,"owners_count":21058479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["directx","graphics","interop","interoperability","rendering","vulkan","winui","winui3","wpf"],"created_at":"2024-09-24T22:48:56.976Z","updated_at":"2025-04-10T05:43:01.809Z","avatar_url":"https://github.com/malstraem.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Overview\n\nThis repo demonstrates interop between Vulkan and DirectX via embedding the former into WinUI 3 and WPF using [VK_KHR_external_memory_win32](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_memory_win32.html) extension. It's basically follows great [Alexander Overvoorde's tutorial](https://vulkan-tutorial.com) for creating resources, but excludes swapchain infrastructure and creates a framebuffer using a shared Direct3D 11 texture.\n\nWhether you are using your own or third party abstractions - it should be easy to adapt the code for use.\n\nThe example is written naively, step by step - see [VulkanInterop.Initialize](source/VulkanInterop.cs#L481) and [Window code behind](samples/MainWindow.xaml.cs).\n\n[Silk.NET](https://github.com/dotnet/Silk.NET) - bindings used for DirectX and Vulkan calls.\n\n[Damaged Helmet](https://sketchfab.com/3d-models/battle-damaged-sci-fi-helmet-pbr-b81008d513954189a063ff901f7abfe4) - model used as an example. [SharpGLTF](https://github.com/vpenades/SharpGLTF) - loader used to read the model.\n\nhttps://github.com/malstraem/vulkan-interop-directx/assets/59914970/c08e451d-378a-4d47-a0ee-46e75faabb58\n\n# Interop process\n\nWe need to\n\n### 1. Prepare the back buffer texture\n\nIn case of WinUI - create a DXGI swapchain, get the texture and set the swapchain to the WinUI SwapChainPanel.\n\n```csharp\nvar swapchainDescription = new SwapChainDesc1\n{\n    ...\n    Width = width,\n    Height = height,\n    Format = Format.FormatR8G8B8A8Unorm,\n    SwapEffect = SwapEffect.FlipSequential,\n    SampleDesc = new SampleDesc(1u, 0u),\n    BufferUsage = DXGI.UsageBackBuffer\n};\n\nThrowHResult(dxgiFactory.CreateSwapChainForComposition\n(\n    dxgiDevice,\n    swapchainDescription,\n    default(ComPtr\u003cIDXGIOutput\u003e),\n    ref swapchain\n));\n\nbackbufferTexture = swapchain.GetBuffer\u003cID3D11Texture2D\u003e(0u);\n\ntarget.As\u003cISwapChainPanelNative\u003e().SetSwapChain(swapchain);\n```\n\nIn case of WPF - create a D3D9 texture and get the surface that will be used with WPF D3DImage.\n\n```csharp\nvoid* d3d9shared = null;\nThrowHResult(d3d9device.CreateTexture\n(\n    width,\n    height,\n    1u,\n    D3D9.UsageRendertarget,\n    Silk.NET.Direct3D9.Format.X8R8G8B8,\n    Pool.Default,\n    ref backbufferTexture,\n    ref d3d9shared\n));\n\nThrowHResult(backbufferTexture.GetSurfaceLevel(0u, ref surface));\n```\n\n### 2. Create a render target D3D11 texture in shared mode\n\nIn case of WinUI - this is regular D3D11 texture.\n\n```csharp\nvar renderTargetDescription = new Texture2DDesc\n{\n    ...\n    Width = width,\n    Height = height,\n    BindFlags = (uint)BindFlag.RenderTarget,\n    MiscFlags = (uint)ResourceMiscFlag.Shared\n};\n\nThrowHResult(d3d11device.CreateTexture2D(renderTargetDescription, null, ref renderTargetTexture));\n```\n\nWith WinUI we also need to query both back buffer and render target textures to D3D11 resources for future copy operations.\n\n```csharp\nbackbufferResource = backbufferTexture.QueryInterface\u003cID3D11Resource\u003e();\nrenderTargetResource = renderTargetTexture.QueryInterface\u003cID3D11Resource\u003e();\n```\n\nIn case of WPF, we get the texture using shared handle of the D3D9 texture we created earlier.\n\n```csharp\nrenderTargetTexture = d3d11device.OpenSharedResource\u003cID3D11Texture2D\u003e(d3d9shared);\n```\n\n### 3. Query D3D11 render target texture to the DXGI resource and get a shared handle\n\n```csharp\nvoid* handle;\n\nvar resource = renderTargetTexture.QueryInterface\u003cIDXGIResource\u003e();\nThrowHResult(resource.GetSharedHandle(\u0026handle));\nresource.Dispose();\n\nrenderTargetSharedHandle = (nint)handle;\n```\n\n### 4. On the Vulkan side - create an image using the previously obtained shared handle\n\nThen create the view and framebuffer - see [VulkanInterop.CreateImageViews](source/VulkanInterop.cs#L271).\n\n```csharp\nvar externalMemoryImageInfo = new ExternalMemoryImageCreateInfo\n(\n    handleTypes: ExternalMemoryHandleTypeFlags.D3D11TextureKmtBit\n);\n\nvar imageInfo = new ImageCreateInfo\n(\n    ...\n    format: targetFormat,\n    usage: ImageUsageFlags.ColorAttachmentBit,\n    pNext: \u0026externalMemoryImageInfo\n);\n\nvar importMemoryInfo = new ImportMemoryWin32HandleInfoKHR\n(\n    handleType: ExternalMemoryHandleTypeFlags.D3D11TextureKmtBit,\n    handle: renderTargetSharedHandle\n);\n\nvk.CreateImage(device, imageInfo, null, out directImage).Check();\n```\n\n\u003e Note that D3D9 `X8R8G8B8` texture format map to Vulkan `B8G8R8A8Unorm`\n\n### 5. Once the framebuffer is created, we are ready to interop\n\nWith WinUI we need to call Direct3D 11 to copy data from the render target to the back buffer and present it.\n\n```csharp\n// *rendering*\nd3d11context.CopyResource(backbufferResource, renderTargetResource);\nThrowHResult(swapchain.Present(0u, (uint)SwapChainFlag.None));\n```\n\nWith WPF we only need to set the D3D9 surface to the D3DImage, because the back buffer already contains the rendered image.\n\n```csharp\nd3dImage.Lock();\n// *rendering*\nd3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, (nint)d3d9surface.Handle);\nd3dImage.AddDirtyRect(new Int32Rect(0, 0, d3dImage.PixelWidth, d3dImage.PixelHeight));\nd3dImage.Unlock();\n```\n\n# Prerequisites and build\n\n* .NET 8\n* [Windows SDK 10.0.22621](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)\n\nThe example is `dotnet build` ready.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalstraem%2Fvulkan-interop-directx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmalstraem%2Fvulkan-interop-directx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmalstraem%2Fvulkan-interop-directx/lists"}