Merge commit 'origin/gallium-master-merge'
[mesa.git] / src / gallium / drivers / cell / ppu / cell_batch.c
1 /**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28
29 #include "cell_context.h"
30 #include "cell_batch.h"
31 #include "cell_fence.h"
32 #include "cell_spu.h"
33
34
35
36 /**
37 * Search the buffer pool for an empty/free buffer and return its index.
38 * Buffers are used for storing vertex data, state and commands which
39 * will be sent to the SPUs.
40 * If no empty buffers are available, wait for one.
41 * \return buffer index in [0, CELL_NUM_BUFFERS-1]
42 */
43 uint
44 cell_get_empty_buffer(struct cell_context *cell)
45 {
46 static uint prev_buffer = 0;
47 uint buf = (prev_buffer + 1) % CELL_NUM_BUFFERS;
48 uint tries = 0;
49
50 /* Find a buffer that's marked as free by all SPUs */
51 while (1) {
52 uint spu, num_free = 0;
53
54 for (spu = 0; spu < cell->num_spus; spu++) {
55 if (cell->buffer_status[spu][buf][0] == CELL_BUFFER_STATUS_FREE) {
56 num_free++;
57
58 if (num_free == cell->num_spus) {
59 /* found a free buffer, now mark status as used */
60 for (spu = 0; spu < cell->num_spus; spu++) {
61 cell->buffer_status[spu][buf][0] = CELL_BUFFER_STATUS_USED;
62 }
63 /*
64 printf("PPU: ALLOC BUFFER %u, %u tries\n", buf, tries);
65 */
66 prev_buffer = buf;
67
68 /* release tex buffer associated w/ prev use of this batch buf */
69 cell_free_fenced_buffers(cell, &cell->fenced_buffers[buf]);
70
71 return buf;
72 }
73 }
74 else {
75 break;
76 }
77 }
78
79 /* try next buf */
80 buf = (buf + 1) % CELL_NUM_BUFFERS;
81
82 tries++;
83 if (tries == 100) {
84 /*
85 printf("PPU WAITING for buffer...\n");
86 */
87 }
88 }
89 }
90
91
92 /**
93 * Append a fence command to the current batch buffer.
94 * Note that we're sure there's always room for this because of the
95 * adjusted size check in cell_batch_free_space().
96 */
97 static void
98 emit_fence(struct cell_context *cell)
99 {
100 const uint batch = cell->cur_batch;
101 const uint size = cell->buffer_size[batch];
102 struct cell_command_fence *fence_cmd;
103 struct cell_fence *fence = &cell->fenced_buffers[batch].fence;
104 uint i;
105
106 /* set fence status to emitted, not yet signalled */
107 for (i = 0; i < cell->num_spus; i++) {
108 fence->status[i][0] = CELL_FENCE_EMITTED;
109 }
110
111 STATIC_ASSERT(sizeof(struct cell_command_fence) % 16 == 0);
112 ASSERT(size % 16 == 0);
113 ASSERT(size + sizeof(struct cell_command_fence) <= CELL_BUFFER_SIZE);
114
115 fence_cmd = (struct cell_command_fence *) (cell->buffer[batch] + size);
116 fence_cmd->opcode[0] = CELL_CMD_FENCE;
117 fence_cmd->fence = fence;
118
119 /* update batch buffer size */
120 cell->buffer_size[batch] = size + sizeof(struct cell_command_fence);
121 }
122
123
124 /**
125 * Flush the current batch buffer to the SPUs.
126 * An empty buffer will be found and set as the new current batch buffer
127 * for subsequent commands/data.
128 */
129 void
130 cell_batch_flush(struct cell_context *cell)
131 {
132 static boolean flushing = FALSE;
133 uint batch = cell->cur_batch;
134 uint size = cell->buffer_size[batch];
135 uint spu, cmd_word;
136
137 assert(!flushing);
138
139 if (size == 0)
140 return;
141
142 /* Before we use this batch buffer, make sure any fenced texture buffers
143 * are released.
144 */
145 if (cell->fenced_buffers[batch].head) {
146 emit_fence(cell);
147 size = cell->buffer_size[batch];
148 }
149
150 flushing = TRUE;
151
152 assert(batch < CELL_NUM_BUFFERS);
153
154 /*
155 printf("cell_batch_dispatch: buf %u at %p, size %u\n",
156 batch, &cell->buffer[batch][0], size);
157 */
158
159 /*
160 * Build "BATCH" command and send to all SPUs.
161 */
162 cmd_word = CELL_CMD_BATCH | (batch << 8) | (size << 16);
163
164 for (spu = 0; spu < cell->num_spus; spu++) {
165 assert(cell->buffer_status[spu][batch][0] == CELL_BUFFER_STATUS_USED);
166 send_mbox_message(cell_global.spe_contexts[spu], cmd_word);
167 }
168
169 /* When the SPUs are done copying the buffer into their locals stores
170 * they'll write a BUFFER_STATUS_FREE message into the buffer_status[]
171 * array indicating that the PPU can re-use the buffer.
172 */
173
174 batch = cell_get_empty_buffer(cell);
175
176 cell->buffer_size[batch] = 0; /* empty */
177 cell->cur_batch = batch;
178
179 flushing = FALSE;
180 }
181
182
183 /**
184 * Return the number of bytes free in the current batch buffer.
185 */
186 uint
187 cell_batch_free_space(const struct cell_context *cell)
188 {
189 uint free = CELL_BUFFER_SIZE - cell->buffer_size[cell->cur_batch];
190 free -= sizeof(struct cell_command_fence);
191 return free;
192 }
193
194
195 /**
196 * Allocate space in the current batch buffer for 'bytes' space.
197 * Bytes must be a multiple of 16 bytes. Allocation will be 16 byte aligned.
198 * \return address in batch buffer to put data
199 */
200 void *
201 cell_batch_alloc16(struct cell_context *cell, uint bytes)
202 {
203 void *pos;
204 uint size;
205
206 ASSERT(bytes % 16 == 0);
207 ASSERT(bytes <= CELL_BUFFER_SIZE);
208 ASSERT(cell->cur_batch >= 0);
209
210 #ifdef ASSERT
211 {
212 uint spu;
213 for (spu = 0; spu < cell->num_spus; spu++) {
214 ASSERT(cell->buffer_status[spu][cell->cur_batch][0]
215 == CELL_BUFFER_STATUS_USED);
216 }
217 }
218 #endif
219
220 size = cell->buffer_size[cell->cur_batch];
221
222 if (bytes > cell_batch_free_space(cell)) {
223 cell_batch_flush(cell);
224 size = 0;
225 }
226
227 ASSERT(size % 16 == 0);
228 ASSERT(size + bytes <= CELL_BUFFER_SIZE);
229
230 pos = (void *) (cell->buffer[cell->cur_batch] + size);
231
232 cell->buffer_size[cell->cur_batch] = size + bytes;
233
234 return pos;
235 }
236
237
238 /**
239 * One-time init of batch buffers.
240 */
241 void
242 cell_init_batch_buffers(struct cell_context *cell)
243 {
244 uint spu, buf;
245
246 /* init command, vertex/index buffer info */
247 for (buf = 0; buf < CELL_NUM_BUFFERS; buf++) {
248 cell->buffer_size[buf] = 0;
249
250 /* init batch buffer status values,
251 * mark 0th buffer as used, rest as free.
252 */
253 for (spu = 0; spu < cell->num_spus; spu++) {
254 if (buf == 0)
255 cell->buffer_status[spu][buf][0] = CELL_BUFFER_STATUS_USED;
256 else
257 cell->buffer_status[spu][buf][0] = CELL_BUFFER_STATUS_FREE;
258 }
259 }
260 }