yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f65d756bf
master
1#include "d3d12-posix-synchapi.h" 2 3#include "slang.h" 4 5#if SLANG_LINUX_FAMILY 6 7#include "core/slang-common.h" 8 9#include <cerrno> 10#include <fcntl.h> 11#include <sys/epoll.h> 12#include <sys/eventfd.h> 13#include <sys/poll.h> 14#include <sys/timerfd.h> 15#include <unistd.h> 16 17// To keep aligned with the d3d12 API, we store file descriptors in the low 32 18// bits of HANDLEs. 19static int _handleToFD (HANDLE h ) 20{ 21auto i = reinterpret_cast < std::intptr_t > (h ); 22int fd = static_cast < int > (i ); 23return fd ; 24} 25 26static int _handleToFlags (HANDLE h ) 27{ 28auto i = reinterpret_cast < std::intptr_t > (h ) >>32 ; 29int flags = static_cast < int > (i ); 30return flags ; 31} 32 33static HANDLE _fdToHandle (int fd ,int flags ) 34{ 35 static_assert(sizeof (int ) <=4 ); 36 static_assert(sizeof (std::intptr_t ) >=8 ); 37return reinterpret_cast < HANDLE > (static_cast < std::intptr_t > (flags ) <<32 |fd ); 38} 39 40 41HANDLE CreateEventEx ( 42LPSECURITY_ATTRIBUTES lpEventAttributes , 43LPCSTR lpName , 44DWORD dwFlags , 45DWORD dwDesiredAccess ) 46{ 47int fd = ::eventfd (dwFlags & CREATE_EVENT_INITIAL_SET ?1 :0 ,EFD_CLOEXEC |EFD_NONBLOCK ); 48// Make sure not to return a zero handle, duplicate the fd if necessary 49if (fd == 0 ) 50 { 51int nextFd = fcntl (fd ,F_DUPFD_CLOEXEC ,0 ); 52if (fcntl (nextFd ,F_SETFL ,O_NONBLOCK )== -1 ) 53 { 54close (nextFd ); 55nextFd = -1 ; 56 } 57close (fd ); 58fd = nextFd ; 59 } 60return fd == -1 ?nullptr :_fdToHandle (fd ,dwFlags ); 61} 62 63BOOL CloseHandle (HANDLE h ) 64{ 65if (h == 0 ) 66 { 67return 1 ; 68 } 69// TODO: Windows does reference counting, how to, dupfd? 70return ::close (_handleToFD (h ))== 0 ; 71return 1 ; 72} 73 74BOOL ResetEvent (HANDLE h ) 75{ 76int fd = _handleToFD (h ); 77pollfd pfd {fd ,POLLIN ,0 }; 78uint64_t x ; 79int r = 0 ; 80int nEvents = poll (& pfd ,1 ,0 ); 81if (pfd .revents != POLLIN ) 82 { 83// Nothing to read, already reset 84return 1 ; 85 } 86if (nEvents != 1 ) 87 { 88return 0 ; 89 } 90r = read (fd ,& x ,sizeof (x )); 91if (r == sizeof (x )) 92 { 93// We reset it 94return 1 ; 95 } 96if (r == -1 && errno == EAGAIN ) 97 { 98// Something else reset it 99return 1 ; 100 } 101return 0 ; 102} 103 104BOOL SetEvent (HANDLE h ) 105{ 106int fd = _handleToFD (h ); 107pollfd pfd {fd ,POLLOUT ,0 }; 108for (;;) 109 { 110int nEvents = poll (& pfd ,1 ,-1 ); 111SLANG_ASSERT (nEvents != -1 ); 112SLANG_ASSERT (nEvents != 0 );// shouldn't have timed out 113const uint64_t one = 1 ; 114int w = ::write (fd ,& one ,sizeof (one )); 115if (w == sizeof (one )) 116 { 117return 1 ; 118 } 119if (errno != EAGAIN ) 120 { 121return 0 ; 122 } 123 } 124} 125 126DWORD WaitForSingleObject (const HANDLE h ,const DWORD ms ) 127{ 128int fd = _handleToFD (h ); 129bool manualReset = _handleToFlags (h )& CREATE_EVENT_MANUAL_RESET ; 130pollfd pfd {fd ,POLLIN ,0 }; 131uint64_t x ; 132int r = 0 ; 133// Implement unlimited waits as timing out with WAIT_FAILED after 5 134// seconds. It's probably something fishy with d3dvk-proton or our synchapi 135// implementation 136const bool isInfinite = ms == INFINITE ; 137const DWORD fiveSeconds = 5000 ; 138int nEvents = poll (& pfd ,1 ,isInfinite ?fiveSeconds :ms ); 139if (pfd .revents != POLLIN ) 140 { 141return WAIT_FAILED ; 142 } 143if (nEvents == -1 ) 144 { 145return WAIT_FAILED ; 146 } 147if (nEvents == 0 ) 148 { 149return isInfinite ?WAIT_FAILED :WAIT_TIMEOUT ; 150 } 151if (manualReset ) 152 { 153return WAIT_OBJECT_0 ; 154 } 155r = read (fd ,& x ,sizeof (x )); 156if (r == sizeof (x )) 157 { 158return WAIT_OBJECT_0 ; 159 } 160if (r == -1 && errno == EAGAIN ) 161 { 162return isInfinite ?WAIT_FAILED :WAIT_TIMEOUT ; 163 } 164return WAIT_FAILED ; 165} 166 167DWORD WaitForMultipleObjects (DWORD n ,const HANDLE * hs ,BOOL bWaitAll ,DWORD requestedMs ) 168{ 169if (n == 0 ) 170 { 171return bWaitAll ?WAIT_OBJECT_0 :WAIT_FAILED ; 172 } 173 174// Bail out of infinite waits after 5 seconds as it's probably a 175// driver/vkd3d-proton/synchapi bug 176const bool isInfinite = requestedMs == INFINITE ; 177const DWORD fiveSeconds = 5000 ; 178const auto dwMilliseconds = isInfinite ?fiveSeconds :requestedMs ; 179 180DWORD res ; 181int fds [n ]; 182int flagss [n ]; 183epoll_event evs [n + 1 ];// +1 for our timer 184int ufd = -1 ; 185int epfd = epoll_create1 (EPOLL_CLOEXEC ); 186if (epfd == -1 ) 187 { 188 gotofail ; 189 } 190 191for (int i = 0 ;i < n ;++ i ) 192 { 193fds [i ]= _handleToFD (hs [i ]); 194flagss [i ]= _handleToFlags (hs [i ]); 195epoll_event ev ; 196ev .data .fd = fds [i ]; 197ev .events = EPOLLIN |EPOLLONESHOT ; 198if (epoll_ctl (epfd ,EPOLL_CTL_ADD ,fds [i ],& ev )== -1 ) 199 { 200 gotofail ; 201 } 202 } 203 204// The wait all case can't be made correct on linux, as we can't atomically 205// read from several fds and eventfd is the interface available from 206// vkd3d-proton. 207// 208// As a best-effort we wait until they're all free, then grab them on one 209// after the other, and put the values back if we can't claim them all, it 210// sucks. 211// 212if (bWaitAll ) 213 { 214// Use a timer to easily know for sure when we've timed out 215if (dwMilliseconds != INFINITE ) 216 { 217ufd = timerfd_create (CLOCK_MONOTONIC ,TFD_CLOEXEC ); 218if (ufd == -1 ) 219 { 220 gotofail ; 221 } 222itimerspec spec ; 223spec .it_interval .tv_sec = 0 ; 224spec .it_interval .tv_nsec = 0 ; 225spec .it_value .tv_sec = 0 ; 226spec .it_value .tv_nsec = 1000000 * dwMilliseconds ; 227if (timerfd_settime (ufd ,0 ,& spec ,nullptr )== -1 ) 228 { 229 gotofail ; 230 } 231evs [n ].data .fd = ufd ; 232evs [n ].events = EPOLLIN |EPOLLONESHOT ; 233if (epoll_ctl (epfd ,EPOLL_CTL_ADD ,ufd ,& evs [n ])== -1 ) 234 { 235 gotofail ; 236 } 237 } 238 239bool timesUp = false; 240int nSeenEvents = 0 ; 241// Repeatedly call epoll_wait to eliminate read fds until the timer 242// expires or we have elimininated all our fds 243do 244 { 245do 246 { 247// Wait until epoll tells us they're all available, or the timer is 248const int nEvents = epoll_wait (epfd ,evs ,n + 1 ,-1 ); 249// We didn't specify a timeout, so 0 results is abnormal 250if (nEvents < 1 ) 251 { 252 gotofail ; 253 } 254 255// Process all the returned fds 256for (int i = 0 ;i < nEvents ;++ i ) 257 { 258if (!(evs [i ].events & EPOLLIN )) 259 { 260// Something exceptional happened on the fd 261// Possibly we could just continue and hope it doesn't 262// happen again? 263 gotofail ; 264 } 265if (evs [i ].data .fd == ufd ) 266 { 267// We're out of time, make this the last loop 268uint64_t x ; 269int r = read (ufd ,& x ,sizeof (x )); 270if (r == sizeof (x )) 271 { 272timesUp = true; 273 } 274else 275 { 276 gotofail ; 277 } 278 } 279else 280 { 281// EPOLLONESHOT has removed this fd 282++ nSeenEvents ; 283 } 284 } 285 }while (!(timesUp || nSeenEvents == n )); 286 287// If we got here without seeing enough events, we must have timed out 288if (nSeenEvents < n ) 289 { 290res = isInfinite ?WAIT_FAILED :WAIT_TIMEOUT ; 291 gotoend ; 292 } 293 294// See if all the events are readable. 295// This isn't strictly necessary from a correctness point of view, 296// but since we're not correct anything we can do helps, and it 297// makes the code a bit cleaner. 298// Put all the events back in our epoll instance and see if they're 299// all readable. 300for (int i = 0 ;i < n ;++ i ) 301 { 302epoll_event modEv ; 303modEv .data .fd = fds [i ]; 304modEv .events = EPOLLIN |EPOLLONESHOT ; 305if (epoll_ctl (epfd ,EPOLL_CTL_MOD ,fds [i ],& modEv )== -1 ) 306 { 307 gotofail ; 308 } 309 } 310// Remove the timer if we're using it 311if (dwMilliseconds != INFINITE && epoll_ctl (epfd ,EPOLL_CTL_DEL ,ufd ,nullptr )== -1 ) 312 { 313 gotofail ; 314 } 315int nEvents = epoll_wait (epfd ,evs ,n ,0 ); 316if (nEvents < 0 ) 317 { 318 gotofail ; 319 } 320else if (nEvents < n ) 321 { 322// They're not all still available :( 323// Put our timer back in and try again from the top 324if (dwMilliseconds != INFINITE && 325epoll_ctl (epfd ,EPOLL_CTL_ADD ,ufd ,& evs [n ])== -1 ) 326 { 327 gotofail ; 328 } 329// Put back the any fds which did trigger 330for (int i = 0 ;i < nEvents ;++ i ) 331 { 332epoll_event modEv = evs [i ]; 333modEv .events = EPOLLIN |EPOLLONESHOT ; 334if (epoll_ctl (epfd ,EPOLL_CTL_MOD ,modEv .data .fd ,& modEv )== -1 ) 335 { 336 gotofail ; 337 } 338 } 339continue ; 340 } 341else if (nEvents == n ) 342 { 343for (int i = 0 ;i < nEvents ;++ i ) 344 { 345if (!(evs -> events & EPOLLIN )) 346 { 347 gotofail ; 348 } 349 } 350 } 351 352// Try to grab all the events 353uint64_t vs [n ]; 354int i ; 355bool failure = false; 356for (i = 0 ;i < n ;++ i ) 357 { 358if (flagss [i ]& CREATE_EVENT_MANUAL_RESET ) 359 { 360// We don't need to read this to unset it 361continue ; 362 } 363int r = read (fds [i ],& vs [i ],sizeof (vs [i ])); 364if (r == sizeof (vs [i ])) 365 { 366continue ; 367 } 368else if (r == -1 && errno == EAGAIN ) 369 { 370// contention, put things back and try again 371break ; 372 } 373else 374 { 375// failure, put things back and fail 376failure = true; 377break ; 378 } 379 } 380if (i < n ) 381 { 382// contention or failure 383for (int j = 0 ;j < i ;++ j ) 384 { 385if (flagss [i ]& CREATE_EVENT_MANUAL_RESET ) 386 { 387// We didn't read, so we shouldn't write 388continue ; 389 } 390// TODO: If this doesn't succeed then another thread has jumped 391// in between our non-atomic reads earlier, oops! 392// 393// This is just one case of failure we can detect, 394// arbitrarily many things may have happened between reads 395// and we just wouldn't know... 396int w = write (fds [j ],& vs [j ],sizeof (vs [j ])); 397SLANG_ASSERT (w == sizeof (vs [j ])); 398 } 399if (failure ) 400 { 401 gotofail ; 402 } 403 } 404else 405 { 406// success 407res = WAIT_OBJECT_0 ; 408 gotoend ; 409 } 410 411// If we get here then we've got some contention, go back to the top and try again (or 412// timeout) 413 }while (!timesUp ); 414 } 415else 416 { 417// Wait any 418const int nEvents = 419epoll_wait (epfd ,evs ,n ,dwMilliseconds == INFINITE ?-1 :dwMilliseconds ); 420if (nEvents == -1 ) 421 { 422 gotofail ; 423 } 424if (nEvents == 0 ) 425 { 426res = isInfinite ?WAIT_FAILED :WAIT_TIMEOUT ; 427 gotoend ; 428 } 429// Try reads until we get one 430for (int i = 0 ;i < nEvents ;++ i ) 431 { 432uint64_t x ; 433if (!evs [i ].events & EPOLLIN ) 434 { 435continue ; 436 } 437const int r = ::read (evs [i ].data .fd ,& x ,sizeof (x )); 438if (r == sizeof (x )) 439 { 440res = WAIT_OBJECT_0 ; 441 gotoend ; 442 } 443if (errno != EAGAIN ) 444 { 445 gotofail ; 446 } 447// Some other waiter got this one first 448 } 449 } 450 451 gotoend ; 452fail : 453res = WAIT_FAILED ; 454end : 455close (ufd ); 456close (epfd ); 457return res ; 458} 459 460#endif // SLANG_LINUX_FAMILY