Path: blob/21.2-virgl/src/gallium/drivers/nouveau/nouveau_fence.c
4570 views
/*1* Copyright 2010 Christoph Bumiller2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*/2122#include "nouveau_screen.h"23#include "nouveau_winsys.h"24#include "nouveau_fence.h"25#include "util/os_time.h"2627#ifdef PIPE_OS_UNIX28#include <sched.h>29#endif3031bool32nouveau_fence_new(struct nouveau_screen *screen, struct nouveau_fence **fence)33{34*fence = CALLOC_STRUCT(nouveau_fence);35if (!*fence)36return false;3738(*fence)->screen = screen;39(*fence)->ref = 1;40list_inithead(&(*fence)->work);4142return true;43}4445static void46nouveau_fence_trigger_work(struct nouveau_fence *fence)47{48struct nouveau_fence_work *work, *tmp;4950LIST_FOR_EACH_ENTRY_SAFE(work, tmp, &fence->work, list) {51work->func(work->data);52list_del(&work->list);53FREE(work);54}55}5657void58nouveau_fence_emit(struct nouveau_fence *fence)59{60struct nouveau_screen *screen = fence->screen;6162assert(fence->state == NOUVEAU_FENCE_STATE_AVAILABLE);6364/* set this now, so that if fence.emit triggers a flush we don't recurse */65fence->state = NOUVEAU_FENCE_STATE_EMITTING;6667++fence->ref;6869if (screen->fence.tail)70screen->fence.tail->next = fence;71else72screen->fence.head = fence;7374screen->fence.tail = fence;7576screen->fence.emit(&screen->base, &fence->sequence);7778assert(fence->state == NOUVEAU_FENCE_STATE_EMITTING);79fence->state = NOUVEAU_FENCE_STATE_EMITTED;80}8182void83nouveau_fence_del(struct nouveau_fence *fence)84{85struct nouveau_fence *it;86struct nouveau_screen *screen = fence->screen;8788if (fence->state == NOUVEAU_FENCE_STATE_EMITTED ||89fence->state == NOUVEAU_FENCE_STATE_FLUSHED) {90if (fence == screen->fence.head) {91screen->fence.head = fence->next;92if (!screen->fence.head)93screen->fence.tail = NULL;94} else {95for (it = screen->fence.head; it && it->next != fence; it = it->next);96it->next = fence->next;97if (screen->fence.tail == fence)98screen->fence.tail = it;99}100}101102if (!list_is_empty(&fence->work)) {103debug_printf("WARNING: deleting fence with work still pending !\n");104nouveau_fence_trigger_work(fence);105}106107FREE(fence);108}109110void111nouveau_fence_cleanup(struct nouveau_screen *screen)112{113if (screen->fence.current) {114struct nouveau_fence *current = NULL;115116/* nouveau_fence_wait will create a new current fence, so wait on the117* _current_ one, and remove both.118*/119nouveau_fence_ref(screen->fence.current, ¤t);120nouveau_fence_wait(current, NULL);121nouveau_fence_ref(NULL, ¤t);122nouveau_fence_ref(NULL, &screen->fence.current);123}124}125126void127nouveau_fence_update(struct nouveau_screen *screen, bool flushed)128{129struct nouveau_fence *fence;130struct nouveau_fence *next = NULL;131u32 sequence = screen->fence.update(&screen->base);132133if (screen->fence.sequence_ack == sequence)134return;135screen->fence.sequence_ack = sequence;136137for (fence = screen->fence.head; fence; fence = next) {138next = fence->next;139sequence = fence->sequence;140141fence->state = NOUVEAU_FENCE_STATE_SIGNALLED;142143nouveau_fence_trigger_work(fence);144nouveau_fence_ref(NULL, &fence);145146if (sequence == screen->fence.sequence_ack)147break;148}149screen->fence.head = next;150if (!next)151screen->fence.tail = NULL;152153if (flushed) {154for (fence = next; fence; fence = fence->next)155if (fence->state == NOUVEAU_FENCE_STATE_EMITTED)156fence->state = NOUVEAU_FENCE_STATE_FLUSHED;157}158}159160#define NOUVEAU_FENCE_MAX_SPINS (1 << 31)161162bool163nouveau_fence_signalled(struct nouveau_fence *fence)164{165struct nouveau_screen *screen = fence->screen;166167if (fence->state == NOUVEAU_FENCE_STATE_SIGNALLED)168return true;169170if (fence->state >= NOUVEAU_FENCE_STATE_EMITTED)171nouveau_fence_update(screen, false);172173return fence->state == NOUVEAU_FENCE_STATE_SIGNALLED;174}175176static bool177nouveau_fence_kick(struct nouveau_fence *fence)178{179struct nouveau_screen *screen = fence->screen;180181/* wtf, someone is waiting on a fence in flush_notify handler? */182assert(fence->state != NOUVEAU_FENCE_STATE_EMITTING);183184if (fence->state < NOUVEAU_FENCE_STATE_EMITTED) {185PUSH_SPACE(screen->pushbuf, 8);186/* The space allocation might trigger a flush, which could emit the187* current fence. So check again.188*/189if (fence->state < NOUVEAU_FENCE_STATE_EMITTED)190nouveau_fence_emit(fence);191}192193if (fence->state < NOUVEAU_FENCE_STATE_FLUSHED)194if (nouveau_pushbuf_kick(screen->pushbuf, screen->pushbuf->channel))195return false;196197if (fence == screen->fence.current)198nouveau_fence_next(screen);199200nouveau_fence_update(screen, false);201202return true;203}204205bool206nouveau_fence_wait(struct nouveau_fence *fence, struct pipe_debug_callback *debug)207{208struct nouveau_screen *screen = fence->screen;209uint32_t spins = 0;210int64_t start = 0;211212if (debug && debug->debug_message)213start = os_time_get_nano();214215if (!nouveau_fence_kick(fence))216return false;217218do {219if (fence->state == NOUVEAU_FENCE_STATE_SIGNALLED) {220if (debug && debug->debug_message)221pipe_debug_message(debug, PERF_INFO,222"stalled %.3f ms waiting for fence",223(os_time_get_nano() - start) / 1000000.f);224return true;225}226if (!spins)227NOUVEAU_DRV_STAT(screen, any_non_kernel_fence_sync_count, 1);228spins++;229#ifdef PIPE_OS_UNIX230if (!(spins % 8)) /* donate a few cycles */231sched_yield();232#endif233234nouveau_fence_update(screen, false);235} while (spins < NOUVEAU_FENCE_MAX_SPINS);236237debug_printf("Wait on fence %u (ack = %u, next = %u) timed out !\n",238fence->sequence,239screen->fence.sequence_ack, screen->fence.sequence);240241return false;242}243244void245nouveau_fence_next(struct nouveau_screen *screen)246{247if (screen->fence.current->state < NOUVEAU_FENCE_STATE_EMITTING) {248if (screen->fence.current->ref > 1)249nouveau_fence_emit(screen->fence.current);250else251return;252}253254nouveau_fence_ref(NULL, &screen->fence.current);255256nouveau_fence_new(screen, &screen->fence.current);257}258259void260nouveau_fence_unref_bo(void *data)261{262struct nouveau_bo *bo = data;263264nouveau_bo_ref(NULL, &bo);265}266267bool268nouveau_fence_work(struct nouveau_fence *fence,269void (*func)(void *), void *data)270{271struct nouveau_fence_work *work;272273if (!fence || fence->state == NOUVEAU_FENCE_STATE_SIGNALLED) {274func(data);275return true;276}277278work = CALLOC_STRUCT(nouveau_fence_work);279if (!work)280return false;281work->func = func;282work->data = data;283list_add(&work->list, &fence->work);284p_atomic_inc(&fence->work_count);285if (fence->work_count > 64)286nouveau_fence_kick(fence);287return true;288}289290291