nvc0: delete memory caches and fence on screen destruction
[mesa.git] / src / gallium / drivers / nvc0 / nvc0_fence.c
1 /*
2 * Copyright 2010 Christoph Bumiller
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 #include "nvc0_fence.h"
24 #include "nvc0_context.h"
25 #include "nvc0_screen.h"
26
27 #ifdef PIPE_OS_UNIX
28 #include <sched.h>
29 #endif
30
31 boolean
32 nvc0_screen_fence_new(struct nvc0_screen *screen, struct nvc0_fence **fence,
33 boolean emit)
34 {
35 *fence = CALLOC_STRUCT(nvc0_fence);
36 if (!*fence)
37 return FALSE;
38
39 (*fence)->screen = screen;
40 (*fence)->ref = 1;
41
42 if (emit)
43 nvc0_fence_emit(*fence);
44
45 return TRUE;
46 }
47
48 void
49 nvc0_fence_emit(struct nvc0_fence *fence)
50 {
51 struct nvc0_screen *screen = fence->screen;
52 struct nouveau_channel *chan = screen->base.channel;
53
54 fence->sequence = ++screen->fence.sequence;
55
56 assert(fence->state == NVC0_FENCE_STATE_AVAILABLE);
57
58 BEGIN_RING(chan, RING_3D(QUERY_ADDRESS_HIGH), 4);
59 OUT_RELOCh(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
60 OUT_RELOCl(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
61 OUT_RING (chan, fence->sequence);
62 OUT_RING (chan, NVC0_3D_QUERY_GET_FENCE);
63
64 ++fence->ref;
65
66 if (screen->fence.tail)
67 screen->fence.tail->next = fence;
68 else
69 screen->fence.head = fence;
70
71 screen->fence.tail = fence;
72
73 fence->state = NVC0_FENCE_STATE_EMITTED;
74 }
75
76 static void
77 nvc0_fence_trigger_release_buffers(struct nvc0_fence *fence);
78
79 void
80 nvc0_fence_del(struct nvc0_fence *fence)
81 {
82 struct nvc0_fence *it;
83 struct nvc0_screen *screen = fence->screen;
84
85 if (fence->state == NVC0_FENCE_STATE_EMITTED) {
86 if (fence == screen->fence.head) {
87 screen->fence.head = fence->next;
88 if (!screen->fence.head)
89 screen->fence.tail = NULL;
90 } else {
91 for (it = screen->fence.head; it && it->next != fence; it = it->next);
92 it->next = fence->next;
93 if (screen->fence.tail == fence)
94 screen->fence.tail = it;
95 }
96 }
97
98 if (fence->buffers) {
99 debug_printf("WARNING: deleting fence with buffers "
100 "still hooked to it !\n");
101 nvc0_fence_trigger_release_buffers(fence);
102 }
103
104 FREE(fence);
105 }
106
107 static void
108 nvc0_fence_trigger_release_buffers(struct nvc0_fence *fence)
109 {
110 struct nvc0_mm_allocation *alloc = fence->buffers;
111
112 while (alloc) {
113 struct nvc0_mm_allocation *next = alloc->next;
114 nvc0_mm_free(alloc);
115 alloc = next;
116 };
117 fence->buffers = NULL;
118 }
119
120 static void
121 nvc0_screen_fence_update(struct nvc0_screen *screen)
122 {
123 struct nvc0_fence *fence;
124 struct nvc0_fence *next = NULL;
125 uint32_t sequence = screen->fence.map[0];
126
127 if (screen->fence.sequence_ack == sequence)
128 return;
129 screen->fence.sequence_ack = sequence;
130
131 for (fence = screen->fence.head; fence; fence = next) {
132 next = fence->next;
133 sequence = fence->sequence;
134
135 fence->state = NVC0_FENCE_STATE_SIGNALLED;
136
137 if (fence->buffers)
138 nvc0_fence_trigger_release_buffers(fence);
139
140 nvc0_fence_reference(&fence, NULL);
141
142 if (sequence == screen->fence.sequence_ack)
143 break;
144 }
145 screen->fence.head = next;
146 if (!next)
147 screen->fence.tail = NULL;
148 }
149
150 #define NVC0_FENCE_MAX_SPINS (1 << 17)
151
152 boolean
153 nvc0_fence_signalled(struct nvc0_fence *fence)
154 {
155 struct nvc0_screen *screen = fence->screen;
156
157 if (fence->state == NVC0_FENCE_STATE_EMITTED)
158 nvc0_screen_fence_update(screen);
159
160 return fence->state == NVC0_FENCE_STATE_SIGNALLED;
161 }
162
163 boolean
164 nvc0_fence_wait(struct nvc0_fence *fence)
165 {
166 struct nvc0_screen *screen = fence->screen;
167 int spins = 0;
168
169 if (fence->state == NVC0_FENCE_STATE_AVAILABLE) {
170 nvc0_fence_emit(fence);
171
172 FIRE_RING(screen->base.channel);
173
174 if (fence == screen->fence.current)
175 nvc0_screen_fence_new(screen, &screen->fence.current, FALSE);
176 }
177
178 do {
179 nvc0_screen_fence_update(screen);
180
181 if (fence->state == NVC0_FENCE_STATE_SIGNALLED)
182 return TRUE;
183 spins++;
184 #ifdef PIPE_OS_UNIX
185 if (!(spins % 8)) /* donate a few cycles */
186 sched_yield();
187 #endif
188 } while (spins < NVC0_FENCE_MAX_SPINS);
189
190 if (spins > 9000)
191 NOUVEAU_ERR("fence %x: been spinning too long\n", fence->sequence);
192
193 return FALSE;
194 }
195
196 void
197 nvc0_screen_fence_next(struct nvc0_screen *screen)
198 {
199 nvc0_fence_emit(screen->fence.current);
200 nvc0_screen_fence_new(screen, &screen->fence.current, FALSE);
201 nvc0_screen_fence_update(screen);
202 }