summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEllie Hermaszewska <ellieh@nvidia.com>2023-04-29 11:32:53 +0800
committerGitHub <noreply@github.com>2023-04-29 11:32:53 +0800
commit5adecbe837d27cf4e0a554ae13a0338743a8cb4b (patch)
treec1e791427a2b57165f950f5df264bbaca551ccaf
parent5df7ada451a993efff2b80bb1af2d8c7579ba00b (diff)
vkd3d and dxvk integration (#2823)
* Add d3d sources for linux builds * Return NOT_IMPLEMENTED for shared handle support on Linux * Enable DirectX api on Linux * Do not report DX11 support without FXC * Initial version of SynchAPI emulation * Neaten dx library name handling * Neaten and use posix-synchapi * Add premake option for DirectX on Vulkan * s/SLANG_ENABLE_VKD3D_PROTON/SLANG_ENABLE_VKD3D * Skip failing tests on vkd3d * Regenerate vs projects * Silence unused var warning
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj2
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj.filters6
-rw-r--r--premake5.lua18
-rw-r--r--slang.h16
-rw-r--r--source/compiler-core/slang-fxc-compiler.cpp3
-rw-r--r--source/core/slang-render-api-util.cpp16
-rw-r--r--tools/gfx-unit-test/copy-texture-tests.cpp11
-rw-r--r--tools/gfx/d3d/d3d-util.cpp24
-rw-r--r--tools/gfx/d3d11/d3d11-device.cpp6
-rw-r--r--tools/gfx/d3d12/d3d12-base.h1
-rw-r--r--tools/gfx/d3d12/d3d12-buffer.cpp4
-rw-r--r--tools/gfx/d3d12/d3d12-fence.cpp4
-rw-r--r--tools/gfx/d3d12/d3d12-posix-synchapi.cpp452
-rw-r--r--tools/gfx/d3d12/d3d12-posix-synchapi.h66
-rw-r--r--tools/gfx/d3d12/d3d12-texture.cpp4
15 files changed, 600 insertions, 33 deletions
diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj
index 583d78e25..3198cd6e1 100644
--- a/build/visual-studio/gfx/gfx.vcxproj
+++ b/build/visual-studio/gfx/gfx.vcxproj
@@ -372,6 +372,7 @@ IF EXIST "$(SolutionDir)tools\gfx\slang.slang"\ (xcopy /Q /E /Y /I "$(SolutionDi
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-framebuffer.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.h" />
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-posix-synchapi.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-query.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.h" />
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.h" />
@@ -490,6 +491,7 @@ IF EXIST "$(SolutionDir)tools\gfx\slang.slang"\ (xcopy /Q /E /Y /I "$(SolutionDi
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-fence.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-helper-functions.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-posix-synchapi.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-query.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-render-pass.cpp" />
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-resource-views.cpp" />
diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters
index 1a7ed3d03..c4fcd6046 100644
--- a/build/visual-studio/gfx/gfx.vcxproj.filters
+++ b/build/visual-studio/gfx/gfx.vcxproj.filters
@@ -183,6 +183,9 @@
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-posix-synchapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\gfx\d3d12\d3d12-query.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -533,6 +536,9 @@
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-pipeline-state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-posix-synchapi.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\gfx\d3d12\d3d12-query.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/premake5.lua b/premake5.lua
index 7226d18ad..06da86754 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -212,6 +212,14 @@ newoption {
allowed = { { "true", "True"}, { "false", "False" } }
}
+newoption {
+ trigger = "dx-on-vk",
+ description = "(Optional) If true will use dxvk and vkd3d-proton for DirectX support",
+ value = "bool",
+ default = "false",
+ allowed = { { "true", "True"}, { "false", "False" } }
+}
+
buildLocation = _OPTIONS["build-location"]
executeBinary = (_OPTIONS["execute-binary"] == "true")
buildGlslang = (_OPTIONS["build-glslang"] == "true")
@@ -227,6 +235,7 @@ deployLLVM = (_OPTIONS["deploy-slang-llvm"] == "true")
deployGLSLang = (_OPTIONS["deploy-slang-glslang"] == "true")
fullDebugValidation = (_OPTIONS["full-debug-validation"] == "true")
enableAsan = (_OPTIONS["enable-asan"] == "true")
+dxOnVk = (_OPTIONS["dx-on-vk"] == "true")
-- If stdlib embedding is enabled, disable stdlib source embedding by default
disableStdlibSource = enableEmbedStdLib
@@ -396,6 +405,10 @@ workspace "slang"
-- files which may be included by another project
defines { "WIN32_LEAN_AND_MEAN", "VC_EXTRALEAN", "NOMINMAX" }
+ if dxOnVk then
+ defines { "SLANG_CONFIG_DX_ON_VK" }
+ end
+
function dump(o)
if type(o) == 'table' then
local s = '{ '
@@ -1028,6 +1041,11 @@ tool "gfx"
else
-- Linux like
addSourceDir "tools/gfx/vulkan"
+ if dxOnVk then
+ addSourceDir "tools/gfx/d3d"
+ addSourceDir "tools/gfx/d3d11"
+ addSourceDir "tools/gfx/d3d12"
+ end
--addSourceDir "tools/gfx/open-gl"
end
diff --git a/slang.h b/slang.h
index 31a98481a..89ef4ce4e 100644
--- a/slang.h
+++ b/slang.h
@@ -167,27 +167,29 @@ Any platforms not detected by the above logic are now now explicitly zeroed out.
#define SLANG_UNIX_FAMILY (SLANG_LINUX_FAMILY || SLANG_APPLE_FAMILY) /* shortcut for unix/posix platforms */
/* Macros concerning DirectX */
+#if !defined(SLANG_CONFIG_DX_ON_VK) || !SLANG_CONFIG_DX_ON_VK
+# define SLANG_ENABLE_DXVK 0
+# define SLANG_ENABLE_VKD3D 0
+#else
+# define SLANG_ENABLE_DXVK 1
+# define SLANG_ENABLE_VKD3D 1
+#endif
+
#if SLANG_WINDOWS_FAMILY
# define SLANG_ENABLE_DIRECTX 1
# define SLANG_ENABLE_DXGI_DEBUG 1
# define SLANG_ENABLE_DXBC_SUPPORT 1
# define SLANG_ENABLE_PIX 1
-# define SLANG_ENABLE_DXVK 0
-# define SLANG_ENABLE_VKD3D_PROTON 0
#elif SLANG_LINUX_FAMILY
-# define SLANG_ENABLE_DIRECTX 0
+# define SLANG_ENABLE_DIRECTX (SLANG_ENABLE_DXVK || SLANG_ENABLE_VKD3D)
# define SLANG_ENABLE_DXGI_DEBUG 0
# define SLANG_ENABLE_DXBC_SUPPORT 0
# define SLANG_ENABLE_PIX 0
-# define SLANG_ENABLE_DXVK 1
-# define SLANG_ENABLE_VKD3D_PROTON 1
#else
# define SLANG_ENABLE_DIRECTX 0
# define SLANG_ENABLE_DXGI_DEBUG 0
# define SLANG_ENABLE_DXBC_SUPPORT 0
# define SLANG_ENABLE_PIX 0
-# define SLANG_ENABLE_DXVK 0
-# define SLANG_ENABLE_VKD3D_PROTON 0
#endif
/* Macro for declaring if a method is no throw. Should be set before the return parameter. */
diff --git a/source/compiler-core/slang-fxc-compiler.cpp b/source/compiler-core/slang-fxc-compiler.cpp
index 4153585d3..b3b952796 100644
--- a/source/compiler-core/slang-fxc-compiler.cpp
+++ b/source/compiler-core/slang-fxc-compiler.cpp
@@ -363,7 +363,8 @@ SlangResult FXCDownstreamCompiler::convert(IArtifact* from, const ArtifactDesc&
{
ComPtr<ISlangSharedLibrary> library;
- SLANG_RETURN_ON_FAIL(DownstreamCompilerUtil::loadSharedLibrary(path, loader, nullptr, "d3dcompiler_47", library));
+ const char* const libName = "d3dcompiler_47";
+ SLANG_RETURN_ON_FAIL(DownstreamCompilerUtil::loadSharedLibrary(path, loader, nullptr, libName, library));
SLANG_ASSERT(library);
if (!library)
diff --git a/source/core/slang-render-api-util.cpp b/source/core/slang-render-api-util.cpp
index ca58091af..334e9a9d1 100644
--- a/source/core/slang-render-api-util.cpp
+++ b/source/core/slang-render-api-util.cpp
@@ -245,7 +245,7 @@ static Token nextToken(Slang::UnownedStringSlice& textInOut, Slang::UnownedStrin
return RenderApiType::Unknown;
}
-#if SLANG_WINDOWS_FAMILY
+#if SLANG_ENABLE_DIRECTX
static bool _canLoadSharedLibrary(const char* libName)
{
SharedLibrary::Handle handle;
@@ -266,18 +266,16 @@ static bool _canLoadSharedLibrary(const char* libName)
#if SLANG_WINDOWS_FAMILY
case RenderApiType::OpenGl: return _canLoadSharedLibrary("opengl32");
case RenderApiType::Vulkan: return _canLoadSharedLibrary("vulkan-1") || _canLoadSharedLibrary("vk_swiftshader");
- case RenderApiType::D3D11: return _canLoadSharedLibrary("d3d11");
- case RenderApiType::D3D12: return _canLoadSharedLibrary("d3d12");
#elif SLANG_UNIX_FAMILY
- case RenderApiType::D3D11: return false;
- case RenderApiType::D3D12: return false;
- // The below can be used once they're confirmed working with gfx
- // case RenderApiType::D3D11: return _canLoadSharedLibrary("dxvk_d3d11");
- // case RenderApiType::D3D12: return _canLoadSharedLibrary("vkd3d-proton-d3d12");
-
case RenderApiType::OpenGl: return true;
case RenderApiType::Vulkan: return true;
#endif
+
+#if SLANG_ENABLE_DIRECTX
+ case RenderApiType::D3D11: return _canLoadSharedLibrary(SLANG_ENABLE_DXVK ? "dxvk_d3d11" : "d3d11");
+ case RenderApiType::D3D12: return _canLoadSharedLibrary(SLANG_ENABLE_VKD3D ? "vkd3d-proton-d3d12" : "d3d12");
+#endif
+
case RenderApiType::CPU: return true;
// We'll assume CUDA is available, and if not, trying to create it will detect it
case RenderApiType::CUDA: return true;
diff --git a/tools/gfx-unit-test/copy-texture-tests.cpp b/tools/gfx-unit-test/copy-texture-tests.cpp
index 6a9d39d22..0129dd818 100644
--- a/tools/gfx-unit-test/copy-texture-tests.cpp
+++ b/tools/gfx-unit-test/copy-texture-tests.cpp
@@ -744,12 +744,23 @@ namespace gfx_test
template<typename T>
void copyTextureTestImpl(IDevice* device, UnitTestContext* context)
{
+ const bool isVkd3d = SLANG_ENABLE_VKD3D &&
+ strcmp(device->getDeviceInfo().apiName, "Direct3D 12") == 0;
+
// Skip Type::Unknown and Type::Buffer as well as Format::Unknown
// TODO: Add support for TextureCube
for (uint32_t i = 2; i < (uint32_t)ITextureResource::Type::_Count - 1; ++i)
{
for (uint32_t j = 1; j < (uint32_t)Format::_Count; ++j)
{
+ // Fails validation VUID-VkImageCreateInfo-imageCreateMaxMipLevels-02251
+ if(isVkd3d && ((Format)j == Format::R32G32B32_TYPELESS ||
+ (Format)j == Format::R32G32B32_FLOAT ||
+ (Format)j == Format::R32G32B32_UINT ||
+ (Format)j == Format::R32G32B32_SINT))
+ {
+ continue;
+ }
auto type = (ITextureResource::Type)i;
auto format = (Format)j;
auto validationFormat = getValidationTextureFormat(format);
diff --git a/tools/gfx/d3d/d3d-util.cpp b/tools/gfx/d3d/d3d-util.cpp
index aff610796..0257ede1b 100644
--- a/tools/gfx/d3d/d3d-util.cpp
+++ b/tools/gfx/d3d/d3d-util.cpp
@@ -431,15 +431,20 @@ bool D3DUtil::isTypeless(DXGI_FORMAT format)
static pD3DCompile compileFunc = nullptr;
if (!compileFunc)
{
+ // On Linux, vkd3d-utils isn't suitable as a unix replacement for fxc
+ // due to at least the following missing feature:
+ // https://bugs.winehq.org/show_bug.cgi?id=54872
+
// TODO(tfoley): maybe want to search for one of a few versions of the DLL
- HMODULE compilerModule = LoadLibraryA("d3dcompiler_47.dll");
- if (!compilerModule)
+ const char* const libName = "d3dcompiler_47";
+ SharedLibrary::Handle compilerModule;
+ if (SLANG_FAILED(SharedLibrary::load(libName, compilerModule)))
{
- fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n");
+ fprintf(stderr, "error: failed to load '%s'\n", libName);
return SLANG_FAIL;
}
- compileFunc = (pD3DCompile)GetProcAddress(compilerModule, "D3DCompile");
+ compileFunc = (pD3DCompile)SharedLibrary::findSymbolAddressByName(compilerModule, "D3DCompile");
if (!compileFunc)
{
fprintf(stderr, "error: failed load symbol 'D3DCompile'\n");
@@ -488,17 +493,14 @@ bool D3DUtil::isTypeless(DXGI_FORMAT format)
/* static */SharedLibrary::Handle D3DUtil::getDxgiModule()
{
-#if SLANG_ENABLE_DXVK
- const char* const libPath = "dxvk_dxgi";
-#else
- const char* const libPath = "dxgi";
-#endif
+ const char* const libName = SLANG_ENABLE_DXVK ? "dxvk_dxgi" : "dxgi";
+
static SharedLibrary::Handle s_dxgiModule = [&](){
SharedLibrary::Handle h = nullptr;
- SharedLibrary::load(libPath, h);
+ SharedLibrary::load(libName, h);
if (!h)
{
- fprintf(stderr, "error: failed to load dll '%s'\n", libPath);
+ fprintf(stderr, "error: failed to load dll '%s'\n", libName);
}
return h;
}();
diff --git a/tools/gfx/d3d11/d3d11-device.cpp b/tools/gfx/d3d11/d3d11-device.cpp
index 4ab26725e..c10a608dc 100644
--- a/tools/gfx/d3d11/d3d11-device.cpp
+++ b/tools/gfx/d3d11/d3d11-device.cpp
@@ -47,11 +47,7 @@ SlangResult DeviceImpl::initialize(const Desc& desc)
// Rather than statically link against D3D, we load it dynamically.
SharedLibrary::Handle d3dModule;
-#if SLANG_ENABLE_DXVK
- const char* libName = "dxvk_d3d11";
-#else
- const char* libName = "d3d11";
-#endif
+ const char* libName = SLANG_ENABLE_DXVK ? "dxvk_d3d11" : "d3d11";
if (SLANG_FAILED(SharedLibrary::load(libName, d3dModule)))
{
fprintf(stderr, "error: failed to load '%s'\n", libName);
diff --git a/tools/gfx/d3d12/d3d12-base.h b/tools/gfx/d3d12/d3d12-base.h
index 52e8d4623..0e648f9c4 100644
--- a/tools/gfx/d3d12/d3d12-base.h
+++ b/tools/gfx/d3d12/d3d12-base.h
@@ -13,6 +13,7 @@
#include "core/slang-chunked-list.h"
#include "d3d12-descriptor-heap.h"
#include "d3d12-resource.h"
+#include "d3d12-posix-synchapi.h"
#pragma push_macro("WIN32_LEAN_AND_MEAN")
#pragma push_macro("NOMINMAX")
diff --git a/tools/gfx/d3d12/d3d12-buffer.cpp b/tools/gfx/d3d12/d3d12-buffer.cpp
index 42babfae6..7d3376607 100644
--- a/tools/gfx/d3d12/d3d12-buffer.cpp
+++ b/tools/gfx/d3d12/d3d12-buffer.cpp
@@ -35,6 +35,9 @@ Result BufferResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
Result BufferResourceImpl::getSharedHandle(InteropHandle* outHandle)
{
+#if !SLANG_WINDOWS_FAMILY
+ return SLANG_E_NOT_IMPLEMENTED;
+#else
// Check if a shared handle already exists for this resource.
if (sharedHandle.handleValue != 0)
{
@@ -51,6 +54,7 @@ Result BufferResourceImpl::getSharedHandle(InteropHandle* outHandle)
outHandle->api = InteropHandleAPI::D3D12;
sharedHandle = *outHandle;
return SLANG_OK;
+#endif
}
Result BufferResourceImpl::map(MemoryRange* rangeToRead, void** outPointer)
diff --git a/tools/gfx/d3d12/d3d12-fence.cpp b/tools/gfx/d3d12/d3d12-fence.cpp
index be3d94fcd..124d9354c 100644
--- a/tools/gfx/d3d12/d3d12-fence.cpp
+++ b/tools/gfx/d3d12/d3d12-fence.cpp
@@ -47,6 +47,9 @@ Result FenceImpl::setCurrentValue(uint64_t value)
Result FenceImpl::getSharedHandle(InteropHandle* outHandle)
{
+#if !SLANG_WINDOWS_FAMILY
+ return SLANG_E_NOT_IMPLEMENTED;
+#else
// Check if a shared handle already exists.
if (sharedHandle.handleValue != 0)
{
@@ -61,6 +64,7 @@ Result FenceImpl::getSharedHandle(InteropHandle* outHandle)
outHandle->api = InteropHandleAPI::D3D12;
sharedHandle = *outHandle;
return SLANG_OK;
+#endif
}
Result FenceImpl::getNativeHandle(InteropHandle* outNativeHandle)
diff --git a/tools/gfx/d3d12/d3d12-posix-synchapi.cpp b/tools/gfx/d3d12/d3d12-posix-synchapi.cpp
new file mode 100644
index 000000000..a46e96141
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-posix-synchapi.cpp
@@ -0,0 +1,452 @@
+#include "d3d12-posix-synchapi.h"
+
+#include "slang.h"
+
+#if SLANG_LINUX_FAMILY
+
+#include "core/slang-common.h"
+
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/poll.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+#include <cerrno>
+
+// To keep aligned with the d3d12 API, we store file descriptors in the low 32
+// bits of HANDLEs.
+static int _handleToFD(HANDLE h)
+{
+ auto i = reinterpret_cast<std::intptr_t>(h);
+ int fd = static_cast<int>(i);
+ return fd;
+}
+
+static int _handleToFlags(HANDLE h)
+{
+ auto i = reinterpret_cast<std::intptr_t>(h) >> 32;
+ int flags = static_cast<int>(i);
+ return flags;
+}
+
+static HANDLE _fdToHandle(int fd, int flags)
+{
+ static_assert(sizeof(int) <= 4);
+ static_assert(sizeof(std::intptr_t) >= 8);
+ return reinterpret_cast<HANDLE>(static_cast<std::intptr_t>(flags) << 32 | fd);
+}
+
+
+HANDLE CreateEventEx(
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ LPCSTR lpName,
+ DWORD dwFlags,
+ DWORD dwDesiredAccess)
+{
+ int fd = ::eventfd(dwFlags & CREATE_EVENT_INITIAL_SET ? 1 : 0, EFD_CLOEXEC | EFD_NONBLOCK);
+ // Make sure not to return a zero handle, duplicate the fd if necessary
+ if(fd == 0)
+ {
+ int nextFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ if(fcntl(nextFd, F_SETFL, O_NONBLOCK) == -1)
+ {
+ close(nextFd);
+ nextFd = -1;
+ }
+ close(fd);
+ fd = nextFd;
+ }
+ return fd == -1 ? nullptr : _fdToHandle(fd, dwFlags);
+}
+
+BOOL CloseHandle(HANDLE h)
+{
+ if(h == 0)
+ {
+ return 1;
+ }
+ // TODO: Windows does reference counting, how to, dupfd?
+ return ::close(_handleToFD(h)) == 0;
+ return 1;
+}
+
+BOOL ResetEvent(HANDLE h)
+{
+ int fd = _handleToFD(h);
+ pollfd pfd{fd, POLLIN, 0};
+ uint64_t x;
+ int r = 0;
+ int nEvents = poll(&pfd, 1, 0);
+ if(pfd.revents != POLLIN)
+ {
+ // Nothing to read, already reset
+ return 1;
+ }
+ if(nEvents != 1)
+ {
+ return 0;
+ }
+ r = read(fd, &x, sizeof(x));
+ if(r == sizeof(x))
+ {
+ // We reset it
+ return 1;
+ }
+ if(r == -1 && errno == EAGAIN)
+ {
+ // Something else reset it
+ return 1;
+ }
+ return 0;
+}
+
+BOOL SetEvent(HANDLE h)
+{
+ int fd = _handleToFD(h);
+ pollfd pfd{fd, POLLOUT, 0};
+ for(;;)
+ {
+ int nEvents = poll(&pfd, 1, -1);
+ SLANG_ASSERT(nEvents != -1);
+ SLANG_ASSERT(nEvents != 0); // shouldn't have timed out
+ const uint64_t one = 1;
+ int w = ::write(fd, &one, sizeof(one));
+ if(w == sizeof(one))
+ {
+ return 1;
+ }
+ if(errno != EAGAIN)
+ {
+ return 0;
+ }
+ }
+}
+
+DWORD WaitForSingleObject(HANDLE h, DWORD ms)
+{
+ int fd = _handleToFD(h);
+ bool manualReset = _handleToFlags(h) & CREATE_EVENT_MANUAL_RESET;
+ pollfd pfd{fd, POLLIN, 0};
+ uint64_t x;
+ int r = 0;
+ int nEvents = poll(&pfd, 1, ms);
+ if(pfd.revents != POLLIN)
+ {
+ return WAIT_FAILED;
+ }
+ if(nEvents == -1)
+ {
+ return WAIT_FAILED;
+ }
+ if (nEvents == 0)
+ {
+ return WAIT_TIMEOUT;
+ }
+ if(manualReset)
+ {
+ return WAIT_OBJECT_0;
+ }
+ r = read(fd, &x, sizeof(x));
+ if(r == sizeof(x))
+ {
+ return WAIT_OBJECT_0;
+ }
+ if(r == -1 && errno == EAGAIN)
+ {
+ return WAIT_TIMEOUT;
+ }
+ return WAIT_FAILED;
+}
+
+DWORD WaitForMultipleObjects(
+ DWORD n,
+ const HANDLE *hs,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds)
+{
+ if(n == 0)
+ {
+ return bWaitAll ? WAIT_OBJECT_0 : WAIT_FAILED;
+ }
+ DWORD res;
+ int fds[n];
+ int flagss[n];
+ epoll_event evs[n+1]; // +1 for our timer
+ int ufd = -1;
+ int epfd = epoll_create1(EPOLL_CLOEXEC);
+ if(epfd == -1)
+ {
+ goto fail;
+ }
+
+ for(int i = 0; i < n; ++i)
+ {
+ fds[i] = _handleToFD(hs[i]);
+ flagss[i] = _handleToFlags(hs[i]);
+ epoll_event ev;
+ ev.data.fd = fds[i];
+ ev.events = EPOLLIN | EPOLLONESHOT;
+ if(epoll_ctl(epfd, EPOLL_CTL_ADD, fds[i], &ev) == -1)
+ {
+ goto fail;
+ }
+ }
+
+ // The wait all case can't be made correct on linux, as we can't atomically
+ // read from several fds and eventfd is the interface available from
+ // vkd3d-proton.
+ //
+ // As a best-effort we wait until they're all free, then grab them on one
+ // after the other, and put the values back if we can't claim them all, it
+ // sucks.
+ //
+ if(bWaitAll)
+ {
+ // Use a timer to easily know for sure when we've timed out
+ if(dwMilliseconds != INFINITE)
+ {
+ ufd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if(ufd == -1)
+ {
+ goto fail;
+ }
+ itimerspec spec;
+ spec.it_interval.tv_sec = 0;
+ spec.it_interval.tv_nsec = 0;
+ spec.it_value.tv_sec = 0;
+ spec.it_value.tv_nsec = 1000000 * dwMilliseconds;
+ if(timerfd_settime(ufd, 0, &spec, nullptr) == -1)
+ {
+ goto fail;
+ }
+ evs[n].data.fd = ufd;
+ evs[n].events = EPOLLIN | EPOLLONESHOT;
+ if(epoll_ctl(epfd, EPOLL_CTL_ADD, ufd, &evs[n]) == -1)
+ {
+ goto fail;
+ }
+ }
+
+ bool timesUp = false;
+ int nSeenEvents = 0;
+ // Repeatedly call epoll_wait to eliminate read fds until the timer
+ // expires or we have elimininated all our fds
+ do
+ {
+ do
+ {
+ // Wait until epoll tells us they're all available, or the timer is
+ const int nEvents = epoll_wait(epfd, evs, n+1, -1);
+ // We didn't specify a timeout, so 0 results is abnormal
+ if(nEvents < 1)
+ {
+ goto fail;
+ }
+
+ // Process all the returned fds
+ for(int i = 0; i < nEvents; ++i)
+ {
+ if(!(evs[i].events & EPOLLIN))
+ {
+ // Something exceptional happened on the fd
+ // Possibly we could just continue and hope it doesn't
+ // happen again?
+ goto fail;
+ }
+ if(evs[i].data.fd == ufd)
+ {
+ // We're out of time, make this the last loop
+ uint64_t x;
+ int r = read(ufd, &x, sizeof(x));
+ if(r == sizeof(x))
+ {
+ timesUp = true;
+ }
+ else
+ {
+ goto fail;
+ }
+ }
+ else
+ {
+ // EPOLLONESHOT has removed this fd
+ ++nSeenEvents;
+ }
+ }
+ }
+ while(!(timesUp || nSeenEvents == n));
+
+ // If we got here without seeing enough events, we must have timed out
+ if(nSeenEvents < n)
+ {
+ res = WAIT_TIMEOUT;
+ goto end;
+ }
+
+ // See if all the events are readable.
+ // This isn't strictly necessary from a correctness point of view,
+ // but since we're not correct anything we can do helps, and it
+ // makes the code a bit cleaner.
+ // Put all the events back in our epoll instance and see if they're
+ // all readable.
+ for(int i = 0; i < n; ++i)
+ {
+ epoll_event modEv;
+ modEv.data.fd = fds[i];
+ modEv.events = EPOLLIN | EPOLLONESHOT;
+ if(epoll_ctl(epfd, EPOLL_CTL_MOD, fds[i], &modEv) == -1)
+ {
+ goto fail;
+ }
+ }
+ // Remove the timer if we're using it
+ if(dwMilliseconds != INFINITE && epoll_ctl(epfd, EPOLL_CTL_DEL, ufd, nullptr) == -1)
+ {
+ goto fail;
+ }
+ int nEvents = epoll_wait(epfd, evs, n, 0);
+ if(nEvents < 0)
+ {
+ goto fail;
+ }
+ else if(nEvents < n)
+ {
+ // They're not all still available :(
+ // Put our timer back in and try again from the top
+ if(dwMilliseconds != INFINITE && epoll_ctl(epfd, EPOLL_CTL_ADD, ufd, &evs[n]) == -1)
+ {
+ goto fail;
+ }
+ // Put back the any fds which did trigger
+ for(int i = 0; i < nEvents; ++i)
+ {
+ epoll_event modEv = evs[i];
+ modEv.events = EPOLLIN | EPOLLONESHOT;
+ if(epoll_ctl(epfd, EPOLL_CTL_MOD, modEv.data.fd, &modEv) == -1)
+ {
+ goto fail;
+ }
+ }
+ continue;
+ }
+ else if(nEvents == n)
+ {
+ for(int i = 0; i < nEvents; ++i)
+ {
+ if(!(evs->events & EPOLLIN))
+ {
+ goto fail;
+ }
+ }
+ }
+
+ // Try to grab all the events
+ uint64_t vs[n];
+ int i;
+ bool failure = false;
+ for(i = 0; i < n; ++i)
+ {
+ if(flagss[i] & CREATE_EVENT_MANUAL_RESET)
+ {
+ // We don't need to read this to unset it
+ continue;
+ }
+ int r = read(fds[i], &vs[i], sizeof(vs[i]));
+ if(r == sizeof(vs[i]))
+ {
+ continue;
+ }
+ else if(r == -1 && errno == EAGAIN)
+ {
+ // contention, put things back and try again
+ break;
+ }
+ else
+ {
+ // failure, put things back and fail
+ failure = true;
+ break;
+ }
+ }
+ if (i < n)
+ {
+ // contention or failure
+ for(int j = 0; j < i; ++j)
+ {
+ if(flagss[i] & CREATE_EVENT_MANUAL_RESET)
+ {
+ // We didn't read, so we shouldn't write
+ continue;
+ }
+ // TODO: If this doesn't succeed then another thread has jumped
+ // in between our non-atomic reads earlier, oops!
+ //
+ // This is just one case of failure we can detect,
+ // arbitrarily many things may have happened between reads
+ // and we just wouldn't know...
+ int w = write(fds[j], &vs[j], sizeof(vs[j]));
+ SLANG_ASSERT(w == sizeof(vs[j]));
+ }
+ if(failure)
+ {
+ goto fail;
+ }
+ }
+ else
+ {
+ // success
+ res = WAIT_OBJECT_0;
+ goto end;
+ }
+
+ // If we get here then we've got some contention, go back to the top and try again (or timeout)
+ }
+ while(!timesUp);
+ }
+ else
+ {
+ // Wait any
+ const int nEvents = epoll_wait(epfd, evs, n, dwMilliseconds == INFINITE ? -1 : dwMilliseconds);
+ if(nEvents == -1)
+ {
+ goto fail;
+ }
+ if(nEvents == 0)
+ {
+ res = WAIT_TIMEOUT;
+ goto end;
+ }
+ // Try reads until we get one
+ for(int i = 0; i < nEvents; ++i)
+ {
+ uint64_t x;
+ if(!evs[i].events & EPOLLIN)
+ {
+ continue;
+ }
+ const int r = ::read(evs[i].data.fd, &x, sizeof(x));
+ if (r == sizeof(x))
+ {
+ res = WAIT_OBJECT_0;
+ goto end;
+ }
+ if (errno != EAGAIN)
+ {
+ goto fail;
+ }
+ // Some other waiter got this one first
+ }
+ }
+
+ goto end;
+fail:
+ res = WAIT_FAILED;
+end:
+ close(ufd);
+ close(epfd);
+ return res;
+}
+
+#endif // SLANG_LINUX_FAMILY
diff --git a/tools/gfx/d3d12/d3d12-posix-synchapi.h b/tools/gfx/d3d12/d3d12-posix-synchapi.h
new file mode 100644
index 000000000..fb556fb51
--- /dev/null
+++ b/tools/gfx/d3d12/d3d12-posix-synchapi.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "slang.h"
+
+#if SLANG_LINUX_FAMILY
+
+#pragma push_macro("WIN32_LEAN_AND_MEAN")
+#pragma push_macro("NOMINMAX")
+#undef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+#define NOMINMAX
+#include <windows.h>
+#pragma pop_macro("NOMINMAX")
+#pragma pop_macro("WIN32_LEAN_AND_MEAN")
+
+////////////////////////////////////////////////////////////////
+//
+// It's important to note that due to platform constraints this can't be a
+// totally faithful implementation of the Windows API.
+//
+// Notably, the "wait all" case in WaitForMultipleObjects can't be made correct
+// on linux, as we can't atomically read from several fds and eventfd is the
+// interface available from vkd3d-proton.
+//
+////////////////////////////////////////////////////////////////
+
+//
+// The synchapi types and macros used in gfx
+//
+#define INFINITE 0xffffffff
+
+#define WAIT_FAILED 0xffffffff
+#define WAIT_OBJECT_0 0
+#define WAIT_TIMEOUT 258
+
+typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
+
+#define CREATE_EVENT_MANUAL_RESET 1
+#define CREATE_EVENT_INITIAL_SET 2
+
+#define SYNCHRONIZE 0x00100000
+#define STANDARD_RIGHTS_REQUIRED 0x000f0000
+#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
+
+HANDLE CreateEventEx(
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ LPCSTR lpName,
+ DWORD dwFlags,
+ DWORD dwDesiredAccess);
+
+BOOL CloseHandle(HANDLE h);
+
+BOOL ResetEvent(HANDLE h);
+
+BOOL SetEvent(HANDLE h);
+
+DWORD WaitForSingleObject(HANDLE h, DWORD ms);
+
+DWORD WaitForMultipleObjects(
+ DWORD nHandles,
+ const HANDLE *handles,
+ BOOL bWaitAll,
+ DWORD dwMilliseconds);
+
+#endif // SLANG_LINUX_FAMILY
diff --git a/tools/gfx/d3d12/d3d12-texture.cpp b/tools/gfx/d3d12/d3d12-texture.cpp
index 4f1898ef7..9f47760e5 100644
--- a/tools/gfx/d3d12/d3d12-texture.cpp
+++ b/tools/gfx/d3d12/d3d12-texture.cpp
@@ -30,6 +30,9 @@ Result TextureResourceImpl::getNativeResourceHandle(InteropHandle* outHandle)
Result TextureResourceImpl::getSharedHandle(InteropHandle* outHandle)
{
+#if !SLANG_WINDOWS_FAMILY
+ return SLANG_E_NOT_IMPLEMENTED;
+#else
// Check if a shared handle already exists for this resource.
if (sharedHandle.handleValue != 0)
{
@@ -45,6 +48,7 @@ Result TextureResourceImpl::getSharedHandle(InteropHandle* outHandle)
pResource, NULL, GENERIC_ALL, nullptr, (HANDLE*)&outHandle->handleValue));
outHandle->api = InteropHandleAPI::D3D12;
return SLANG_OK;
+#endif
}
Result TextureResourceImpl::setDebugName(const char* name)