diff --git a/src/interfaces/dxvk_interfaces.h b/src/interfaces/dxvk_interfaces.h index 3e03ec48..9f8e085e 100644 --- a/src/interfaces/dxvk_interfaces.h +++ b/src/interfaces/dxvk_interfaces.h @@ -159,6 +159,67 @@ ID3D11VkExtContext1 : public ID3D11VkExtContext { uint32_t numWriteResources) = 0; }; +/** + * \brief NVAPI custom shader semantic (SMP / multi-view) + * + * Mirrors the information from NVAPI's NV_CUSTOM_SEMANTIC without + * depending on NVIDIA headers. Type values match NV_CUSTOM_SEMANTIC_TYPE + * (2 = viewport mask, 4 = viewport mask 2, 5 = per-view position). + * + * Name[256] matches NVIDIA's own NVCustomSemanticNameString buffer size + * exactly. Every semantic name observed fits comfortably in far less, + * but matching NVIDIA's maximum removes any truncation risk and lets the + * compiler prove the strncpy calls below cannot truncate, which silences + * -Wstringop-truncation properly rather than suppressing it. + */ +struct D3D11_VK_NV_CUSTOM_SEMANTIC { + uint32_t Type; + char Name[256]; + BOOL RegisterSpecified; + uint32_t RegisterNum; + uint32_t RegisterMask; +}; + +/** + * \brief Extended D3D11 device, revision 2 + * + * Adds NVAPI-style extended shader creation for + * SMP / multi-view rendering (dxvk-nvapi interop). + */ +MIDL_INTERFACE("1d5c6a10-9f4b-4e0a-b6a4-2c8e13d70f51") +ID3D11VkExtDevice2 : public ID3D11VkExtDevice1 { + + virtual HRESULT STDMETHODCALLTYPE CreateVertexShaderNvSemantics( + const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, + uint32_t NumSemantics, + ID3D11VertexShader** ppVertexShader) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateGeometryShaderNvSemantics( + const void* pShaderBytecode, + SIZE_T BytecodeLength, + ID3D11ClassLinkage* pClassLinkage, + const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, + uint32_t NumSemantics, + BOOL UseViewportMask, + ID3D11GeometryShader** ppGeometryShader) = 0; +}; + +/** + * \brief Extended D3D11 context, revision 2 + * + * Adds the SMP multi-view mode toggle (dxvk-nvapi interop). + */ +MIDL_INTERFACE("7e2c1b9f-4d38-4a11-9b6e-0f5a8c42d3e7") +ID3D11VkExtContext2 : public ID3D11VkExtContext1 { + + virtual void STDMETHODCALLTYPE SetMultiviewModeNV( + uint32_t NumViews, + BOOL IndependentViewportMask) = 0; +}; + MIDL_INTERFACE("2a289dbd-2d0a-4a51-89f7-f2adce465cd6") IDXGIVkInteropFactory1 : public IDXGIVkInteropFactory { virtual HRESULT STDMETHODCALLTYPE GetGlobalHDRState( @@ -176,6 +237,8 @@ __CRT_UUID_DECL(IDXGIVkInteropFactory1, 0x2a289dbd, 0x2d0a, 0x4a51, 0x89, 0xf7, __CRT_UUID_DECL(IDXGIVkInteropAdapter, 0x3a6d8f2c, 0xb0e8, 0x4ab4, 0xb4, 0xdc, 0x4f, 0xd2, 0x48, 0x91, 0xbf, 0xa5); __CRT_UUID_DECL(ID3D11VkExtDevice, 0x8a6e3c42, 0xf74c, 0x45b7, 0x82, 0x65, 0xa2, 0x31, 0xb6, 0x77, 0xca, 0x17); __CRT_UUID_DECL(ID3D11VkExtDevice1, 0xcfcf64ef, 0x9586, 0x46d0, 0xbc, 0xa4, 0x97, 0xcf, 0x2c, 0xa6, 0x1b, 0x06); +__CRT_UUID_DECL(ID3D11VkExtDevice2, 0x1d5c6a10, 0x9f4b, 0x4e0a, 0xb6, 0xa4, 0x2c, 0x8e, 0x13, 0xd7, 0x0f, 0x51); __CRT_UUID_DECL(ID3D11VkExtContext, 0xfd0bca13, 0x5cb6, 0x4c3a, 0x98, 0x7e, 0x47, 0x50, 0xde, 0x2c, 0xa7, 0x91); __CRT_UUID_DECL(ID3D11VkExtContext1, 0x874b09b2, 0xae0b, 0x41d8, 0x84, 0x76, 0x5f, 0x3b, 0x7a, 0x0e, 0x87, 0x9d); +__CRT_UUID_DECL(ID3D11VkExtContext2, 0x7e2c1b9f, 0x4d38, 0x4a11, 0x9b, 0x6e, 0x0f, 0x5a, 0x8c, 0x42, 0xd3, 0xe7); #endif diff --git a/src/nvapi/nvapi_d3d11_device.cpp b/src/nvapi/nvapi_d3d11_device.cpp index a96e1d2f..9960c476 100644 --- a/src/nvapi/nvapi_d3d11_device.cpp +++ b/src/nvapi/nvapi_d3d11_device.cpp @@ -1,5 +1,7 @@ #include "nvapi_d3d11_device.h" #include "../util/com_pointer.h" +#include "../util/util_string.h" +#include "../util/util_log.h" namespace dxvk { std::unordered_map> NvapiD3d11Device::m_nvapiDeviceMap; @@ -60,7 +62,7 @@ namespace dxvk { } NvapiD3d11Device::NvapiD3d11Device(ID3D11VkExtDevice* dxvkDevice, ID3D11VkExtContext* dxvkContext) - : m_dxvkDevice(static_cast(dxvkDevice)), m_dxvkContext(static_cast(dxvkContext)) { // NOLINT(*-pro-type-static-cast-downcast) + : m_dxvkDevice(static_cast(dxvkDevice)), m_dxvkContext(static_cast(dxvkContext)) { // NOLINT(*-pro-type-static-cast-downcast) m_supportsExtDepthBounds = m_dxvkDevice->GetExtensionSupport(D3D11_VK_EXT_DEPTH_BOUNDS); m_supportsNvxBinaryImport = m_dxvkDevice->GetExtensionSupport(D3D11_VK_NVX_BINARY_IMPORT); m_supportsExtBarrierControl = m_dxvkDevice->GetExtensionSupport(D3D11_VK_EXT_BARRIER_CONTROL); @@ -69,6 +71,34 @@ namespace dxvk { m_supportsExtDevice1 = probeInterfaceChain(dxvkDevice, {__uuidof(ID3D11VkExtDevice1)}) >= 1; m_supportsExtContext1 = probeInterfaceChain(dxvkContext, {__uuidof(ID3D11VkExtContext1)}) >= 1; + + // Revision-2 interfaces exist only on a DXVK that implements the + // multi-view entry points. Without them, shader creation falls back + // to the plain path. + m_supportsExtDevice2 = probeInterfaceChain(dxvkDevice, {__uuidof(ID3D11VkExtDevice1), __uuidof(ID3D11VkExtDevice2)}) >= 2; + m_supportsExtContext2 = probeInterfaceChain(dxvkContext, {__uuidof(ID3D11VkExtContext1), __uuidof(ID3D11VkExtContext2)}) >= 2; + } + + HRESULT NvapiD3d11Device::CreateVertexShaderNvSemantics(const void* pShaderBytecode, size_t bytecodeLength, ID3D11ClassLinkage* pClassLinkage, const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, uint32_t numSemantics, ID3D11VertexShader** ppVertexShader) const { + if (!m_supportsExtDevice2) + return E_NOTIMPL; + + return m_dxvkDevice->CreateVertexShaderNvSemantics(pShaderBytecode, bytecodeLength, pClassLinkage, pSemantics, numSemantics, ppVertexShader); + } + + HRESULT NvapiD3d11Device::CreateGeometryShaderNvSemantics(const void* pShaderBytecode, size_t bytecodeLength, ID3D11ClassLinkage* pClassLinkage, const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, uint32_t numSemantics, bool useViewportMask, ID3D11GeometryShader** ppGeometryShader) const { + if (!m_supportsExtDevice2) + return E_NOTIMPL; + + return m_dxvkDevice->CreateGeometryShaderNvSemantics(pShaderBytecode, bytecodeLength, pClassLinkage, pSemantics, numSemantics, useViewportMask, ppGeometryShader); + } + + bool NvapiD3d11Device::SetMultiviewMode(uint32_t numViews, bool independentViewportMask) const { + if (!m_supportsExtContext2) + return false; + + m_dxvkContext->SetMultiviewModeNV(numViews, independentViewportMask ? TRUE : FALSE); + return true; } HRESULT NvapiD3d11Device::SetDepthBoundsTest(const bool enable, const float minDepth, const float maxDepth) const { diff --git a/src/nvapi/nvapi_d3d11_device.h b/src/nvapi/nvapi_d3d11_device.h index fddb7b41..6d422aa2 100644 --- a/src/nvapi/nvapi_d3d11_device.h +++ b/src/nvapi/nvapi_d3d11_device.h @@ -30,14 +30,18 @@ namespace dxvk { [[nodiscard]] HRESULT CreateSamplerStateAndGetDriverHandle(const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState, uint32_t* pDriverHandle) const; [[nodiscard]] bool IsFatbinPTXSupported() const; + [[nodiscard]] HRESULT CreateVertexShaderNvSemantics(const void* pShaderBytecode, size_t bytecodeLength, ID3D11ClassLinkage* pClassLinkage, const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, uint32_t numSemantics, ID3D11VertexShader** ppVertexShader) const; + [[nodiscard]] HRESULT CreateGeometryShaderNvSemantics(const void* pShaderBytecode, size_t bytecodeLength, ID3D11ClassLinkage* pClassLinkage, const D3D11_VK_NV_CUSTOM_SEMANTIC* pSemantics, uint32_t numSemantics, bool useViewportMask, ID3D11GeometryShader** ppGeometryShader) const; + bool SetMultiviewMode(uint32_t numViews, bool independentViewportMask) const; + private: [[nodiscard]] static NvapiD3d11Device* Get(IUnknown* deviceOrContext); static std::unordered_map> m_nvapiDeviceMap; static std::mutex m_mutex; - ID3D11VkExtDevice1* m_dxvkDevice{}; - ID3D11VkExtContext1* m_dxvkContext{}; + ID3D11VkExtDevice2* m_dxvkDevice{}; + ID3D11VkExtContext2* m_dxvkContext{}; bool m_supportsExtDepthBounds; bool m_supportsNvxBinaryImport; @@ -46,5 +50,7 @@ namespace dxvk { bool m_supportsExtMultiDrawIndirect; bool m_supportsExtDevice1; bool m_supportsExtContext1; + bool m_supportsExtDevice2; + bool m_supportsExtContext2; }; } diff --git a/src/nvapi_d3d.cpp b/src/nvapi_d3d.cpp index 09fc3843..caf84a93 100644 --- a/src/nvapi_d3d.cpp +++ b/src/nvapi_d3d.cpp @@ -3,6 +3,7 @@ #include "nvapi/nvapi_d3d_low_latency_device.h" #include "util/util_statuscode.h" #include "util/util_env.h" +#include "nvapi/nvapi_d3d11_device.h" using namespace dxvk; @@ -378,3 +379,203 @@ NVAPI_FUNCTION NvAPI_D3D_SetLatencyMarker(IUnknown* pDev, NV_LATENCY_MARKER_PARA return Error(n, alreadyLoggedError); } } + +// --------------------------------------------------------------------------- +// Simultaneous Multi-Projection (SMP) / Multi-View support +// +// These entry points let a game ask whether the GPU can do multi-view +// rendering, and set the mode. iRacing uses them for its "Nvidia Simultaneous +// Multi-Projection" option on triple screens. Capability queries are answered +// from the GPU generation. Mode changes are forwarded into DXVK when it +// implements the extended interfaces, and accepted but ignored otherwise. +// +// Hardware capability rules (same as NVIDIA's Windows driver): +// - Single-Pass Stereo (2 views): Pascal (GTX 10xx) or newer +static NvapiAdapter* GetMultiViewAdapter(IUnknown* pDeviceOrContext) { + if (!pDeviceOrContext || !nvapiAdapterRegistry) + return nullptr; + + // The game may hand us an ID3D11Device or an ID3D11DeviceContext, + // normalize to the device first + Com device; + if (FAILED(pDeviceOrContext->QueryInterface(IID_PPV_ARGS(&device)))) { + Com context; + if (FAILED(pDeviceOrContext->QueryInterface(IID_PPV_ARGS(&context)))) + return nullptr; + + context->GetDevice(&device); + } + + // Walk D3D11 device -> DXGI device -> DXGI adapter -> adapter LUID, + // then look that LUID up in dxvk-nvapi's adapter registry + Com dxgiDevice; + if (FAILED(device->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))) + return nullptr; + + Com dxgiAdapter; + if (FAILED(dxgiDevice->GetAdapter(&dxgiAdapter))) + return nullptr; + + DXGI_ADAPTER_DESC adapterDesc{}; + if (FAILED(dxgiAdapter->GetDesc(&adapterDesc))) + return nullptr; + + return nvapiAdapterRegistry->FindAdapter(adapterDesc.AdapterLuid); +} + +NVAPI_FUNCTION NvAPI_D3D_QueryMultiViewSupport(IUnknown* pDevice, NV_QUERY_MULTIVIEW_SUPPORT_PARAMS* pQueryMultiViewSupportedParams) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevice), log::fmt::ptr(pQueryMultiViewSupportedParams)); + + if (!nvapiAdapterRegistry) + return ApiNotInitialized(n); + + if (!pDevice || !pQueryMultiViewSupportedParams) + return InvalidArgument(n); + + if (pQueryMultiViewSupportedParams->version != NV_QUERY_MULTIVIEW_SUPPORT_PARAMS_VER1) + return IncompatibleStructVersion(n, pQueryMultiViewSupportedParams->version); + + auto pascalOrNewer = false; + auto turingOrNewer = false; + if (auto adapter = GetMultiViewAdapter(pDevice)) { + auto architectureId = adapter->GetArchitectureId(); + pascalOrNewer = architectureId >= NV_GPU_ARCHITECTURE_GP100; + turingOrNewer = architectureId >= NV_GPU_ARCHITECTURE_TU100; + } + + pQueryMultiViewSupportedParams->bMultiViewSupported = turingOrNewer; + pQueryMultiViewSupportedParams->bSinglePassStereoSupported = pascalOrNewer; + pQueryMultiViewSupportedParams->bSinglePassStereoXYZWSupported = turingOrNewer; + + return Ok(str::format(n, " (MultiView=", turingOrNewer ? "supported" : "unsupported", "/SPS=", pascalOrNewer ? "supported" : "unsupported", ")"), alreadyLogged); +} + +NVAPI_FUNCTION NvAPI_D3D_SetMultiViewMode(IUnknown* pDevOrContext, NV_MULTIVIEW_PARAMS* pMultiViewParams) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevOrContext), log::fmt::ptr(pMultiViewParams)); + + if (!pDevOrContext || !pMultiViewParams) + return InvalidArgument(n); + + if (pMultiViewParams->version != NV_MULTIVIEW_PARAMS_VER1) + return IncompatibleStructVersion(n, pMultiViewParams->version); + + if (pMultiViewParams->numViews == 0 || pMultiViewParams->numViews > NV_MULTIVIEW_MAX_SUPPORTED_VIEWS) + return InvalidArgument(n); + + // Forward the toggle to DXVK when it implements the extended interfaces + if (auto device = NvapiD3d11Device::GetOrCreate(pDevOrContext); + device && device->SetMultiviewMode(pMultiViewParams->numViews, pMultiViewParams->independentViewportMaskEnable != 0)) + return Ok(str::format(n, " (numViews=", pMultiViewParams->numViews, ") (forwarded)"), alreadyLogged); + + // Otherwise accept and log the request without acting on it. NVIDIA + // documents this call as asynchronous, so returning OK only means the + // arguments were valid. + return Ok(str::format(n, " (numViews=", pMultiViewParams->numViews, ") (not implemented, ignoring)"), alreadyLogged); +} + +NVAPI_FUNCTION NvAPI_D3D_QuerySinglePassStereoSupport(IUnknown* pDevice, NV_QUERY_SINGLE_PASS_STEREO_SUPPORT_PARAMS* pQuerySinglePassStereoSupportedParams) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevice), log::fmt::ptr(pQuerySinglePassStereoSupportedParams)); + + if (!nvapiAdapterRegistry) + return ApiNotInitialized(n); + + if (!pDevice || !pQuerySinglePassStereoSupportedParams) + return InvalidArgument(n); + + auto pascalOrNewer = false; + auto turingOrNewer = false; + if (auto adapter = GetMultiViewAdapter(pDevice)) { + auto architectureId = adapter->GetArchitectureId(); + pascalOrNewer = architectureId >= NV_GPU_ARCHITECTURE_GP100; + turingOrNewer = architectureId >= NV_GPU_ARCHITECTURE_TU100; + } + + switch (pQuerySinglePassStereoSupportedParams->version) { + case NV_QUERY_SINGLE_PASS_STEREO_SUPPORT_PARAMS_VER1: { + auto pParamsV1 = reinterpret_cast(pQuerySinglePassStereoSupportedParams); + pParamsV1->bSinglePassStereoSupported = pascalOrNewer; + break; + } + case NV_QUERY_SINGLE_PASS_STEREO_SUPPORT_PARAMS_VER2: + pQuerySinglePassStereoSupportedParams->bSinglePassStereoSupported = pascalOrNewer; + pQuerySinglePassStereoSupportedParams->bSinglePassStereoXYZWSupported = turingOrNewer; + break; + default: + return IncompatibleStructVersion(n, pQuerySinglePassStereoSupportedParams->version); + } + + return Ok(str::format(n, " (SPS=", pascalOrNewer ? "supported" : "unsupported", ")"), alreadyLogged); +} + +NVAPI_FUNCTION NvAPI_D3D_SetSinglePassStereoMode(IUnknown* pDevOrContext, NvU32 numViews, NvU32 renderTargetIndexOffset, NvU8 independentViewportMaskEnable) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevOrContext), numViews, renderTargetIndexOffset, independentViewportMaskEnable); + + if (!pDevOrContext || numViews == 0) + return InvalidArgument(n); + + // Accept and log the request, see NvAPI_D3D_SetMultiViewMode + return Ok(str::format(n, " (numViews=", numViews, ") (not implemented, ignoring)"), alreadyLogged); +} + +NVAPI_FUNCTION NvAPI_D3D_QueryModifiedWSupport(IUnknown* pDev, NV_QUERY_MODIFIED_W_SUPPORT_PARAMS* pQueryModifiedWSupportedParams) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDev), log::fmt::ptr(pQueryModifiedWSupportedParams)); + + if (!nvapiAdapterRegistry) + return ApiNotInitialized(n); + + if (!pDev || !pQueryModifiedWSupportedParams) + return InvalidArgument(n); + + if (pQueryModifiedWSupportedParams->version != NV_QUERY_MODIFIED_W_SUPPORT_PARAMS_VER1) + return IncompatibleStructVersion(n, pQueryModifiedWSupportedParams->version); + + // Modified-W (Lens Matched Shading) needs Pascal or newer + auto pascalOrNewer = false; + if (auto adapter = GetMultiViewAdapter(pDev)) + pascalOrNewer = adapter->GetArchitectureId() >= NV_GPU_ARCHITECTURE_GP100; + + pQueryModifiedWSupportedParams->bModifiedWSupported = pascalOrNewer; + + // Accept and log the request, see NvAPI_D3D_SetMultiViewMode + return Ok(str::format(n, " (ModifiedW=", pascalOrNewer ? "supported" : "unsupported", ")"), alreadyLogged); +} + +NVAPI_FUNCTION NvAPI_D3D_SetModifiedWMode(IUnknown* pDevOrContext, NV_MODIFIED_W_PARAMS* psModifiedWParams) { + constexpr auto n = __func__; + thread_local bool alreadyLogged = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevOrContext), log::fmt::ptr(psModifiedWParams)); + + if (!pDevOrContext || !psModifiedWParams) + return InvalidArgument(n); + + if (psModifiedWParams->version != NV_MODIFIED_W_PARAMS_VER1) + return IncompatibleStructVersion(n, psModifiedWParams->version); + + if (psModifiedWParams->numEntries > NV_MODIFIED_W_MAX_VIEWPORTS) + return InvalidArgument(n); + + // Accept and log the request, see NvAPI_D3D_SetMultiViewMode + return Ok(str::format(n, " (numEntries=", psModifiedWParams->numEntries, ") (not implemented, ignoring)"), alreadyLogged); +} \ No newline at end of file diff --git a/src/nvapi_d3d11.cpp b/src/nvapi_d3d11.cpp index 6851e8e8..9e8c72df 100644 --- a/src/nvapi_d3d11.cpp +++ b/src/nvapi_d3d11.cpp @@ -549,3 +549,151 @@ NVAPI_FUNCTION NvAPI_D3D11_GetCudaTextureObject(ID3D11Device* pDevice, NvU32 srv return Error(n, alreadyLoggedError); } } + +// --------------------------------------------------------------------------- +// Extended shader creation +// +// A game calls these to create vertex and geometry shaders carrying +// NVIDIA-specific extensions: custom semantics such as NV_POSITION_VIEW_1 and +// NV_VIEWPORT_MASK, and viewport-broadcast flags. Without these entry points +// the game gets a null pointer, shader setup fails, and it may hang at +// session load. +// +// Where DXVK implements the extended interfaces, the request is forwarded +// with the semantic list intact. Otherwise the shader is created through the +// plain D3D11 path: the bytecode is ordinary DXBC and the shader is valid, but +// the NVIDIA-specific outputs are not acted on, so multi-view rendering will +// not work. +// --------------------------------------------------------------------------- + +// Renders the game's custom-semantic requests as a readable string for the log, +// e.g. "NV_VIEWPORT_MASK(type=2,reg=auto)". These names/types are the exact +// wiring a real SMP implementation will need to honor. +static std::string DescribeCustomSemantics(NvU32 numCustomSemantics, const NV_CUSTOM_SEMANTIC* pCustomSemantics) { + if (numCustomSemantics == 0 || !pCustomSemantics) + return "none"; + + std::string result; + for (auto i = 0U; i < numCustomSemantics; i++) { + if (i > 0) + result += ", "; + + auto& semantic = pCustomSemantics[i]; + result += str::format( + semantic.NVCustomSemanticNameString, + "(type=", semantic.NVCustomSemanticType, + ",reg=", semantic.RegisterSpecified ? str::format(semantic.RegisterNum) : std::string("auto"), + ")"); + } + + return result; +} + +NVAPI_FUNCTION NvAPI_D3D11_CreateVertexShaderEx(ID3D11Device* pDevice, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, const NvAPI_D3D11_CREATE_VERTEX_SHADER_EX* pCreateVertexShaderExArgs, ID3D11VertexShader** ppVertexShader) { + constexpr auto n = __func__; + thread_local bool alreadyLoggedError = false; + thread_local bool alreadyLoggedOk = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevice), log::fmt::ptr(pShaderBytecode), BytecodeLength, log::fmt::ptr(pClassLinkage), log::fmt::ptr(pCreateVertexShaderExArgs), log::fmt::ptr(ppVertexShader)); + + if (!pDevice || !pShaderBytecode || BytecodeLength == 0 || !pCreateVertexShaderExArgs || !ppVertexShader) + return InvalidArgument(n); + + auto version = pCreateVertexShaderExArgs->version; + if (version != NVAPI_D3D11_CREATEVERTEXSHADEREX_VER_1 + && version != NVAPI_D3D11_CREATEVERTEXSHADEREX_VER_2 + && version != NVAPI_D3D11_CREATEVERTEXSHADEREX_VER_3) + return IncompatibleStructVersion(n, version); + + if (pCreateVertexShaderExArgs->NumCustomSemantics > NV_CUSTOM_SEMANTIC_MAX_LIMIT) + return InvalidArgument(n); + + // forward to an SMP-capable DXVK when present + if (auto device = NvapiD3d11Device::GetOrCreate(pDevice)) { + std::array semantics{}; + for (auto i = 0U; i < pCreateVertexShaderExArgs->NumCustomSemantics; i++) { + auto& src = pCreateVertexShaderExArgs->pCustomSemantics[i]; + auto& dst = semantics[i]; + dst.Type = src.NVCustomSemanticType; + std::strncpy(dst.Name, src.NVCustomSemanticNameString, sizeof(dst.Name) - 1); + dst.RegisterSpecified = src.RegisterSpecified; + dst.RegisterNum = src.RegisterNum; + dst.RegisterMask = src.RegisterMask; + } + + if (SUCCEEDED(device->CreateVertexShaderNvSemantics(pShaderBytecode, BytecodeLength, pClassLinkage, semantics.data(), pCreateVertexShaderExArgs->NumCustomSemantics, ppVertexShader))) + return Ok(str::format(n, " (forwarded to DXVK)"), alreadyLoggedOk); + } + // Fallback: the shader is created as a plain VS when DXVK does not + // support the extended entry point, or when forwarding failed. + if (pCreateVertexShaderExArgs->NumCustomSemantics > 0) + log::info(str::format(n, ": semantics [", + DescribeCustomSemantics(pCreateVertexShaderExArgs->NumCustomSemantics, pCreateVertexShaderExArgs->pCustomSemantics), + "] - created as plain VS, extensions ignored")); + + if (FAILED(pDevice->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader))) + return Error(n, alreadyLoggedError); + + return Ok(str::format(n, " (NumCustomSemantics=", pCreateVertexShaderExArgs->NumCustomSemantics, ") (pass-through)"), alreadyLoggedOk); +} + +NVAPI_FUNCTION NvAPI_D3D11_CreateGeometryShaderEx_2(ID3D11Device* pDevice, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, const NvAPI_D3D11_CREATE_GEOMETRY_SHADER_EX* pCreateGeometryShaderExArgs, ID3D11GeometryShader** ppGeometryShader) { + constexpr auto n = __func__; + thread_local bool alreadyLoggedError = false; + thread_local bool alreadyLoggedOk = false; + + if (log::tracing()) + log::trace(n, log::fmt::ptr(pDevice), log::fmt::ptr(pShaderBytecode), BytecodeLength, log::fmt::ptr(pClassLinkage), log::fmt::ptr(pCreateGeometryShaderExArgs), log::fmt::ptr(ppGeometryShader)); + + if (!pDevice || !pShaderBytecode || BytecodeLength == 0 || !pCreateGeometryShaderExArgs || !ppGeometryShader) + return InvalidArgument(n); + + if (pCreateGeometryShaderExArgs->version != NVAPI_D3D11_CREATEGEOMETRYSHADEREX_2_VERSION) + return IncompatibleStructVersion(n, pCreateGeometryShaderExArgs->version); + + if (pCreateGeometryShaderExArgs->NumCustomSemantics > NV_CUSTOM_SEMANTIC_MAX_LIMIT) + return InvalidArgument(n); + + // GS descriptors carry the SMP-relevant flags, so log every call's flags. + // (ForceFastGS note: NVIDIA requires such a GS to be a trivial pass-through, + // so running it as a NORMAL geometry shader is functionally identical - + // we only lose the hardware fast path, not correctness.) + log::info(str::format(n, ": flags [ViewportMask=", pCreateGeometryShaderExArgs->UseViewportMask, + ", OffsetRtIndexByVpIndex=", pCreateGeometryShaderExArgs->OffsetRtIndexByVpIndex, + ", ForceFastGS=", pCreateGeometryShaderExArgs->ForceFastGS, + ", DontUseViewportOrder=", pCreateGeometryShaderExArgs->DontUseViewportOrder, + ", CoordinateSwizzle=", pCreateGeometryShaderExArgs->UseCoordinateSwizzle, + ", SpecificShaderExt=", pCreateGeometryShaderExArgs->UseSpecificShaderExt, + "] semantics [", + DescribeCustomSemantics(pCreateGeometryShaderExArgs->NumCustomSemantics, pCreateGeometryShaderExArgs->pCustomSemantics), + "] - created as plain GS, extensions ignored")); + + // Forward to DXVK when it implements the extended interfaces + if (auto device = NvapiD3d11Device::GetOrCreate(pDevice)) { + std::array semantics{}; + for (auto i = 0U; i < pCreateGeometryShaderExArgs->NumCustomSemantics; i++) { + auto& src = pCreateGeometryShaderExArgs->pCustomSemantics[i]; + auto& dst = semantics[i]; + dst.Type = src.NVCustomSemanticType; + std::strncpy(dst.Name, src.NVCustomSemanticNameString, sizeof(dst.Name) - 1); + dst.RegisterSpecified = src.RegisterSpecified; + dst.RegisterNum = src.RegisterNum; + dst.RegisterMask = src.RegisterMask; + } + + if (SUCCEEDED(device->CreateGeometryShaderNvSemantics(pShaderBytecode, BytecodeLength, pClassLinkage, semantics.data(), pCreateGeometryShaderExArgs->NumCustomSemantics, pCreateGeometryShaderExArgs->UseViewportMask != 0, ppGeometryShader))) + return Ok(str::format(n, " (forwarded to DXVK)"), alreadyLoggedOk); + } + + // Fallback: create the shader without the extensions + if (pCreateGeometryShaderExArgs->NumCustomSemantics > 0) + log::info(str::format(n, ": flags [ViewportMask=", pCreateGeometryShaderExArgs->UseViewportMask, + "] semantics [", DescribeCustomSemantics(pCreateGeometryShaderExArgs->NumCustomSemantics, pCreateGeometryShaderExArgs->pCustomSemantics), + "] - created as plain GS, extensions ignored")); + + if (FAILED(pDevice->CreateGeometryShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppGeometryShader))) + return Error(n, alreadyLoggedError); + + return Ok(str::format(n, " (NumCustomSemantics=", pCreateGeometryShaderExArgs->NumCustomSemantics, ") (pass-through fallback)"), alreadyLoggedOk); +} \ No newline at end of file diff --git a/src/nvapi_interface.cpp b/src/nvapi_interface.cpp index 3e455589..b96d4be2 100644 --- a/src/nvapi_interface.cpp +++ b/src/nvapi_interface.cpp @@ -98,6 +98,8 @@ NVAPI_QUERY_INTERFACE nvapi_QueryInterface(NvU32 id) { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_GetResourceGPUVirtualAddressEx) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateDevice) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateDeviceAndSwapChain) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateVertexShaderEx) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_CreateGeometryShaderEx_2) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D1x_GetGraphicsCapabilities) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D1x_Present) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D11_MultiGPU_Init) @@ -144,6 +146,12 @@ NVAPI_QUERY_INTERFACE nvapi_QueryInterface(NvU32 id) { INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_Sleep) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_GetLatency) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetLatencyMarker) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_QueryMultiViewSupport) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetMultiViewMode) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_QuerySinglePassStereoSupport) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetSinglePassStereoMode) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_QueryModifiedWSupport) + INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_D3D_SetModifiedWMode) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_Vulkan_InitLowLatencyDevice) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_Vulkan_DestroyLowLatencyDevice) INSERT_AND_RETURN_WHEN_EQUALS(NvAPI_Vulkan_GetSleepStatus) diff --git a/tests/nvapi_d3d11.cpp b/tests/nvapi_d3d11.cpp index 5cb4d0c6..3314edc7 100644 --- a/tests/nvapi_d3d11.cpp +++ b/tests/nvapi_d3d11.cpp @@ -27,6 +27,8 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { .LR_SIDE_EFFECT(*_2 = static_cast(&device)) .LR_SIDE_EFFECT(deviceRefCount++) .RETURN(S_OK); + ALLOW_CALL(device, QueryInterface(__uuidof(ID3D11VkExtDevice2), _)) + .RETURN(E_NOINTERFACE); ALLOW_CALL(device, AddRef()) .LR_SIDE_EFFECT(deviceRefCount++) .RETURN(deviceRefCount); @@ -57,6 +59,8 @@ TEST_CASE("D3D11 methods succeed", "[.d3d11]") { .LR_SIDE_EFFECT(*_2 = static_cast(&context)) .LR_SIDE_EFFECT(contextRefCount++) .RETURN(S_OK); + ALLOW_CALL(context, QueryInterface(__uuidof(ID3D11VkExtContext2), _)) + .RETURN(E_NOINTERFACE); ALLOW_CALL(context, AddRef()) .LR_SIDE_EFFECT(contextRefCount++) .RETURN(contextRefCount);