Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines
Starting to add additional timers for different stages of the process of
updating in order to get more insight into what is slowing it down.
The update takes 9ms, which is much longer than it used to.
Engine-specific timers are coming later.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/FAudio/src/FAudio_platform_sdl2.c
544 lines | 14.7 KiB | text/x-c | CLexer
544 lines | 14.7 KiB | text/x-c | CLexer
r0 | /* FAudio - XAudio Reimplementation for FNA | |||
* | ||||
* Copyright (c) 2011-2020 Ethan Lee, Luigi Auriemma, and the MonoGame Team | ||||
* | ||||
* This software is provided 'as-is', without any express or implied warranty. | ||||
* In no event will the authors be held liable for any damages arising from | ||||
* the use of this software. | ||||
* | ||||
* Permission is granted to anyone to use this software for any purpose, | ||||
* including commercial applications, and to alter it and redistribute it | ||||
* freely, subject to the following restrictions: | ||||
* | ||||
* 1. The origin of this software must not be misrepresented; you must not | ||||
* claim that you wrote the original software. If you use this software in a | ||||
* product, an acknowledgment in the product documentation would be | ||||
* appreciated but is not required. | ||||
* | ||||
* 2. Altered source versions must be plainly marked as such, and must not be | ||||
* misrepresented as being the original software. | ||||
* | ||||
* 3. This notice may not be removed or altered from any source distribution. | ||||
* | ||||
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com> | ||||
* | ||||
*/ | ||||
#include "FAudio_internal.h" | ||||
#include <SDL.h> | ||||
/* Mixer Thread */ | ||||
static void FAudio_INTERNAL_MixCallback(void *userdata, Uint8 *stream, int len) | ||||
{ | ||||
FAudio *audio = (FAudio*) userdata; | ||||
FAudio_zero(stream, len); | ||||
if (audio->active) | ||||
{ | ||||
FAudio_INTERNAL_UpdateEngine( | ||||
audio, | ||||
(float*) stream | ||||
); | ||||
} | ||||
} | ||||
/* Platform Functions */ | ||||
void FAudio_PlatformAddRef() | ||||
{ | ||||
/* SDL tracks ref counts for each subsystem */ | ||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) | ||||
{ | ||||
SDL_Log("SDL_INIT_AUDIO failed: %s", SDL_GetError()); | ||||
} | ||||
FAudio_INTERNAL_InitSIMDFunctions( | ||||
SDL_HasSSE2(), | ||||
SDL_HasNEON() | ||||
); | ||||
} | ||||
void FAudio_PlatformRelease() | ||||
{ | ||||
/* SDL tracks ref counts for each subsystem */ | ||||
SDL_QuitSubSystem(SDL_INIT_AUDIO); | ||||
} | ||||
void FAudio_PlatformInit( | ||||
FAudio *audio, | ||||
uint32_t flags, | ||||
uint32_t deviceIndex, | ||||
FAudioWaveFormatExtensible *mixFormat, | ||||
uint32_t *updateSize, | ||||
void** platformDevice | ||||
) { | ||||
SDL_AudioDeviceID device; | ||||
SDL_AudioSpec want, have; | ||||
FAudio_assert(mixFormat != NULL); | ||||
FAudio_assert(updateSize != NULL); | ||||
/* Build the device spec */ | ||||
want.freq = mixFormat->Format.nSamplesPerSec; | ||||
want.format = AUDIO_F32; | ||||
want.channels = mixFormat->Format.nChannels; | ||||
want.silence = 0; | ||||
want.callback = FAudio_INTERNAL_MixCallback; | ||||
want.userdata = audio; | ||||
if (flags & FAUDIO_1024_QUANTUM) | ||||
{ | ||||
/* Get the sample count for a 21.33ms frame. | ||||
* For 48KHz this should be 1024. | ||||
*/ | ||||
want.samples = (int) ( | ||||
want.freq / (1000.0 / (64.0 / 3.0)) | ||||
); | ||||
} | ||||
else | ||||
{ | ||||
want.samples = want.freq / 100; | ||||
} | ||||
/* Open the device (or at least try to) */ | ||||
iosretry: | ||||
device = SDL_OpenAudioDevice( | ||||
deviceIndex > 0 ? SDL_GetAudioDeviceName(deviceIndex - 1, 0) : NULL, | ||||
0, | ||||
&want, | ||||
&have, | ||||
0 | ||||
); | ||||
if (device == 0) | ||||
{ | ||||
const char *err = SDL_GetError(); | ||||
SDL_Log("OpenAudioDevice failed: %s", err); | ||||
/* iOS has a weird thing where you can't open a stream when the | ||||
* app is in the background, even though the program is meant | ||||
* to be suspended and thus not trip this in the first place. | ||||
* | ||||
* Startup suspend behavior when an app is opened then closed | ||||
* is a big pile of crap, basically. | ||||
* | ||||
* Google the error code and you'll find that this has been a | ||||
* long-standing issue that nobody seems to care about. | ||||
* -flibit | ||||
*/ | ||||
if (SDL_strstr(err, "Code=561015905") != NULL) | ||||
{ | ||||
goto iosretry; | ||||
} | ||||
FAudio_assert(0 && "Failed to open audio device!"); | ||||
return; | ||||
} | ||||
/* Write up the received format for the engine */ | ||||
WriteWaveFormatExtensible(mixFormat, have.channels, have.freq); | ||||
*updateSize = have.samples; | ||||
/* SDL_AudioDeviceID is a Uint32, anybody using a 16-bit PC still? */ | ||||
*platformDevice = (void*) ((size_t) device); | ||||
/* Start the thread! */ | ||||
SDL_PauseAudioDevice(device, 0); | ||||
} | ||||
void FAudio_PlatformQuit(void* platformDevice) | ||||
{ | ||||
SDL_CloseAudioDevice((SDL_AudioDeviceID) ((size_t) platformDevice)); | ||||
} | ||||
uint32_t FAudio_PlatformGetDeviceCount() | ||||
{ | ||||
uint32_t devCount = SDL_GetNumAudioDevices(0); | ||||
if (devCount == 0) | ||||
{ | ||||
return 0; | ||||
} | ||||
return devCount + 1; /* Add one for "Default Device" */ | ||||
} | ||||
void FAudio_UTF8_To_UTF16(const char *src, uint16_t *dst, size_t len); | ||||
uint32_t FAudio_PlatformGetDeviceDetails( | ||||
uint32_t index, | ||||
FAudioDeviceDetails *details | ||||
) { | ||||
const char *name, *envvar; | ||||
int channels, rate; | ||||
FAudio_zero(details, sizeof(FAudioDeviceDetails)); | ||||
if (index >= FAudio_PlatformGetDeviceCount()) | ||||
{ | ||||
return FAUDIO_E_INVALID_CALL; | ||||
} | ||||
details->DeviceID[0] = L'0' + index; | ||||
if (index == 0) | ||||
{ | ||||
name = "Default Device"; | ||||
details->Role = FAudioGlobalDefaultDevice; | ||||
/* This variable will look like a DSound GUID or WASAPI ID, i.e. | ||||
* "{0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}" | ||||
*/ | ||||
envvar = SDL_getenv("FAUDIO_FORCE_DEFAULT_DEVICEID"); | ||||
if (envvar != NULL) | ||||
{ | ||||
FAudio_UTF8_To_UTF16( | ||||
envvar, | ||||
(uint16_t*) details->DeviceID, | ||||
sizeof(details->DeviceID) | ||||
); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
name = SDL_GetAudioDeviceName(index - 1, 0); | ||||
details->Role = FAudioNotDefaultDevice; | ||||
} | ||||
FAudio_UTF8_To_UTF16( | ||||
name, | ||||
(uint16_t*) details->DisplayName, | ||||
sizeof(details->DisplayName) | ||||
); | ||||
/* TODO: SDL_GetAudioDeviceSpec! */ | ||||
envvar = SDL_getenv("SDL_AUDIO_FREQUENCY"); | ||||
if (!envvar || ((rate = SDL_atoi(envvar)) == 0)) | ||||
{ | ||||
rate = 48000; | ||||
} | ||||
envvar = SDL_getenv("SDL_AUDIO_CHANNELS"); | ||||
if (!envvar || ((channels = SDL_atoi(envvar)) == 0)) | ||||
{ | ||||
channels = 2; | ||||
} | ||||
WriteWaveFormatExtensible(&details->OutputFormat, channels, rate); | ||||
return 0; | ||||
} | ||||
/* Threading */ | ||||
FAudioThread FAudio_PlatformCreateThread( | ||||
FAudioThreadFunc func, | ||||
const char *name, | ||||
void* data | ||||
) { | ||||
return (FAudioThread) SDL_CreateThread( | ||||
(SDL_ThreadFunction) func, | ||||
name, | ||||
data | ||||
); | ||||
} | ||||
void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval) | ||||
{ | ||||
SDL_WaitThread((SDL_Thread*) thread, retval); | ||||
} | ||||
void FAudio_PlatformThreadPriority(FAudioThreadPriority priority) | ||||
{ | ||||
SDL_SetThreadPriority((SDL_ThreadPriority) priority); | ||||
} | ||||
uint64_t FAudio_PlatformGetThreadID(void) | ||||
{ | ||||
return (uint64_t) SDL_ThreadID(); | ||||
} | ||||
FAudioMutex FAudio_PlatformCreateMutex() | ||||
{ | ||||
return (FAudioMutex) SDL_CreateMutex(); | ||||
} | ||||
void FAudio_PlatformDestroyMutex(FAudioMutex mutex) | ||||
{ | ||||
SDL_DestroyMutex((SDL_mutex*) mutex); | ||||
} | ||||
void FAudio_PlatformLockMutex(FAudioMutex mutex) | ||||
{ | ||||
SDL_LockMutex((SDL_mutex*) mutex); | ||||
} | ||||
void FAudio_PlatformUnlockMutex(FAudioMutex mutex) | ||||
{ | ||||
SDL_UnlockMutex((SDL_mutex*) mutex); | ||||
} | ||||
void FAudio_sleep(uint32_t ms) | ||||
{ | ||||
SDL_Delay(ms); | ||||
} | ||||
/* Time */ | ||||
uint32_t FAudio_timems() | ||||
{ | ||||
return SDL_GetTicks(); | ||||
} | ||||
/* FAudio I/O */ | ||||
FAudioIOStream* FAudio_fopen(const char *path) | ||||
{ | ||||
FAudioIOStream *io = (FAudioIOStream*) FAudio_malloc( | ||||
sizeof(FAudioIOStream) | ||||
); | ||||
SDL_RWops *rwops = SDL_RWFromFile(path, "rb"); | ||||
io->data = rwops; | ||||
io->read = (FAudio_readfunc) rwops->read; | ||||
io->seek = (FAudio_seekfunc) rwops->seek; | ||||
io->close = (FAudio_closefunc) rwops->close; | ||||
io->lock = FAudio_PlatformCreateMutex(); | ||||
return io; | ||||
} | ||||
FAudioIOStream* FAudio_memopen(void *mem, int len) | ||||
{ | ||||
FAudioIOStream *io = (FAudioIOStream*) FAudio_malloc( | ||||
sizeof(FAudioIOStream) | ||||
); | ||||
SDL_RWops *rwops = SDL_RWFromMem(mem, len); | ||||
io->data = rwops; | ||||
io->read = (FAudio_readfunc) rwops->read; | ||||
io->seek = (FAudio_seekfunc) rwops->seek; | ||||
io->close = (FAudio_closefunc) rwops->close; | ||||
io->lock = FAudio_PlatformCreateMutex(); | ||||
return io; | ||||
} | ||||
uint8_t* FAudio_memptr(FAudioIOStream *io, size_t offset) | ||||
{ | ||||
SDL_RWops *rwops = (SDL_RWops*) io->data; | ||||
FAudio_assert(rwops->type == SDL_RWOPS_MEMORY); | ||||
return rwops->hidden.mem.base + offset; | ||||
} | ||||
void FAudio_close(FAudioIOStream *io) | ||||
{ | ||||
io->close(io->data); | ||||
FAudio_PlatformDestroyMutex((FAudioMutex) io->lock); | ||||
FAudio_free(io); | ||||
} | ||||
#ifdef FAUDIO_DUMP_VOICES | ||||
FAudioIOStreamOut* FAudio_fopen_out(const char *path, const char *mode) | ||||
{ | ||||
FAudioIOStreamOut *io = (FAudioIOStreamOut*) FAudio_malloc( | ||||
sizeof(FAudioIOStreamOut) | ||||
); | ||||
SDL_RWops *rwops = SDL_RWFromFile(path, mode); | ||||
io->data = rwops; | ||||
io->read = (FAudio_readfunc) rwops->read; | ||||
io->write = (FAudio_writefunc) rwops->write; | ||||
io->seek = (FAudio_seekfunc) rwops->seek; | ||||
io->size = (FAudio_sizefunc) rwops->size; | ||||
io->close = (FAudio_closefunc) rwops->close; | ||||
io->lock = FAudio_PlatformCreateMutex(); | ||||
return io; | ||||
} | ||||
void FAudio_close_out(FAudioIOStreamOut *io) | ||||
{ | ||||
io->close(io->data); | ||||
FAudio_PlatformDestroyMutex((FAudioMutex) io->lock); | ||||
FAudio_free(io); | ||||
} | ||||
#endif /* FAUDIO_DUMP_VOICES */ | ||||
/* UTF8->UTF16 Conversion, taken from PhysicsFS */ | ||||
#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF | ||||
#define UNICODE_BOGUS_CHAR_CODEPOINT '?' | ||||
static uint32_t FAudio_UTF8_CodePoint(const char **_str) | ||||
{ | ||||
const char *str = *_str; | ||||
uint32_t retval = 0; | ||||
uint32_t octet = (uint32_t) ((uint8_t) *str); | ||||
uint32_t octet2, octet3, octet4; | ||||
if (octet == 0) /* null terminator, end of string. */ | ||||
return 0; | ||||
else if (octet < 128) /* one octet char: 0 to 127 */ | ||||
{ | ||||
(*_str)++; /* skip to next possible start of codepoint. */ | ||||
return octet; | ||||
} /* else if */ | ||||
else if ((octet > 127) && (octet < 192)) /* bad (starts with 10xxxxxx). */ | ||||
{ | ||||
/* | ||||
* Apparently each of these is supposed to be flagged as a bogus | ||||
* char, instead of just resyncing to the next valid codepoint. | ||||
*/ | ||||
(*_str)++; /* skip to next possible start of codepoint. */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
} /* else if */ | ||||
else if (octet < 224) /* two octets */ | ||||
{ | ||||
(*_str)++; /* advance at least one byte in case of an error */ | ||||
octet -= (128+64); | ||||
octet2 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
*_str += 1; /* skip to next possible start of codepoint. */ | ||||
retval = ((octet << 6) | (octet2 - 128)); | ||||
if ((retval >= 0x80) && (retval <= 0x7FF)) | ||||
return retval; | ||||
} /* else if */ | ||||
else if (octet < 240) /* three octets */ | ||||
{ | ||||
(*_str)++; /* advance at least one byte in case of an error */ | ||||
octet -= (128+64+32); | ||||
octet2 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet3 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
*_str += 2; /* skip to next possible start of codepoint. */ | ||||
retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) ); | ||||
/* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */ | ||||
switch (retval) | ||||
{ | ||||
case 0xD800: | ||||
case 0xDB7F: | ||||
case 0xDB80: | ||||
case 0xDBFF: | ||||
case 0xDC00: | ||||
case 0xDF80: | ||||
case 0xDFFF: | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
} /* switch */ | ||||
/* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */ | ||||
if ((retval >= 0x800) && (retval <= 0xFFFD)) | ||||
return retval; | ||||
} /* else if */ | ||||
else if (octet < 248) /* four octets */ | ||||
{ | ||||
(*_str)++; /* advance at least one byte in case of an error */ | ||||
octet -= (128+64+32+16); | ||||
octet2 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet3 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet4 = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet4 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
*_str += 3; /* skip to next possible start of codepoint. */ | ||||
retval = ( ((octet << 18)) | ((octet2 - 128) << 12) | | ||||
((octet3 - 128) << 6) | ((octet4 - 128)) ); | ||||
if ((retval >= 0x10000) && (retval <= 0x10FFFF)) | ||||
return retval; | ||||
} /* else if */ | ||||
/* | ||||
* Five and six octet sequences became illegal in rfc3629. | ||||
* We throw the codepoint away, but parse them to make sure we move | ||||
* ahead the right number of bytes and don't overflow the buffer. | ||||
*/ | ||||
else if (octet < 252) /* five octets */ | ||||
{ | ||||
(*_str)++; /* advance at least one byte in case of an error */ | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
*_str += 4; /* skip to next possible start of codepoint. */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
} /* else if */ | ||||
else /* six octets */ | ||||
{ | ||||
(*_str)++; /* advance at least one byte in case of an error */ | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
octet = (uint32_t) ((uint8_t) *(++str)); | ||||
if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
*_str += 6; /* skip to next possible start of codepoint. */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
} /* else if */ | ||||
return UNICODE_BOGUS_CHAR_VALUE; | ||||
} | ||||
void FAudio_UTF8_To_UTF16(const char *src, uint16_t *dst, size_t len) | ||||
{ | ||||
len -= sizeof (uint16_t); /* save room for null char. */ | ||||
while (len >= sizeof (uint16_t)) | ||||
{ | ||||
uint32_t cp = FAudio_UTF8_CodePoint(&src); | ||||
if (cp == 0) | ||||
break; | ||||
else if (cp == UNICODE_BOGUS_CHAR_VALUE) | ||||
cp = UNICODE_BOGUS_CHAR_CODEPOINT; | ||||
if (cp > 0xFFFF) /* encode as surrogate pair */ | ||||
{ | ||||
if (len < (sizeof (uint16_t) * 2)) | ||||
break; /* not enough room for the pair, stop now. */ | ||||
cp -= 0x10000; /* Make this a 20-bit value */ | ||||
*(dst++) = 0xD800 + ((cp >> 10) & 0x3FF); | ||||
len -= sizeof (uint16_t); | ||||
cp = 0xDC00 + (cp & 0x3FF); | ||||
} /* if */ | ||||
*(dst++) = cp; | ||||
len -= sizeof (uint16_t); | ||||
} /* while */ | ||||
*dst = 0; | ||||
} | ||||
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */ | ||||