experimental thread I/O code, fps counter fix
[picodrive.git] / Pico / cd / buffering.c
CommitLineData
6cadc2da 1// Buffering handling
2// (c) Copyright 2007, Grazvydas "notaz" Ignotas
3
8b71d0eb 4#include "../PicoInt.h"
5
8b71d0eb 6int PicoCDBuffers = 0;
7static unsigned char *cd_buffer = NULL;
8static int prev_lba = 0x80000000;
9
10static int hits, reads;
11
da310283 12//#define THREADED_CD_IO
13
14/* threaded reader */
15#ifdef THREADED_CD_IO
16#include <pthread.h>
17#define tioprintf printf
18
19static pthread_t thr_thread = 0;
20static pthread_cond_t thr_cond = PTHREAD_COND_INITIALIZER;
21static pthread_mutex_t thr_mutex = PTHREAD_MUTEX_INITIALIZER;
22static unsigned char *thr_buffer[2][2048 + 304] __attribute__((aligned(4)));
23static int thr_lba_need;
24static int thr_lba_have[2];
25
26static void thr_read_lba(int slot, int lba)
27{
28 int is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
29 int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
30
31 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
32 pm_read(thr_buffer[slot], 2048, Pico_mcd->TOC.Tracks[0].F);
33 thr_lba_have[slot] = lba;
34}
35
36static void *buffering_thread(void *arg)
37{
38 int free_slot, lba;
39
40 elprintf(EL_STATUS, "CD I/O thread started.");
41
42 pthread_mutex_lock(&thr_mutex);
43
44 while (1)
45 {
46 if (thr_lba_need < 0) goto wait;
47
48 free_slot = -1;
49 if (thr_lba_have[0] == -1) free_slot = 0;
50 if (thr_lba_have[1] == -1) free_slot = 1;
51 if (free_slot == -1) goto wait;
52
53 lba = thr_lba_need;
54 if (lba != thr_lba_have[free_slot^1]) {
55 thr_read_lba(free_slot, lba);
56 tioprintf("t done %i %i\n", lba, free_slot);
57 continue;
58 }
59 lba++;
60 if (lba != thr_lba_have[free_slot^1]) {
61 thr_read_lba(free_slot, lba);
62 tioprintf("t done %i %i\n", lba, free_slot);
63 continue;
64 }
65
66wait:
67 pthread_cond_wait(&thr_cond, &thr_mutex);
68 tioprintf("t wake\n");
69 }
70
71 pthread_mutex_unlock(&thr_mutex);
72
73 return NULL;
74}
75
76static void threaded_read(void *dest, int lba)
77{
78 int i, have = -1;
79 tioprintf("\n");
80
81 if (lba == thr_lba_have[0]) have = 0;
82 if (lba == thr_lba_have[1]) have = 1;
83 if (have != -1)
84 {
85 tioprintf("r hit %i %i\n", lba, have);
86 memcpy32(dest, (int *)thr_buffer[have], 2048/4);
87 if (lba != prev_lba) {
88 thr_lba_have[have] = -1; // make free slot
89 thr_lba_need = lba + 1; // guess a sequential read..
90 pthread_cond_signal(&thr_cond);
91 sched_yield();
92 prev_lba = lba;
93 }
94 return;
95 }
96
97 tioprintf("r miss %i\n", lba);
98 thr_lba_need = lba;
99 pthread_mutex_lock(&thr_mutex);
100 pthread_mutex_unlock(&thr_mutex);
101 if (lba == thr_lba_have[0]) have = 0;
102 if (lba == thr_lba_have[1]) have = 1;
103 if (have == -1)
104 {
105 // try again..
106 thr_lba_have[0] = thr_lba_have[1] = -1;
107 for (i = 0; have == -1 && i < 10; i++)
108 {
109 tioprintf("r hard %i\n", lba);
110 pthread_cond_signal(&thr_cond);
111 sched_yield();
112 pthread_mutex_lock(&thr_mutex);
113 pthread_mutex_unlock(&thr_mutex);
114 if (lba == thr_lba_have[0]) have = 0;
115 if (lba == thr_lba_have[1]) have = 1;
116 }
117 }
118
119 // we MUST have the data at this point..
120 if (have == -1) { tioprintf("BUG!\n"); exit(1); }
121 tioprintf("r reco %i %i\n", lba, have);
122 memcpy32(dest, (int *)thr_buffer[have], 2048/4);
123 thr_lba_have[have] = -1;
124 pthread_cond_signal(&thr_cond);
125
126 prev_lba = lba;
127 return;
128}
129#endif
130
8b71d0eb 131
132void PicoCDBufferInit(void)
133{
134 void *tmp;
135
136 prev_lba = 0x80000000;
137 hits = reads = 0;
da310283 138 cd_buffer = NULL;
8b71d0eb 139
140 if (PicoCDBuffers <= 1) {
141 PicoCDBuffers = 0;
da310283 142 goto no_buffering; /* buffering off */
8b71d0eb 143 }
144
145 /* try alloc'ing until we succeed */
146 while (PicoCDBuffers > 0)
147 {
8022f53d 148 tmp = realloc(cd_buffer, PicoCDBuffers * 2048 + 304);
8b71d0eb 149 if (tmp != NULL) break;
150 PicoCDBuffers >>= 1;
151 }
152
da310283 153 if (PicoCDBuffers > 0) {
154 cd_buffer = tmp;
155 return;
156 }
8b71d0eb 157
da310283 158no_buffering:;
159#ifdef THREADED_CD_IO
160 thr_lba_need = thr_lba_have[0] = thr_lba_have[1] = -1;
161 if (thr_thread == 0)
162 {
163 pthread_create(&thr_thread, NULL, buffering_thread, NULL);
164 }
165#endif
8b71d0eb 166}
167
168
169void PicoCDBufferFree(void)
170{
da310283 171#ifdef THREADED_CD_IO
172 pthread_mutex_lock(&thr_mutex);
173 pthread_mutex_unlock(&thr_mutex);
174#endif
8b71d0eb 175 if (cd_buffer) {
176 free(cd_buffer);
177 cd_buffer = NULL;
178 }
179 if (reads)
c6196c0f 180 elprintf(EL_STATUS, "CD buffer hits: %i/%i (%i%%)\n", hits, reads, hits * 100 / reads);
8b71d0eb 181}
182
183
184/* this is a try to fight slow SD access of GP2X */
eff55556 185PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba)
8b71d0eb 186{
66fdc0f0 187 int is_bin, offs, read_len, moved = 0;
8b71d0eb 188 reads++;
189
190 is_bin = Pico_mcd->TOC.Tracks[0].ftype == TYPE_BIN;
191
192 if (PicoCDBuffers <= 0)
193 {
da310283 194#ifdef THREADED_CD_IO
195 threaded_read(dest, lba);
196 return;
197#else
8b71d0eb 198 /* no buffering */
199 int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
200 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
201 pm_read(dest, 2048, Pico_mcd->TOC.Tracks[0].F);
202 return;
da310283 203#endif
8b71d0eb 204 }
205
206 /* hit? */
207 offs = lba - prev_lba;
208 if (offs >= 0 && offs < PicoCDBuffers)
209 {
210 hits++;
211 if (offs == 0) dprintf("CD buffer seek to old %i -> %i\n", prev_lba, lba);
212 memcpy32(dest, (int *)(cd_buffer + offs*2048), 2048/4);
213 return;
214 }
215
216 if (prev_lba + PicoCDBuffers != lba)
217 {
218 int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
219 dprintf("CD buffer seek %i -> %i\n", prev_lba, lba);
220 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
221 }
222
223 dprintf("CD buffer miss %i -> %i\n", prev_lba, lba);
224
225 if (lba < prev_lba && prev_lba - lba < PicoCDBuffers)
226 {
8b71d0eb 227 read_len = prev_lba - lba;
66fdc0f0 228 dprintf("CD buffer move=%i, read_len=%i", PicoCDBuffers - read_len, read_len);
8b71d0eb 229 memmove(cd_buffer + read_len*2048, cd_buffer, (PicoCDBuffers - read_len)*2048);
66fdc0f0 230 moved = 1;
8b71d0eb 231 }
232 else
233 {
234 read_len = PicoCDBuffers;
235 }
236
66fdc0f0 237 if (PicoMessage != NULL && read_len >= 512)
238 {
239 PicoMessage("Buffering data...");
240 }
241
8b71d0eb 242 if (is_bin)
243 {
8022f53d 244 int i = 0;
245#if REDUCE_IO_CALLS
b542be46 246 int bufs = (read_len*2048) / (2048+304);
8022f53d 247 pm_read(cd_buffer, bufs*(2048+304), Pico_mcd->TOC.Tracks[0].F);
248 for (i = 1; i < bufs; i++)
249 // should really use memmove here, but my memcpy32 implementation is also suitable here
250 memcpy32((int *)(cd_buffer + i*2048), (int *)(cd_buffer + i*(2048+304)), 2048/4);
251#endif
b542be46 252 for (; i < read_len - 1; i++)
8b71d0eb 253 {
8022f53d 254 pm_read(cd_buffer + i*2048, 2048 + 304, Pico_mcd->TOC.Tracks[0].F);
255 // pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR); // seeking is slower, in PSP case even more
8b71d0eb 256 }
b542be46 257 // further data might be moved, do not overwrite
258 pm_read(cd_buffer + i*2048, 2048, Pico_mcd->TOC.Tracks[0].F);
259 pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR);
8b71d0eb 260 }
261 else
262 {
263 pm_read(cd_buffer, read_len*2048, Pico_mcd->TOC.Tracks[0].F);
264 }
265 memcpy32(dest, (int *) cd_buffer, 2048/4);
266 prev_lba = lba;
66fdc0f0 267
268 if (moved)
269 {
270 /* file pointer must point to the same data in file, as would-be data after our buffer */
271 int where_seek;
272 lba += PicoCDBuffers;
273 where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11);
274 pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET);
275 }
8b71d0eb 276}
277