From ef4785f32516963fbdaa6befacd0e3571a37be2c Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 9 May 2024 22:29:00 +0100 Subject: [PATCH] wineopenxr: Support creating a D3D12 XR instance. --- wineopenxr/openxr.c | 179 ++++++++++++++++++++++++++-- wineopenxr/openxr_private.h | 5 + wineopenxr/vkd3d-proton-interop.h | 186 ++++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 wineopenxr/vkd3d-proton-interop.h diff --git a/wineopenxr/openxr.c b/wineopenxr/openxr.c index df686375..cbc1bd94 100644 --- a/wineopenxr/openxr.c +++ b/wineopenxr/openxr.c @@ -28,6 +28,7 @@ #include "wine/vulkan.h" #define VULKAN_H_ 1// tell dxvk-interop not to include vulkan.h #include "dxvk-interop.h" +#include "vkd3d-proton-interop.h" #undef WINE_VK_HOST #define XR_USE_GRAPHICS_API_D3D11 1 #define XR_USE_GRAPHICS_API_D3D12 1 @@ -79,6 +80,7 @@ static struct substitute_extensions[] = { {"XR_KHR_D3D11_enable", "XR_KHR_vulkan_enable"}, + {"XR_KHR_D3D12_enable", "XR_KHR_vulkan_enable"}, {"XR_KHR_win32_convert_performance_counter_time", "XR_KHR_convert_timespec_time", TRUE, TRUE}, }; @@ -682,7 +684,6 @@ XrResult WINAPI wine_xrEnumerateInstanceExtensionProperties(const char *layerNam else dst = (*propertyCountOutput)++; strcpy(properties[dst].extensionName, substitute_extensions[j].win32_ext); - break; } } } @@ -790,9 +791,13 @@ XrResult WINAPI wine_xrGetD3D11GraphicsRequirementsKHR(XrInstance instance, XrResult WINAPI wine_xrGetD3D12GraphicsRequirementsKHR(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR *graphicsRequirements) { - WINE_FIXME("unimplemented\n"); - /* FIXME */ - return XR_ERROR_INITIALIZATION_FAILED; + XrGraphicsRequirementsD3D11KHR requirements; + XrResult result = wine_xrGetD3D11GraphicsRequirementsKHR(instance, systemId, &requirements); + if (result != XR_SUCCESS) + return result; + graphicsRequirements->adapterLuid = requirements.adapterLuid; + graphicsRequirements->minFeatureLevel = D3D_FEATURE_LEVEL_11_0; + return XR_SUCCESS; } XrResult WINAPI wine_xrGetInstanceProcAddr(XrInstance instance, const char *fn_name, PFN_xrVoidFunction *out_fn) @@ -907,6 +912,12 @@ XrResult WINAPI wine_xrDestroyInstance(XrInstance instance) if(wine_instance->dxvk_device) wine_instance->dxvk_device->lpVtbl->Release(wine_instance->dxvk_device); + if (wine_instance->d3d12_device) + { + wine_instance->d3d12_device->lpVtbl->Release(wine_instance->d3d12_device); + wine_instance->d3d12_queue->lpVtbl->Release(wine_instance->d3d12_queue); + } + heap_free(wine_instance); return XR_SUCCESS; @@ -1041,6 +1052,60 @@ XrResult WINAPI wine_xrCreateSession(XrInstance instance, const XrSessionCreateI break; } + case XR_TYPE_GRAPHICS_BINDING_D3D12_KHR: + { + const XrGraphicsBindingD3D12KHR *their_d3d12_binding = createInfo->next; + HRESULT hr; + UINT32 queue_index; + VkQueueFlags queue_flags; + ID3D12DeviceExt1 *device_ext; + hr = ID3D12Device_QueryInterface(their_d3d12_binding->device, &IID_ID3D12DXVKInteropDevice, (void**)&wine_instance->d3d12_device); + if (FAILED(hr)) + { + WINE_WARN("Given ID3D12Device doesn't support ID3D12DXVKInteropDevice. Only vkd3d-proton is supported.\n"); + return XR_ERROR_VALIDATION_FAILURE; + } + hr = ID3D12Device_QueryInterface(their_d3d12_binding->device, &IID_ID3D12DeviceExt1, (void **)&device_ext); + if (FAILED(hr)) + { + WINE_WARN("Given ID3D12Device doesn't support ID3D12DeviceExt1. Only vkd3d-proton is supported.\n"); + return XR_ERROR_VALIDATION_FAILURE; + } + + our_vk_binding.type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR; + our_vk_binding.next = NULL; + + wine_instance->d3d12_queue = their_d3d12_binding->queue; + their_d3d12_binding->queue->lpVtbl->AddRef(their_d3d12_binding->queue); + + wine_instance->d3d12_device->lpVtbl->GetVulkanHandles(wine_instance->d3d12_device, + &our_vk_binding.instance, &our_vk_binding.physicalDevice, &our_vk_binding.device); + device_ext->lpVtbl->GetVulkanQueueInfoEx(device_ext, their_d3d12_binding->queue, + &wine_instance->vk_queue, &queue_index, &queue_flags, &our_vk_binding.queueFamilyIndex); + device_ext->lpVtbl->Release(device_ext); + + wine_instance->vk_device = our_vk_binding.device = get_native_VkDevice(our_vk_binding.device); + wine_instance->vk_queue = get_native_VkQueue(wine_instance->vk_queue); + our_vk_binding.queueIndex = queue_index; + + our_vk_binding.instance = get_native_VkInstance(our_vk_binding.instance); + + if ((res = do_vulkan_init(wine_instance, our_vk_binding.instance)) != XR_SUCCESS) + return res; + + if (wine_instance->vk_phys_dev != get_native_VkPhysicalDevice(our_vk_binding.physicalDevice)) + WINE_WARN("VK physical device does not match that from xrGetVulkanGraphicsDeviceKHR.\n"); + + our_vk_binding.physicalDevice = wine_instance->vk_phys_dev; + + our_create_info = *createInfo; + our_create_info.next = &our_vk_binding; + createInfo = &our_create_info; + + session_type = SESSION_TYPE_D3D12; + + break; + } default: WINE_WARN("Unhandled graphics binding type: %d\n", ((XrBaseInStructure *)createInfo->next)->type); break; @@ -1572,7 +1637,7 @@ XrResult WINAPI wine_xrEnumerateSwapchainFormats(XrSession session, uint32_t for WINE_TRACE("%p, %u, %p, %p\n", session, formatCapacityInput, formatCountOutput, formats); - if (wine_session->session_type != SESSION_TYPE_D3D11) + if (wine_session->session_type != SESSION_TYPE_D3D11 && wine_session->session_type != SESSION_TYPE_D3D12) return xrEnumerateSwapchainFormats(wine_session->session, formatCapacityInput, formatCountOutput, formats); res = xrEnumerateSwapchainFormats(wine_session->session, 0, &real_format_count, NULL); @@ -1620,7 +1685,7 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat wine_swapchain = heap_alloc_zero(sizeof(*wine_swapchain)); wine_swapchain->create_info = *createInfo; - if(wine_session->session_type == SESSION_TYPE_D3D11){ + if(wine_session->session_type == SESSION_TYPE_D3D11 || wine_session->session_type == SESSION_TYPE_D3D12){ BOOL format_is_depth; our_createInfo = *createInfo; our_createInfo.format = map_format_dxgi_to_vulkan(createInfo->format); @@ -1656,6 +1721,18 @@ XrResult WINAPI wine_xrCreateSwapchain(XrSession session, const XrSwapchainCreat return XR_SUCCESS; } +static void release_d3d12_resources(wine_XrSwapchain *wine_swapchain, uint32_t image_count) +{ + XrSwapchainImageD3D12KHR *d3d12_images = (XrSwapchainImageD3D12KHR *)wine_swapchain->images; + UINT i; + if (!image_count) + return; + + for (i = 0; i < image_count; i++) + if (d3d12_images[i].texture) + d3d12_images[i].texture->lpVtbl->Release(d3d12_images[i].texture); +} + XrResult WINAPI wine_xrDestroySwapchain(XrSwapchain swapchain) { wine_XrSwapchain *wine_swapchain = (wine_XrSwapchain *)swapchain; @@ -1669,7 +1746,8 @@ XrResult WINAPI wine_xrDestroySwapchain(XrSwapchain swapchain) XrSwapchainImageD3D11KHR *d3d11_images = (XrSwapchainImageD3D11KHR *)wine_swapchain->images; for (i = 0; i < wine_swapchain->image_count; i++) d3d11_images[i].texture->lpVtbl->Release(d3d11_images[i].texture); - } + } else if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12) + release_d3d12_resources(wine_swapchain, wine_swapchain->image_count); heap_free(wine_swapchain->images); wine_swapchain->image_count = 0; } @@ -1713,10 +1791,10 @@ XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t XrSwapchainImageVulkanKHR *our_vk = NULL; HRESULT hr; size_t image_size = 0; - uint32_t i, to_copy; + uint32_t i; WINE_TRACE("%p, %u, %p, %p\n", swapchain, imageCapacityInput, imageCountOutput, images); - if (wine_swapchain->wine_session->session_type != SESSION_TYPE_D3D11) + if (wine_swapchain->wine_session->session_type != SESSION_TYPE_D3D11 && wine_swapchain->wine_session->session_type != SESSION_TYPE_D3D12) return xrEnumerateSwapchainImages(wine_swapchain->swapchain, imageCapacityInput, imageCountOutput, images); if (!wine_swapchain->image_count) { @@ -1772,17 +1850,80 @@ XrResult WINAPI wine_xrEnumerateSwapchainImages(XrSwapchain swapchain, uint32_t WINE_TRACE("Successfully allocated texture %p\n", our_d3d11[i].texture); } wine_swapchain->images = (XrSwapchainImageBaseHeader *)our_d3d11; + } else if(wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12){ + XrSwapchainImageD3D12KHR *our_d3d12; + D3D12_RESOURCE_DESC1 desc; + ID3D12DeviceExt1 *device_ext; + HRESULT hr = wine_instance->d3d12_device->lpVtbl->QueryInterface(wine_instance->d3d12_device, &IID_ID3D12DeviceExt1, (void **)&device_ext); + BOOL format_is_depth = is_vulkan_format_depth(map_format_dxgi_to_vulkan(wine_swapchain->create_info.format)); + BOOL succeeded = TRUE; + if (FAILED(hr)) + { + WINE_ERR("Cannot get vkd3d-proton interface: %08x\n", hr); + return XR_ERROR_VALIDATION_FAILURE; + } + + desc.Alignment = 0; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Width = wine_swapchain->create_info.width; + desc.Height = wine_swapchain->create_info.height; + desc.MipLevels = wine_swapchain->create_info.mipCount; + desc.DepthOrArraySize = wine_swapchain->create_info.arraySize; + desc.Format = wine_swapchain->create_info.format; + WINE_TRACE("creating vkd3d-proton texture with dxgi format %d (%x)\n", + desc.Format, desc.Format); + desc.SampleDesc.Count = wine_swapchain->create_info.sampleCount; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = 0; + if (!format_is_depth) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + else + { + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + if (!(wine_swapchain->create_info.usageFlags & XR_SWAPCHAIN_USAGE_SAMPLED_BIT)) + desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + } + if (wine_swapchain->create_info.usageFlags & XR_SWAPCHAIN_USAGE_UNORDERED_ACCESS_BIT) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + our_d3d12 = heap_alloc_zero(sizeof(XrSwapchainImageD3D12KHR) * image_count); + for(i = 0; i < image_count; ++i) + { + hr = device_ext->lpVtbl->CreateResourceFromBorrowedHandle(device_ext, + &desc, our_vk[i].image, &our_d3d12[i].texture); + if(FAILED(hr)) + { + WINE_ERR("Failed to create vkd3d-proton texture from VkImage: %08x\n", hr); + succeeded = FALSE; + break; + } + WINE_TRACE("Successfully allocated texture %p\n", our_d3d12[i].texture); + } + device_ext->lpVtbl->Release(device_ext); + + wine_swapchain->images = (XrSwapchainImageBaseHeader *)our_d3d12; + if (!succeeded) + { + release_d3d12_resources(wine_swapchain, image_count); + heap_free(wine_swapchain->images); + wine_swapchain->images = NULL; + heap_free(our_vk); + return XR_ERROR_RUNTIME_FAILURE; + } } heap_free(our_vk); wine_swapchain->image_count = image_count; + WINE_TRACE("image count %d\n", image_count); } - to_copy = min(wine_swapchain->image_count, imageCapacityInput); *imageCountOutput = wine_swapchain->image_count; if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D11){ image_size = sizeof(XrSwapchainImageD3D11KHR); + } else if (wine_swapchain->wine_session->session_type == SESSION_TYPE_D3D12){ + image_size = sizeof(XrSwapchainImageD3D12KHR); } - memcpy(images, wine_swapchain->images, image_size * to_copy); + memcpy(images, wine_swapchain->images, image_size * min(wine_swapchain->image_count, imageCapacityInput)); return XR_SUCCESS; } @@ -1917,6 +2058,7 @@ XrResult WINAPI wine_xrEndFrame(XrSession session, const XrFrameEndInfo *frameEn wine_XrSession *wine_session = (wine_XrSession *)session; uint32_t i, view_idx = 0, view_info_idx = 0; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; XrFrameEndInfo our_frameEndInfo; XrResult res; @@ -1946,9 +2088,16 @@ XrResult WINAPI wine_xrEndFrame(XrSession session, const XrFrameEndInfo *frameEn dxvk_device->lpVtbl->FlushRenderingCommands(dxvk_device); dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); } + else if ((d3d12_device = wine_session->wine_instance->d3d12_device)) + { + WINE_TRACE("Locking vkd3d-proton submission queue.\n"); + d3d12_device->lpVtbl->LockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); + } res = xrEndFrame(((wine_XrSession *)session)->session, &our_frameEndInfo); if (dxvk_device) dxvk_device->lpVtbl->ReleaseSubmissionQueue(dxvk_device); + else if (d3d12_device) + d3d12_device->lpVtbl->UnlockCommandQueue(d3d12_device, wine_session->wine_instance->d3d12_queue); return res; } @@ -1976,6 +2125,10 @@ XrResult WINAPI wine_xrAcquireSwapchainImage(XrSwapchain swapchain, const XrSwap XrResult ret; WINE_TRACE("%p, %p, %p\n", swapchain, acquireInfo, index); + + if (wine_session->session_type == SESSION_TYPE_D3D12) + return XR_ERROR_RUNTIME_FAILURE; + if ((dxvk_device = wine_session->wine_instance->dxvk_device)) dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); ret = xrAcquireSwapchainImage(((wine_XrSwapchain *)swapchain)->swapchain, acquireInfo, index); @@ -1991,6 +2144,10 @@ XrResult WINAPI wine_xrReleaseSwapchainImage(XrSwapchain swapchain, const XrSwap XrResult ret; WINE_TRACE("%p, %p\n", swapchain, releaseInfo); + + if (wine_session->session_type == SESSION_TYPE_D3D12) + return XR_ERROR_RUNTIME_FAILURE; + if ((dxvk_device = wine_session->wine_instance->dxvk_device)) dxvk_device->lpVtbl->LockSubmissionQueue(dxvk_device); ret = xrReleaseSwapchainImage(((wine_XrSwapchain *)swapchain)->swapchain, releaseInfo); diff --git a/wineopenxr/openxr_private.h b/wineopenxr/openxr_private.h index e110291d..511b31ee 100644 --- a/wineopenxr/openxr_private.h +++ b/wineopenxr/openxr_private.h @@ -20,6 +20,11 @@ typedef struct wine_XrInstance { XrSystemId systemId; IDXGIVkInteropDevice2 *dxvk_device; + ID3D12DXVKInteropDevice *d3d12_device; + ID3D12CommandQueue *d3d12_queue; + + VkDevice vk_device; + VkQueue vk_queue; } wine_XrInstance; union CompositionLayer; diff --git a/wineopenxr/vkd3d-proton-interop.h b/wineopenxr/vkd3d-proton-interop.h new file mode 100644 index 00000000..34c2cb12 --- /dev/null +++ b/wineopenxr/vkd3d-proton-interop.h @@ -0,0 +1,186 @@ +/*** Partially copied from autogenerated header by WIDL 6.4 from ../src-vkd3d-proton/include/vkd3d_device_vkd3d_ext.idl ***/ + +#pragma once + +#include +#include + +#ifndef __ID3D12DeviceExt1_FWD_DEFINED__ +#define __ID3D12DeviceExt1_FWD_DEFINED__ +typedef interface ID3D12DeviceExt1 ID3D12DeviceExt1; +#endif + +#ifndef __ID3D12DXVKInteropDevice_FWD_DEFINED__ +#define __ID3D12DXVKInteropDevice_FWD_DEFINED__ +typedef interface ID3D12DXVKInteropDevice ID3D12DXVKInteropDevice; +#endif + +/***************************************************************************** + * ID3D12DeviceExt1 interface + */ +#ifndef __ID3D12DeviceExt1_INTERFACE_DEFINED__ +#define __ID3D12DeviceExt1_INTERFACE_DEFINED__ +typedef struct D3D12_UAV_INFO D3D12_UAV_INFO; +typedef struct D3D12_VK_EXTENSION D3D12_VK_EXTENSION; +typedef struct D3D12_CUBIN_DATA_HANDLE D3D12_CUBIN_DATA_HANDLE; + +DEFINE_GUID(IID_ID3D12DeviceExt1, 0x099a73fd, 0x2199, 0x4f45, 0xbf,0x48, 0x0e,0xb8,0x6f,0x6f,0xdb,0x65); +typedef struct ID3D12DeviceExt1Vtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + ID3D12DeviceExt1 *This, + REFIID riid, + void **object); + + ULONG (STDMETHODCALLTYPE *AddRef)( + ID3D12DeviceExt1 *This); + + ULONG (STDMETHODCALLTYPE *Release)( + ID3D12DeviceExt1 *This); + + /*** ID3D12DeviceExt methods ***/ + HRESULT (STDMETHODCALLTYPE *GetVulkanHandles)( + ID3D12DeviceExt1 *This, + VkInstance *vk_instance, + VkPhysicalDevice *vk_physical_device, + VkDevice *vk_device); + + BOOL (STDMETHODCALLTYPE *GetExtensionSupport)( + ID3D12DeviceExt1 *This, + D3D12_VK_EXTENSION extension); + + HRESULT (STDMETHODCALLTYPE *CreateCubinComputeShaderWithName)( + ID3D12DeviceExt1 *This, + const void *cubin_data, + UINT32 cubin_size, + UINT32 block_x, + UINT32 block_y, + UINT32 block_z, + const char *shader_name, + D3D12_CUBIN_DATA_HANDLE **handle); + + HRESULT (STDMETHODCALLTYPE *DestroyCubinComputeShader)( + ID3D12DeviceExt1 *This, + D3D12_CUBIN_DATA_HANDLE *handle); + + HRESULT (STDMETHODCALLTYPE *GetCudaTextureObject)( + ID3D12DeviceExt1 *This, + D3D12_CPU_DESCRIPTOR_HANDLE srv_handle, + D3D12_CPU_DESCRIPTOR_HANDLE sampler_handle, + UINT32 *cuda_texture_handle); + + HRESULT (STDMETHODCALLTYPE *GetCudaSurfaceObject)( + ID3D12DeviceExt1 *This, + D3D12_CPU_DESCRIPTOR_HANDLE uav_handle, + UINT32 *cuda_surface_handle); + + HRESULT (STDMETHODCALLTYPE *CaptureUAVInfo)( + ID3D12DeviceExt1 *This, + D3D12_UAV_INFO *uav_info); + + /*** ID3D12DeviceExt1 methods ***/ + HRESULT (STDMETHODCALLTYPE *CreateResourceFromBorrowedHandle)( + ID3D12DeviceExt1 *This, + const D3D12_RESOURCE_DESC1 *desc, + UINT64 vk_handle, + ID3D12Resource **resource); + + HRESULT (STDMETHODCALLTYPE *GetVulkanQueueInfoEx)( + ID3D12DeviceExt1 *This, + ID3D12CommandQueue *queue, + VkQueue *vk_queue, + UINT32 *vk_queue_index, + UINT32 *vk_queue_flags, + UINT32 *vk_queue_family); + + END_INTERFACE +} ID3D12DeviceExt1Vtbl; + +interface ID3D12DeviceExt1 { + CONST_VTBL ID3D12DeviceExt1Vtbl* lpVtbl; +}; +#endif + +/***************************************************************************** + * ID3D12DXVKInteropDevice interface + */ +#ifndef __ID3D12DXVKInteropDevice_INTERFACE_DEFINED__ +#define __ID3D12DXVKInteropDevice_INTERFACE_DEFINED__ + +DEFINE_GUID(IID_ID3D12DXVKInteropDevice, 0x39da4e09, 0xbd1c, 0x4198, 0x9f,0xae, 0x86,0xbb,0xe3,0xbe,0x41,0xfd); +typedef struct ID3D12DXVKInteropDeviceVtbl { + BEGIN_INTERFACE + + /*** IUnknown methods ***/ + HRESULT (STDMETHODCALLTYPE *QueryInterface)( + ID3D12DXVKInteropDevice *This, + REFIID riid, + void **object); + + ULONG (STDMETHODCALLTYPE *AddRef)( + ID3D12DXVKInteropDevice *This); + + ULONG (STDMETHODCALLTYPE *Release)( + ID3D12DXVKInteropDevice *This); + + /*** ID3D12DXVKInteropDevice methods ***/ + HRESULT (STDMETHODCALLTYPE *GetDXGIAdapter)( + ID3D12DXVKInteropDevice *This, + REFIID iid, + void **object); + + HRESULT (STDMETHODCALLTYPE *GetInstanceExtensions)( + ID3D12DXVKInteropDevice *This, + UINT *extension_count, + const char **extensions); + + HRESULT (STDMETHODCALLTYPE *GetDeviceExtensions)( + ID3D12DXVKInteropDevice *This, + UINT *extension_count, + const char **extensions); + + HRESULT (STDMETHODCALLTYPE *GetDeviceFeatures)( + ID3D12DXVKInteropDevice *This, + const VkPhysicalDeviceFeatures2 **features); + + HRESULT (STDMETHODCALLTYPE *GetVulkanHandles)( + ID3D12DXVKInteropDevice *This, + VkInstance *vk_instance, + VkPhysicalDevice *vk_physical_device, + VkDevice *vk_device); + + HRESULT (STDMETHODCALLTYPE *GetVulkanQueueInfo)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue, + VkQueue *vk_queue, + UINT32 *vk_queue_family); + + void (STDMETHODCALLTYPE *GetVulkanImageLayout)( + ID3D12DXVKInteropDevice *This, + ID3D12Resource *resource, + D3D12_RESOURCE_STATES state, + VkImageLayout *vk_layout); + + HRESULT (STDMETHODCALLTYPE *GetVulkanResourceInfo)( + ID3D12DXVKInteropDevice *This, + ID3D12Resource *resource, + UINT64 *vk_handle, + UINT64 *buffer_offset); + + HRESULT (STDMETHODCALLTYPE *LockCommandQueue)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue); + + HRESULT (STDMETHODCALLTYPE *UnlockCommandQueue)( + ID3D12DXVKInteropDevice *This, + ID3D12CommandQueue *queue); + + END_INTERFACE +} ID3D12DXVKInteropDeviceVtbl; + +interface ID3D12DXVKInteropDevice { + CONST_VTBL ID3D12DXVKInteropDeviceVtbl* lpVtbl; +}; +#endif