Implemented new quality setting, "siren_reverb_max_length".

Optimizations to Impulse Response Reverb convolution algorithm to speed up FFT's and reduce memory consumption.
GL_EXT_texture_storage extension now functioning for cube maps.
This commit is contained in:
2013-05-15 15:03:08 -07:00
parent f9f0c5c704
commit 1c7aea8d50
9 changed files with 134 additions and 78 deletions

View File

@@ -47,6 +47,7 @@ KRAudioManager::KRAudioManager(KRContext &context) : KRContextObject(context)
m_enable_audio = true;
m_enable_hrtf = true;
m_enable_reverb = true;
m_reverb_max_length = 8.0f;
m_anticlick_block = true;
mach_timebase_info(&m_timebase_info);
@@ -73,7 +74,9 @@ KRAudioManager::KRAudioManager(KRContext &context) : KRContextObject(context)
m_output_sample = 0;
m_fft_setup = NULL;
for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) {
m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N] = NULL;
}
m_reverb_input_samples = NULL;
m_reverb_input_next_sample = 0;
@@ -132,11 +135,21 @@ bool KRAudioManager::getEnableReverb()
return m_enable_reverb;
}
float KRAudioManager::getReverbMaxLength()
{
return m_reverb_max_length;
}
void KRAudioManager::setEnableReverb(bool enable)
{
m_enable_reverb = enable;
}
void KRAudioManager::setReverbMaxLength(float max_length)
{
m_reverb_max_length = max_length;
}
KRScene *KRAudioManager::getListenerScene()
{
return m_listener_scene;
@@ -242,7 +255,7 @@ void KRAudioManager::renderReverbImpulseResponse(int impulse_response_offset, in
memset(reverb_sample_data_complex.realp + frame_count, 0, frame_count * sizeof(float));
memset(reverb_sample_data_complex.imagp, 0, fft_size * sizeof(float));
vDSP_fft_zip(m_fft_setup, &reverb_sample_data_complex, 1, fft_size_log2, kFFTDirection_Forward);
vDSP_fft_zip(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &reverb_sample_data_complex, 1, fft_size_log2, kFFTDirection_Forward);
float scale = 0.5f / fft_size;
@@ -252,7 +265,7 @@ void KRAudioManager::renderReverbImpulseResponse(int impulse_response_offset, in
for(unordered_map<std::string, siren_reverb_zone_weight_info>::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) {
siren_reverb_zone_weight_info zi = (*zone_itr).second;
if(zi.reverb_sample) {
if(impulse_response_offset < zi.reverb_sample->getFrameCount()) { // Optimization - when mixing multiple impulse responses (i.e. fading between reverb zones), do not process blocks past the end of a shorter impulse response sample when they differ in length
if(impulse_response_offset < KRMIN(zi.reverb_sample->getFrameCount(), m_reverb_max_length * 44100)) { // Optimization - when mixing multiple impulse responses (i.e. fading between reverb zones), do not process blocks past the end of a shorter impulse response sample when they differ in length
if(first_sample) {
// If this is the first or only sample, write directly to the first half of the FFT input buffer
first_sample = false;
@@ -269,9 +282,11 @@ void KRAudioManager::renderReverbImpulseResponse(int impulse_response_offset, in
memset(impulse_block_data_complex.realp + frame_count, 0, frame_count * sizeof(float));
memset(impulse_block_data_complex.imagp, 0, fft_size * sizeof(float));
vDSP_fft_zip(m_fft_setup, &impulse_block_data_complex, 1, fft_size_log2, kFFTDirection_Forward);
vDSP_fft_zip(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &impulse_block_data_complex, 1, fft_size_log2, kFFTDirection_Forward);
vDSP_zvmul(&reverb_sample_data_complex, 1, &impulse_block_data_complex, 1, &conv_data_complex, 1, fft_size, 1);
vDSP_fft_zip(m_fft_setup, &conv_data_complex, 1, fft_size_log2, kFFTDirection_Inverse);
vDSP_fft_zip(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], &conv_data_complex, 1, fft_size_log2, kFFTDirection_Inverse);
vDSP_vsmul(conv_data_complex.realp, 1, &scale, conv_data_complex.realp, 1, fft_size);
@@ -323,7 +338,7 @@ void KRAudioManager::renderReverb()
for(unordered_map<std::string, siren_reverb_zone_weight_info>::iterator zone_itr=m_reverb_zone_weights.begin(); zone_itr != m_reverb_zone_weights.end(); zone_itr++) {
siren_reverb_zone_weight_info zi = (*zone_itr).second;
if(zi.reverb_sample) {
int zone_sample_blocks = zi.reverb_sample->getFrameCount() / KRENGINE_AUDIO_BLOCK_LENGTH + 1;
int zone_sample_blocks = KRMIN(zi.reverb_sample->getFrameCount(), m_reverb_max_length * 44100) / KRENGINE_AUDIO_BLOCK_LENGTH + 1;
impulse_response_blocks = KRMAX(impulse_response_blocks, zone_sample_blocks);
}
}
@@ -815,7 +830,7 @@ void KRAudioManager::initHRTF()
sample->sample(0, 128, channel, spectral.realp, 1.0f, false);
memset(spectral.realp + 128, 0, sizeof(float) * 128);
memset(spectral.imagp, 0, sizeof(float) * 256);
vDSP_fft_zip(m_fft_setup, &spectral, 1, 8, kFFTDirection_Forward);
vDSP_fft_zip(m_fft_setup[8 - KRENGINE_AUDIO_BLOCK_LOG2N], &spectral, 1, 8, kFFTDirection_Forward);
m_hrtf_spectral[channel][pos] = spectral;
}
sample_index++;
@@ -1021,9 +1036,10 @@ void KRAudioManager::initSiren()
m_workspace[2].imagp = m_workspace_data + KRENGINE_REVERB_WORKSPACE_SIZE * 5;
m_reverb_sequence = 0;
m_fft_setup = vDSP_create_fftsetup( KRENGINE_REVERB_MAX_FFT_LOG2, kFFTRadix2);
for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) {
m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N] = vDSP_create_fftsetup( KRENGINE_REVERB_MAX_FFT_LOG2, kFFTRadix2);
}
// ----====---- Initialize Core Audio Objects ----====----
OSDEBUG(NewAUGraph(&m_auGraph));
@@ -1178,9 +1194,11 @@ void KRAudioManager::cleanupSiren()
m_reverb_impulse_responses_weight[i] = 0.0f;
}
if(m_fft_setup) {
vDSP_destroy_fftsetup(m_fft_setup);
m_fft_setup = NULL;
for(int i=KRENGINE_AUDIO_BLOCK_LOG2N; i <= KRENGINE_REVERB_MAX_FFT_LOG2; i++) {
if(m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N]) {
vDSP_destroy_fftsetup(m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N]);
m_fft_setup[i - KRENGINE_AUDIO_BLOCK_LOG2N] = NULL;
}
}
}
@@ -1737,9 +1755,10 @@ void KRAudioManager::renderHRTF()
}
float scale = 0.5f / fft_size;
vDSP_fft_zip(m_fft_setup, hrtf_sample, 1, fft_size_log2, kFFTDirection_Forward);
vDSP_fft_zip(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_sample, 1, fft_size_log2, kFFTDirection_Forward);
vDSP_zvmul(hrtf_sample, 1, &hrtf_spectral, 1, hrtf_convolved, 1, fft_size, 1);
vDSP_fft_zip(m_fft_setup, hrtf_convolved, 1, fft_size_log2, kFFTDirection_Inverse);
vDSP_fft_zip(m_fft_setup[fft_size_log2 - KRENGINE_AUDIO_BLOCK_LOG2N], hrtf_convolved, 1, fft_size_log2, kFFTDirection_Inverse);
vDSP_vsmul(hrtf_convolved->realp, 1, &scale, hrtf_convolved->realp, 1, fft_size);
int output_offset = (m_output_accumulation_block_start) % (KRENGINE_REVERB_MAX_SAMPLES * KRENGINE_MAX_OUTPUT_CHANNELS);

View File

@@ -47,7 +47,7 @@ const int KRENGINE_AUDIO_BUFFERS_PER_SOURCE = 3;
const int KRENGINE_AUDIO_BLOCK_LENGTH = 128; // Length of one block to process. Determines the latency of the audio system and sets size for FFT's used in HRTF convolution
const int KRENGINE_AUDIO_BLOCK_LOG2N = 7; // 2 ^ KRENGINE_AUDIO_BLOCK_LOG2N = KRENGINE_AUDIO_BLOCK_LENGTH
const int KRENGINE_REVERB_MAX_FFT_LOG2 = 17;
const int KRENGINE_REVERB_MAX_FFT_LOG2 = 15;
const int KRENGINE_REVERB_WORKSPACE_SIZE = 1 << KRENGINE_REVERB_MAX_FFT_LOG2;
const float KRENGINE_AUDIO_CUTOFF = 0.02f; // Cutoff gain level, to cull out processing of very quiet sounds
@@ -139,10 +139,14 @@ public:
bool getEnableReverb();
void setEnableReverb(bool enable);
float getReverbMaxLength();
void setReverbMaxLength(float max_length);
private:
bool m_enable_audio;
bool m_enable_hrtf;
bool m_enable_reverb;
float m_reverb_max_length;
KRScene *m_listener_scene; // For now, only one scene is allowed to have active audio at once
@@ -198,7 +202,7 @@ private:
int m_output_accumulation_block_start;
int m_output_sample;
FFTSetup m_fft_setup;
FFTSetup m_fft_setup[KRENGINE_REVERB_MAX_FFT_LOG2 - KRENGINE_AUDIO_BLOCK_LOG2N + 1];
float *m_workspace_data;
DSPSplitComplex m_workspace[3];

View File

@@ -196,7 +196,8 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
@"siren_enable" : @49,
@"siren_enable_reverb" : @50,
@"siren_enable_hrtf" : @51,
@"max_anisotropy" : @52
@"siren_reverb_max_length" : @52,
@"max_anisotropy" : @53
} copy];
[self loadShaders];
@@ -285,7 +286,7 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
-(NSString *)getParameterLabelWithIndex: (int)i
{
NSString *parameter_labels[53] = {
NSString *parameter_labels[54] = {
@"Camera FOV",
@"Shadow Quality (0 - 2)",
@"Enable per-pixel lighting",
@@ -338,13 +339,14 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
@"Siren - Enable Audio",
@"Siren - Enable Reverb",
@"Siren - Enable HRTF",
@"Siren - Max Reverb Len",
@"Anisotropic Filtering"
};
return parameter_labels[i];
}
-(KREngineParameterType)getParameterTypeWithIndex: (int)i
{
KREngineParameterType types[53] = {
KREngineParameterType types[54] = {
KRENGINE_PARAMETER_FLOAT,
KRENGINE_PARAMETER_INT,
@@ -398,13 +400,14 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
KRENGINE_PARAMETER_BOOL,
KRENGINE_PARAMETER_BOOL,
KRENGINE_PARAMETER_BOOL,
KRENGINE_PARAMETER_FLOAT,
KRENGINE_PARAMETER_FLOAT
};
return types[i];
}
-(float)getParameterValueWithIndex: (int)i
{
float values[53] = {
float values[54] = {
_settings.perspective_fov,
(float)_settings.m_cShadowBuffers,
_settings.bEnablePerPixel ? 1.0f : 0.0f,
@@ -457,6 +460,7 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
_settings.siren_enable,
_settings.siren_enable_reverb,
_settings.siren_enable_hrtf,
_settings.siren_reverb_max_length,
_settings.max_anisotropy
};
return values[i];
@@ -659,6 +663,9 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
_settings.siren_enable_hrtf = bNewBoolVal;
break;
case 52:
_settings.siren_reverb_max_length = v;
break;
case 53:
_settings.max_anisotropy = v;
break;
}
@@ -666,13 +673,13 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
-(float)getParameterMinWithIndex: (int)i
{
float minValues[53] = {
float minValues[54] = {
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.01f, 50.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
0.0f, 0.0f, 0.0f, 0.05f, 1.0f
};
return minValues[i];
@@ -680,13 +687,13 @@ void kraken::set_parameter(const std::string &parameter_name, float parameter_va
-(float)getParameterMaxWithIndex: (int)i
{
float maxValues[53] = {
float maxValues[54] = {
PI, 3.0f, 1.0f, 1.0, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 10.0f,
1.0f, 10.0f, 2.0f, 1.0f, 1.0f, 1.0f, 5.0f, 1.0f, 0.5f, 1.0f,
2.0f, 2.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 10.0f, 1000.0f, 1.0f, 5.0f, 1000.0f, 1.0f, 5.0f, 3.0f,
1000.0f, 1000.0f, 0.01f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 10.0f, 1.0f, (float)(KRRenderSettings::KRENGINE_DEBUG_DISPLAY_NUMBER - 1),
1.0f, 1.0f, 1.0f, 8.0f
1.0f, 1.0f, 1.0f, 10.0f, 8.0f
};
return maxValues[i];

View File

@@ -13,6 +13,7 @@ KRRenderSettings::KRRenderSettings()
siren_enable = true;
siren_enable_reverb = true;
siren_enable_hrtf = true;
siren_reverb_max_length = 2.0f;
m_enable_realtime_occlusion = true;
bShowShadowBuffer = false;
@@ -92,6 +93,7 @@ KRRenderSettings& KRRenderSettings::operator=(const KRRenderSettings &s)
siren_enable = s.siren_enable;
siren_enable_reverb = s.siren_enable_reverb;
siren_enable_hrtf = s.siren_enable_hrtf;
siren_reverb_max_length = s.siren_reverb_max_length;
bEnablePerPixel = s.bEnablePerPixel;
bEnableDiffuseMap = s.bEnableDiffuseMap;

View File

@@ -105,6 +105,7 @@ public:
bool siren_enable;
bool siren_enable_reverb;
bool siren_enable_hrtf;
float siren_reverb_max_length;
float max_anisotropy;

View File

@@ -73,6 +73,7 @@ void KRScene::renderFrame(float deltaTime, int width, int height) {
getContext().getAudioManager()->setEnableAudio(camera->settings.siren_enable);
getContext().getAudioManager()->setEnableHRTF(camera->settings.siren_enable_hrtf);
getContext().getAudioManager()->setEnableReverb(camera->settings.siren_enable_reverb);
getContext().getAudioManager()->setReverbMaxLength(camera->settings.siren_reverb_max_length);
getContext().getTextureManager()->setMaxAnisotropy(camera->settings.max_anisotropy);
camera->renderFrame(deltaTime, width, height);

View File

@@ -56,11 +56,10 @@ bool KRTexture2D::createGLTexture(int lod_max_dim) {
m_textureMemUsed = 0;
prev_lod_max_dim = m_current_lod_max_dim;
}
#else
releaseHandle();
#endif
m_current_lod_max_dim = 0;
GLDEBUG(glGenTextures(1, &m_iHandle));

View File

@@ -55,35 +55,59 @@ KRTextureCube::~KRTextureCube()
bool KRTextureCube::createGLTexture(int lod_max_dim)
{
bool success = true;
GLuint prev_handle = 0;
int prev_lod_max_dim = 0;
long prev_mem_size = 0;
#if GL_APPLE_copy_texture_levels && GL_EXT_texture_storage
if(m_iHandle != 0) {
prev_handle = m_iHandle;
prev_mem_size = getMemSize();
m_iHandle = 0;
m_textureMemUsed = 0;
prev_lod_max_dim = m_current_lod_max_dim;
}
#else
releaseHandle();
#endif
m_current_lod_max_dim = 0;
GLDEBUG(glGenTextures(1, &m_iHandle));
if(m_iHandle == 0) {
return false;
}
GLDEBUG(glBindTexture(GL_TEXTURE_CUBE_MAP, m_iHandle));
bool bMipMaps = false;
success = false;
} else {
GLDEBUG(glBindTexture(GL_TEXTURE_CUBE_MAP, m_iHandle));
bool bMipMaps = false;
for(int i=0; i<6; i++) {
std::string faceName = getName() + SUFFIXES[i];
KRTexture2D *faceTexture = (KRTexture2D *)getContext().getTextureManager()->getTexture(faceName);
if(faceTexture) {
if(faceTexture->hasMipmaps()) bMipMaps = true;
faceTexture->uploadTexture(TARGETS[i], lod_max_dim, m_current_lod_max_dim, m_textureMemUsed, 0, 0);
for(int i=0; i<6; i++) {
std::string faceName = getName() + SUFFIXES[i];
KRTexture2D *faceTexture = (KRTexture2D *)getContext().getTextureManager()->getTexture(faceName);
if(faceTexture) {
if(faceTexture->hasMipmaps()) bMipMaps = true;
faceTexture->uploadTexture(TARGETS[i], lod_max_dim, m_current_lod_max_dim, m_textureMemUsed, prev_lod_max_dim, prev_handle);
}
}
if(bMipMaps) {
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
} else {
// GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
GLDEBUG(glGenerateMipmap(GL_TEXTURE_CUBE_MAP));
}
}
if(bMipMaps) {
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
} else {
// GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GLDEBUG(glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
GLDEBUG(glGenerateMipmap(GL_TEXTURE_CUBE_MAP));
if(prev_handle != 0) {
getContext().getTextureManager()->memoryChanged(-prev_mem_size);
GLDEBUG(glDeleteTextures(1, &prev_handle));
}
return true;
return success;
}
long KRTextureCube::getMemRequiredForSize(int max_dim)

View File

@@ -190,41 +190,40 @@ bool KRTexturePVR::uploadTexture(GLenum target, int lod_max_dim, int &current_lo
#if GL_EXT_texture_storage
//void TexStorage2DEXT(enum target, sizei levels, enum internalformat, sizei width, sizei height);
//TexStorage2DEXT(target, )
int level_count=0;
int max_lod_width=0;
int max_lod_height=0;
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
if(width <= target_dim && height <= target_dim) {
if(max_lod_width == 0) {
max_lod_width = width;
max_lod_height = height;
}
if(width > current_lod_max_dim) {
current_lod_max_dim = width;
}
if(height > current_lod_max_dim) {
current_lod_max_dim = height;
}
level_count++;
if(target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || target == GL_TEXTURE_2D) {
// Call glTexStorage2DEXT only for the first uploadTexture used when creating a texture
int level_count=0;
int max_lod_width=0;
int max_lod_height=0;
for(std::list<dataBlockStruct>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
if(width <= target_dim && height <= target_dim) {
if(max_lod_width == 0) {
max_lod_width = width;
max_lod_height = height;
}
level_count++;
}
width = width >> 1;
if(width < 1) {
width = 1;
}
height = height >> 1;
if(height < 1) {
height = 1;
}
}
width = width >> 1;
if(width < 1) {
width = 1;
width = m_iWidth;
height = m_iHeight;
if(target == GL_TEXTURE_CUBE_MAP_POSITIVE_X) {
glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP, level_count, m_internalFormat, max_lod_width, max_lod_height);
} else if(target == GL_TEXTURE_2D) {
glTexStorage2DEXT(target, level_count, m_internalFormat, max_lod_width, max_lod_height);
}
height = height >> 1;
if(height < 1) {
height = 1;
}
}
width = m_iWidth;
height = m_iHeight;
glTexStorage2DEXT(target, level_count, m_internalFormat, max_lod_width, max_lod_height);
}
#endif
// Upload texture data