13464ebd5Sriastradh/* 23464ebd5Sriastradh * Copyright 2010 Christoph Bumiller 33464ebd5Sriastradh * 43464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a 53464ebd5Sriastradh * copy of this software and associated documentation files (the "Software"), 63464ebd5Sriastradh * to deal in the Software without restriction, including without limitation 73464ebd5Sriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense, 83464ebd5Sriastradh * and/or sell copies of the Software, and to permit persons to whom the 93464ebd5Sriastradh * Software is furnished to do so, subject to the following conditions: 103464ebd5Sriastradh * 113464ebd5Sriastradh * The above copyright notice and this permission notice shall be included in 123464ebd5Sriastradh * all copies or substantial portions of the Software. 133464ebd5Sriastradh * 143464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 153464ebd5Sriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 163464ebd5Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17af69d88dSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18af69d88dSmrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19af69d88dSmrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20af69d88dSmrg * OTHER DEALINGS IN THE SOFTWARE. 213464ebd5Sriastradh */ 223464ebd5Sriastradh 233464ebd5Sriastradh#include "nouveau_screen.h" 24af69d88dSmrg#include "nouveau_winsys.h" 253464ebd5Sriastradh#include "nouveau_fence.h" 2601e04c3fSmrg#include "util/os_time.h" 273464ebd5Sriastradh 283464ebd5Sriastradh#ifdef PIPE_OS_UNIX 293464ebd5Sriastradh#include <sched.h> 303464ebd5Sriastradh#endif 313464ebd5Sriastradh 3201e04c3fSmrgbool 3301e04c3fSmrgnouveau_fence_new(struct nouveau_screen *screen, struct nouveau_fence **fence) 343464ebd5Sriastradh{ 353464ebd5Sriastradh *fence = CALLOC_STRUCT(nouveau_fence); 363464ebd5Sriastradh if (!*fence) 3701e04c3fSmrg return false; 383464ebd5Sriastradh 393464ebd5Sriastradh (*fence)->screen = screen; 403464ebd5Sriastradh (*fence)->ref = 1; 417ec681f3Smrg list_inithead(&(*fence)->work); 423464ebd5Sriastradh 4301e04c3fSmrg return true; 443464ebd5Sriastradh} 453464ebd5Sriastradh 463464ebd5Sriastradhstatic void 473464ebd5Sriastradhnouveau_fence_trigger_work(struct nouveau_fence *fence) 483464ebd5Sriastradh{ 493464ebd5Sriastradh struct nouveau_fence_work *work, *tmp; 503464ebd5Sriastradh 513464ebd5Sriastradh LIST_FOR_EACH_ENTRY_SAFE(work, tmp, &fence->work, list) { 523464ebd5Sriastradh work->func(work->data); 537ec681f3Smrg list_del(&work->list); 543464ebd5Sriastradh FREE(work); 553464ebd5Sriastradh } 563464ebd5Sriastradh} 573464ebd5Sriastradh 583464ebd5Sriastradhvoid 593464ebd5Sriastradhnouveau_fence_emit(struct nouveau_fence *fence) 603464ebd5Sriastradh{ 613464ebd5Sriastradh struct nouveau_screen *screen = fence->screen; 623464ebd5Sriastradh 633464ebd5Sriastradh assert(fence->state == NOUVEAU_FENCE_STATE_AVAILABLE); 643464ebd5Sriastradh 653464ebd5Sriastradh /* set this now, so that if fence.emit triggers a flush we don't recurse */ 663464ebd5Sriastradh fence->state = NOUVEAU_FENCE_STATE_EMITTING; 673464ebd5Sriastradh 683464ebd5Sriastradh ++fence->ref; 693464ebd5Sriastradh 703464ebd5Sriastradh if (screen->fence.tail) 713464ebd5Sriastradh screen->fence.tail->next = fence; 723464ebd5Sriastradh else 733464ebd5Sriastradh screen->fence.head = fence; 743464ebd5Sriastradh 753464ebd5Sriastradh screen->fence.tail = fence; 763464ebd5Sriastradh 773464ebd5Sriastradh screen->fence.emit(&screen->base, &fence->sequence); 783464ebd5Sriastradh 793464ebd5Sriastradh assert(fence->state == NOUVEAU_FENCE_STATE_EMITTING); 803464ebd5Sriastradh fence->state = NOUVEAU_FENCE_STATE_EMITTED; 813464ebd5Sriastradh} 823464ebd5Sriastradh 833464ebd5Sriastradhvoid 843464ebd5Sriastradhnouveau_fence_del(struct nouveau_fence *fence) 853464ebd5Sriastradh{ 863464ebd5Sriastradh struct nouveau_fence *it; 873464ebd5Sriastradh struct nouveau_screen *screen = fence->screen; 883464ebd5Sriastradh 893464ebd5Sriastradh if (fence->state == NOUVEAU_FENCE_STATE_EMITTED || 903464ebd5Sriastradh fence->state == NOUVEAU_FENCE_STATE_FLUSHED) { 913464ebd5Sriastradh if (fence == screen->fence.head) { 923464ebd5Sriastradh screen->fence.head = fence->next; 933464ebd5Sriastradh if (!screen->fence.head) 943464ebd5Sriastradh screen->fence.tail = NULL; 953464ebd5Sriastradh } else { 963464ebd5Sriastradh for (it = screen->fence.head; it && it->next != fence; it = it->next); 973464ebd5Sriastradh it->next = fence->next; 983464ebd5Sriastradh if (screen->fence.tail == fence) 993464ebd5Sriastradh screen->fence.tail = it; 1003464ebd5Sriastradh } 1013464ebd5Sriastradh } 1023464ebd5Sriastradh 1037ec681f3Smrg if (!list_is_empty(&fence->work)) { 1043464ebd5Sriastradh debug_printf("WARNING: deleting fence with work still pending !\n"); 1053464ebd5Sriastradh nouveau_fence_trigger_work(fence); 1063464ebd5Sriastradh } 1073464ebd5Sriastradh 1083464ebd5Sriastradh FREE(fence); 1093464ebd5Sriastradh} 1103464ebd5Sriastradh 1117ec681f3Smrgvoid 1127ec681f3Smrgnouveau_fence_cleanup(struct nouveau_screen *screen) 1137ec681f3Smrg{ 1147ec681f3Smrg if (screen->fence.current) { 1157ec681f3Smrg struct nouveau_fence *current = NULL; 1167ec681f3Smrg 1177ec681f3Smrg /* nouveau_fence_wait will create a new current fence, so wait on the 1187ec681f3Smrg * _current_ one, and remove both. 1197ec681f3Smrg */ 1207ec681f3Smrg nouveau_fence_ref(screen->fence.current, ¤t); 1217ec681f3Smrg nouveau_fence_wait(current, NULL); 1227ec681f3Smrg nouveau_fence_ref(NULL, ¤t); 1237ec681f3Smrg nouveau_fence_ref(NULL, &screen->fence.current); 1247ec681f3Smrg } 1257ec681f3Smrg} 1267ec681f3Smrg 1273464ebd5Sriastradhvoid 12801e04c3fSmrgnouveau_fence_update(struct nouveau_screen *screen, bool flushed) 1293464ebd5Sriastradh{ 1303464ebd5Sriastradh struct nouveau_fence *fence; 1313464ebd5Sriastradh struct nouveau_fence *next = NULL; 1323464ebd5Sriastradh u32 sequence = screen->fence.update(&screen->base); 1333464ebd5Sriastradh 1343464ebd5Sriastradh if (screen->fence.sequence_ack == sequence) 1353464ebd5Sriastradh return; 1363464ebd5Sriastradh screen->fence.sequence_ack = sequence; 1373464ebd5Sriastradh 1383464ebd5Sriastradh for (fence = screen->fence.head; fence; fence = next) { 1393464ebd5Sriastradh next = fence->next; 1403464ebd5Sriastradh sequence = fence->sequence; 1413464ebd5Sriastradh 1423464ebd5Sriastradh fence->state = NOUVEAU_FENCE_STATE_SIGNALLED; 1433464ebd5Sriastradh 1443464ebd5Sriastradh nouveau_fence_trigger_work(fence); 1453464ebd5Sriastradh nouveau_fence_ref(NULL, &fence); 1463464ebd5Sriastradh 1473464ebd5Sriastradh if (sequence == screen->fence.sequence_ack) 1483464ebd5Sriastradh break; 1493464ebd5Sriastradh } 1503464ebd5Sriastradh screen->fence.head = next; 1513464ebd5Sriastradh if (!next) 1523464ebd5Sriastradh screen->fence.tail = NULL; 1533464ebd5Sriastradh 1543464ebd5Sriastradh if (flushed) { 1553464ebd5Sriastradh for (fence = next; fence; fence = fence->next) 1563464ebd5Sriastradh if (fence->state == NOUVEAU_FENCE_STATE_EMITTED) 1573464ebd5Sriastradh fence->state = NOUVEAU_FENCE_STATE_FLUSHED; 1583464ebd5Sriastradh } 1593464ebd5Sriastradh} 1603464ebd5Sriastradh 1613464ebd5Sriastradh#define NOUVEAU_FENCE_MAX_SPINS (1 << 31) 1623464ebd5Sriastradh 16301e04c3fSmrgbool 1643464ebd5Sriastradhnouveau_fence_signalled(struct nouveau_fence *fence) 1653464ebd5Sriastradh{ 1663464ebd5Sriastradh struct nouveau_screen *screen = fence->screen; 1673464ebd5Sriastradh 168af69d88dSmrg if (fence->state == NOUVEAU_FENCE_STATE_SIGNALLED) 16901e04c3fSmrg return true; 170af69d88dSmrg 1713464ebd5Sriastradh if (fence->state >= NOUVEAU_FENCE_STATE_EMITTED) 17201e04c3fSmrg nouveau_fence_update(screen, false); 1733464ebd5Sriastradh 1743464ebd5Sriastradh return fence->state == NOUVEAU_FENCE_STATE_SIGNALLED; 1753464ebd5Sriastradh} 1763464ebd5Sriastradh 17701e04c3fSmrgstatic bool 17801e04c3fSmrgnouveau_fence_kick(struct nouveau_fence *fence) 1793464ebd5Sriastradh{ 1803464ebd5Sriastradh struct nouveau_screen *screen = fence->screen; 1813464ebd5Sriastradh 1823464ebd5Sriastradh /* wtf, someone is waiting on a fence in flush_notify handler? */ 1833464ebd5Sriastradh assert(fence->state != NOUVEAU_FENCE_STATE_EMITTING); 1843464ebd5Sriastradh 18501e04c3fSmrg if (fence->state < NOUVEAU_FENCE_STATE_EMITTED) { 18601e04c3fSmrg PUSH_SPACE(screen->pushbuf, 8); 18701e04c3fSmrg /* The space allocation might trigger a flush, which could emit the 18801e04c3fSmrg * current fence. So check again. 18901e04c3fSmrg */ 19001e04c3fSmrg if (fence->state < NOUVEAU_FENCE_STATE_EMITTED) 19101e04c3fSmrg nouveau_fence_emit(fence); 19201e04c3fSmrg } 1933464ebd5Sriastradh 1943464ebd5Sriastradh if (fence->state < NOUVEAU_FENCE_STATE_FLUSHED) 195af69d88dSmrg if (nouveau_pushbuf_kick(screen->pushbuf, screen->pushbuf->channel)) 19601e04c3fSmrg return false; 197af69d88dSmrg 198af69d88dSmrg if (fence == screen->fence.current) 199af69d88dSmrg nouveau_fence_next(screen); 2003464ebd5Sriastradh 20101e04c3fSmrg nouveau_fence_update(screen, false); 20201e04c3fSmrg 20301e04c3fSmrg return true; 20401e04c3fSmrg} 20501e04c3fSmrg 20601e04c3fSmrgbool 20701e04c3fSmrgnouveau_fence_wait(struct nouveau_fence *fence, struct pipe_debug_callback *debug) 20801e04c3fSmrg{ 20901e04c3fSmrg struct nouveau_screen *screen = fence->screen; 21001e04c3fSmrg uint32_t spins = 0; 21101e04c3fSmrg int64_t start = 0; 2123464ebd5Sriastradh 21301e04c3fSmrg if (debug && debug->debug_message) 21401e04c3fSmrg start = os_time_get_nano(); 21501e04c3fSmrg 21601e04c3fSmrg if (!nouveau_fence_kick(fence)) 21701e04c3fSmrg return false; 21801e04c3fSmrg 21901e04c3fSmrg do { 22001e04c3fSmrg if (fence->state == NOUVEAU_FENCE_STATE_SIGNALLED) { 22101e04c3fSmrg if (debug && debug->debug_message) 22201e04c3fSmrg pipe_debug_message(debug, PERF_INFO, 22301e04c3fSmrg "stalled %.3f ms waiting for fence", 22401e04c3fSmrg (os_time_get_nano() - start) / 1000000.f); 22501e04c3fSmrg return true; 22601e04c3fSmrg } 227af69d88dSmrg if (!spins) 228af69d88dSmrg NOUVEAU_DRV_STAT(screen, any_non_kernel_fence_sync_count, 1); 2293464ebd5Sriastradh spins++; 2303464ebd5Sriastradh#ifdef PIPE_OS_UNIX 2313464ebd5Sriastradh if (!(spins % 8)) /* donate a few cycles */ 2323464ebd5Sriastradh sched_yield(); 2333464ebd5Sriastradh#endif 23401e04c3fSmrg 23501e04c3fSmrg nouveau_fence_update(screen, false); 2363464ebd5Sriastradh } while (spins < NOUVEAU_FENCE_MAX_SPINS); 2373464ebd5Sriastradh 2383464ebd5Sriastradh debug_printf("Wait on fence %u (ack = %u, next = %u) timed out !\n", 2393464ebd5Sriastradh fence->sequence, 2403464ebd5Sriastradh screen->fence.sequence_ack, screen->fence.sequence); 2413464ebd5Sriastradh 24201e04c3fSmrg return false; 2433464ebd5Sriastradh} 2443464ebd5Sriastradh 2453464ebd5Sriastradhvoid 2463464ebd5Sriastradhnouveau_fence_next(struct nouveau_screen *screen) 2473464ebd5Sriastradh{ 24801e04c3fSmrg if (screen->fence.current->state < NOUVEAU_FENCE_STATE_EMITTING) { 24901e04c3fSmrg if (screen->fence.current->ref > 1) 25001e04c3fSmrg nouveau_fence_emit(screen->fence.current); 25101e04c3fSmrg else 25201e04c3fSmrg return; 25301e04c3fSmrg } 2543464ebd5Sriastradh 2553464ebd5Sriastradh nouveau_fence_ref(NULL, &screen->fence.current); 2563464ebd5Sriastradh 25701e04c3fSmrg nouveau_fence_new(screen, &screen->fence.current); 25801e04c3fSmrg} 25901e04c3fSmrg 26001e04c3fSmrgvoid 26101e04c3fSmrgnouveau_fence_unref_bo(void *data) 26201e04c3fSmrg{ 26301e04c3fSmrg struct nouveau_bo *bo = data; 26401e04c3fSmrg 26501e04c3fSmrg nouveau_bo_ref(NULL, &bo); 26601e04c3fSmrg} 26701e04c3fSmrg 26801e04c3fSmrgbool 26901e04c3fSmrgnouveau_fence_work(struct nouveau_fence *fence, 27001e04c3fSmrg void (*func)(void *), void *data) 27101e04c3fSmrg{ 27201e04c3fSmrg struct nouveau_fence_work *work; 27301e04c3fSmrg 27401e04c3fSmrg if (!fence || fence->state == NOUVEAU_FENCE_STATE_SIGNALLED) { 27501e04c3fSmrg func(data); 27601e04c3fSmrg return true; 27701e04c3fSmrg } 27801e04c3fSmrg 27901e04c3fSmrg work = CALLOC_STRUCT(nouveau_fence_work); 28001e04c3fSmrg if (!work) 28101e04c3fSmrg return false; 28201e04c3fSmrg work->func = func; 28301e04c3fSmrg work->data = data; 2847ec681f3Smrg list_add(&work->list, &fence->work); 28501e04c3fSmrg p_atomic_inc(&fence->work_count); 28601e04c3fSmrg if (fence->work_count > 64) 28701e04c3fSmrg nouveau_fence_kick(fence); 28801e04c3fSmrg return true; 2893464ebd5Sriastradh} 290