diff options
author | Adam Moss <amoss@nvidia.com> | 2021-09-27 20:47:19 +0300 |
---|---|---|
committer | Jens Peters <jp7677@gmail.com> | 2021-09-27 21:30:51 +0300 |
commit | c9eae842c5aa94d3b2871b2c4c39fb5f04401340 (patch) | |
tree | 27944f6fb808f588dcaabe617aa087e52ed883e5 | |
parent | 383dc2c97c408c0bf78b6d2aeff24a37231e22fe (diff) |
Implement the required NvAPI entrypoints for D3D11 DLSS support.
(To initialize DLSS successfully at runtime this also needs some
corresponding DXVK changes.)
-rw-r--r-- | src/d3d11/nvapi_d3d11_device.cpp | 140 | ||||
-rw-r--r-- | src/d3d11/nvapi_d3d11_device.h | 17 | ||||
-rw-r--r-- | src/dxvk/dxvk_interfaces.cpp | 2 | ||||
-rw-r--r-- | src/dxvk/dxvk_interfaces.h | 62 | ||||
-rw-r--r-- | src/nvapi_d3d.cpp | 55 | ||||
-rw-r--r-- | src/nvapi_d3d11.cpp | 159 | ||||
-rw-r--r-- | src/nvapi_interface.cpp | 15 | ||||
-rw-r--r-- | tests/nvapi_d3d.cpp | 34 | ||||
-rw-r--r-- | tests/nvapi_d3d11.cpp | 158 | ||||
-rw-r--r-- | tests/nvapi_d3d11_mocks.cpp | 11 |
10 files changed, 623 insertions, 30 deletions
diff --git a/src/d3d11/nvapi_d3d11_device.cpp b/src/d3d11/nvapi_d3d11_device.cpp index c910d71..ef45972 100644 --- a/src/d3d11/nvapi_d3d11_device.cpp +++ b/src/d3d11/nvapi_d3d11_device.cpp @@ -46,43 +46,135 @@ namespace dxvk { return true; } - Com<ID3D11VkExtContext> NvapiD3d11Device::GetSdbtDeviceContext(IUnknown* deviceOrContext) { - std::scoped_lock lock(m_depthBoundsDeviceOrContextMutex); - auto it = m_depthBoundsDeviceOrContextMap.find(deviceOrContext); - if (it != m_depthBoundsDeviceOrContextMap.end()) - return it->second; + bool NvapiD3d11Device::CreateCubinComputeShaderWithName(ID3D11Device* pDevice, const void* pCubin, NvU32 size, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const char* pShaderName, NVDX_ObjectHandle* phShader) { + // note - creating a cuda kernel is a one-shot thing, no need to add complexity by caching availability of the underlying extensions, we'll just let DXVK tell us if it failed. + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; - auto deviceContextExt = GetDeviceContextExt(deviceOrContext, D3D11_VK_EXT_DEPTH_BOUNDS); - if (deviceContextExt != nullptr) - m_depthBoundsDeviceOrContextMap.emplace(deviceOrContext, deviceContextExt.ptr()); + return d3d11ExtDevice1->CreateCubinComputeShaderWithNameNVX(pCubin, size, blockX, blockY, blockZ, pShaderName, reinterpret_cast<IUnknown**>(phShader)); + } - return deviceContextExt; + bool NvapiD3d11Device::LaunchCubinShader(ID3D11DeviceContext* pDeviceContext, NVDX_ObjectHandle hShader, NvU32 gridX, NvU32 gridY, NvU32 gridZ, const void* pParams, NvU32 paramSize, const NVDX_ObjectHandle* pReadResources, NvU32 numReadResources, const NVDX_ObjectHandle* pWriteResources, NvU32 numWriteResources) { + auto binaryImportDeviceContext = GetBinaryImportDeviceContext(pDeviceContext); + if (binaryImportDeviceContext == nullptr) + return false; + + return binaryImportDeviceContext->LaunchCubinShaderNVX(reinterpret_cast<IUnknown*>(hShader), gridX, gridY, gridZ, pParams, paramSize, reinterpret_cast<void* const*>(pReadResources), numReadResources, reinterpret_cast<void* const*>(pWriteResources), numWriteResources); } - Com<ID3D11VkExtContext> NvapiD3d11Device::GetBarrierControlDeviceContext(IUnknown* deviceOrContext) { - std::scoped_lock lock(m_barrierControlDeviceOrContextMutex); - auto it = m_barrierControlDeviceOrContextMap.find(deviceOrContext); - if (it != m_barrierControlDeviceOrContextMap.end()) - return it->second; + bool NvapiD3d11Device::DestroyCubinShader(ID3D11Device* /*unused*/, NVDX_ObjectHandle hShader) { + IUnknown* cushader = reinterpret_cast<IUnknown*>(hShader); + if (cushader) + cushader->Release(); - auto deviceContextExt = GetDeviceContextExt(deviceOrContext, D3D11_VK_EXT_BARRIER_CONTROL); - if (deviceContextExt != nullptr) - m_barrierControlDeviceOrContextMap.emplace(deviceOrContext, deviceContextExt.ptr()); + return cushader != nullptr; + } - return deviceContextExt; + bool NvapiD3d11Device::GetResourceDriverHandle(ID3D11Device* /*unused*/, ID3D11Resource* pResource, NVDX_ObjectHandle* phObject) { + // This trivial implementation is here instead of in DXVK for performance reasons; DXVK will assume that this is a straight cast though, so if either layer has to change that assumption then they will have to be upgraded in lockstep. + *phObject = reinterpret_cast<NVDX_ObjectHandle>(pResource); + return true; + } + + bool NvapiD3d11Device::GetResourceHandleGPUVirtualAddressAndSize(ID3D11Device* pDevice, NVDX_ObjectHandle hObject, NvU64* gpuVAStart, NvU64* gpuVASize) { + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; + + return d3d11ExtDevice1->GetResourceHandleGPUVirtualAddressAndSizeNVX(reinterpret_cast<void*>(hObject), reinterpret_cast<uint64_t*>(gpuVAStart), reinterpret_cast<uint64_t*>(gpuVASize)); + } + + bool NvapiD3d11Device::CreateUnorderedAccessViewAndGetDriverHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, NvU32* pDriverHandle) { + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; + + return d3d11ExtDevice1->CreateUnorderedAccessViewAndGetDriverHandleNVX(pResource, pDesc, ppUAV, reinterpret_cast<uint32_t*>(pDriverHandle)); + } + + bool NvapiD3d11Device::CreateShaderResourceViewAndGetDriverHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, NvU32* pDriverHandle) { + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; + + return d3d11ExtDevice1->CreateShaderResourceViewAndGetDriverHandleNVX(pResource, pDesc, ppSRV, reinterpret_cast<uint32_t*>(pDriverHandle)); + } + + bool NvapiD3d11Device::GetCudaTextureObject(ID3D11Device* pDevice, uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle) { + // note - deriving a 'cuda texture object' is typically a one-shot thing, no need to add complexity by caching availability of the underlying extensions, we'll just let DXVK tell us if it failed. + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; + + return d3d11ExtDevice1->GetCudaTextureObjectNVX(srvDriverHandle, samplerDriverHandle, reinterpret_cast<uint32_t*>(pCudaTextureHandle)); + } + + bool NvapiD3d11Device::CreateSamplerStateAndGetDriverHandle(ID3D11Device* pDevice, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle) { + // note - getting the driver handle for a sampler state is typically a one-shot thing, no need to add complexity by caching availability of the underlying extensions, we'll just let DXVK tell us if it failed. + Com<ID3D11VkExtDevice1> d3d11ExtDevice1; + if (FAILED(pDevice->QueryInterface(IID_PPV_ARGS(&d3d11ExtDevice1)))) + return false; + + return d3d11ExtDevice1->CreateSamplerStateAndGetDriverHandleNVX(pSamplerDesc, ppSamplerState, pDriverHandle); + } + + bool NvapiD3d11Device::IsFatbinPTXSupported(ID3D11Device* pDevice) { + return (GetBinaryImportDeviceContext(pDevice) != nullptr); + } + + Com<ID3D11VkExtContext> NvapiD3d11Device::GetSdbtDeviceContext(IUnknown* deviceOrContext) { + std::scoped_lock lock(m_depthBoundsDeviceOrContextMutex); + return CacheDxvkDeviceContext(deviceOrContext, m_depthBoundsDeviceOrContextMap, D3D11_VK_EXT_DEPTH_BOUNDS); + } + + Com<ID3D11VkExtContext> NvapiD3d11Device::GetBarrierControlDeviceContext(IUnknown* deviceOrContext) { + std::scoped_lock lock(m_barrierControlDeviceOrContextMutex); + return CacheDxvkDeviceContext(deviceOrContext, m_barrierControlDeviceOrContextMap, D3D11_VK_EXT_BARRIER_CONTROL); } Com<ID3D11VkExtContext> NvapiD3d11Device::GetMultiDrawDeviceContext(ID3D11DeviceContext* deviceContext) { std::scoped_lock lock(m_multiDrawIndirectContextMutex); - auto it = m_multiDrawIndirectContextMap.find(deviceContext); - if (it != m_multiDrawIndirectContextMap.end()) + return CacheDxvkDeviceContext(deviceContext, m_multiDrawIndirectContextMap, D3D11_VK_EXT_MULTI_DRAW_INDIRECT); + } + + Com<ID3D11VkExtContext1> NvapiD3d11Device::GetBinaryImportDeviceContext(IUnknown* deviceOrContext) { + std::scoped_lock lock(m_binaryImportContextMutex); + auto context = CacheDxvkDeviceContext(deviceOrContext, m_binaryImportContextMap, D3D11_VK_NVX_BINARY_IMPORT); + if (context == nullptr) + return nullptr; + + Com<ID3D11VkExtContext1> d3d11ExtContext1; + if (FAILED(context->QueryInterface(IID_PPV_ARGS(&d3d11ExtContext1)))) + return nullptr; + + return d3d11ExtContext1; + } + + Com<ID3D11VkExtContext1> NvapiD3d11Device::GetImageViewHandleDeviceContext(ID3D11DeviceContext* deviceContext) { + std::scoped_lock lock(m_imageViewHandleContextMutex); + auto context = CacheDxvkDeviceContext(deviceContext, m_imageViewHandleContextMap, D3D11_VK_NVX_IMAGE_VIEW_HANDLE); + if (context == nullptr) + return nullptr; + + Com<ID3D11VkExtContext1> d3d11ExtContext1; + if (FAILED(context->QueryInterface(IID_PPV_ARGS(&d3d11ExtContext1)))) + return nullptr; + + return d3d11ExtContext1; + } + + Com<ID3D11VkExtContext> NvapiD3d11Device::CacheDxvkDeviceContext(IUnknown* deviceOrContext, std::unordered_map<IUnknown*, ID3D11VkExtContext*>& cacheMap, D3D11_VK_EXTENSION extension) { + auto it = cacheMap.find(deviceOrContext); + if (it != cacheMap.end()) return it->second; - auto deviceContextExt = GetDeviceContextExt(deviceContext, D3D11_VK_EXT_MULTI_DRAW_INDIRECT); - if (deviceContextExt != nullptr) - m_multiDrawIndirectContextMap.emplace(deviceContext, deviceContextExt.ptr()); + auto dxvkDeviceContext = GetDeviceContextExt(deviceOrContext, extension); + /* it could be beneficial to cache nullptrs, but it confuses the tests */ + if (dxvkDeviceContext != nullptr) + cacheMap.emplace(deviceOrContext, dxvkDeviceContext.ptr()); - return deviceContextExt; + return dxvkDeviceContext; } Com<ID3D11VkExtContext> NvapiD3d11Device::GetDeviceContextExt(IUnknown* deviceOrContext, D3D11_VK_EXTENSION extension) { diff --git a/src/d3d11/nvapi_d3d11_device.h b/src/d3d11/nvapi_d3d11_device.h index ebe687b..57b3119 100644 --- a/src/d3d11/nvapi_d3d11_device.h +++ b/src/d3d11/nvapi_d3d11_device.h @@ -13,19 +13,36 @@ namespace dxvk { static bool EndUAVOverlap(IUnknown* deviceOrContext); static bool MultiDrawInstancedIndirect(ID3D11DeviceContext* deviceContext, NvU32 drawCount, ID3D11Buffer* buffer, NvU32 alignedByteOffsetForArgs, NvU32 alignedByteStrideForArgs); static bool MultiDrawIndexedInstancedIndirect(ID3D11DeviceContext* deviceContext, NvU32 drawCount, ID3D11Buffer* buffer, NvU32 alignedByteOffsetForArgs, NvU32 alignedByteStrideForArgs); + static bool CreateCubinComputeShaderWithName(ID3D11Device* pDevice, const void* pCubin, NvU32 size, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const char* pShaderName, NVDX_ObjectHandle* phShader); + static bool LaunchCubinShader(ID3D11DeviceContext* pDeviceContext, NVDX_ObjectHandle hShader, NvU32 gridX, NvU32 gridY, NvU32 gridZ, const void* pParams, NvU32 paramSize, const NVDX_ObjectHandle* pReadResources, NvU32 numReadResources, const NVDX_ObjectHandle* pWriteResources, NvU32 numWriteResources); + static bool DestroyCubinShader(ID3D11Device* pDevice, NVDX_ObjectHandle hShader); + static bool GetResourceDriverHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, NVDX_ObjectHandle* phObject); + static bool GetResourceHandleGPUVirtualAddressAndSize(ID3D11Device* pDevice, NVDX_ObjectHandle hObject, NvU64* gpuVAStart, NvU64* gpuVASize); + static bool CreateUnorderedAccessViewAndGetDriverHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, NvU32* pDriverHandle); + static bool CreateShaderResourceViewAndGetDriverHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, NvU32* pDriverHandle); + static bool GetCudaTextureObject(ID3D11Device* pDevice, uint32_t srvDriverHandle, uint32_t samplerDriverHandle, uint32_t* pCudaTextureHandle); + static bool CreateSamplerStateAndGetDriverHandle(ID3D11Device* pDevice, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle); + static bool IsFatbinPTXSupported(ID3D11Device* pDevice); private: inline static std::unordered_map<IUnknown*, ID3D11VkExtContext*> m_depthBoundsDeviceOrContextMap; inline static std::unordered_map<IUnknown*, ID3D11VkExtContext*> m_barrierControlDeviceOrContextMap; inline static std::unordered_map<IUnknown*, ID3D11VkExtContext*> m_multiDrawIndirectContextMap; + inline static std::unordered_map<IUnknown*, ID3D11VkExtContext*> m_binaryImportContextMap; + inline static std::unordered_map<IUnknown*, ID3D11VkExtContext*> m_imageViewHandleContextMap; inline static std::mutex m_depthBoundsDeviceOrContextMutex; inline static std::mutex m_barrierControlDeviceOrContextMutex; inline static std::mutex m_multiDrawIndirectContextMutex; + inline static std::mutex m_binaryImportContextMutex; + inline static std::mutex m_imageViewHandleContextMutex; [[nodiscard]] static Com<ID3D11VkExtContext> GetSdbtDeviceContext(IUnknown* deviceOrContext); [[nodiscard]] static Com<ID3D11VkExtContext> GetBarrierControlDeviceContext(IUnknown* deviceOrContext); [[nodiscard]] static Com<ID3D11VkExtContext> GetMultiDrawDeviceContext(ID3D11DeviceContext* deviceContext); + [[nodiscard]] static Com<ID3D11VkExtContext1> GetBinaryImportDeviceContext(IUnknown* deviceOrContext); + [[nodiscard]] static Com<ID3D11VkExtContext1> GetImageViewHandleDeviceContext(ID3D11DeviceContext* deviceContext); + [[nodiscard]] static Com<ID3D11VkExtContext> CacheDxvkDeviceContext(IUnknown* deviceOrContext, std::unordered_map<IUnknown*, ID3D11VkExtContext*>& cacheMap, D3D11_VK_EXTENSION extension); [[nodiscard]] static Com<ID3D11VkExtContext> GetDeviceContextExt(IUnknown* deviceOrContext, D3D11_VK_EXTENSION extension); [[nodiscard]] static Com<ID3D11VkExtContext> GetDeviceContextExt(ID3D11DeviceContext* deviceContext, D3D11_VK_EXTENSION extension); [[nodiscard]] static Com<ID3D11VkExtContext> GetDeviceContextExt(ID3D11Device* device, ID3D11DeviceContext* deviceContext, D3D11_VK_EXTENSION extension); diff --git a/src/dxvk/dxvk_interfaces.cpp b/src/dxvk/dxvk_interfaces.cpp index be4e68b..e4e5cf9 100644 --- a/src/dxvk/dxvk_interfaces.cpp +++ b/src/dxvk/dxvk_interfaces.cpp @@ -2,4 +2,6 @@ const GUID IDXGIVkInteropAdapter::guid = {0x3a6d8f2c,0xb0e8,0x4ab4,{0xb4,0xdc,0x4f,0xd2,0x48,0x91,0xbf,0xa5}}; const GUID ID3D11VkExtDevice::guid = {0x8a6e3c42,0xf74c,0x45b7,{0x82,0x65,0xa2,0x31,0xb6,0x77,0xca,0x17}}; +const GUID ID3D11VkExtDevice1::guid = {0xcfcf64ef,0x9586,0x46d0,{0xbc,0xa4,0x97,0xcf,0x2c,0xa6,0x1b,0x06}}; const GUID ID3D11VkExtContext::guid = {0xfd0bca13,0x5cb6,0x4c3a,{0x98,0x7e,0x47,0x50,0xde,0x2c,0xa7,0x91}}; +const GUID ID3D11VkExtContext1::guid = {0x874b09b2,0xae0b,0x41d8,{0x84,0x76,0x5f,0x3b,0x7a,0x0e,0x87,0x9d}}; diff --git a/src/dxvk/dxvk_interfaces.h b/src/dxvk/dxvk_interfaces.h index 6a73caf..9a49516 100644 --- a/src/dxvk/dxvk_interfaces.h +++ b/src/dxvk/dxvk_interfaces.h @@ -14,6 +14,8 @@ enum D3D11_VK_EXTENSION : uint32_t { D3D11_VK_EXT_MULTI_DRAW_INDIRECT_COUNT = 1, D3D11_VK_EXT_DEPTH_BOUNDS = 2, D3D11_VK_EXT_BARRIER_CONTROL = 3, + D3D11_VK_NVX_BINARY_IMPORT = 4, + D3D11_VK_NVX_IMAGE_VIEW_HANDLE = 5, }; enum D3D11_VK_BARRIER_CONTROL : uint32_t { @@ -55,6 +57,47 @@ ID3D11VkExtDevice : public IUnknown { D3D11_VK_EXTENSION Extension) = 0; }; +MIDL_INTERFACE("cfcf64ef-9586-46d0-bca4-97cf2ca61b06") +ID3D11VkExtDevice1 : public ID3D11VkExtDevice { + static const GUID guid; + + virtual bool STDMETHODCALLTYPE GetResourceHandleGPUVirtualAddressAndSizeNVX( + void* hObject, + uint64_t* gpuVAStart, + uint64_t* gpuVASize) = 0; + + virtual bool STDMETHODCALLTYPE CreateUnorderedAccessViewAndGetDriverHandleNVX( + ID3D11Resource* pResource, + const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, + ID3D11UnorderedAccessView** ppUAV, + uint32_t* pDriverHandle) = 0; + + virtual bool STDMETHODCALLTYPE CreateShaderResourceViewAndGetDriverHandleNVX( + ID3D11Resource* pResource, + const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, + ID3D11ShaderResourceView** ppSRV, + uint32_t* pDriverHandle) = 0; + + virtual bool STDMETHODCALLTYPE CreateSamplerStateAndGetDriverHandleNVX( + const D3D11_SAMPLER_DESC* pSamplerDesc, + ID3D11SamplerState** ppSamplerState, + uint32_t* pDriverHandle) = 0; + + virtual bool STDMETHODCALLTYPE CreateCubinComputeShaderWithNameNVX( + const void* pCubin, + uint32_t size, + uint32_t blockX, + uint32_t blockY, + uint32_t blockZ, + const char* pShaderName, + IUnknown** phShader) = 0; + + virtual bool STDMETHODCALLTYPE GetCudaTextureObjectNVX( + uint32_t srvDriverHandle, + uint32_t samplerDriverHandle, + uint32_t* pCudaTextureHandle) = 0; +}; + MIDL_INTERFACE("fd0bca13-5cb6-4c3a-987e-4750de2ca791") ID3D11VkExtContext : public IUnknown { static const GUID guid; @@ -96,6 +139,25 @@ ID3D11VkExtContext : public IUnknown { UINT ControlFlags) = 0; }; +MIDL_INTERFACE("874b09b2-ae0b-41d8-8476-5f3b7a0e879d") +ID3D11VkExtContext1 : public ID3D11VkExtContext { + static const GUID guid; + + virtual bool STDMETHODCALLTYPE LaunchCubinShaderNVX( + IUnknown* hShader, + uint32_t gridX, + uint32_t gridY, + uint32_t gridZ, + const void* pParams, + uint32_t paramSize, + void* const* pReadResources, + uint32_t numReadResources, + void* const* pWriteResources, + uint32_t numWriteResources) = 0; +}; + DXVK_DEFINE_GUID(IDXGIVkInteropAdapter) DXVK_DEFINE_GUID(ID3D11VkExtDevice) +DXVK_DEFINE_GUID(ID3D11VkExtDevice1) DXVK_DEFINE_GUID(ID3D11VkExtContext) +DXVK_DEFINE_GUID(ID3D11VkExtContext1) diff --git a/src/nvapi_d3d.cpp b/src/nvapi_d3d.cpp index fdfcb6d..3d8bdfc 100644 --- a/src/nvapi_d3d.cpp +++ b/src/nvapi_d3d.cpp @@ -6,7 +6,10 @@ extern "C" { NvAPI_Status __cdecl NvAPI_D3D_GetObjectHandleForResource(IUnknown *pDevice, IUnknown *pResource, NVDX_ObjectHandle *pHandle) { static bool alreadyLogged = false; - return NoImplementation("NvAPI_D3D_GetObjectHandleForResource", alreadyLogged); + // Fake-implement with a dumb passthrough, though no other NvAPI entry points + // we're likely to implement should care about the actual handle value. + *pHandle = (NVDX_ObjectHandle)pResource; + return Ok("NvAPI_D3D_GetObjectHandleForResource", alreadyLogged); } NvAPI_Status __cdecl NvAPI_D3D_SetResourceHint(IUnknown *pDev, NVDX_ObjectHandle obj, NVAPI_D3D_SETRESOURCEHINT_CATEGORY dwHintCategory, NvU32 dwHintName, NvU32 *pdwHintValue) { @@ -14,8 +17,20 @@ extern "C" { return NoImplementation("NvAPI_D3D_SetResourceHint", alreadyLogged); } + NvAPI_Status __cdecl NvAPI_D3D_BeginResourceRendering(IUnknown *pDeviceOrContext, NVDX_ObjectHandle obj, NvU32 Flags) { + static bool alreadyLogged = false; + // Synchronisation hints for SLI... + return Ok("NvAPI_D3D_BeginResourceRendering", alreadyLogged); + } + + NvAPI_Status __cdecl NvAPI_D3D_EndResourceRendering(IUnknown *pDeviceOrContext, NVDX_ObjectHandle obj, NvU32 Flags) { + static bool alreadyLogged = false; + return Ok("NvAPI_D3D_EndResourceRendering", alreadyLogged); + } + NvAPI_Status __cdecl NvAPI_D3D_GetCurrentSLIState(IUnknown *pDevice, NV_GET_CURRENT_SLI_STATE *pSliState) { constexpr auto n = "NvAPI_D3D_GetCurrentSLIState"; + static bool alreadyLoggedOk = false; if (pDevice == nullptr || pSliState == nullptr) return InvalidArgument(n); @@ -34,6 +49,42 @@ extern "C" { if (pSliState->version == NV_GET_CURRENT_SLI_STATE_VER2) pSliState->numVRSLIGpus = 0; - return Ok(n); + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D1x_GetGraphicsCapabilities(IUnknown *pDevice, + NvU32 structVersion, + NV_D3D1x_GRAPHICS_CAPS *pGraphicsCaps) { + constexpr auto n = "NvAPI_D3D1x_GetGraphicsCapabilities"; + static bool alreadyLoggedOk = false; + + switch(structVersion) { + case NV_D3D1x_GRAPHICS_CAPS_VER1: + memset(pGraphicsCaps, 0, sizeof(NV_D3D1x_GRAPHICS_CAPS_V1)); + break; + case NV_D3D1x_GRAPHICS_CAPS_VER2: + memset(pGraphicsCaps, 0, sizeof(NV_D3D1x_GRAPHICS_CAPS_V2)); + break; + default: + return IncompatibleStructVersion(n); + } + + switch(structVersion) { + case NV_D3D1x_GRAPHICS_CAPS_VER2: + // bFastUAVClearSupported is reported mostly for the sake of DLSS. + // All NVIDIA Vulkan drivers support this. + (*reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS_V2*>(pGraphicsCaps)).bFastUAVClearSupported = 1; + // dummy SM version number (unused by DLSS): + (*reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS_V2*>(pGraphicsCaps)).majorSMVersion = 0; + (*reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS_V2*>(pGraphicsCaps)).minorSMVersion = 0; + + [[fallthrough]]; + + case NV_D3D1x_GRAPHICS_CAPS_VER1: + (*reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS_V1*>(pGraphicsCaps)).bExclusiveScissorRectsSupported = 0; + (*reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS_V1*>(pGraphicsCaps)).bVariablePixelRateShadingSupported = 0; + } + + return Ok(n, alreadyLoggedOk); } } diff --git a/src/nvapi_d3d11.cpp b/src/nvapi_d3d11.cpp index 467aa62..c0a22e1 100644 --- a/src/nvapi_d3d11.cpp +++ b/src/nvapi_d3d11.cpp @@ -88,4 +88,163 @@ extern "C" { return Ok(str::format(n, " ", code, " (", fromCode(code), ")")); } + + NvAPI_Status __cdecl NvAPI_D3D11_CreateCubinComputeShader(ID3D11Device* pDevice, const void* pCubin, NvU32 size, NvU32 blockX, NvU32 blockY, NvU32 blockZ, NVDX_ObjectHandle* phShader) { + constexpr auto n = "NvAPI_D3D11_CreateCubinComputeShader"; + + if (pDevice == nullptr || pCubin == nullptr || phShader == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::CreateCubinComputeShaderWithName(pDevice, pCubin, size, blockX, blockY, blockZ, nullptr, phShader)) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_CreateCubinComputeShaderWithName(ID3D11Device* pDevice, const void* pCubin, NvU32 size, NvU32 blockX, NvU32 blockY, NvU32 blockZ, const char* pShaderName, NVDX_ObjectHandle* phShader) { + constexpr auto n = "NvAPI_D3D11_CreateCubinComputeShaderWithName"; + + if (pDevice == nullptr || pCubin == nullptr || phShader == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::CreateCubinComputeShaderWithName(pDevice, pCubin, size, blockX, blockY, blockZ, pShaderName, phShader)) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_LaunchCubinShader(ID3D11DeviceContext* pDeviceContext, NVDX_ObjectHandle hShader, NvU32 gridX, NvU32 gridY, NvU32 gridZ, const void* pParams, NvU32 paramSize, const NVDX_ObjectHandle* pReadResources, NvU32 numReadResources, const NVDX_ObjectHandle* pWriteResources, NvU32 numWriteResources) { + constexpr auto n = "NvAPI_D3D11_LaunchCubinShader"; + static bool alreadyLoggedError = false; + static bool alreadyLoggedOk = false; + + if (pDeviceContext == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::LaunchCubinShader(pDeviceContext, hShader, gridX, gridY, gridZ, pParams, paramSize, pReadResources, numReadResources, pWriteResources, numWriteResources)) + return Error(n, alreadyLoggedError); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D11_DestroyCubinComputeShader(ID3D11Device* pDevice, NVDX_ObjectHandle hShader) { + constexpr auto n = "NvAPI_D3D11_DestroyCubinComputeShader"; + + if (pDevice == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::DestroyCubinShader(pDevice, hShader)) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_IsFatbinPTXSupported(ID3D11Device* pDevice, bool* pSupported) { + constexpr auto n = "NvAPI_D3D11_IsFatbinPTXSupported"; + static bool alreadyLoggedOk = false; + + if (pDevice == nullptr || pSupported == nullptr) + return InvalidArgument(n); + + *pSupported = NvapiD3d11Device::IsFatbinPTXSupported(pDevice); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D11_CreateUnorderedAccessView(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAV, NvU32* pDriverHandle) { + constexpr auto n = "NvAPI_D3D11_CreateUnorderedAccessView"; + + if (pDevice == nullptr || pResource == nullptr || pDesc == nullptr || ppUAV == nullptr || pDriverHandle == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::CreateUnorderedAccessViewAndGetDriverHandle(pDevice, pResource, pDesc, ppUAV, pDriverHandle)) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_CreateShaderResourceView(ID3D11Device* pDevice, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRV, NvU32* pDriverHandle) { + constexpr auto n = "NvAPI_D3D11_CreateShaderResourceView"; + + if (pDevice == nullptr || pResource == nullptr || pDesc == nullptr || ppSRV == nullptr || pDriverHandle == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::CreateShaderResourceViewAndGetDriverHandle(pDevice, pResource, pDesc, ppSRV, pDriverHandle)) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_GetResourceHandle(ID3D11Device* pDevice, ID3D11Resource* pResource, NVDX_ObjectHandle* phObject) { + constexpr auto n = "NvAPI_D3D11_GetResourceHandle"; + static bool alreadyLoggedOk = false; + static bool alreadyLoggedError = false; + + if (pDevice == nullptr || pResource == nullptr || phObject == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::GetResourceDriverHandle(pDevice, pResource, phObject)) + return Error(n, alreadyLoggedError); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D11_GetResourceGPUVirtualAddress(ID3D11Device* pDevice, const NVDX_ObjectHandle hResource, NvU64* pGpuVA) { + constexpr auto n = "NvAPI_D3D11_GetResourceGPUVirtualAddress"; + static bool alreadyLoggedOk = false; + static bool alreadyLoggedError = false; + + if (pDevice == nullptr || hResource == NVDX_OBJECT_NONE || pGpuVA == nullptr) + return InvalidArgument(n); + + NvU64 dummy; + if (!NvapiD3d11Device::GetResourceHandleGPUVirtualAddressAndSize(pDevice, hResource, pGpuVA, &dummy)) + return Error(n, alreadyLoggedError); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D11_GetResourceGPUVirtualAddressEx(ID3D11Device* pDevice, NV_GET_GPU_VIRTUAL_ADDRESS* pParams) { + constexpr auto n = "NvAPI_D3D11_GetResourceGPUVirtualAddressEx"; + static bool alreadyLoggedOk = false; + static bool alreadyLoggedError = false; + + if (pDevice == nullptr || pParams == nullptr) + return InvalidArgument(n); + + if (pParams->version != NV_GET_GPU_VIRTUAL_ADDRESS_VER1) + return IncompatibleStructVersion(n); + + if (pParams->hResource == NVDX_OBJECT_NONE) + return InvalidArgument(n); + + if (!NvapiD3d11Device::GetResourceHandleGPUVirtualAddressAndSize(pDevice, pParams->hResource, &pParams->gpuVAStart, &pParams->gpuVASize)) + return Error(n, alreadyLoggedError); + + return Ok(n, alreadyLoggedOk); + } + + NvAPI_Status __cdecl NvAPI_D3D11_CreateSamplerState(ID3D11Device* pDevice, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, NvU32* pDriverHandle) { + constexpr auto n = "NvAPI_D3D11_CreateSamplerState"; + + if (pDevice == nullptr || pSamplerDesc == nullptr || ppSamplerState == nullptr || pDriverHandle == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::CreateSamplerStateAndGetDriverHandle(pDevice, pSamplerDesc, ppSamplerState, reinterpret_cast<uint32_t*>(pDriverHandle))) + return Error(n); + + return Ok(n); + } + + NvAPI_Status __cdecl NvAPI_D3D11_GetCudaTextureObject(ID3D11Device* pDevice, NvU32 srvDriverHandle, NvU32 samplerDriverHandle, NvU32* pCudaTextureHandle) { + constexpr auto n = "NvAPI_D3D11_GetCudaTextureObject"; + + if (pDevice == nullptr) + return InvalidArgument(n); + + if (!NvapiD3d11Device::GetCudaTextureObject(pDevice, srvDriverHandle, samplerDriverHandle, reinterpret_cast<uint32_t*>(pCudaTextureHandle))) + return Error(n); + + return Ok(n); + } } diff --git a/src/nvapi_interface.cpp b/src/nvapi_interface.cpp index 4a1eea5..723d31a 100644 --- a/src/nvapi_interface.cpp +++ b/src/nvapi_interface.cpp @@ -42,6 +42,19 @@ extern "C" { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_MultiDrawInstancedIndirect) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_MultiDrawIndexedInstancedIndirect) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_IsNvShaderExtnOpCodeSupported) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateCubinComputeShader) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateCubinComputeShaderWithName) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_LaunchCubinShader) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_DestroyCubinComputeShader) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_IsFatbinPTXSupported) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateUnorderedAccessView) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateSamplerState) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_GetCudaTextureObject) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateShaderResourceView) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_GetResourceHandle) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_GetResourceGPUVirtualAddress) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_GetResourceGPUVirtualAddressEx) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D1x_GetGraphicsCapabilities) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D12_IsNvShaderExtnOpCodeSupported) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D12_CreateCubinComputeShaderWithName) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D12_CreateCubinComputeShader) @@ -55,6 +68,8 @@ extern "C" { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_GetObjectHandleForResource) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetResourceHint) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_GetCurrentSLIState) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_BeginResourceRendering) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_EndResourceRendering) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetGPUType) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetPCIIdentifiers) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_GPU_GetFullName) diff --git a/tests/nvapi_d3d.cpp b/tests/nvapi_d3d.cpp index cdf4850..04c4c29 100644 --- a/tests/nvapi_d3d.cpp +++ b/tests/nvapi_d3d.cpp @@ -16,9 +16,9 @@ class UnknownMock : public mock_interface<IUnknown> { TEST_CASE("D3D methods succeed", "[.d3d]") { UnknownMock unknown; - SECTION("GetObjectHandleForResource returns no-implementation") { + SECTION("GetObjectHandleForResource returns OK") { NVDX_ObjectHandle handle; - REQUIRE(NvAPI_D3D_GetObjectHandleForResource(&unknown, &unknown, &handle) == NVAPI_NO_IMPLEMENTATION); + REQUIRE(NvAPI_D3D_GetObjectHandleForResource(&unknown, &unknown, &handle) == NVAPI_OK); } SECTION("GetObjectHandleForResource returns no-implementation") { @@ -26,6 +26,18 @@ TEST_CASE("D3D methods succeed", "[.d3d]") { REQUIRE(NvAPI_D3D_SetResourceHint(&unknown, NVDX_ObjectHandle(), NVAPI_D3D_SRH_CATEGORY_SLI, 1, &value) == NVAPI_NO_IMPLEMENTATION); } + SECTION("SetResourceHint returns NoImplementation") { + REQUIRE(NvAPI_D3D_SetResourceHint(&unknown, NVDX_ObjectHandle(), NVAPI_D3D_SRH_CATEGORY_SLI, 0, nullptr) == NVAPI_NO_IMPLEMENTATION); + } + + SECTION("BeginResourceRendering returns OK") { + REQUIRE(NvAPI_D3D_BeginResourceRendering(&unknown, NVDX_ObjectHandle(), 0) == NVAPI_OK); + } + + SECTION("BeginResourceRendering returns OK") { + REQUIRE(NvAPI_D3D_EndResourceRendering(&unknown, NVDX_ObjectHandle(), 0) == NVAPI_OK); + } + SECTION("GetCurrentSLIState returns OK") { NV_GET_CURRENT_SLI_STATE state; state.version = NV_GET_CURRENT_SLI_STATE_VER2; @@ -38,4 +50,22 @@ TEST_CASE("D3D methods succeed", "[.d3d]") { REQUIRE(state.bIsCurAFRGroupNew == false); REQUIRE(state.numVRSLIGpus == 0); } + + SECTION("GetGraphicsCapabilities (V1) returns OK") { + NV_D3D1x_GRAPHICS_CAPS_V1 caps; + REQUIRE(NvAPI_D3D1x_GetGraphicsCapabilities(&unknown, NV_D3D1x_GRAPHICS_CAPS_VER1, reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS*>(&caps)) == NVAPI_OK); + // returned bExclusiveScissorRectsSupported/bVariablePixelRateShadingSupported values not enforced by testing + } + + SECTION("GetGraphicsCapabilities (V2) returns OK") { + NV_D3D1x_GRAPHICS_CAPS_V2 caps; + REQUIRE(NvAPI_D3D1x_GetGraphicsCapabilities(&unknown, NV_D3D1x_GRAPHICS_CAPS_VER2, reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS*>(&caps)) == NVAPI_OK); + REQUIRE(caps.bFastUAVClearSupported == 1); + // returned minorSMVersion/majorSMVersion not enforced by testing since current returns are dummy values anyway + } + + SECTION("GetGraphicsCapabilities (FUTURE VERSION) returns IncompatibleStructVersion") { + NV_D3D1x_GRAPHICS_CAPS caps; + REQUIRE(NvAPI_D3D1x_GetGraphicsCapabilities(&unknown, NV_D3D1x_GRAPHICS_CAPS_VER+1, reinterpret_cast<NV_D3D1x_GRAPHICS_CAPS*>(&caps)) == NVAPI_INCOMPATIBLE_STRUCT_VERSION); + } } diff --git a/tests/nvapi_d3d11.cpp b/tests/nvapi_d3d11.cpp index 6115971..fe76783 100644 --- a/tests/nvapi_d3d11.cpp +++ b/tests/nvapi_d3d11.cpp @@ -7,6 +7,12 @@ using namespace trompeloeil; +class UnknownMock : public mock_interface<IUnknown> { + MAKE_MOCK2 (QueryInterface, HRESULT(REFIID, void * *), override); + MAKE_MOCK0 (AddRef, ULONG(), override); + MAKE_MOCK0 (Release, ULONG(), override); +}; + TEST_CASE("D3D11 methods succeed", "[.d3d11]") { D3D11DxvkDeviceMock device; D3D11DxvkDeviceContextMock context; @@ -21,6 +27,10 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { .LR_SIDE_EFFECT(*_2 = static_cast<ID3D11VkExtDevice*>(&device)) .LR_SIDE_EFFECT(deviceRefCount++) .RETURN(S_OK); + ALLOW_CALL(device, QueryInterface(ID3D11VkExtDevice1::guid, _)) + .LR_SIDE_EFFECT(*_2 = static_cast<ID3D11VkExtDevice1*>(&device)) + .LR_SIDE_EFFECT(deviceRefCount++) + .RETURN(S_OK); ALLOW_CALL(device, AddRef()) .LR_SIDE_EFFECT(deviceRefCount++) .RETURN(deviceRefCount); @@ -43,6 +53,10 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { .LR_SIDE_EFFECT(*_2 = static_cast<ID3D11VkExtContext*>(&context)) .LR_SIDE_EFFECT(contextRefCount++) .RETURN(S_OK); + ALLOW_CALL(context, QueryInterface(ID3D11VkExtContext1::guid, _)) + .LR_SIDE_EFFECT(*_2 = static_cast<ID3D11VkExtContext1*>(&context)) + .LR_SIDE_EFFECT(contextRefCount++) + .RETURN(S_OK); ALLOW_CALL(context, AddRef()) .LR_SIDE_EFFECT(contextRefCount++) .RETURN(contextRefCount); @@ -56,8 +70,12 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { SECTION("D3D11 methods without DXVK return error") { ALLOW_CALL(device, QueryInterface(ID3D11VkExtDevice::guid, _)) .RETURN(E_NOINTERFACE); + ALLOW_CALL(device, QueryInterface(ID3D11VkExtDevice1::guid, _)) + .RETURN(E_NOINTERFACE); ALLOW_CALL(context, QueryInterface(ID3D11VkExtContext::guid, _)) .RETURN(E_NOINTERFACE); + ALLOW_CALL(context, QueryInterface(ID3D11VkExtContext1::guid, _)) + .RETURN(E_NOINTERFACE); FORBID_CALL(context, SetDepthBoundsTest(_, _, _)); FORBID_CALL(context, SetBarrierControl(_)); FORBID_CALL(context, MultiDrawIndirect(_, _, _, _)); @@ -113,6 +131,95 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { REQUIRE(contextRefCount == 0); } + SECTION("LaunchCubinShader without DXVK extension support returns error") { + ALLOW_CALL(device, GetExtensionSupport(D3D11_VK_NVX_BINARY_IMPORT)) + .RETURN(false); + FORBID_CALL(context, LaunchCubinShaderNVX(_, _, _, _, _, _, _, _, _, _)); + + REQUIRE(NvAPI_D3D11_LaunchCubinShader(static_cast<ID3D11DeviceContext*>(&context), NVDX_ObjectHandle(), 0,0,0, nullptr, 0, nullptr, 0, nullptr, 0) == NVAPI_ERROR); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("Failing CreateCubinComputeShader/CreateCubinComputeShaderWithName inside DXVK returns error") { + ALLOW_CALL(device, CreateCubinComputeShaderWithNameNVX(_, _, _, _, _, _, _)) + .RETURN(false); + + NVDX_ObjectHandle objhandle; + REQUIRE(NvAPI_D3D11_CreateCubinComputeShaderWithName(static_cast<ID3D11Device*>(&device), "X", 1U, 0U,0U,0U, "foo", &objhandle) == NVAPI_ERROR); + REQUIRE(NvAPI_D3D11_CreateCubinComputeShader(static_cast<ID3D11Device*>(&device), "X", 1U, 0U,0U,0U, &objhandle) == NVAPI_ERROR); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("LaunchCubinShader/CreateCubinComputeShader/CreateCubinComputeShaderWithName without extended DXVK interface returns error") { + ALLOW_CALL(device, QueryInterface(ID3D11VkExtDevice1::guid, _)) + .RETURN(E_NOINTERFACE); + ALLOW_CALL(context, QueryInterface(ID3D11VkExtContext1::guid, _)) + .RETURN(E_NOINTERFACE); + FORBID_CALL(context, LaunchCubinShaderNVX(_, _, _, _, _, _, _, _, _, _)); + FORBID_CALL(device, CreateCubinComputeShaderWithNameNVX(_, _, _, _, _, _, _)); + + REQUIRE(NvAPI_D3D11_LaunchCubinShader(static_cast<ID3D11DeviceContext*>(&context), NVDX_ObjectHandle(), 0,0,0, nullptr, 0, nullptr, 0, nullptr, 0) == NVAPI_ERROR); + NVDX_ObjectHandle objhandle; + REQUIRE(NvAPI_D3D11_CreateCubinComputeShaderWithName(static_cast<ID3D11Device*>(&device), "X", 1U, 0U,0U,0U, "foo", &objhandle) == NVAPI_ERROR); + REQUIRE(NvAPI_D3D11_CreateCubinComputeShader(static_cast<ID3D11Device*>(&device), "X", 1U, 0U,0U,0U, &objhandle) == NVAPI_ERROR); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("IsFatbinPTXSupported without DXVK extension succeeds but reports unsupported") { + ALLOW_CALL(device, GetExtensionSupport(D3D11_VK_NVX_BINARY_IMPORT)) + .RETURN(false); + + bool sup = true; + REQUIRE(NvAPI_D3D11_IsFatbinPTXSupported(static_cast<ID3D11Device*>(&device), &sup) == NVAPI_OK); + REQUIRE(sup == false); + REQUIRE(deviceRefCount == 0); + } + + SECTION("IsFatbinPTXSupported without DXVK extension succeeds but reports unsupported") { + ALLOW_CALL(device, GetExtensionSupport(D3D11_VK_NVX_BINARY_IMPORT)) + .RETURN(false); + + bool sup = true; + REQUIRE(NvAPI_D3D11_IsFatbinPTXSupported(static_cast<ID3D11Device*>(&device), &sup) == NVAPI_OK); + REQUIRE(sup == false); + REQUIRE(deviceRefCount == 0); + } + + SECTION("(NvAPI_D3D11_)CreateUnorderedAccessView/CreateShaderResourceView/GetResourceGPUVirtualAddress/GetResourceGPUVirtualAddressEx/GetCudaTextureObject/CreateSamplerState without extended DXVK interface returns error") { + ALLOW_CALL(device, QueryInterface(ID3D11VkExtDevice1::guid, _)) + .RETURN(E_NOINTERFACE); + ALLOW_CALL(context, QueryInterface(ID3D11VkExtContext1::guid, _)) + .RETURN(E_NOINTERFACE); + FORBID_CALL(device, CreateUnorderedAccessViewAndGetDriverHandleNVX(_, _, _, _)); + FORBID_CALL(device, CreateShaderResourceViewAndGetDriverHandleNVX(_, _, _, _)); + FORBID_CALL(device, GetResourceHandleGPUVirtualAddressAndSizeNVX(_, _, _)); + FORBID_CALL(device, GetCudaTextureObjectNVX(_, _, _)); + FORBID_CALL(device, CreateSamplerStateAndGetDriverHandleNVX(_, _, _)); + + D3D11BufferMock res; + NvU32 driverhandle; + D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc; + ID3D11UnorderedAccessView* pUAV; + REQUIRE(NvAPI_D3D11_CreateUnorderedAccessView(static_cast<ID3D11Device*>(&device), &res, &uavdesc, &pUAV, &driverhandle) == NVAPI_ERROR); + D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc; + ID3D11ShaderResourceView* pSRV; + REQUIRE(NvAPI_D3D11_CreateShaderResourceView(static_cast<ID3D11Device*>(&device), &res, &srvdesc, &pSRV, &driverhandle) == NVAPI_ERROR); + NvU64 gpuVAStart; + REQUIRE(NvAPI_D3D11_GetResourceGPUVirtualAddress(static_cast<ID3D11Device*>(&device), NVDX_ObjectHandle(1), &gpuVAStart) == NVAPI_ERROR); + NV_GET_GPU_VIRTUAL_ADDRESS gva; + gva.version = NV_GET_GPU_VIRTUAL_ADDRESS_VER1; + gva.hResource = NVDX_ObjectHandle(1); + REQUIRE(NvAPI_D3D11_GetResourceGPUVirtualAddressEx(static_cast<ID3D11Device*>(&device), &gva) == NVAPI_ERROR); + D3D11_SAMPLER_DESC samplerdesc; + ID3D11SamplerState* pSamplerState; + REQUIRE(NvAPI_D3D11_CreateSamplerState(static_cast<ID3D11Device*>(&device), &samplerdesc, &pSamplerState, &driverhandle) == NVAPI_ERROR); + REQUIRE(NvAPI_D3D11_GetCudaTextureObject(static_cast<ID3D11Device*>(&device), 0x1, 0x2, &driverhandle) == NVAPI_ERROR); + REQUIRE(deviceRefCount == 0); + } + // Test failing scenarios first because caches won't be reset between tests (we don't cache negatives) SECTION("IsNvShaderExtnOpCodeSupported returns OK") { @@ -178,4 +285,55 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { REQUIRE(deviceRefCount == 0); REQUIRE(contextRefCount == 0); } + + SECTION("DestroyCubinComputeShader treats handle as a COM object and releases it") { + // it's part of our contract with DXVK that cuda shader handles are really COM objects + UnknownMock comobj; + REQUIRE_CALL(comobj, Release()) + .TIMES(1) + .RETURN(42); + + NVDX_ObjectHandle handle = reinterpret_cast<NVDX_ObjectHandle>(&comobj); + REQUIRE(NvAPI_D3D11_DestroyCubinComputeShader(static_cast<ID3D11Device*>(&device), handle) == NVAPI_OK); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("DestroyCubinComputeShader does not succeed (or crash) on a NULL or NVDX_OBJECT_NONE handle") { + // this also checks the assumption that NVDX_OBJECT_NONE casts <-> NULL, which should forever be true but will break some stuff in subtle ways if not, so ¯\_(ツ)_/¯ + REQUIRE(NVDX_OBJECT_NONE == reinterpret_cast<NVDX_ObjectHandle>(NULL /* not nullptr which is castproofed */)); + REQUIRE(reinterpret_cast<void*>(NVDX_OBJECT_NONE) == nullptr); + REQUIRE(NvAPI_D3D11_DestroyCubinComputeShader(static_cast<ID3D11Device*>(&device), nullptr) != NVAPI_OK); + REQUIRE(NvAPI_D3D11_DestroyCubinComputeShader(static_cast<ID3D11Device*>(&device), NVDX_OBJECT_NONE) != NVAPI_OK); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("GetResourceHandle returns OK") { + // Test device call twice to ensure correct reference counting when hitting the device cache + D3D11BufferMock res; + NVDX_ObjectHandle handle = reinterpret_cast<NVDX_ObjectHandle>(&res); + REQUIRE(NvAPI_D3D11_GetResourceHandle(static_cast<ID3D11Device*>(&device), &res, &handle) == NVAPI_OK); + REQUIRE(NvAPI_D3D11_GetResourceHandle(static_cast<ID3D11Device*>(&device), &res, &handle) == NVAPI_OK); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("GetResourceHandle with NULL argument returns InvalidArgument") { + D3D11BufferMock res; + NVDX_ObjectHandle handle; + REQUIRE(NvAPI_D3D11_GetResourceHandle(nullptr, &res, &handle) == NVAPI_INVALID_ARGUMENT); + REQUIRE(NvAPI_D3D11_GetResourceHandle(static_cast<ID3D11Device*>(&device), nullptr, &handle) == NVAPI_INVALID_ARGUMENT); + REQUIRE(NvAPI_D3D11_GetResourceHandle(static_cast<ID3D11Device*>(&device), &res, nullptr) == NVAPI_INVALID_ARGUMENT); + REQUIRE(deviceRefCount == 0); + REQUIRE(contextRefCount == 0); + } + + SECTION("GetResourceHandle output handle is a simple recast of the input resource pointer") { + // While the handles returned by NvAPI are opaque with unspecified values, our interaction with DXVK *requires* that we implement them as a simple recast + D3D11BufferMock res; + NVDX_ObjectHandle handle; + REQUIRE(NvAPI_D3D11_GetResourceHandle(static_cast<ID3D11Device*>(&device), &res, &handle) == NVAPI_OK); + REQUIRE(reinterpret_cast<void*>(handle) == reinterpret_cast<void*>(&res)); + } } diff --git a/tests/nvapi_d3d11_mocks.cpp b/tests/nvapi_d3d11_mocks.cpp index 1620db9..a510a71 100644 --- a/tests/nvapi_d3d11_mocks.cpp +++ b/tests/nvapi_d3d11_mocks.cpp @@ -2,7 +2,7 @@ using namespace trompeloeil; -class ID3D11DxvkDevice : public ID3D11Device, public ID3D11VkExtDevice {}; +class ID3D11DxvkDevice : public ID3D11Device, public ID3D11VkExtDevice1 {}; class D3D11DxvkDeviceMock : public mock_interface<ID3D11DxvkDevice> { MAKE_MOCK2(QueryInterface, HRESULT(REFIID, void**), override); @@ -49,9 +49,15 @@ class D3D11DxvkDeviceMock : public mock_interface<ID3D11DxvkDevice> { IMPLEMENT_MOCK0(GetExceptionMode); IMPLEMENT_MOCK1(GetImmediateContext); IMPLEMENT_MOCK1(GetExtensionSupport); + IMPLEMENT_MOCK3(GetCudaTextureObjectNVX); + IMPLEMENT_MOCK7(CreateCubinComputeShaderWithNameNVX); + IMPLEMENT_MOCK4(CreateUnorderedAccessViewAndGetDriverHandleNVX); + IMPLEMENT_MOCK4(CreateShaderResourceViewAndGetDriverHandleNVX); + IMPLEMENT_MOCK3(CreateSamplerStateAndGetDriverHandleNVX); + IMPLEMENT_MOCK3(GetResourceHandleGPUVirtualAddressAndSizeNVX); }; -class ID3D11DxvkDeviceContext : public ID3D11DeviceContext, public ID3D11VkExtContext {}; +class ID3D11DxvkDeviceContext : public ID3D11DeviceContext, public ID3D11VkExtContext1 {}; class D3D11DxvkDeviceContextMock : public mock_interface<ID3D11DxvkDeviceContext> { MAKE_MOCK2(QueryInterface, HRESULT(REFIID, void**), override); @@ -175,6 +181,7 @@ class D3D11DxvkDeviceContextMock : public mock_interface<ID3D11DxvkDeviceContext IMPLEMENT_MOCK6(MultiDrawIndexedIndirectCount); IMPLEMENT_MOCK3(SetDepthBoundsTest); IMPLEMENT_MOCK1(SetBarrierControl); + IMPLEMENT_MOCK10(LaunchCubinShaderNVX); }; class D3D11BufferMock : public mock_interface<ID3D11Buffer> { |