Tizen 2.0 Release
[profile/ivi/osmesa.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 }