src/platform/psp2/psp2-context.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "psp2-context.h"
7
8#include <mgba/core/blip_buf.h>
9#include <mgba/core/core.h>
10
11#ifdef M_CORE_GBA
12#include <mgba/internal/gba/gba.h>
13#endif
14#ifdef M_CORE_GB
15#include <mgba/internal/gb/gb.h>
16#endif
17
18#include "feature/gui/gui-runner.h"
19#include <mgba/internal/gba/input.h>
20
21#include <mgba-util/memory.h>
22#include <mgba-util/circle-buffer.h>
23#include <mgba-util/math.h>
24#include <mgba-util/threading.h>
25#include <mgba-util/vfs.h>
26#include <mgba-util/platform/psp2/sce-vfs.h>
27
28#include <psp2/audioout.h>
29#include <psp2/camera.h>
30#include <psp2/ctrl.h>
31#include <psp2/display.h>
32#include <psp2/gxm.h>
33#include <psp2/kernel/sysmem.h>
34#include <psp2/motion.h>
35
36#include <vita2d.h>
37
38#define RUMBLE_PWM 8
39#define CDRAM_ALIGN 0x40000
40
41static enum ScreenMode {
42 SM_BACKDROP,
43 SM_PLAIN,
44 SM_FULL,
45 SM_ASPECT,
46 SM_MAX
47} screenMode;
48
49static void* outputBuffer;
50static vita2d_texture* tex;
51static vita2d_texture* screenshot;
52static Thread audioThread;
53
54static struct mSceRotationSource {
55 struct mRotationSource d;
56 struct SceMotionSensorState state;
57} rotation;
58
59static struct mSceRumble {
60 struct mRumble d;
61 struct CircleBuffer history;
62 int current;
63} rumble;
64
65static struct mSceImageSource {
66 struct mImageSource d;
67 SceUID memblock;
68 void* buffer;
69 unsigned cam;
70 size_t bufferOffset;
71} camera;
72
73static struct mAVStream stream;
74
75bool frameLimiter = true;
76
77extern const uint8_t _binary_backdrop_png_start[];
78static vita2d_texture* backdrop = 0;
79
80#define PSP2_SAMPLES 512
81#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16)
82
83static struct mPSP2AudioContext {
84 struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE];
85 size_t writeOffset;
86 size_t readOffset;
87 size_t samples;
88 Mutex mutex;
89 Condition cond;
90 bool running;
91} audioContext;
92
93void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) {
94 mInputBindKey(map, PSP2_INPUT, __builtin_ctz(pspKey), key);
95}
96
97static THREAD_ENTRY _audioThread(void* context) {
98 struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context;
99 uint32_t zeroBuffer[PSP2_SAMPLES] = {0};
100 int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO);
101 while (audio->running) {
102 MutexLock(&audio->mutex);
103 void* buffer;
104 if (audio->samples >= PSP2_SAMPLES) {
105 buffer = &audio->buffer[audio->readOffset];
106 audio->samples -= PSP2_SAMPLES;
107 audio->readOffset += PSP2_SAMPLES;
108 if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) {
109 audio->readOffset = 0;
110 }
111 ConditionWake(&audio->cond);
112 } else {
113 buffer = zeroBuffer;
114 }
115 MutexUnlock(&audio->mutex);
116
117 sceAudioOutOutput(audioPort, buffer);
118 }
119 sceAudioOutReleasePort(audioPort);
120 return 0;
121}
122
123static void _sampleRotation(struct mRotationSource* source) {
124 struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
125 sceMotionGetSensorState(&rotation->state, 1);
126}
127
128static int32_t _readTiltX(struct mRotationSource* source) {
129 struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
130 return rotation->state.accelerometer.x * 0x30000000;
131}
132
133static int32_t _readTiltY(struct mRotationSource* source) {
134 struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
135 return rotation->state.accelerometer.y * -0x30000000;
136}
137
138static int32_t _readGyroZ(struct mRotationSource* source) {
139 struct mSceRotationSource* rotation = (struct mSceRotationSource*) source;
140 return rotation->state.gyro.z * -0x10000000;
141}
142
143static void _setRumble(struct mRumble* source, int enable) {
144 struct mSceRumble* rumble = (struct mSceRumble*) source;
145 rumble->current += enable;
146 if (CircleBufferSize(&rumble->history) == RUMBLE_PWM) {
147 int8_t oldLevel;
148 CircleBufferRead8(&rumble->history, &oldLevel);
149 rumble->current -= oldLevel;
150 }
151 CircleBufferWrite8(&rumble->history, enable);
152 int small = (rumble->current << 21) / 65793;
153 int big = ((rumble->current * rumble->current) << 18) / 65793;
154 struct SceCtrlActuator state = {
155 small,
156 big
157 };
158 sceCtrlSetActuator(1, &state);
159}
160
161static void _resetCamera(struct mSceImageSource* imageSource) {
162 if (!imageSource->cam) {
163 return;
164 }
165
166 sceCameraOpen(imageSource->cam - 1, &(SceCameraInfo) {
167 .size = sizeof(SceCameraInfo),
168 .format = 5, // SCE_CAMERA_FORMAT_ABGR
169 .resolution = SCE_CAMERA_RESOLUTION_176_144,
170 .framerate = SCE_CAMERA_FRAMERATE_30_FPS,
171 .sizeIBase = 176 * 144 * 4,
172 .pitch = 0,
173 .pIBase = imageSource->buffer,
174 });
175 sceCameraStart(imageSource->cam - 1);
176}
177
178static void _startRequestImage(struct mImageSource* source, unsigned w, unsigned h, int colorFormats) {
179 UNUSED(colorFormats);
180 struct mSceImageSource* imageSource = (struct mSceImageSource*) source;
181
182 if (!imageSource->buffer) {
183 imageSource->memblock = sceKernelAllocMemBlock("camera", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, CDRAM_ALIGN, NULL);
184 sceKernelGetMemBlockBase(imageSource->memblock, &imageSource->buffer);
185 }
186
187 if (!imageSource->cam) {
188 return;
189 }
190
191 _resetCamera(imageSource);
192 imageSource->bufferOffset = (176 - w) / 2 + (144 - h) * 176 / 2;
193
194 SceCameraRead read = {
195 sizeof(SceCameraRead),
196 1
197 };
198 sceCameraRead(imageSource->cam - 1, &read);
199}
200
201static void _stopRequestImage(struct mImageSource* source) {
202 struct mSceImageSource* imageSource = (struct mSceImageSource*) source;
203 if (imageSource->cam) {
204 sceCameraStop(imageSource->cam - 1);
205 sceCameraClose(imageSource->cam - 1);
206 }
207 sceKernelFreeMemBlock(imageSource->memblock);
208 imageSource->buffer = NULL;
209}
210
211
212static void _requestImage(struct mImageSource* source, const void** buffer, size_t* stride, enum mColorFormat* colorFormat) {
213 struct mSceImageSource* imageSource = (struct mSceImageSource*) source;
214
215 if (!imageSource->cam) {
216 memset(imageSource->buffer, 0, 176 * 144 * 4);
217 *buffer = (uint32_t*) imageSource->buffer;
218 *stride = 176;
219 *colorFormat = mCOLOR_XBGR8;
220 return;
221 }
222
223 *buffer = (uint32_t*) imageSource->buffer + imageSource->bufferOffset;
224 *stride = 176;
225 *colorFormat = mCOLOR_XBGR8;
226
227 SceCameraRead read = {
228 sizeof(SceCameraRead),
229 1
230 };
231 sceCameraRead(imageSource->cam - 1, &read);
232}
233
234static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
235 UNUSED(stream);
236 MutexLock(&audioContext.mutex);
237 struct GBAStereoSample* samples = &audioContext.buffer[audioContext.writeOffset];
238 while (audioContext.samples == PSP2_AUDIO_BUFFER_SIZE) {
239 if (!frameLimiter) {
240 blip_clear(left);
241 blip_clear(right);
242 MutexUnlock(&audioContext.mutex);
243 return;
244 }
245 ConditionWait(&audioContext.cond, &audioContext.mutex);
246 }
247 blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true);
248 blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true);
249 audioContext.samples += PSP2_SAMPLES;
250 audioContext.writeOffset += PSP2_SAMPLES;
251 if (audioContext.writeOffset >= PSP2_AUDIO_BUFFER_SIZE) {
252 audioContext.writeOffset = 0;
253 }
254 MutexUnlock(&audioContext.mutex);
255}
256
257uint16_t mPSP2PollInput(struct mGUIRunner* runner) {
258 SceCtrlData pad;
259 sceCtrlPeekBufferPositive(0, &pad, 1);
260
261 int activeKeys = mInputMapKeyBits(&runner->core->inputMap, PSP2_INPUT, pad.buttons, 0);
262 int angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 0, pad.ly);
263 if (angles != GBA_KEY_NONE) {
264 activeKeys |= 1 << angles;
265 }
266 angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 1, pad.lx);
267 if (angles != GBA_KEY_NONE) {
268 activeKeys |= 1 << angles;
269 }
270 angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 2, pad.ry);
271 if (angles != GBA_KEY_NONE) {
272 activeKeys |= 1 << angles;
273 }
274 angles = mInputMapAxis(&runner->core->inputMap, PSP2_INPUT, 3, pad.rx);
275 if (angles != GBA_KEY_NONE) {
276 activeKeys |= 1 << angles;
277 }
278 return activeKeys;
279}
280
281void mPSP2SetFrameLimiter(struct mGUIRunner* runner, bool limit) {
282 UNUSED(runner);
283 frameLimiter = limit;
284}
285
286void mPSP2Setup(struct mGUIRunner* runner) {
287 mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1);
288 mCoreLoadForeignConfig(runner->core, &runner->config);
289
290 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
291 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
292 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);
293 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_SELECT, GBA_KEY_SELECT);
294 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_UP, GBA_KEY_UP);
295 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_DOWN, GBA_KEY_DOWN);
296 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_LEFT, GBA_KEY_LEFT);
297 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_RIGHT, GBA_KEY_RIGHT);
298 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_LTRIGGER, GBA_KEY_L);
299 mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_RTRIGGER, GBA_KEY_R);
300
301 struct mInputAxis desc = { GBA_KEY_DOWN, GBA_KEY_UP, 192, 64 };
302 mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 0, &desc);
303 desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
304 mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
305
306 unsigned width, height;
307 runner->core->desiredVideoDimensions(runner->core, &width, &height);
308 tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
309 screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
310
311 outputBuffer = vita2d_texture_get_datap(tex);
312 runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
313 runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES);
314
315 rotation.d.sample = _sampleRotation;
316 rotation.d.readTiltX = _readTiltX;
317 rotation.d.readTiltY = _readTiltY;
318 rotation.d.readGyroZ = _readGyroZ;
319 runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
320
321 rumble.d.setRumble = _setRumble;
322 CircleBufferInit(&rumble.history, RUMBLE_PWM);
323 runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
324
325 camera.d.startRequestImage = _startRequestImage;
326 camera.d.stopRequestImage = _stopRequestImage;
327 camera.d.requestImage = _requestImage;
328 camera.buffer = NULL;
329 camera.cam = 1;
330 runner->core->setPeripheral(runner->core, mPERIPH_IMAGE_SOURCE, &camera.d);
331
332
333 stream.videoDimensionsChanged = NULL;
334 stream.postAudioFrame = NULL;
335 stream.postAudioBuffer = _postAudioBuffer;
336 stream.postVideoFrame = NULL;
337 runner->core->setAVStream(runner->core, &stream);
338
339 frameLimiter = true;
340 backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start);
341
342 unsigned mode;
343 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
344 screenMode = mode;
345 }
346 if (mCoreConfigGetUIntValue(&runner->config, "camera", &mode)) {
347 camera.cam = mode;
348 }
349}
350
351void mPSP2LoadROM(struct mGUIRunner* runner) {
352 float rate = 60.0f / 1.001f;
353 sceDisplayGetRefreshRate(&rate);
354 double ratio = GBAAudioCalculateRatio(1, rate, 1);
355 blip_set_rates(runner->core->getAudioChannel(runner->core, 0), runner->core->frequency(runner->core), 48000 * ratio);
356 blip_set_rates(runner->core->getAudioChannel(runner->core, 1), runner->core->frequency(runner->core), 48000 * ratio);
357
358 switch (runner->core->platform(runner->core)) {
359#ifdef M_CORE_GBA
360 case PLATFORM_GBA:
361 if (((struct GBA*) runner->core->board)->memory.hw.devices & (HW_TILT | HW_GYRO)) {
362 sceMotionStartSampling();
363 }
364 break;
365#endif
366#ifdef M_CORE_GB
367 case PLATFORM_GB:
368 if (((struct GB*) runner->core->board)->memory.mbcType == GB_MBC7) {
369 sceMotionStartSampling();
370 }
371 break;
372#endif
373 default:
374 break;
375 }
376
377 MutexInit(&audioContext.mutex);
378 ConditionInit(&audioContext.cond);
379 memset(audioContext.buffer, 0, sizeof(audioContext.buffer));
380 audioContext.readOffset = 0;
381 audioContext.writeOffset = 0;
382 audioContext.running = true;
383 ThreadCreate(&audioThread, _audioThread, &audioContext);
384}
385
386
387void mPSP2UnloadROM(struct mGUIRunner* runner) {
388 switch (runner->core->platform(runner->core)) {
389#ifdef M_CORE_GBA
390 case PLATFORM_GBA:
391 if (((struct GBA*) runner->core->board)->memory.hw.devices & (HW_TILT | HW_GYRO)) {
392 sceMotionStopSampling();
393 }
394 break;
395#endif
396#ifdef M_CORE_GB
397 case PLATFORM_GB:
398 if (((struct GB*) runner->core->board)->memory.mbcType == GB_MBC7) {
399 sceMotionStopSampling();
400 }
401 break;
402#endif
403 default:
404 break;
405 }
406}
407
408void mPSP2Paused(struct mGUIRunner* runner) {
409 UNUSED(runner);
410 struct SceCtrlActuator state = {
411 0,
412 0
413 };
414 sceCtrlSetActuator(1, &state);
415 frameLimiter = true;
416}
417
418void mPSP2Unpaused(struct mGUIRunner* runner) {
419 unsigned mode;
420 if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode != screenMode) {
421 screenMode = mode;
422 }
423
424 if (mCoreConfigGetUIntValue(&runner->config, "camera", &mode)) {
425 if (mode != camera.cam) {
426 if (camera.buffer) {
427 sceCameraStop(camera.cam - 1);
428 sceCameraClose(camera.cam - 1);
429 }
430 camera.cam = mode;
431 if (camera.buffer) {
432 _resetCamera(&camera);
433 }
434 }
435 }
436}
437
438void mPSP2Teardown(struct mGUIRunner* runner) {
439 UNUSED(runner);
440 CircleBufferDeinit(&rumble.history);
441 vita2d_free_texture(tex);
442 vita2d_free_texture(screenshot);
443 frameLimiter = true;
444}
445
446void _drawTex(vita2d_texture* t, unsigned width, unsigned height, bool faded) {
447 unsigned w = width;
448 unsigned h = height;
449 // Get greatest common divisor
450 while (w != 0) {
451 int temp = h % w;
452 h = w;
453 w = temp;
454 }
455 int gcd = h;
456 int aspectw = width / gcd;
457 int aspecth = height / gcd;
458 float scalex;
459 float scaley;
460
461 switch (screenMode) {
462 case SM_BACKDROP:
463 default:
464 vita2d_draw_texture_tint(backdrop, 0, 0, (faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
465 // Fall through
466 case SM_PLAIN:
467 w = 960 / width;
468 h = 544 / height;
469 if (w * height > 544) {
470 scalex = h;
471 w = width * h;
472 h = height * h;
473 } else {
474 scalex = w;
475 w = width * w;
476 h = height * w;
477 }
478 scaley = scalex;
479 break;
480 case SM_ASPECT:
481 w = 960 / aspectw;
482 h = 544 / aspecth;
483 if (w * aspecth > 544) {
484 w = aspectw * h;
485 h = aspecth * h;
486 } else {
487 w = aspectw * w;
488 h = aspecth * w;
489 }
490 scalex = w / (float) width;
491 scaley = scalex;
492 break;
493 case SM_FULL:
494 w = 960;
495 h = 544;
496 scalex = 960.0f / width;
497 scaley = 544.0f / height;
498 break;
499 }
500 vita2d_draw_texture_tint_part_scale(t,
501 (960.0f - w) / 2.0f, (544.0f - h) / 2.0f,
502 0, 0, width, height,
503 scalex, scaley,
504 (faded ? 0 : 0xC0000000) | 0x3FFFFFFF);
505}
506
507void mPSP2Draw(struct mGUIRunner* runner, bool faded) {
508 unsigned width, height;
509 runner->core->desiredVideoDimensions(runner->core, &width, &height);
510 _drawTex(tex, width, height, faded);
511}
512
513void mPSP2DrawScreenshot(struct mGUIRunner* runner, const uint32_t* pixels, unsigned width, unsigned height, bool faded) {
514 UNUSED(runner);
515 uint32_t* texpixels = vita2d_texture_get_datap(screenshot);
516 unsigned y;
517 for (y = 0; y < height; ++y) {
518 memcpy(&texpixels[256 * y], &pixels[width * y], width * 4);
519 }
520 _drawTex(screenshot, width, height, faded);
521}
522
523void mPSP2IncrementScreenMode(struct mGUIRunner* runner) {
524 screenMode = (screenMode + 1) % SM_MAX;
525 mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
526}
527
528__attribute__((noreturn, weak)) void __assert_func(const char* file, int line, const char* func, const char* expr) {
529 printf("ASSERT FAILED: %s in %s at %s:%i\n", expr, func, file, line);
530 exit(1);
531}