1 #include "n64video.h"
2 #include "common.h"
3 #include "msg.h"
4 #include "vdac.h"
5 #include "parallel_al.h"
6 
7 #include <memory.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 
12 #ifdef HAVE_RDP_DUMP
13 #include "rdp_dump.h"
14 #endif
15 
16 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
17 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
18 #define CLAMP(x, lo, hi) (((x) > (hi)) ? (hi) : (((x) < (lo)) ? (lo) : (x)))
19 
20 #define SIGN16(x)   ((int16_t)(x))
21 #define SIGN8(x)    ((int8_t)(x))
22 
23 #define SIGN(x, numb)	(((x) & ((1 << (numb)) - 1)) | -((x) & (1 << ((numb) - 1))))
24 #define SIGNF(x, numb)	((x) | -((x) & (1 << ((numb) - 1))))
25 
26 #define TRELATIVE(x, y)     ((x) - ((y) << 3))
27 
28 #define PIXELS_TO_BYTES(pix, siz) (((pix) << (siz)) >> 1)
29 
30 // RGBA5551 to RGBA8888 helper
31 #define RGBA16_R(x) (((x) >> 8) & 0xf8)
32 #define RGBA16_G(x) (((x) & 0x7c0) >> 3)
33 #define RGBA16_B(x) (((x) & 0x3e) << 2)
34 
35 // RGBA8888 helper
36 #define RGBA32_R(x) (((x) >> 24) & 0xff)
37 #define RGBA32_G(x) (((x) >> 16) & 0xff)
38 #define RGBA32_B(x) (((x) >> 8) & 0xff)
39 #define RGBA32_A(x) ((x) & 0xff)
40 
41 // maximum number of commands to buffer for parallel processing
42 #define CMD_BUFFER_SIZE 1024
43 
44 // maximum data size of a single command in bytes
45 #define CMD_MAX_SIZE 176
46 
47 // maximum data size of a single command in 32 bit integers
48 #define CMD_MAX_INTS (CMD_MAX_SIZE / sizeof(int32_t))
49 
50 // extracts the command ID from a command buffer
51 #define CMD_ID(cmd) ((*(cmd) >> 24) & 0x3f)
52 
53 // list of command IDs
54 #define CMD_ID_NO_OP                           0x00
55 #define CMD_ID_FILL_TRIANGLE                   0x08
56 #define CMD_ID_FILL_ZBUFFER_TRIANGLE           0x09
57 #define CMD_ID_TEXTURE_TRIANGLE                0x0a
58 #define CMD_ID_TEXTURE_ZBUFFER_TRIANGLE        0x0b
59 #define CMD_ID_SHADE_TRIANGLE                  0x0c
60 #define CMD_ID_SHADE_ZBUFFER_TRIANGLE          0x0d
61 #define CMD_ID_SHADE_TEXTURE_TRIANGLE          0x0e
62 #define CMD_ID_SHADE_TEXTURE_Z_BUFFER_TRIANGLE 0x0f
63 #define CMD_ID_TEXTURE_RECTANGLE               0x24
64 #define CMD_ID_TEXTURE_RECTANGLE_FLIP          0x25
65 #define CMD_ID_SYNC_LOAD                       0x26
66 #define CMD_ID_SYNC_PIPE                       0x27
67 #define CMD_ID_SYNC_TILE                       0x28
68 #define CMD_ID_SYNC_FULL                       0x29
69 #define CMD_ID_SET_KEY_GB                      0x2a
70 #define CMD_ID_SET_KEY_R                       0x2b
71 #define CMD_ID_SET_CONVERT                     0x2c
72 #define CMD_ID_SET_SCISSOR                     0x2d
73 #define CMD_ID_SET_PRIM_DEPTH                  0x2e
74 #define CMD_ID_SET_OTHER_MODES                 0x2f
75 #define CMD_ID_LOAD_TLUT                       0x30
76 #define CMD_ID_SET_TILE_SIZE                   0x32
77 #define CMD_ID_LOAD_BLOCK                      0x33
78 #define CMD_ID_LOAD_TILE                       0x34
79 #define CMD_ID_SET_TILE                        0x35
80 #define CMD_ID_FILL_RECTANGLE                  0x36
81 #define CMD_ID_SET_FILL_COLOR                  0x37
82 #define CMD_ID_SET_FOG_COLOR                   0x38
83 #define CMD_ID_SET_BLEND_COLOR                 0x39
84 #define CMD_ID_SET_PRIM_COLOR                  0x3a
85 #define CMD_ID_SET_ENV_COLOR                   0x3b
86 #define CMD_ID_SET_COMBINE                     0x3c
87 #define CMD_ID_SET_TEXTURE_IMAGE               0x3d
88 #define CMD_ID_SET_MASK_IMAGE                  0x3e
89 #define CMD_ID_SET_COLOR_IMAGE                 0x3f
90 
91 static struct n64video_config config;
92 
93 static struct
94 {
95     bool fillmbitcrashes, vbusclock, nolerp;
96 } onetimewarnings;
97 
98 static int rdp_pipeline_crashed = 0;
99 
clamp(int32_t value,int32_t min,int32_t max)100 static STRICTINLINE int32_t clamp(int32_t value, int32_t min, int32_t max)
101 {
102     if (value < min)
103         return min;
104     else if (value > max)
105         return max;
106     return value;
107 }
108 
irand(uint32_t * state)109 static STRICTINLINE uint32_t irand(uint32_t* state)
110 {
111     *state = *state * 0x343fd + 0x269ec3;
112     return ((*state >> 16) & 0x7fff);
113 }
114 
115 #include "n64video/rdp.c"
116 #include "n64video/vi.c"
117 
118 static uint32_t rdp_cmd_buf[CMD_BUFFER_SIZE][CMD_MAX_INTS];
119 static uint32_t rdp_cmd_buf_pos;
120 
121 static uint32_t rdp_cmd_pos;
122 static uint32_t rdp_cmd_id;
123 static uint32_t rdp_cmd_len;
124 
125 // table of commands that require thread synchronization in
126 // multithreaded mode
127 static bool rdp_cmd_sync[64];
128 
cmd_run_buffered(uint32_t worker_id)129 static void cmd_run_buffered(uint32_t worker_id)
130 {
131     uint32_t pos;
132     for (pos = 0; pos < rdp_cmd_buf_pos; pos++)
133         rdp_cmd(worker_id, rdp_cmd_buf[pos]);
134 }
135 
cmd_flush(void)136 static void cmd_flush(void)
137 {
138     // only run if there's something buffered
139     if (rdp_cmd_buf_pos) {
140         // let workers run all buffered commands in parallel
141         parallel_run(cmd_run_buffered);
142         // reset buffer by starting from the beginning
143         rdp_cmd_buf_pos = 0;
144     }
145 }
146 
cmd_init(void)147 static void cmd_init(void)
148 {
149     rdp_cmd_pos = 0;
150     rdp_cmd_id = 0;
151     rdp_cmd_len = CMD_MAX_INTS;
152 }
153 
n64video_config_init(struct n64video_config * config)154 void n64video_config_init(struct n64video_config* config)
155 {
156     memset(config, 0, sizeof(*config));
157 
158     // config defaults that aren't false or 0
159     config->parallel = true;
160     config->vi.vsync = true;
161     config->dp.compat = DP_COMPAT_MEDIUM;
162 }
163 
rdp_init_worker(uint32_t worker_id)164 void rdp_init_worker(uint32_t worker_id)
165 {
166     rdp_init(worker_id, parallel_num_workers());
167 }
168 
169 #ifdef HAVE_RDP_DUMP
170 static bool rdp_dump_in_command_list;
171 #endif
172 
n64video_init(struct n64video_config * _config)173 void n64video_init(struct n64video_config* _config)
174 {
175     if (_config)
176         config = *_config;
177 
178     // initialize static lookup tables and RDP state, once is enough
179     static bool static_init;
180     if (!static_init)
181     {
182         blender_init_lut();
183         coverage_init_lut();
184         combiner_init_lut();
185         tex_init_lut();
186         z_init_lut();
187 
188         fb_init(0);
189         combiner_init(0);
190         tex_init(0);
191         rasterizer_init(0);
192 
193         static_init = true;
194     }
195 
196 #ifdef HAVE_RDP_DUMP
197     const char *rdp_dump_path = getenv("RDP_DUMP");
198     if (rdp_dump_path)
199     {
200         rdp_dump_init(rdp_dump_path, config.gfx.rdram_size, sizeof(rdram_hidden));
201         // Force no MT when dumping for sanity.
202         config.parallel = false;
203     }
204     rdp_dump_in_command_list = false;
205 #endif
206 
207     // enable sync switches depending on compatibility mode
208     memset(rdp_cmd_sync, 0, sizeof(rdp_cmd_sync));
209     switch (config.dp.compat) {
210         case DP_COMPAT_HIGH:
211             rdp_cmd_sync[CMD_ID_SET_TEXTURE_IMAGE] = true;
212         case DP_COMPAT_MEDIUM:
213             rdp_cmd_sync[CMD_ID_SET_MASK_IMAGE] = true;
214             rdp_cmd_sync[CMD_ID_SET_COLOR_IMAGE] = true;
215         case DP_COMPAT_LOW:
216             rdp_cmd_sync[CMD_ID_SYNC_FULL] = true;
217     }
218 
219     // init internals
220     rdram_init();
221     vi_init();
222     cmd_init();
223 
224     rdp_pipeline_crashed = 0;
225     memset(&onetimewarnings, 0, sizeof(onetimewarnings));
226 
227     if (config.parallel)
228     {
229        uint32_t i;
230        // init worker system
231        parallel_alinit(config.num_workers);
232 
233        // sync states from main worker
234        for (i = 1; i < parallel_num_workers(); i++)
235           memcpy(&state[i], &state[0], sizeof(struct rdp_state));
236 
237        // init workers
238        parallel_run(rdp_init_worker);
239     }
240     else
241         rdp_init(0, 1);
242 }
243 
n64video_process_list(void)244 void n64video_process_list(void)
245 {
246     uint32_t** dp_reg = config.gfx.dp_reg;
247     uint32_t dp_current_al = (*dp_reg[DP_CURRENT] & ~7) >> 2;
248     uint32_t dp_end_al = (*dp_reg[DP_END] & ~7) >> 2;
249 
250     // don't do anything if the RDP has crashed or the registers are not set up correctly
251     if (rdp_pipeline_crashed || dp_end_al <= dp_current_al) {
252         return;
253     }
254 
255     // while there's data in the command buffer...
256     while (dp_end_al - dp_current_al > 0) {
257         uint32_t i, toload;
258         bool xbus_dma = (*dp_reg[DP_STATUS] & DP_STATUS_XBUS_DMA) != 0;
259         uint32_t* dmem = (uint32_t*)config.gfx.dmem;
260         uint32_t* cmd_buf = rdp_cmd_buf[rdp_cmd_buf_pos];
261 
262         // when reading the first int, extract the command ID and update the buffer length
263         if (rdp_cmd_pos == 0) {
264             if (xbus_dma) {
265                 cmd_buf[rdp_cmd_pos++] = dmem[dp_current_al++ & 0x3ff];
266             } else {
267                 cmd_buf[rdp_cmd_pos++] = rdram_read_idx32(dp_current_al++);
268             }
269 
270             rdp_cmd_id = CMD_ID(cmd_buf);
271             rdp_cmd_len = rdp_commands[rdp_cmd_id].length >> 2;
272         }
273 
274         // copy more data from the N64 to the local command buffer
275         toload = MIN(dp_end_al - dp_current_al, rdp_cmd_len - 1);
276 
277         if (xbus_dma) {
278             for (i = 0; i < toload; i++) {
279                 cmd_buf[rdp_cmd_pos++] = dmem[dp_current_al++ & 0x3ff];
280             }
281         } else {
282             for (i = 0; i < toload; i++) {
283                 cmd_buf[rdp_cmd_pos++] = rdram_read_idx32(dp_current_al++);
284             }
285         }
286 
287         // if there's enough data for the current command...
288         if (rdp_cmd_pos == rdp_cmd_len) {
289 
290 #ifdef HAVE_RDP_DUMP
291             if (!rdp_dump_in_command_list)
292             {
293                 rdp_dump_flush_dram(config.gfx.rdram, config.gfx.rdram_size);
294                 rdp_dump_flush_hidden_dram(rdram_hidden, sizeof(rdram_hidden));
295                 rdp_dump_in_command_list = true;
296             }
297 
298             if (rdp_cmd_id == CMD_ID_SYNC_FULL)
299             {
300                 rdp_dump_signal_complete();
301                 rdp_dump_in_command_list = false;
302             }
303             else
304             {
305                 rdp_dump_emit_command(rdp_cmd_id, cmd_buf, rdp_cmd_len);
306             }
307 #endif
308 
309             // check if parallel processing is enabled
310             if (config.parallel) {
311                 // special case: sync_full always needs to be run in main thread
312                 if (rdp_cmd_id == CMD_ID_SYNC_FULL) {
313                     // first, run all pending commands
314                     cmd_flush();
315 
316                     // parameters are unused, so NULL is fine
317                     rdp_sync_full(0, NULL);
318                 } else {
319                     // increment buffer position
320                     rdp_cmd_buf_pos++;
321 
322                     // flush buffer when it is full or when the current command requires a sync
323                     if (rdp_cmd_buf_pos >= CMD_BUFFER_SIZE || rdp_cmd_sync[rdp_cmd_id]) {
324                         cmd_flush();
325                     }
326                 }
327             } else {
328                 // run command directly
329                 rdp_cmd(0, cmd_buf);
330             }
331 
332             // send Z-buffer address to VI for "depth" output mode
333             if (rdp_cmd_id == CMD_ID_SET_MASK_IMAGE) {
334                 vi_set_zbuffer_address(cmd_buf[1] & 0x0ffffff);
335             }
336 
337             // reset current command buffer to prepare for the next one
338             cmd_init();
339         }
340     }
341 
342     // update DP registers to indicate that all bytes have been read
343     *dp_reg[DP_START] = *dp_reg[DP_CURRENT] = *dp_reg[DP_END];
344 }
345 
n64video_close(void)346 void n64video_close(void)
347 {
348 #ifdef HAVE_RDP_DUMP
349     if (rdp_dump_in_command_list)
350         rdp_dump_in_command_list = false;
351     rdp_dump_end();
352 #endif
353 
354     vi_close();
355     parallel_close();
356 }
357