diff options
Diffstat (limited to 'drivers')
31 files changed, 1203 insertions, 278 deletions
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 0bb03d23e..1e17e7253 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -37,19 +37,20 @@ #include <errno.h> -Error AudioDriverALSA::init() { - - active = false; - thread_exited = false; - exit_thread = false; - pcm_open = false; - samples_in = NULL; - samples_out = NULL; - +Error AudioDriverALSA::init_device() { mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); speaker_mode = SPEAKER_MODE_STEREO; channels = 2; + // If there is a specified device check that it is really present + if (device_name != "Default") { + Array list = get_device_list(); + if (list.find(device_name) == -1) { + device_name = "Default"; + new_device = "Default"; + } + } + int status; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; @@ -65,7 +66,16 @@ Error AudioDriverALSA::init() { //6 chans - "plug:surround51" //4 chans - "plug:surround40"; - status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if (device_name == "Default") { + status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + } else { + String device = device_name; + int pos = device.find(";"); + if (pos != -1) { + device = device.substr(0, pos); + } + status = snd_pcm_open(&pcm_handle, device.utf8().get_data(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + } ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN); @@ -129,15 +139,28 @@ Error AudioDriverALSA::init() { status = snd_pcm_sw_params(pcm_handle, swparams); CHECK_FAIL(status < 0); - samples_in = memnew_arr(int32_t, period_size * channels); - samples_out = memnew_arr(int16_t, period_size * channels); + samples_in.resize(period_size * channels); + samples_out.resize(period_size * channels); snd_pcm_nonblock(pcm_handle, 0); - mutex = Mutex::create(); - thread = Thread::create(AudioDriverALSA::thread_func, this); - return OK; +} + +Error AudioDriverALSA::init() { + + active = false; + thread_exited = false; + exit_thread = false; + pcm_open = false; + + Error err = init_device(); + if (err == OK) { + mutex = Mutex::create(); + thread = Thread::create(AudioDriverALSA::thread_func, this); + } + + return err; }; void AudioDriverALSA::thread_func(void *p_udata) { @@ -152,7 +175,7 @@ void AudioDriverALSA::thread_func(void *p_udata) { } else { ad->lock(); - ad->audio_server_process(ad->period_size, ad->samples_in); + ad->audio_server_process(ad->period_size, ad->samples_in.ptrw()); ad->unlock(); @@ -167,7 +190,7 @@ void AudioDriverALSA::thread_func(void *p_udata) { while (todo) { if (ad->exit_thread) break; - uint8_t *src = (uint8_t *)ad->samples_out; + uint8_t *src = (uint8_t *)ad->samples_out.ptr(); int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo); if (wrote < 0) { @@ -193,6 +216,26 @@ void AudioDriverALSA::thread_func(void *p_udata) { total += wrote; todo -= wrote; }; + + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + ad->finish_device(); + + Error err = ad->init_device(); + if (err != OK) { + ERR_PRINT("ALSA: init_device error"); + ad->device_name = "Default"; + ad->new_device = "Default"; + + err = ad->init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } }; ad->thread_exited = true; @@ -213,6 +256,49 @@ AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const { return speaker_mode; }; +Array AudioDriverALSA::get_device_list() { + + Array list; + + list.push_back("Default"); + + void **hints; + + if (snd_device_name_hint(-1, "pcm", &hints) < 0) + return list; + + for (void **n = hints; *n != NULL; n++) { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *desc = snd_device_name_get_hint(*n, "DESC"); + + if (name != NULL && !strncmp(name, "plughw", 6)) { + if (desc) { + list.push_back(String(name) + ";" + String(desc)); + } else { + list.push_back(String(name)); + } + } + + if (desc != NULL) + free(desc); + if (name != NULL) + free(name); + } + snd_device_name_free_hint(hints); + + return list; +} + +String AudioDriverALSA::get_device() { + + return device_name; +} + +void AudioDriverALSA::set_device(String device) { + + new_device = device; +} + void AudioDriverALSA::lock() { if (!thread || !mutex) @@ -227,6 +313,14 @@ void AudioDriverALSA::unlock() { mutex->unlock(); }; +void AudioDriverALSA::finish_device() { + + if (pcm_open) { + snd_pcm_close(pcm_handle); + pcm_open = NULL; + } +} + void AudioDriverALSA::finish() { if (!thread) @@ -235,17 +329,13 @@ void AudioDriverALSA::finish() { exit_thread = true; Thread::wait_to_finish(thread); - if (pcm_open) - snd_pcm_close(pcm_handle); - - if (samples_in) { - memdelete_arr(samples_in); - memdelete_arr(samples_out); - }; + finish_device(); memdelete(thread); - if (mutex) + if (mutex) { memdelete(mutex); + mutex = NULL; + } thread = NULL; }; @@ -254,6 +344,9 @@ AudioDriverALSA::AudioDriverALSA() { mutex = NULL; thread = NULL; pcm_handle = NULL; + + device_name = "Default"; + new_device = "Default"; }; AudioDriverALSA::~AudioDriverALSA(){ diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index 8ed60dfdc..2878e100a 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -44,8 +44,14 @@ class AudioDriverALSA : public AudioDriver { snd_pcm_t *pcm_handle; - int32_t *samples_in; - int16_t *samples_out; + String device_name; + String new_device; + + Vector<int32_t> samples_in; + Vector<int16_t> samples_out; + + Error init_device(); + void finish_device(); static void thread_func(void *p_udata); @@ -71,6 +77,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 0b39f9ebc..6e451eabc 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -37,16 +37,22 @@ #define kOutputBus 0 #ifdef OSX_ENABLED -static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { +OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData) { AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; - driver->reopen(); + // If our selected device is the Default call set_device to update the + // kAudioOutputUnitProperty_CurrentDevice property + if (driver->device_name == "Default") { + driver->set_device("Default"); + } return noErr; } #endif -Error AudioDriverCoreAudio::initDevice() { +Error AudioDriverCoreAudio::init_device() { AudioComponentDescription desc; zeromem(&desc, sizeof(desc)); desc.componentType = kAudioUnitType_Output; @@ -129,7 +135,7 @@ Error AudioDriverCoreAudio::initDevice() { return OK; } -Error AudioDriverCoreAudio::finishDevice() { +Error AudioDriverCoreAudio::finish_device() { OSStatus result; if (active) { @@ -153,49 +159,18 @@ Error AudioDriverCoreAudio::init() { channels = 2; #ifdef OSX_ENABLED - outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal; - outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster; + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; - result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); ERR_FAIL_COND_V(result != noErr, FAILED); #endif - return initDevice(); + return init_device(); }; -Error AudioDriverCoreAudio::reopen() { - bool restart = false; - - lock(); - - if (active) { - restart = true; - } - - Error err = finishDevice(); - if (err != OK) { - ERR_PRINT("finishDevice failed"); - unlock(); - return err; - } - - err = initDevice(); - if (err != OK) { - ERR_PRINT("initDevice failed"); - unlock(); - return err; - } - - if (restart) { - start(); - } - - unlock(); - - return OK; -} - OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, @@ -242,13 +217,24 @@ void AudioDriverCoreAudio::start() { if (!active) { OSStatus result = AudioOutputUnitStart(audio_unit); if (result != noErr) { - ERR_PRINT("AudioOutputUnitStart failed"); + ERR_PRINT(("AudioOutputUnitStart failed, code: " + itos(result)).utf8().get_data()); } else { active = true; } } }; +void AudioDriverCoreAudio::stop() { + if (active) { + OSStatus result = AudioOutputUnitStop(audio_unit); + if (result != noErr) { + ERR_PRINT(("AudioOutputUnitStop failed, code: " + itos(result)).utf8().get_data()); + } else { + active = false; + } + } +} + int AudioDriverCoreAudio::get_mix_rate() const { return mix_rate; }; @@ -257,6 +243,150 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); }; +#ifdef OSX_ENABLED + +Array AudioDriverCoreAudio::get_device_list() { + + Array list; + + list.push_back("Default"); + + AudioObjectPropertyAddress prop; + + prop.mSelector = kAudioHardwarePropertyDevices; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + UInt32 size = 0; + AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size); + AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices); + + UInt32 deviceCount = size / sizeof(AudioDeviceID); + for (UInt32 i = 0; i < deviceCount; i++) { + prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mSelector = kAudioDevicePropertyStreamConfiguration; + + AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); + AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); + + UInt32 outputChannelCount = 0; + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + + free(bufferList); + + if (outputChannelCount >= 1) { + CFStringRef cfname; + + size = sizeof(CFStringRef); + prop.mSelector = kAudioObjectPropertyName; + + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname); + + CFIndex length = CFStringGetLength(cfname); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { + // Append the ID to the name in case we have devices with duplicate name + list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")"); + } + + free(buffer); + } + } + + free(audioDevices); + + return list; +} + +String AudioDriverCoreAudio::get_device() { + + return device_name; +} + +void AudioDriverCoreAudio::set_device(String device) { + + device_name = device; + if (!active) { + return; + } + + AudioDeviceID deviceId; + bool found = false; + if (device_name != "Default") { + AudioObjectPropertyAddress prop; + + prop.mSelector = kAudioHardwarePropertyDevices; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + UInt32 size = 0; + AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size); + AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); + AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices); + + UInt32 deviceCount = size / sizeof(AudioDeviceID); + for (UInt32 i = 0; i < deviceCount && !found; i++) { + prop.mScope = kAudioDevicePropertyScopeOutput; + prop.mSelector = kAudioDevicePropertyStreamConfiguration; + + AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size); + AudioBufferList *bufferList = (AudioBufferList *)malloc(size); + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList); + + UInt32 outputChannelCount = 0; + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + outputChannelCount += bufferList->mBuffers[j].mNumberChannels; + + free(bufferList); + + if (outputChannelCount >= 1) { + CFStringRef cfname; + + size = sizeof(CFStringRef); + prop.mSelector = kAudioObjectPropertyName; + + AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname); + + CFIndex length = CFStringGetLength(cfname); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { + String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; + if (name == device_name) { + deviceId = audioDevices[i]; + found = true; + } + } + + free(buffer); + } + } + + free(audioDevices); + } + + if (!found) { + UInt32 size = sizeof(AudioDeviceID); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + + OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId); + ERR_FAIL_COND(result != noErr); + + found = true; + } + + if (found) { + OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + ERR_FAIL_COND(result != noErr); + } +} + +#endif + void AudioDriverCoreAudio::lock() { if (mutex) mutex->lock(); @@ -276,10 +406,15 @@ bool AudioDriverCoreAudio::try_lock() { void AudioDriverCoreAudio::finish() { OSStatus result; - finishDevice(); + finish_device(); #ifdef OSX_ENABLED - result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + AudioObjectPropertyAddress prop; + prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + prop.mScope = kAudioObjectPropertyScopeGlobal; + prop.mElement = kAudioObjectPropertyElementMaster; + + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); if (result != noErr) { ERR_PRINT("AudioObjectRemovePropertyListener failed"); } @@ -309,6 +444,8 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() { buffer_frames = 0; samples_in.clear(); + + device_name = "Default"; }; AudioDriverCoreAudio::~AudioDriverCoreAudio(){}; diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 51256085d..c44e22552 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -43,12 +43,12 @@ class AudioDriverCoreAudio : public AudioDriver { AudioComponentInstance audio_unit; -#ifdef OSX_ENABLED - AudioObjectPropertyAddress outputDeviceAddress; -#endif + bool active; Mutex *mutex; + String device_name; + int mix_rate; unsigned int channels; unsigned int buffer_frames; @@ -56,14 +56,20 @@ class AudioDriverCoreAudio : public AudioDriver { Vector<int32_t> samples_in; +#ifdef OSX_ENABLED + static OSStatus output_device_address_cb(AudioObjectID inObjectID, + UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, + void *inClientData); +#endif + static OSStatus output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); - Error initDevice(); - Error finishDevice(); + Error init_device(); + Error finish_device(); public: const char *get_name() const { @@ -74,12 +80,17 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; +#ifdef OSX_ENABLED + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); +#endif virtual void lock(); virtual void unlock(); virtual void finish(); bool try_lock(); - Error reopen(); + void stop(); AudioDriverCoreAudio(); ~AudioDriverCoreAudio(); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 5bca3ee54..6e7e1793e 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -452,6 +452,11 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p texture->mipmaps = mipmaps; } +void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { + // TODO + ERR_PRINT("Not implemented (ask Karroffel to do it :p)"); +} + Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { Texture *texture = texture_owner.getornull(p_texture); @@ -1297,6 +1302,10 @@ Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleto return Transform2D(); } +void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { + +} + void RasterizerStorageGLES2::update_dirty_skeletons() { } diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 9f8d8d100..b735f2e14 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -237,6 +237,7 @@ public: virtual RID texture_create(); virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; @@ -555,6 +556,7 @@ public: virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const; virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform); virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; + virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); /* Light API */ diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index f41bfe838..ad6c2f850 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -872,9 +872,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() { actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform"; actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed"; - actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; - actions[VS::SHADER_SPATIAL].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; + actions[VS::SHADER_PARTICLES].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; + actions[VS::SHADER_PARTICLES].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; + actions[VS::SHADER_PARTICLES].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; vertex_name = "vertex"; fragment_name = "fragment"; diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index c33fdaa35..ff423bf0d 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -160,6 +160,7 @@ void RasterizerCanvasGLES3::canvas_begin() { state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false); state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false); + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false); state.canvas_shader.set_custom_shader(0); state.canvas_shader.bind(); @@ -180,6 +181,7 @@ void RasterizerCanvasGLES3::canvas_begin() { glBindVertexArray(data.canvas_quad_array); state.using_texture_rect = true; state.using_ninepatch = false; + state.using_skeleton = false; } void RasterizerCanvasGLES3::canvas_end() { @@ -284,6 +286,10 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate); state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform); state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix); + if (state.using_skeleton) { + state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM, state.skeleton_transform); + state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM_INVERSE, state.skeleton_transform_inverse); + } if (storage->frame.current_rt) { state.canvas_shader.set_uniform(CanvasShaderGLES3::SCREEN_PIXEL_SIZE, Vector2(1.0 / storage->frame.current_rt->width, 1.0 / storage->frame.current_rt->height)); } else { @@ -293,7 +299,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat state.using_ninepatch = p_ninepatch; } -void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) { +void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights) { glBindVertexArray(data.polygon_buffer_pointer_array); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); @@ -301,11 +307,17 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun uint32_t buffer_ofs = 0; //vertex +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); +#endif glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs); buffer_ofs += sizeof(Vector2) * p_vertex_count; //color +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); +#endif if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); @@ -322,6 +334,10 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun buffer_ofs += sizeof(Color) * p_vertex_count; } +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); +#endif + if (p_uvs) { glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); @@ -333,6 +349,32 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); +#endif + + if (p_bones && p_weights) { + + glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones); + glEnableVertexAttribArray(VS::ARRAY_BONES); + //glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, false, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs); + glVertexAttribIPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs); + buffer_ofs += sizeof(int) * 4 * p_vertex_count; + + glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights); + glEnableVertexAttribArray(VS::ARRAY_WEIGHTS); + glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, false, sizeof(float) * 4, ((uint8_t *)0) + buffer_ofs); + buffer_ofs += sizeof(float) * 4 * p_vertex_count; + + } else if (state.using_skeleton) { + glVertexAttribI4ui(VS::ARRAY_BONES, 0, 0, 0, 0); + glVertexAttrib4f(VS::ARRAY_WEIGHTS, 0, 0, 0, 0); + } + +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); +#endif + //bind the indices buffer. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices); @@ -342,6 +384,12 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun storage->frame.canvas_draw_commands++; + if (p_bones && p_weights) { + //not used so often, so disable when used + glDisableVertexAttribArray(VS::ARRAY_BONES); + glDisableVertexAttribArray(VS::ARRAY_WEIGHTS); + } + glBindVertexArray(0); } @@ -735,7 +783,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); } - _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); + + _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1, polygon->bones.ptr(), polygon->weights.ptr()); #ifdef GLES_OVER_GL if (polygon->antialiased) { glEnable(GL_LINE_SMOOTH); @@ -921,7 +970,7 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur } _bind_canvas_texture(RID(), RID()); - _draw_polygon(indices, numpoints * 3, numpoints + 1, points, NULL, &circle->color, true); + _draw_polygon(indices, numpoints * 3, numpoints + 1, points, NULL, &circle->color, true, NULL, NULL); //_draw_polygon(numpoints*3,indices,points,NULL,&circle->color,RID(),true); //canvas_draw_circle(circle->indices.size(),circle->indices.ptr(),circle->points.ptr(),circle->uvs.ptr(),circle->colors.ptr(),circle->texture,circle->colors.size()==1); @@ -1068,6 +1117,7 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons RID canvas_last_material; bool prev_distance_field = false; + bool prev_use_skeleton = false; while (p_item_list) { @@ -1106,6 +1156,36 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons } } + RasterizerStorageGLES3::Skeleton *skeleton = NULL; + + { + //skeleton handling + if (ci->skeleton.is_valid()) { + skeleton = storage->skeleton_owner.getornull(ci->skeleton); + if (!skeleton->use_2d) { + skeleton = NULL; + } else { + state.skeleton_transform = p_transform * skeleton->base_transform_2d; + state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse(); + } + } + + bool use_skeleton = skeleton != NULL; + if (prev_use_skeleton != use_skeleton) { + rebind_shader = true; + state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, use_skeleton); + prev_use_skeleton = use_skeleton; + } + + if (skeleton) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1); + glBindTexture(GL_TEXTURE_2D, skeleton->texture); + state.using_skeleton = true; + } else { + state.using_skeleton = false; + } + } + //begin rect Item *material_owner = ci->material_owner ? ci->material_owner : ci; @@ -1130,6 +1210,9 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons if (shader_ptr->canvas_item.uses_screen_texture && !state.canvas_texscreen_used) { //copy if not copied before _copy_texscreen(Rect2()); + + // blend mode will have been enabled so make sure we disable it again later on + last_blend_mode = last_blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED ? last_blend_mode : -1; } if (shader_ptr != shader_cache) { @@ -1201,14 +1284,30 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons } int blend_mode = shader_cache ? shader_cache->canvas_item.blend_mode : RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX; + if (blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) { + blend_mode = RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX; + } bool unshaded = shader_cache && (shader_cache->canvas_item.light_mode == RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_UNSHADED || blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX); bool reclip = false; if (last_blend_mode != blend_mode) { + if (last_blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED) { + // re-enable it + glEnable(GL_BLEND); + } else if (blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED) { + // disable it + glDisable(GL_BLEND); + } switch (blend_mode) { + case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED: { + + // nothing to do here + + } break; case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 1dc17f98d..bfaf1fdb4 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -84,6 +84,9 @@ public: Color canvas_item_modulate; Transform2D extra_matrix; Transform2D final_transform; + bool using_skeleton; + Transform2D skeleton_transform; + Transform2D skeleton_transform_inverse; } state; @@ -123,7 +126,7 @@ public: _FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map); _FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs); - _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); + _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights); _FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 5f57deef1..12e29827b 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -111,8 +111,6 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL strcpy(debType, "Portability"); else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) strcpy(debType, "Performance"); - else if (type == _EXT_DEBUG_TYPE_OTHER_ARB) - strcpy(debType, "Other"); if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) strcpy(debSev, "High"); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 5b6b3d44f..8da2c2f9c 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2362,7 +2362,11 @@ void RasterizerSceneGLES3::_draw_sky(RasterizerStorageGLES3::Sky *p_sky, const C ERR_FAIL_COND(!tex); glActiveTexture(GL_TEXTURE0); - glBindTexture(tex->target, tex->tex_id); + + if (tex->proxy && tex->proxy->tex_id) + glBindTexture(tex->target, tex->proxy->tex_id); + else + glBindTexture(tex->target, tex->tex_id); if (storage->config.srgb_decode_supported && tex->srgb && !tex->using_srgb) { @@ -3906,6 +3910,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_FILMIC); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_ACES); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINDHART_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_REINHARDT); + state.tonemap_shader.set_conditional(TonemapShaderGLES3::KEEP_3D_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, env->auto_exposure); state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_FILTER_BICUBIC, env->glow_bicubic_upscale); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index a287dca1e..945df3545 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "rasterizer_storage_gles3.h" +#include "engine.h" #include "project_settings.h" #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" @@ -827,6 +828,58 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p //texture_set_flags(p_texture,texture->flags); } +// Uploads pixel data to a sub-region of a texture, for the specified mipmap. +// The texture pixels must have been allocated before, because most features seen in texture_set_data() make no sense in a partial update. +// TODO If we want this to be usable without pre-filling pixels with a full image, we have to call glTexImage2D() with null data. +void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) { + + Texture *texture = texture_owner.get(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->render_target); + ERR_FAIL_COND(texture->format != p_image->get_format()); + ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND(src_w <= 0 || src_h <= 0); + ERR_FAIL_COND(src_x < 0 || src_y < 0 || src_x + src_w > p_image->get_width() || src_y + src_h > p_image->get_height()); + ERR_FAIL_COND(dst_x < 0 || dst_y < 0 || dst_x + src_w > texture->alloc_width || dst_y + src_h > texture->alloc_height); + ERR_FAIL_COND(p_dst_mip < 0 || p_dst_mip >= texture->mipmaps); + + GLenum type; + GLenum format; + GLenum internal_format; + bool compressed; + bool srgb; + + // Because OpenGL wants data as a dense array, we have to extract the sub-image if the source rect isn't the full image + Ref<Image> p_sub_img = p_image; + if (src_x > 0 || src_y > 0 || src_w != p_image->get_width() || src_h != p_image->get_height()) { + p_sub_img = p_image->get_rect(Rect2(src_x, src_y, src_w, src_h)); + } + + Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, format, internal_format, type, compressed, srgb); + + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D; + + PoolVector<uint8_t>::Read read = img->get_data().read(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + int src_data_size = img->get_data().size(); + int src_ofs = 0; + + if (texture->compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]); + + } else { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // `format` has to match the internal_format used when the texture was created + glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]); + } +} + Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const { Texture *texture = texture_owner.get(p_texture); @@ -1614,6 +1667,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { shaders.actions_canvas.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_SUB); shaders.actions_canvas.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MUL); shaders.actions_canvas.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_PMALPHA); + shaders.actions_canvas.render_mode_values["blend_disabled"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_DISABLED); shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED); shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY); @@ -1911,7 +1965,7 @@ void RasterizerStorageGLES3::material_set_param(RID p_material, const StringName Variant RasterizerStorageGLES3::material_get_param(RID p_material, const StringName &p_param) const { const Material *material = material_owner.get(p_material); - ERR_FAIL_COND_V(!material, RID()); + ERR_FAIL_COND_V(!material, Variant()); if (material->params.has(p_param)) return material->params[p_param]; @@ -4495,6 +4549,15 @@ Transform2D RasterizerStorageGLES3::skeleton_bone_get_transform_2d(RID p_skeleto return ret; } +void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + + ERR_FAIL_COND(!skeleton->use_2d); + + skeleton->base_transform_2d = p_base_transform; +} + void RasterizerStorageGLES3::update_dirty_skeletons() { glActiveTexture(GL_TEXTURE0); @@ -5855,6 +5918,8 @@ void RasterizerStorageGLES3::update_particles() { shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, particles->emitting); shaders.particles.set_uniform(ParticlesShaderGLES3::RANDOMNESS, particles->randomness); + bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0; + if (particles->clear && particles->pre_process_time > 0.0) { float frame_time; @@ -5872,7 +5937,15 @@ void RasterizerStorageGLES3::update_particles() { } if (particles->fixed_fps > 0) { - float frame_time = 1.0 / particles->fixed_fps; + float frame_time; + float decr; + if (zero_time_scale) { + frame_time = 0.0; + decr = 1.0 / particles->fixed_fps; + } else { + frame_time = 1.0 / particles->fixed_fps; + decr = frame_time; + } float delta = frame.delta; if (delta > 0.1) { //avoid recursive stalls if fps goes below 10 delta = 0.1; @@ -5883,13 +5956,16 @@ void RasterizerStorageGLES3::update_particles() { while (todo >= frame_time) { _particles_process(particles, frame_time); - todo -= frame_time; + todo -= decr; } particles->frame_remainder = todo; } else { - _particles_process(particles, frame.delta); + if (zero_time_scale) + _particles_process(particles, 0.0); + else + _particles_process(particles, frame.delta); } particle_update_list.remove(particle_update_list.first()); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index ef2b24726..6b626cbd0 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -340,6 +340,7 @@ public: virtual RID texture_create(); virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT); virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); + virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT); virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const; virtual void texture_set_flags(RID p_texture, uint32_t p_flags); virtual uint32_t texture_get_flags(RID p_texture) const; @@ -420,6 +421,7 @@ public: BLEND_MODE_SUB, BLEND_MODE_MUL, BLEND_MODE_PMALPHA, + BLEND_MODE_DISABLED, }; int blend_mode; @@ -868,6 +870,7 @@ public: GLuint texture; SelfList<Skeleton> update_list; Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton + Transform2D base_transform_2d; Skeleton() : update_list(this) { @@ -891,6 +894,7 @@ public: virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const; virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform); virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const; + virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform); /* Light API */ diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index 070c661c8..eb8d6c485 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -795,7 +795,6 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_HEIGHT"] = "light_height"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_COLOR"] = "light_color"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_UV"] = "light_uv"; - //actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_SHADOW_COLOR"]="light_shadow_color"; actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT"] = "light"; actions[VS::SHADER_CANVAS_ITEM].renames["SHADOW_COLOR"] = "shadow_color"; @@ -805,9 +804,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_CANVAS_ITEM].usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; actions[VS::SHADER_CANVAS_ITEM].usage_defines["NORMAL"] = "#define NORMAL_USED\n"; actions[VS::SHADER_CANVAS_ITEM].usage_defines["NORMALMAP"] = "#define NORMALMAP_USED\n"; - actions[VS::SHADER_CANVAS_ITEM].usage_defines["SHADOW_COLOR"] = "#define SHADOW_COLOR_USED\n"; actions[VS::SHADER_CANVAS_ITEM].usage_defines["LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; - actions[VS::SHADER_CANVAS_ITEM].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; /** SPATIAL SHADER **/ @@ -914,6 +911,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() { actions[VS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; + actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; /* PARTICLES SHADER */ diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index f436ef06f..326aab4c7 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -4,6 +4,11 @@ layout(location=0) in highp vec2 vertex; layout(location=3) in vec4 color_attrib; +#ifdef USE_SKELETON +layout(location=6) in uvec4 bone_indices; // attrib:6 +layout(location=7) in vec4 bone_weights; // attrib:7 +#endif + #ifdef USE_TEXTURE_RECT uniform vec4 dst_rect; @@ -51,6 +56,12 @@ out highp vec2 pixel_size_interp; #endif +#ifdef USE_SKELETON +uniform mediump sampler2D skeleton_texture; // texunit:-1 +uniform highp mat4 skeleton_transform; +uniform highp mat4 skeleton_transform_inverse; +#endif + #ifdef USE_LIGHTING layout(std140) uniform LightData { //ubo:1 @@ -75,7 +86,6 @@ out vec4 light_uv_interp; out vec4 local_rot; - #ifdef USE_SHADOWS out highp vec2 pos; #endif @@ -101,6 +111,7 @@ MATERIAL_UNIFORMS #endif + VERTEX_SHADER_GLOBALS void main() { @@ -146,6 +157,7 @@ void main() { #endif + #define extra_matrix extra_matrix2 { @@ -175,6 +187,49 @@ VERTEX_SHADER_CODE #endif +#ifdef USE_SKELETON + + if (bone_weights!=vec4(0.0)){ //must be a valid bone + //skeleton transform + + ivec4 bone_indicesi = ivec4(bone_indices); + + ivec2 tex_ofs = ivec2( bone_indicesi.x%256, (bone_indicesi.x/256)*2 ); + + highp mat2x4 m = mat2x4( + texelFetch(skeleton_texture,tex_ofs,0), + texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0) + ) * bone_weights.x; + + tex_ofs = ivec2( bone_indicesi.y%256, (bone_indicesi.y/256)*2 ); + + m+= mat2x4( + texelFetch(skeleton_texture,tex_ofs,0), + texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0) + ) * bone_weights.y; + + tex_ofs = ivec2( bone_indicesi.z%256, (bone_indicesi.z/256)*2 ); + + m+= mat2x4( + texelFetch(skeleton_texture,tex_ofs,0), + texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0) + ) * bone_weights.z; + + + tex_ofs = ivec2( bone_indicesi.w%256, (bone_indicesi.w/256)*2 ); + + m+= mat2x4( + texelFetch(skeleton_texture,tex_ofs,0), + texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0) + ) * bone_weights.w; + + mat4 bone_matrix = skeleton_transform * transpose(mat4(m[0],m[1],vec4(0.0,0.0,1.0,0.0),vec4(0.0,0.0,0.0,1.0))) * skeleton_transform_inverse; + + outvec = bone_matrix * outvec; + } + +#endif + gl_Position = projection_matrix * outvec; #ifdef USE_LIGHTING @@ -207,6 +262,7 @@ uniform mediump sampler2D color_texture; // texunit:0 uniform highp vec2 color_texpixel_size; uniform mediump sampler2D normal_texture; // texunit:1 + in highp vec2 uv_interp; in mediump vec4 color_interp; @@ -285,7 +341,19 @@ MATERIAL_UNIFORMS FRAGMENT_SHADER_GLOBALS -void light_compute(inout vec4 light,vec2 light_vec,float light_height,vec4 light_color,vec2 light_uv,vec4 shadow,vec3 normal,vec2 uv,vec2 screen_uv,vec4 color) { +void light_compute( + inout vec4 light, + inout vec2 light_vec, + inout float light_height, + inout vec4 light_color, + vec2 light_uv, + inout vec4 shadow_color, + vec3 normal, + vec2 uv, +#if defined(SCREEN_UV_USED) + vec2 screen_uv, +#endif + vec4 color) { #if defined(USE_LIGHT_SHADER_CODE) @@ -462,39 +530,41 @@ FRAGMENT_SHADER_CODE float att=1.0; vec2 light_uv = light_uv_interp.xy; - vec4 light = texture(light_texture,light_uv) * light_color; -#if defined(SHADOW_COLOR_USED) - vec4 shadow_color=vec4(0.0,0.0,0.0,0.0); -#endif + vec4 light = texture(light_texture,light_uv); if (any(lessThan(light_uv_interp.xy,vec2(0.0,0.0))) || any(greaterThanEqual(light_uv_interp.xy,vec2(1.0,1.0)))) { color.a*=light_outside_alpha; //invisible } else { + float real_light_height = light_height; + vec4 real_light_color = light_color; + vec4 real_light_shadow_color = light_shadow_color; #if defined(USE_LIGHT_SHADER_CODE) //light is written by the light shader - light_compute(light,light_vec,light_height,light_color,light_uv,shadow,normal,uv,screen_uv,color); + light_compute( + light, + light_vec, + real_light_height, + real_light_color, + light_uv, + real_light_shadow_color, + normal, + uv, +#if defined(SCREEN_UV_USED) + screen_uv, +#endif + color); +#endif -#else + light *= real_light_color; if (normal_used) { - - vec3 light_normal = normalize(vec3(light_vec,-light_height)); + vec3 light_normal = normalize(vec3(light_vec,-real_light_height)); light*=max(dot(-light_normal,normal),0.0); } color*=light; -/* -#ifdef USE_NORMAL - color.xy=local_rot.xy;//normal.xy; - color.zw=vec2(0.0,1.0); -#endif -*/ - -//light shader code -#endif - #ifdef USE_SHADOWS @@ -634,13 +704,8 @@ FRAGMENT_SHADER_CODE #endif - -#if defined(SHADOW_COLOR_USED) - color=mix(shadow_color,color,shadow_attenuation); -#else //color*=shadow_attenuation; - color=mix(light_shadow_color,color,shadow_attenuation); -#endif + color=mix(real_light_shadow_color,color,shadow_attenuation); //use shadows #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index d3644bffd..f5481c597 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1206,6 +1206,7 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 bino float omni_attenuation = pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w ); vec3 light_attenuation = vec3(omni_attenuation); +#if !defined(SHADOWS_DISABLED) if (omni_lights[idx].light_params.w>0.5) { //there is a shadowmap @@ -1252,6 +1253,7 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 bino #endif light_attenuation*=mix(omni_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); } +#endif //SHADOWS_DISABLED light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,omni_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,omni_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim * omni_attenuation,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); @@ -1270,6 +1272,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi spot_attenuation*= 1.0 - pow( spot_rim, spot_lights[idx].light_params.x); vec3 light_attenuation = vec3(spot_attenuation); +#if !defined(SHADOWS_DISABLED) if (spot_lights[idx].light_params.w>0.5) { //there is a shadowmap highp vec4 splane=(spot_lights[idx].shadow_matrix * vec4(vertex,1.0)); @@ -1287,6 +1290,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi #endif light_attenuation*=mix(spot_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow); } +#endif //SHADOWS_DISABLED light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,spot_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,spot_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim * spot_attenuation,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light); @@ -1785,6 +1789,7 @@ FRAGMENT_SHADER_CODE float depth_z = -vertex.z; #ifdef LIGHT_DIRECTIONAL_SHADOW +#if !defined(SHADOWS_DISABLED) #ifdef LIGHT_USE_PSSM4 if (depth_z < shadow_split_offsets.w) { @@ -1927,6 +1932,7 @@ FRAGMENT_SHADER_CODE } +#endif // !defined(SHADOWS_DISABLED) #endif //LIGHT_DIRECTIONAL_SHADOW #ifdef USE_VERTEX_LIGHTING diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index 2f671158b..a75871f08 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -258,9 +258,13 @@ void main() { #endif +#ifdef KEEP_3D_LINEAR + // leave color as is... +#else //regular Linear -> SRGB conversion vec3 a = vec3(0.055); color.rgb = mix( (vec3(1.0)+a)*pow(color.rgb,vec3(1.0/2.4))-a , 12.92*color.rgb , lessThan(color.rgb,vec3(0.0031308))); +#endif #if defined(USING_GLOW) glow = mix( (vec3(1.0)+a)*pow(glow,vec3(1.0/2.4))-a , 12.92*glow , lessThan(glow,vec3(0.0031308))); diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 9639f0634..0f47949b4 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -37,195 +37,223 @@ #include "os/os.h" #include "project_settings.h" -void pa_state_cb(pa_context *c, void *userdata) { - pa_context_state_t state; - int *pa_ready = (int *)userdata; +void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - state = pa_context_get_state(c); - switch (state) { - case PA_CONTEXT_FAILED: + switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: - *pa_ready = 2; + case PA_CONTEXT_FAILED: + ad->pa_ready = -1; break; case PA_CONTEXT_READY: - *pa_ready = 1; + ad->pa_ready = 1; break; } } -void sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - unsigned int *channels = (unsigned int *)userdata; +void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; // If eol is set to a positive number, you're at the end of the list if (eol > 0) { return; } - *channels = l->channel_map.channels; + ad->pa_map = l->channel_map; + ad->pa_status++; } -void server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { - char *default_output = (char *)userdata; +void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; - strncpy(default_output, i->default_sink_name, 1024); + ad->default_device = i->default_sink_name; + ad->pa_status++; } -static unsigned int detect_channels() { - - pa_mainloop *pa_ml; - pa_mainloop_api *pa_mlapi; - pa_operation *pa_op; - pa_context *pa_ctx; - - int state = 0; - int pa_ready = 0; - - char default_output[1024]; - unsigned int channels = 2; - - pa_ml = pa_mainloop_new(); - pa_mlapi = pa_mainloop_get_api(pa_ml); - pa_ctx = pa_context_new(pa_mlapi, "Godot"); - - int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); - if (ret < 0) { - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); +void AudioDriverPulseAudio::detect_channels() { - return 2; - } + pa_channel_map_init_stereo(&pa_map); - pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); + if (device_name == "Default") { + // Get the default output device name + pa_status = 0; + pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } - // Wait until the pa server is ready - while (pa_ready == 0) { - pa_mainloop_iterate(pa_ml, 1, NULL); + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } } - // Check if there was an error connecting to the pa server - if (pa_ready == 2) { - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - - return 2; + char device[1024]; + if (device_name == "Default") { + strcpy(device, default_device.utf8().get_data()); + } else { + strcpy(device, device_name.utf8().get_data()); } - // Get the default output device name - pa_op = pa_context_get_server_info(pa_ctx, &server_info_cb, (void *)default_output); + // Now using the device name get the amount of channels + pa_status = 0; + pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); if (pa_op) { - while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { - ret = pa_mainloop_iterate(pa_ml, 1, NULL); + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); if (ret < 0) { ERR_PRINT("pa_mainloop_iterate error"); } } pa_operation_unref(pa_op); - - // Now using the device name get the amount of channels - pa_op = pa_context_get_sink_info_by_name(pa_ctx, default_output, &sink_info_cb, (void *)&channels); - if (pa_op) { - while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { - ret = pa_mainloop_iterate(pa_ml, 1, NULL); - if (ret < 0) { - ERR_PRINT("pa_mainloop_iterate error"); - } - } - - pa_operation_unref(pa_op); - } else { - ERR_PRINT("pa_context_get_sink_info_by_name error"); - } } else { - ERR_PRINT("pa_context_get_server_info error"); + ERR_PRINT("pa_context_get_sink_info_by_name error"); } - - pa_context_disconnect(pa_ctx); - pa_context_unref(pa_ctx); - pa_mainloop_free(pa_ml); - - return channels; } -Error AudioDriverPulseAudio::init() { +Error AudioDriverPulseAudio::init_device() { - active = false; - thread_exited = false; - exit_thread = false; - - mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + // If there is a specified device check that it is really present + if (device_name != "Default") { + Array list = get_device_list(); + if (list.find(device_name) == -1) { + device_name = "Default"; + new_device = "Default"; + } + } // Detect the amount of channels PulseAudio is using - // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal, - // if not then pa_channels will have the real amount of channels PulseAudio is using and channels - // will have the amount of channels Godot is using (in this case it's pa_channels + 1) - pa_channels = detect_channels(); - switch (pa_channels) { + // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal, + // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels + // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1) + detect_channels(); + switch (pa_map.channels) { case 1: // Mono case 3: // Surround 2.1 case 5: // Surround 5.0 case 7: // Surround 7.0 - channels = pa_channels + 1; + channels = pa_map.channels + 1; break; case 2: // Stereo case 4: // Surround 4.0 case 6: // Surround 5.1 case 8: // Surround 7.1 - channels = pa_channels; + channels = pa_map.channels; break; default: - ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_channels)); - ERR_FAIL_V(ERR_CANT_OPEN); + WARN_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels)); + pa_channel_map_init_stereo(&pa_map); + channels = 2; break; } - pa_sample_spec spec; - spec.format = PA_SAMPLE_S16LE; - spec.channels = pa_channels; - spec.rate = mix_rate; - int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); - pa_buffer_size = buffer_frames * pa_channels; + pa_buffer_size = buffer_frames * pa_map.channels; if (OS::get_singleton()->is_stdout_verbose()) { - print_line("PulseAudio: detected " + itos(pa_channels) + " channels"); + print_line("PulseAudio: detected " + itos(pa_map.channels) + " channels"); print_line("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } + pa_sample_spec spec; + spec.format = PA_SAMPLE_S16LE; + spec.channels = pa_map.channels; + spec.rate = mix_rate; + + pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map); + if (pa_str == NULL) { + ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx)))); + ERR_FAIL_V(ERR_CANT_OPEN); + } + pa_buffer_attr attr; // set to appropriate buffer length (in bytes) from global settings - attr.tlength = pa_buffer_size * sizeof(int16_t); + // Note: PulseAudio defaults to 4 fragments, which means that the actual + // latency is tlength / fragments. It seems that the PulseAudio has no way + // to get the fragments number so we're hardcoding this to the default of 4 + const int fragments = 4; + attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments; // set them to be automatically chosen attr.prebuf = (uint32_t)-1; attr.maxlength = (uint32_t)-1; attr.minreq = (uint32_t)-1; - int error_code; - pulse = pa_simple_new(NULL, // default server - "Godot", // application name - PA_STREAM_PLAYBACK, - NULL, // default device - "Sound", // stream description - &spec, - NULL, // use default channel map - &attr, // use buffering attributes from above - &error_code); - - if (pulse == NULL) { - fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code)); - ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN); - } + const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data(); + pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); + int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL); + ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); samples_in.resize(buffer_frames * channels); samples_out.resize(pa_buffer_size); - mutex = Mutex::create(); - thread = Thread::create(AudioDriverPulseAudio::thread_func, this); + return OK; +} + +Error AudioDriverPulseAudio::init() { + + active = false; + thread_exited = false; + exit_thread = false; + + mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); + + pa_ml = pa_mainloop_new(); + ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN); + + pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot"); + ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN); + + pa_ready = 0; + pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this); + + int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); + if (ret < 0) { + if (pa_ctx) { + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; + } + + return ERR_CANT_OPEN; + } + + while (pa_ready == 0) { + pa_mainloop_iterate(pa_ml, 1, NULL); + } + + if (pa_ready < 0) { + if (pa_ctx) { + pa_context_disconnect(pa_ctx); + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; + } + + return ERR_CANT_OPEN; + } + + Error err = init_device(); + if (err == OK) { + mutex = Mutex::create(); + thread = Thread::create(AudioDriverPulseAudio::thread_func, this); + } return OK; } @@ -233,9 +261,24 @@ Error AudioDriverPulseAudio::init() { float AudioDriverPulseAudio::get_latency() { if (latency == 0) { //only do this once since it's approximate anyway - int error_code; - pa_usec_t palat = pa_simple_get_latency(pulse, &error_code); - latency = double(palat) / 1000000.0; + lock(); + + pa_usec_t palat = 0; + if (pa_stream_get_state(pa_str) == PA_STREAM_READY) { + int negative = 0; + + if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) { + if (negative) { + palat = 0; + } + } + } + + if (palat > 0) { + latency = double(palat) / 1000000.0; + } + + unlock(); } return latency; @@ -258,7 +301,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ad->unlock(); - if (ad->channels == ad->pa_channels) { + if (ad->channels == ad->pa_map.channels) { for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { ad->samples_out[i] = ad->samples_in[i] >> 16; } @@ -268,7 +311,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { unsigned int out_idx = 0; for (unsigned int i = 0; i < ad->buffer_frames; i++) { - for (unsigned int j = 0; j < ad->pa_channels - 1; j++) { + for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16; } uint32_t l = ad->samples_in[in_idx++]; @@ -278,17 +321,66 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // pa_simple_write always consumes the entire buffer - int error_code; int byte_size = ad->pa_buffer_size * sizeof(int16_t); - if (pa_simple_write(ad->pulse, ad->samples_out.ptr(), byte_size, &error_code) < 0) { - // can't recover here - fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code)); - ad->active = false; - ad->exit_thread = true; - break; + + ad->lock(); + + int ret; + do { + ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); + } while (ret > 0); + + if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) { + const void *ptr = ad->samples_out.ptr(); + while (byte_size > 0) { + size_t bytes = pa_stream_writable_size(ad->pa_str); + if (bytes > 0) { + if (bytes > byte_size) { + bytes = byte_size; + } + + ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); + if (ret >= 0) { + byte_size -= bytes; + ptr = (const char *)ptr + bytes; + } + } else { + ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); + if (ret == 0) { + // If pa_mainloop_iterate returns 0 sleep for 1 msec to wait + // for the stream to be able to process more bytes + ad->unlock(); + + OS::get_singleton()->delay_usec(1000); + + ad->lock(); + } + } + } } + + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + ad->finish_device(); + + Error err = ad->init_device(); + if (err != OK) { + ERR_PRINT("PulseAudio: init_device error"); + ad->device_name = "Default"; + ad->new_device = "Default"; + + err = ad->init_device(); + if (err != OK) { + ad->active = false; + ad->exit_thread = true; + break; + } + } + } + + ad->unlock(); } ad->thread_exited = true; @@ -309,6 +401,61 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } +void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { + AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + int ctr = 0; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + ad->pa_devices.push_back(l->name); + ad->pa_status++; +} + +Array AudioDriverPulseAudio::get_device_list() { + + pa_devices.clear(); + pa_devices.push_back("Default"); + + if (pa_ctx == NULL) { + return pa_devices; + } + + lock(); + + // Get the device list + pa_status = 0; + pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this); + if (pa_op) { + while (pa_status == 0) { + int ret = pa_mainloop_iterate(pa_ml, 1, NULL); + if (ret < 0) { + ERR_PRINT("pa_mainloop_iterate error"); + } + } + + pa_operation_unref(pa_op); + } else { + ERR_PRINT("pa_context_get_server_info error"); + } + + unlock(); + + return pa_devices; +} + +String AudioDriverPulseAudio::get_device() { + + return device_name; +} + +void AudioDriverPulseAudio::set_device(String device) { + + new_device = device; +} + void AudioDriverPulseAudio::lock() { if (!thread || !mutex) @@ -323,6 +470,15 @@ void AudioDriverPulseAudio::unlock() { mutex->unlock(); } +void AudioDriverPulseAudio::finish_device() { + + if (pa_str) { + pa_stream_disconnect(pa_str); + pa_stream_unref(pa_str); + pa_str = NULL; + } +} + void AudioDriverPulseAudio::finish() { if (!thread) @@ -331,9 +487,17 @@ void AudioDriverPulseAudio::finish() { exit_thread = true; Thread::wait_to_finish(thread); - if (pulse) { - pa_simple_free(pulse); - pulse = NULL; + finish_device(); + + if (pa_ctx) { + pa_context_disconnect(pa_ctx); + pa_context_unref(pa_ctx); + pa_ctx = NULL; + } + + if (pa_ml) { + pa_mainloop_free(pa_ml); + pa_ml = NULL; } memdelete(thread); @@ -347,9 +511,16 @@ void AudioDriverPulseAudio::finish() { AudioDriverPulseAudio::AudioDriverPulseAudio() { + pa_ml = NULL; + pa_ctx = NULL; + pa_str = NULL; + mutex = NULL; thread = NULL; - pulse = NULL; + + device_name = "Default"; + new_device = "Default"; + default_device = ""; samples_in.clear(); samples_out.clear(); @@ -358,7 +529,8 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() { buffer_frames = 0; pa_buffer_size = 0; channels = 0; - pa_channels = 0; + pa_ready = 0; + pa_status = 0; active = false; thread_exited = false; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 5737f2431..b471f5f9d 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -37,14 +37,21 @@ #include "core/os/thread.h" #include "servers/audio_server.h" -#include <pulse/simple.h> +#include <pulse/pulseaudio.h> class AudioDriverPulseAudio : public AudioDriver { Thread *thread; Mutex *mutex; - pa_simple *pulse; + pa_mainloop *pa_ml; + pa_context *pa_ctx; + pa_stream *pa_str; + pa_channel_map pa_map; + + String device_name; + String new_device; + String default_device; Vector<int32_t> samples_in; Vector<int16_t> samples_out; @@ -53,7 +60,9 @@ class AudioDriverPulseAudio : public AudioDriver { unsigned int buffer_frames; unsigned int pa_buffer_size; int channels; - int pa_channels; + int pa_ready; + int pa_status; + Array pa_devices; bool active; bool thread_exited; @@ -61,6 +70,16 @@ class AudioDriverPulseAudio : public AudioDriver { float latency; + static void pa_state_cb(pa_context *c, void *userdata); + static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata); + static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); + + Error init_device(); + void finish_device(); + + void detect_channels(); + static void thread_func(void *p_udata); public: @@ -72,6 +91,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index ed6f2e24e..457486797 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -133,7 +133,7 @@ Error AudioDriverRtAudio::init() { break; } catch (RtAudioError &e) { // try with less channels - ERR_PRINT("Unable to open audio, retrying with fewer channels.."); + ERR_PRINT("Unable to open audio, retrying with fewer channels..."); switch (speaker_mode) { case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_MODE_STEREO; break; diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index a7a3eef93..c25d34125 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -136,7 +136,7 @@ void FileAccessUnix::close() { if (save_path != "") { //unlink(save_path.utf8().get_data()); - //print_line("renaming.."); + //print_line("renaming..."); int rename_error = rename((save_path + ".tmp").utf8().get_data(), save_path.utf8().get_data()); if (rename_error && close_fail_notify) { @@ -184,7 +184,7 @@ size_t FileAccessUnix::get_position() const { ERR_FAIL_COND_V(!f, 0); - int pos = ftell(f); + long pos = ftell(f); if (pos < 0) { check_errors(); ERR_FAIL_V(0); @@ -196,10 +196,10 @@ size_t FileAccessUnix::get_len() const { ERR_FAIL_COND_V(!f, 0); - int pos = ftell(f); + long pos = ftell(f); ERR_FAIL_COND_V(pos < 0, 0); ERR_FAIL_COND_V(fseek(f, 0, SEEK_END), 0); - int size = ftell(f); + long size = ftell(f); ERR_FAIL_COND_V(size < 0, 0); ERR_FAIL_COND_V(fseek(f, pos, SEEK_SET), 0); diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 032d91f0d..949609bb9 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -101,16 +101,18 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; }; - hints.ai_flags &= !AI_NUMERICHOST; + hints.ai_flags &= ~AI_NUMERICHOST; int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result); if (s != 0) { - ERR_PRINT("getaddrinfo failed!"); + ERR_PRINT("getaddrinfo failed! Cannot resolve hostname."); return IP_Address(); }; if (result == NULL || result->ai_addr == NULL) { ERR_PRINT("Invalid response from getaddrinfo"); + if (result) + freeaddrinfo(result); return IP_Address(); }; diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 31c8e4ade..1e34d63b1 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -73,6 +73,23 @@ void OS_Unix::debug_break() { assert(false); }; +static void handle_interrupt(int sig) { + if (ScriptDebugger::get_singleton() == NULL) + return; + + ScriptDebugger::get_singleton()->set_depth(-1); + ScriptDebugger::get_singleton()->set_lines_left(1); +} + +void OS_Unix::initialize_debugging() { + + if (ScriptDebugger::get_singleton() != NULL) { + struct sigaction action; + action.sa_handler = handle_interrupt; + sigaction(SIGINT, &action, NULL); + } +} + int OS_Unix::unix_initialize_audio(int p_audio_driver) { return 0; @@ -349,6 +366,12 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle String path = p_path; + if (FileAccess::exists(path) && path.is_rel_path()) { + // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path, + // otherwise it will end up searching various system directories for the lib instead and finally failing. + path = "./" + path; + } + if (!FileAccess::exists(path)) { //this code exists so gdnative can load .so files from within the executable path path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index db0fe1e00..95b74d23f 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -101,6 +101,7 @@ public: virtual int get_processor_count() const; virtual void debug_break(); + virtual void initialize_debugging(); virtual String get_executable_path() const; virtual String get_user_data_dir() const; diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp index 17112e5ab..6d798f32f 100644 --- a/drivers/unix/stream_peer_tcp_posix.cpp +++ b/drivers/unix/stream_peer_tcp_posix.cpp @@ -124,11 +124,14 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port, sock_type = p_sock_type; sockfd = p_sockfd; #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); - + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif status = STATUS_CONNECTING; @@ -150,10 +153,14 @@ Error StreamPeerTCPPosix::connect_to_host(const IP_Address &p_host, uint16_t p_p }; #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif struct sockaddr_storage their_addr; @@ -308,7 +315,9 @@ void StreamPeerTCPPosix::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) { + ERR_PRINT("Unable to set TCP no delay option"); + } } bool StreamPeerTCPPosix::is_connected_to_host() const { diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp index 07ffe3b00..67ab981f4 100644 --- a/drivers/unix/tcp_server_posix.cpp +++ b/drivers/unix/tcp_server_posix.cpp @@ -91,10 +91,14 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address) ERR_FAIL_COND_V(sockfd == -1, FAILED); #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif int reuse = 1; @@ -113,6 +117,7 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address) ERR_FAIL_V(FAILED); }; } else { + close(sockfd); return ERR_ALREADY_IN_USE; }; @@ -157,10 +162,14 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() { int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size); ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>()); #ifndef NO_FCNTL - fcntl(fd, F_SETFL, O_NONBLOCK); + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(fd, FIONBIO, &bval); + if (ioctl(fd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index aae6c0d30..e1680601a 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -35,6 +35,19 @@ #include "os/os.h" #include "project_settings.h" +#include <functiondiscoverykeys.h> + +#ifndef PKEY_Device_FriendlyName + +#undef DEFINE_PROPERTYKEY +/* clang-format off */ +#define DEFINE_PROPERTYKEY(id, a, b, c, d, e, f, g, h, i, j, k, l) \ + const PROPERTYKEY id = { { a, b, c, { d, e, f, g, h, i, j, k, } }, l }; +/* clang-format on */ + +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); +#endif + const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); @@ -121,7 +134,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + if (device_name == "Default") { + hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + } else { + IMMDeviceCollection *devices = NULL; + + hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + + LPWSTR strId = NULL; + bool found = false; + + UINT count = 0; + hr = devices->GetCount(&count); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); + + for (ULONG i = 0; i < count && !found; i++) { + IMMDevice *device = NULL; + + hr = devices->Item(i, &device); + ERR_BREAK(hr != S_OK); + + IPropertyStore *props = NULL; + hr = device->OpenPropertyStore(STGM_READ, &props); + ERR_BREAK(hr != S_OK); + + PROPVARIANT propvar; + PropVariantInit(&propvar); + + hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + ERR_BREAK(hr != S_OK); + + if (device_name == String(propvar.pwszVal)) { + hr = device->GetId(&strId); + ERR_BREAK(hr != S_OK); + + found = true; + } + + PropVariantClear(&propvar); + props->Release(); + device->Release(); + } + + if (found) { + hr = enumerator->GetDevice(strId, &device); + } + + if (strId) { + CoTaskMemFree(strId); + } + + if (device == NULL) { + hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); + } + } if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. @@ -294,6 +361,64 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } +Array AudioDriverWASAPI::get_device_list() { + + Array list; + IMMDeviceCollection *devices = NULL; + IMMDeviceEnumerator *enumerator = NULL; + + list.push_back(String("Default")); + + CoInitialize(NULL); + + HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + UINT count = 0; + hr = devices->GetCount(&count); + ERR_FAIL_COND_V(hr != S_OK, Array()); + + for (ULONG i = 0; i < count; i++) { + IMMDevice *device = NULL; + + hr = devices->Item(i, &device); + ERR_BREAK(hr != S_OK); + + IPropertyStore *props = NULL; + hr = device->OpenPropertyStore(STGM_READ, &props); + ERR_BREAK(hr != S_OK); + + PROPVARIANT propvar; + PropVariantInit(&propvar); + + hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); + ERR_BREAK(hr != S_OK); + + list.push_back(String(propvar.pwszVal)); + + PropVariantClear(&propvar); + props->Release(); + device->Release(); + } + + devices->Release(); + enumerator->Release(); + return list; +} + +String AudioDriverWASAPI::get_device() { + + return device_name; +} + +void AudioDriverWASAPI::set_device(String device) { + + new_device = device; +} + void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) { if (ad->format_tag == WAVE_FORMAT_PCM) { switch (ad->bits_per_sample) { @@ -409,7 +534,8 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } - if (default_device_changed) { + // If we're using the Default device and it changed finish it so we'll re-init the device + if (ad->device_name == "Default" && default_device_changed) { Error err = ad->finish_device(); if (err != OK) { ERR_PRINT("WASAPI: finish_device error"); @@ -418,6 +544,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { default_device_changed = false; } + // User selected a new device, finish the current one so we'll init the new device + if (ad->device_name != ad->new_device) { + ad->device_name = ad->new_device; + Error err = ad->finish_device(); + if (err != OK) { + ERR_PRINT("WASAPI: finish_device error"); + } + } + if (!ad->audio_client) { Error err = ad->init_device(true); if (err == OK) { @@ -492,6 +627,9 @@ AudioDriverWASAPI::AudioDriverWASAPI() { thread_exited = false; exit_thread = false; active = false; + + device_name = "Default"; + new_device = "Default"; } #endif diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 2b19f0cca..c97f4c288 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -49,6 +49,9 @@ class AudioDriverWASAPI : public AudioDriver { Mutex *mutex; Thread *thread; + String device_name; + String new_device; + WORD format_tag; WORD bits_per_sample; @@ -80,6 +83,9 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); virtual void lock(); virtual void unlock(); virtual void finish(); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 23c8ea2ec..aa0fd34e0 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -85,10 +85,31 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { return ERR_FILE_CANT_OPEN; }; +#ifdef TOOLS_ENABLED + // Windows is case insensitive, but all other platforms are sensitive to it + // To ease cross-platform development, we issue a warning if users try to access + // a file using the wrong case (which *works* on Windows, but won't on other + // platforms). + if (p_mode_flags == READ) { + WIN32_FIND_DATAW d = { 0 }; + HANDLE f = FindFirstFileW(path.c_str(), &d); + if (f) { + String fname = d.cFileName; + if (fname != String()) { + + String base_file = path.get_file(); + if (base_file != fname && base_file.findn(fname) == 0) { + WARN_PRINTS("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms."); + } + } + FindClose(f); + } + } +#endif + if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) { save_path = path; path = path + ".tmp"; - //print_line("saving instead to "+path); } f = _wfopen(path.c_str(), mode_string); @@ -113,7 +134,7 @@ void FileAccessWindows::close() { if (save_path != "") { //unlink(save_path.utf8().get_data()); - //print_line("renaming.."); + //print_line("renaming..."); //_wunlink(save_path.c_str()); //unlink if exists //int rename_error = _wrename((save_path+".tmp").c_str(),save_path.c_str()); @@ -141,7 +162,7 @@ void FileAccessWindows::close() { } if (rename_error) { attempts--; - OS::get_singleton()->delay_usec(1000000); //wait 100msec and try again + OS::get_singleton()->delay_usec(100000); // wait 100msec and try again } } diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp index 55775fc23..cb501ce35 100644 --- a/drivers/windows/stream_peer_tcp_winsock.cpp +++ b/drivers/windows/stream_peer_tcp_winsock.cpp @@ -335,7 +335,9 @@ Error StreamPeerTCPWinsock::connect_to_host(const IP_Address &p_host, uint16_t p void StreamPeerTCPWinsock::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0) { + ERR_PRINT("Unable to set TCP no delay option"); + } } int StreamPeerTCPWinsock::get_available_bytes() const { diff --git a/drivers/windows/tcp_server_winsock.cpp b/drivers/windows/tcp_server_winsock.cpp index 413a0d19b..ddb955549 100644 --- a/drivers/windows/tcp_server_winsock.cpp +++ b/drivers/windows/tcp_server_winsock.cpp @@ -105,6 +105,7 @@ Error TCPServerWinsock::listen(uint16_t p_port, const IP_Address &p_bind_address ERR_FAIL_V(FAILED); }; } else { + closesocket(sockfd); return ERR_ALREADY_IN_USE; }; |
