1 /**************************************************************************
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
29 #include "cell_context.h"
30 #include "cell_batch.h"
31 #include "cell_fence.h"
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]
44 cell_get_empty_buffer(struct cell_context
*cell
)
46 static uint prev_buffer
= 0;
47 uint buf
= (prev_buffer
+ 1) % CELL_NUM_BUFFERS
;
50 /* Find a buffer that's marked as free by all SPUs */
52 uint spu
, num_free
= 0;
54 for (spu
= 0; spu
< cell
->num_spus
; spu
++) {
55 if (cell
->buffer_status
[spu
][buf
][0] == CELL_BUFFER_STATUS_FREE
) {
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
;
64 printf("PPU: ALLOC BUFFER %u, %u tries\n", buf, tries);
68 /* release tex buffer associated w/ prev use of this batch buf */
69 cell_free_fenced_buffers(cell
, &cell
->fenced_buffers
[buf
]);
80 buf
= (buf
+ 1) % CELL_NUM_BUFFERS
;
85 printf("PPU WAITING for buffer...\n");
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().
98 emit_fence(struct cell_context
*cell
)
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
;
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
;
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
);
115 fence_cmd
= (struct cell_command_fence
*) (cell
->buffer
[batch
] + size
);
116 fence_cmd
->opcode
[0] = CELL_CMD_FENCE
;
117 fence_cmd
->fence
= fence
;
119 /* update batch buffer size */
120 cell
->buffer_size
[batch
] = size
+ sizeof(struct cell_command_fence
);
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.
130 cell_batch_flush(struct cell_context
*cell
)
132 static boolean flushing
= FALSE
;
133 uint batch
= cell
->cur_batch
;
134 uint size
= cell
->buffer_size
[batch
];
142 /* Before we use this batch buffer, make sure any fenced texture buffers
145 if (cell
->fenced_buffers
[batch
].head
) {
147 size
= cell
->buffer_size
[batch
];
152 assert(batch
< CELL_NUM_BUFFERS
);
155 printf("cell_batch_dispatch: buf %u at %p, size %u\n",
156 batch, &cell->buffer[batch][0], size);
160 * Build "BATCH" command and send to all SPUs.
162 cmd_word
= CELL_CMD_BATCH
| (batch
<< 8) | (size
<< 16);
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
);
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.
174 batch
= cell_get_empty_buffer(cell
);
176 cell
->buffer_size
[batch
] = 0; /* empty */
177 cell
->cur_batch
= batch
;
184 * Return the number of bytes free in the current batch buffer.
187 cell_batch_free_space(const struct cell_context
*cell
)
189 uint free
= CELL_BUFFER_SIZE
- cell
->buffer_size
[cell
->cur_batch
];
190 free
-= sizeof(struct cell_command_fence
);
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
201 cell_batch_alloc16(struct cell_context
*cell
, uint bytes
)
206 ASSERT(bytes
% 16 == 0);
207 ASSERT(bytes
<= CELL_BUFFER_SIZE
);
208 ASSERT(cell
->cur_batch
>= 0);
213 for (spu
= 0; spu
< cell
->num_spus
; spu
++) {
214 ASSERT(cell
->buffer_status
[spu
][cell
->cur_batch
][0]
215 == CELL_BUFFER_STATUS_USED
);
220 size
= cell
->buffer_size
[cell
->cur_batch
];
222 if (bytes
> cell_batch_free_space(cell
)) {
223 cell_batch_flush(cell
);
227 ASSERT(size
% 16 == 0);
228 ASSERT(size
+ bytes
<= CELL_BUFFER_SIZE
);
230 pos
= (void *) (cell
->buffer
[cell
->cur_batch
] + size
);
232 cell
->buffer_size
[cell
->cur_batch
] = size
+ bytes
;
239 * One-time init of batch buffers.
242 cell_init_batch_buffers(struct cell_context
*cell
)
246 /* init command, vertex/index buffer info */
247 for (buf
= 0; buf
< CELL_NUM_BUFFERS
; buf
++) {
248 cell
->buffer_size
[buf
] = 0;
250 /* init batch buffer status values,
251 * mark 0th buffer as used, rest as free.
253 for (spu
= 0; spu
< cell
->num_spus
; spu
++) {
255 cell
->buffer_status
[spu
][buf
][0] = CELL_BUFFER_STATUS_USED
;
257 cell
->buffer_status
[spu
][buf
][0] = CELL_BUFFER_STATUS_FREE
;