diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/gfx-unit-test/copy-texture-tests.cpp | 11 | ||||
| -rw-r--r-- | tools/gfx/d3d/d3d-util.cpp | 24 | ||||
| -rw-r--r-- | tools/gfx/d3d11/d3d11-device.cpp | 6 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-base.h | 1 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-buffer.cpp | 4 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-fence.cpp | 4 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-posix-synchapi.cpp | 452 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-posix-synchapi.h | 66 | ||||
| -rw-r--r-- | tools/gfx/d3d12/d3d12-texture.cpp | 4 |
9 files changed, 556 insertions, 16 deletions
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) |
