all repos — mgba @ 029d0e169b154dd60e02a0134da25328f8719de1

mGBA Game Boy Advance Emulator

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