-
Notifications
You must be signed in to change notification settings - Fork 63
Fix badly architectured audio file reads #1747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
67dda5c
to
6ba82b5
Compare
Added the same fix for opus files too. |
Very nice! |
6ba82b5
to
b1e5c5c
Compare
I'm not sure what the hell happened with CI there, it never actually ran anything, and the only change I made was rebasing on master. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This decreases the load time at start-up by easily 2x
Are you sure you're not accidentally measuring the difference between the 2 randomly selected menu soundtracks? One takes around 0.2s to load, and the other around 0.9s.
In any case, it did make a nice ~10% improvement for LoadOggCodec
in my tests.
Master, short soundtrack (heartbeat1.ogg)
SoundLoader-ext N=10 min=215355us max=261480us avg=243623us tot=2436236us
This branch, short soundtrack
SoundLoader-ext N=10 min=172695us max=232609us avg=217946us tot=2179463us
Master, long soundtrack (heartbeat.ogg)
SoundLoader-ext N=10 min=820111us max=1047663us avg=921785us tot=9217856us
This branch, long soundtrack
SoundLoader-ext N=10 min=779843us max=934458us avg=837928us tot=8379280us
@@ -383,8 +383,10 @@ namespace Audio { | |||
|
|||
streams[streamNum]->SetGain(volume); | |||
|
|||
AudioData audioData(rate, width, channels, (width * numSamples * channels), | |||
reinterpret_cast<const char*>(data)); | |||
AudioData audioData { rate, width, channels, width * numSamples * channels }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this leaks data
. Though the existing code is an unused and broken mess that frees data
using the wrong type
: sampleRate{sampleRate} | ||
, byteDepth{byteDepth} | ||
, numberOfChannels{numberOfChannels} | ||
, size{size} | ||
, rawSamples{rawSamples} | ||
, size( newSize ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need size
now that the vector knows its own size?
char* data = new char[size]; | ||
AudioData out { sampleRate, byteDepth, numChannels, size }; | ||
out.rawSamples.reserve( size ); | ||
std::copy_n(audioFile.data() + dataOffset + 8, size, out.rawSamples.data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rawSamples
still has size 0 at this point so you can't write to it. You can use assign
here
opus_int16* buffer = new opus_int16[numberOfChannels * 5760]; | ||
/* The largest opus file we have so far is sound/thunder/weather.opus | ||
It uncompress to ~7mb. Use 64mb here just in case */ | ||
static constexpr uint32_t MAX_USED_FILE_SIZE = 64 * 1024 * 1024; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
64M of int16 is 128MB
static constexpr uint32_t MAX_USED_FILE_SIZE = 64 * 1024 * 1024; | ||
static constexpr uint32_t MAX_READ_SIZE = 1 * 1024 * 1024; | ||
|
||
static opus_int16 buffer[MAX_USED_FILE_SIZE]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the extra 64M of memory be allocated permanently seems suboptimal. Why not reserve 64M in the vector and call shrink_to_fit
at the end? The code would also be simpler that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that ever work? ov_read()
and op_read()
expect a pointer, you can't use reserve()
there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reserve MAX_USED_FILE_SIZE once at the beginning, then resize
adding an additional MAX_READ_SIZE before each read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is straight up worse than the static buffer since it generates an extra ~300mb because each sound's samples have to be >= MAX_READ_SIZE. Reducing MAX_READ_SIZE would defeat the point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, with reserve()
+ resize()
, the vector's size()
could no longer be used in place of AudioData::size
since the sizes would no longer match.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would that ever work?
ov_read()
andop_read()
expect a pointer, you can't usereserve()
there.
Yeah you'd have to resize
it up by the read size first.
That is straight up worse than the static buffer since it generates an extra ~300mb because each sound's samples have to be >= MAX_READ_SIZE. Reducing MAX_READ_SIZE would defeat the point.
Hence shrink_to_fit
.
But anyway, I thought of another solution that allows using the current version of the code mostly unchanged: use Hunk_AllocateTempMemory
. This will allow the memory used by the large music file to be recycled, assuming that enough large allocations are made afterward (which should be the case when loading a map).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hence shrink_to_fit.
Only ifMAX_READ_SIZE
is reduced.
But anyway, I thought of another solution that allows using the current version of the code mostly unchanged: use Hunk_AllocateTempMemory. This will allow the memory used by the large music file to be recycled, assuming that enough large allocations are made afterward (which should be the case when loading a map).
True, that'd probably be the best solution.
@@ -151,20 +151,43 @@ AudioData LoadOggCodec(std::string filename) | |||
int sampleRate = oggInfo->rate; | |||
int numberOfChannels = oggInfo->channels; | |||
|
|||
char buffer[4096]; | |||
/* The largest ogg file we have so far is res-soundtrack/sound/ui/heartbeat.ogg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 2 comments I made on OpusCodec apply here as well. Also when I logged it, heartbeat.ogg was 49M samples / 98MB.
Removes most of the reallocations that happen because the file size is not known in advance.
b1e5c5c
to
3f3d3b4
Compare
Removes most of the reallocations that happen because the file size is not known in advance.
This decreases the load time at start-up by easily 2x, and slightly improves load time for maps.