diff options
Diffstat (limited to 'drivers')
92 files changed, 8353 insertions, 5183 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index dd81fc645c..6cfcb1d18c 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -24,6 +24,7 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["vulkan"]: + SConscript("spirv-reflect/SCsub") SConscript("vulkan/SCsub") if env["opengl3"]: SConscript("gl_context/SCsub") @@ -31,7 +32,6 @@ if env["opengl3"]: # Core dependencies SConscript("png/SCsub") -SConscript("spirv-reflect/SCsub") env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index f86c4d82ef..f4c87da9e9 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -50,7 +50,7 @@ Error AudioDriverALSA::init_device() { // If there is a specified device check that it is really present if (device_name != "Default") { - Array list = get_device_list(); + PackedStringArray list = get_device_list(); if (list.find(device_name) == -1) { device_name = "Default"; new_device = "Default"; @@ -168,9 +168,8 @@ Error AudioDriverALSA::init() { return ERR_CANT_OPEN; } - active = false; - thread_exited = false; - exit_thread = false; + active.clear(); + exit_thread.clear(); Error err = init_device(); if (err == OK) { @@ -183,11 +182,11 @@ Error AudioDriverALSA::init() { void AudioDriverALSA::thread_func(void *p_udata) { AudioDriverALSA *ad = static_cast<AudioDriverALSA *>(p_udata); - while (!ad->exit_thread) { + while (!ad->exit_thread.is_set()) { ad->lock(); ad->start_counting_ticks(); - if (!ad->active) { + if (!ad->active.is_set()) { for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) { ad->samples_out.write[i] = 0; } @@ -203,7 +202,7 @@ void AudioDriverALSA::thread_func(void *p_udata) { int todo = ad->period_size; int total = 0; - while (todo && !ad->exit_thread) { + while (todo && !ad->exit_thread.is_set()) { int16_t *src = (int16_t *)ad->samples_out.ptr(); int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo); @@ -222,8 +221,8 @@ void AudioDriverALSA::thread_func(void *p_udata) { wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0); if (wrote < 0) { ERR_PRINT("ALSA: Failed and can't recover: " + String(snd_strerror(wrote))); - ad->active = false; - ad->exit_thread = true; + ad->active.clear(); + ad->exit_thread.set(); } } } @@ -241,8 +240,8 @@ void AudioDriverALSA::thread_func(void *p_udata) { err = ad->init_device(); if (err != OK) { - ad->active = false; - ad->exit_thread = true; + ad->active.clear(); + ad->exit_thread.set(); } } } @@ -250,12 +249,10 @@ void AudioDriverALSA::thread_func(void *p_udata) { ad->stop_counting_ticks(); ad->unlock(); } - - ad->thread_exited = true; } void AudioDriverALSA::start() { - active = true; + active.set(); } int AudioDriverALSA::get_mix_rate() const { @@ -266,8 +263,8 @@ AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const { return speaker_mode; } -Array AudioDriverALSA::get_device_list() { - Array list; +PackedStringArray AudioDriverALSA::get_device_list() { + PackedStringArray list; list.push_back("Default"); @@ -327,7 +324,7 @@ void AudioDriverALSA::finish_device() { } void AudioDriverALSA::finish() { - exit_thread = true; + exit_thread.set(); thread.wait_to_finish(); finish_device(); diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h index dbb40fa088..fa1dba38ed 100644 --- a/drivers/alsa/audio_driver_alsa.h +++ b/drivers/alsa/audio_driver_alsa.h @@ -35,6 +35,7 @@ #include "core/os/mutex.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "servers/audio_server.h" #include "asound-so_wrap.h" @@ -64,9 +65,8 @@ class AudioDriverALSA : public AudioDriver { snd_pcm_uframes_t period_size; int channels = 0; - bool active = false; - bool thread_exited = false; - mutable bool exit_thread = false; + SafeFlag active; + SafeFlag exit_thread; public: const char *get_name() const { @@ -77,7 +77,7 @@ public: virtual void start(); virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; - virtual Array get_device_list(); + virtual PackedStringArray get_device_list(); virtual String get_device(); virtual void set_device(String device); virtual void lock(); diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp index c334146dd2..61fecccb6b 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.cpp +++ b/drivers/alsamidi/midi_driver_alsamidi.cpp @@ -37,85 +37,135 @@ #include <errno.h> -static int get_message_size(uint8_t message) { - switch (message & 0xF0) { - case 0x80: // note off - case 0x90: // note on - case 0xA0: // aftertouch - case 0xB0: // continuous controller - case 0xE0: // pitch bend - case 0xF2: // song position pointer - return 3; - - case 0xC0: // patch change - case 0xD0: // channel pressure - case 0xF1: // time code quarter frame - case 0xF3: // song select +MIDIDriverALSAMidi::MessageCategory MIDIDriverALSAMidi::msg_category(uint8_t msg_part) { + if (msg_part >= 0xf8) { + return MessageCategory::RealTime; + } else if (msg_part >= 0xf0) { + // System Exclusive begin/end are specified as System Common Category messages, + // but we separate them here and give them their own categories as their + // behavior is significantly different. + if (msg_part == 0xf0) { + return MessageCategory::SysExBegin; + } else if (msg_part == 0xf7) { + return MessageCategory::SysExEnd; + } + return MessageCategory::SystemCommon; + } else if (msg_part >= 0x80) { + return MessageCategory::Voice; + } + return MessageCategory::Data; +} + +size_t MIDIDriverALSAMidi::msg_expected_data(uint8_t status_byte) { + if (msg_category(status_byte) == MessageCategory::Voice) { + // Voice messages have a channel number in the status byte, mask it out. + status_byte &= 0xf0; + } + + switch (status_byte) { + case 0x80: // Note Off + case 0x90: // Note On + case 0xA0: // Polyphonic Key Pressure (Aftertouch) + case 0xB0: // Control Change (CC) + case 0xE0: // Pitch Bend Change + case 0xF2: // Song Position Pointer return 2; - case 0xF0: // SysEx start - case 0xF4: // reserved - case 0xF5: // reserved - case 0xF6: // tune request - case 0xF7: // SysEx end - case 0xF8: // timing clock - case 0xF9: // reserved - case 0xFA: // start - case 0xFB: // continue - case 0xFC: // stop - case 0xFD: // reserved - case 0xFE: // active sensing - case 0xFF: // reset + case 0xC0: // Program Change + case 0xD0: // Channel Pressure (Aftertouch) + case 0xF1: // MIDI Time Code Quarter Frame + case 0xF3: // Song Select return 1; } - return 256; + return 0; +} + +void MIDIDriverALSAMidi::InputConnection::parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver, + uint64_t timestamp) { + switch (msg_category(byte)) { + case MessageCategory::RealTime: + // Real-Time messages are single byte messages that can + // occur at any point. + // We pass them straight through. + driver.receive_input_packet(timestamp, &byte, 1); + break; + + case MessageCategory::Data: + // We don't currently forward System Exclusive messages so skip their data. + // Collect any expected data for other message types. + if (!skipping_sys_ex && expected_data > received_data) { + buffer[received_data + 1] = byte; + received_data++; + + // Forward a complete message and reset relevant state. + if (received_data == expected_data) { + driver.receive_input_packet(timestamp, buffer, received_data + 1); + received_data = 0; + + if (msg_category(buffer[0]) != MessageCategory::Voice) { + // Voice Category messages can be sent with "running status". + // This means they don't resend the status byte until it changes. + // For other categories, we reset expected data, to require a new status byte. + expected_data = 0; + } + } + } + break; + + case MessageCategory::SysExBegin: + buffer[0] = byte; + skipping_sys_ex = true; + break; + + case MessageCategory::SysExEnd: + expected_data = 0; + skipping_sys_ex = false; + break; + + case MessageCategory::Voice: + case MessageCategory::SystemCommon: + buffer[0] = byte; + received_data = 0; + expected_data = msg_expected_data(byte); + skipping_sys_ex = false; + if (expected_data == 0) { + driver.receive_input_packet(timestamp, &byte, 1); + } + break; + } +} + +int MIDIDriverALSAMidi::InputConnection::read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp) { + int ret; + do { + uint8_t byte = 0; + ret = snd_rawmidi_read(rawmidi_ptr, &byte, 1); + + if (ret < 0) { + if (ret != -EAGAIN) { + ERR_PRINT("snd_rawmidi_read error: " + String(snd_strerror(ret))); + } + } else { + parse_byte(byte, driver, timestamp); + } + } while (ret > 0); + + return ret; } void MIDIDriverALSAMidi::thread_func(void *p_udata) { MIDIDriverALSAMidi *md = static_cast<MIDIDriverALSAMidi *>(p_udata); uint64_t timestamp = 0; - uint8_t buffer[256]; - int expected_size = 255; - int bytes = 0; - - while (!md->exit_thread) { - int ret; + while (!md->exit_thread.is_set()) { md->lock(); - for (int i = 0; i < md->connected_inputs.size(); i++) { - snd_rawmidi_t *midi_in = md->connected_inputs[i]; - do { - uint8_t byte = 0; - ret = snd_rawmidi_read(midi_in, &byte, 1); - if (ret < 0) { - if (ret != -EAGAIN) { - ERR_PRINT("snd_rawmidi_read error: " + String(snd_strerror(ret))); - } - } else { - if (byte & 0x80) { - // Flush previous packet if there is any - if (bytes) { - md->receive_input_packet(timestamp, buffer, bytes); - bytes = 0; - } - expected_size = get_message_size(byte); - // After a SysEx start, all bytes are data until a SysEx end, so - // we're going to end the command at the SES, and let the common - // driver ignore the following data bytes. - } + InputConnection *connections = md->connected_inputs.ptrw(); + size_t connection_count = md->connected_inputs.size(); - if (bytes < 256) { - buffer[bytes++] = byte; - // If we know the size of the current packet receive it if it reached the expected size - if (bytes >= expected_size) { - md->receive_input_packet(timestamp, buffer, bytes); - bytes = 0; - } - } - } - } while (ret > 0); + for (size_t i = 0; i < connection_count; i++) { + connections[i].read_in(*md, timestamp); } md->unlock(); @@ -139,7 +189,7 @@ Error MIDIDriverALSAMidi::open() { snd_rawmidi_t *midi_in; int ret = snd_rawmidi_open(&midi_in, nullptr, name, SND_RAWMIDI_NONBLOCK); if (ret >= 0) { - connected_inputs.insert(i++, midi_in); + connected_inputs.insert(i++, InputConnection(midi_in)); } } @@ -149,18 +199,18 @@ Error MIDIDriverALSAMidi::open() { } snd_device_name_free_hint(hints); - exit_thread = false; + exit_thread.clear(); thread.start(MIDIDriverALSAMidi::thread_func, this); return OK; } void MIDIDriverALSAMidi::close() { - exit_thread = true; + exit_thread.set(); thread.wait_to_finish(); for (int i = 0; i < connected_inputs.size(); i++) { - snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr; snd_rawmidi_close(midi_in); } connected_inputs.clear(); @@ -179,7 +229,7 @@ PackedStringArray MIDIDriverALSAMidi::get_connected_inputs() { lock(); for (int i = 0; i < connected_inputs.size(); i++) { - snd_rawmidi_t *midi_in = connected_inputs[i]; + snd_rawmidi_t *midi_in = connected_inputs[i].rawmidi_ptr; snd_rawmidi_info_t *info; snd_rawmidi_info_malloc(&info); @@ -193,7 +243,7 @@ PackedStringArray MIDIDriverALSAMidi::get_connected_inputs() { } MIDIDriverALSAMidi::MIDIDriverALSAMidi() { - exit_thread = false; + exit_thread.clear(); } MIDIDriverALSAMidi::~MIDIDriverALSAMidi() { diff --git a/drivers/alsamidi/midi_driver_alsamidi.h b/drivers/alsamidi/midi_driver_alsamidi.h index b0fa8c297a..9265dede3d 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.h +++ b/drivers/alsamidi/midi_driver_alsamidi.h @@ -36,6 +36,7 @@ #include "core/os/midi_driver.h" #include "core/os/mutex.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "core/templates/vector.h" #include "../alsa/asound-so_wrap.h" @@ -45,12 +46,48 @@ class MIDIDriverALSAMidi : public MIDIDriver { Thread thread; Mutex mutex; - Vector<snd_rawmidi_t *> connected_inputs; + class InputConnection { + public: + InputConnection() = default; + InputConnection(snd_rawmidi_t *midi_in) : + rawmidi_ptr{ midi_in } {} - bool exit_thread; + // Read in and parse available data, forwarding any complete messages through the driver. + int read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp); + + snd_rawmidi_t *rawmidi_ptr = nullptr; + + private: + static const size_t MSG_BUFFER_SIZE = 3; + uint8_t buffer[MSG_BUFFER_SIZE] = { 0 }; + size_t expected_data = 0; + size_t received_data = 0; + bool skipping_sys_ex = false; + void parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver, uint64_t timestamp); + }; + + Vector<InputConnection> connected_inputs; + + SafeFlag exit_thread; static void thread_func(void *p_udata); + enum class MessageCategory { + Data, + Voice, + SysExBegin, + SystemCommon, // excluding System Exclusive Begin/End + SysExEnd, + RealTime, + }; + + // If the passed byte is a status byte, return the associated message category, + // else return MessageCategory::Data. + static MessageCategory msg_category(uint8_t msg_part); + + // Return the number of data bytes expected for the provided status byte. + static size_t msg_expected_data(uint8_t status_byte); + void lock() const; void unlock() const; diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index 276e69e470..1db85e2a60 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -38,7 +38,7 @@ #define kOutputBus 0 #define kInputBus 1 -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { @@ -72,7 +72,7 @@ Error AudioDriverCoreAudio::init() { AudioComponentDescription desc; memset(&desc, 0, sizeof(desc)); desc.componentType = kAudioUnitType_Output; -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED desc.componentSubType = kAudioUnitSubType_HALOutput; #else desc.componentSubType = kAudioUnitSubType_RemoteIO; @@ -85,7 +85,7 @@ Error AudioDriverCoreAudio::init() { OSStatus result = AudioComponentInstanceNew(comp, &audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -135,7 +135,7 @@ Error AudioDriverCoreAudio::init() { // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_frames = closest_power_of_2(latency * mix_rate / 1000); -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED result = AudioUnitSetProperty(audio_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, kOutputBus, &buffer_frames, sizeof(UInt32)); ERR_FAIL_COND_V(result != noErr, FAILED); #endif @@ -215,6 +215,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, } ad->lock(); + ad->start_counting_ticks(); AudioBufferList bufferList; bufferList.mNumberBuffers = 1; @@ -237,6 +238,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, ERR_PRINT("AudioUnitRender failed, code: " + itos(result)); } + ad->stop_counting_ticks(); ad->unlock(); return result; @@ -313,7 +315,7 @@ void AudioDriverCoreAudio::finish() { ERR_PRINT("AudioUnitUninitialize failed"); } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -339,7 +341,7 @@ Error AudioDriverCoreAudio::capture_init() { AudioComponentDescription desc; memset(&desc, 0, sizeof(desc)); desc.componentType = kAudioUnitType_Output; -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED desc.componentSubType = kAudioUnitSubType_HALOutput; #else desc.componentSubType = kAudioUnitSubType_RemoteIO; @@ -352,7 +354,7 @@ Error AudioDriverCoreAudio::capture_init() { OSStatus result = AudioComponentInstanceNew(comp, &input_unit); ERR_FAIL_COND_V(result != noErr, FAILED); -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -370,7 +372,7 @@ Error AudioDriverCoreAudio::capture_init() { ERR_FAIL_COND_V(result != noErr, FAILED); UInt32 size; -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioDeviceID deviceId; size = sizeof(AudioDeviceID); AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; @@ -447,7 +449,7 @@ void AudioDriverCoreAudio::capture_finish() { ERR_PRINT("AudioUnitUninitialize failed"); } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -491,10 +493,10 @@ Error AudioDriverCoreAudio::capture_stop() { return OK; } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED -Array AudioDriverCoreAudio::_get_device_list(bool capture) { - Array list; +PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { + PackedStringArray list; list.push_back("Default"); @@ -637,7 +639,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } } -Array AudioDriverCoreAudio::get_device_list() { +PackedStringArray AudioDriverCoreAudio::get_device_list() { return _get_device_list(); } @@ -659,7 +661,7 @@ void AudioDriverCoreAudio::capture_set_device(const String &p_name) { } } -Array AudioDriverCoreAudio::capture_get_device_list() { +PackedStringArray AudioDriverCoreAudio::capture_get_device_list() { return _get_device_list(true); } diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index f86037f092..aac5077bb1 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef COREAUDIO_ENABLED - #ifndef AUDIO_DRIVER_COREAUDIO_H #define AUDIO_DRIVER_COREAUDIO_H +#ifdef COREAUDIO_ENABLED + #include "servers/audio_server.h" #import <AudioUnit/AudioUnit.h> -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED #import <CoreAudio/AudioHardware.h> #endif @@ -58,8 +58,8 @@ class AudioDriverCoreAudio : public AudioDriver { Vector<int32_t> samples_in; Vector<int16_t> input_buf; -#ifdef OSX_ENABLED - Array _get_device_list(bool capture = false); +#ifdef MACOS_ENABLED + PackedStringArray _get_device_list(bool capture = false); void _set_device(const String &device, bool capture = false); static OSStatus input_device_address_cb(AudioObjectID inObjectID, @@ -106,12 +106,12 @@ public: bool try_lock(); void stop(); -#ifdef OSX_ENABLED - virtual Array get_device_list(); +#ifdef MACOS_ENABLED + virtual PackedStringArray get_device_list(); virtual String get_device(); virtual void set_device(String device); - virtual Array capture_get_device_list(); + virtual PackedStringArray capture_get_device_list(); virtual void capture_set_device(const String &p_name); virtual String capture_get_device(); #endif @@ -120,6 +120,6 @@ public: ~AudioDriverCoreAudio() {} }; -#endif +#endif // COREAUDIO_ENABLED -#endif +#endif // AUDIO_DRIVER_COREAUDIO_H diff --git a/drivers/coremidi/midi_driver_coremidi.h b/drivers/coremidi/midi_driver_coremidi.h index 0085141c6d..ac2ad99eb3 100644 --- a/drivers/coremidi/midi_driver_coremidi.h +++ b/drivers/coremidi/midi_driver_coremidi.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef COREMIDI_ENABLED - #ifndef MIDI_DRIVER_COREMIDI_H #define MIDI_DRIVER_COREMIDI_H +#ifdef COREMIDI_ENABLED + #include "core/os/midi_driver.h" #include "core/templates/vector.h" @@ -57,5 +57,6 @@ public: virtual ~MIDIDriverCoreMidi(); }; -#endif // MIDI_DRIVER_COREMIDI_H #endif // COREMIDI_ENABLED + +#endif // MIDI_DRIVER_COREMIDI_H diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub index ddeec6f4c6..7e8bd22960 100644 --- a/drivers/gl_context/SCsub +++ b/drivers/gl_context/SCsub @@ -2,7 +2,7 @@ Import("env") -if env["platform"] in ["haiku", "osx", "windows", "linuxbsd"]: +if env["platform"] in ["haiku", "macos", "windows", "linuxbsd"]: # Thirdparty source files thirdparty_dir = "#thirdparty/glad/" thirdparty_sources = [ diff --git a/drivers/gles3/SCsub b/drivers/gles3/SCsub index 5760fd714e..506312df80 100644 --- a/drivers/gles3/SCsub +++ b/drivers/gles3/SCsub @@ -7,3 +7,4 @@ env.add_source_files(env.drivers_sources, "*.cpp") SConscript("shaders/SCsub") SConscript("storage/SCsub") SConscript("effects/SCsub") +SConscript("environment/SCsub") diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index c8e6c2b476..3acbcf6b53 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -111,21 +111,18 @@ CopyEffects::~CopyEffects() { glDeleteVertexArrays(1, &screen_triangle_array); glDeleteBuffers(1, &quad); glDeleteVertexArrays(1, &quad_array); + copy.shader.version_free(copy.shader_version); } -void CopyEffects::copy_to_rect(const Rect2i &p_rect) { +void CopyEffects::copy_to_rect(const Rect2 &p_rect) { copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); - glBindVertexArray(quad_array); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + draw_screen_quad(); } void CopyEffects::copy_screen() { copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); - glBindVertexArray(screen_triangle_array); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); + draw_screen_triangle(); } void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) { @@ -157,8 +154,19 @@ void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) { copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_region.position.x, p_region.position.y, p_region.size.x, p_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); copy.shader.version_set_uniform(CopyShaderGLES3::COLOR_IN, p_color, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR); + draw_screen_quad(); +} + +void CopyEffects::draw_screen_triangle() { + glBindVertexArray(screen_triangle_array); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} + +void CopyEffects::draw_screen_quad() { glBindVertexArray(quad_array); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); } + #endif // GLES3_ENABLED diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index 1cf1ac9404..a817c3f0dc 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef COPY_GL_H -#define COPY_GL_H +#ifndef COPY_EFFECTS_GLES3_H +#define COPY_EFFECTS_GLES3_H #ifdef GLES3_ENABLED @@ -61,13 +61,16 @@ public: ~CopyEffects(); // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. - void copy_to_rect(const Rect2i &p_rect); + void copy_to_rect(const Rect2 &p_rect); void copy_screen(); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); void set_color(const Color &p_color, const Rect2i &p_region); + void draw_screen_triangle(); + void draw_screen_quad(); }; } //namespace GLES3 #endif // GLES3_ENABLED -#endif // !COPY_GL_H + +#endif // COPY_EFFECTS_GLES3_H diff --git a/drivers/gles3/environment/SCsub b/drivers/gles3/environment/SCsub new file mode 100644 index 0000000000..91e1140b75 --- /dev/null +++ b/drivers/gles3/environment/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/gles3/environment/fog.cpp b/drivers/gles3/environment/fog.cpp new file mode 100644 index 0000000000..8587c5f9f7 --- /dev/null +++ b/drivers/gles3/environment/fog.cpp @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* fog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "fog.h" + +using namespace GLES3; + +/* FOG */ + +RID Fog::fog_volume_allocate() { + return RID(); +} + +void Fog::fog_volume_initialize(RID p_rid) { +} + +void Fog::fog_volume_free(RID p_rid) { +} + +void Fog::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { +} + +void Fog::fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) { +} + +void Fog::fog_volume_set_material(RID p_fog_volume, RID p_material) { +} + +AABB Fog::fog_volume_get_aabb(RID p_fog_volume) const { + return AABB(); +} + +RS::FogVolumeShape Fog::fog_volume_get_shape(RID p_fog_volume) const { + return RS::FOG_VOLUME_SHAPE_BOX; +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/environment/fog.h b/drivers/gles3/environment/fog.h new file mode 100644 index 0000000000..fcde7399dd --- /dev/null +++ b/drivers/gles3/environment/fog.h @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* fog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FOG_GLES3_H +#define FOG_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/local_vector.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "servers/rendering/environment/renderer_fog.h" + +namespace GLES3 { + +class Fog : public RendererFog { +public: + /* FOG VOLUMES */ + + virtual RID fog_volume_allocate() override; + virtual void fog_volume_initialize(RID p_rid) override; + virtual void fog_volume_free(RID p_rid) override; + + virtual void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override; + virtual void fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) override; + virtual void fog_volume_set_material(RID p_fog_volume, RID p_material) override; + virtual AABB fog_volume_get_aabb(RID p_fog_volume) const override; + virtual RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override; +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // FOG_GLES3_H diff --git a/drivers/gles3/environment/gi.cpp b/drivers/gles3/environment/gi.cpp new file mode 100644 index 0000000000..5b16d3539f --- /dev/null +++ b/drivers/gles3/environment/gi.cpp @@ -0,0 +1,140 @@ +/*************************************************************************/ +/* gi.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "gi.h" + +using namespace GLES3; + +/* VOXEL GI API */ + +RID GI::voxel_gi_allocate() { + return RID(); +} + +void GI::voxel_gi_free(RID p_rid) { +} + +void GI::voxel_gi_initialize(RID p_rid) { +} + +void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { +} + +AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const { + return AABB(); +} + +Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const { + return Vector3i(); +} + +Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const { + return Vector<uint8_t>(); +} + +Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const { + return Vector<int>(); +} + +Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const { + return Transform3D(); +} + +void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) { +} + +float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const { + return 0; +} + +void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) { +} + +float GI::voxel_gi_get_propagation(RID p_voxel_gi) const { + return 0; +} + +void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_range) { +} + +float GI::voxel_gi_get_energy(RID p_voxel_gi) const { + return 0.0; +} + +void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) { +} + +float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const { + return 1.0; +} + +void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) { +} + +float GI::voxel_gi_get_bias(RID p_voxel_gi) const { + return 0.0; +} + +void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) { +} + +float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const { + return 0.0; +} + +void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) { +} + +bool GI::voxel_gi_is_interior(RID p_voxel_gi) const { + return false; +} + +void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) { +} + +bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const { + return false; +} + +uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const { + return 0; +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/environment/gi.h b/drivers/gles3/environment/gi.h new file mode 100644 index 0000000000..5b0aad380e --- /dev/null +++ b/drivers/gles3/environment/gi.h @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* gi.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GI_GLES3_H +#define GI_GLES3_H + +#ifdef GLES3_ENABLED + +#include "core/templates/local_vector.h" +#include "core/templates/rid_owner.h" +#include "core/templates/self_list.h" +#include "servers/rendering/environment/renderer_gi.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +namespace GLES3 { + +class GI : public RendererGI { +public: + /* VOXEL GI API */ + + virtual RID voxel_gi_allocate() override; + virtual void voxel_gi_free(RID p_rid) override; + virtual void voxel_gi_initialize(RID p_rid) override; + virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override; + + virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override; + virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override; + virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override; + virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override; + virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override; + + virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override; + virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override; + virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override; + virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override; + virtual float voxel_gi_get_energy(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override; + virtual float voxel_gi_get_bias(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override; + virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override; + virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override; + + virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override; + virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override; + + virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override; +}; + +}; // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // GI_GLES3_H diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 32d279a635..0a64cda787 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -34,28 +34,15 @@ #include "core/os/os.h" #include "rasterizer_scene_gles3.h" -#include "rasterizer_storage_gles3.h" #include "core/config/project_settings.h" +#include "core/math/geometry_2d.h" #include "servers/rendering/rendering_server_default.h" #include "storage/config.h" #include "storage/material_storage.h" +#include "storage/mesh_storage.h" #include "storage/texture_storage.h" -#ifndef GLES_OVER_GL -#define glClearDepth glClearDepthf -#endif - -//static const GLenum gl_primitive[] = { -// GL_POINTS, -// GL_LINES, -// GL_LINE_STRIP, -// GL_LINE_LOOP, -// GL_TRIANGLES, -// GL_TRIANGLE_STRIP, -// GL_TRIANGLE_FAN -//}; - void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { p_mat4[0] = p_transform.columns[0][0]; p_mat4[1] = p_transform.columns[0][1]; @@ -115,7 +102,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans p_mat4[15] = 1; } -void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { +void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); @@ -124,9 +111,198 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ // Clear out any state that may have been left from the 3D pass. reset_canvas(); - // TODO: Setup Directional Lights + if (state.canvas_instance_data_buffers[state.current_buffer].fence != GLsync()) { + GLint syncStatus; + glGetSynciv(state.canvas_instance_data_buffers[state.current_buffer].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. + if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { +#ifndef WEB_ENABLED + // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. + glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms +#endif + } else { + // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use + // Allocate a new buffer and use that. + _allocate_instance_data_buffer(); + } + } else { + // Already finished all rendering commands, we can use it. + state.canvas_instance_data_buffers[state.current_buffer].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_buffer].fence); + state.canvas_instance_data_buffers[state.current_buffer].fence = GLsync(); + } + } + + //setup directional lights if exist + + uint32_t light_count = 0; + uint32_t directional_light_count = 0; + { + Light *l = p_directional_light_list; + uint32_t index = 0; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + + Vector2 canvas_light_dir = l->xform_cache.columns[1].normalized(); + + state.light_uniforms[index].position[0] = -canvas_light_dir.x; + state.light_uniforms[index].position[1] = -canvas_light_dir.y; + + _update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height; //0..1 here + + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + if (state.shadow_fb != 0) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + directional_light_count = light_count; + state.using_directional_lights = directional_light_count > 0; + } + + //setup lights if exist + + { + Light *l = p_light_list; + uint32_t index = light_count; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse(); + + Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss + state.light_uniforms[index].position[0] = canvas_light_pos.x; + state.light_uniforms[index].position[1] = canvas_light_pos.y; + + _update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix); + _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height * (p_canvas_transform.columns[0].length() + p_canvas_transform.columns[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + if (state.shadow_fb != 0) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + + if (clight->texture.is_valid()) { + Rect2 atlas_rect = GLES3::TextureStorage::get_singleton()->texture_atlas_get_texture_rect(clight->texture); + state.light_uniforms[index].atlas_rect[0] = atlas_rect.position.x; + state.light_uniforms[index].atlas_rect[1] = atlas_rect.position.y; + state.light_uniforms[index].atlas_rect[2] = atlas_rect.size.width; + state.light_uniforms[index].atlas_rect[3] = atlas_rect.size.height; + + } else { + state.light_uniforms[index].atlas_rect[0] = 0; + state.light_uniforms[index].atlas_rect[1] = 0; + state.light_uniforms[index].atlas_rect[2] = 0; + state.light_uniforms[index].atlas_rect[3] = 0; + } - // TODO: Setup lights + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + } + + if (light_count > 0) { + glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].light_ubo); + +#ifdef WEB_ENABLED + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, state.light_uniforms); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.light_uniforms, sizeof(LightUniform) * light_count); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + + GLuint texture_atlas = texture_storage->texture_atlas_get_texture(); + if (texture_atlas == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); + texture_atlas = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_2D, texture_atlas); + GLuint shadow_tex = state.shadow_texture; + if (shadow_tex == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); + shadow_tex = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, shadow_tex); + } { //update canvas state uniform buffer @@ -135,7 +311,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ Size2i ssize = texture_storage->render_target_get_size(p_to_render_target); Transform3D screen_transform; - screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); + screen_transform.translate_local(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f); screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f)); _update_transform_to_mat4(screen_transform, state_buffer.screen_transform); _update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform); @@ -155,13 +331,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - // TODO: temporary, this should be set at the top of this function - glViewport(0, 0, render_target_size.x, render_target_size.y); - state_buffer.time = state.time; state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; - state_buffer.directional_light_count = 0; //directional_light_count; + state_buffer.directional_light_count = directional_light_count; Vector2 canvas_scale = p_canvas_transform.get_scale(); @@ -180,31 +353,42 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_state_buffer); + + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); - GLuint global_buffer = material_storage->global_variables_get_uniform_buffer(); + GLuint global_buffer = material_storage->global_shader_parameters_get_uniform_buffer(); glBindBufferBase(GL_UNIFORM_BUFFER, GLOBAL_UNIFORM_LOCATION, global_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); } + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D, texture_storage->render_target_get_sdf_texture(p_to_render_target)); + { state.default_filter = p_default_filter; state.default_repeat = p_default_repeat; } + Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target); + glViewport(0, 0, render_target_size.x, render_target_size.y); + r_sdf_used = false; int item_count = 0; bool backbuffer_cleared = false; bool time_used = false; - bool material_screen_texture_found = false; + bool material_screen_texture_cached = false; + bool material_screen_texture_mipmaps_cached = false; Rect2 back_buffer_rect; bool backbuffer_copy = false; + bool backbuffer_gen_mipmaps = false; Item *ci = p_item_list; Item *canvas_group_owner = nullptr; + uint32_t starting_index = 0; + while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { backbuffer_copy = true; @@ -222,9 +406,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ GLES3::CanvasMaterialData *md = static_cast<GLES3::CanvasMaterialData *>(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM)); if (md && md->shader_data->valid) { if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) { - if (!material_screen_texture_found) { + if (!material_screen_texture_cached) { backbuffer_copy = true; back_buffer_rect = Rect2(); + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; + } else if (!material_screen_texture_mipmaps_cached) { + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; } } @@ -240,14 +427,15 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ if (ci->canvas_group_owner != nullptr) { if (canvas_group_owner == nullptr) { // Canvas group begins here, render until before this item - - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; - Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; - - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { + Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -266,7 +454,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (ci == canvas_group_owner) { - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, true); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -274,25 +462,36 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } canvas_group_owner = nullptr; + // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it. + backbuffer_cleared = false; } if (backbuffer_copy) { //render anything pending, including clearing if no items - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; - texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, true); + texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); backbuffer_copy = false; - material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies + backbuffer_gen_mipmaps = false; + material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies. + material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps; + } + + if (backbuffer_gen_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, back_buffer_rect); + + backbuffer_gen_mipmaps = false; + material_screen_texture_mipmaps_cached = true; } // just add all items for now items[item_count++] = ci; if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); //then reset item_count = 0; } @@ -304,78 +503,130 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ RenderingServerDefault::redraw_request(); } + state.canvas_instance_data_buffers[state.current_buffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + // Clear out state used in 2D pass reset_canvas(); + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); } -void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - Item *current_clip = nullptr; - - Transform2D canvas_transform_inverse = p_canvas_transform_inverse; canvas_begin(p_to_render_target, p_to_backbuffer); - RID prev_material; + if (p_item_count <= 0) { + // Nothing to draw, just call canvas_begin() to clear the render target and return. + return; + } + uint32_t index = 0; - GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; - GLES3::CanvasShaderData *shader_data_cache = nullptr; + Item *current_clip = nullptr; + + // Record Batches. + // First item always forms its own batch. + bool batch_broken = false; + _new_batch(batch_broken, index); - state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); - state.current_tex_ptr = nullptr; - state.current_normal = RID(); - state.current_specular = RID(); - state.canvas_texscreen_used = false; - state.current_shader_version = state.canvas_shader_default_version; + // Override the start position and index as we want to start from where we finished off last time. + state.canvas_instance_batches[state.current_batch_index].start = r_last_index * sizeof(InstanceData); + index = 0; + _align_instance_data_buffer(index); for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; - if (current_clip != ci->final_clip_owner) { - _render_batch(index); - + if (ci->final_clip_owner != state.canvas_instance_batches[state.current_batch_index].clip) { + _new_batch(batch_broken, index); + state.canvas_instance_batches[state.current_batch_index].clip = ci->final_clip_owner; current_clip = ci->final_clip_owner; - //setup clip - if (current_clip) { - glEnable(GL_SCISSOR_TEST); - glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y); - } else { - glDisable(GL_SCISSOR_TEST); - } } RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - - if (material.is_null() && ci->canvas_group != nullptr) { - material = default_canvas_group_material; + if (ci->canvas_group != nullptr) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + if (!p_to_backbuffer) { + material = default_clip_children_material; + } + } else { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } } - if (material != prev_material) { - _render_batch(index); + GLES3::CanvasShaderData *shader_data_cache = nullptr; + if (material != state.canvas_instance_batches[state.current_batch_index].material) { + _new_batch(batch_broken, index); + GLES3::CanvasMaterialData *material_data = nullptr; if (material.is_valid()) { material_data = static_cast<GLES3::CanvasMaterialData *>(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM)); } + shader_data_cache = nullptr; if (material_data) { if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { - // Bind uniform buffer and textures - material_data->bind_uniforms(); - state.current_shader_version = material_data->shader_data->version; shader_data_cache = material_data->shader_data; - } else { - state.current_shader_version = state.canvas_shader_default_version; - shader_data_cache = nullptr; } - } else { - state.current_shader_version = state.canvas_shader_default_version; - shader_data_cache = nullptr; } - prev_material = material; + + state.canvas_instance_batches[state.current_batch_index].material = material; + state.canvas_instance_batches[state.current_batch_index].material_data = material_data; } GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX; + _record_item_commands(ci, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken); + } + + if (index == 0) { + // Nothing to render, just return. + state.current_batch_index = 0; + state.canvas_instance_batches.clear(); + return; + } + + // Copy over all data needed for rendering. + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].ubo); +#ifdef WEB_ENABLED + glBufferSubData(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.instance_data_array, index * sizeof(InstanceData)); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif + + glDisable(GL_SCISSOR_TEST); + current_clip = nullptr; + + GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; + + state.current_tex = RID(); + + for (uint32_t i = 0; i <= state.current_batch_index; i++) { + //setup clip + if (current_clip != state.canvas_instance_batches[i].clip) { + current_clip = state.canvas_instance_batches[i].clip; + if (current_clip) { + glEnable(GL_SCISSOR_TEST); + glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y); + } else { + glDisable(GL_SCISSOR_TEST); + } + } + + GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data; + CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant; + uint64_t specialization = 0; + specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); + specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1; + _bind_material(material_data, variant, specialization); + + GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode; + if (last_blend_mode != blend_mode) { if (last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { // re-enable it @@ -388,6 +639,16 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou switch (blend_mode) { case GLES3::CanvasShaderData::BLEND_MODE_DISABLED: { // Nothing to do here. + } break; + case GLES3::CanvasShaderData::BLEND_MODE_LCD: { + glBlendEquation(GL_FUNC_ADD); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); + } + Color blend_color = state.canvas_instance_batches[state.current_batch_index].blend_color; + glBlendColor(blend_color.r, blend_color.g, blend_color.b, blend_color.a); } break; case GLES3::CanvasShaderData::BLEND_MODE_MIX: { @@ -438,38 +699,74 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou last_blend_mode = blend_mode; } - _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index); + _render_batch(p_lights, i); } - // Render last command - _render_batch(index); + + state.current_batch_index = 0; + state.canvas_instance_batches.clear(); + r_last_index += index; } -void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index) { - // Used by Polygon and Mesh. - static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; +void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken) { + RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; - RS::CanvasItemTextureFilter current_filter = state.default_filter; - RS::CanvasItemTextureRepeat current_repeat = state.default_repeat; + if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { + _new_batch(r_batch_broken, r_index); - if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { - current_filter = p_item->texture_filter; + state.canvas_instance_batches[state.current_batch_index].filter = texture_filter; } - if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { - current_repeat = p_item->texture_repeat; + RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat; + + if (texture_repeat != state.canvas_instance_batches[state.current_batch_index].repeat) { + _new_batch(r_batch_broken, r_index); + + state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat; } Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; Transform2D draw_transform; // Used by transform command Color base_color = p_item->final_modulate; - uint32_t base_flags = 0; + Size2 texpixel_size; bool reclip = false; bool skipping = false; + // TODO: consider making lights a per-batch property and then baking light operations in the shader for better performance. + uint32_t lights[4] = { 0, 0, 0, 0 }; + + uint16_t light_count = 0; + + { + Light *light = p_lights; + + while (light) { + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + uint32_t light_index = light->render_index_cache; + lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + + light_count++; + + if (light_count == data.max_lights_per_item) { + break; + } + } + light = light->next_ptr; + } + + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } + + bool lights_disabled = light_count == 0 && !state.using_directional_lights; + + if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; + } + const Item::Command *c = p_item->commands; while (c) { if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { @@ -482,6 +779,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); } + // Zero out most fields. for (int i = 0; i < 4; i++) { state.instance_data_array[r_index].modulation[i] = 0.0; state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; @@ -495,30 +793,52 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].pad[0] = 0.0; state.instance_data_array[r_index].pad[1] = 0.0; + state.instance_data_array[r_index].lights[0] = lights[0]; + state.instance_data_array[r_index].lights[1] = lights[1]; + state.instance_data_array[r_index].lights[2] = lights[2]; + state.instance_data_array[r_index].lights[3] = lights[3]; + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + Color blend_color; + if (c->type == Item::Command::TYPE_RECT) { + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); + if (rect->flags & CANVAS_RECT_LCD) { + p_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; + blend_color = rect->modulate * base_color; + } + } + + if (p_blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].blend_mode = p_blend_mode; + state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color; + } + switch (c->type) { case Item::Command::TYPE_RECT: { const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); - if (rect->flags & CANVAS_RECT_TILE) { - current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + if (rect->flags & CANVAS_RECT_TILE && state.canvas_instance_batches[state.current_batch_index].repeat != RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } - if (rect->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) { - _render_batch(r_index); - - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_RECT; + if (rect->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_RECT) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = rect->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD; } - _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_QUAD); + + _prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); Rect2 src_rect; Rect2 dst_rect; if (rect->texture != RID()) { - src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * state.current_pixel_size, rect->source.size * state.current_pixel_size) : Rect2(0, 0, 1, 1); + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); dst_rect = Rect2(rect->rect.position, rect->rect.size); if (dst_rect.size.width < 0) { @@ -567,6 +887,8 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].msdf[1] = rect->outline; // Outline size. state.instance_data_array[r_index].msdf[2] = 0.f; // Reserved. state.instance_data_array[r_index].msdf[3] = 0.f; // Reserved. + } else if (rect->flags & CANVAS_RECT_LCD) { + state.instance_data_array[r_index].flags |= FLAGS_USE_LCD; } state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r; @@ -584,36 +906,32 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width; state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height; - r_index++; - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); - } + _add_to_batch(r_index, r_batch_broken); } break; case Item::Command::TYPE_NINEPATCH: { const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); - if (np->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_NINEPATCH) { - _render_batch(r_index); - - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_NINEPATCH; + if (np->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_NINEPATCH) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = np->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH; } - //bind textures - _bind_canvas_texture(np->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_NINEPATCH); + _prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); Rect2 src_rect; Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); if (np->texture == RID()) { - state.current_pixel_size = Size2(1, 1); + texpixel_size = Size2(1, 1); src_rect = Rect2(0, 0, 1, 1); } else { if (np->source != Rect2()) { - src_rect = Rect2(np->source.position.x * state.current_pixel_size.width, np->source.position.y * state.current_pixel_size.height, np->source.size.x * state.current_pixel_size.width, np->source.size.y * state.current_pixel_size.height); + src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); state.instance_data_array[r_index].color_texture_pixel_size[0] = 1.0 / np->source.size.width; state.instance_data_array[r_index].color_texture_pixel_size[1] = 1.0 / np->source.size.height; @@ -649,32 +967,26 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].ninepatch_margins[2] = np->margin[SIDE_RIGHT]; state.instance_data_array[r_index].ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; - r_index++; - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); - } + _add_to_batch(r_index, r_batch_broken); // Restore if overridden. - state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x; - state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y; + state.instance_data_array[r_index].color_texture_pixel_size[0] = texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = texpixel_size.y; } break; case Item::Command::TYPE_POLYGON: { const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); - PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); - ERR_CONTINUE(!pb); + // Polygon's can't be batched, so always create a new batch + _new_batch(r_batch_broken, r_index); - if (polygon->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { - _render_batch(r_index); + state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_POLYGON; - } - _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); + _prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); - state.current_primitive = polygon->primitive; state.instance_data_array[r_index].modulation[0] = base_color.r; state.instance_data_array[r_index].modulation[1] = base_color.g; state.instance_data_array[r_index].modulation[2] = base_color.b; @@ -686,30 +998,22 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].ninepatch_margins[j] = 0; } - _bind_instance_data_buffer(1); - glBindVertexArray(pb->vertex_array); - - if (pb->index_buffer != 0) { - glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); - } else { - glDrawArrays(prim[polygon->primitive], 0, pb->count); - } - glBindVertexArray(0); - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); + _add_to_batch(r_index, r_batch_broken); } break; case Item::Command::TYPE_PRIMITIVE: { const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); - if (state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) { - _render_batch(r_index); - state.current_primitive_points = primitive->point_count; - state.current_command = Item::Command::TYPE_PRIMITIVE; + if (primitive->point_count != state.canvas_instance_batches[state.current_batch_index].primitive_points || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_PRIMITIVE) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = primitive->texture; + state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE; } - _bind_canvas_texture(RID(), current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_PRIMITIVE); + + _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j].x; @@ -720,101 +1024,61 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } - r_index++; + + _add_to_batch(r_index, r_batch_broken); + if (primitive->point_count == 4) { - // Reset base data + // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config for (uint32_t j = 0; j < 3; j++) { - //second half of triangle - state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; - state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; - state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + offset].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + offset].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j + offset] * base_color; state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } - r_index++; - } - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); + + _add_to_batch(r_index, r_batch_broken); } } break; case Item::Command::TYPE_MESH: case Item::Command::TYPE_MULTIMESH: case Item::Command::TYPE_PARTICLES: { - GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); - RID mesh; - RID mesh_instance; - RID texture; - Color modulate(1, 1, 1, 1); - uint32_t instance_count = 1; - GLuint multimesh_buffer = 0; - uint32_t multimesh_stride = 0; - uint32_t multimesh_color_offset = 0; - uint32_t multimesh_custom_data_offset = 0; - bool multimesh_uses_color = false; - bool multimesh_uses_custom_data = false; + // Mesh's can't be batched, so always create a new batch + _new_batch(r_batch_broken, r_index); + Color modulate(1, 1, 1, 1); + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; if (c->type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); - mesh = m->mesh; - mesh_instance = m->mesh_instance; - texture = m->texture; - modulate = m->modulate; + state.canvas_instance_batches[state.current_batch_index].tex = m->texture; _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world); + modulate = m->modulate; } else if (c->type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); - RID multimesh = mm->multimesh; - mesh = mesh_storage->multimesh_get_mesh(multimesh); - texture = mm->texture; - - if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { - break; - } - - instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); - - if (instance_count == 0) { - break; + state.canvas_instance_batches[state.current_batch_index].tex = mm->texture; + uint32_t instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(mm->multimesh); + if (instance_count > 1) { + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; } - - multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); - multimesh_stride = mesh_storage->multimesh_get_stride(multimesh); - multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); - multimesh_custom_data_offset = mesh_storage->multimesh_get_custom_data_offset(multimesh); - multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh); - multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + } else if (c->type == Item::Command::TYPE_PARTICLES) { + WARN_PRINT_ONCE("Particles not supported yet, sorry :("); } - // TODO: implement particles here - - if (mesh.is_null()) { - break; - } + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].command_type = c->type; - if (texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) { - _render_batch(r_index); - state.current_primitive_points = 0; - state.current_command = c->type; - } - - _bind_canvas_texture(texture, current_filter, current_repeat, r_index); - if (instance_count == 1) { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); - } else if (instance_count > 1) { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED); - } else { - ERR_PRINT("Must have at least one mesh instance to draw mesh"); - } - - uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r; state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g; @@ -826,80 +1090,9 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[j] = 0; state.instance_data_array[r_index].ninepatch_margins[j] = 0; } - _bind_instance_data_buffer(1); - for (uint32_t j = 0; j < surf_count; j++) { - void *surface = mesh_storage->mesh_get_surface(mesh, j); - - RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); - ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - - GLuint vertex_array_gl = 0; - GLuint index_array_gl = 0; - - uint32_t input_mask = 0; // 2D meshes always use the same vertex format - if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); - } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl); - } - - index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); - bool use_index_buffer = false; - glBindVertexArray(vertex_array_gl); - if (index_array_gl != 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); - use_index_buffer = true; - } - - if (instance_count > 1) { - // Bind instance buffers. - glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); - glEnableVertexAttribArray(5); - glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); - glVertexAttribDivisor(5, 1); - glEnableVertexAttribArray(6); - glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); - glVertexAttribDivisor(6, 1); - - if (multimesh_uses_color) { - glEnableVertexAttribArray(7); - glVertexAttribPointer(7, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float))); - glVertexAttribDivisor(7, 1); - } - if (multimesh_uses_custom_data) { - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_custom_data_offset * sizeof(float))); - glVertexAttribDivisor(8, 1); - } - } - - GLenum primitive_gl = prim[int(primitive)]; - if (instance_count == 1) { - if (use_index_buffer) { - glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0); - } else { - glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)); - } - } else if (instance_count > 1) { - if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); - } else { - glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); - } - } - - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - if (instance_count > 1) { - glDisableVertexAttribArray(5); - glDisableVertexAttribArray(6); - glDisableVertexAttribArray(7); - glDisableVertexAttribArray(8); - } - } + _add_to_batch(r_index, r_batch_broken); } break; + case Item::Command::TYPE_TRANSFORM: { const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); draw_transform = transform->xform; @@ -909,30 +1102,30 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); if (current_clip) { if (ci->ignore != reclip) { + _new_batch(r_batch_broken, r_index); if (ci->ignore) { - glDisable(GL_SCISSOR_TEST); + state.canvas_instance_batches[state.current_batch_index].clip = nullptr; reclip = true; } else { - // Scissor area is already set - glEnable(GL_SCISSOR_TEST); + state.canvas_instance_batches[state.current_batch_index].clip = current_clip; reclip = false; } } } } break; + case Item::Command::TYPE_ANIMATION_SLICE: { - /* const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); - double current_time = RendererCompositorRD::singleton->get_total_time(); + double current_time = RSG::rasterizer->get_total_time(); double local_time = Math::fposmod(current_time - as->offset, as->animation_length); skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); RenderingServerDefault::redraw_request(); // animation visible means redraw request - */ } break; } c = c->next; + r_batch_broken = false; } if (current_clip && reclip) { @@ -941,101 +1134,795 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } } -void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { - if (r_index > 0) { - _bind_instance_data_buffer(r_index); - glBindVertexArray(data.canvas_quad_array); - if (state.current_primitive_points == 0) { - glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index); - } else { - static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; - glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index); - } - glBindBuffer(GL_UNIFORM_BUFFER, 0); +void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { + ERR_FAIL_COND(!state.canvas_instance_batches[state.current_batch_index].command); - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); - //copy the new data into the base of the batch - for (int i = 0; i < 4; i++) { - state.instance_data_array[0].modulation[i] = state.instance_data_array[r_index].modulation[i]; - state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i]; - state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i]; - state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i]; - state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i]; - } - state.instance_data_array[0].flags = state.instance_data_array[r_index].flags; - state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0]; - state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1]; - - state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0]; - state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1]; - for (int i = 0; i < 6; i++) { - state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i]; - } + // Used by Polygon and Mesh. + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + + _bind_canvas_texture(state.canvas_instance_batches[p_index].tex, state.canvas_instance_batches[p_index].filter, state.canvas_instance_batches[p_index].repeat); - r_index = 0; + // Bind the region of the UBO used by this batch. + // If region exceeds the boundary of the UBO, just ignore. + uint32_t range_bytes = data.max_instances_per_batch * sizeof(InstanceData); + if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - 1) * sizeof(InstanceData)) { + return; + } else if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - data.max_instances_per_batch) * sizeof(InstanceData)) { + // If we have less than a full batch at the end, we can just draw it anyway. + // OpenGL will complain about the UBO being smaller than expected, but it should render fine. + range_bytes = (data.max_instances_per_ubo - 1) * sizeof(InstanceData) - state.canvas_instance_batches[p_index].start; + } + + uint32_t range_start = state.canvas_instance_batches[p_index].start; + glBindBufferRange(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].ubo, range_start, range_bytes); + + switch (state.canvas_instance_batches[p_index].command_type) { + case Item::Command::TYPE_RECT: + case Item::Command::TYPE_NINEPATCH: { + glBindVertexArray(data.indexed_quad_array); + glDrawElements(GL_TRIANGLES, state.canvas_instance_batches[p_index].instance_count * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindVertexArray(0); + + } break; + + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(state.canvas_instance_batches[p_index].command); + + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_FAIL_COND(!pb); + + glBindVertexArray(pb->vertex_array); + + if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { + glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a); + } + + if (pb->index_buffer != 0) { + glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); + } else { + glDrawArrays(prim[polygon->primitive], 0, pb->count); + } + glBindVertexArray(0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { + // Reset so this doesn't pollute other draw calls. + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + } + } break; + + case Item::Command::TYPE_PRIMITIVE: { + glBindVertexArray(data.canvas_quad_array); + const GLenum primitive[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; + int instance_count = state.canvas_instance_batches[p_index].instance_count; + if (instance_count > 1) { + glDrawArraysInstanced(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points, instance_count); + } else { + glDrawArrays(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + } break; + + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + RID mesh; + RID mesh_instance; + RID texture; + uint32_t instance_count = 1; + GLuint multimesh_buffer = 0; + uint32_t multimesh_stride = 0; + uint32_t multimesh_color_offset = 0; + bool multimesh_uses_color = false; + bool multimesh_uses_custom_data = false; + + if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(state.canvas_instance_batches[p_index].command); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(state.canvas_instance_batches[p_index].command); + RID multimesh = mm->multimesh; + mesh = mesh_storage->multimesh_get_mesh(multimesh); + + if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } + + instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); + + if (instance_count == 0) { + break; + } + + multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); + multimesh_stride = mesh_storage->multimesh_get_stride(multimesh); + multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); + multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh); + multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_PARTICLES) { + // Do nothing for now. + } + + ERR_FAIL_COND(mesh.is_null()); + + uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + + for (uint32_t j = 0; j < surf_count; j++) { + void *surface = mesh_storage->mesh_get_surface(mesh, j); + + RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); + + GLuint vertex_array_gl = 0; + GLuint index_array_gl = 0; + + uint32_t input_mask = 0; // 2D meshes always use the same vertex format + if (mesh_instance.is_valid()) { + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); + } else { + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl); + } + + index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); + bool use_index_buffer = false; + glBindVertexArray(vertex_array_gl); + if (index_array_gl != 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); + use_index_buffer = true; + } + + if (instance_count > 1) { + // Bind instance buffers. + glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribDivisor(1, 1); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribDivisor(2, 1); + + if (multimesh_uses_color || multimesh_uses_custom_data) { + glEnableVertexAttribArray(5); + glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float))); + glVertexAttribDivisor(5, 1); + } + } + + GLenum primitive_gl = prim[int(primitive)]; + if (instance_count == 1) { + if (use_index_buffer) { + glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0); + } else { + glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)); + } + } else if (instance_count > 1) { + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); + } + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (instance_count > 1) { + glDisableVertexAttribArray(5); + glDisableVertexAttribArray(6); + glDisableVertexAttribArray(7); + glDisableVertexAttribArray(8); + } + } + + } break; + case Item::Command::TYPE_TRANSFORM: + case Item::Command::TYPE_CLIP_IGNORE: + case Item::Command::TYPE_ANIMATION_SLICE: { + // Can ignore these as they only impact batch creation. + } break; } } -void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) { - if (p_max_index == 0) { +void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { + if (r_index >= data.max_instances_per_ubo - 1) { + ERR_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'"); return; } - // If the previous operation is not done yet, allocate a new buffer - if (state.fences[state.current_buffer] != GLsync()) { - GLint syncStatus; - glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); - if (syncStatus == GL_UNSIGNALED) { - _allocate_instance_data_buffer(); + + if (state.canvas_instance_batches[state.current_batch_index].instance_count >= data.max_instances_per_batch) { + _new_batch(r_batch_broken, r_index); + } + + state.canvas_instance_batches[state.current_batch_index].instance_count++; + r_index++; +} + +void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) { + if (state.canvas_instance_batches.size() == 0) { + state.canvas_instance_batches.push_back(Batch()); + return; + } + + if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) { + return; + } + + r_batch_broken = true; + + // Copy the properties of the current batch, we will manually update the things that changed. + Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; + new_batch.instance_count = 0; + new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count * sizeof(InstanceData); + + state.current_batch_index++; + state.canvas_instance_batches.push_back(new_batch); + _align_instance_data_buffer(r_index); +} + +void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { + if (p_material_data) { + if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) { + // Bind uniform buffer and textures + p_material_data->bind_uniforms(); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); } else { - glDeleteSync(state.fences[state.current_buffer]); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } + } else { + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } - - glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]); -#ifdef JAVASCRIPT_ENABLED - //WebGL 2.0 does not support mapping buffers, so use slow glBufferData instead - glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * p_max_index, state.instance_data_array, GL_DYNAMIC_DRAW); -#else - void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * p_max_index); - glUnmapBuffer(GL_UNIFORM_BUFFER); -#endif } RID RasterizerCanvasGLES3::light_create() { - return RID(); + CanvasLight canvas_light; + return canvas_light_owner.make_rid(canvas_light); } void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl); + if (cl->texture == p_texture) { + return; + } + if (cl->texture.is_valid()) { + texture_storage->texture_remove_from_texture_atlas(cl->texture); + } + cl->texture = p_texture; + + if (cl->texture.is_valid()) { + texture_storage->texture_add_to_texture_atlas(cl->texture); + } } void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl); + + cl->shadow.enabled = p_enable; } void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) { + GLES3::Config *config = GLES3::Config::get_singleton(); + + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl->shadow.enabled); + + _update_shadow_atlas(); + + cl->shadow.z_far = p_far; + cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(data.max_lights_per_render * 2); + + glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb); + glViewport(0, p_shadow_index * 2, state.shadow_texture_size, 2); + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDisable(GL_BLEND); + + glEnable(GL_SCISSOR_TEST); + glScissor(0, p_shadow_index * 2, state.shadow_texture_size, 2); + glClearColor(p_far, p_far, p_far, 1.0); + glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + + CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA; + shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + + for (int i = 0; i < 4; i++) { + glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); + + Projection projection; + { + real_t fov = 90; + real_t nearp = p_near; + real_t farp = p_far; + real_t aspect = 1.0; + + real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); + real_t ymin = -ymax; + real_t xmin = ymin * aspect; + real_t xmax = ymax * aspect; + + projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + } + + Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + + projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant); + + static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, p_far, shadow_render.shader_version, variant); + + LightOccluderInstance *instance = p_occluders; + + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + + if (!co || co->vertex_array == 0 || !(p_light_mask & instance->light_mask)) { + instance = instance->next; + continue; + } + + Transform2D modelview = p_light_xform * instance->xform_cache; + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant); + + if (co->cull_mode != cull_mode) { + if (co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) { + glDisable(GL_CULL_FACE); + } else { + if (cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) { + // Last time was disabled, so enable and set proper face. + glEnable(GL_CULL_FACE); + } + glCullFace(co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE ? GL_FRONT : GL_BACK); + } + cull_mode = co->cull_mode; + } + + glBindVertexArray(co->vertex_array); + glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0); + + instance = instance->next; + } + } + + glBindVertexArray(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); } void RasterizerCanvasGLES3::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) { + GLES3::Config *config = GLES3::Config::get_singleton(); + + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl->shadow.enabled); + + _update_shadow_atlas(); + + Vector2 light_dir = p_light_xform.columns[1].normalized(); + + Vector2 center = p_clip_rect.get_center(); + + float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center)); + + Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance); + float distance = to_edge_distance * 2.0 + p_cull_distance; + float half_size = p_clip_rect.size.length() * 0.5; //shadow length, must keep this no matter the angle + + cl->shadow.z_far = distance; + cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(data.max_lights_per_render * 2); + + Transform2D to_light_xform; + + to_light_xform[2] = from_pos; + to_light_xform[1] = light_dir; + to_light_xform[0] = -light_dir.orthogonal(); + + to_light_xform.invert(); + + glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb); + glViewport(0, p_shadow_index * 2, state.shadow_texture_size, 2); + + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDisable(GL_BLEND); + + glEnable(GL_SCISSOR_TEST); + glScissor(0, p_shadow_index * 2, state.shadow_texture_size, 2); + glClearColor(1.0, 1.0, 1.0, 1.0); + glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); + RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + + CanvasOcclusionShaderGLES3::ShaderVariant variant = config->float_texture_supported ? CanvasOcclusionShaderGLES3::MODE_SHADOW : CanvasOcclusionShaderGLES3::MODE_SHADOW_RGBA; + shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + + Projection projection; + projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); + projection = projection * Projection(Transform3D().looking_at(Vector3(0, 1, 0), Vector3(0, 0, -1)).affine_inverse()); + + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, 0.0, 1.0, shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, distance, shadow_render.shader_version, variant); + + LightOccluderInstance *instance = p_occluders; + + while (instance) { + OccluderPolygon *co = occluder_polygon_owner.get_or_null(instance->occluder); + + if (!co || co->vertex_array == 0 || !(p_light_mask & instance->light_mask)) { + instance = instance->next; + continue; + } + + Transform2D modelview = to_light_xform * instance->xform_cache; + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant); + + if (co->cull_mode != cull_mode) { + if (co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) { + glDisable(GL_CULL_FACE); + } else { + if (cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED) { + // Last time was disabled, so enable and set proper face. + glEnable(GL_CULL_FACE); + } + glCullFace(co->cull_mode == RS::CANVAS_OCCLUDER_POLYGON_CULL_CLOCKWISE ? GL_FRONT : GL_BACK); + } + cull_mode = co->cull_mode; + } + + glBindVertexArray(co->vertex_array); + glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0); + + instance = instance->next; + } + + Transform2D to_shadow; + to_shadow.columns[0].x = 1.0 / -(half_size * 2.0); + to_shadow.columns[2].x = 0.5; + + cl->shadow.directional_xform = to_shadow * to_light_xform; + + glBindVertexArray(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_CULL_FACE); +} + +void RasterizerCanvasGLES3::_update_shadow_atlas() { + GLES3::Config *config = GLES3::Config::get_singleton(); + + if (state.shadow_fb == 0) { + glActiveTexture(GL_TEXTURE0); + + glGenFramebuffers(1, &state.shadow_fb); + glBindFramebuffer(GL_FRAMEBUFFER, state.shadow_fb); + + glGenRenderbuffers(1, &state.shadow_depth_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, state.shadow_depth_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, state.shadow_texture_size, data.max_lights_per_render * 2); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, state.shadow_depth_buffer); + + glGenTextures(1, &state.shadow_texture); + glBindTexture(GL_TEXTURE_2D, state.shadow_texture); + if (config->float_texture_supported) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, state.shadow_texture_size, data.max_lights_per_render * 2, 0, GL_RED, GL_FLOAT, nullptr); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, state.shadow_texture_size, data.max_lights_per_render * 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, state.shadow_texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &state.shadow_fb); + glDeleteTextures(1, &state.shadow_texture); + glDeleteRenderbuffers(1, &state.shadow_depth_buffer); + state.shadow_fb = 0; + state.shadow_texture = 0; + state.shadow_depth_buffer = 0; + WARN_PRINT("Could not create CanvasItem shadow atlas, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } + glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); + } } void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + GLuint fb = texture_storage->render_target_get_sdf_framebuffer(p_render_target); + Rect2i rect = texture_storage->render_target_get_sdf_rect(p_render_target); + + Transform2D to_sdf; + to_sdf.columns[0] *= rect.size.width; + to_sdf.columns[1] *= rect.size.height; + to_sdf.columns[2] = rect.position; + + Transform2D to_clip; + to_clip.columns[0] *= 2.0; + to_clip.columns[1] *= 2.0; + to_clip.columns[2] = -Vector2(1.0, 1.0); + + to_clip = to_clip * to_sdf.affine_inverse(); + + glBindFramebuffer(GL_FRAMEBUFFER, fb); + glViewport(0, 0, rect.size.width, rect.size.height); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + CanvasOcclusionShaderGLES3::ShaderVariant variant = CanvasOcclusionShaderGLES3::MODE_SDF; + shadow_render.shader.version_bind_shader(shadow_render.shader_version, variant); + + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, Projection(), shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, 0.0, 0.0, shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::Z_FAR, 0.0, shadow_render.shader_version, variant); + + LightOccluderInstance *instance = p_occluders; + + while (instance) { + OccluderPolygon *oc = occluder_polygon_owner.get_or_null(instance->occluder); + + if (!oc || oc->sdf_vertex_array == 0) { + instance = instance->next; + continue; + } + + Transform2D modelview = to_clip * instance->xform_cache; + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW1, modelview.columns[0][0], modelview.columns[1][0], 0, modelview.columns[2][0], shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant); + + glBindVertexArray(oc->sdf_vertex_array); + glDrawElements(oc->sdf_is_lines ? GL_LINES : GL_TRIANGLES, oc->sdf_index_count, GL_UNSIGNED_INT, 0); + + instance = instance->next; + } + + texture_storage->render_target_sdf_process(p_render_target); //done rendering, process it + glBindVertexArray(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } RID RasterizerCanvasGLES3::occluder_polygon_create() { - return RID(); + OccluderPolygon occluder; + + return occluder_polygon_owner.make_rid(occluder); } void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) { + OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); + ERR_FAIL_COND(!oc); + + Vector<Vector2> lines; + + if (p_points.size()) { + int lc = p_points.size() * 2; + + lines.resize(lc - (p_closed ? 0 : 2)); + { + Vector2 *w = lines.ptrw(); + const Vector2 *r = p_points.ptr(); + + int max = lc / 2; + if (!p_closed) { + max--; + } + for (int i = 0; i < max; i++) { + Vector2 a = r[i]; + Vector2 b = r[(i + 1) % (lc / 2)]; + w[i * 2 + 0] = a; + w[i * 2 + 1] = b; + } + } + } + + if (oc->line_point_count != lines.size() && oc->vertex_array != 0) { + glDeleteVertexArrays(1, &oc->vertex_array); + glDeleteBuffers(1, &oc->vertex_buffer); + glDeleteBuffers(1, &oc->index_buffer); + + oc->vertex_array = 0; + oc->vertex_buffer = 0; + oc->index_buffer = 0; + } + + if (lines.size()) { + Vector<uint8_t> geometry; + Vector<uint8_t> indices; + int lc = lines.size(); + + geometry.resize(lc * 6 * sizeof(float)); + indices.resize(lc * 3 * sizeof(uint16_t)); + + { + uint8_t *vw = geometry.ptrw(); + float *vwptr = reinterpret_cast<float *>(vw); + uint8_t *iw = indices.ptrw(); + uint16_t *iwptr = (uint16_t *)iw; + + const Vector2 *lr = lines.ptr(); + + const int POLY_HEIGHT = 16384; + + for (int i = 0; i < lc / 2; i++) { + vwptr[i * 12 + 0] = lr[i * 2 + 0].x; + vwptr[i * 12 + 1] = lr[i * 2 + 0].y; + vwptr[i * 12 + 2] = POLY_HEIGHT; + + vwptr[i * 12 + 3] = lr[i * 2 + 1].x; + vwptr[i * 12 + 4] = lr[i * 2 + 1].y; + vwptr[i * 12 + 5] = POLY_HEIGHT; + + vwptr[i * 12 + 6] = lr[i * 2 + 1].x; + vwptr[i * 12 + 7] = lr[i * 2 + 1].y; + vwptr[i * 12 + 8] = -POLY_HEIGHT; + + vwptr[i * 12 + 9] = lr[i * 2 + 0].x; + vwptr[i * 12 + 10] = lr[i * 2 + 0].y; + vwptr[i * 12 + 11] = -POLY_HEIGHT; + + iwptr[i * 6 + 0] = i * 4 + 0; + iwptr[i * 6 + 1] = i * 4 + 1; + iwptr[i * 6 + 2] = i * 4 + 2; + + iwptr[i * 6 + 3] = i * 4 + 2; + iwptr[i * 6 + 4] = i * 4 + 3; + iwptr[i * 6 + 5] = i * 4 + 0; + } + } + + if (oc->vertex_array == 0) { + oc->line_point_count = lc; + glGenVertexArrays(1, &oc->vertex_array); + glBindVertexArray(oc->vertex_array); + glGenBuffers(1, &oc->vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, oc->vertex_buffer); + + glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(float), geometry.ptr(), GL_STATIC_DRAW); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr); + + glGenBuffers(1, &oc->index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * lc * sizeof(uint16_t), indices.ptr(), GL_STATIC_DRAW); + glBindVertexArray(0); + } else { + glBindVertexArray(oc->vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, oc->vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(float), geometry.ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * lc * sizeof(uint16_t), indices.ptr(), GL_STATIC_DRAW); + } + } + + // sdf + + Vector<int> sdf_indices; + + if (p_points.size()) { + if (p_closed) { + sdf_indices = Geometry2D::triangulate_polygon(p_points); + oc->sdf_is_lines = false; + } else { + int max = p_points.size(); + sdf_indices.resize(max * 2); + + int *iw = sdf_indices.ptrw(); + for (int i = 0; i < max; i++) { + iw[i * 2 + 0] = i; + iw[i * 2 + 1] = (i + 1) % max; + } + oc->sdf_is_lines = true; + } + } + + if (oc->sdf_index_count != sdf_indices.size() && oc->sdf_point_count != p_points.size() && oc->sdf_vertex_array != 0) { + glDeleteVertexArrays(1, &oc->sdf_vertex_array); + glDeleteBuffers(1, &oc->sdf_vertex_buffer); + glDeleteBuffers(1, &oc->sdf_index_buffer); + + oc->sdf_vertex_array = 0; + oc->sdf_vertex_buffer = 0; + oc->sdf_index_buffer = 0; + + oc->sdf_index_count = sdf_indices.size(); + oc->sdf_point_count = p_points.size(); + } + + if (sdf_indices.size()) { + if (oc->sdf_vertex_array == 0) { + oc->sdf_index_count = sdf_indices.size(); + oc->sdf_point_count = p_points.size(); + glGenVertexArrays(1, &oc->sdf_vertex_array); + glBindVertexArray(oc->sdf_vertex_array); + glGenBuffers(1, &oc->sdf_vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer); + + glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), nullptr); + + glGenBuffers(1, &oc->sdf_index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW); + glBindVertexArray(0); + } else { + glBindBuffer(GL_ARRAY_BUFFER, oc->sdf_vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, p_points.size() * 2 * sizeof(float), p_points.to_byte_array().ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oc->sdf_index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sdf_indices.size() * sizeof(uint32_t), sdf_indices.to_byte_array().ptr(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + } } void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) { + OccluderPolygon *oc = occluder_polygon_owner.get_or_null(p_occluder); + ERR_FAIL_COND(!oc); + oc->cull_mode = p_mode; } void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { + GLES3::Config *config = GLES3::Config::get_singleton(); + p_size = nearest_power_of_2_templated(p_size); + if (p_size == state.shadow_texture_size) { + return; + } + + if (p_size > config->max_texture_size) { + p_size = config->max_texture_size; + WARN_PRINT("Attempting to set CanvasItem shadow atlas size to " + itos(p_size) + " which is beyond limit of " + itos(config->max_texture_size) + "supported by hardware."); + } + + state.shadow_texture_size = p_size; } bool RasterizerCanvasGLES3::free(RID p_rid) { + if (canvas_light_owner.owns(p_rid)) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND_V(!cl, false); + canvas_light_owner.free(p_rid); + } else if (occluder_polygon_owner.owns(p_rid)) { + occluder_polygon_set_shape(p_rid, Vector<Vector2>(), false); + occluder_polygon_owner.free(p_rid); + } else { + return false; + } + return true; } @@ -1059,12 +1946,12 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb glBindTexture(GL_TEXTURE_2D, render_target->backbuffer); } - if (render_target->is_transparent) { + if (render_target->is_transparent || p_to_backbuffer) { state.transparent_render_target = true; glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { state.transparent_render_target = false; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); } if (render_target && render_target->clear_requested) { @@ -1080,30 +1967,28 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb glBindTexture(GL_TEXTURE_2D, tex->tex_id); } -void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index) { +void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); if (p_texture == RID()) { - p_texture = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); + p_texture = default_canvas_texture; } - if (state.current_tex == p_texture) { - return; //nothing to do, its the same + if (state.current_tex == p_texture && state.current_filter_mode == p_base_filter && state.current_repeat_mode == p_base_repeat) { + return; } + state.current_tex = p_texture; + state.current_filter_mode = p_base_filter; + state.current_repeat_mode = p_base_repeat; GLES3::CanvasTexture *ct = nullptr; GLES3::Texture *t = texture_storage->get_texture(p_texture); if (t) { - //regular texture - if (!t->canvas_texture) { - t->canvas_texture = memnew(GLES3::CanvasTexture); - t->canvas_texture->diffuse = p_texture; - } - + ERR_FAIL_COND(!t->canvas_texture); ct = t->canvas_texture; } else { ct = texture_storage->get_canvas_texture(p_texture); @@ -1111,7 +1996,7 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe if (!ct) { // Invalid Texture RID. - _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index); + _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat); return; } @@ -1124,20 +2009,12 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); if (!texture) { - state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); - GLES3::Texture *tex = texture_storage->get_texture(state.current_tex); - state.current_tex_ptr = tex; - ct->size_cache = Size2i(tex->width, tex->height); + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex->tex_id); } else { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture->tex_id); - - state.current_tex = p_texture; - state.current_tex_ptr = texture; - ct->size_cache = Size2i(texture->width, texture->height); - texture->gl_set_filter(filter); texture->gl_set_repeat(repeat); } @@ -1145,45 +2022,78 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); if (!normal_map) { - state.current_normal = RID(); - ct->use_normal_cache = false; - glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_NORMAL)); glBindTexture(GL_TEXTURE_2D, tex->tex_id); - } else { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); - state.current_normal = ct->normal_map; - ct->use_normal_cache = true; - texture->gl_set_filter(filter); - texture->gl_set_repeat(repeat); + normal_map->gl_set_filter(filter); + normal_map->gl_set_repeat(repeat); } GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular); if (!specular_map) { - state.current_specular = RID(); - ct->use_specular_cache = false; glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); glBindTexture(GL_TEXTURE_2D, tex->tex_id); } else { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); glBindTexture(GL_TEXTURE_2D, specular_map->tex_id); - state.current_specular = ct->specular; - ct->use_specular_cache = true; - texture->gl_set_filter(filter); - texture->gl_set_repeat(repeat); + specular_map->gl_set_filter(filter); + specular_map->gl_set_repeat(repeat); + } +} + +void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + if (p_texture == RID()) { + p_texture = default_canvas_texture; } - if (ct->use_specular_cache) { + GLES3::CanvasTexture *ct = nullptr; + + GLES3::Texture *t = texture_storage->get_texture(p_texture); + + if (t) { + //regular texture + if (!t->canvas_texture) { + t->canvas_texture = memnew(GLES3::CanvasTexture); + t->canvas_texture->diffuse = p_texture; + } + + ct = t->canvas_texture; + } else { + ct = texture_storage->get_canvas_texture(p_texture); + } + + if (!ct) { + // Invalid Texture RID. + _prepare_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_texpixel_size); + return; + } + + GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); + Size2i size_cache; + if (!texture) { + ct->diffuse = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); + GLES3::Texture *tex = texture_storage->get_texture(ct->diffuse); + size_cache = Size2i(tex->width, tex->height); + } else { + size_cache = Size2i(texture->width, texture->height); + } + + GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); + + if (ct->specular_color.a < 0.999) { state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; } else { state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; } - if (ct->use_normal_cache) { + if (normal_map) { state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; } else { state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; @@ -1194,11 +2104,11 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8; state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255)); - state.current_pixel_size.x = 1.0 / float(ct->size_cache.x); - state.current_pixel_size.y = 1.0 / float(ct->size_cache.y); + r_texpixel_size.x = 1.0 / float(size_cache.x); + r_texpixel_size.y = 1.0 / float(size_cache.y); - state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x; - state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y; + state.instance_data_array[r_index].color_texture_pixel_size[0] = r_texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = r_texpixel_size.y; } void RasterizerCanvasGLES3::reset_canvas() { @@ -1208,16 +2118,16 @@ void RasterizerCanvasGLES3::reset_canvas() { glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 3); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } -void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) { -} - -void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) { -} - void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) { } @@ -1248,7 +2158,6 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec polygon_buffer.resize(buffer_size * sizeof(float)); { glBindBuffer(GL_ARRAY_BUFFER, pb.vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, stride * vertex_count * sizeof(float), nullptr, GL_STATIC_DRAW); // TODO may not be necessary uint8_t *r = polygon_buffer.ptrw(); float *fptr = reinterpret_cast<float *>(r); uint32_t *uptr = (uint32_t *)r; @@ -1268,11 +2177,7 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec } // Next add colors - if (p_colors.size() == 1) { - glDisableVertexAttribArray(RS::ARRAY_COLOR); - Color m = p_colors[0]; - glVertexAttrib4f(RS::ARRAY_COLOR, m.r, m.g, m.b, m.a); - } else if ((uint32_t)p_colors.size() == vertex_count) { + if ((uint32_t)p_colors.size() == vertex_count) { glEnableVertexAttribArray(RS::ARRAY_COLOR); glVertexAttribPointer(RS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float))); @@ -1287,7 +2192,8 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec base_offset += 4; } else { glDisableVertexAttribArray(RS::ARRAY_COLOR); - glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + pb.color_disabled = true; + pb.color = p_colors.size() == 1 ? p_colors[0] : Color(1.0, 1.0, 1.0, 1.0); } if ((uint32_t)p_uvs.size() == vertex_count) { @@ -1360,7 +2266,6 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec } glGenBuffers(1, &pb.index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pb.index_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, nullptr, GL_STATIC_DRAW); // TODO may not be necessary glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, index_buffer.ptr(), GL_STATIC_DRAW); pb.count = p_indices.size(); } @@ -1393,20 +2298,53 @@ void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { // Creates a new uniform buffer and uses it right away // This expands the instance buffer continually -// In theory allocations can reach as high as number_of_draw_calls * 3 frames +// In theory allocations can reach as high as number of windows * 3 frames // because OpenGL can start rendering subsequent frames before finishing the current one void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { - GLuint new_buffer; - glGenBuffers(1, &new_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + state.current_buffer = (state.current_buffer + 1); - state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer); - state.fences.insert(state.current_buffer, GLsync()); + DataBuffer db; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; + db.last_frame_used = RSG::rasterizer->get_frame_number(); + state.canvas_instance_data_buffers.insert(state.current_buffer, db); state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +// Batch start positions need to be aligned to the device's GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT. +// This needs to be called anytime a new batch is created. +void RasterizerCanvasGLES3::_align_instance_data_buffer(uint32_t &r_index) { + if (GLES3::Config::get_singleton()->uniform_buffer_offset_alignment > int(sizeof(InstanceData))) { + uint32_t offset = state.canvas_instance_batches[state.current_batch_index].start % GLES3::Config::get_singleton()->uniform_buffer_offset_alignment; + if (offset > 0) { + // uniform_buffer_offset_alignment can be 4, 16, 32, or 256. Our instance batches are 128 bytes. + // Accordingly, this branch is only triggered if we are 128 bytes off. + uint32_t offset_bytes = GLES3::Config::get_singleton()->uniform_buffer_offset_alignment - offset; + state.canvas_instance_batches[state.current_batch_index].start += offset_bytes; + // Offset the instance array so it stays in sync with batch start points. + // This creates gaps in the instance buffer with wasted space, but we can't help it. + r_index += offset_bytes / sizeof(InstanceData); + if (r_index > 0) { + // In this case we need to copy over the basic data. + state.instance_data_array[r_index] = state.instance_data_array[r_index - 1]; + } + } + } +} + void RasterizerCanvasGLES3::set_time(double p_time) { state.time = p_time; } @@ -1417,12 +2355,13 @@ RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() { return singleton; } -RasterizerCanvasGLES3::RasterizerCanvasGLES3(RasterizerStorageGLES3 *p_storage) { +RasterizerCanvasGLES3::RasterizerCanvasGLES3() { singleton = this; - storage = p_storage; + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); + polygon_buffers.last_id = 1; // quad buffer { glGenBuffers(1, &data.canvas_quad_vertices); @@ -1546,39 +2485,80 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3(RasterizerStorageGLES3 *p_storage) int uniform_max_size = config->max_uniform_buffer_size; if (uniform_max_size < 65536) { - state.max_lights_per_render = 64; - state.max_instances_per_batch = 128; + data.max_lights_per_render = 64; + data.max_instances_per_batch = 128; } else { - state.max_lights_per_render = 256; - state.max_instances_per_batch = 512; + data.max_lights_per_render = 256; + data.max_instances_per_batch = 512; } - // Reserve 64 Uniform Buffers for instance data - state.canvas_instance_data_buffers.resize(64); - state.fences.resize(64); - glGenBuffers(64, state.canvas_instance_data_buffers.ptr()); - for (int i = 0; i < 64; i++) { - state.fences[i] = GLsync(); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[i]); - glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2 + data.max_instances_per_ubo = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); + data.max_instance_buffer_size = data.max_instances_per_ubo * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb + state.canvas_instance_data_buffers.resize(3); + state.canvas_instance_batches.reserve(200); + + for (int i = 0; i < 3; i++) { + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + DataBuffer db; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; + db.last_frame_used = 0; + db.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.canvas_instance_data_buffers[i] = db; } glBindBuffer(GL_UNIFORM_BUFFER, 0); - state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch); + state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_ubo); + state.light_uniforms = memnew_arr(LightUniform, data.max_lights_per_render); - glGenBuffers(1, &state.canvas_state_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + { + const uint32_t no_of_instances = data.max_instances_per_batch; + + glGenVertexArrays(1, &data.indexed_quad_array); + glBindVertexArray(data.indexed_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); + + const uint32_t num_indices = 6; + const uint32_t quad_indices[num_indices] = { 0, 2, 1, 3, 2, 0 }; + + const uint32_t total_indices = no_of_instances * num_indices; + uint32_t *indices = new uint32_t[total_indices]; + for (uint32_t i = 0; i < total_indices; i++) { + uint32_t quad = i / num_indices; + uint32_t quad_local = i % num_indices; + indices[i] = quad_indices[quad_local] + quad * num_indices; + } + + glGenBuffers(1, &data.indexed_quad_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.indexed_quad_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * total_indices, indices, GL_STATIC_DRAW); + glBindVertexArray(0); + delete[] indices; + } String global_defines; - global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now - global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n"; - global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n"; + global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now + global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; + global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines); - state.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + + shadow_render.shader.initialize(); + shadow_render.shader_version = shadow_render.shader.version_create(); { default_canvas_group_shader = material_storage->shader_allocate(); @@ -1605,16 +2585,42 @@ void fragment() { material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); } - state.current_shader_version = state.canvas_shader_default_version; + { + default_clip_children_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_clip_children_shader); + + material_storage->shader_set_code(default_clip_children_shader, R"( +// Default clip children shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + COLOR.rgb = c.rgb; +} +)"); + default_clip_children_material = material_storage->material_allocate(); + material_storage->material_initialize(default_clip_children_material); + + material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); + } + + default_canvas_texture = texture_storage->canvas_texture_allocate(); + texture_storage->canvas_texture_initialize(default_canvas_texture); + state.time = 0.0; } RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { - GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + singleton = nullptr; - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(state.canvas_shader_default_version); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version); + shadow_render.shader.version_free(shadow_render.shader_version); material_storage->material_free(default_canvas_group_material); material_storage->shader_free(default_canvas_group_shader); + material_storage->material_free(default_clip_children_material); + material_storage->shader_free(default_clip_children_shader); singleton = nullptr; glDeleteBuffers(1, &data.canvas_quad_vertices); @@ -1622,6 +2628,19 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { glDeleteBuffers(1, &data.canvas_quad_vertices); glDeleteVertexArrays(1, &data.canvas_quad_array); + + GLES3::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture); + memdelete_arr(state.instance_data_array); + memdelete_arr(state.light_uniforms); + + if (state.shadow_fb != 0) { + glDeleteFramebuffers(1, &state.shadow_fb); + glDeleteTextures(1, &state.shadow_texture); + glDeleteRenderbuffers(1, &state.shadow_depth_buffer); + state.shadow_fb = 0; + state.shadow_texture = 0; + state.shadow_depth_buffer = 0; + } } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 31e82401f9..d672d05e14 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -28,19 +28,19 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RASTERIZER_CANVAS_OPENGL_H -#define RASTERIZER_CANVAS_OPENGL_H +#ifndef RASTERIZER_CANVAS_GLES3_H +#define RASTERIZER_CANVAS_GLES3_H #ifdef GLES3_ENABLED #include "rasterizer_scene_gles3.h" -#include "rasterizer_storage_gles3.h" #include "servers/rendering/renderer_canvas_render.h" #include "servers/rendering/renderer_compositor.h" #include "storage/material_storage.h" #include "storage/texture_storage.h" #include "shaders/canvas.glsl.gen.h" +#include "shaders/canvas_occlusion.glsl.gen.h" class RasterizerSceneGLES3; @@ -74,6 +74,7 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27), FLAGS_USE_MSDF = (1 << 28), + FLAGS_USE_LCD = (1 << 29), }; enum { @@ -96,6 +97,63 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { DEFAULT_MAX_LIGHTS_PER_RENDER = 256, }; + /******************/ + /**** LIGHTING ****/ + /******************/ + + struct CanvasLight { + RID texture; + struct { + bool enabled = false; + float z_far; + float y_offset; + Transform2D directional_xform; + } shadow; + }; + + RID_Owner<CanvasLight> canvas_light_owner; + + struct OccluderPolygon { + RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED; + int line_point_count = 0; + GLuint vertex_buffer = 0; + GLuint vertex_array = 0; + GLuint index_buffer = 0; + + int sdf_point_count = 0; + int sdf_index_count = 0; + GLuint sdf_vertex_buffer = 0; + GLuint sdf_vertex_array = 0; + GLuint sdf_index_buffer = 0; + bool sdf_is_lines = false; + }; + + RID_Owner<OccluderPolygon> occluder_polygon_owner; + + void _update_shadow_atlas(); + + struct { + CanvasOcclusionShaderGLES3 shader; + RID shader_version; + } shadow_render; + + struct LightUniform { + float matrix[8]; //light to texture coordinate matrix + float shadow_matrix[8]; //light to shadow coordinate matrix + float color[4]; + + uint8_t shadow_color[4]; + uint32_t flags; //index to light texture + float shadow_pixel_size; + float height; + + float position[2]; + float shadow_z_far_inv; + float shadow_y_ofs; + + float atlas_rect[4]; + }; + public: enum { BASE_UNIFORM_LOCATION = 0, @@ -125,6 +183,23 @@ public: uint32_t pad2; }; + struct PolygonBuffers { + GLuint vertex_buffer = 0; + GLuint vertex_array = 0; + GLuint index_buffer = 0; + int count = 0; + bool color_disabled = false; + Color color; + }; + + struct { + HashMap<PolygonID, PolygonBuffers> polygons; + PolygonID last_id = 0; + } polygon_buffers; + + RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; + void free_polygon(PolygonID p_polygon) override; + struct InstanceData { float world[6]; float color_texture_pixel_size[2]; @@ -156,42 +231,86 @@ public: GLuint canvas_quad_vertices; GLuint canvas_quad_array; + GLuint indexed_quad_buffer; + GLuint indexed_quad_array; + GLuint particle_quad_vertices; GLuint particle_quad_array; GLuint ninepatch_vertices; GLuint ninepatch_elements; + + RID canvas_shader_default_version; + + uint32_t max_lights_per_render = 256; + uint32_t max_lights_per_item = 16; + uint32_t max_instances_per_batch = 512; + uint32_t max_instances_per_ubo = 16384; + uint32_t max_instance_buffer_size = 16384 * 128; } data; + struct Batch { + // Position in the UBO measured in bytes + uint32_t start = 0; + uint32_t instance_count = 0; + + RID tex = RID(); + RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; + RS::CanvasItemTextureRepeat repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; + + GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; + Color blend_color = Color(1.0, 1.0, 1.0, 1.0); + + Item *clip = nullptr; + + RID material = RID(); + GLES3::CanvasMaterialData *material_data = nullptr; + CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD; + + const Item::Command *command = nullptr; + Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. + uint32_t primitive_points = 0; + + bool lights_disabled = false; + }; + + // DataBuffer contains our per-frame data. I.e. the resources that are updated each frame. + // We track them and ensure that they don't get reused until at least 2 frames have passed + // to avoid the GPU stalling to wait for a resource to become available. + struct DataBuffer { + GLuint ubo = 0; + GLuint light_ubo = 0; + GLuint state_ubo = 0; + uint64_t last_frame_used = -3; + GLsync fence = GLsync(); + }; + struct State { - GLuint canvas_state_buffer; - LocalVector<GLuint> canvas_instance_data_buffers; - LocalVector<GLsync> fences; + LocalVector<DataBuffer> canvas_instance_data_buffers; + LocalVector<Batch> canvas_instance_batches; uint32_t current_buffer = 0; + uint32_t current_buffer_index = 0; + uint32_t current_batch_index = 0; InstanceData *instance_data_array = nullptr; - bool canvas_texscreen_used; - RID canvas_shader_current_version; - RID canvas_shader_default_version; + + LightUniform *light_uniforms = nullptr; + + GLuint shadow_texture = 0; + GLuint shadow_depth_buffer = 0; + GLuint shadow_fb = 0; + int shadow_texture_size = 2048; + + bool using_directional_lights = false; RID current_tex = RID(); - Size2 current_pixel_size = Size2(); - RID current_normal = RID(); - RID current_specular = RID(); - GLES3::Texture *current_tex_ptr; - RID current_shader_version = RID(); - RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX; - uint32_t current_primitive_points = 0; - Item::Command::Type current_command = Item::Command::TYPE_RECT; + RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; + RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; bool transparent_render_target = false; double time = 0.0; - uint32_t max_lights_per_render; - uint32_t max_lights_per_item; - uint32_t max_instances_per_batch; - RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; } state; @@ -201,20 +320,17 @@ public: RID default_canvas_texture; RID default_canvas_group_material; RID default_canvas_group_shader; + RID default_clip_children_material; + RID default_clip_children_shader; typedef void Texture; - RasterizerStorageGLES3 *storage = nullptr; - void canvas_begin(RID p_to_render_target, bool p_to_backbuffer); //virtual void draw_window_margins(int *black_margin, RID *black_image) override; void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample); void reset_canvas(); - void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache); - - virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override; RID light_create() override; void light_set_texture(RID p_rid, RID p_texture) override; @@ -231,36 +347,26 @@ public: bool free(RID p_rid) override; void update() override; - void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index); - - struct PolygonBuffers { - GLuint vertex_buffer; - GLuint vertex_array; - GLuint index_buffer; - int count; - }; - - struct { - HashMap<PolygonID, PolygonBuffers> polygons; - PolygonID last_id; - } polygon_buffers; - - RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; - void free_polygon(PolygonID p_polygon) override; + void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat); + void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size); void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); - void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index); - void _render_batch(uint32_t &p_max_index); - void _bind_instance_data_buffer(uint32_t p_max_index); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer = false); + void _record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch); + void _render_batch(Light *p_lights, uint32_t p_index); + void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); + void _new_batch(bool &r_batch_broken, uint32_t &r_index); + void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); + void _align_instance_data_buffer(uint32_t &r_index); void set_time(double p_time); static RasterizerCanvasGLES3 *get_singleton(); - RasterizerCanvasGLES3(RasterizerStorageGLES3 *p_storage); + RasterizerCanvasGLES3(); ~RasterizerCanvasGLES3(); }; #endif // GLES3_ENABLED -#endif // RASTERIZER_CANVAS_OPENGL_H + +#endif // RASTERIZER_CANVAS_GLES3_H diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index c8705dc8c8..b2d01b02fb 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "rasterizer_gles3.h" +#include "storage/utilities.h" #ifdef GLES3_ENABLED @@ -68,7 +69,7 @@ #endif #endif -#if !defined(IPHONE_ENABLED) && !defined(JAVASCRIPT_ENABLED) +#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED) // We include EGL below to get debug callback on GLES2 platforms, // but EGL is not available on iOS. #define CAN_DEBUG @@ -99,26 +100,13 @@ void RasterizerGLES3::begin_frame(double frame_step) { canvas->set_time(time_total); scene->set_time(time_total, frame_step); - storage->info.render_final = storage->info.render; - storage->info.render.reset(); + GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); + utils->_capture_timestamps_begin(); //scene->iteration(); } void RasterizerGLES3::end_frame(bool p_swap_buffers) { - // if (OS::get_singleton()->is_layered_allowed()) { - // if (!OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { - //clear alpha - // glColorMask(false, false, false, true); - // glClearColor(0.5, 0, 0, 1); - // glClear(GL_COLOR_BUFFER_BIT); - // glColorMask(true, true, true, true); - // } - // } - - // glClearColor(1, 0, 0, 1); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - if (p_swap_buffers) { DisplayServer::get_singleton()->swap_buffers(); } else { @@ -197,12 +185,15 @@ void RasterizerGLES3::initialize() { void RasterizerGLES3::finalize() { memdelete(scene); memdelete(canvas); - memdelete(storage); + memdelete(gi); + memdelete(fog); + memdelete(copy_effects); memdelete(light_storage); memdelete(particles_storage); memdelete(mesh_storage); memdelete(material_storage); memdelete(texture_storage); + memdelete(utilities); memdelete(config); } @@ -263,57 +254,69 @@ RasterizerGLES3::RasterizerGLES3() { // OpenGL needs to be initialized before initializing the Rasterizers config = memnew(GLES3::Config); + utilities = memnew(GLES3::Utilities); texture_storage = memnew(GLES3::TextureStorage); material_storage = memnew(GLES3::MaterialStorage); mesh_storage = memnew(GLES3::MeshStorage); particles_storage = memnew(GLES3::ParticlesStorage); light_storage = memnew(GLES3::LightStorage); copy_effects = memnew(GLES3::CopyEffects); - storage = memnew(RasterizerStorageGLES3); - canvas = memnew(RasterizerCanvasGLES3(storage)); - scene = memnew(RasterizerSceneGLES3(storage)); + gi = memnew(GLES3::GI); + fog = memnew(GLES3::Fog); + canvas = memnew(RasterizerCanvasGLES3()); + scene = memnew(RasterizerSceneGLES3()); } RasterizerGLES3::~RasterizerGLES3() { } void RasterizerGLES3::prepare_for_blitting_render_targets() { + // This is a hack, but this function is called one time after all viewports have been updated. + // So it marks the end of the frame for all viewports + // In the OpenGL renderer we have to call end_frame for each viewport so we can swap the + // buffers for each window before proceeding to the next. + // This allows us to only increment the frame after all viewports are done. + GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); + utils->capture_timestamps_end(); } -void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); +void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) { + GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target); - GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); ERR_FAIL_COND(!rt); - // TODO: do we need a keep 3d linear option? - - // Make sure we are drawing to the right context. - DisplayServer::get_singleton()->gl_window_make_current(p_screen); - - if (rt->external.fbo != 0) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + GLuint read_fbo = 0; + if (rt->view_count > 1) { + glGenFramebuffers(1, &read_fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer); } else { glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); } + glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); // Flip content upside down to correct for coordinates. - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + Vector2i screen_rect_end = p_screen_rect.get_end(); + glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, + p_screen_rect.position.x, screen_rect_end.y, screen_rect_end.x, p_screen_rect.position.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (read_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &read_fbo); + } } // is this p_screen useless in a multi window environment? void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) { - // All blits are going to the system framebuffer, so just bind once. - glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); - for (int i = 0; i < p_amount; i++) { const BlitToScreen &blit = p_render_targets[i]; RID rid_rt = blit.render_target; Rect2 dst_rect = blit.dst_rect; - _blit_render_target_to_screen(rid_rt, p_screen, dst_rect); + _blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0); } } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 5f1cbab849..d7d26685b4 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -28,15 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RASTERIZER_OPENGL_H -#define RASTERIZER_OPENGL_H +#ifndef RASTERIZER_GLES3_H +#define RASTERIZER_GLES3_H #ifdef GLES3_ENABLED #include "effects/copy_effects.h" +#include "environment/fog.h" +#include "environment/gi.h" #include "rasterizer_canvas_gles3.h" #include "rasterizer_scene_gles3.h" -#include "rasterizer_storage_gles3.h" #include "servers/rendering/renderer_compositor.h" #include "storage/config.h" #include "storage/light_storage.h" @@ -44,6 +45,7 @@ #include "storage/mesh_storage.h" #include "storage/particles_storage.h" #include "storage/texture_storage.h" +#include "storage/utilities.h" class RasterizerGLES3 : public RendererCompositor { private: @@ -54,25 +56,29 @@ private: protected: GLES3::Config *config = nullptr; + GLES3::Utilities *utilities = nullptr; GLES3::TextureStorage *texture_storage = nullptr; GLES3::MaterialStorage *material_storage = nullptr; GLES3::MeshStorage *mesh_storage = nullptr; GLES3::ParticlesStorage *particles_storage = nullptr; GLES3::LightStorage *light_storage = nullptr; + GLES3::GI *gi = nullptr; + GLES3::Fog *fog = nullptr; GLES3::CopyEffects *copy_effects = nullptr; - RasterizerStorageGLES3 *storage = nullptr; RasterizerCanvasGLES3 *canvas = nullptr; RasterizerSceneGLES3 *scene = nullptr; - void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect); + void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer); public: + RendererUtilities *get_utilities() { return utilities; } RendererLightStorage *get_light_storage() { return light_storage; } RendererMaterialStorage *get_material_storage() { return material_storage; } RendererMeshStorage *get_mesh_storage() { return mesh_storage; } RendererParticlesStorage *get_particles_storage() { return particles_storage; } RendererTextureStorage *get_texture_storage() { return texture_storage; } - RendererStorage *get_storage() { return storage; } + RendererGI *get_gi() { return gi; } + RendererFog *get_fog() { return fog; } RendererCanvasRender *get_canvas() { return canvas; } RendererSceneRender *get_scene() { return scene; } @@ -97,8 +103,9 @@ public: low_end = true; } - uint64_t get_frame_number() const { return frame; } - double get_frame_delta_time() const { return delta; } + _ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; } + _ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; } + _ALWAYS_INLINE_ double get_total_time() const { return time_total; } RasterizerGLES3(); ~RasterizerGLES3(); @@ -106,4 +113,4 @@ public: #endif // GLES3_ENABLED -#endif +#endif // RASTERIZER_GLES3_H diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 3fe0ae4876..734911ccdb 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -32,20 +32,17 @@ #include "core/config/project_settings.h" #include "core/templates/sort_array.h" #include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/rendering_server_globals.h" #include "storage/config.h" +#include "storage/mesh_storage.h" +#include "storage/texture_storage.h" #ifdef GLES3_ENABLED -uint64_t RasterizerSceneGLES3::auto_exposure_counter = 2; - RasterizerSceneGLES3 *RasterizerSceneGLES3::singleton = nullptr; -RasterizerSceneGLES3 *RasterizerSceneGLES3::get_singleton() { - return singleton; -} - -RendererSceneRender::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) { - RS::InstanceType type = storage->get_base_type(p_base); +RenderGeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) { + RS::InstanceType type = RSG::utilities->get_base_type(p_base); ERR_FAIL_COND_V(!((1 << type) & RS::INSTANCE_GEOMETRY_MASK), nullptr); GeometryInstanceGLES3 *ginstance = geometry_instance_alloc.alloc(); @@ -54,176 +51,36 @@ RendererSceneRender::GeometryInstance *RasterizerSceneGLES3::geometry_instance_c ginstance->data->base = p_base; ginstance->data->base_type = type; - _geometry_instance_mark_dirty(ginstance); + ginstance->_mark_dirty(); return ginstance; } -void RasterizerSceneGLES3::geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->skeleton = p_skeleton; - - _geometry_instance_mark_dirty(ginstance); - ginstance->data->dirty_dependencies = true; -} - -void RasterizerSceneGLES3::geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->material_override = p_override; - - _geometry_instance_mark_dirty(ginstance); - ginstance->data->dirty_dependencies = true; -} - -void RasterizerSceneGLES3::geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->material_overlay = p_overlay; - - _geometry_instance_mark_dirty(ginstance); - ginstance->data->dirty_dependencies = true; -} - -void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_materials) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->surface_materials = p_materials; - - _geometry_instance_mark_dirty(ginstance); - ginstance->data->dirty_dependencies = true; -} - -void RasterizerSceneGLES3::geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ERR_FAIL_COND(!ginstance); - ginstance->mesh_instance = p_mesh_instance; - - _geometry_instance_mark_dirty(ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->transform = p_transform; - ginstance->mirror = p_transform.basis.determinant() < 0; - ginstance->data->aabb = p_aabb; - ginstance->transformed_aabb = p_transformed_aabb; - - Vector3 model_scale_vec = p_transform.basis.get_scale_abs(); - // handle non uniform scale here - - float max_scale = MAX(model_scale_vec.x, MAX(model_scale_vec.y, model_scale_vec.z)); - float min_scale = MIN(model_scale_vec.x, MIN(model_scale_vec.y, model_scale_vec.z)); - ginstance->non_uniform_scale = max_scale >= 0.0 && (min_scale / max_scale) < 0.9; - - ginstance->lod_model_scale = max_scale; -} - -void RasterizerSceneGLES3::geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->layer_mask = p_layer_mask; -} - -void RasterizerSceneGLES3::geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->lod_bias = p_lod_bias; -} - -void RasterizerSceneGLES3::geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->force_alpha = CLAMP(1.0 - p_transparency, 0, 1); -} - -void RasterizerSceneGLES3::geometry_instance_set_fade_range(GeometryInstance *p_geometry_instance, bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->fade_near = p_enable_near; - ginstance->fade_near_begin = p_near_begin; - ginstance->fade_near_end = p_near_end; - ginstance->fade_far = p_enable_far; - ginstance->fade_far_begin = p_far_begin; - ginstance->fade_far_end = p_far_end; -} - -void RasterizerSceneGLES3::geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->parent_fade_alpha = p_alpha; -} - -void RasterizerSceneGLES3::geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->use_baked_light = p_enable; - - _geometry_instance_mark_dirty(ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->use_dynamic_gi = p_enable; - _geometry_instance_mark_dirty(ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->shader_parameters_offset = p_offset; - _geometry_instance_mark_dirty(ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - ginstance->data->cast_double_sided_shadows = p_enable; - _geometry_instance_mark_dirty(ginstance); -} - uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() { return (1 << RS::INSTANCE_LIGHT); } -void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); - +void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) { GLES3::Config *config = GLES3::Config::get_singleton(); - ginstance->omni_light_count = 0; - ginstance->spot_light_count = 0; - ginstance->omni_lights.clear(); - ginstance->spot_lights.clear(); + omni_light_count = 0; + spot_light_count = 0; + omni_lights.clear(); + spot_lights.clear(); for (uint32_t i = 0; i < p_light_instance_count; i++) { - RS::LightType type = light_instance_get_type(p_light_instances[i]); + RS::LightType type = GLES3::LightStorage::get_singleton()->light_instance_get_type(p_light_instances[i]); switch (type) { case RS::LIGHT_OMNI: { - if (ginstance->omni_light_count < (uint32_t)config->max_lights_per_object) { - ginstance->omni_lights.push_back(p_light_instances[i]); - ginstance->omni_light_count++; + if (omni_light_count < (uint32_t)config->max_lights_per_object) { + omni_lights.push_back(p_light_instances[i]); + omni_light_count++; } } break; case RS::LIGHT_SPOT: { - if (ginstance->spot_light_count < (uint32_t)config->max_lights_per_object) { - ginstance->spot_lights.push_back(p_light_instances[i]); - ginstance->spot_light_count++; + if (spot_light_count < (uint32_t)config->max_lights_per_object) { + spot_lights.push_back(p_light_instances[i]); + spot_light_count++; } } break; default: @@ -232,21 +89,7 @@ void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstan } } -void RasterizerSceneGLES3::geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) { -} - -void RasterizerSceneGLES3::geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) { -} - -void RasterizerSceneGLES3::geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) { -} - -void RasterizerSceneGLES3::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - ERR_FAIL_COND(!ginstance); -} - -void RasterizerSceneGLES3::geometry_instance_free(GeometryInstance *p_geometry_instance) { +void RasterizerSceneGLES3::geometry_instance_free(RenderGeometryInstance *p_geometry_instance) { GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); ERR_FAIL_COND(!ginstance); GeometryInstanceSurface *surf = ginstance->surface_caches; @@ -259,24 +102,29 @@ void RasterizerSceneGLES3::geometry_instance_free(GeometryInstance *p_geometry_i geometry_instance_alloc.free(ginstance); } -void RasterizerSceneGLES3::_geometry_instance_mark_dirty(GeometryInstance *p_geometry_instance) { - GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); - if (ginstance->dirty_list_element.in_list()) { +void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() { + if (dirty_list_element.in_list()) { return; } //clear surface caches - GeometryInstanceSurface *surf = ginstance->surface_caches; + GeometryInstanceSurface *surf = surface_caches; while (surf) { GeometryInstanceSurface *next = surf->next; - geometry_instance_surface_alloc.free(surf); + RasterizerSceneGLES3::get_singleton()->geometry_instance_surface_alloc.free(surf); surf = next; } - ginstance->surface_caches = nullptr; + surface_caches = nullptr; + + RasterizerSceneGLES3::get_singleton()->geometry_instance_dirty_list.add(&dirty_list_element); +} + +void RasterizerSceneGLES3::GeometryInstanceGLES3::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) { +} - geometry_instance_dirty_list.add(&ginstance->dirty_list_element); +void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) { } void RasterizerSceneGLES3::_update_dirty_geometry_instances() { @@ -285,16 +133,16 @@ void RasterizerSceneGLES3::_update_dirty_geometry_instances() { } } -void RasterizerSceneGLES3::_geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker) { +void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) { switch (p_notification) { - case RendererStorage::DEPENDENCY_CHANGED_MATERIAL: - case RendererStorage::DEPENDENCY_CHANGED_MESH: - case RendererStorage::DEPENDENCY_CHANGED_PARTICLES: - case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH: - case RendererStorage::DEPENDENCY_CHANGED_SKELETON_DATA: { - static_cast<RasterizerSceneGLES3 *>(singleton)->_geometry_instance_mark_dirty(static_cast<GeometryInstance *>(p_tracker->userdata)); + case Dependency::DEPENDENCY_CHANGED_MATERIAL: + case Dependency::DEPENDENCY_CHANGED_MESH: + case Dependency::DEPENDENCY_CHANGED_PARTICLES: + case Dependency::DEPENDENCY_CHANGED_MULTIMESH: + case Dependency::DEPENDENCY_CHANGED_SKELETON_DATA: { + static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); } break; - case RendererStorage::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { + case Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES: { GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_tracker->userdata); if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) { ginstance->instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(ginstance->data->base); @@ -306,8 +154,8 @@ void RasterizerSceneGLES3::_geometry_instance_dependency_changed(RendererStorage } } -void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker) { - static_cast<RasterizerSceneGLES3 *>(singleton)->_geometry_instance_mark_dirty(static_cast<GeometryInstance *>(p_tracker->userdata)); +void RasterizerSceneGLES3::_geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker) { + static_cast<RenderGeometryInstance *>(p_tracker->userdata)->_mark_dirty(); } void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh) { @@ -376,7 +224,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry sdcache->surface_index = p_surface; if (ginstance->data->dirty_dependencies) { - storage->base_update_dependency(p_mesh, &ginstance->data->dependency_tracker); + RSG::utilities->base_update_dependency(p_mesh, &ginstance->data->dependency_tracker); } //shadow @@ -464,7 +312,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface(GeometryInstanceGLES3 } } -void RasterizerSceneGLES3::_geometry_instance_update(GeometryInstance *p_geometry_instance) { +void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_geometry_instance) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance); @@ -491,7 +339,7 @@ void RasterizerSceneGLES3::_geometry_instance_update(GeometryInstance *p_geometr } } - ginstance->instance_count = 1; + ginstance->instance_count = -1; } break; @@ -550,32 +398,6 @@ void RasterizerSceneGLES3::_geometry_instance_update(GeometryInstance *p_geometr ginstance->dirty_list_element.remove_from_list(); } -/* SHADOW ATLAS API */ - -RID RasterizerSceneGLES3::shadow_atlas_create() { - return RID(); -} - -void RasterizerSceneGLES3::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { -} - -void RasterizerSceneGLES3::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { -} - -bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { - return false; -} - -void RasterizerSceneGLES3::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { -} - -int RasterizerSceneGLES3::get_directional_light_shadow_size(RID p_light_intance) { - return 0; -} - -void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) { -} - /* SKY API */ void RasterizerSceneGLES3::_free_sky_data(Sky *p_sky) { @@ -634,6 +456,13 @@ void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { _invalidate_sky(sky); } +float RasterizerSceneGLES3::sky_get_baked_exposure(RID p_sky) const { + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND_V(!sky, 1.0); + + return sky->baked_exposure; +} + void RasterizerSceneGLES3::_invalidate_sky(Sky *p_sky) { if (!p_sky->dirty) { p_sky->dirty = true; @@ -647,8 +476,7 @@ void RasterizerSceneGLES3::_update_dirty_skys() { while (sky) { if (sky->radiance == 0) { - sky->mipmap_count = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBA8) + 1; - + sky->mipmap_count = Image::get_image_required_mipmaps(sky->radiance_size, sky->radiance_size, Image::FORMAT_RGBA8) - 1; // Left uninitialized, will attach a texture at render time glGenFramebuffers(1, &sky->radiance_framebuffer); @@ -675,7 +503,7 @@ void RasterizerSceneGLES3::_update_dirty_skys() { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count - 1); glGenTextures(1, &sky->raw_radiance); glBindTexture(GL_TEXTURE_CUBE_MAP, sky->raw_radiance); @@ -696,7 +524,8 @@ void RasterizerSceneGLES3::_update_dirty_skys() { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, sky->mipmap_count - 1); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } @@ -712,13 +541,13 @@ void RasterizerSceneGLES3::_update_dirty_skys() { dirty_sky_list = nullptr; } -void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { +void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - ERR_FAIL_COND(!p_env); + ERR_FAIL_COND(p_render_data->environment.is_null()); GLES3::SkyMaterialData *material = nullptr; - Sky *sky = sky_owner.get_or_null(p_env->sky); + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_render_data->environment)); RID sky_material; @@ -766,10 +595,11 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, sky->reflection_dirty = true; } + glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer); if (shader_data->uses_light) { sky_globals.directional_light_count = 0; for (int i = 0; i < (int)p_lights.size(); i++) { - LightInstance *li = light_instance_owner.get_or_null(p_lights[i]); + GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(p_lights[i]); if (!li) { continue; } @@ -790,6 +620,14 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, float sign = light_storage->light_is_negative(base) ? -1 : 1; sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + if (is_using_physical_light_units()) { + sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_render_data->camera_attributes.is_valid()) { + sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + Color linear_col = light_storage->light_get_color(base); sky_light_data.color[0] = linear_col.r; sky_light_data.color[1] = linear_col.g; @@ -799,7 +637,7 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, float angular_diameter = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); if (angular_diameter > 0.0) { - angular_diameter = Math::tan(Math::deg2rad(angular_diameter)); + angular_diameter = Math::tan(Math::deg_to_rad(angular_diameter)); } else { angular_diameter = 0.0; } @@ -819,6 +657,7 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, light_data_dirty = true; for (uint32_t i = sky_globals.directional_light_count; i < sky_globals.max_directional_lights; i++) { sky_globals.directional_lights[i].enabled = false; + sky_globals.last_frame_directional_lights[i].enabled = false; } } @@ -840,7 +679,6 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, } if (light_data_dirty) { - glBindBufferBase(GL_UNIFORM_BUFFER, SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, sky_globals.directional_light_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLightData) * sky_globals.max_directional_lights, sky_globals.directional_lights, GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); @@ -853,25 +691,25 @@ void RasterizerSceneGLES3::_setup_sky(Environment *p_env, RID p_render_buffers, } if (!sky->radiance) { + _invalidate_sky(sky); _update_dirty_skys(); } } } -void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - ERR_FAIL_COND(!p_env); + ERR_FAIL_COND(p_env.is_null()); - Sky *sky = sky_owner.get_or_null(p_env->sky); + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); ERR_FAIL_COND(!sky); GLES3::SkyMaterialData *material_data = nullptr; RID sky_material; - RS::EnvironmentBG background = p_env->background; + RS::EnvironmentBG background = environment_get_background(p_env); if (sky) { - ERR_FAIL_COND(!sky); sky_material = sky->material; if (sky_material.is_valid()) { @@ -898,42 +736,43 @@ void RasterizerSceneGLES3::_draw_sky(Environment *p_env, const CameraMatrix &p_p ERR_FAIL_COND(!shader_data); // Camera - CameraMatrix camera; + Projection camera; - if (p_env->sky_custom_fov) { + if (environment_get_sky_custom_fov(p_env)) { float near_plane = p_projection.get_z_near(); float far_plane = p_projection.get_z_far(); float aspect = p_projection.get_aspect(); - camera.set_perspective(p_env->sky_custom_fov, aspect, near_plane, far_plane); + camera.set_perspective(environment_get_sky_custom_fov(p_env), aspect, near_plane, far_plane); } else { camera = p_projection; } - Basis sky_transform = p_env->sky_orientation; + Basis sky_transform = environment_get_sky_orientation(p_env); sky_transform.invert(); sky_transform = p_transform.basis * sky_transform; GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, sky_transform, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.columns[2][0], camera.columns[0][0], camera.columns[2][1], camera.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); glBindVertexArray(sky_globals.screen_triangle_array); glDrawArrays(GL_TRIANGLES, 0, 3); } -void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - ERR_FAIL_COND(!p_env); + ERR_FAIL_COND(p_env.is_null()); - Sky *sky = sky_owner.get_or_null(p_env->sky); + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); ERR_FAIL_COND(!sky); GLES3::SkyMaterialData *material_data = nullptr; RID sky_material; - RS::EnvironmentBG background = p_env->background; + RS::EnvironmentBG background = environment_get_background(p_env); if (sky) { ERR_FAIL_COND(!sky); @@ -987,7 +826,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Camera int max_processing_layer = sky->mipmap_count; // Update radiance cubemap - if (sky->reflection_dirty && (sky->processing_layer >= max_processing_layer || update_single_frame)) { + if (sky->reflection_dirty && (sky->processing_layer > max_processing_layer || update_single_frame)) { static const Vector3 view_normals[6] = { Vector3(+1, 0, 0), Vector3(-1, 0, 0), @@ -1005,19 +844,19 @@ void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Camera Vector3(0, -1, 0) }; - CameraMatrix cm; + Projection cm; cm.set_perspective(90, 1, 0.01, 10.0); - CameraMatrix correction; - correction.set_depth_correction(true); + Projection correction; + correction.columns[1][1] = -1.0; cm = correction * cm; GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.columns[2][0], cm.columns[0][0], cm.columns[2][1], cm.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - // Bind a vertex array or else OpenGL complains. We won't actually use it glBindVertexArray(sky_globals.screen_triangle_array); glViewport(0, 0, sky->radiance_size, sky->radiance_size); @@ -1038,7 +877,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Camera _filter_sky_radiance(sky, 0); //Just copy over the first mipmap } sky->processing_layer = 1; - + sky->baked_exposure = p_luminance_multiplier; sky->reflection_dirty = false; } else { if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { @@ -1048,6 +887,45 @@ void RasterizerSceneGLES3::_update_sky_radiance(Environment *p_env, const Camera } } +// Helper functions for IBL filtering + +Vector3 importance_sample_GGX(Vector2 xi, float roughness4) { + // Compute distribution direction + float phi = 2.0 * Math_PI * xi.x; + float cos_theta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y)); + float sin_theta = sqrt(1.0 - cos_theta * cos_theta); + + // Convert to spherical direction + Vector3 half_vector; + half_vector.x = sin_theta * cos(phi); + half_vector.y = sin_theta * sin(phi); + half_vector.z = cos_theta; + + return half_vector; +} + +float distribution_GGX(float NdotH, float roughness4) { + float NdotH2 = NdotH * NdotH; + float denom = (NdotH2 * (roughness4 - 1.0) + 1.0); + denom = Math_PI * denom * denom; + + return roughness4 / denom; +} + +float radical_inverse_vdC(uint32_t bits) { + bits = (bits << 16) | (bits >> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); + + return float(bits) * 2.3283064365386963e-10; +} + +Vector2 hammersley(uint32_t i, uint32_t N) { + return Vector2(float(i) / float(N), radical_inverse_vdC(i)); +} + void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); @@ -1059,27 +937,68 @@ void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) { if (p_base_layer == 0) { glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + // Copy over base layer without filtering. mode = CubemapFilterShaderGLES3::MODE_COPY; - - //Copy over base layer } - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, sky_globals.radical_inverse_vdc_cache_tex); int size = p_sky->radiance_size >> p_base_layer; glViewport(0, 0, size, size); glBindVertexArray(sky_globals.screen_triangle_array); material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode); - material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, sky_globals.ggx_samples, scene_globals.cubemap_filter_shader_version, mode); - material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::ROUGHNESS, float(p_base_layer) / (p_sky->mipmap_count - 1.0), scene_globals.cubemap_filter_shader_version, mode); - material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_SIZE, float(size), scene_globals.cubemap_filter_shader_version, mode); + + if (p_base_layer > 0) { + const uint32_t sample_counts[4] = { 1, sky_globals.ggx_samples / 4, sky_globals.ggx_samples / 2, sky_globals.ggx_samples }; + uint32_t sample_count = sample_counts[MIN(3, p_base_layer)]; + + float roughness = float(p_base_layer) / (p_sky->mipmap_count); + float roughness4 = roughness * roughness; + roughness4 *= roughness4; + + float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size); + + LocalVector<float> sample_directions; + sample_directions.resize(4 * sample_count); + + uint32_t index = 0; + float weight = 0.0; + for (uint32_t i = 0; i < sample_count; i++) { + Vector2 xi = hammersley(i, sample_count); + Vector3 dir = importance_sample_GGX(xi, roughness4); + Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0)); + + if (light_vec.z < 0.0) { + continue; + } + + sample_directions[index * 4] = light_vec.x; + sample_directions[index * 4 + 1] = light_vec.y; + sample_directions[index * 4 + 2] = light_vec.z; + + float D = distribution_GGX(dir.z, roughness4); + float pdf = D * dir.z / (4.0 * dir.z) + 0.0001; + + float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001); + + float mip_level = MAX(0.5 * log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_base_layer - 3)), 1.0); + + sample_directions[index * 4 + 3] = mip_level; + weight += light_vec.z; + index++; + } + + glUniform4fv(material_storage->shaders.cubemap_filter_shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, scene_globals.cubemap_filter_shader_version, mode), sample_count, sample_directions.ptr()); + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, scene_globals.cubemap_filter_shader_version, mode); + material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, scene_globals.cubemap_filter_shader_version, mode); + } for (int i = 0; i < 6; i++) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_sky->radiance, p_base_layer); #ifdef DEBUG_ENABLED GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status)); + } #endif material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, scene_globals.cubemap_filter_shader_version, mode); @@ -1096,84 +1015,6 @@ Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bo /* ENVIRONMENT API */ -RID RasterizerSceneGLES3::environment_allocate() { - return environment_owner.allocate_rid(); -} - -void RasterizerSceneGLES3::environment_initialize(RID p_rid) { - environment_owner.initialize_rid(p_rid); -} - -void RasterizerSceneGLES3::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->background = p_bg; -} - -void RasterizerSceneGLES3::environment_set_sky(RID p_env, RID p_sky) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->sky = p_sky; -} - -void RasterizerSceneGLES3::environment_set_sky_custom_fov(RID p_env, float p_scale) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->sky_custom_fov = p_scale; -} - -void RasterizerSceneGLES3::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->sky_orientation = p_orientation; -} - -void RasterizerSceneGLES3::environment_set_bg_color(RID p_env, const Color &p_color) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->bg_color = p_color; -} - -void RasterizerSceneGLES3::environment_set_bg_energy(RID p_env, float p_energy) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->bg_energy = p_energy; -} - -void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->canvas_max_layer = p_max_layer; -} - -void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->ambient_light = p_color; - env->ambient_source = p_ambient; - env->ambient_light_energy = p_energy; - env->ambient_sky_contribution = p_sky_contribution; - env->reflection_source = p_reflection_source; -} - -void RasterizerSceneGLES3::environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - ERR_FAIL_COND_MSG(p_levels.size() != 7, "Size of array of glow levels must be 7"); - env->glow_enabled = p_enable; - env->glow_levels = p_levels; - env->glow_intensity = p_intensity; - env->glow_strength = p_strength; - env->glow_mix = p_mix; - env->glow_bloom = p_bloom_threshold; - env->glow_blend_mode = p_blend_mode; - env->glow_hdr_bleed_threshold = p_hdr_bleed_threshold; - env->glow_hdr_bleed_scale = p_hdr_bleed_scale; - env->glow_hdr_luminance_cap = p_hdr_luminance_cap; - env->glow_map_strength = p_glow_map_strength; - env->glow_map = p_glow_map; -} - void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool p_enable) { glow_bicubic_upscale = p_enable; } @@ -1182,35 +1023,15 @@ void RasterizerSceneGLES3::environment_glow_set_use_high_quality(bool p_enable) glow_high_quality = p_enable; } -void RasterizerSceneGLES3::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->ssr_enabled = p_enable; - env->ssr_max_steps = p_max_steps; - env->ssr_fade_in = p_fade_int; - env->ssr_fade_out = p_fade_out; - env->ssr_depth_tolerance = p_depth_tolerance; -} - void RasterizerSceneGLES3::environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) { } -void RasterizerSceneGLES3::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); -} - void RasterizerSceneGLES3::environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { } -void RasterizerSceneGLES3::environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) { -} void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { } -void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { -} - void RasterizerSceneGLES3::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { } @@ -1220,49 +1041,6 @@ void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_converge(RS::Environm void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) { } -void RasterizerSceneGLES3::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->exposure = p_exposure; - env->tone_mapper = p_tone_mapper; - if (!env->auto_exposure && p_auto_exposure) { - env->auto_exposure_version = ++auto_exposure_counter; - } - env->auto_exposure = p_auto_exposure; - env->white = p_white; - env->min_luminance = p_min_luminance; - env->max_luminance = p_max_luminance; - env->auto_exp_speed = p_auto_exp_speed; - env->auto_exp_scale = p_auto_exp_scale; -} - -void RasterizerSceneGLES3::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->adjustments_enabled = p_enable; - env->adjustments_brightness = p_brightness; - env->adjustments_contrast = p_contrast; - env->adjustments_saturation = p_saturation; - env->use_1d_color_correction = p_use_1d_color_correction; - env->color_correction = p_color_correction; -} - -void RasterizerSceneGLES3::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND(!env); - env->fog_enabled = p_enable; - env->fog_light_color = p_light_color; - env->fog_light_energy = p_light_energy; - env->fog_sun_scatter = p_sun_scatter; - env->fog_density = p_density; - env->fog_height = p_height; - env->fog_height_density = p_height_density; - env->fog_aerial_perspective = p_aerial_perspective; -} - -void RasterizerSceneGLES3::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) { -} - void RasterizerSceneGLES3::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) { } @@ -1270,82 +1048,13 @@ void RasterizerSceneGLES3::environment_set_volumetric_fog_filter_active(bool p_e } Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, Ref<Image>()); return Ref<Image>(); } -bool RasterizerSceneGLES3::is_environment(RID p_env) const { - return environment_owner.owns(p_env); -} - -RS::EnvironmentBG RasterizerSceneGLES3::environment_get_background(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, RS::ENV_BG_MAX); - return env->background; -} - -int RasterizerSceneGLES3::environment_get_canvas_max_layer(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0); - return env->canvas_max_layer; -} - -RID RasterizerSceneGLES3::camera_effects_allocate() { - return RID(); -} - -void RasterizerSceneGLES3::camera_effects_initialize(RID p_rid) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { -} - -void RasterizerSceneGLES3::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { -} - -void RasterizerSceneGLES3::shadows_quality_set(RS::ShadowQuality p_quality) { -} - -void RasterizerSceneGLES3::directional_shadow_quality_set(RS::ShadowQuality p_quality) { -} - -RID RasterizerSceneGLES3::light_instance_create(RID p_light) { - RID li = light_instance_owner.make_rid(LightInstance()); - - LightInstance *light_instance = light_instance_owner.get_or_null(li); - - light_instance->self = li; - light_instance->light = p_light; - light_instance->light_type = RSG::light_storage->light_get_type(p_light); - - return li; -} - -void RasterizerSceneGLES3::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); - ERR_FAIL_COND(!light_instance); - - light_instance->transform = p_transform; -} - -void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); - ERR_FAIL_COND(!light_instance); - - light_instance->aabb = p_aabb; -} - -void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { +void RasterizerSceneGLES3::positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } -void RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) { +void RasterizerSceneGLES3::directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } RID RasterizerSceneGLES3::fog_volume_instance_create(RID p_fog_volume) { @@ -1366,57 +1075,6 @@ Vector3 RasterizerSceneGLES3::fog_volume_instance_get_position(RID p_fog_volume_ return Vector3(); } -RID RasterizerSceneGLES3::reflection_atlas_create() { - return RID(); -} - -int RasterizerSceneGLES3::reflection_atlas_get_size(RID p_ref_atlas) const { - return 0; -} - -void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { -} - -RID RasterizerSceneGLES3::reflection_probe_instance_create(RID p_probe) { - return RID(); -} - -void RasterizerSceneGLES3::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { -} - -void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) { -} - -bool RasterizerSceneGLES3::reflection_probe_instance_needs_redraw(RID p_instance) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_has_reflection(RID p_instance) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_postprocess_step(RID p_instance) { - return true; -} - -RID RasterizerSceneGLES3::decal_instance_create(RID p_decal) { - return RID(); -} - -void RasterizerSceneGLES3::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) { -} - -RID RasterizerSceneGLES3::lightmap_instance_create(RID p_lightmap) { - return RID(); -} - -void RasterizerSceneGLES3::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { -} - RID RasterizerSceneGLES3::voxel_gi_instance_create(RID p_voxel_gi) { return RID(); } @@ -1428,7 +1086,7 @@ bool RasterizerSceneGLES3::voxel_gi_needs_update(RID p_probe) const { return false; } -void RasterizerSceneGLES3::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) { +void RasterizerSceneGLES3::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects) { } void RasterizerSceneGLES3::voxel_gi_set_quality(RS::VoxelGIQuality) { @@ -1489,13 +1147,13 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const if (inst->omni_light_count) { inst->omni_light_gl_cache.resize(inst->omni_light_count); for (uint32_t j = 0; j < inst->omni_light_count; j++) { - inst->omni_light_gl_cache[j] = light_instance_get_gl_id(inst->omni_lights[j]); + inst->omni_light_gl_cache[j] = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(inst->omni_lights[j]); } } if (inst->spot_light_count) { inst->spot_light_gl_cache.resize(inst->spot_light_count); for (uint32_t j = 0; j < inst->spot_light_count; j++) { - inst->spot_light_gl_cache[j] = light_instance_get_gl_id(inst->spot_lights[j]); + inst->spot_light_gl_cache[j] = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(inst->spot_lights[j]); } } } @@ -1508,12 +1166,13 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - //lod - Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->lod_camera_plane.normal); - Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->lod_camera_plane.normal); + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - float distance_min = p_render_data->lod_camera_plane.distance_to(lod_support_min); - float distance_max = p_render_data->lod_camera_plane.distance_to(lod_support_max); + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->cam_transform.origin.distance_to(lod_support_max); float distance = 0.0; @@ -1530,8 +1189,8 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const distance = 1.0; } - uint32_t indices; - surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices); + uint32_t indices = 0; + surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices); /* if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); @@ -1605,14 +1264,27 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const // Needs to be called after _setup_lights so that directional_light_count is accurate. void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows) { - CameraMatrix correction; - correction.set_depth_correction(p_flip_y); - CameraMatrix projection = correction * p_render_data->cam_projection; + Projection correction; + correction.columns[1][1] = p_flip_y ? -1.0 : 1.0; + Projection projection = correction * p_render_data->cam_projection; //store camera into ubo - RasterizerStorageGLES3::store_camera(projection, scene_state.ubo.projection_matrix); - RasterizerStorageGLES3::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix); - RasterizerStorageGLES3::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix); - RasterizerStorageGLES3::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix); + GLES3::MaterialStorage::store_camera(projection, scene_state.ubo.projection_matrix); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix); + GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix); + GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix); + + if (p_render_data->view_count > 1) { + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + projection = correction * p_render_data->view_projection[v]; + GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_ubo.projection_matrix_view[v]); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_ubo.inv_projection_matrix_view[v]); + + scene_state.multiview_ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x; + scene_state.multiview_ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y; + scene_state.multiview_ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z; + scene_state.multiview_ubo.eye_offset[v][3] = 0.0; + } + } scene_state.ubo.directional_light_count = p_render_data->directional_light_count; @@ -1630,72 +1302,100 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da scene_state.ubo.time = time; if (is_environment(p_render_data->environment)) { - Environment *env = environment_owner.get_or_null(p_render_data->environment); - RS::EnvironmentBG env_bg = env->background; - RS::EnvironmentAmbientSource ambient_src = env->ambient_source; + RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); + RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); + + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); - float bg_energy = env->bg_energy; - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; - scene_state.ubo.ambient_color_sky_mix = env->ambient_sky_contribution; + scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); //ambient if (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && (env_bg == RS::ENV_BG_CLEAR_COLOR || env_bg == RS::ENV_BG_COLOR)) { - Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : env->bg_color; + Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { - float energy = env->ambient_light_energy; - Color color = env->ambient_light; + float energy = environment_get_ambient_light_energy(p_render_data->environment); + Color color = environment_get_ambient_light(p_render_data->environment); color = color.srgb_to_linear(); scene_state.ubo.ambient_light_color_energy[0] = color.r * energy; scene_state.ubo.ambient_light_color_energy[1] = color.g * energy; scene_state.ubo.ambient_light_color_energy[2] = color.b * energy; - Basis sky_transform = env->sky_orientation; + Basis sky_transform = environment_get_sky_orientation(p_render_data->environment); sky_transform = sky_transform.inverse() * p_render_data->cam_transform.basis; - RasterizerStorageGLES3::store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform); + GLES3::MaterialStorage::store_transform_3x3(sky_transform, scene_state.ubo.radiance_inverse_xform); scene_state.ubo.use_ambient_cubemap = (ambient_src == RS::ENV_AMBIENT_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ambient_src == RS::ENV_AMBIENT_SOURCE_SKY; scene_state.ubo.use_ambient_light = scene_state.ubo.use_ambient_cubemap || ambient_src == RS::ENV_AMBIENT_SOURCE_COLOR; } //specular - RS::EnvironmentReflectionSource ref_src = env->reflection_source; + RS::EnvironmentReflectionSource ref_src = environment_get_reflection_source(p_render_data->environment); if ((ref_src == RS::ENV_REFLECTION_SOURCE_BG && env_bg == RS::ENV_BG_SKY) || ref_src == RS::ENV_REFLECTION_SOURCE_SKY) { scene_state.ubo.use_reflection_cubemap = true; } else { scene_state.ubo.use_reflection_cubemap = false; } - scene_state.ubo.fog_enabled = env->fog_enabled; - scene_state.ubo.fog_density = env->fog_density; - scene_state.ubo.fog_height = env->fog_height; - scene_state.ubo.fog_height_density = env->fog_height_density; - scene_state.ubo.fog_aerial_perspective = env->fog_aerial_perspective; + scene_state.ubo.fog_enabled = environment_get_fog_enabled(p_render_data->environment); + scene_state.ubo.fog_density = environment_get_fog_density(p_render_data->environment); + scene_state.ubo.fog_height = environment_get_fog_height(p_render_data->environment); + scene_state.ubo.fog_height_density = environment_get_fog_height_density(p_render_data->environment); + scene_state.ubo.fog_aerial_perspective = environment_get_fog_aerial_perspective(p_render_data->environment); - Color fog_color = env->fog_light_color.srgb_to_linear(); - float fog_energy = env->fog_light_energy; + Color fog_color = environment_get_fog_light_color(p_render_data->environment).srgb_to_linear(); + float fog_energy = environment_get_fog_light_energy(p_render_data->environment); scene_state.ubo.fog_light_color[0] = fog_color.r * fog_energy; scene_state.ubo.fog_light_color[1] = fog_color.g * fog_energy; scene_state.ubo.fog_light_color[2] = fog_color.b * fog_energy; - scene_state.ubo.fog_sun_scatter = env->fog_sun_scatter; + scene_state.ubo.fog_sun_scatter = environment_get_fog_sun_scatter(p_render_data->environment); } else { } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + if (scene_state.ubo_buffer == 0) { glGenBuffers(1, &scene_state.ubo_buffer); } glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); + + if (p_render_data->view_count > 1) { + if (scene_state.multiview_buffer == 0) { + glGenBuffers(1, &scene_state.multiview_buffer); + } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } } // Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer @@ -1714,7 +1414,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b int num_lights = lights.size(); for (int i = 0; i < num_lights; i++) { - LightInstance *li = light_instance_owner.get_or_null(lights[i]); + GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(lights[i]); if (!li) { continue; } @@ -1741,7 +1441,17 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b float sign = light_storage->light_is_negative(base) ? -1 : 1; - light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + if (is_using_physical_light_units()) { + light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } else { + light_data.energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); light_data.color[0] = linear_col.r; @@ -1749,7 +1459,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.color[2] = linear_col.b; float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); - light_data.size = 1.0 - Math::cos(Math::deg2rad(size)); //angle to cosine offset + light_data.size = 1.0 - Math::cos(Math::deg_to_rad(size)); //angle to cosine offset light_data.specular = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR); @@ -1809,20 +1519,20 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b } if (r_omni_light_count) { - SortArray<InstanceSort<LightInstance>> sorter; + SortArray<InstanceSort<GLES3::LightInstance>> sorter; sorter.sort(scene_state.omni_light_sort, r_omni_light_count); } if (r_spot_light_count) { - SortArray<InstanceSort<LightInstance>> sorter; + SortArray<InstanceSort<GLES3::LightInstance>> sorter; sorter.sort(scene_state.spot_light_sort, r_spot_light_count); } for (uint32_t i = 0; i < (r_omni_light_count + r_spot_light_count); i++) { uint32_t index = (i < r_omni_light_count) ? i : i - (r_omni_light_count); LightData &light_data = (i < r_omni_light_count) ? scene_state.omni_lights[index] : scene_state.spot_lights[index]; - //RS::LightType type = (i < omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; - LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; + RS::LightType type = (i < r_omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; + GLES3::LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; RID base = li->light; Transform3D light_transform = li->transform; @@ -1865,7 +1575,26 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b } } - float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * fade; + + if (is_using_physical_light_units()) { + energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (type == RS::LIGHT_OMNI) { + energy *= 1.0 / (Math_PI * 4.0); + } else { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + energy *= 1.0 / Math_PI; + } + } else { + energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } light_data.color[0] = linear_col.r * energy; light_data.color[1] = linear_col.g * energy; @@ -1876,58 +1605,61 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b light_data.inv_spot_attenuation = 1.0f / light_storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ATTENUATION); float spot_angle = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPOT_ANGLE); - light_data.cos_spot_angle = Math::cos(Math::deg2rad(spot_angle)); + light_data.cos_spot_angle = Math::cos(Math::deg_to_rad(spot_angle)); light_data.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0; - light_data.shadow_enabled = false; + light_data.shadow_opacity = 0.0; } // TODO, to avoid stalls, should rotate between 3 buffers based on frame index. // TODO, consider mapping the buffer as in 2D + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_OMNILIGHT_UNIFORM_LOCATION, scene_state.omni_light_buffer); if (r_omni_light_count) { - glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_OMNILIGHT_UNIFORM_LOCATION, scene_state.omni_light_buffer); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightData) * r_omni_light_count, scene_state.omni_lights); } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_SPOTLIGHT_UNIFORM_LOCATION, scene_state.spot_light_buffer); if (r_spot_light_count) { - glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_SPOTLIGHT_UNIFORM_LOCATION, scene_state.spot_light_buffer); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightData) * r_spot_light_count, scene_state.spot_lights); } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, scene_state.directional_light_buffer); if (r_directional_light_count) { - glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, scene_state.directional_light_buffer); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLightData) * r_directional_light_count, scene_state.directional_lights); } glBindBuffer(GL_UNIFORM_BUFFER, 0); } -void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RenderingMethod::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); - RenderBuffers *rb = nullptr; + Ref<RenderSceneBuffersGLES3> rb; if (p_render_buffers.is_valid()) { - rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); + rb = p_render_buffers; + ERR_FAIL_COND(rb.is_null()); } + GLES3::RenderTarget *rt = texture_storage->get_render_target(rb->render_target); + ERR_FAIL_COND(!rt); + // Assign render data // Use the format from rendererRD RenderDataGLES3 render_data; { - render_data.render_buffers = p_render_buffers; - render_data.transparent_bg = rb->is_transparent; + render_data.render_buffers = rb; + render_data.transparent_bg = rb.is_valid() ? rb->is_transparent : false; // Our first camera is used by default render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); render_data.cam_projection = p_camera_data->main_projection; - render_data.view_projection[0] = p_camera_data->main_projection; render_data.cam_orthogonal = p_camera_data->is_orthogonal; render_data.view_count = p_camera_data->view_count; for (uint32_t v = 0; v < p_camera_data->view_count; v++) { + render_data.view_eye_offset[v] = p_camera_data->view_offset[v].origin; render_data.view_projection[v] = p_camera_data->view_projection[v]; } @@ -1938,13 +1670,12 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * render_data.lights = &p_lights; render_data.reflection_probes = &p_reflection_probes; render_data.environment = p_environment; - render_data.camera_effects = p_camera_effects; + render_data.camera_attributes = p_camera_attributes; render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe_pass = p_reflection_probe_pass; // this should be the same for all cameras.. render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); - render_data.lod_camera_plane = Plane(-p_camera_data->main_transform.basis.get_column(Vector3::AXIS_Z), p_camera_data->main_transform.get_origin()); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { render_data.screen_mesh_lod_threshold = 0.0; @@ -1967,18 +1698,16 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * // Fill Light lists here ////////// - GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_variables_get_uniform_buffer(); + GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_parameters_get_uniform_buffer(); glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer); Color clear_color; if (p_render_buffers.is_valid()) { clear_color = texture_storage->render_target_get_clear_request_color(rb->render_target); } else { - clear_color = storage->get_default_clear_color(); + clear_color = texture_storage->get_default_clear_color(); } - Environment *env = environment_owner.get_or_null(p_environment); - bool fb_cleared = false; Size2i screen_size; @@ -1988,10 +1717,10 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; SceneState::TonemapUBO tonemap_ubo; - if (env) { - tonemap_ubo.exposure = env->exposure; - tonemap_ubo.white = env->white; - tonemap_ubo.tonemapper = int32_t(env->tone_mapper); + if (render_data.environment.is_valid()) { + tonemap_ubo.exposure = environment_get_exposure(render_data.environment); + tonemap_ubo.white = environment_get_white(render_data.environment); + tonemap_ubo.tonemapper = int32_t(environment_get_tone_mapper(render_data.environment)); } if (scene_state.tonemap_buffer == 0) { @@ -2001,8 +1730,22 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW); + scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization. + + bool flip_y = !render_data.reflection_probe.is_valid(); + + if (rt->overridden.color.is_valid()) { + // If we've overridden the render target's color texture, then don't render upside down. + // We're probably rendering directly to an XR device. + flip_y = false; + } + if (!flip_y) { + // If we're rendering right-side up, then we need to change the winding order. + glFrontFace(GL_CW); + } + _setup_lights(&render_data, false, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count); - _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, !render_data.reflection_probe.is_valid(), clear_color, false); + _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, flip_y, clear_color, false); _fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR); render_list[RENDER_LIST_OPAQUE].sort_by_key(); @@ -2011,28 +1754,35 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * bool draw_sky = false; bool draw_sky_fog_only = false; bool keep_color = false; + float sky_energy_multiplier = 1.0; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black - } else if (env) { - RS::EnvironmentBG bg_mode = env->background; - float bg_energy = env->bg_energy; + } else if (render_data.environment.is_valid()) { + RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(render_data.environment); + bg_energy_multiplier *= environment_get_bg_intensity(render_data.environment); + + if (render_data.camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(render_data.camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - if (env->fog_enabled) { + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; + if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); } } break; case RS::ENV_BG_COLOR: { - clear_color = env->bg_color; - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; - if (env->fog_enabled) { + clear_color = environment_get_bg_color(render_data.environment); + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; + if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); } @@ -2052,20 +1802,22 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * } } // setup sky if used for ambient, reflections, or background - if (draw_sky || draw_sky_fog_only || env->reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || env->ambient_source == RS::ENV_AMBIENT_SOURCE_SKY) { + if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) { RENDER_TIMESTAMP("Setup Sky"); - CameraMatrix projection = render_data.cam_projection; + Projection projection = render_data.cam_projection; if (render_data.reflection_probe.is_valid()) { - CameraMatrix correction; - correction.set_depth_correction(true); + Projection correction; + correction.columns[1][1] = -1.0; projection = correction * render_data.cam_projection; } - _setup_sky(env, p_render_buffers, *render_data.lights, projection, render_data.cam_transform, screen_size); + sky_energy_multiplier *= bg_energy_multiplier; - if (env->sky.is_valid()) { - if (env->reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || env->ambient_source == RS::ENV_AMBIENT_SOURCE_SKY || (env->reflection_source == RS::ENV_REFLECTION_SOURCE_BG && env->background == RS::ENV_BG_SKY)) { - _update_sky_radiance(env, projection, render_data.cam_transform); + _setup_sky(&render_data, *render_data.lights, projection, render_data.cam_transform, screen_size); + + if (environment_get_sky(render_data.environment).is_valid()) { + if (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY || (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_BG && environment_get_background(render_data.environment) == RS::ENV_BG_SKY)) { + _update_sky_radiance(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier); } } else { // do not try to draw sky if invalid @@ -2074,7 +1826,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * } } - glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); glViewport(0, 0, rb->width, rb->height); // Do depth prepass if it's explicitly enabled @@ -2145,7 +1897,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS; } - if (!env || (env && !env->fog_enabled)) { + if (render_data.environment.is_null() || (render_data.environment.is_valid() && !environment_get_fog_enabled(render_data.environment))) { spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG; } } @@ -2169,7 +1921,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; - _draw_sky(env, render_data.cam_projection, render_data.cam_transform); + _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -2180,8 +1932,13 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * _render_list_template<PASS_MODE_COLOR_TRANSPARENT>(&render_list_params_alpha, &render_data, 0, render_list[RENDER_LIST_ALPHA].elements.size(), true); - if (p_render_buffers.is_valid()) { - _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); + if (!flip_y) { + // Restore the default winding order. + glFrontFace(GL_CCW); + } + + if (rb.is_valid()) { + _render_buffers_debug_draw(rb, p_shadow_atlas, p_occluder_debug_tex); } glDisable(GL_BLEND); texture_storage->render_target_disable_clear_request(rb->render_target); @@ -2200,8 +1957,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLES3::SceneMaterialData *prev_material_data = nullptr; GLES3::SceneShaderData *prev_shader = nullptr; GeometryInstanceGLES3 *prev_inst = nullptr; + SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR; + SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized + + // @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it. + uint32_t base_spec_constants = 0; - SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized. + if (p_render_data->view_count > 1) { + base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW; + } switch (p_pass_mode) { case PASS_MODE_COLOR: @@ -2216,12 +1980,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } break; } - if (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { - Environment *env = environment_owner.get_or_null(p_render_data->environment); + if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 2); GLuint texture_to_bind = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_CUBEMAP_BLACK))->tex_id; - if (env) { - Sky *sky = sky_owner.get_or_null(env->sky); + if (p_render_data->environment.is_valid()) { + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_render_data->environment)); if (sky && sky->radiance != 0) { texture_to_bind = sky->radiance; // base_spec_constant |= USE_RADIANCE_MAP; @@ -2230,6 +1993,8 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } + bool should_request_redraw = false; + for (uint32_t i = p_from_element; i < p_to_element; i++) { const GeometryInstanceSurface *surf = p_params->elements[i]; GeometryInstanceGLES3 *inst = surf->owner; @@ -2242,13 +2007,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, continue; } - //uint32_t base_spec_constants = p_params->spec_constant_base_flags; - GLES3::SceneShaderData *shader; GLES3::SceneMaterialData *material_data; void *mesh_surface; - if (p_pass_mode == PASS_MODE_SHADOW) { + if constexpr (p_pass_mode == PASS_MODE_SHADOW) { shader = surf->shader_shadow; material_data = surf->material_shadow; mesh_surface = surf->surface_shadow; @@ -2262,7 +2025,12 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, continue; } - if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { + //request a redraw if one of the shaders uses TIME + if (shader->uses_time) { + should_request_redraw = true; + } + + if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { if (scene_state.current_depth_test != shader->depth_test) { if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) { glDisable(GL_DEPTH_TEST); @@ -2289,9 +2057,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, scene_state.current_depth_draw = shader->depth_draw; } - if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { + if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { GLES3::SceneShaderData::BlendMode desired_blend_mode; - if (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { + if constexpr (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { desired_blend_mode = GLES3::SceneShaderData::BLEND_MODE_ADD; } else { desired_blend_mode = shader->blend_mode; @@ -2382,16 +2150,17 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); if (prev_vertex_array_gl != vertex_array_gl) { - glBindVertexArray(vertex_array_gl); + if (vertex_array_gl != 0) { + glBindVertexArray(vertex_array_gl); + } prev_vertex_array_gl = vertex_array_gl; } - bool use_index_buffer = false; + bool use_index_buffer = index_array_gl != 0; if (prev_index_array_gl != index_array_gl) { if (index_array_gl != 0) { // Bind index each time so we can use LODs glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); - use_index_buffer = true; } prev_index_array_gl = index_array_gl; } @@ -2406,50 +2175,96 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, prev_material_data = material_data; } - if (prev_shader != shader) { - material_storage->shaders.scene_shader.version_bind_shader(shader->version, shader_variant); + SceneShaderGLES3::ShaderVariant instance_variant = shader_variant; + if (inst->instance_count > 0) { + instance_variant = SceneShaderGLES3::ShaderVariant(1 + int(shader_variant)); + } + + if (prev_shader != shader || prev_variant != instance_variant) { + material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants); float opaque_prepass_threshold = 0.0; - if (p_pass_mode == PASS_MODE_DEPTH) { + if constexpr (p_pass_mode == PASS_MODE_DEPTH) { opaque_prepass_threshold = 0.99; - } else if (p_pass_mode == PASS_MODE_SHADOW) { + } else if constexpr (p_pass_mode == PASS_MODE_SHADOW) { opaque_prepass_threshold = 0.1; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, shader_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, base_spec_constants); prev_shader = shader; + prev_variant = instance_variant; } - if (prev_inst != inst) { + if (prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) { // Rebind the light indices. - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, shader_variant); - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, shader_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant, base_spec_constants); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant, base_spec_constants); if (inst->omni_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, shader_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); } if (inst->spot_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, shader_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); } prev_inst = inst; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, shader_variant); - - if (use_index_buffer) { - glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants); + if (inst->instance_count > 0) { + // Using MultiMesh. + // Bind instance buffers. + + GLuint multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(inst->data->base); + glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); + uint32_t multimesh_stride = mesh_storage->multimesh_get_stride(inst->data->base); + glEnableVertexAttribArray(12); + glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribDivisor(12, 1); + glEnableVertexAttribArray(13); + glVertexAttribPointer(13, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribDivisor(13, 1); + glEnableVertexAttribArray(14); + glVertexAttribPointer(14, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 8)); + glVertexAttribDivisor(14, 1); + + if (mesh_storage->multimesh_uses_colors(inst->data->base) || mesh_storage->multimesh_uses_custom_data(inst->data->base)) { + glEnableVertexAttribArray(15); + glVertexAttribIPointer(15, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(mesh_storage->multimesh_get_color_offset(inst->data->base) * sizeof(float))); + glVertexAttribDivisor(15, 1); + } + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), inst->instance_count); + } } else { - glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface)); + // Using regular Mesh. + if (use_index_buffer) { + glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface), mesh_storage->mesh_surface_get_index_type(mesh_surface), 0); + } else { + glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(mesh_surface)); + } } + if (inst->instance_count > 0) { + glDisableVertexAttribArray(12); + glDisableVertexAttribArray(13); + glDisableVertexAttribArray(14); + glDisableVertexAttribArray(15); + } + } + + // Make the actual redraw request + if (should_request_redraw) { + RenderingServerDefault::redraw_request(); } } -void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { } -void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) { +void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) { } void RasterizerSceneGLES3::set_time(double p_time, double p_step) { @@ -2461,74 +2276,10 @@ void RasterizerSceneGLES3::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_dra debug_draw = p_debug_draw; } -RID RasterizerSceneGLES3::render_buffers_create() { - RenderBuffers rb; - return render_buffers_owner.make_rid(rb); -} - -void RasterizerSceneGLES3::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_fsr_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - - RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers); - ERR_FAIL_COND(!rb); - - //rb->internal_width = p_internal_width; // ignore for now - //rb->internal_height = p_internal_height; - rb->width = p_width; - rb->height = p_height; - //rb->fsr_sharpness = p_fsr_sharpness; - rb->render_target = p_render_target; - //rb->msaa = p_msaa; - //rb->screen_space_aa = p_screen_space_aa; - //rb->use_debanding = p_use_debanding; - //rb->view_count = p_view_count; - - _free_render_buffer_data(rb); - - GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); - - rb->is_transparent = rt->is_transparent; - - // framebuffer - glGenFramebuffers(1, &rb->framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer); - - glBindTexture(GL_TEXTURE_2D, rt->color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); - - glGenTextures(1, &rb->depth_texture); - glBindTexture(GL_TEXTURE_2D, rb->depth_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rb->depth_texture, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - _free_render_buffer_data(rb); - WARN_PRINT("Could not create 3D renderbuffer, status: " + texture_storage->get_framebuffer_error(status)); - return; - } -} - -void RasterizerSceneGLES3::_free_render_buffer_data(RenderBuffers *rb) { - if (rb->depth_texture) { - glDeleteTextures(1, &rb->depth_texture); - rb->depth_texture = 0; - } - if (rb->framebuffer) { - glDeleteFramebuffers(1, &rb->framebuffer); - rb->framebuffer = 0; - } +Ref<RenderSceneBuffers> RasterizerSceneGLES3::render_buffers_create() { + Ref<RenderSceneBuffersGLES3> rb; + rb.instantiate(); + return rb; } //clear render buffers @@ -2556,7 +2307,7 @@ void RasterizerSceneGLES3::_free_render_buffer_data(RenderBuffers *rb) { } */ -void RasterizerSceneGLES3::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { +void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { } void RasterizerSceneGLES3::gi_set_use_half_resolution(bool p_enable) { @@ -2575,28 +2326,23 @@ void RasterizerSceneGLES3::sub_surface_scattering_set_quality(RS::SubSurfaceScat void RasterizerSceneGLES3::sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) { } -TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) { +TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) { return TypedArray<Image>(); } bool RasterizerSceneGLES3::free(RID p_rid) { - if (environment_owner.owns(p_rid)) { - environment_owner.free(p_rid); + if (is_environment(p_rid)) { + environment_free(p_rid); } else if (sky_owner.owns(p_rid)) { Sky *sky = sky_owner.get_or_null(p_rid); ERR_FAIL_COND_V(!sky, false); _free_sky_data(sky); sky_owner.free(p_rid); - } else if (render_buffers_owner.owns(p_rid)) { - RenderBuffers *rb = render_buffers_owner.get_or_null(p_rid); - ERR_FAIL_COND_V(!rb, false); - _free_render_buffer_data(rb); - render_buffers_owner.free(p_rid); - - } else if (light_instance_owner.owns(p_rid)) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_rid); - ERR_FAIL_COND_V(!light_instance, false); - light_instance_owner.free(p_rid); + } else if (GLES3::LightStorage::get_singleton()->owns_light_instance(p_rid)) { + GLES3::LightStorage::get_singleton()->light_instance_free(p_rid); + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + //not much to delete, just free it + RSG::camera_attributes->camera_attributes_free(p_rid); } else { return false; } @@ -2616,11 +2362,14 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) { void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) { } -RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) { +RasterizerSceneGLES3::RasterizerSceneGLES3() { + singleton = this; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); - storage = p_storage; + // Quality settings. + use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"); { // Setup Lights @@ -2630,13 +2379,13 @@ RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) { uint32_t light_buffer_size = config->max_renderable_lights * sizeof(LightData); scene_state.omni_lights = memnew_arr(LightData, config->max_renderable_lights); - scene_state.omni_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + scene_state.omni_light_sort = memnew_arr(InstanceSort<GLES3::LightInstance>, config->max_renderable_lights); glGenBuffers(1, &scene_state.omni_light_buffer); glBindBuffer(GL_UNIFORM_BUFFER, scene_state.omni_light_buffer); glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); scene_state.spot_lights = memnew_arr(LightData, config->max_renderable_lights); - scene_state.spot_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + scene_state.spot_light_sort = memnew_arr(InstanceSort<GLES3::LightInstance>, config->max_renderable_lights); glGenBuffers(1, &scene_state.spot_light_buffer); glBindBuffer(GL_UNIFORM_BUFFER, scene_state.spot_light_buffer); glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); @@ -2663,7 +2412,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) { { String global_defines; - global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now + global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n"; global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n"; global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "\n"; @@ -2677,7 +2426,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage) { scene_globals.default_shader = material_storage->shader_allocate(); material_storage->shader_initialize(scene_globals.default_shader); material_storage->shader_set_code(scene_globals.default_shader, R"( -// Default 3D material shader (clustered). +// Default 3D material shader. shader_type spatial; @@ -2702,13 +2451,17 @@ void fragment() { sky_globals.ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples"); String global_defines; - global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now + global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(sky_globals.max_directional_lights) + "\n"; material_storage->shaders.sky_shader.initialize(global_defines); sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create(); material_storage->shaders.sky_shader.version_bind_shader(sky_globals.shader_default_version, SkyShaderGLES3::MODE_BACKGROUND); + } - material_storage->shaders.cubemap_filter_shader.initialize(); + { + String global_defines; + global_defines += "\n#define MAX_SAMPLE_COUNT " + itos(sky_globals.ggx_samples) + "\n"; + material_storage->shaders.cubemap_filter_shader.initialize(global_defines); scene_globals.cubemap_filter_shader_version = material_storage->shaders.cubemap_filter_shader.version_create(); material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, CubemapFilterShaderGLES3::MODE_DEFAULT); } @@ -2778,39 +2531,12 @@ void sky() { glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } - // Radical inverse vdc cache texture used for cubemap filtering. - { - glGenTextures(1, &sky_globals.radical_inverse_vdc_cache_tex); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, sky_globals.radical_inverse_vdc_cache_tex); - - uint8_t radical_inverse[512]; - - for (uint32_t i = 0; i < 512; i++) { - uint32_t bits = i; - - bits = (bits << 16) | (bits >> 16); - bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); - bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); - bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); - bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); - - float value = float(bits) * 2.3283064365386963e-10; - radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); - } - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 512, 1, 0, GL_RED, GL_UNSIGNED_BYTE, radical_inverse); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //need this for proper sampling - - glBindTexture(GL_TEXTURE_2D, 0); - } #ifdef GLES_OVER_GL glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); #endif + + // MultiMesh may read from color when color is disabled, so make sure that the color defaults to white instead of black; + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); } RasterizerSceneGLES3::~RasterizerSceneGLES3() { @@ -2826,21 +2552,23 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() { // Scene Shader GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version); GLES3::MaterialStorage::get_singleton()->shaders.cubemap_filter_shader.version_free(scene_globals.cubemap_filter_shader_version); - storage->free(scene_globals.default_material); - storage->free(scene_globals.default_shader); + RSG::material_storage->material_free(scene_globals.default_material); + RSG::material_storage->shader_free(scene_globals.default_shader); // Sky Shader GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_free(sky_globals.shader_default_version); - storage->free(sky_globals.default_material); - storage->free(sky_globals.default_shader); - storage->free(sky_globals.fog_material); - storage->free(sky_globals.fog_shader); + RSG::material_storage->material_free(sky_globals.default_material); + RSG::material_storage->shader_free(sky_globals.default_shader); + RSG::material_storage->material_free(sky_globals.fog_material); + RSG::material_storage->shader_free(sky_globals.fog_shader); glDeleteBuffers(1, &sky_globals.screen_triangle); glDeleteVertexArrays(1, &sky_globals.screen_triangle_array); glDeleteTextures(1, &sky_globals.radical_inverse_vdc_cache_tex); glDeleteBuffers(1, &sky_globals.directional_light_buffer); memdelete_arr(sky_globals.directional_lights); memdelete_arr(sky_globals.last_frame_directional_lights); + + singleton = nullptr; } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index f76861bc90..3a759425e2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -28,16 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RASTERIZER_SCENE_OPENGL_H -#define RASTERIZER_SCENE_OPENGL_H +#ifndef RASTERIZER_SCENE_GLES3_H +#define RASTERIZER_SCENE_GLES3_H #ifdef GLES3_ENABLED -#include "core/math/camera_matrix.h" +#include "core/math/projection.h" #include "core/templates/paged_allocator.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" -#include "rasterizer_storage_gles3.h" #include "scene/resources/mesh.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_scene_render.h" @@ -45,6 +44,10 @@ #include "shader_gles3.h" #include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/sky.glsl.gen.h" +#include "storage/light_storage.h" +#include "storage/material_storage.h" +#include "storage/render_scene_buffers_gles3.h" +#include "storage/utilities.h" enum RenderListType { RENDER_LIST_OPAQUE, //used for opaque objects @@ -71,6 +74,7 @@ enum SceneUniformLocation { SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SCENE_MULTIVIEW_UNIFORM_LOCATION, }; enum SkyUniformLocation { @@ -87,44 +91,45 @@ enum { SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, SPEC_CONSTANT_DISABLE_FOG = 4, + SPEC_CONSTANT_USE_RADIANCE_MAP = 5, + SPEC_CONSTANT_USE_MULTIVIEW = 6, }; struct RenderDataGLES3 { - RID render_buffers = RID(); + Ref<RenderSceneBuffersGLES3> render_buffers; bool transparent_bg = false; Transform3D cam_transform = Transform3D(); Transform3D inv_cam_transform = Transform3D(); - CameraMatrix cam_projection = CameraMatrix(); + Projection cam_projection = Projection(); bool cam_orthogonal = false; // For stereo rendering uint32_t view_count = 1; - CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS]; + Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS]; + Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS]; float z_near = 0.0; float z_far = 0.0; - const PagedArray<RendererSceneRender::GeometryInstance *> *instances = nullptr; + const PagedArray<RenderGeometryInstance *> *instances = nullptr; const PagedArray<RID> *lights = nullptr; const PagedArray<RID> *reflection_probes = nullptr; RID environment = RID(); - RID camera_effects = RID(); + RID camera_attributes = RID(); RID reflection_probe = RID(); int reflection_probe_pass = 0; float lod_distance_multiplier = 0.0; - Plane lod_camera_plane = Plane(); float screen_mesh_lod_threshold = 0.0; uint32_t directional_light_count = 0; uint32_t spot_light_count = 0; uint32_t omni_light_count = 0; - RendererScene::RenderInfo *render_info = nullptr; + RenderingMethod::RenderInfo *render_info = nullptr; }; -class RasterizerStorageGLES3; class RasterizerCanvasGLES3; class RasterizerSceneGLES3 : public RendererSceneRender { @@ -164,7 +169,7 @@ private: float inv_spot_attenuation; float cos_spot_angle; float specular_amount; - uint32_t shadow_enabled; + float shadow_opacity; }; static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); @@ -181,35 +186,7 @@ private: }; static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes"); - struct LightInstance { - RS::LightType light_type = RS::LIGHT_DIRECTIONAL; - - AABB aabb; - RID self; - RID light; - Transform3D transform; - - Vector3 light_vector; - Vector3 spot_vector; - float linear_att = 0.0; - - uint64_t shadow_pass = 0; - uint64_t last_scene_pass = 0; - uint64_t last_scene_shadow_pass = 0; - uint64_t last_pass = 0; - uint32_t cull_mask = 0; - uint32_t light_directional_index = 0; - - Rect2 directional_rect; - - uint32_t gl_id = -1; - - LightInstance() {} - }; - - mutable RID_Owner<LightInstance> light_instance_owner; - - struct GeometryInstanceGLES3; + class GeometryInstanceGLES3; // Cached data for drawing surfaces struct GeometryInstanceSurface { @@ -264,33 +241,16 @@ private: GeometryInstanceGLES3 *owner = nullptr; }; - struct GeometryInstanceGLES3 : public GeometryInstance { + class GeometryInstanceGLES3 : public RenderGeometryInstanceBase { + public: //used during rendering - bool mirror = false; - bool non_uniform_scale = false; - float lod_bias = 0.0; - float lod_model_scale = 1.0; - AABB transformed_aabb; //needed for LOD - float depth = 0; - uint32_t flags_cache = 0; bool store_transform_cache = true; - int32_t shader_parameters_offset = -1; - uint32_t layer_mask = 1; - uint32_t instance_count = 0; + int32_t instance_count = 0; - RID mesh_instance; bool can_sdfgi = false; bool using_projectors = false; bool using_softshadows = false; - bool fade_near = false; - float fade_near_begin = 0; - float fade_near_end = 0; - bool fade_far = false; - float fade_far_begin = 0; - float fade_far_end = 0; - float force_alpha = 1.0; - float parent_fade_alpha = 1.0; uint32_t omni_light_count = 0; LocalVector<RID> omni_lights; @@ -300,52 +260,40 @@ private: LocalVector<uint32_t> spot_light_gl_cache; //used during setup - uint32_t base_flags = 0; - Transform3D transform; GeometryInstanceSurface *surface_caches = nullptr; SelfList<GeometryInstanceGLES3> dirty_list_element; - struct Data { - //data used less often goes into regular heap - RID base; - RS::InstanceType base_type; - - RID skeleton; - Vector<RID> surface_materials; - RID material_override; - RID material_overlay; - AABB aabb; - - bool use_dynamic_gi = false; - bool use_baked_light = false; - bool cast_double_sided_shadows = false; - bool mirror = false; - bool dirty_dependencies = false; + GeometryInstanceGLES3() : + dirty_list_element(this) {} - RendererStorage::DependencyTracker dependency_tracker; - }; + virtual void _mark_dirty() override; + virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override; + virtual void set_lightmap_capture(const Color *p_sh9) override; - Data *data = nullptr; + virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override; + virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {} + virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {} + virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {} - GeometryInstanceGLES3() : - dirty_list_element(this) {} + virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override {} }; enum { - INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 5, - INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 6, - INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 8, - INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 9, - INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 10, - INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 11, + INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4, + INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5, + INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7, + INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8, + INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9, + INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 10, + INSTANCE_DATA_FLAG_PARTICLES = 1 << 11, INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12, INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13, INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14, INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15, }; - static void _geometry_instance_dependency_changed(RendererStorage::DependencyChangedNotification p_notification, RendererStorage::DependencyTracker *p_tracker); - static void _geometry_instance_dependency_deleted(const RID &p_dependency, RendererStorage::DependencyTracker *p_tracker); + static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker); + static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker); SelfList<GeometryInstanceGLES3>::List geometry_instance_dirty_list; @@ -356,8 +304,7 @@ private: void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh); void _geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, RID p_mat_src, RID p_mesh); void _geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh); - void _geometry_instance_mark_dirty(GeometryInstance *p_geometry_instance); - void _geometry_instance_update(GeometryInstance *p_geometry_instance); + void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance); void _update_dirty_geometry_instances(); struct SceneState { @@ -374,7 +321,7 @@ private: float ambient_color_sky_mix; uint32_t material_uv2_mode; - float pad2; + float emissive_exposure_normalization; uint32_t use_ambient_light = 0; uint32_t use_ambient_cubemap = 0; @@ -387,7 +334,7 @@ private: uint32_t directional_light_count; float z_far; float z_near; - float pad1; + float IBL_exposure_normalization; uint32_t fog_enabled; float fog_density; @@ -399,6 +346,13 @@ private: }; static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + struct MultiviewUBO { + float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4]; + }; + static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes"); + struct TonemapUBO { float exposure = 1.0; float white = 1.0; @@ -409,6 +363,8 @@ private: UBO ubo; GLuint ubo_buffer = 0; + MultiviewUBO multiview_ubo; + GLuint multiview_buffer = 0; GLuint tonemap_buffer = 0; bool used_depth_prepass = false; @@ -426,8 +382,8 @@ private: LightData *omni_lights = nullptr; LightData *spot_lights = nullptr; - InstanceSort<LightInstance> *omni_light_sort; - InstanceSort<LightInstance> *spot_light_sort; + InstanceSort<GLES3::LightInstance> *omni_light_sort; + InstanceSort<GLES3::LightInstance> *spot_light_sort; GLuint omni_light_buffer = 0; GLuint spot_light_buffer = 0; uint32_t omni_light_count = 0; @@ -520,137 +476,26 @@ protected: double time; double time_step = 0; - struct RenderBuffers { - int internal_width = 0; - int internal_height = 0; - int width = 0; - int height = 0; - //float fsr_sharpness = 0.2f; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; - //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; - //bool use_debanding = false; - //uint32_t view_count = 1; - - bool is_transparent = false; - - RID render_target; - GLuint internal_texture = 0; // Used for rendering when post effects are enabled - GLuint depth_texture = 0; // Main depth texture - GLuint framebuffer = 0; // Main framebuffer, contains internal_texture and depth_texture or render_target->color and depth_texture - - //built-in textures used for ping pong image processing and blurring - struct Blur { - RID texture; - - struct Mipmap { - RID texture; - int width; - int height; - GLuint fbo; - }; - - Vector<Mipmap> mipmaps; - }; - - Blur blur[2]; //the second one starts from the first mipmap - }; - bool screen_space_roughness_limiter = false; float screen_space_roughness_limiter_amount = 0.25; float screen_space_roughness_limiter_limit = 0.18; - mutable RID_Owner<RenderBuffers, true> render_buffers_owner; + void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); - void _free_render_buffer_data(RenderBuffers *rb); - void _allocate_blur_textures(RenderBuffers *rb); - void _allocate_depth_backbuffer_textures(RenderBuffers *rb); + /* Camera Attributes */ - void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); - - /* Environment */ + struct CameraAttributes { + float exposure_multiplier = 1.0; + float exposure_normalization = 1.0; + }; - struct Environment { - // BG - RS::EnvironmentBG background = RS::ENV_BG_CLEAR_COLOR; - RID sky; - float sky_custom_fov = 0.0; - Basis sky_orientation; - Color bg_color; - float bg_energy = 1.0; - int canvas_max_layer = 0; - RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG; - Color ambient_light; - float ambient_light_energy = 1.0; - float ambient_sky_contribution = 1.0; - RS::EnvironmentReflectionSource reflection_source = RS::ENV_REFLECTION_SOURCE_BG; - Color ao_color; - - /// Tonemap - - RS::EnvironmentToneMapper tone_mapper; - float exposure = 1.0; - float white = 1.0; - bool auto_exposure = false; - float min_luminance = 0.2; - float max_luminance = 8.0; - float auto_exp_speed = 0.2; - float auto_exp_scale = 0.5; - uint64_t auto_exposure_version = 0; - - // Fog - bool fog_enabled = false; - Color fog_light_color = Color(0.5, 0.6, 0.7); - float fog_light_energy = 1.0; - float fog_sun_scatter = 0.0; - float fog_density = 0.001; - float fog_height = 0.0; - float fog_height_density = 0.0; //can be negative to invert effect - float fog_aerial_perspective = 0.0; + bool use_physical_light_units = false; + mutable RID_Owner<CameraAttributes, true> camera_attributes_owner; - /// Glow - bool glow_enabled = false; - Vector<float> glow_levels; - float glow_intensity = 0.8; - float glow_strength = 1.0; - float glow_bloom = 0.0; - float glow_mix = 0.01; - RS::EnvironmentGlowBlendMode glow_blend_mode = RS::ENV_GLOW_BLEND_MODE_SOFTLIGHT; - float glow_hdr_bleed_threshold = 1.0; - float glow_hdr_luminance_cap = 12.0; - float glow_hdr_bleed_scale = 2.0; - float glow_map_strength = 1.0; - RID glow_map = RID(); - - /// SSAO - bool ssao_enabled = false; - float ssao_radius = 1.0; - float ssao_intensity = 2.0; - float ssao_power = 1.5; - float ssao_detail = 0.5; - float ssao_horizon = 0.06; - float ssao_sharpness = 0.98; - float ssao_direct_light_affect = 0.0; - float ssao_ao_channel_affect = 0.0; - - /// SSR - bool ssr_enabled = false; - int ssr_max_steps = 64; - float ssr_fade_in = 0.15; - float ssr_fade_out = 2.0; - float ssr_depth_tolerance = 0.2; - - /// Adjustments - bool adjustments_enabled = false; - float adjustments_brightness = 1.0f; - float adjustments_contrast = 1.0f; - float adjustments_saturation = 1.0f; - bool use_1d_color_correction = false; - RID color_correction = RID(); - }; + /* Environment */ RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM; bool ssao_half_size = false; - bool ssao_using_half_size = false; float ssao_adaptive_target = 0.5; int ssao_blur_passes = 2; float ssao_fadeout_from = 50.0; @@ -660,10 +505,6 @@ protected: bool glow_high_quality = false; RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; - static uint64_t auto_exposure_counter; - - mutable RID_Owner<Environment, true> environment_owner; - /* Sky */ struct SkyGlobals { @@ -719,6 +560,7 @@ protected: bool dirty = false; int processing_layer = 0; Sky *dirty_list = nullptr; + float baked_exposure = 1.0; //State to track when radiance cubemap needs updating GLES3::SkyMaterialData *prev_material; @@ -729,67 +571,34 @@ protected: Sky *dirty_sky_list = nullptr; mutable RID_Owner<Sky, true> sky_owner; - void _setup_sky(Environment *p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); + void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); - void _update_sky_radiance(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform); + void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _filter_sky_radiance(Sky *p_sky, int p_base_layer); - void _draw_sky(Environment *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform); + void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _free_sky_data(Sky *p_sky); public: - RasterizerStorageGLES3 *storage; - RasterizerCanvasGLES3 *canvas; - - GeometryInstance *geometry_instance_create(RID p_base) override; - void geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) override; - void geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) override; - void geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) override; - void geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) override; - void geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) override; - void geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) override; - void geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) override; - void geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) override; - void geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) override; - void geometry_instance_set_fade_range(GeometryInstance *p_geometry_instance, bool p_enable_near, float p_near_begin, float p_near_end, bool p_enable_far, float p_far_begin, float p_far_end) override; - void geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) override; - void geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) override; - void geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) override; - void geometry_instance_set_use_lightmap(GeometryInstance *p_geometry_instance, RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override; - void geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) override; - void geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) override; - void geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) override; + static RasterizerSceneGLES3 *get_singleton() { return singleton; } - uint32_t geometry_instance_get_pair_mask() override; - void geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) override; - void geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override; - void geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) override; - void geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override; - void geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) override; - - void geometry_instance_free(GeometryInstance *p_geometry_instance) override; + RasterizerCanvasGLES3 *canvas = nullptr; - /* SHADOW ATLAS API */ + RenderGeometryInstance *geometry_instance_create(RID p_base) override; + void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override; - RID shadow_atlas_create() override; - void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override; - void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override; - bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override; - - void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override; - int get_directional_light_shadow_size(RID p_light_intance) override; - void set_directional_shadow_count(int p_count) override; + uint32_t geometry_instance_get_pair_mask() override; /* SDFGI UPDATE */ - void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} - int sdfgi_get_pending_region_count(RID p_render_buffers) const override { + void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} + int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override { return 0; } - AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override { + AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return AABB(); } - uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override { + uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override { return 0; } @@ -801,112 +610,51 @@ public: void sky_set_mode(RID p_sky, RS::SkyMode p_mode) override; void sky_set_material(RID p_sky, RID p_material) override; Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override; + float sky_get_baked_exposure(RID p_sky) const; /* ENVIRONMENT API */ - RID environment_allocate() override; - void environment_initialize(RID p_rid) override; - void environment_set_background(RID p_env, RS::EnvironmentBG p_bg) override; - void environment_set_sky(RID p_env, RID p_sky) override; - void environment_set_sky_custom_fov(RID p_env, float p_scale) override; - void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) override; - void environment_set_bg_color(RID p_env, const Color &p_color) override; - void environment_set_bg_energy(RID p_env, float p_energy) override; - void environment_set_canvas_max_layer(RID p_env, int p_max_layer) override; - void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) override; - - void environment_set_glow(RID p_env, bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap, float p_glow_map_strength, RID p_glow_map) override; void environment_glow_set_use_bicubic_upscale(bool p_enable) override; void environment_glow_set_use_high_quality(bool p_enable) override; - void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) override; void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override; - void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_power, float p_detail, float p_horizon, float p_sharpness, float p_light_affect, float p_ao_channel_affect) override; + void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; - void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override; - void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; - void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; + void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override; - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) override; - - void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) override; - - void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) override; - void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) override; void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override; void environment_set_volumetric_fog_filter_active(bool p_enable) override; Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; - bool is_environment(RID p_env) const override; - RS::EnvironmentBG environment_get_background(RID p_env) const override; - int environment_get_canvas_max_layer(RID p_env) const override; - - RID camera_effects_allocate() override; - void camera_effects_initialize(RID p_rid) override; - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; - - void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override; - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; - - void shadows_quality_set(RS::ShadowQuality p_quality) override; - void directional_shadow_quality_set(RS::ShadowQuality p_quality) override; - - RID light_instance_create(RID p_light) override; - void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; - void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; - void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; - void light_instance_mark_visible(RID p_light_instance) override; - - _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { - LightInstance *li = light_instance_owner.get_or_null(p_light_instance); - return li->light_type; - } - _FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) { - LightInstance *li = light_instance_owner.get_or_null(p_light_instance); - return li->gl_id; + _FORCE_INLINE_ bool is_using_physical_light_units() { + return use_physical_light_units; } + void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; + void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; + RID fog_volume_instance_create(RID p_fog_volume) override; void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override; void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override; RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override; Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override; - RID reflection_atlas_create() override; - int reflection_atlas_get_size(RID p_ref_atlas) const override; - void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override; - - RID reflection_probe_instance_create(RID p_probe) override; - void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; - void reflection_probe_release_atlas_index(RID p_instance) override; - bool reflection_probe_instance_needs_redraw(RID p_instance) override; - bool reflection_probe_instance_has_reflection(RID p_instance) override; - bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; - bool reflection_probe_instance_postprocess_step(RID p_instance) override; - - RID decal_instance_create(RID p_decal) override; - void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; - - RID lightmap_instance_create(RID p_lightmap) override; - void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; - RID voxel_gi_instance_create(RID p_voxel_gi) override; void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override; bool voxel_gi_needs_update(RID p_probe) const override; - void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override; + void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects) override; void voxel_gi_set_quality(RS::VoxelGIQuality) override; - void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; - void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override; + void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; + void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override; void set_scene_pass(uint64_t p_pass) override { scene_pass = p_pass; @@ -922,8 +670,7 @@ public: return debug_draw; } - RID render_buffers_create() override; - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_internal_width, int p_internal_height, int p_width, int p_height, float p_fsr_sharpness, float p_fsr_mipmap_bias, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + Ref<RenderSceneBuffers> render_buffers_create() override; void gi_set_use_half_resolution(bool p_enable) override; void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override; @@ -932,7 +679,7 @@ public: void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override; void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override; - TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override; + TypedArray<Image> bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) override; bool free(RID p_rid) override; void update() override; @@ -941,11 +688,10 @@ public: void decals_set_filter(RS::DecalFilter p_filter) override; void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; - static RasterizerSceneGLES3 *get_singleton(); - RasterizerSceneGLES3(RasterizerStorageGLES3 *p_storage); + RasterizerSceneGLES3(); ~RasterizerSceneGLES3(); }; #endif // GLES3_ENABLED -#endif // RASTERIZER_SCENE_OPENGL_H +#endif // RASTERIZER_SCENE_GLES3_H diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp deleted file mode 100644 index 012cda953c..0000000000 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/*************************************************************************/ -/* rasterizer_storage_gles3.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "rasterizer_storage_gles3.h" - -#ifdef GLES3_ENABLED - -#include "core/config/project_settings.h" -#include "core/math/transform_3d.h" -// #include "rasterizer_canvas_gles3.h" -#include "rasterizer_scene_gles3.h" -#include "servers/rendering/shader_language.h" - -void RasterizerStorageGLES3::base_update_dependency(RID p_base, DependencyTracker *p_instance) { - if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_base)) { - GLES3::Mesh *mesh = GLES3::MeshStorage::get_singleton()->get_mesh(p_base); - p_instance->update_dependency(&mesh->dependency); - } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_base)) { - GLES3::MultiMesh *multimesh = GLES3::MeshStorage::get_singleton()->get_multimesh(p_base); - p_instance->update_dependency(&multimesh->dependency); - if (multimesh->mesh.is_valid()) { - base_update_dependency(multimesh->mesh, p_instance); - } - } else if (GLES3::LightStorage::get_singleton()->owns_light(p_base)) { - GLES3::Light *l = GLES3::LightStorage::get_singleton()->get_light(p_base); - p_instance->update_dependency(&l->dependency); - } -} - -/* VOXEL GI API */ - -RID RasterizerStorageGLES3::voxel_gi_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::voxel_gi_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { -} - -AABB RasterizerStorageGLES3::voxel_gi_get_bounds(RID p_voxel_gi) const { - return AABB(); -} - -Vector3i RasterizerStorageGLES3::voxel_gi_get_octree_size(RID p_voxel_gi) const { - return Vector3i(); -} - -Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_octree_cells(RID p_voxel_gi) const { - return Vector<uint8_t>(); -} - -Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_data_cells(RID p_voxel_gi) const { - return Vector<uint8_t>(); -} - -Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_distance_field(RID p_voxel_gi) const { - return Vector<uint8_t>(); -} - -Vector<int> RasterizerStorageGLES3::voxel_gi_get_level_counts(RID p_voxel_gi) const { - return Vector<int>(); -} - -Transform3D RasterizerStorageGLES3::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const { - return Transform3D(); -} - -void RasterizerStorageGLES3::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) { -} - -float RasterizerStorageGLES3::voxel_gi_get_dynamic_range(RID p_voxel_gi) const { - return 0; -} - -void RasterizerStorageGLES3::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) { -} - -float RasterizerStorageGLES3::voxel_gi_get_propagation(RID p_voxel_gi) const { - return 0; -} - -void RasterizerStorageGLES3::voxel_gi_set_energy(RID p_voxel_gi, float p_range) { -} - -float RasterizerStorageGLES3::voxel_gi_get_energy(RID p_voxel_gi) const { - return 0.0; -} - -void RasterizerStorageGLES3::voxel_gi_set_bias(RID p_voxel_gi, float p_range) { -} - -float RasterizerStorageGLES3::voxel_gi_get_bias(RID p_voxel_gi) const { - return 0.0; -} - -void RasterizerStorageGLES3::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) { -} - -float RasterizerStorageGLES3::voxel_gi_get_normal_bias(RID p_voxel_gi) const { - return 0.0; -} - -void RasterizerStorageGLES3::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) { -} - -bool RasterizerStorageGLES3::voxel_gi_is_interior(RID p_voxel_gi) const { - return false; -} - -void RasterizerStorageGLES3::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) { -} - -bool RasterizerStorageGLES3::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const { - return false; -} - -void RasterizerStorageGLES3::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) { -} - -float RasterizerStorageGLES3::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const { - return 0; -} - -uint32_t RasterizerStorageGLES3::voxel_gi_get_version(RID p_voxel_gi) { - return 0; -} - -/* OCCLUDER */ - -void RasterizerStorageGLES3::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { -} - -/* FOG */ - -RID RasterizerStorageGLES3::fog_volume_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::fog_volume_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { -} - -void RasterizerStorageGLES3::fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) { -} - -void RasterizerStorageGLES3::fog_volume_set_material(RID p_fog_volume, RID p_material) { -} - -AABB RasterizerStorageGLES3::fog_volume_get_aabb(RID p_fog_volume) const { - return AABB(); -} - -RS::FogVolumeShape RasterizerStorageGLES3::fog_volume_get_shape(RID p_fog_volume) const { - return RS::FOG_VOLUME_SHAPE_BOX; -} - -/* VISIBILITY NOTIFIER */ -RID RasterizerStorageGLES3::visibility_notifier_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::visibility_notifier_initialize(RID p_notifier) { -} - -void RasterizerStorageGLES3::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) { -} - -void RasterizerStorageGLES3::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) { -} - -AABB RasterizerStorageGLES3::visibility_notifier_get_aabb(RID p_notifier) const { - return AABB(); -} - -void RasterizerStorageGLES3::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) { -} - -/* CANVAS SHADOW */ - -RID RasterizerStorageGLES3::canvas_light_shadow_buffer_create(int p_width) { - CanvasLightShadow *cls = memnew(CanvasLightShadow); - - if (p_width > config->max_texture_size) { - p_width = config->max_texture_size; - } - - cls->size = p_width; - cls->height = 16; - - glActiveTexture(GL_TEXTURE0); - - glGenFramebuffers(1, &cls->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo); - - glGenRenderbuffers(1, &cls->depth); - glBindRenderbuffer(GL_RENDERBUFFER, cls->depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, cls->size, cls->height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, cls->depth); - - glGenTextures(1, &cls->distance); - glBindTexture(GL_TEXTURE_2D, cls->distance); - if (config->use_rgba_2d_shadows) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, GL_RED, GL_FLOAT, nullptr); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cls->distance, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - //printf("errnum: %x\n",status); - glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - memdelete(cls); - ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID()); - } - - return canvas_light_shadow_owner.make_rid(cls); -} - -/* LIGHT SHADOW MAPPING */ -/* - -RID RasterizerStorageGLES3::canvas_light_occluder_create() { - CanvasOccluder *co = memnew(CanvasOccluder); - co->index_id = 0; - co->vertex_id = 0; - co->len = 0; - - return canvas_occluder_owner.make_rid(co); -} - -void RasterizerStorageGLES3::canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) { - CanvasOccluder *co = canvas_occluder_owner.get(p_occluder); - ERR_FAIL_COND(!co); - - co->lines = p_lines; - - if (p_lines.size() != co->len) { - if (co->index_id) { - glDeleteBuffers(1, &co->index_id); - } if (co->vertex_id) { - glDeleteBuffers(1, &co->vertex_id); - } - - co->index_id = 0; - co->vertex_id = 0; - co->len = 0; - } - - if (p_lines.size()) { - PoolVector<float> geometry; - PoolVector<uint16_t> indices; - int lc = p_lines.size(); - - geometry.resize(lc * 6); - indices.resize(lc * 3); - - PoolVector<float>::Write vw = geometry.write(); - PoolVector<uint16_t>::Write iw = indices.write(); - - PoolVector<Vector2>::Read lr = p_lines.read(); - - const int POLY_HEIGHT = 16384; - - for (int i = 0; i < lc / 2; i++) { - vw[i * 12 + 0] = lr[i * 2 + 0].x; - vw[i * 12 + 1] = lr[i * 2 + 0].y; - vw[i * 12 + 2] = POLY_HEIGHT; - - vw[i * 12 + 3] = lr[i * 2 + 1].x; - vw[i * 12 + 4] = lr[i * 2 + 1].y; - vw[i * 12 + 5] = POLY_HEIGHT; - - vw[i * 12 + 6] = lr[i * 2 + 1].x; - vw[i * 12 + 7] = lr[i * 2 + 1].y; - vw[i * 12 + 8] = -POLY_HEIGHT; - - vw[i * 12 + 9] = lr[i * 2 + 0].x; - vw[i * 12 + 10] = lr[i * 2 + 0].y; - vw[i * 12 + 11] = -POLY_HEIGHT; - - iw[i * 6 + 0] = i * 4 + 0; - iw[i * 6 + 1] = i * 4 + 1; - iw[i * 6 + 2] = i * 4 + 2; - - iw[i * 6 + 3] = i * 4 + 2; - iw[i * 6 + 4] = i * 4 + 3; - iw[i * 6 + 5] = i * 4 + 0; - } - - //if same buffer len is being set, just use BufferSubData to avoid a pipeline flush - - if (!co->vertex_id) { - glGenBuffers(1, &co->vertex_id); - glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); - glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(real_t), vw.ptr(), GL_STATIC_DRAW); - } else { - glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); - glBufferSubData(GL_ARRAY_BUFFER, 0, lc * 6 * sizeof(real_t), vw.ptr()); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - - if (!co->index_id) { - glGenBuffers(1, &co->index_id); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, lc * 3 * sizeof(uint16_t), iw.ptr(), GL_DYNAMIC_DRAW); - } else { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, lc * 3 * sizeof(uint16_t), iw.ptr()); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind - - co->len = lc; - } -} -*/ - -RS::InstanceType RasterizerStorageGLES3::get_base_type(RID p_rid) const { - if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) { - return RS::INSTANCE_MESH; - } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { - return RS::INSTANCE_MULTIMESH; - } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { - return RS::INSTANCE_LIGHT; - } - return RS::INSTANCE_NONE; -} - -bool RasterizerStorageGLES3::free(RID p_rid) { - if (GLES3::TextureStorage::get_singleton()->owns_render_target(p_rid)) { - GLES3::TextureStorage::get_singleton()->render_target_free(p_rid); - return true; - } else if (GLES3::TextureStorage::get_singleton()->owns_texture(p_rid)) { - GLES3::TextureStorage::get_singleton()->texture_free(p_rid); - return true; - } else if (GLES3::TextureStorage::get_singleton()->owns_canvas_texture(p_rid)) { - GLES3::TextureStorage::get_singleton()->canvas_texture_free(p_rid); - return true; - } else if (GLES3::MaterialStorage::get_singleton()->owns_shader(p_rid)) { - GLES3::MaterialStorage::get_singleton()->shader_free(p_rid); - return true; - } else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) { - GLES3::MaterialStorage::get_singleton()->material_free(p_rid); - return true; - } else if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) { - GLES3::MeshStorage::get_singleton()->mesh_free(p_rid); - return true; - } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { - GLES3::MeshStorage::get_singleton()->multimesh_free(p_rid); - return true; - } else if (GLES3::MeshStorage::get_singleton()->owns_mesh_instance(p_rid)) { - GLES3::MeshStorage::get_singleton()->mesh_instance_free(p_rid); - return true; - } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { - GLES3::LightStorage::get_singleton()->light_free(p_rid); - return true; - } else { - return false; - } - /* - else if (reflection_probe_owner.owns(p_rid)) { - // delete the texture - ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid); - reflection_probe->instance_remove_deps(); - - reflection_probe_owner.free(p_rid); - memdelete(reflection_probe); - - return true; - } else if (lightmap_capture_data_owner.owns(p_rid)) { - // delete the texture - LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get_or_null(p_rid); - lightmap_capture->instance_remove_deps(); - - lightmap_capture_data_owner.free(p_rid); - memdelete(lightmap_capture); - return true; - - } else if (canvas_occluder_owner.owns(p_rid)) { - CanvasOccluder *co = canvas_occluder_owner.get_or_null(p_rid); - if (co->index_id) { - glDeleteBuffers(1, &co->index_id); - } - if (co->vertex_id) { - glDeleteBuffers(1, &co->vertex_id); - } - - canvas_occluder_owner.free(p_rid); - memdelete(co); - - return true; - - } else if (canvas_light_shadow_owner.owns(p_rid)) { - CanvasLightShadow *cls = canvas_light_shadow_owner.get_or_null(p_rid); - glDeleteFramebuffers(1, &cls->fbo); - glDeleteRenderbuffers(1, &cls->depth); - glDeleteTextures(1, &cls->distance); - canvas_light_shadow_owner.free(p_rid); - memdelete(cls); - - return true; - */ -} - -bool RasterizerStorageGLES3::has_os_feature(const String &p_feature) const { - if (!config) { - return false; - } - - if (p_feature == "rgtc") { - return config->rgtc_supported; - } - - if (p_feature == "s3tc") { - return config->s3tc_supported; - } - - if (p_feature == "bptc") { - return config->bptc_supported; - } - - if (p_feature == "etc" || p_feature == "etc2") { - return config->etc2_supported; - } - - return false; -} - -//////////////////////////////////////////// - -void RasterizerStorageGLES3::set_debug_generate_wireframes(bool p_generate) { -} - -//void RasterizerStorageGLES3::render_info_begin_capture() { -// info.snap = info.render; -//} - -//void RasterizerStorageGLES3::render_info_end_capture() { -// info.snap.object_count = info.render.object_count - info.snap.object_count; -// info.snap.draw_call_count = info.render.draw_call_count - info.snap.draw_call_count; -// info.snap.material_switch_count = info.render.material_switch_count - info.snap.material_switch_count; -// info.snap.surface_switch_count = info.render.surface_switch_count - info.snap.surface_switch_count; -// info.snap.shader_rebind_count = info.render.shader_rebind_count - info.snap.shader_rebind_count; -// info.snap.vertices_count = info.render.vertices_count - info.snap.vertices_count; -// info.snap._2d_item_count = info.render._2d_item_count - info.snap._2d_item_count; -// info.snap._2d_draw_call_count = info.render._2d_draw_call_count - info.snap._2d_draw_call_count; -//} - -//int RasterizerStorageGLES3::get_captured_render_info(RS::RenderInfo p_info) { -// switch (p_info) { -// case RS::INFO_OBJECTS_IN_FRAME: { -// return info.snap.object_count; -// } break; -// case RS::INFO_VERTICES_IN_FRAME: { -// return info.snap.vertices_count; -// } break; -// case RS::INFO_MATERIAL_CHANGES_IN_FRAME: { -// return info.snap.material_switch_count; -// } break; -// case RS::INFO_SHADER_CHANGES_IN_FRAME: { -// return info.snap.shader_rebind_count; -// } break; -// case RS::INFO_SURFACE_CHANGES_IN_FRAME: { -// return info.snap.surface_switch_count; -// } break; -// case RS::INFO_DRAW_CALLS_IN_FRAME: { -// return info.snap.draw_call_count; -// } break; -// /* -// case RS::INFO_2D_ITEMS_IN_FRAME: { -// return info.snap._2d_item_count; -// } break; -// case RS::INFO_2D_DRAW_CALLS_IN_FRAME: { -// return info.snap._2d_draw_call_count; -// } break; -// */ -// default: { -// return get_render_info(p_info); -// } -// } -//} - -//int RasterizerStorageGLES3::get_render_info(RS::RenderInfo p_info) { -// switch (p_info) { -// case RS::INFO_OBJECTS_IN_FRAME: -// return info.render_final.object_count; -// case RS::INFO_VERTICES_IN_FRAME: -// return info.render_final.vertices_count; -// case RS::INFO_MATERIAL_CHANGES_IN_FRAME: -// return info.render_final.material_switch_count; -// case RS::INFO_SHADER_CHANGES_IN_FRAME: -// return info.render_final.shader_rebind_count; -// case RS::INFO_SURFACE_CHANGES_IN_FRAME: -// return info.render_final.surface_switch_count; -// case RS::INFO_DRAW_CALLS_IN_FRAME: -// return info.render_final.draw_call_count; -// /* -// case RS::INFO_2D_ITEMS_IN_FRAME: -// return info.render_final._2d_item_count; -// case RS::INFO_2D_DRAW_CALLS_IN_FRAME: -// return info.render_final._2d_draw_call_count; -//*/ -// case RS::INFO_USAGE_VIDEO_MEM_TOTAL: -// return 0; //no idea -// case RS::INFO_VIDEO_MEM_USED: -// return info.vertex_mem + info.texture_mem; -// case RS::INFO_TEXTURE_MEM_USED: -// return info.texture_mem; -// case RS::INFO_VERTEX_MEM_USED: -// return info.vertex_mem; -// default: -// return 0; //no idea either -// } -//} - -String RasterizerStorageGLES3::get_video_adapter_name() const { - return (const char *)glGetString(GL_RENDERER); -} - -String RasterizerStorageGLES3::get_video_adapter_vendor() const { - return (const char *)glGetString(GL_VENDOR); -} - -RenderingDevice::DeviceType RasterizerStorageGLES3::get_video_adapter_type() const { - return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER; -} - -String RasterizerStorageGLES3::get_video_adapter_api_version() const { - return (const char *)glGetString(GL_VERSION); -} - -void RasterizerStorageGLES3::initialize() { - config = GLES3::Config::get_singleton(); - - // skeleton buffer - { - resources.skeleton_transform_buffer_size = 0; - glGenBuffers(1, &resources.skeleton_transform_buffer); - } - - // radical inverse vdc cache texture - // used for cubemap filtering - glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); - /* - uint8_t radical_inverse[512]; - - for (uint32_t i = 0; i < 512; i++) { - uint32_t bits = i; - - bits = (bits << 16) | (bits >> 16); - bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1); - bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2); - bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4); - bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); - - float value = float(bits) * 2.3283064365386963e-10; - radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); - } - - //glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, radical_inverse); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //need this for proper sampling - */ - glBindTexture(GL_TEXTURE_2D, 0); - - { - glGenFramebuffers(1, &resources.mipmap_blur_fbo); - glGenTextures(1, &resources.mipmap_blur_color); - } - -#ifdef GLES_OVER_GL - glEnable(GL_PROGRAM_POINT_SIZE); -#endif -} - -void RasterizerStorageGLES3::finalize() { -} - -void RasterizerStorageGLES3::update_memory_info() { -} - -uint64_t RasterizerStorageGLES3::get_rendering_info(RS::RenderingInfo p_info) { - return 0; -} - -void RasterizerStorageGLES3::update_dirty_resources() { - GLES3::MaterialStorage::get_singleton()->_update_global_variables(); - GLES3::MaterialStorage::get_singleton()->_update_queued_materials(); - //GLES3::MeshStorage::get_singleton()->_update_dirty_skeletons(); - GLES3::MeshStorage::get_singleton()->_update_dirty_multimeshes(); -} - -RasterizerStorageGLES3::RasterizerStorageGLES3() { - initialize(); -} - -RasterizerStorageGLES3::~RasterizerStorageGLES3() { -} - -#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h deleted file mode 100644 index 7ac3db4537..0000000000 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ /dev/null @@ -1,322 +0,0 @@ -/*************************************************************************/ -/* rasterizer_storage_gles3.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RASTERIZER_STORAGE_OPENGL_H -#define RASTERIZER_STORAGE_OPENGL_H - -#ifdef GLES3_ENABLED - -#include "core/templates/local_vector.h" -#include "core/templates/rid_owner.h" -#include "core/templates/self_list.h" -#include "servers/rendering/renderer_compositor.h" -#include "servers/rendering/renderer_storage.h" -#include "servers/rendering/shader_compiler.h" -#include "servers/rendering/shader_language.h" -#include "storage/config.h" -#include "storage/light_storage.h" -#include "storage/material_storage.h" -#include "storage/mesh_storage.h" -#include "storage/texture_storage.h" - -// class RasterizerCanvasGLES3; -// class RasterizerSceneGLES3; - -class RasterizerStorageGLES3 : public RendererStorage { -public: - // RasterizerCanvasGLES3 *canvas; - // RasterizerSceneGLES3 *scene; - - GLES3::Config *config = nullptr; - - static _FORCE_INLINE_ void store_transform(const Transform3D &p_mtx, float *p_array) { - p_array[0] = p_mtx.basis.rows[0][0]; - p_array[1] = p_mtx.basis.rows[1][0]; - p_array[2] = p_mtx.basis.rows[2][0]; - p_array[3] = 0; - p_array[4] = p_mtx.basis.rows[0][1]; - p_array[5] = p_mtx.basis.rows[1][1]; - p_array[6] = p_mtx.basis.rows[2][1]; - p_array[7] = 0; - p_array[8] = p_mtx.basis.rows[0][2]; - p_array[9] = p_mtx.basis.rows[1][2]; - p_array[10] = p_mtx.basis.rows[2][2]; - p_array[11] = 0; - p_array[12] = p_mtx.origin.x; - p_array[13] = p_mtx.origin.y; - p_array[14] = p_mtx.origin.z; - p_array[15] = 1; - } - - static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) { - p_array[0] = p_mtx.rows[0][0]; - p_array[1] = p_mtx.rows[1][0]; - p_array[2] = p_mtx.rows[2][0]; - p_array[3] = 0; - p_array[4] = p_mtx.rows[0][1]; - p_array[5] = p_mtx.rows[1][1]; - p_array[6] = p_mtx.rows[2][1]; - p_array[7] = 0; - p_array[8] = p_mtx.rows[0][2]; - p_array[9] = p_mtx.rows[1][2]; - p_array[10] = p_mtx.rows[2][2]; - p_array[11] = 0; - } - - static _FORCE_INLINE_ void store_camera(const CameraMatrix &p_mtx, float *p_array) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - p_array[i * 4 + j] = p_mtx.matrix[i][j]; - } - } - } - - struct Resources { - GLuint mipmap_blur_fbo; - GLuint mipmap_blur_color; - - GLuint radical_inverse_vdc_cache_tex; - bool use_rgba_2d_shadows; - - size_t skeleton_transform_buffer_size; - GLuint skeleton_transform_buffer; - LocalVector<float> skeleton_transform_cpu_buffer; - - } resources; - - struct Info { - uint64_t texture_mem = 0; - uint64_t vertex_mem = 0; - - struct Render { - uint32_t object_count; - uint32_t draw_call_count; - uint32_t material_switch_count; - uint32_t surface_switch_count; - uint32_t shader_rebind_count; - uint32_t vertices_count; - uint32_t _2d_item_count; - uint32_t _2d_draw_call_count; - - void reset() { - object_count = 0; - draw_call_count = 0; - material_switch_count = 0; - surface_switch_count = 0; - shader_rebind_count = 0; - vertices_count = 0; - _2d_item_count = 0; - _2d_draw_call_count = 0; - } - } render, render_final, snap; - - Info() { - render.reset(); - render_final.reset(); - } - - } info; - - ///////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////API//////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////////// - -public: - virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override; - - /* VOXEL GI API */ - - RID voxel_gi_allocate() override; - void voxel_gi_initialize(RID p_rid) override; - void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override; - - AABB voxel_gi_get_bounds(RID p_voxel_gi) const override; - Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override; - Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override; - Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override; - Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override; - - Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override; - Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override; - - void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override; - float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override; - - void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override; - float voxel_gi_get_propagation(RID p_voxel_gi) const override; - - void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override; - float voxel_gi_get_energy(RID p_voxel_gi) const override; - - void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override; - float voxel_gi_get_bias(RID p_voxel_gi) const override; - - void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override; - float voxel_gi_get_normal_bias(RID p_voxel_gi) const override; - - void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override; - bool voxel_gi_is_interior(RID p_voxel_gi) const override; - - void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override; - bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override; - - void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override; - float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override; - - uint32_t voxel_gi_get_version(RID p_voxel_gi) override; - - /* OCCLUDER */ - - void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); - - /* FOG VOLUMES */ - - RID fog_volume_allocate() override; - void fog_volume_initialize(RID p_rid) override; - - void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override; - void fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) override; - void fog_volume_set_material(RID p_fog_volume, RID p_material) override; - AABB fog_volume_get_aabb(RID p_fog_volume) const override; - RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override; - - /* VISIBILITY NOTIFIER */ - RID visibility_notifier_allocate() override; - void visibility_notifier_initialize(RID p_notifier) override; - void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override; - void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override; - - AABB visibility_notifier_get_aabb(RID p_notifier) const override; - void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override; - - // access from canvas - // GLES3::RenderTarget * render_target_get(RID p_render_target); - - /* CANVAS SHADOW */ - - struct CanvasLightShadow { - RID self; - int size; - int height; - GLuint fbo; - GLuint depth; - GLuint distance; //for older devices - }; - - RID_PtrOwner<CanvasLightShadow> canvas_light_shadow_owner; - - RID canvas_light_shadow_buffer_create(int p_width); - - /* LIGHT SHADOW MAPPING */ - /* - struct CanvasOccluder { - RID self; - - GLuint vertex_id; // 0 means, unconfigured - GLuint index_id; // 0 means, unconfigured - LocalVector<Vector2> lines; - int len; - }; - - RID_Owner<CanvasOccluder> canvas_occluder_owner; - - RID canvas_light_occluder_create(); - void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines); -*/ - - RS::InstanceType get_base_type(RID p_rid) const override; - - bool free(RID p_rid) override; - - void initialize(); - void finalize(); - - void update_memory_info() override; - uint64_t get_rendering_info(RS::RenderingInfo p_info) override; - - bool has_os_feature(const String &p_feature) const override; - - void update_dirty_resources() override; - - void set_debug_generate_wireframes(bool p_generate) override; - - // void render_info_begin_capture() override; - // void render_info_end_capture() override; - // int get_captured_render_info(RS::RenderInfo p_info) override; - - // int get_render_info(RS::RenderInfo p_info) override; - String get_video_adapter_name() const override; - String get_video_adapter_vendor() const override; - RenderingDevice::DeviceType get_video_adapter_type() const override; - String get_video_adapter_api_version() const override; - - void capture_timestamps_begin() override {} - void capture_timestamp(const String &p_name) override {} - uint32_t get_captured_timestamps_count() const override { - return 0; - } - uint64_t get_captured_timestamps_frame() const override { - return 0; - } - uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override { - return 0; - } - uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override { - return 0; - } - String get_captured_timestamp_name(uint32_t p_index) const override { - return String(); - } - - //bool validate_framebuffer(); // Validate currently bound framebuffer, does not touch global state - String get_framebuffer_error(GLenum p_status); - - RasterizerStorageGLES3(); - ~RasterizerStorageGLES3(); -}; - -inline String RasterizerStorageGLES3::get_framebuffer_error(GLenum p_status) { -#if defined(DEBUG_ENABLED) && defined(GLES_OVER_GL) - if (p_status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) { - return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; - } else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) { - return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; - } else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER) { - return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; - } else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER) { - return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; - } -#endif - return itos(p_status); -} - -#endif // GLES3_ENABLED - -#endif // RASTERIZER_STORAGE_OPENGL_H diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 21ccef3518..2ff7f72180 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -142,7 +142,7 @@ RID ShaderGLES3::version_create() { return version_owner.make_rid(version); } -void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) { +void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) { #ifdef GLES_OVER_GL builder.append("#version 330\n"); builder.append("#define USE_GLES_OVER_GL\n"); @@ -171,6 +171,24 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant } builder.append("\n"); //make sure defines begin at newline + // Insert multiview extension loading, because it needs to appear before + // any non-preprocessor code (like the "precision highp..." lines below). + builder.append("#ifdef USE_MULTIVIEW\n"); + builder.append("#if defined(GL_OVR_multiview2)\n"); + builder.append("#extension GL_OVR_multiview2 : require\n"); + builder.append("#elif defined(GL_OVR_multiview)\n"); + builder.append("#extension GL_OVR_multiview : require\n"); + builder.append("#endif\n"); + if (p_stage_type == StageType::STAGE_TYPE_VERTEX) { + builder.append("layout(num_views=2) in;\n"); + } + builder.append("#define ViewIndex gl_ViewID_OVR\n"); + builder.append("#define MAX_VIEWS 2\n"); + builder.append("#else\n"); + builder.append("#define ViewIndex 0\n"); + builder.append("#define MAX_VIEWS 1\n"); + builder.append("#endif\n"); + // Default to highp precision unless specified otherwise. builder.append("precision highp float;\n"); builder.append("precision highp int;\n"); @@ -180,8 +198,9 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("precision highp sampler2DArray;\n"); #endif - for (uint32_t i = 0; i < p_template.chunks.size(); i++) { - const StageTemplate::Chunk &chunk = p_template.chunks[i]; + const StageTemplate &stage_template = stage_templates[p_stage_type]; + for (uint32_t i = 0; i < stage_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = stage_template.chunks[i]; switch (chunk.type) { case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) @@ -224,7 +243,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //vertex stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization); spec.vert_id = glCreateShader(GL_VERTEX_SHADER); String builder_string = builder.as_string(); @@ -272,7 +291,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //fragment stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization); spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER); String builder_string = builder.as_string(); @@ -413,7 +432,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "vertex"; @@ -425,7 +444,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver //fragment stage { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "fragment"; @@ -472,7 +491,7 @@ String ShaderGLES3::_version_get_sha1(Version *p_version) const { bool ShaderGLES3::_load_from_cache(Version *p_version) { #if 0 String sha1 = _version_get_sha1(p_version); - String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); if (f.is_null()) { @@ -538,7 +557,7 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) { void ShaderGLES3::_save_to_cache(Version *p_version) { #if 0 String sha1 = _version_get_sha1(p_version); - String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; + String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache"; Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); ERR_FAIL_COND(f.is_null()); diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index e1385669cd..3ab7642357 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SHADER_OPENGL_H -#define SHADER_OPENGL_H +#ifndef SHADER_GLES3_H +#define SHADER_GLES3_H -#include "core/math/camera_matrix.h" +#include "core/math/projection.h" #include "core/os/mutex.h" #include "core/string/string_builder.h" #include "core/templates/hash_map.h" @@ -75,10 +75,10 @@ private: CharString general_defines; // A version is a high-level construct which is a combination of built-in and user-defined shader code, Each user-created Shader makes one version - // Variants use #ifdefs to toggle behaviour on and off to change behaviour of the shader + // Variants use #ifdefs to toggle behavior on and off to change behavior of the shader // All variants are compiled each time a new version is created - // Specializations use #ifdefs to toggle behaviour on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance - // Use specializations to enable and disabled advanced features, use variants to toggle behaviour when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass) + // Specializations use #ifdefs to toggle behavior on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance + // Use specializations to enable and disabled advanced features, use variants to toggle behavior when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass) struct Version { Vector<StringName> texture_uniforms; CharString uniforms; @@ -153,7 +153,7 @@ private: StageTemplate stage_templates[STAGE_TYPE_MAX]; - void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization); + void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization); void _add_stage(const char *p_code, StageType p_stage_type); @@ -208,8 +208,10 @@ protected: spec = version->variants[p_variant].lookup_ptr(specialization_default_mask); } - ERR_FAIL_COND(!spec); // Should never happen - ERR_FAIL_COND(!spec->ok); // Should never happen + if (!spec || !spec->ok) { + WARN_PRINT_ONCE("shader failed to compile, unable to bind shader."); + return; + } glUseProgram(spec->id); current_shader = spec; @@ -248,5 +250,6 @@ public: virtual ~ShaderGLES3(); }; -#endif // SHADER_OPENGL_H -#endif +#endif // GLES3_ENABLED + +#endif // SHADER_GLES3_H diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index d8dd573f57..b8bb08ec34 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -10,10 +10,12 @@ if "GLES3_GLSL" in env["BUILDERS"]: glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files] # make sure we recompile shaders if include files change - env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files) + env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"]) env.GLES3_GLSL("canvas.glsl") env.GLES3_GLSL("copy.glsl") env.GLES3_GLSL("scene.glsl") env.GLES3_GLSL("sky.glsl") env.GLES3_GLSL("cubemap_filter.glsl") + env.GLES3_GLSL("canvas_occlusion.glsl") + env.GLES3_GLSL("canvas_sdf.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 9c426dd3ef..ca806304c5 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -10,6 +10,7 @@ mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING #[specializations] DISABLE_LIGHTING = false +USE_RGBA_SHADOWS = false #[vertex] @@ -23,10 +24,9 @@ layout(location = 11) in vec4 weight_attrib; #ifdef USE_INSTANCING -layout(location = 5) in highp vec4 instance_xform0; -layout(location = 6) in highp vec4 instance_xform1; -layout(location = 7) in lowp vec4 instance_color; -layout(location = 8) in highp vec4 instance_custom_data; +layout(location = 1) in highp vec4 instance_xform0; +layout(location = 2) in highp vec4 instance_xform1; +layout(location = 5) in highp uvec4 instance_color_custom_data; // Color packed into xy, custom_data packed into zw for compatibility with 3D #endif @@ -61,20 +61,18 @@ out vec2 pixel_size_interp; void main() { vec4 instance_custom = vec4(0.0); - draw_data_instance = gl_InstanceID; -#ifdef USE_PRIMITIVE - //weird bug, - //this works +#ifdef USE_PRIMITIVE + draw_data_instance = gl_InstanceID; vec2 vertex; vec2 uv; vec4 color; - if (gl_VertexID == 0) { + if (gl_VertexID % 3 == 0) { vertex = draw_data[draw_data_instance].point_a; uv = draw_data[draw_data_instance].uv_a; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba)); - } else if (gl_VertexID == 1) { + } else if (gl_VertexID % 3 == 1) { vertex = draw_data[draw_data_instance].point_b; uv = draw_data[draw_data_instance].uv_b; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba)); @@ -87,6 +85,7 @@ void main() { vec4 bone_weights = vec4(0.0); #elif defined(USE_ATTRIBUTES) + draw_data_instance = gl_InstanceID; #ifdef USE_INSTANCING draw_data_instance = 0; #endif @@ -98,14 +97,15 @@ void main() { vec4 bone_weights = weight_attrib; #ifdef USE_INSTANCING + vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y)); color *= instance_color; - instance_custom = instance_custom_data; + instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w)); #endif #else - - vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - vec2 vertex_base = vertex_base_arr[gl_VertexID]; + draw_data_instance = gl_VertexID / 6; + vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0)); + vec2 vertex_base = vertex_base_arr[gl_VertexID % 6]; vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); vec4 color = draw_data[draw_data_instance].modulation; @@ -127,6 +127,8 @@ void main() { } #endif + vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + #ifdef USE_POINT_SIZE float point_size = 1.0; #endif @@ -210,8 +212,10 @@ void main() { #include "canvas_uniforms_inc.glsl" #include "stdlib_inc.glsl" +#ifndef DISABLE_LIGHTING uniform sampler2D atlas_texture; //texunit:-2 uniform sampler2D shadow_atlas_texture; //texunit:-3 +#endif // DISABLE_LIGHTING uniform sampler2D screen_texture; //texunit:-4 uniform sampler2D sdf_texture; //texunit:-5 uniform sampler2D normal_texture; //texunit:-6 @@ -241,13 +245,19 @@ layout(std140) uniform MaterialUniforms{ }; #endif +#GLOBALS + +float vec4_to_float(vec4 p_vec) { + return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0; +} + vec2 screen_uv_to_sdf(vec2 p_uv) { return screen_to_sdf * p_uv; } float texture_sdf(vec2 p_sdf) { vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; - float d = texture(sdf_texture, uv).r; + float d = vec4_to_float(texture(sdf_texture, uv)); d *= SDF_MAX_LENGTH; return d * tex_to_sdf; } @@ -257,16 +267,15 @@ vec2 texture_sdf_normal(vec2 p_sdf) { const float EPSILON = 0.001; return normalize(vec2( - texture(sdf_texture, uv + vec2(EPSILON, 0.0)).r - texture(sdf_texture, uv - vec2(EPSILON, 0.0)).r, - texture(sdf_texture, uv + vec2(0.0, EPSILON)).r - texture(sdf_texture, uv - vec2(0.0, EPSILON)).r)); + vec4_to_float(texture(sdf_texture, uv + vec2(EPSILON, 0.0))) - vec4_to_float(texture(sdf_texture, uv - vec2(EPSILON, 0.0))), + vec4_to_float(texture(sdf_texture, uv + vec2(0.0, EPSILON))) - vec4_to_float(texture(sdf_texture, uv - vec2(0.0, EPSILON))))); } vec2 sdf_to_screen_uv(vec2 p_sdf) { return p_sdf * sdf_to_screen; } -#GLOBALS - +#ifndef DISABLE_LIGHTING #ifdef LIGHT_CODE_USED vec4 light_compute( @@ -281,6 +290,14 @@ vec4 light_compute( vec2 uv, vec4 color, bool is_directional) { vec4 light = vec4(0.0); + vec3 light_direction = vec3(0.0); + + if (is_directional) { + light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z)); + light_position = vec3(0.0); + } else { + light_direction = normalize(light_position - light_vertex); + } #CODE : LIGHT @@ -289,49 +306,6 @@ vec4 light_compute( #endif -#ifdef USE_NINEPATCH - -float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { - float tex_size = 1.0 / tex_pixel_size; - - if (pixel < margin_begin) { - return pixel * tex_pixel_size; - } else if (pixel >= draw_size - margin_end) { - return (tex_size - (draw_size - pixel)) * tex_pixel_size; - } else { - if (!bool(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) { - draw_center--; - } - - // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. - if (np_repeat == 0) { // Stretch. - // Convert to ratio. - float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); - // Scale to source texture. - return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; - } else if (np_repeat == 1) { // Tile. - // Convert to offset. - float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); - // Scale to source texture. - return (margin_begin + ofs) * tex_pixel_size; - } else if (np_repeat == 2) { // Tile Fit. - // Calculate scale. - float src_area = draw_size - margin_begin - margin_end; - float dst_area = tex_size - margin_begin - margin_end; - float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); - // Convert to ratio. - float ratio = (pixel - margin_begin) / src_area; - ratio = mod(ratio * scale, 1.0); - // Scale to source texture. - return (margin_begin + ratio * dst_area) * tex_pixel_size; - } else { // Shouldn't happen, but silences compiler warning. - return 0.0; - } - } -} - -#endif - vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) { float cNdotL = max(0.0, dot(normal, light_vec)); @@ -355,6 +329,22 @@ vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 lig } } +#ifdef USE_RGBA_SHADOWS + +#define SHADOW_DEPTH(m_uv) (dot(textureLod(shadow_atlas_texture, (m_uv), 0.0), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0) + +#else + +#define SHADOW_DEPTH(m_uv) (textureLod(shadow_atlas_texture, (m_uv), 0.0).r) + +#endif + +#define SHADOW_TEST(m_uv) \ + { \ + highp float sd = SHADOW_DEPTH(m_uv); \ + shadow += step(sd, shadow_uv.z / shadow_uv.w); \ + } + //float distance = length(shadow_pos); vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv #ifdef LIGHT_CODE_USED @@ -362,40 +352,38 @@ vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv vec3 shadow_modulate #endif ) { - float shadow; - uint shadow_mode = light_data[light_base].flags & LIGHT_FLAGS_FILTER_MASK; + float shadow = 0.0; + uint shadow_mode = light_array[light_base].flags & LIGHT_FLAGS_FILTER_MASK; if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) { - shadow = textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; + SHADOW_TEST(shadow_uv.xy); } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) { - vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); - shadow = 0.0; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; + vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0); shadow /= 5.0; } else { //PCF13 - vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0); - shadow = 0.0; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 6.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 5.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 4.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 3.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 3.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 4.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 5.0, 0.0).x; - shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 6.0, 0.0).x; + vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 6.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 5.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 4.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 3.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 3.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 4.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 5.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 6.0); shadow /= 13.0; } - vec4 shadow_color = unpackUnorm4x8(light_data[light_base].shadow_color); + vec4 shadow_color = unpackUnorm4x8(light_array[light_base].shadow_color); #ifdef LIGHT_CODE_USED shadow_color.rgb *= shadow_modulate; #endif @@ -406,7 +394,7 @@ vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv } void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { - uint blend_mode = light_data[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK; switch (blend_mode) { case LIGHT_FLAGS_BLEND_MODE_ADD: { @@ -421,6 +409,51 @@ void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { } } +#endif + +#ifdef USE_NINEPATCH + +float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) { + float tex_size = 1.0 / tex_pixel_size; + + if (pixel < margin_begin) { + return pixel * tex_pixel_size; + } else if (pixel >= draw_size - margin_end) { + return (tex_size - (draw_size - pixel)) * tex_pixel_size; + } else { + if (!bool(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) { + draw_center--; + } + + // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. + if (np_repeat == 0) { // Stretch. + // Convert to ratio. + float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; + } else if (np_repeat == 1) { // Tile. + // Convert to offset. + float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end); + // Scale to source texture. + return (margin_begin + ofs) * tex_pixel_size; + } else if (np_repeat == 2) { // Tile Fit. + // Calculate scale. + float src_area = draw_size - margin_begin - margin_end; + float dst_area = tex_size - margin_begin - margin_end; + float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); + // Convert to ratio. + float ratio = (pixel - margin_begin) / src_area; + ratio = mod(ratio * scale, 1.0); + // Scale to source texture. + return (margin_begin + ratio * dst_area) * tex_pixel_size; + } else { // Shouldn't happen, but silences compiler warning. + return 0.0; + } + } +} + +#endif + float msdf_median(float r, float g, float b, float a) { return min(max(min(r, g), min(max(r, g), b)), a); } @@ -473,7 +506,13 @@ void main() { float a = clamp(d * px_size + 0.5, 0.0, 1.0); color.a = a * color.a; } - + } else if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_LCD)) { + vec4 lcd_sample = texture(color_texture, uv); + if (lcd_sample.a == 1.0) { + color.rgb = lcd_sample.rgb * color.a; + } else { + color = vec4(0.0, 0.0, 0.0, 0.0); + } } else { #else { @@ -481,8 +520,8 @@ void main() { color *= texture(color_texture, uv); } - uint light_count = (draw_data[draw_data_instance].flags >> FLAGS_LIGHT_COUNT_SHIFT) & uint(0xF); //max 16 lights - bool using_light = light_count > uint(0) || directional_light_count > uint(0); + uint light_count = (draw_data[draw_data_instance].flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights + bool using_light = light_count > 0u || directional_light_count > 0u; vec3 normal; @@ -523,6 +562,8 @@ void main() { vec2 screen_uv = vec2(0.0); #endif + vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + vec3 light_vertex = vec3(vertex, 0.0); vec2 shadow_vertex = vertex; @@ -548,10 +589,7 @@ void main() { normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); } - vec3 base_color = color.rgb; - if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_LIGHT_MASK)) { - color = vec4(0.0); //invisible by default due to using light mask - } + vec4 base_color = color; #ifdef MODE_LIGHT_ONLY color = vec4(0.0); @@ -561,28 +599,32 @@ void main() { #if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED) - for (uint i = uint(0); i < directional_light_count; i++) { + // Directional Lights + + for (uint i = 0u; i < directional_light_count; i++) { uint light_base = i; - vec2 direction = light_data[light_base].position; - vec4 light_color = light_data[light_base].color; + vec2 direction = light_array[light_base].position; + vec4 light_color = light_array[light_base].color; #ifdef LIGHT_CODE_USED vec4 shadow_modulate = vec4(1.0); - light_color = light_compute(light_vertex, vec3(direction, light_data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true); + light_color = light_compute(light_vertex, vec3(direction, light_array[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true); #else if (normal_used) { - vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_data[light_base].height)); - light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array[light_base].height)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; } #endif - if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { - vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. - vec4 shadow_uv = vec4(shadow_pos.x, light_data[light_base].shadow_y_ofs, shadow_pos.y * light_data[light_base].shadow_zfar_inv, 1.0); + vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0); light_color = light_shadow_compute(light_base, light_color, shadow_uv #ifdef LIGHT_CODE_USED @@ -597,50 +639,51 @@ void main() { // Positional Lights - for (uint i = uint(0); i < MAX_LIGHTS_PER_ITEM; i++) { + for (uint i = 0u; i < MAX_LIGHTS_PER_ITEM; i++) { if (i >= light_count) { break; } uint light_base; - if (i < uint(8)) { - if (i < uint(4)) { - light_base = draw_data[draw_data_instance].lights.x; + if (i < 8u) { + if (i < 4u) { + light_base = draw_data[draw_data_instance].lights[0]; } else { - light_base = draw_data[draw_data_instance].lights.y; + light_base = draw_data[draw_data_instance].lights[1]; } } else { - if (i < uint(12)) { - light_base = draw_data[draw_data_instance].lights.z; + if (i < 12u) { + light_base = draw_data[draw_data_instance].lights[2]; } else { - light_base = draw_data[draw_data_instance].lights.w; + light_base = draw_data[draw_data_instance].lights[3]; } } - light_base >>= (i & uint(3)) * uint(8); + light_base >>= (i & 3u) * 8u; light_base &= uint(0xFF); - vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_data[light_base].texture_matrix[0], light_data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. - vec2 tex_uv_atlas = tex_uv * light_data[light_base].atlas_rect.zw + light_data[light_base].atlas_rect.xy; + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy; vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); - vec4 light_base_color = light_data[light_base].color; + vec4 light_base_color = light_array[light_base].color; #ifdef LIGHT_CODE_USED vec4 shadow_modulate = vec4(1.0); - vec3 light_position = vec3(light_data[light_base].position, light_data[light_base].height); + vec3 light_position = vec3(light_array[light_base].position, light_array[light_base].height); light_color.rgb *= light_base_color.rgb; - light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, false); + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false); #else light_color.rgb *= light_base_color.rgb * light_base_color.a; if (normal_used) { - vec3 light_pos = vec3(light_data[light_base].position, light_data[light_base].height); + vec3 light_pos = vec3(light_array[light_base].position, light_array[light_base].height); vec3 pos = light_vertex; vec3 light_vec = normalize(light_pos - pos); - float cNdotL = max(0.0, dot(normal, light_vec)); - light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; } #endif if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { @@ -648,37 +691,37 @@ void main() { light_color.a = 0.0; } - if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { - vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. vec2 pos_norm = normalize(shadow_pos); vec2 pos_abs = abs(pos_norm); vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y); vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot? float tex_ofs; - float distance; + float dist; if (pos_rot.y > 0.0) { if (pos_rot.x > 0.0) { tex_ofs = pos_box.y * 0.125 + 0.125; - distance = shadow_pos.x; + dist = shadow_pos.x; } else { tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125); - distance = shadow_pos.y; + dist = shadow_pos.y; } } else { if (pos_rot.x < 0.0) { tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); - distance = -shadow_pos.x; + dist = -shadow_pos.x; } else { tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125); - distance = -shadow_pos.y; + dist = -shadow_pos.y; } } - distance *= light_data[light_base].shadow_zfar_inv; + dist *= light_array[light_base].shadow_zfar_inv; //float distance = length(shadow_pos); - vec4 shadow_uv = vec4(tex_ofs, light_data[light_base].shadow_y_ofs, distance, 1.0); + vec4 shadow_uv = vec4(tex_ofs, light_array[light_base].shadow_y_ofs, dist, 1.0); light_color = light_shadow_compute(light_base, light_color, shadow_uv #ifdef LIGHT_CODE_USED @@ -690,7 +733,7 @@ void main() { light_blend_compute(light_base, light_color, color.rgb); } -#endif // UNSHADED +#endif frag_color = color; } diff --git a/drivers/gles3/shaders/canvas_occlusion.glsl b/drivers/gles3/shaders/canvas_occlusion.glsl new file mode 100644 index 0000000000..512800839a --- /dev/null +++ b/drivers/gles3/shaders/canvas_occlusion.glsl @@ -0,0 +1,68 @@ +/* clang-format off */ +#[modes] + +mode_sdf = +mode_shadow = #define MODE_SHADOW +mode_shadow_RGBA = #define MODE_SHADOW \n#define USE_RGBA_SHADOWS + +#[specializations] + +#[vertex] + +layout(location = 0) in vec3 vertex; + +uniform highp mat4 projection; +uniform highp vec4 modelview1; +uniform highp vec4 modelview2; +uniform highp vec2 direction; +uniform highp float z_far; + +#ifdef MODE_SHADOW +out float depth; +#endif + +void main() { + highp vec4 vtx = vec4(vertex, 1.0) * mat4(modelview1, modelview2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + +#ifdef MODE_SHADOW + depth = dot(direction, vtx.xy); +#endif + gl_Position = projection * vtx; +} + +#[fragment] + + +uniform highp mat4 projection; +uniform highp vec4 modelview1; +uniform highp vec4 modelview2; +uniform highp vec2 direction; +uniform highp float z_far; + +#ifdef MODE_SHADOW +in highp float depth; +#endif + +#ifdef USE_RGBA_SHADOWS +layout(location = 0) out lowp vec4 out_buf; +#else +layout(location = 0) out highp float out_buf; +#endif + +void main() { + float out_depth = 1.0; + +#ifdef MODE_SHADOW + out_depth = depth / z_far; +#endif + +#ifdef USE_RGBA_SHADOWS + out_depth = clamp(out_depth, -1.0, 1.0); + out_depth = out_depth * 0.5 + 0.5; + highp vec4 comp = fract(out_depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0); + out_buf = comp; +#else + out_buf = out_depth; +#endif +} diff --git a/drivers/gles3/shaders/canvas_sdf.glsl b/drivers/gles3/shaders/canvas_sdf.glsl new file mode 100644 index 0000000000..424ec22457 --- /dev/null +++ b/drivers/gles3/shaders/canvas_sdf.glsl @@ -0,0 +1,205 @@ +/* clang-format off */ +#[modes] + +mode_load = #define MODE_LOAD +mode_load_shrink = #define MODE_LOAD_SHRINK +mode_process = #define MODE_PROCESS +mode_store = #define MODE_STORE +mode_store_shrink = #define MODE_STORE_SHRINK + +#[specializations] + +#[vertex] + +layout(location = 0) in vec2 vertex_attrib; + +/* clang-format on */ + +uniform ivec2 size; +uniform int stride; +uniform int shift; +uniform ivec2 base_size; + +void main() { + gl_Position = vec4(vertex_attrib, 1.0, 1.0); +} + +/* clang-format off */ +#[fragment] + +#define SDF_MAX_LENGTH 16384.0 + +#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) +uniform lowp sampler2D src_pixels;//texunit:0 +#else +uniform highp isampler2D src_process;//texunit:0 +#endif + +uniform ivec2 size; +uniform int stride; +uniform int shift; +uniform ivec2 base_size; + +#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) || defined(MODE_PROCESS) +layout(location = 0) out ivec4 distance_field; +#else +layout(location = 0) out vec4 distance_field; +#endif + +vec4 float_to_vec4(float p_float) { + highp vec4 comp = fract(p_float * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0)); + comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0); + return comp; +} + +void main() { + ivec2 pos = ivec2(gl_FragCoord.xy); + +#ifdef MODE_LOAD + + bool solid = texelFetch(src_pixels, pos, 0).r > 0.5; + distance_field = solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0); +#endif + +#ifdef MODE_LOAD_SHRINK + + int s = 1 << shift; + ivec2 base = pos << shift; + ivec2 center = base + ivec2(shift); + + ivec2 rel = ivec2(32767); + float d = 1e20; + int found = 0; + int solid_found = 0; + for (int i = 0; i < s; i++) { + for (int j = 0; j < s; j++) { + ivec2 src_pos = base + ivec2(i, j); + if (any(greaterThanEqual(src_pos, base_size))) { + continue; + } + bool solid = texelFetch(src_pixels, src_pos, 0).r > 0.5; + if (solid) { + float dist = length(vec2(src_pos - center)); + if (dist < d) { + d = dist; + rel = src_pos; + } + solid_found++; + } + found++; + } + } + + if (solid_found == found) { + //mark solid only if all are solid + rel = ivec2(-32767); + } + + distance_field = ivec4(rel, 0, 0); +#endif + +#ifdef MODE_PROCESS + + ivec2 base = pos << shift; + ivec2 center = base + ivec2(shift); + + ivec2 rel = texelFetch(src_process, pos, 0).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + + if (center != rel) { + //only process if it does not point to itself + const int ofs_table_size = 8; + const ivec2 ofs_table[ofs_table_size] = ivec2[]( + ivec2(-1, -1), + ivec2(0, -1), + ivec2(+1, -1), + + ivec2(-1, 0), + ivec2(+1, 0), + + ivec2(-1, +1), + ivec2(0, +1), + ivec2(+1, +1)); + + float dist = length(vec2(rel - center)); + for (int i = 0; i < ofs_table_size; i++) { + ivec2 src_pos = pos + ofs_table[i] * stride; + if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, size))) { + continue; + } + ivec2 src_rel = texelFetch(src_process, src_pos, 0).xy; + bool src_solid = src_rel.x < 0; + if (src_solid) { + src_rel = -src_rel - ivec2(1); + } + + if (src_solid != solid) { + src_rel = ivec2(src_pos << shift); //point to itself if of different type + } + + float src_dist = length(vec2(src_rel - center)); + if (src_dist < dist) { + dist = src_dist; + rel = src_rel; + } + } + } + + if (solid) { + rel = -rel - ivec2(1); + } + + distance_field = ivec4(rel, 0, 0); +#endif + +#ifdef MODE_STORE + + ivec2 rel = texelFetch(src_process, pos, 0).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + + float d = length(vec2(rel - pos)); + + if (solid) { + d = -d; + } + + d /= SDF_MAX_LENGTH; + d = clamp(d, -1.0, 1.0); + distance_field = float_to_vec4(d*0.5+0.5); + +#endif + +#ifdef MODE_STORE_SHRINK + + ivec2 base = pos << shift; + ivec2 center = base + ivec2(shift); + + ivec2 rel = texelFetch(src_process, pos, 0).xy; + + bool solid = rel.x < 0; + + if (solid) { + rel = -rel - ivec2(1); + } + + float d = length(vec2(rel - center)); + + if (solid) { + d = -d; + } + d /= SDF_MAX_LENGTH; + d = clamp(d, -1.0, 1.0); + distance_field = float_to_vec4(d*0.5+0.5); + +#endif +} diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl deleted file mode 100644 index 94485abd11..0000000000 --- a/drivers/gles3/shaders/canvas_shadow.glsl +++ /dev/null @@ -1,60 +0,0 @@ -/* clang-format off */ -[vertex] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif - -layout(location = 0) in highp vec3 vertex; - -uniform highp mat4 projection_matrix; -/* clang-format on */ -uniform highp mat4 light_matrix; -uniform highp mat4 model_matrix; -uniform highp float distance_norm; - -out highp vec4 position_interp; - -void main() { - gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0))); - position_interp = gl_Position; -} - -/* clang-format off */ -[fragment] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -#if defined(USE_HIGHP_PRECISION) -precision highp float; -precision highp int; -#else -precision mediump float; -precision mediump int; -#endif -#endif - -in highp vec4 position_interp; -/* clang-format on */ - -void main() { - highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias - -#ifdef USE_RGBA_SHADOWS - - highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0)); - comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0); - frag_color = comp; -#else - - frag_color = vec4(depth); -#endif -} diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index e08a15e59d..dd5ebecb1a 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -25,6 +25,7 @@ #define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27) #define FLAGS_USE_MSDF uint(1 << 28) +#define FLAGS_USE_LCD uint(1 << 29) // must be always 128 bytes long struct DrawData { @@ -58,8 +59,8 @@ struct DrawData { uvec4 lights; }; -layout(std140) uniform GlobalVariableData { //ubo:1 - vec4 global_variables[MAX_GLOBAL_VARIABLES]; +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; layout(std140) uniform CanvasData { //ubo:0 @@ -81,6 +82,7 @@ layout(std140) uniform CanvasData { //ubo:0 uint pad2; }; +#ifndef DISABLE_LIGHTING #define LIGHT_FLAGS_BLEND_MASK uint(3 << 16) #define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16) #define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16) @@ -111,9 +113,9 @@ struct Light { }; layout(std140) uniform LightData { //ubo:2 - Light light_data[MAX_LIGHTS]; + Light light_array[MAX_LIGHTS]; }; - +#endif // DISABLE_LIGHTING layout(std140) uniform DrawDataInstances { //ubo:3 DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index ca2fc7e36d..796ba79c2e 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -2,7 +2,7 @@ #[modes] mode_default = #define MODE_SIMPLE_COPY -mode_copy_section = #define USE_COPY_SECTION +mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -25,8 +25,7 @@ void main() { gl_Position = vec4(vertex_attrib, 1.0, 1.0); #ifdef USE_COPY_SECTION - gl_Position.xy = (copy_section.xy + (uv_interp.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0; - uv_interp = copy_section.xy + uv_interp * copy_section.zw; + gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; #endif } diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index ebf0c08ec4..88464876f1 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -29,19 +29,15 @@ uniform samplerCube source_cube; //texunit:0 /* clang-format on */ uniform int face_id; -uniform float roughness; -uniform float face_size; -uniform int sample_count; -//Todo, profile on low end hardware to see if fixed loop is faster -#ifdef USE_FIXED_SAMPLES -#define FIXED_SAMPLE_COUNT 32 +#ifndef MODE_DIRECT_WRITE +uniform int sample_count; +uniform vec4 sample_directions_mip[MAX_SAMPLE_COUNT]; +uniform float weight; #endif in highp vec2 uv_interp; -uniform sampler2D radical_inverse_vdc_cache; // texunit:1 - layout(location = 0) out vec4 frag_color; #define M_PI 3.14159265359 @@ -93,48 +89,6 @@ vec3 texelCoordToVec(vec2 uv, int faceID) { return normalize(result); } -vec3 ImportanceSampleGGX(vec2 xi, float roughness4) { - // Compute distribution direction - float Phi = 2.0 * M_PI * xi.x; - float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y)); - float SinTheta = sqrt(1.0 - CosTheta * CosTheta); - - // Convert to spherical direction - vec3 H; - H.x = SinTheta * cos(Phi); - H.y = SinTheta * sin(Phi); - H.z = CosTheta; - - return H; -} - -float DistributionGGX(float NdotH, float roughness4) { - float NdotH2 = NdotH * NdotH; - float denom = (NdotH2 * (roughness4 - 1.0) + 1.0); - denom = M_PI * denom * denom; - - return roughness4 / denom; -} - -// https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float GGX(float NdotV, float a) { - float k = a / 2.0; - return NdotV / (NdotV * (1.0 - k) + k); -} - -// https://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html -float G_Smith(float a, float nDotV, float nDotL) { - return GGX(nDotL, a * a) * GGX(nDotV, a * a); -} - -float radical_inverse_VdC(int i) { - return texture(radical_inverse_vdc_cache, vec2(float(i) / 512.0, 0.0)).x; -} - -vec2 Hammersley(int i, int N) { - return vec2(float(i) / float(N), radical_inverse_VdC(i)); -} - void main() { vec3 color = vec3(0.0); vec2 uv = uv_interp; @@ -145,9 +99,6 @@ void main() { #else vec4 sum = vec4(0.0); - float solid_angle_texel = 4.0 * M_PI / (6.0 * face_size * face_size); - float roughness2 = roughness * roughness; - float roughness4 = roughness2 * roughness2; vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); mat3 T; T[0] = normalize(cross(UpVector, N)); @@ -155,32 +106,15 @@ void main() { T[2] = N; for (int sample_num = 0; sample_num < sample_count; sample_num++) { - vec2 xi = Hammersley(sample_num, sample_count); - - vec3 H = T * ImportanceSampleGGX(xi, roughness4); - float NdotH = dot(N, H); - vec3 L = (2.0 * NdotH * H - N); - - float NdotL = clamp(dot(N, L), 0.0, 1.0); - - if (NdotL > 0.0) { - float D = DistributionGGX(NdotH, roughness4); - float pdf = D * NdotH / (4.0 * NdotH) + 0.0001; - - float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001); - - float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel); - - vec3 val = textureLod(source_cube, L, mipLevel).rgb; - // Mix using linear - val = srgb_to_linear(val); - - sum.rgb += val * NdotL; - sum.a += NdotL; - } + vec4 sample_direction_mip = sample_directions_mip[sample_num]; + vec3 L = T * sample_direction_mip.xyz; + vec3 val = textureLod(source_cube, L, sample_direction_mip.w).rgb; + // Mix using linear + val = srgb_to_linear(val); + sum.rgb += val * sample_direction_mip.z; } - sum /= sum.a; + sum /= weight; sum.rgb = linear_to_srgb(sum.rgb); frag_color = vec4(sum.rgb, 1.0); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index fbea4bdbe4..ed176c7829 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -2,8 +2,11 @@ #[modes] mode_color = #define BASE_PASS +mode_color_instancing = #define BASE_PASS \n#define USE_INSTANCING mode_additive = #define USE_ADDITIVE_LIGHTING +mode_additive_instancing = #define USE_ADDITIVE_LIGHTING \n#define USE_INSTANCING mode_depth = #define MODE_RENDER_DEPTH +mode_depth_instancing = #define MODE_RENDER_DEPTH \n#define USE_INSTANCING #[specializations] @@ -13,6 +16,7 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_MULTIVIEW = false #[vertex] @@ -32,8 +36,8 @@ USE_RADIANCE_MAP = true /* from RenderingServer: ARRAY_VERTEX = 0, // RG32F or RGB32F (depending on 2D bit) -ARRAY_NORMAL = 1, // A2B10G10R10, A is ignored. -ARRAY_TANGENT = 2, // A2B10G10R10, A flips sign of binormal. +ARRAY_NORMAL = 1, // RG16 octahedral compression +ARRAY_TANGENT = 2, // RG16 octahedral compression, sign stored in sign of G ARRAY_COLOR = 3, // RGBA8 ARRAY_TEX_UV = 4, // RG32F ARRAY_TEX_UV2 = 5, // RG32F @@ -43,8 +47,6 @@ ARRAY_CUSTOM2 = 8, ARRAY_CUSTOM3 = 9, ARRAY_BONES = 10, // RGBA16UI (x2 if 8 weights) ARRAY_WEIGHTS = 11, // RGBA16UNORM (x2 if 8 weights) -ARRAY_INDEX = 12, // 16 or 32 bits depending on length > 0xFFFF. -ARRAY_MAX = 13 */ /* INPUT ATTRIBS */ @@ -53,11 +55,11 @@ layout(location = 0) in highp vec3 vertex_attrib; /* clang-format on */ #ifdef NORMAL_USED -layout(location = 1) in vec3 normal_attrib; +layout(location = 1) in vec2 normal_attrib; #endif #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) -layout(location = 2) in vec4 tangent_attrib; +layout(location = 2) in vec2 tangent_attrib; #endif #if defined(COLOR_USED) @@ -96,8 +98,22 @@ layout(location = 10) in uvec4 bone_attrib; layout(location = 11) in vec4 weight_attrib; #endif -layout(std140) uniform GlobalVariableData { //ubo:1 - vec4 global_variables[MAX_GLOBAL_VARIABLES]; +vec3 oct_to_vec3(vec2 e) { + vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); + float t = max(-v.z, 0.0); + v.xy += t * -sign(v.xy); + return v; +} + +#ifdef USE_INSTANCING +layout(location = 12) in highp vec4 instance_xform0; +layout(location = 13) in highp vec4 instance_xform1; +layout(location = 14) in highp vec4 instance_xform2; +layout(location = 15) in highp uvec4 instance_color_custom_data; // Color packed into xy, Custom data into zw. +#endif + +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; layout(std140) uniform SceneData { // ubo:2 @@ -138,6 +154,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + uniform highp mat4 world_transform; #ifdef USE_LIGHTMAP @@ -195,20 +220,29 @@ void main() { highp vec3 vertex = vertex_attrib; highp mat4 model_matrix = world_transform; +#ifdef USE_INSTANCING + highp mat4 m = mat4(instance_xform0, instance_xform1, instance_xform2, vec4(0.0, 0.0, 0.0, 1.0)); + model_matrix = model_matrix * transpose(m); +#endif #ifdef NORMAL_USED - vec3 normal = normal_attrib * 2.0 - 1.0; + vec3 normal = oct_to_vec3(normal_attrib * 2.0 - 1.0); #endif highp mat3 model_normal_matrix = mat3(model_matrix); #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - vec3 tangent = tangent_attrib.xyz * 2.0 - 1.0; - float binormalf = tangent_attrib.a * 2.0 - 1.0; + vec2 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0; + vec3 tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0)); + float binormalf = sign(signed_tangent_attrib.y); vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif #if defined(COLOR_USED) color_interp = color_attrib; +#ifdef USE_INSTANCING + vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y)); + color_interp *= instance_color; +#endif #endif #if defined(UV_USED) @@ -226,10 +260,20 @@ void main() { #if defined(OVERRIDE_POSITION) highp vec4 position; #endif - highp mat4 projection_matrix = scene_data.projection_matrix; - highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix; +#ifdef USE_MULTIVIEW + mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; +#else + mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; +#endif //USE_MULTIVIEW + +#ifdef USE_INSTANCING + vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w)); +#else vec4 instance_custom = vec4(0.0); +#endif // Using world coordinates #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) @@ -311,7 +355,6 @@ void main() { /* clang-format off */ #[fragment] - // Default to SPECULAR_SCHLICK_GGX. #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON) #define SPECULAR_SCHLICK_GGX @@ -379,8 +422,8 @@ uniform samplerCube radiance_map; // texunit:-2 #endif -layout(std140) uniform GlobalVariableData { //ubo:1 - vec4 global_variables[MAX_GLOBAL_VARIABLES]; +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; /* Material Uniforms */ @@ -435,6 +478,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + /* clang-format off */ #GLOBALS @@ -475,7 +527,7 @@ struct LightData { //this structure needs to be as packed as possible mediump float cone_attenuation; mediump float cone_angle; mediump float specular_amount; - bool shadow_enabled; + mediump float shadow_opacity; }; #ifndef DISABLE_LIGHT_OMNI layout(std140) uniform OmniLightData { // ubo:5 @@ -502,8 +554,13 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) -uniform highp sampler2D screen_texture; // texunit:-5 +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray depth_buffer; // texunit:-6 +uniform highp sampler2DArray screen_texture; // texunit:-5 +#else uniform highp sampler2D depth_buffer; // texunit:-6 +uniform highp sampler2D screen_texture; // texunit:-5 +#endif uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; @@ -601,11 +658,12 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance #if defined(DIFFUSE_LAMBERT_WRAP) - // energy conserving lambert wrap shader - diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); + // Energy conserving lambert wrap shader. + // https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/ + diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI); #elif defined(DIFFUSE_TOON) - diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL); + diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL) * (1.0 / M_PI); #elif defined(DIFFUSE_BURLEY) @@ -666,7 +724,10 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte #endif // LIGHT_ANISOTROPY_USED // F float cLdotH5 = SchlickFresnel(cLdotH); - vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0); + // Calculate Fresnel using cheap approximate specular occlusion term from Filament: + // https://google.github.io/filament/Filament.html#lighting/occlusion/specularocclusion + float f90 = clamp(50.0 * f0.g, 0.0, 1.0); + vec3 F = f0 + (f90 - f0) * cLdotH5; vec3 specular_brdf_NL = cNdotL * D * F * G; @@ -731,7 +792,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f if (omni_lights[idx].size > 0.0) { float t = omni_lights[idx].size / max(0.001, light_length); - size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); } light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, @@ -780,7 +841,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f if (spot_lights[idx].size > 0.0) { float t = spot_lights[idx].size / max(0.001, light_length); - size_A = max(0.0, 1.0 - 1 / sqrt(1 + t * t)); + size_A = max(0.0, 1.0 - 1.0 / sqrt(1.0 + t * t)); } light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, @@ -852,7 +913,11 @@ vec4 fog_process(vec3 vertex) { void main() { //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; +#ifdef USE_MULTIVIEW + vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz); +#else vec3 view = -normalize(vertex_interp); +#endif vec3 albedo = vec3(1.0); vec3 backlight = vec3(0.0); vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0); @@ -1029,6 +1094,7 @@ void main() { #else vec3 ref_vec = reflect(-view, normal); #endif + ref_vec = mix(ref_vec, normal, roughness * roughness); float horizon = min(1.0 + dot(ref_vec, normal), 1.0); ref_vec = scene_data.radiance_inverse_xform * ref_vec; specular_light = textureLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).rgb; @@ -1063,9 +1129,15 @@ void main() { #if defined(CUSTOM_IRRADIANCE_USED) ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED - ambient_light *= albedo.rgb; - ambient_light *= ao; + { +#if defined(AMBIENT_LIGHT_DISABLED) + ambient_light = vec3(0.0, 0.0, 0.0); +#else + ambient_light *= albedo.rgb; + ambient_light *= ao; +#endif // AMBIENT_LIGHT_DISABLED + } // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); @@ -1087,7 +1159,7 @@ void main() { float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; - specular_light *= env.x * f0 + env.y; + specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, 0.0, 1.0); #endif } diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 50ab38bc31..4c0fe47f6b 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -42,8 +42,8 @@ uniform sampler2D half_res; //texunit:-2 uniform sampler2D quarter_res; //texunit:-3 #endif -layout(std140) uniform GlobalVariableData { //ubo:1 - vec4 global_variables[MAX_GLOBAL_VARIABLES]; +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; struct DirectionalLightData { @@ -92,6 +92,7 @@ uniform mat4 orientation; uniform vec4 projection; uniform vec3 position; uniform float time; +uniform float luminance_multiplier; uniform float fog_aerial_perspective; uniform vec3 fog_light_color; @@ -103,6 +104,15 @@ uniform uint directional_light_count; layout(location = 0) out vec4 frag_color; +#ifdef USE_DEBANDING +// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare +vec3 interleaved_gradient_noise(vec2 pos) { + const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); + float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0; + return vec3(res, -res, res) / 255.0; +} +#endif + void main() { vec3 cube_normal; cube_normal.z = -1.0; @@ -149,6 +159,8 @@ void main() { } + color *= luminance_multiplier; + // Convert to Linear for tonemapping so color matches scene shader better color = srgb_to_linear(color); color *= exposure; @@ -165,4 +177,8 @@ void main() { frag_color.rgb = color; frag_color.a = alpha; + +#ifdef USE_DEBANDING + frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy); +#endif } diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index a478cf9170..0b769e77f2 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -44,7 +44,11 @@ in vec2 uv_interp; layout(location = 0) out vec4 frag_color; +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray source; //texunit:0 +#else uniform highp sampler2D source; //texunit:0 +#endif #if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) #define USING_GLOW // only use glow when at least one glow level is selected @@ -191,10 +195,17 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { const float FXAA_REDUCE_MUL = (1.0 / 8.0); const float FXAA_SPAN_MAX = 8.0; +#ifdef USE_MULTIVIEW + vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; +#else vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; +#endif vec3 rgbM = color; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); @@ -219,8 +230,13 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { dir * rcpDirMin)) * pixel_size; +#ifdef USE_MULTIVIEW + vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); +#else vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); +#endif float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) { @@ -231,7 +247,11 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { } void main() { +#ifdef USE_MULTIVIEW + vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0); +#else vec4 color = textureLod(source, uv_interp, 0.0); +#endif #ifdef USE_FXAA color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index f2809734a9..9b496c0999 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -34,6 +34,15 @@ #include "core/config/project_settings.h" #include "core/templates/vector.h" +#ifdef ANDROID_ENABLED +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <GLES3/gl3platform.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + using namespace GLES3; #define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF @@ -62,32 +71,42 @@ Config::Config() { s3tc_supported = true; rgtc_supported = true; //RGTC - core since OpenGL version 3.0 #else - float_texture_supported = extensions.has("GL_ARB_texture_float") || extensions.has("GL_OES_texture_float"); + float_texture_supported = extensions.has("GL_EXT_color_buffer_float"); etc2_supported = true; +#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) + // Some Android devices report support for S3TC but we don't expect that and don't export the textures. + // This could be fixed but so few devices support it that it doesn't seem useful (and makes bigger APKs). + // For good measure we do the same hack for iOS, just in case. + s3tc_supported = false; +#else s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc"); - rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc"); #endif - -#ifdef GLES_OVER_GL - use_rgba_2d_shadows = false; -#else - use_rgba_2d_shadows = !(float_texture_supported && extensions.has("GL_EXT_texture_rg")); + rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc"); #endif glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size); - // the use skeleton software path should be used if either float texture is not supported, - // OR max_vertex_texture_image_units is zero - use_skeleton_software = (float_texture_supported == false) || (max_vertex_texture_image_units == 0); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment); support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic"); if (support_anisotropic_filter) { glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level); - anisotropic_level = MIN(float(1 << int(ProjectSettings::get_singleton()->get("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); + anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); + } + + multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#ifdef ANDROID_ENABLED + if (multiview_supported) { + eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); + if (eglFramebufferTextureMultiviewOVR == nullptr) { + multiview_supported = false; + } } +#endif force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index db76aa79fb..87202fde84 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -44,6 +44,10 @@ #include OPENGL_INCLUDE_H #endif +#ifdef ANDROID_ENABLED +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +#endif + namespace GLES3 { class Config { @@ -52,18 +56,19 @@ private: public: bool use_nearest_mip_filter = false; - bool use_skeleton_software = false; bool use_depth_prepass = true; - bool use_rgba_2d_shadows = false; int max_vertex_texture_image_units = 0; int max_texture_image_units = 0; int max_texture_size = 0; + int max_viewport_size[2] = { 0, 0 }; int max_uniform_buffer_size = 0; int max_renderable_elements = 0; int max_renderable_lights = 0; int max_lights_per_object = 0; + int uniform_buffer_offset_alignment = 0; + // TODO implement wireframe in OpenGL // bool generate_wireframes; @@ -80,6 +85,11 @@ public: bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + bool multiview_supported = false; +#ifdef ANDROID_ENABLED + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; +#endif + static Config *get_singleton() { return singleton; }; Config(); @@ -90,4 +100,4 @@ public: #endif // GLES3_ENABLED -#endif // !CONFIG_GLES3_H +#endif // CONFIG_GLES3_H diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 954aa11c0d..b6bd4a2760 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -58,6 +58,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; light.param[RS::LIGHT_PARAM_RANGE] = 1.0; light.param[RS::LIGHT_PARAM_SIZE] = 0.0; @@ -70,10 +71,10 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET] = 0.6; light.param[RS::LIGHT_PARAM_SHADOW_FADE_START] = 0.8; light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0; + light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02; light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; - light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; light_owner.initialize_rid(p_light, light); @@ -139,12 +140,12 @@ void LightStorage::light_set_param(RID p_light, RS::LightParam p_param, float p_ case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE: case RS::LIGHT_PARAM_SHADOW_BIAS: { light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } break; case RS::LIGHT_PARAM_SIZE: { if ((light->param[p_param] > CMP_EPSILON) != (p_value > CMP_EPSILON)) { //changing from no size to size and the opposite - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } break; default: { @@ -160,7 +161,7 @@ void LightStorage::light_set_shadow(RID p_light, bool p_enabled) { light->shadow = p_enabled; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_projector(RID p_light, RID p_texture) { @@ -182,7 +183,7 @@ void LightStorage::light_set_projector(RID p_light, RID p_texture) { if (light->projector.is_valid()) { texture_storage->texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); } } @@ -200,7 +201,7 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) { light->cull_mask = p_mask; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) { @@ -220,7 +221,7 @@ void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) light->reverse_cull = p_enabled; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { @@ -230,7 +231,7 @@ void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mod light->bake_mode = p_bake_mode; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { @@ -240,7 +241,7 @@ void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMo light->omni_shadow_mode = p_mode; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) { @@ -256,7 +257,7 @@ void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirec light->directional_shadow_mode = p_mode; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) { @@ -265,7 +266,7 @@ void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable light->directional_blend_splits = p_enable; light->version++; - light->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_LIGHT); + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); } bool LightStorage::light_directional_get_blend_splits(RID p_light) const { @@ -317,7 +318,7 @@ AABB LightStorage::light_get_aabb(RID p_light) const { switch (light->type) { case RS::LIGHT_SPOT: { float len = light->param[RS::LIGHT_PARAM_RANGE]; - float size = Math::tan(Math::deg2rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len; + float size = Math::tan(Math::deg_to_rad(light->param[RS::LIGHT_PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); }; case RS::LIGHT_OMNI: { @@ -332,6 +333,46 @@ AABB LightStorage::light_get_aabb(RID p_light) const { ERR_FAIL_V(AABB()); } +/* LIGHT INSTANCE API */ + +RID LightStorage::light_instance_create(RID p_light) { + RID li = light_instance_owner.make_rid(LightInstance()); + + LightInstance *light_instance = light_instance_owner.get_or_null(li); + + light_instance->self = li; + light_instance->light = p_light; + light_instance->light_type = light_get_type(p_light); + + return li; +} + +void LightStorage::light_instance_free(RID p_light_instance) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + light_instance_owner.free(p_light_instance); +} + +void LightStorage::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; +} + +void LightStorage::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->aabb = p_aabb; +} + +void LightStorage::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { +} + +void LightStorage::light_instance_mark_visible(RID p_light_instance) { +} + /* PROBE API */ RID LightStorage::reflection_probe_allocate() { @@ -418,16 +459,67 @@ float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { return 0.0; } +/* REFLECTION ATLAS */ + +RID LightStorage::reflection_atlas_create() { + return RID(); +} + +void LightStorage::reflection_atlas_free(RID p_ref_atlas) { +} + +int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const { + return 0; +} + +void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { +} + +/* REFLECTION PROBE INSTANCE */ + +RID LightStorage::reflection_probe_instance_create(RID p_probe) { + return RID(); +} + +void LightStorage::reflection_probe_instance_free(RID p_instance) { +} + +void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { +} + +void LightStorage::reflection_probe_release_atlas_index(RID p_instance) { +} + +bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) { + return false; +} + +bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) { + return false; +} + +bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { + return false; +} + +bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) { + return true; +} + /* LIGHTMAP CAPTURE */ RID LightStorage::lightmap_allocate() { - return RID(); + return lightmap_owner.allocate_rid(); } void LightStorage::lightmap_initialize(RID p_rid) { + lightmap_owner.initialize_rid(p_rid, Lightmap()); } void LightStorage::lightmap_free(RID p_rid) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + lightmap->dependency.deleted_notify(p_rid); + lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { @@ -442,6 +534,9 @@ void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { } +void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { return PackedVector3Array(); } @@ -476,4 +571,148 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const { return 0; } +/* LIGHTMAP INSTANCE */ + +RID LightStorage::lightmap_instance_create(RID p_lightmap) { + return RID(); +} + +void LightStorage::lightmap_instance_free(RID p_lightmap) { +} + +void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { +} + +/* LIGHT SHADOW MAPPING */ +/* + +RID LightStorage::canvas_light_occluder_create() { + CanvasOccluder *co = memnew(CanvasOccluder); + co->index_id = 0; + co->vertex_id = 0; + co->len = 0; + + return canvas_occluder_owner.make_rid(co); +} + +void LightStorage::canvas_light_occluder_set_polylines(RID p_occluder, const PoolVector<Vector2> &p_lines) { + CanvasOccluder *co = canvas_occluder_owner.get(p_occluder); + ERR_FAIL_COND(!co); + + co->lines = p_lines; + + if (p_lines.size() != co->len) { + if (co->index_id) { + glDeleteBuffers(1, &co->index_id); + } if (co->vertex_id) { + glDeleteBuffers(1, &co->vertex_id); + } + + co->index_id = 0; + co->vertex_id = 0; + co->len = 0; + } + + if (p_lines.size()) { + PoolVector<float> geometry; + PoolVector<uint16_t> indices; + int lc = p_lines.size(); + + geometry.resize(lc * 6); + indices.resize(lc * 3); + + PoolVector<float>::Write vw = geometry.write(); + PoolVector<uint16_t>::Write iw = indices.write(); + + PoolVector<Vector2>::Read lr = p_lines.read(); + + const int POLY_HEIGHT = 16384; + + for (int i = 0; i < lc / 2; i++) { + vw[i * 12 + 0] = lr[i * 2 + 0].x; + vw[i * 12 + 1] = lr[i * 2 + 0].y; + vw[i * 12 + 2] = POLY_HEIGHT; + + vw[i * 12 + 3] = lr[i * 2 + 1].x; + vw[i * 12 + 4] = lr[i * 2 + 1].y; + vw[i * 12 + 5] = POLY_HEIGHT; + + vw[i * 12 + 6] = lr[i * 2 + 1].x; + vw[i * 12 + 7] = lr[i * 2 + 1].y; + vw[i * 12 + 8] = -POLY_HEIGHT; + + vw[i * 12 + 9] = lr[i * 2 + 0].x; + vw[i * 12 + 10] = lr[i * 2 + 0].y; + vw[i * 12 + 11] = -POLY_HEIGHT; + + iw[i * 6 + 0] = i * 4 + 0; + iw[i * 6 + 1] = i * 4 + 1; + iw[i * 6 + 2] = i * 4 + 2; + + iw[i * 6 + 3] = i * 4 + 2; + iw[i * 6 + 4] = i * 4 + 3; + iw[i * 6 + 5] = i * 4 + 0; + } + + //if same buffer len is being set, just use BufferSubData to avoid a pipeline flush + + if (!co->vertex_id) { + glGenBuffers(1, &co->vertex_id); + glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); + glBufferData(GL_ARRAY_BUFFER, lc * 6 * sizeof(real_t), vw.ptr(), GL_STATIC_DRAW); + } else { + glBindBuffer(GL_ARRAY_BUFFER, co->vertex_id); + glBufferSubData(GL_ARRAY_BUFFER, 0, lc * 6 * sizeof(real_t), vw.ptr()); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + if (!co->index_id) { + glGenBuffers(1, &co->index_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, lc * 3 * sizeof(uint16_t), iw.ptr(), GL_DYNAMIC_DRAW); + } else { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, co->index_id); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, lc * 3 * sizeof(uint16_t), iw.ptr()); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind + + co->len = lc; + } +} +*/ + +/* SHADOW ATLAS API */ + +RID LightStorage::shadow_atlas_create() { + return RID(); +} + +void LightStorage::shadow_atlas_free(RID p_atlas) { +} + +void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { +} + +void LightStorage::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { +} + +bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + return false; +} + +void LightStorage::shadow_atlas_update(RID p_atlas) { +} + +void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { +} + +int LightStorage::get_directional_light_shadow_size(RID p_light_intance) { + return 0; +} + +void LightStorage::set_directional_shadow_count(int p_count) { +} + #endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 5acaf45aa3..12cb2c4393 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -36,9 +36,10 @@ #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" +#include "drivers/gles3/storage/texture_storage.h" #include "servers/rendering/renderer_compositor.h" -#include "servers/rendering/renderer_storage.h" #include "servers/rendering/storage/light_storage.h" +#include "servers/rendering/storage/utilities.h" #include "platform_config.h" #ifndef OPENGL_INCLUDE_H @@ -72,7 +73,34 @@ struct Light { RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY; uint64_t version = 0; - RendererStorage::Dependency dependency; + Dependency dependency; +}; + +/* Light instance */ +struct LightInstance { + RS::LightType light_type = RS::LIGHT_DIRECTIONAL; + + AABB aabb; + RID self; + RID light; + Transform3D transform; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att = 0.0; + + uint64_t shadow_pass = 0; + uint64_t last_scene_pass = 0; + uint64_t last_scene_shadow_pass = 0; + uint64_t last_pass = 0; + uint32_t cull_mask = 0; + uint32_t light_directional_index = 0; + + Rect2 directional_rect; + + uint32_t gl_id = -1; + + LightInstance() {} }; /* REFLECTION PROBE */ @@ -92,8 +120,9 @@ struct ReflectionProbe { bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; + float baked_exposure = 1.0; - RendererStorage::Dependency dependency; + Dependency dependency; }; /* LIGHTMAP */ @@ -103,6 +132,7 @@ struct Lightmap { bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + float baked_exposure = 1.0; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -115,7 +145,7 @@ struct Lightmap { int32_t over = EMPTY_LEAF, under = EMPTY_LEAF; }; - RendererStorage::Dependency dependency; + Dependency dependency; }; class LightStorage : public RendererLightStorage { @@ -125,6 +155,9 @@ private: /* LIGHT */ mutable RID_Owner<Light, true> light_owner; + /* Light instance */ + mutable RID_Owner<LightInstance> light_instance_owner; + /* REFLECTION PROBE */ mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner; @@ -244,7 +277,7 @@ public: const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); - return light_owner.owns(light->projector); + return TextureStorage::get_singleton()->owns_texture(light->projector); } _FORCE_INLINE_ bool light_is_negative(RID p_light) const { @@ -261,17 +294,32 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } - _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const { - const Light *light = light_owner.get_or_null(p_light); - ERR_FAIL_COND_V(!light, 0.0); - - return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE]; - } - virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override; + /* LIGHT INSTANCE API */ + + LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }; + bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }; + + virtual RID light_instance_create(RID p_light) override; + virtual void light_instance_free(RID p_light_instance) override; + + virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; + virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; + virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; + virtual void light_instance_mark_visible(RID p_light_instance) override; + + _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->light_type; + } + _FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->gl_id; + } + /* PROBE API */ virtual RID reflection_probe_allocate() override; @@ -302,8 +350,29 @@ public: virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override; virtual bool reflection_probe_renders_shadows(RID p_probe) const override; + /* REFLECTION ATLAS */ + + virtual RID reflection_atlas_create() override; + virtual void reflection_atlas_free(RID p_ref_atlas) override; + virtual int reflection_atlas_get_size(RID p_ref_atlas) const override; + virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override; + + /* REFLECTION PROBE INSTANCE */ + + virtual RID reflection_probe_instance_create(RID p_probe) override; + virtual void reflection_probe_instance_free(RID p_instance) override; + virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; + virtual void reflection_probe_release_atlas_index(RID p_instance) override; + virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override; + virtual bool reflection_probe_instance_has_reflection(RID p_instance) override; + virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; + virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override; + /* LIGHTMAP CAPTURE */ + Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }; + bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }; + virtual RID lightmap_allocate() override; virtual void lightmap_initialize(RID p_rid) override; virtual void lightmap_free(RID p_rid) override; @@ -312,6 +381,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; @@ -321,10 +391,47 @@ public: virtual bool lightmap_is_interior(RID p_lightmap) const override; virtual void lightmap_set_probe_capture_update_speed(float p_speed) override; virtual float lightmap_get_probe_capture_update_speed() const override; + + /* LIGHT SHADOW MAPPING */ + /* + struct CanvasOccluder { + RID self; + + GLuint vertex_id; // 0 means, unconfigured + GLuint index_id; // 0 means, unconfigured + LocalVector<Vector2> lines; + int len; + }; + + RID_Owner<CanvasOccluder> canvas_occluder_owner; + + RID canvas_light_occluder_create(); + void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines); + */ + + /* LIGHTMAP INSTANCE */ + + virtual RID lightmap_instance_create(RID p_lightmap) override; + virtual void lightmap_instance_free(RID p_lightmap) override; + virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; + + /* SHADOW ATLAS API */ + + virtual RID shadow_atlas_create() override; + virtual void shadow_atlas_free(RID p_atlas) override; + virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override; + virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override; + virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override; + + virtual void shadow_atlas_update(RID p_atlas) override; + + virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override; + virtual int get_directional_light_shadow_size(RID p_light_intance) override; + virtual void set_directional_shadow_count(int p_count) override; }; } // namespace GLES3 -#endif // !GLES3_ENABLED +#endif // GLES3_ENABLED -#endif // !LIGHT_STORAGE_GLES3_H +#endif // LIGHT_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 8799db6bc6..2d6a793c9e 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -89,7 +89,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = v & 1 ? 1 : 0; gui[1] = v & 2 ? 1 : 0; } @@ -116,7 +116,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -145,7 +145,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy } } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -176,75 +176,86 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy } } break; case ShaderLanguage::TYPE_IVEC2: { - Vector<int> iv = value; - int s = iv.size(); int32_t *gui = (int32_t *)data; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 2 * p_array_size; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 2 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0, j = 0; i < count; i += 2, j += 4) { - if (i < s) { - gui[j] = r[i]; - gui[j + 1] = r[i + 1]; - } else { - gui[j] = 0; - gui[j + 1] = 0; + const int *r = iv.ptr(); + for (int i = 0, j = 0; i < count; i += 2, j += 4) { + if (i < s) { + gui[j] = r[i]; + gui[j + 1] = r[i + 1]; + } else { + gui[j] = 0; + gui[j + 1] = 0; + } + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored } - gui[j + 2] = 0; // ignored - gui[j + 3] = 0; // ignored + } else { + Vector2i v = value; + gui[0] = v.x; + gui[1] = v.y; } } break; case ShaderLanguage::TYPE_IVEC3: { - Vector<int> iv = value; - int s = iv.size(); int32_t *gui = (int32_t *)data; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 3 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0, j = 0; i < count; i += 3, j += 4) { - if (i < s) { - gui[j] = r[i]; - gui[j + 1] = r[i + 1]; - gui[j + 2] = r[i + 2]; - } else { - gui[j] = 0; - gui[j + 1] = 0; - gui[j + 2] = 0; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 3 * p_array_size; + + const int *r = iv.ptr(); + for (int i = 0, j = 0; i < count; i += 3, j += 4) { + if (i < s) { + gui[j] = r[i]; + gui[j + 1] = r[i + 1]; + gui[j + 2] = r[i + 2]; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + } + gui[j + 3] = 0; // ignored } - gui[j + 3] = 0; // ignored + } else { + Vector3i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; } } break; case ShaderLanguage::TYPE_IVEC4: { - Vector<int> iv = value; - int s = iv.size(); int32_t *gui = (int32_t *)data; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 4 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0; i < count; i += 4) { - if (i < s) { - gui[i] = r[i]; - gui[i + 1] = r[i + 1]; - gui[i + 2] = r[i + 2]; - gui[i + 3] = r[i + 3]; - } else { - gui[i] = 0; - gui[i + 1] = 0; - gui[i + 2] = 0; - gui[i + 3] = 0; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 4 * p_array_size; + + const int *r = iv.ptr(); + for (int i = 0; i < count; i += 4) { + if (i < s) { + gui[i] = r[i]; + gui[i + 1] = r[i + 1]; + gui[i + 2] = r[i + 2]; + gui[i + 3] = r[i + 3]; + } else { + gui[i] = 0; + gui[i + 1] = 0; + gui[i + 2] = 0; + gui[i + 3] = 0; + } } + } else { + Vector4i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + gui[3] = v.w; } } break; case ShaderLanguage::TYPE_UINT: { @@ -271,75 +282,86 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy } } break; case ShaderLanguage::TYPE_UVEC2: { - Vector<int> iv = value; - int s = iv.size(); uint32_t *gui = (uint32_t *)data; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 2 * p_array_size; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 2 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0, j = 0; i < count; i += 2, j += 4) { - if (i < s) { - gui[j] = r[i]; - gui[j + 1] = r[i + 1]; - } else { - gui[j] = 0; - gui[j + 1] = 0; + const int *r = iv.ptr(); + for (int i = 0, j = 0; i < count; i += 2, j += 4) { + if (i < s) { + gui[j] = r[i]; + gui[j + 1] = r[i + 1]; + } else { + gui[j] = 0; + gui[j + 1] = 0; + } + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored } - gui[j + 2] = 0; // ignored - gui[j + 3] = 0; // ignored + } else { + Vector2i v = value; + gui[0] = v.x; + gui[1] = v.y; } } break; case ShaderLanguage::TYPE_UVEC3: { - Vector<int> iv = value; - int s = iv.size(); uint32_t *gui = (uint32_t *)data; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 3 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0, j = 0; i < count; i += 3, j += 4) { - if (i < s) { - gui[j] = r[i]; - gui[j + 1] = r[i + 1]; - gui[j + 2] = r[i + 2]; - } else { - gui[j] = 0; - gui[j + 1] = 0; - gui[j + 2] = 0; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 3 * p_array_size; + + const int *r = iv.ptr(); + for (int i = 0, j = 0; i < count; i += 3, j += 4) { + if (i < s) { + gui[j] = r[i]; + gui[j + 1] = r[i + 1]; + gui[j + 2] = r[i + 2]; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + } + gui[j + 3] = 0; // ignored } - gui[j + 3] = 0; // ignored + } else { + Vector3i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; } } break; case ShaderLanguage::TYPE_UVEC4: { - Vector<int> iv = value; - int s = iv.size(); uint32_t *gui = (uint32_t *)data; - if (p_array_size <= 0) { - p_array_size = 1; - } - int count = 4 * p_array_size; - - const int *r = iv.ptr(); - for (int i = 0; i < count; i++) { - if (i < s) { - gui[i] = r[i]; - gui[i + 1] = r[i + 1]; - gui[i + 2] = r[i + 2]; - gui[i + 3] = r[i + 3]; - } else { - gui[i] = 0; - gui[i + 1] = 0; - gui[i + 2] = 0; - gui[i + 3] = 0; + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + int count = 4 * p_array_size; + + const int *r = iv.ptr(); + for (int i = 0; i < count; i++) { + if (i < s) { + gui[i] = r[i]; + gui[i + 1] = r[i + 1]; + gui[i + 2] = r[i + 2]; + gui[i + 3] = r[i + 3]; + } else { + gui[i] = 0; + gui[i + 1] = 0; + gui[i + 2] = 0; + gui[i + 3] = 0; + } } + } else { + Vector4i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + gui[3] = v.w; } } break; case ShaderLanguage::TYPE_FLOAT: { @@ -504,6 +526,13 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[1] = v.y; gui[2] = v.z; gui[3] = v.w; + } else if (value.get_type() == Variant::VECTOR4) { + Vector4 v = value; + + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + gui[3] = v.w; } else { Plane v = value; @@ -660,7 +689,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[i + 15] = 1; } } - } else { + } else if (value.get_type() == Variant::TRANSFORM3D) { Transform3D v = value; gui[0] = v.basis.rows[0][0]; gui[1] = v.basis.rows[1][0]; @@ -681,6 +710,13 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[13] = v.origin.y; gui[14] = v.origin.z; gui[15] = 1; + } else { + Projection v = value; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + gui[i * 4 + j] = v.columns[i][j]; + } + } } } break; default: { @@ -692,7 +728,7 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; - *gui = value[0].boolean ? 1 : 0; + gui[0] = value[0].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC2: { uint32_t *gui = (uint32_t *)data; @@ -861,7 +897,9 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, case ShaderLanguage::TYPE_BVEC3: case ShaderLanguage::TYPE_IVEC3: case ShaderLanguage::TYPE_UVEC3: - case ShaderLanguage::TYPE_VEC3: + case ShaderLanguage::TYPE_VEC3: { + memset(data, 0, 12 * p_array_size); + } break; case ShaderLanguage::TYPE_BVEC4: case ShaderLanguage::TYPE_IVEC4: case ShaderLanguage::TYPE_UVEC4: @@ -937,12 +975,12 @@ void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguag if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) { //this is a global variable, get the index to it - GlobalVariables::Variable *gv = material_storage->global_variables.variables.getptr(E.key); + GlobalShaderUniforms::Variable *gv = material_storage->global_shader_uniforms.variables.getptr(E.key); uint32_t index = 0; if (gv) { index = gv->buffer_index; } else { - WARN_PRINT("Shader uses global uniform '" + E.key + "', but it was removed at some point. Material will not display correctly."); + WARN_PRINT("Shader uses global parameter '" + E.key + "', but it was removed at some point. Material will not display correctly."); } uint32_t offset = p_uniform_offsets[E.value.order]; @@ -993,9 +1031,9 @@ void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguag if (uses_global_buffer != (global_buffer_E != nullptr)) { if (uses_global_buffer) { - global_buffer_E = material_storage->global_variables.materials_using_buffer.push_back(self); + global_buffer_E = material_storage->global_shader_uniforms.materials_using_buffer.push_back(self); } else { - material_storage->global_variables.materials_using_buffer.erase(global_buffer_E); + material_storage->global_shader_uniforms.materials_using_buffer.erase(global_buffer_E); global_buffer_E = nullptr; } } @@ -1006,20 +1044,20 @@ MaterialData::~MaterialData() { if (global_buffer_E) { //unregister global buffers - material_storage->global_variables.materials_using_buffer.erase(global_buffer_E); + material_storage->global_shader_uniforms.materials_using_buffer.erase(global_buffer_E); } if (global_texture_E) { //unregister global textures for (const KeyValue<StringName, uint64_t> &E : used_global_textures) { - GlobalVariables::Variable *v = material_storage->global_variables.variables.getptr(E.key); + GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(E.key); if (v) { v->texture_materials.erase(self); } } //unregister material from those using global textures - material_storage->global_variables.materials_using_texture.erase(global_texture_E); + material_storage->global_shader_uniforms.materials_using_texture.erase(global_texture_E); } if (uniform_buffer) { @@ -1047,13 +1085,19 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet Vector<RID> textures; + if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || + p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || + p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { + continue; + } + if (p_texture_uniforms[i].global) { uses_global_textures = true; - GlobalVariables::Variable *v = material_storage->global_variables.variables.getptr(uniform_name); + GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(uniform_name); if (v) { if (v->buffer_index >= 0) { - WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it changed type and is no longer a texture!."); + WARN_PRINT("Shader uses global parameter texture '" + String(uniform_name) + "', but it changed type and is no longer a texture!."); } else { HashMap<StringName, uint64_t>::Iterator E = used_global_textures.find(uniform_name); @@ -1068,7 +1112,7 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet } } else { - WARN_PRINT("Shader uses global uniform texture '" + String(uniform_name) + "', but it was removed at some point. Material will not display correctly."); + WARN_PRINT("Shader uses global parameter texture '" + String(uniform_name) + "', but it was removed at some point. Material will not display correctly."); } } else { HashMap<StringName, Variant>::ConstIterator V = p_parameters.find(uniform_name); @@ -1120,6 +1164,9 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: { gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_BLACK); } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_TRANSPARENT: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_TRANSPARENT); + } break; case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_ANISO); } break; @@ -1222,7 +1269,7 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet if (E.value != global_textures_pass) { to_delete.push_back(E.key); - GlobalVariables::Variable *v = material_storage->global_variables.variables.getptr(E.key); + GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(E.key); if (v) { v->texture_materials.erase(self); } @@ -1236,9 +1283,9 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet //handle registering/unregistering global textures if (uses_global_textures != (global_texture_E != nullptr)) { if (uses_global_textures) { - global_texture_E = material_storage->global_variables.materials_using_texture.push_back(self); + global_texture_E = material_storage->global_shader_uniforms.materials_using_texture.push_back(self); } else { - material_storage->global_variables.materials_using_texture.erase(global_texture_E); + material_storage->global_shader_uniforms.materials_using_texture.erase(global_texture_E); global_texture_E = nullptr; } } @@ -1305,22 +1352,22 @@ MaterialStorage::MaterialStorage() { material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func; material_data_request_func[RS::SHADER_FOG] = nullptr; - static_assert(sizeof(GlobalVariables::Value) == 16); + static_assert(sizeof(GlobalShaderUniforms::Value) == 16); - global_variables.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size")); - if (global_variables.buffer_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) { - global_variables.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size); + global_shader_uniforms.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size")); + if (global_shader_uniforms.buffer_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) { + global_shader_uniforms.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size); WARN_PRINT("Project setting: rendering/limits/global_shader_variables/buffer_size exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size)); } - global_variables.buffer_values = memnew_arr(GlobalVariables::Value, global_variables.buffer_size); - memset(global_variables.buffer_values, 0, sizeof(GlobalVariables::Value) * global_variables.buffer_size); - global_variables.buffer_usage = memnew_arr(GlobalVariables::ValueUsage, global_variables.buffer_size); - global_variables.buffer_dirty_regions = memnew_arr(bool, global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); - memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE); - glGenBuffers(1, &global_variables.buffer); - glBindBuffer(GL_UNIFORM_BUFFER, global_variables.buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalVariables::Value) * global_variables.buffer_size, nullptr, GL_DYNAMIC_DRAW); + global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size); + memset(global_shader_uniforms.buffer_values, 0, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size); + global_shader_uniforms.buffer_usage = memnew_arr(GlobalShaderUniforms::ValueUsage, global_shader_uniforms.buffer_size); + global_shader_uniforms.buffer_dirty_regions = memnew_arr(bool, global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE); + memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE); + glGenBuffers(1, &global_shader_uniforms.buffer); + glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); { @@ -1334,9 +1381,9 @@ MaterialStorage::MaterialStorage() { actions.renames["POINT_SIZE"] = "gl_PointSize"; actions.renames["MODEL_MATRIX"] = "model_matrix"; - actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; - actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; - actions.renames["TIME"] = "canvas_data.time"; + actions.renames["CANVAS_MATRIX"] = "canvas_transform"; + actions.renames["SCREEN_MATRIX"] = "screen_transform"; + actions.renames["TIME"] = "time"; actions.renames["PI"] = _MKSTR(Math_PI); actions.renames["TAU"] = _MKSTR(Math_TAU); actions.renames["E"] = _MKSTR(Math_E); @@ -1348,19 +1395,21 @@ MaterialStorage::MaterialStorage() { actions.renames["NORMAL_MAP"] = "normal_map"; actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth"; actions.renames["TEXTURE"] = "color_texture"; - actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size"; + actions.renames["TEXTURE_PIXEL_SIZE"] = "color_texture_pixel_size"; actions.renames["NORMAL_TEXTURE"] = "normal_texture"; actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; actions.renames["SCREEN_UV"] = "screen_uv"; actions.renames["SCREEN_TEXTURE"] = "screen_texture"; - actions.renames["SCREEN_PIXEL_SIZE"] = "canvas_data.screen_pixel_size"; + actions.renames["SCREEN_PIXEL_SIZE"] = "screen_pixel_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; actions.renames["POINT_COORD"] = "gl_PointCoord"; actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; actions.renames["VERTEX_ID"] = "gl_VertexIndex"; actions.renames["LIGHT_POSITION"] = "light_position"; + actions.renames["LIGHT_DIRECTION"] = "light_direction"; + actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional"; actions.renames["LIGHT_COLOR"] = "light_color"; actions.renames["LIGHT_ENERGY"] = "light_energy"; actions.renames["LIGHT"] = "light"; @@ -1378,6 +1427,7 @@ MaterialStorage::MaterialStorage() { actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; actions.usage_defines["LIGHT"] = "#define LIGHT_SHADER_CODE_USED\n"; + actions.usage_defines["SPECULAR_SHININESS"] = "#define SPECULAR_SHININESS_USED\n"; actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; @@ -1410,8 +1460,8 @@ MaterialStorage::MaterialStorage() { actions.renames["UV2"] = "uv2_interp"; actions.renames["COLOR"] = "color_interp"; actions.renames["POINT_SIZE"] = "gl_PointSize"; - actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; - actions.renames["VERTEX_ID"] = "gl_VertexIndex"; + actions.renames["INSTANCE_ID"] = "gl_InstanceID"; + actions.renames["VERTEX_ID"] = "gl_VertexID"; actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold"; actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale"; @@ -1452,12 +1502,12 @@ MaterialStorage::MaterialStorage() { actions.renames["POINT_COORD"] = "gl_PointCoord"; actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; actions.renames["SCREEN_UV"] = "screen_uv"; - actions.renames["SCREEN_TEXTURE"] = "color_buffer"; - actions.renames["DEPTH_TEXTURE"] = "depth_buffer"; - actions.renames["NORMAL_ROUGHNESS_TEXTURE"] = "normal_roughness_buffer"; + //actions.renames["SCREEN_TEXTURE"] = "color_buffer"; //Not implemented in 3D yet. + //actions.renames["DEPTH_TEXTURE"] = "depth_buffer"; // Not implemented in 3D yet. + //actions.renames["NORMAL_ROUGHNESS_TEXTURE"] = "normal_roughness_buffer"; // Not implemented in 3D yet actions.renames["DEPTH"] = "gl_FragDepth"; actions.renames["OUTPUT_IS_SRGB"] = "true"; - actions.renames["FOG"] = "custom_fog"; + actions.renames["FOG"] = "fog"; actions.renames["RADIANCE"] = "custom_radiance"; actions.renames["IRRADIANCE"] = "custom_irradiance"; actions.renames["BONE_INDICES"] = "bone_attrib"; @@ -1468,6 +1518,11 @@ MaterialStorage::MaterialStorage() { actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB"; + actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz"; + actions.renames["CAMERA_POSITION_WORLD"] = "scene_data.inv_view_matrix[3].xyz"; + actions.renames["CAMERA_DIRECTION_WORLD"] = "scene_data.view_matrix[3].xyz"; + actions.renames["NODE_POSITION_VIEW"] = "(model_matrix * scene_data.view_matrix)[3].xyz"; + actions.renames["VIEW_INDEX"] = "ViewIndex"; actions.renames["VIEW_MONO_LEFT"] = "0"; actions.renames["VIEW_RIGHT"] = "1"; @@ -1603,7 +1658,7 @@ ShaderCompiler::DefaultIdentifierActions actions; actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; - actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISON_SCALE\n"; + actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISION_SCALE\n"; actions.sampler_array_name = "material_samplers"; actions.base_texture_binding_index = 1; @@ -1613,7 +1668,7 @@ ShaderCompiler::DefaultIdentifierActions actions; actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; - actions.global_buffer_array_variable = "global_variables.data"; + actions.global_buffer_array_variable = "global_shader_uniforms.data"; particles_shader.compiler.initialize(actions); */ @@ -1664,6 +1719,7 @@ ShaderCompiler::DefaultIdentifierActions actions; actions.usage_defines["HALF_RES_COLOR"] = "\n#define USES_HALF_RES_COLOR\n"; actions.usage_defines["QUARTER_RES_COLOR"] = "\n#define USES_QUARTER_RES_COLOR\n"; actions.render_mode_defines["disable_fog"] = "#define DISABLE_FOG\n"; + actions.render_mode_defines["use_debanding"] = "#define USE_DEBANDING\n"; actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; @@ -1675,25 +1731,25 @@ ShaderCompiler::DefaultIdentifierActions actions; MaterialStorage::~MaterialStorage() { //shaders.copy.version_free(shaders.copy_version); - memdelete_arr(global_variables.buffer_values); - memdelete_arr(global_variables.buffer_usage); - memdelete_arr(global_variables.buffer_dirty_regions); - glDeleteBuffers(1, &global_variables.buffer); + memdelete_arr(global_shader_uniforms.buffer_values); + memdelete_arr(global_shader_uniforms.buffer_usage); + memdelete_arr(global_shader_uniforms.buffer_dirty_regions); + glDeleteBuffers(1, &global_shader_uniforms.buffer); singleton = nullptr; } -/* GLOBAL VARIABLE API */ +/* GLOBAL SHADER UNIFORM API */ -int32_t MaterialStorage::_global_variable_allocate(uint32_t p_elements) { +int32_t MaterialStorage::_global_shader_uniform_allocate(uint32_t p_elements) { int32_t idx = 0; - while (idx + p_elements <= global_variables.buffer_size) { - if (global_variables.buffer_usage[idx].elements == 0) { + while (idx + p_elements <= global_shader_uniforms.buffer_size) { + if (global_shader_uniforms.buffer_usage[idx].elements == 0) { bool valid = true; for (uint32_t i = 1; i < p_elements; i++) { - if (global_variables.buffer_usage[idx + i].elements > 0) { + if (global_shader_uniforms.buffer_usage[idx + i].elements > 0) { valid = false; - idx += i + global_variables.buffer_usage[idx + i].elements; + idx += i + global_shader_uniforms.buffer_usage[idx + i].elements; break; } } @@ -1704,17 +1760,17 @@ int32_t MaterialStorage::_global_variable_allocate(uint32_t p_elements) { return idx; } else { - idx += global_variables.buffer_usage[idx].elements; + idx += global_shader_uniforms.buffer_usage[idx].elements; } } return -1; } -void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::GlobalVariableType p_type, const Variant &p_value) { +void MaterialStorage::_global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderParameterType p_type, const Variant &p_value) { switch (p_type) { case RS::GLOBAL_VAR_TYPE_BOOL: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; bool b = p_value; bv.x = b ? 1.0 : 0.0; bv.y = 0.0; @@ -1723,7 +1779,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_BVEC2: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; @@ -1731,7 +1787,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0.0; } break; case RS::GLOBAL_VAR_TYPE_BVEC3: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; @@ -1739,7 +1795,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0.0; } break; case RS::GLOBAL_VAR_TYPE_BVEC4: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; uint32_t bvec = p_value; bv.x = (bvec & 1) ? 1.0 : 0.0; bv.y = (bvec & 2) ? 1.0 : 0.0; @@ -1747,7 +1803,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = (bvec & 8) ? 1.0 : 0.0; } break; case RS::GLOBAL_VAR_TYPE_INT: { - GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; int32_t v = p_value; bv.x = v; bv.y = 0; @@ -1755,7 +1811,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC2: { - GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; Vector2i v = p_value; bv.x = v.x; bv.y = v.y; @@ -1763,7 +1819,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC3: { - GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; Vector3i v = p_value; bv.x = v.x; bv.y = v.y; @@ -1771,7 +1827,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_IVEC4: { - GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; Vector<int32_t> v = p_value; bv.x = v.size() >= 1 ? v[0] : 0; bv.y = v.size() >= 2 ? v[1] : 0; @@ -1779,7 +1835,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = v.size() >= 4 ? v[3] : 0; } break; case RS::GLOBAL_VAR_TYPE_RECT2I: { - GlobalVariables::ValueInt &bv = *(GlobalVariables::ValueInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; Rect2i v = p_value; bv.x = v.position.x; bv.y = v.position.y; @@ -1787,7 +1843,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = v.size.y; } break; case RS::GLOBAL_VAR_TYPE_UINT: { - GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; uint32_t v = p_value; bv.x = v; bv.y = 0; @@ -1795,7 +1851,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC2: { - GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; Vector2i v = p_value; bv.x = v.x; bv.y = v.y; @@ -1803,7 +1859,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC3: { - GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; Vector3i v = p_value; bv.x = v.x; bv.y = v.y; @@ -1811,7 +1867,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_UVEC4: { - GlobalVariables::ValueUInt &bv = *(GlobalVariables::ValueUInt *)&global_variables.buffer_values[p_index]; + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; Vector<int32_t> v = p_value; bv.x = v.size() >= 1 ? v[0] : 0; bv.y = v.size() >= 2 ? v[1] : 0; @@ -1819,7 +1875,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = v.size() >= 4 ? v[3] : 0; } break; case RS::GLOBAL_VAR_TYPE_FLOAT: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; float v = p_value; bv.x = v; bv.y = 0; @@ -1827,7 +1883,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC2: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; Vector2 v = p_value; bv.x = v.x; bv.y = v.y; @@ -1835,7 +1891,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC3: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; Vector3 v = p_value; bv.x = v.x; bv.y = v.y; @@ -1843,7 +1899,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = 0; } break; case RS::GLOBAL_VAR_TYPE_VEC4: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; Plane v = p_value; bv.x = v.normal.x; bv.y = v.normal.y; @@ -1851,14 +1907,14 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = v.d; } break; case RS::GLOBAL_VAR_TYPE_COLOR: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; Color v = p_value; bv.x = v.r; bv.y = v.g; bv.z = v.b; bv.w = v.a; - GlobalVariables::Value &bv_linear = global_variables.buffer_values[p_index + 1]; + GlobalShaderUniforms::Value &bv_linear = global_shader_uniforms.buffer_values[p_index + 1]; //v = v.srgb_to_linear(); bv_linear.x = v.r; bv_linear.y = v.g; @@ -1867,7 +1923,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_RECT2: { - GlobalVariables::Value &bv = global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; Rect2 v = p_value; bv.x = v.position.x; bv.y = v.position.y; @@ -1875,7 +1931,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob bv.w = v.size.y; } break; case RS::GLOBAL_VAR_TYPE_MAT2: { - GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; Vector<float> m2 = p_value; if (m2.size() < 4) { m2.resize(4); @@ -1892,7 +1948,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_MAT3: { - GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; Basis v = p_value; bv[0].x = v.rows[0][0]; bv[0].y = v.rows[1][0]; @@ -1911,7 +1967,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_MAT4: { - GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; Vector<float> m2 = p_value; if (m2.size() < 16) { @@ -1940,7 +1996,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { - GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; Transform2D v = p_value; bv[0].x = v.columns[0][0]; bv[0].y = v.columns[0][1]; @@ -1959,7 +2015,7 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { - GlobalVariables::Value *bv = &global_variables.buffer_values[p_index]; + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; Transform3D v = p_value; bv[0].x = v.basis.rows[0][0]; bv[0].y = v.basis.rows[1][0]; @@ -1988,15 +2044,15 @@ void MaterialStorage::_global_variable_store_in_buffer(int32_t p_index, RS::Glob } } -void MaterialStorage::_global_variable_mark_buffer_dirty(int32_t p_index, int32_t p_elements) { +void MaterialStorage::_global_shader_uniform_mark_buffer_dirty(int32_t p_index, int32_t p_elements) { int32_t prev_chunk = -1; for (int32_t i = 0; i < p_elements; i++) { - int32_t chunk = (p_index + i) / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; + int32_t chunk = (p_index + i) / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE; if (chunk != prev_chunk) { - if (!global_variables.buffer_dirty_regions[chunk]) { - global_variables.buffer_dirty_regions[chunk] = true; - global_variables.buffer_dirty_region_count++; + if (!global_shader_uniforms.buffer_dirty_regions[chunk]) { + global_shader_uniforms.buffer_dirty_regions[chunk] = true; + global_shader_uniforms.buffer_dirty_region_count++; } } @@ -2004,16 +2060,16 @@ void MaterialStorage::_global_variable_mark_buffer_dirty(int32_t p_index, int32_ } } -void MaterialStorage::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { - ERR_FAIL_COND(global_variables.variables.has(p_name)); - GlobalVariables::Variable gv; +void MaterialStorage::global_shader_parameter_add(const StringName &p_name, RS::GlobalShaderParameterType p_type, const Variant &p_value) { + ERR_FAIL_COND(global_shader_uniforms.variables.has(p_name)); + GlobalShaderUniforms::Variable gv; gv.type = p_type; gv.value = p_value; gv.buffer_index = -1; if (p_type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { //is texture - global_variables.must_update_texture_materials = true; //normally there are none + global_shader_uniforms.must_update_texture_materials = true; //normally there are none } else { gv.buffer_elements = 1; if (p_type == RS::GLOBAL_VAR_TYPE_COLOR || p_type == RS::GLOBAL_VAR_TYPE_MAT2) { @@ -2030,56 +2086,56 @@ void MaterialStorage::global_variable_add(const StringName &p_name, RS::GlobalVa } //is vector, allocate in buffer and update index - gv.buffer_index = _global_variable_allocate(gv.buffer_elements); + gv.buffer_index = _global_shader_uniform_allocate(gv.buffer_elements); ERR_FAIL_COND_MSG(gv.buffer_index < 0, vformat("Failed allocating global variable '%s' out of buffer memory. Consider increasing it in the Project Settings.", String(p_name))); - global_variables.buffer_usage[gv.buffer_index].elements = gv.buffer_elements; - _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); - _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + global_shader_uniforms.buffer_usage[gv.buffer_index].elements = gv.buffer_elements; + _global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value); + _global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); - global_variables.must_update_buffer_materials = true; //normally there are none + global_shader_uniforms.must_update_buffer_materials = true; //normally there are none } - global_variables.variables[p_name] = gv; + global_shader_uniforms.variables[p_name] = gv; } -void MaterialStorage::global_variable_remove(const StringName &p_name) { - if (!global_variables.variables.has(p_name)) { +void MaterialStorage::global_shader_parameter_remove(const StringName &p_name) { + if (!global_shader_uniforms.variables.has(p_name)) { return; } - GlobalVariables::Variable &gv = global_variables.variables[p_name]; + GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name]; if (gv.buffer_index >= 0) { - global_variables.buffer_usage[gv.buffer_index].elements = 0; - global_variables.must_update_buffer_materials = true; + global_shader_uniforms.buffer_usage[gv.buffer_index].elements = 0; + global_shader_uniforms.must_update_buffer_materials = true; } else { - global_variables.must_update_texture_materials = true; + global_shader_uniforms.must_update_texture_materials = true; } - global_variables.variables.erase(p_name); + global_shader_uniforms.variables.erase(p_name); } -Vector<StringName> MaterialStorage::global_variable_get_list() const { +Vector<StringName> MaterialStorage::global_shader_parameter_get_list() const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(Vector<StringName>(), "This function should never be used outside the editor, it can severely damage performance."); } Vector<StringName> names; - for (const KeyValue<StringName, GlobalVariables::Variable> &E : global_variables.variables) { + for (const KeyValue<StringName, GlobalShaderUniforms::Variable> &E : global_shader_uniforms.variables) { names.push_back(E.key); } names.sort_custom<StringName::AlphCompare>(); return names; } -void MaterialStorage::global_variable_set(const StringName &p_name, const Variant &p_value) { - ERR_FAIL_COND(!global_variables.variables.has(p_name)); - GlobalVariables::Variable &gv = global_variables.variables[p_name]; +void MaterialStorage::global_shader_parameter_set(const StringName &p_name, const Variant &p_value) { + ERR_FAIL_COND(!global_shader_uniforms.variables.has(p_name)); + GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name]; gv.value = p_value; if (gv.override.get_type() == Variant::NIL) { if (gv.buffer_index >= 0) { //buffer - _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); - _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + _global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value); + _global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); } else { //texture MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -2092,26 +2148,26 @@ void MaterialStorage::global_variable_set(const StringName &p_name, const Varian } } -void MaterialStorage::global_variable_set_override(const StringName &p_name, const Variant &p_value) { - if (!global_variables.variables.has(p_name)) { +void MaterialStorage::global_shader_parameter_set_override(const StringName &p_name, const Variant &p_value) { + if (!global_shader_uniforms.variables.has(p_name)) { return; //variable may not exist } ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT); - GlobalVariables::Variable &gv = global_variables.variables[p_name]; + GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name]; gv.override = p_value; if (gv.buffer_index >= 0) { //buffer if (gv.override.get_type() == Variant::NIL) { - _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.value); + _global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value); } else { - _global_variable_store_in_buffer(gv.buffer_index, gv.type, gv.override); + _global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.override); } - _global_variable_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + _global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); } else { //texture MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -2123,42 +2179,42 @@ void MaterialStorage::global_variable_set_override(const StringName &p_name, con } } -Variant MaterialStorage::global_variable_get(const StringName &p_name) const { +Variant MaterialStorage::global_shader_parameter_get(const StringName &p_name) const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(Variant(), "This function should never be used outside the editor, it can severely damage performance."); } - if (!global_variables.variables.has(p_name)) { + if (!global_shader_uniforms.variables.has(p_name)) { return Variant(); } - return global_variables.variables[p_name].value; + return global_shader_uniforms.variables[p_name].value; } -RS::GlobalVariableType MaterialStorage::global_variable_get_type_internal(const StringName &p_name) const { - if (!global_variables.variables.has(p_name)) { +RS::GlobalShaderParameterType MaterialStorage::global_shader_parameter_get_type_internal(const StringName &p_name) const { + if (!global_shader_uniforms.variables.has(p_name)) { return RS::GLOBAL_VAR_TYPE_MAX; } - return global_variables.variables[p_name].type; + return global_shader_uniforms.variables[p_name].type; } -RS::GlobalVariableType MaterialStorage::global_variable_get_type(const StringName &p_name) const { +RS::GlobalShaderParameterType MaterialStorage::global_shader_parameter_get_type(const StringName &p_name) const { if (!Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_V_MSG(RS::GLOBAL_VAR_TYPE_MAX, "This function should never be used outside the editor, it can severely damage performance."); } - return global_variable_get_type_internal(p_name); + return global_shader_parameter_get_type_internal(p_name); } -void MaterialStorage::global_variables_load_settings(bool p_load_textures) { +void MaterialStorage::global_shader_parameters_load_settings(bool p_load_textures) { List<PropertyInfo> settings; ProjectSettings::get_singleton()->get_property_list(&settings); for (const PropertyInfo &E : settings) { if (E.name.begins_with("shader_globals/")) { StringName name = E.name.get_slice("/", 1); - Dictionary d = ProjectSettings::get_singleton()->get(E.name); + Dictionary d = GLOBAL_GET(E.name); ERR_CONTINUE(!d.has("type")); ERR_CONTINUE(!d.has("value")); @@ -2196,11 +2252,11 @@ void MaterialStorage::global_variables_load_settings(bool p_load_textures) { "samplerCube", }; - RS::GlobalVariableType gvtype = RS::GLOBAL_VAR_TYPE_MAX; + RS::GlobalShaderParameterType gvtype = RS::GLOBAL_VAR_TYPE_MAX; for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) { if (global_var_type_names[i] == type) { - gvtype = RS::GlobalVariableType(i); + gvtype = RS::GlobalShaderParameterType(i); break; } } @@ -2222,53 +2278,55 @@ void MaterialStorage::global_variables_load_settings(bool p_load_textures) { value = resource; } - if (global_variables.variables.has(name)) { + if (global_shader_uniforms.variables.has(name)) { //has it, update it - global_variable_set(name, value); + global_shader_parameter_set(name, value); } else { - global_variable_add(name, gvtype, value); + global_shader_parameter_add(name, gvtype, value); } } } } -void MaterialStorage::global_variables_clear() { - global_variables.variables.clear(); +void MaterialStorage::global_shader_parameters_clear() { + global_shader_uniforms.variables.clear(); } -GLuint MaterialStorage::global_variables_get_uniform_buffer() const { - return global_variables.buffer; +GLuint MaterialStorage::global_shader_parameters_get_uniform_buffer() const { + return global_shader_uniforms.buffer; } -int32_t MaterialStorage::global_variables_instance_allocate(RID p_instance) { - ERR_FAIL_COND_V(global_variables.instance_buffer_pos.has(p_instance), -1); - int32_t pos = _global_variable_allocate(ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); - global_variables.instance_buffer_pos[p_instance] = pos; //save anyway +int32_t MaterialStorage::global_shader_parameters_instance_allocate(RID p_instance) { + ERR_FAIL_COND_V(global_shader_uniforms.instance_buffer_pos.has(p_instance), -1); + int32_t pos = _global_shader_uniform_allocate(ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); + global_shader_uniforms.instance_buffer_pos[p_instance] = pos; //save anyway ERR_FAIL_COND_V_MSG(pos < 0, -1, "Too many instances using shader instance variables. Increase buffer size in Project Settings."); - global_variables.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES; + global_shader_uniforms.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES; return pos; } -void MaterialStorage::global_variables_instance_free(RID p_instance) { - ERR_FAIL_COND(!global_variables.instance_buffer_pos.has(p_instance)); - int32_t pos = global_variables.instance_buffer_pos[p_instance]; +void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) { + ERR_FAIL_COND(!global_shader_uniforms.instance_buffer_pos.has(p_instance)); + int32_t pos = global_shader_uniforms.instance_buffer_pos[p_instance]; if (pos >= 0) { - global_variables.buffer_usage[pos].elements = 0; + global_shader_uniforms.buffer_usage[pos].elements = 0; } - global_variables.instance_buffer_pos.erase(p_instance); + global_shader_uniforms.instance_buffer_pos.erase(p_instance); } -void MaterialStorage::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { - if (!global_variables.instance_buffer_pos.has(p_instance)) { +void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) { + if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) { return; //just not allocated, ignore } - int32_t pos = global_variables.instance_buffer_pos[p_instance]; + int32_t pos = global_shader_uniforms.instance_buffer_pos[p_instance]; if (pos < 0) { return; //again, not allocated, ignore } ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); - ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + + Variant::Type value_type = p_value.get_type(); + ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { ShaderLanguage::TYPE_MAX, //nil @@ -2283,73 +2341,91 @@ void MaterialStorage::global_variables_instance_update(RID p_instance, int p_ind ShaderLanguage::TYPE_VEC3, // vec3 ShaderLanguage::TYPE_IVEC3, //vec3i ShaderLanguage::TYPE_MAX, //xform2d not supported here + ShaderLanguage::TYPE_VEC4, //vec4 + ShaderLanguage::TYPE_IVEC4, //vec4i ShaderLanguage::TYPE_VEC4, //plane ShaderLanguage::TYPE_VEC4, //quat ShaderLanguage::TYPE_MAX, //aabb not supported here ShaderLanguage::TYPE_MAX, //basis not supported here ShaderLanguage::TYPE_MAX, //xform not supported here + ShaderLanguage::TYPE_MAX, //projection not supported here ShaderLanguage::TYPE_VEC4 //color }; - ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()]; + ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX; + if (value_type == Variant::INT && p_flags_count > 0) { + switch (p_flags_count) { + case 1: + datatype = ShaderLanguage::TYPE_BVEC2; + break; + case 2: + datatype = ShaderLanguage::TYPE_BVEC3; + break; + case 3: + datatype = ShaderLanguage::TYPE_BVEC4; + break; + } + } else { + datatype = datatype_from_value[value_type]; + } - ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported pos += p_index; - _fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_variables.buffer_values[pos]); - _global_variable_mark_buffer_dirty(pos, 1); + _fill_std140_variant_ubo_value(datatype, 0, p_value, (uint8_t *)&global_shader_uniforms.buffer_values[pos]); + _global_shader_uniform_mark_buffer_dirty(pos, 1); } -void MaterialStorage::_update_global_variables() { +void MaterialStorage::_update_global_shader_uniforms() { MaterialStorage *material_storage = MaterialStorage::get_singleton(); - if (global_variables.buffer_dirty_region_count > 0) { - uint32_t total_regions = global_variables.buffer_size / GlobalVariables::BUFFER_DIRTY_REGION_SIZE; - if (total_regions / global_variables.buffer_dirty_region_count <= 4) { + if (global_shader_uniforms.buffer_dirty_region_count > 0) { + uint32_t total_regions = global_shader_uniforms.buffer_size / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE; + if (total_regions / global_shader_uniforms.buffer_dirty_region_count <= 4) { // 25% of regions dirty, just update all buffer - glBindBuffer(GL_UNIFORM_BUFFER, global_variables.buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalVariables::Value) * global_variables.buffer_size, global_variables.buffer_values, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(GlobalShaderUniforms::Value) * global_shader_uniforms.buffer_size, global_shader_uniforms.buffer_values, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); - memset(global_variables.buffer_dirty_regions, 0, sizeof(bool) * total_regions); + memset(global_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * total_regions); } else { - uint32_t region_byte_size = sizeof(GlobalVariables::Value) * GlobalVariables::BUFFER_DIRTY_REGION_SIZE; - glBindBuffer(GL_UNIFORM_BUFFER, global_variables.buffer); + uint32_t region_byte_size = sizeof(GlobalShaderUniforms::Value) * GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE; + glBindBuffer(GL_UNIFORM_BUFFER, global_shader_uniforms.buffer); for (uint32_t i = 0; i < total_regions; i++) { - if (global_variables.buffer_dirty_regions[i]) { - glBufferSubData(GL_UNIFORM_BUFFER, i * region_byte_size, region_byte_size, &global_variables.buffer_values[i * GlobalVariables::BUFFER_DIRTY_REGION_SIZE]); - global_variables.buffer_dirty_regions[i] = false; + if (global_shader_uniforms.buffer_dirty_regions[i]) { + glBufferSubData(GL_UNIFORM_BUFFER, i * region_byte_size, region_byte_size, &global_shader_uniforms.buffer_values[i * GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE]); + global_shader_uniforms.buffer_dirty_regions[i] = false; } } glBindBuffer(GL_UNIFORM_BUFFER, 0); } - global_variables.buffer_dirty_region_count = 0; + global_shader_uniforms.buffer_dirty_region_count = 0; } - if (global_variables.must_update_buffer_materials) { + if (global_shader_uniforms.must_update_buffer_materials) { // only happens in the case of a buffer variable added or removed, // so not often. - for (const RID &E : global_variables.materials_using_buffer) { + for (const RID &E : global_shader_uniforms.materials_using_buffer) { Material *material = material_storage->get_material(E); ERR_CONTINUE(!material); //wtf material_storage->_material_queue_update(material, true, false); } - global_variables.must_update_buffer_materials = false; + global_shader_uniforms.must_update_buffer_materials = false; } - if (global_variables.must_update_texture_materials) { + if (global_shader_uniforms.must_update_texture_materials) { // only happens in the case of a buffer variable added or removed, // so not often. - for (const RID &E : global_variables.materials_using_texture) { + for (const RID &E : global_shader_uniforms.materials_using_texture) { Material *material = material_storage->get_material(E); ERR_CONTINUE(!material); //wtf material_storage->_material_queue_update(material, false, true); } - global_variables.must_update_texture_materials = false; + global_shader_uniforms.must_update_texture_materials = false; } } @@ -2444,7 +2520,7 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { if (shader->data) { for (const KeyValue<StringName, HashMap<int, RID>> &E : shader->default_texture_parameter) { for (const KeyValue<int, RID> &E2 : E.value) { - shader->data->set_default_texture_param(E.key, E2.value, E2.key); + shader->data->set_default_texture_parameter(E.key, E2.value, E2.key); } } } @@ -2456,26 +2532,33 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { for (Material *E : shader->owners) { Material *material = E; - material->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MATERIAL); + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); _material_queue_update(material, true, true); } } +void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) { + GLES3::Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + shader->path_hint = p_path; +} + String MaterialStorage::shader_get_code(RID p_shader) const { const GLES3::Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, String()); return shader->code; } -void MaterialStorage::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const { +void MaterialStorage::get_shader_parameter_list(RID p_shader, List<PropertyInfo> *p_param_list) const { GLES3::Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); if (shader->data) { - return shader->data->get_param_list(p_param_list); + return shader->data->get_shader_uniform_list(p_param_list); } } -void MaterialStorage::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) { +void MaterialStorage::shader_set_default_texture_parameter(RID p_shader, const StringName &p_name, RID p_texture, int p_index) { GLES3::Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND(!shader); @@ -2494,7 +2577,7 @@ void MaterialStorage::shader_set_default_texture_param(RID p_shader, const Strin } } if (shader->data) { - shader->data->set_default_texture_param(p_name, p_texture, p_index); + shader->data->set_default_texture_parameter(p_name, p_texture, p_index); } for (Material *E : shader->owners) { Material *material = E; @@ -2502,7 +2585,7 @@ void MaterialStorage::shader_set_default_texture_param(RID p_shader, const Strin } } -RID MaterialStorage::shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const { +RID MaterialStorage::shader_get_default_texture_parameter(RID p_shader, const StringName &p_name, int p_index) const { const GLES3::Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) { @@ -2512,7 +2595,7 @@ RID MaterialStorage::shader_get_default_texture_param(RID p_shader, const String return RID(); } -Variant MaterialStorage::shader_get_param_default(RID p_shader, const StringName &p_param) const { +Variant MaterialStorage::shader_get_parameter_default(RID p_shader, const StringName &p_param) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, Variant()); if (shader->data) { @@ -2593,7 +2676,7 @@ void MaterialStorage::material_set_shader(RID p_material, RID p_shader) { } if (p_shader.is_null()) { - material->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MATERIAL); + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); material->shader_id = 0; return; } @@ -2616,7 +2699,7 @@ void MaterialStorage::material_set_shader(RID p_material, RID p_shader) { material->data->set_next_pass(material->next_pass); material->data->set_render_priority(material->priority); //updating happens later - material->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MATERIAL); + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); _material_queue_update(material, true, true); } @@ -2632,7 +2715,7 @@ void MaterialStorage::material_set_param(RID p_material, const StringName &p_par } if (material->shader && material->shader->data) { //shader is valid - bool is_texture = material->shader->data->is_param_texture(p_param); + bool is_texture = material->shader->data->is_parameter_texture(p_param); _material_queue_update(material, !is_texture, is_texture); } else { _material_queue_update(material, true, true); @@ -2662,7 +2745,7 @@ void MaterialStorage::material_set_next_pass(RID p_material, RID p_next_material material->data->set_next_pass(p_next_material); } - material->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MATERIAL); + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); } void MaterialStorage::material_set_render_priority(RID p_material, int priority) { @@ -2715,7 +2798,7 @@ void MaterialStorage::material_get_instance_shader_parameters(RID p_material, Li } } -void MaterialStorage::material_update_dependency(RID p_material, RendererStorage::DependencyTracker *p_instance) { +void MaterialStorage::material_update_dependency(RID p_material, DependencyTracker *p_instance) { Material *material = material_owner.get_or_null(p_material); ERR_FAIL_COND(!material); p_instance->update_dependency(&material->dependency); @@ -2734,6 +2817,7 @@ void CanvasShaderData::set_code(const String &p_code) { ubo_size = 0; uniforms.clear(); uses_screen_texture = false; + uses_screen_texture_mipmaps = false; uses_sdf = false; uses_time = false; @@ -2744,7 +2828,6 @@ void CanvasShaderData::set_code(const String &p_code) { ShaderCompiler::GeneratedCode gen_code; int blend_modei = BLEND_MODE_MIX; - uses_screen_texture = false; ShaderCompiler::IdentifierActions actions; actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; @@ -2771,6 +2854,7 @@ void CanvasShaderData::set_code(const String &p_code) { } blend_mode = BlendMode(blend_modei); + uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; #if 0 print_line("**compiling shader:"); @@ -2778,12 +2862,16 @@ void CanvasShaderData::set_code(const String &p_code) { for (int i = 0; i < gen_code.defines.size(); i++) { print_line(gen_code.defines[i]); } + + HashMap<String, String>::Iterator el = gen_code.code.begin(); + while (el) { + print_line("\n**code " + el->key + ":\n" + el->value); + ++el; + } + print_line("\n**uniforms:\n" + gen_code.uniforms); - print_line("\n**vertex_globals:\n" + gen_code.vertex_global); - print_line("\n**vertex_code:\n" + gen_code.vertex); - print_line("\n**fragment_globals:\n" + gen_code.fragment_global); - print_line("\n**fragment_code:\n" + gen_code.fragment); - print_line("\n**light_code:\n" + gen_code.light); + print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]); + print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]); #endif Vector<StringName> texture_uniform_names; @@ -2801,7 +2889,7 @@ void CanvasShaderData::set_code(const String &p_code) { valid = true; } -void CanvasShaderData::set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) { +void CanvasShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { if (!p_texture.is_valid()) { if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { default_texture_params[p_name].erase(p_index); @@ -2818,11 +2906,14 @@ void CanvasShaderData::set_default_texture_param(const StringName &p_name, RID p } } -void CanvasShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { +void CanvasShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { HashMap<int, StringName> order; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { continue; } if (E.value.texture_order >= 0) { @@ -2832,7 +2923,22 @@ void CanvasShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { } } + String last_group; for (const KeyValue<int, StringName> &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); @@ -2854,7 +2960,7 @@ void CanvasShaderData::get_instance_param_list(List<RendererMaterialStorage::Ins } } -bool CanvasShaderData::is_param_texture(const StringName &p_param) const { +bool CanvasShaderData::is_parameter_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; } @@ -3000,12 +3106,16 @@ void SkyShaderData::set_code(const String &p_code) { for (int i = 0; i < gen_code.defines.size(); i++) { print_line(gen_code.defines[i]); } + + HashMap<String, String>::Iterator el = gen_code.code.begin(); + while (el) { + print_line("\n**code " + el->key + ":\n" + el->value); + ++el; + } + print_line("\n**uniforms:\n" + gen_code.uniforms); - // print_line("\n**vertex_globals:\n" + gen_code.vertex_global); - // print_line("\n**vertex_code:\n" + gen_code.vertex); - print_line("\n**fragment_globals:\n" + gen_code.fragment_global); - print_line("\n**fragment_code:\n" + gen_code.fragment); - print_line("\n**light_code:\n" + gen_code.light); + print_line("\n**vertex_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX]); + print_line("\n**fragment_globals:\n" + gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT]); #endif Vector<StringName> texture_uniform_names; @@ -3023,7 +3133,7 @@ void SkyShaderData::set_code(const String &p_code) { valid = true; } -void SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) { +void SkyShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { if (!p_texture.is_valid()) { if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { default_texture_params[p_name].erase(p_index); @@ -3040,7 +3150,7 @@ void SkyShaderData::set_default_texture_param(const StringName &p_name, RID p_te } } -void SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { +void SkyShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { RBMap<int, StringName> order; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { @@ -3055,7 +3165,22 @@ void SkyShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { } } + String last_group; for (const KeyValue<int, StringName> &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); @@ -3077,7 +3202,7 @@ void SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::Instan } } -bool SkyShaderData::is_param_texture(const StringName &p_param) const { +bool SkyShaderData::is_parameter_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; } @@ -3168,7 +3293,6 @@ void SceneShaderData::set_code(const String &p_code) { valid = false; ubo_size = 0; uniforms.clear(); - uses_screen_texture = false; if (code.is_empty()) { return; //just invalid, but no error @@ -3296,6 +3420,7 @@ void SceneShaderData::set_code(const String &p_code) { vertex_input_mask |= uses_custom3 << 8; vertex_input_mask |= uses_bones << 9; vertex_input_mask |= uses_weights << 10; + uses_screen_texture_mipmaps = gen_code.uses_screen_texture_mipmaps; #if 0 print_line("**compiling shader:"); @@ -3304,11 +3429,10 @@ void SceneShaderData::set_code(const String &p_code) { print_line(gen_code.defines[i]); } - Map<String, String>::Element *el = gen_code.code.front(); + HashMap<String, String>::Iterator el = gen_code.code.begin(); while (el) { - print_line("\n**code " + el->key() + ":\n" + el->value()); - - el = el->next(); + print_line("\n**code " + el->key + ":\n" + el->value); + ++el; } print_line("\n**uniforms:\n" + gen_code.uniforms); @@ -3336,7 +3460,7 @@ void SceneShaderData::set_code(const String &p_code) { valid = true; } -void SceneShaderData::set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) { +void SceneShaderData::set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) { if (!p_texture.is_valid()) { if (default_texture_params.has(p_name) && default_texture_params[p_name].has(p_index)) { default_texture_params[p_name].erase(p_index); @@ -3353,11 +3477,14 @@ void SceneShaderData::set_default_texture_param(const StringName &p_name, RID p_ } } -void SceneShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { +void SceneShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const { RBMap<int, StringName> order; for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { - if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE || + E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_DEPTH_TEXTURE) { continue; } @@ -3368,7 +3495,22 @@ void SceneShaderData::get_param_list(List<PropertyInfo> *p_param_list) const { } } + String last_group; for (const KeyValue<int, StringName> &E : order) { + String group = uniforms[E.value].group; + if (!uniforms[E.value].subgroup.is_empty()) { + group += "::" + uniforms[E.value].subgroup; + } + + if (group != last_group) { + PropertyInfo pi; + pi.usage = PROPERTY_USAGE_GROUP; + pi.name = group; + p_param_list->push_back(pi); + + last_group = group; + } + PropertyInfo pi = ShaderLanguage::uniform_to_property_info(uniforms[E.value]); pi.name = E.value; p_param_list->push_back(pi); @@ -3390,7 +3532,7 @@ void SceneShaderData::get_instance_param_list(List<RendererMaterialStorage::Inst } } -bool SceneShaderData::is_param_texture(const StringName &p_param) const { +bool SceneShaderData::is_parameter_texture(const StringName &p_param) const { if (!uniforms.has(p_param)) { return false; } diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 09f6680bec..24d9a0fee1 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -37,10 +37,10 @@ #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/renderer_compositor.h" -#include "servers/rendering/renderer_storage.h" #include "servers/rendering/shader_compiler.h" #include "servers/rendering/shader_language.h" #include "servers/rendering/storage/material_storage.h" +#include "servers/rendering/storage/utilities.h" #include "../shaders/canvas.glsl.gen.h" #include "../shaders/cubemap_filter.glsl.gen.h" @@ -53,11 +53,11 @@ namespace GLES3 { struct ShaderData { virtual void set_code(const String &p_Code) = 0; - virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) = 0; - virtual void get_param_list(List<PropertyInfo> *p_param_list) const = 0; + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index) = 0; + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const = 0; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const = 0; - virtual bool is_param_texture(const StringName &p_param) const = 0; + virtual bool is_parameter_texture(const StringName &p_param) const = 0; virtual bool is_animated() const = 0; virtual bool casts_shadows() const = 0; virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; @@ -73,6 +73,7 @@ struct Material; struct Shader { ShaderData *data = nullptr; String code; + String path_hint; RS::ShaderMode mode; HashMap<StringName, HashMap<int, RID>> default_texture_parameter; HashSet<Material *> owners; @@ -125,7 +126,7 @@ struct Material { RID next_pass; SelfList<Material> update_element; - RendererStorage::Dependency dependency; + Dependency dependency; Material() : update_element(this) {} @@ -141,6 +142,7 @@ struct CanvasShaderData : public ShaderData { BLEND_MODE_MUL, BLEND_MODE_PMALPHA, BLEND_MODE_DISABLED, + BLEND_MODE_LCD, }; bool valid; @@ -158,15 +160,16 @@ struct CanvasShaderData : public ShaderData { HashMap<StringName, HashMap<int, RID>> default_texture_params; bool uses_screen_texture = false; + bool uses_screen_texture_mipmaps = false; bool uses_sdf = false; bool uses_time = false; virtual void set_code(const String &p_Code); - virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); - virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; virtual Variant get_default_parameter(const StringName &p_parameter) const; @@ -213,10 +216,10 @@ struct SkyShaderData : public ShaderData { bool uses_light; virtual void set_code(const String &p_Code); - virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); - virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; virtual Variant get_default_parameter(const StringName &p_parameter) const; @@ -311,6 +314,7 @@ struct SceneShaderData : public ShaderData { bool uses_sss; bool uses_transmittance; bool uses_screen_texture; + bool uses_screen_texture_mipmaps; bool uses_depth_texture; bool uses_normal_texture; bool uses_time; @@ -333,11 +337,11 @@ struct SceneShaderData : public ShaderData { uint32_t index = 0; virtual void set_code(const String &p_Code); - virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); - virtual void get_param_list(List<PropertyInfo> *p_param_list) const; + virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index); + virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; - virtual bool is_param_texture(const StringName &p_param) const; + virtual bool is_parameter_texture(const StringName &p_param) const; virtual bool is_animated() const; virtual bool casts_shadows() const; virtual Variant get_default_parameter(const StringName &p_parameter) const; @@ -364,15 +368,15 @@ struct SceneMaterialData : public MaterialData { MaterialData *_create_scene_material_func(ShaderData *p_shader); -/* Global variable structs */ -struct GlobalVariables { +/* Global shader uniform structs */ +struct GlobalShaderUniforms { enum { BUFFER_DIRTY_REGION_SIZE = 1024 }; struct Variable { HashSet<RID> texture_materials; // materials using this - RS::GlobalVariableType type; + RS::GlobalShaderParameterType type; Variant value; Variant override; int32_t buffer_index; //for vectors @@ -428,13 +432,13 @@ private: friend struct MaterialData; static MaterialStorage *singleton; - /* GLOBAL VARIABLE API */ + /* GLOBAL SHADER UNIFORM API */ - GlobalVariables global_variables; + GlobalShaderUniforms global_shader_uniforms; - int32_t _global_variable_allocate(uint32_t p_elements); - void _global_variable_store_in_buffer(int32_t p_index, RS::GlobalVariableType p_type, const Variant &p_value); - void _global_variable_mark_buffer_dirty(int32_t p_index, int32_t p_elements); + int32_t _global_shader_uniform_allocate(uint32_t p_elements); + void _global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderParameterType p_type, const Variant &p_value); + void _global_shader_uniform_mark_buffer_dirty(int32_t p_index, int32_t p_elements); /* SHADER API */ @@ -453,6 +457,48 @@ public: MaterialStorage(); virtual ~MaterialStorage(); + static _FORCE_INLINE_ void store_transform(const Transform3D &p_mtx, float *p_array) { + p_array[0] = p_mtx.basis.rows[0][0]; + p_array[1] = p_mtx.basis.rows[1][0]; + p_array[2] = p_mtx.basis.rows[2][0]; + p_array[3] = 0; + p_array[4] = p_mtx.basis.rows[0][1]; + p_array[5] = p_mtx.basis.rows[1][1]; + p_array[6] = p_mtx.basis.rows[2][1]; + p_array[7] = 0; + p_array[8] = p_mtx.basis.rows[0][2]; + p_array[9] = p_mtx.basis.rows[1][2]; + p_array[10] = p_mtx.basis.rows[2][2]; + p_array[11] = 0; + p_array[12] = p_mtx.origin.x; + p_array[13] = p_mtx.origin.y; + p_array[14] = p_mtx.origin.z; + p_array[15] = 1; + } + + static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) { + p_array[0] = p_mtx.rows[0][0]; + p_array[1] = p_mtx.rows[1][0]; + p_array[2] = p_mtx.rows[2][0]; + p_array[3] = 0; + p_array[4] = p_mtx.rows[0][1]; + p_array[5] = p_mtx.rows[1][1]; + p_array[6] = p_mtx.rows[2][1]; + p_array[7] = 0; + p_array[8] = p_mtx.rows[0][2]; + p_array[9] = p_mtx.rows[1][2]; + p_array[10] = p_mtx.rows[2][2]; + p_array[11] = 0; + } + + static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + p_array[i * 4 + j] = p_mtx.columns[i][j]; + } + } + } + struct Shaders { CanvasShaderGLES3 canvas_shader; SkyShaderGLES3 sky_shader; @@ -465,28 +511,28 @@ public: ShaderCompiler compiler_sky; } shaders; - /* GLOBAL VARIABLE API */ + /* GLOBAL SHADER UNIFORM API */ - void _update_global_variables(); + void _update_global_shader_uniforms(); - virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override; - virtual void global_variable_remove(const StringName &p_name) override; - virtual Vector<StringName> global_variable_get_list() const override; + virtual void global_shader_parameter_add(const StringName &p_name, RS::GlobalShaderParameterType p_type, const Variant &p_value) override; + virtual void global_shader_parameter_remove(const StringName &p_name) override; + virtual Vector<StringName> global_shader_parameter_get_list() const override; - virtual void global_variable_set(const StringName &p_name, const Variant &p_value) override; - virtual void global_variable_set_override(const StringName &p_name, const Variant &p_value) override; - virtual Variant global_variable_get(const StringName &p_name) const override; - virtual RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override; - RS::GlobalVariableType global_variable_get_type_internal(const StringName &p_name) const; + virtual void global_shader_parameter_set(const StringName &p_name, const Variant &p_value) override; + virtual void global_shader_parameter_set_override(const StringName &p_name, const Variant &p_value) override; + virtual Variant global_shader_parameter_get(const StringName &p_name) const override; + virtual RS::GlobalShaderParameterType global_shader_parameter_get_type(const StringName &p_name) const override; + RS::GlobalShaderParameterType global_shader_parameter_get_type_internal(const StringName &p_name) const; - virtual void global_variables_load_settings(bool p_load_textures = true) override; - virtual void global_variables_clear() override; + virtual void global_shader_parameters_load_settings(bool p_load_textures = true) override; + virtual void global_shader_parameters_clear() override; - virtual int32_t global_variables_instance_allocate(RID p_instance) override; - virtual void global_variables_instance_free(RID p_instance) override; - virtual void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override; + virtual void global_shader_parameters_instance_free(RID p_instance) override; + virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override; - GLuint global_variables_get_uniform_buffer() const; + GLuint global_shader_parameters_get_uniform_buffer() const; /* SHADER API */ @@ -500,12 +546,13 @@ public: virtual void shader_free(RID p_rid) override; virtual void shader_set_code(RID p_shader, const String &p_code) override; + virtual void shader_set_path_hint(RID p_shader, const String &p_path) override; virtual String shader_get_code(RID p_shader) const override; - virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; + virtual void get_shader_parameter_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; - virtual void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override; - virtual RID shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const override; - virtual Variant shader_get_param_default(RID p_shader, const StringName &p_param) const override; + virtual void shader_set_default_texture_parameter(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override; + virtual RID shader_get_default_texture_parameter(RID p_shader, const StringName &p_name, int p_index) const override; + virtual Variant shader_get_parameter_default(RID p_shader, const StringName &p_name) const override; virtual RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override; @@ -534,7 +581,7 @@ public: virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override; - virtual void material_update_dependency(RID p_material, RendererStorage::DependencyTracker *p_instance) override; + virtual void material_update_dependency(RID p_material, DependencyTracker *p_instance) override; _FORCE_INLINE_ uint32_t material_get_shader_id(RID p_material) { Material *material = material_owner.get_or_null(p_material); @@ -555,4 +602,4 @@ public: #endif // GLES3_ENABLED -#endif // !MATERIAL_STORAGE_GLES3_H +#endif // MATERIAL_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 822be25337..9ec0fc0286 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -32,6 +32,7 @@ #include "mesh_storage.h" #include "material_storage.h" +#include "utilities.h" using namespace GLES3; @@ -63,6 +64,8 @@ void MeshStorage::mesh_free(RID p_rid) { mesh_clear(p_rid); mesh_set_shadow_mesh(p_rid, RID()); Mesh *mesh = mesh_owner.get_or_null(p_rid); + ERR_FAIL_COND(!mesh); + mesh->dependency.deleted_notify(p_rid); if (mesh->instances.size()) { ERR_PRINT("deleting mesh with active instances"); @@ -71,7 +74,7 @@ void MeshStorage::mesh_free(RID p_rid) { for (Mesh *E : mesh->shadow_owners) { Mesh *shadow_owner = E; shadow_owner->shadow_mesh = RID(); - shadow_owner->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); } } mesh_owner.free(p_rid); @@ -121,11 +124,11 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) } break; case RS::ARRAY_NORMAL: { - stride += sizeof(int32_t); + stride += sizeof(uint16_t) * 2; } break; case RS::ARRAY_TANGENT: { - stride += sizeof(int32_t); + stride += sizeof(uint16_t) * 2; } break; case RS::ARRAY_COLOR: { @@ -183,11 +186,13 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->format = p_surface.format; s->primitive = p_surface.primitive; - glGenBuffers(1, &s->vertex_buffer); - glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, p_surface.vertex_data.size(), p_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind - s->vertex_buffer_size = p_surface.vertex_data.size(); + if (p_surface.vertex_data.size()) { + glGenBuffers(1, &s->vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, p_surface.vertex_data.size(), p_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + s->vertex_buffer_size = p_surface.vertex_data.size(); + } if (p_surface.attribute_data.size()) { glGenBuffers(1, &s->attribute_buffer); @@ -211,7 +216,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) } if (p_surface.index_count) { - bool is_index_16 = p_surface.vertex_count <= 65536; + bool is_index_16 = p_surface.vertex_count <= 65536 && p_surface.vertex_count > 0; glGenBuffers(1, &s->index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_surface.index_data.size(), p_surface.index_data.ptr(), GL_STATIC_DRAW); @@ -230,10 +235,13 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind s->lods[i].edge_length = p_surface.lods[i].edge_length; s->lods[i].index_count = p_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4); + s->lods[i].index_buffer_size = p_surface.lods[i].index_data.size(); } } } + ERR_FAIL_COND_MSG(!p_surface.index_count && !p_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both"); + s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. @@ -251,7 +259,10 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); } for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { - mesh->bone_aabbs.write[i].merge_with(p_surface.bone_aabbs[i]); + const AABB &bone = p_surface.bone_aabbs[i]; + if (bone.has_volume()) { + mesh->bone_aabbs.write[i].merge_with(bone); + } } mesh->aabb.merge_with(p_surface.aabb); } @@ -266,12 +277,12 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) _mesh_instance_add_surface(mi, mesh, mesh->surface_count - 1); } - mesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); for (Mesh *E : mesh->shadow_owners) { Mesh *shadow_owner = E; shadow_owner->shadow_mesh = RID(); - shadow_owner->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); } mesh->material_cache.clear(); @@ -298,12 +309,48 @@ RS::BlendShapeMode MeshStorage::mesh_get_blend_shape_mode(RID p_mesh) const { } void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->vertex_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->attribute_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->attribute_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->skin_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->skin_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { @@ -312,7 +359,7 @@ void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_mat ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); mesh->surfaces[p_surface]->material = p_material; - mesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MATERIAL); + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); mesh->material_cache.clear(); } @@ -333,48 +380,16 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { RS::SurfaceData sd; sd.format = s.format; - { - Vector<uint8_t> ret; - ret.resize(s.vertex_buffer_size); - glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer); - -#if defined(__EMSCRIPTEN__) - { - uint8_t *w = ret.ptrw(); - glGetBufferSubData(GL_ARRAY_BUFFER, 0, s.vertex_buffer_size, w); - } -#else - void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, s.vertex_buffer_size, GL_MAP_READ_BIT); - ERR_FAIL_NULL_V(data, RS::SurfaceData()); - { - uint8_t *w = ret.ptrw(); - memcpy(w, data, s.vertex_buffer_size); - } - glUnmapBuffer(GL_ARRAY_BUFFER); -#endif - sd.vertex_data = ret; + if (s.vertex_buffer != 0) { + sd.vertex_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_buffer_size); } if (s.attribute_buffer != 0) { - Vector<uint8_t> ret; - ret.resize(s.attribute_buffer_size); - glBindBuffer(GL_ARRAY_BUFFER, s.attribute_buffer); + sd.attribute_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.attribute_buffer, s.attribute_buffer_size); + } -#if defined(__EMSCRIPTEN__) - { - uint8_t *w = ret.ptrw(); - glGetBufferSubData(GL_ARRAY_BUFFER, 0, s.attribute_buffer_size, w); - } -#else - void *data = glMapBufferRange(GL_ARRAY_BUFFER, 0, s.attribute_buffer_size, GL_MAP_READ_BIT); - ERR_FAIL_NULL_V(data, RS::SurfaceData()); - { - uint8_t *w = ret.ptrw(); - memcpy(w, data, s.attribute_buffer_size); - } - glUnmapBuffer(GL_ARRAY_BUFFER); -#endif - sd.attribute_data = ret; + if (s.skin_buffer != 0) { + sd.skin_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.skin_buffer, s.skin_buffer_size); } sd.vertex_count = s.vertex_count; @@ -382,33 +397,14 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { sd.primitive = s.primitive; if (sd.index_count) { - Vector<uint8_t> ret; - ret.resize(s.index_buffer_size); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s.index_buffer); - -#if defined(__EMSCRIPTEN__) - { - uint8_t *w = ret.ptrw(); - glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s.index_buffer_size, w); - } -#else - void *data = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, s.index_buffer_size, GL_MAP_READ_BIT); - ERR_FAIL_NULL_V(data, RS::SurfaceData()); - { - uint8_t *w = ret.ptrw(); - memcpy(w, data, s.index_buffer_size); - } - glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); -#endif - sd.index_data = ret; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + sd.index_data = Utilities::buffer_get_data(GL_ELEMENT_ARRAY_BUFFER, s.index_buffer, s.index_buffer_size); } sd.aabb = s.aabb; for (uint32_t i = 0; i < s.lod_count; i++) { RS::SurfaceData::LOD lod; lod.edge_length = s.lods[i].edge_length; - //lod.index_data = RD::get_singleton()->buffer_get_data(s.lods[i].index_buffer); + lod.index_data = Utilities::buffer_get_data(GL_ELEMENT_ARRAY_BUFFER, s.lods[i].index_buffer, s.lods[i].index_buffer_size); sd.lods.push_back(lod); } @@ -559,7 +555,7 @@ void MeshStorage::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { shadow_mesh->shadow_owners.insert(mesh); } - mesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); } void MeshStorage::mesh_clear(RID p_mesh) { @@ -594,6 +590,21 @@ void MeshStorage::mesh_clear(RID p_mesh) { glDeleteBuffers(1, &s.index_buffer); s.index_buffer = 0; } + + if (s.versions) { + memfree(s.versions); //reallocs, so free with memfree. + } + + if (s.lod_count) { + for (uint32_t j = 0; j < s.lod_count; j++) { + if (s.lods[j].index_buffer != 0) { + glDeleteBuffers(1, &s.lods[j].index_buffer); + s.lods[j].index_buffer = 0; + } + } + memdelete_arr(s.lods); + } + memdelete(mesh->surfaces[i]); } if (mesh->surfaces) { @@ -608,12 +619,12 @@ void MeshStorage::mesh_clear(RID p_mesh) { _mesh_instance_clear(mi); } mesh->has_bone_weights = false; - mesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); for (Mesh *E : mesh->shadow_owners) { Mesh *shadow_owner = E; shadow_owner->shadow_mesh = RID(); - shadow_owner->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); } } @@ -648,17 +659,16 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V } break; case RS::ARRAY_NORMAL: { attribs[i].offset = vertex_stride; - // Will need to change to accommodate octahedral compression - attribs[i].size = 4; - attribs[i].type = GL_UNSIGNED_INT_2_10_10_10_REV; - vertex_stride += sizeof(float); + attribs[i].size = 2; + attribs[i].type = GL_UNSIGNED_SHORT; + vertex_stride += sizeof(uint16_t) * 2; attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_TANGENT: { attribs[i].offset = vertex_stride; - attribs[i].size = 4; - attribs[i].type = GL_UNSIGNED_INT_2_10_10_10_REV; - vertex_stride += sizeof(float); + attribs[i].size = 2; + attribs[i].type = GL_UNSIGNED_SHORT; + vertex_stride += sizeof(uint16_t) * 2; attribs[i].normalized = GL_TRUE; } break; case RS::ARRAY_COLOR: { @@ -723,17 +733,6 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V for (int i = 0; i < RS::ARRAY_INDEX; i++) { if (!attribs[i].enabled) { glDisableVertexAttribArray(i); - if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) { - if (i == RS::ARRAY_COLOR) { - glVertexAttrib4f(i, 1, 1, 1, 1); - } else if (i == RS::ARRAY_TEX_UV) { - glVertexAttrib2f(i, 1, 1); - } else if (i == RS::ARRAY_BONES) { - glVertexAttrib4f(i, 1, 1, 1, 1); - } else if (i == RS::ARRAY_WEIGHTS) { - glVertexAttrib4f(i, 1, 1, 1, 1); - } - } continue; } if (i <= RS::ARRAY_TANGENT) { @@ -949,8 +948,8 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->uses_colors = p_use_colors; multimesh->color_offset_cache = p_transform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; multimesh->uses_custom_data = p_use_custom_data; - multimesh->custom_data_offset_cache = multimesh->color_offset_cache + (p_use_colors ? 4 : 0); - multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 4 : 0); + multimesh->custom_data_offset_cache = multimesh->color_offset_cache + (p_use_colors ? 2 : 0); + multimesh->stride_cache = multimesh->custom_data_offset_cache + (p_use_custom_data ? 2 : 0); multimesh->buffer_set = false; multimesh->data_cache = Vector<float>(); @@ -965,7 +964,7 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: glBindBuffer(GL_ARRAY_BUFFER, 0); } - multimesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MULTIMESH); + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { @@ -977,7 +976,7 @@ int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); - if (multimesh->mesh == p_mesh) { + if (multimesh->mesh == p_mesh || p_mesh.is_null()) { return; } multimesh->mesh = p_mesh; @@ -990,23 +989,22 @@ void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { //we have a data cache, just mark it dirty _multimesh_mark_all_dirty(multimesh, false, true); } else if (multimesh->instances) { - //need to re-create AABB unfortunately, calling this has a penalty + // Need to re-create AABB. Unfortunately, calling this has a penalty. if (multimesh->buffer_set) { - // TODO add a function to RasterizerStorage to get data from a buffer - //Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); - //const uint8_t *r = buffer.ptr(); - //const float *data = (const float *)r; - //_multimesh_re_create_aabb(multimesh, data, multimesh->instances); + Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); + const uint8_t *r = buffer.ptr(); + const float *data = (const float *)r; + _multimesh_re_create_aabb(multimesh, data, multimesh->instances); } } - multimesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MESH); + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); } #define MULTIMESH_DIRTY_REGION_SIZE 512 void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { - if (multimesh->data_cache.size() > 0) { + if (multimesh->data_cache.size() > 0 || multimesh->instances == 0) { return; //already local } ERR_FAIL_COND(multimesh->data_cache.size() > 0); @@ -1017,10 +1015,11 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { float *w = multimesh->data_cache.ptrw(); if (multimesh->buffer_set) { - //Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); + Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); + { - // const uint8_t *r = buffer.ptr(); - // memcpy(w, r, buffer.size()); + const uint8_t *r = buffer.ptr(); + memcpy(w, r, buffer.size()); } } else { memset(w, 0, (size_t)multimesh->instances * multimesh->stride_cache * sizeof(float)); @@ -1186,14 +1185,12 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con _multimesh_make_local(multimesh); { + // Colors are packed into 2 floats. float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache + multimesh->color_offset_cache; - - dataptr[0] = p_color.r; - dataptr[1] = p_color.g; - dataptr[2] = p_color.b; - dataptr[3] = p_color.a; + uint16_t val[4] = { Math::make_half_float(p_color.r), Math::make_half_float(p_color.g), Math::make_half_float(p_color.b), Math::make_half_float(p_color.a) }; + memcpy(dataptr, val, 2 * 4); } _multimesh_mark_dirty(multimesh, p_index, false); @@ -1211,11 +1208,8 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde float *w = multimesh->data_cache.ptrw(); float *dataptr = w + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; - - dataptr[0] = p_color.r; - dataptr[1] = p_color.g; - dataptr[2] = p_color.b; - dataptr[3] = p_color.a; + uint16_t val[4] = { Math::make_half_float(p_color.r), Math::make_half_float(p_color.g), Math::make_half_float(p_color.b), Math::make_half_float(p_color.a) }; + memcpy(dataptr, val, 2 * 4); } _multimesh_mark_dirty(multimesh, p_index, false); @@ -1306,11 +1300,12 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->color_offset_cache; - - c.r = dataptr[0]; - c.g = dataptr[1]; - c.b = dataptr[2]; - c.a = dataptr[3]; + uint16_t raw_data[4]; + memcpy(raw_data, dataptr, 2 * 4); + c.r = Math::half_to_float(raw_data[0]); + c.g = Math::half_to_float(raw_data[1]); + c.b = Math::half_to_float(raw_data[2]); + c.a = Math::half_to_float(raw_data[3]); } return c; @@ -1329,11 +1324,12 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind const float *r = multimesh->data_cache.ptr(); const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; - - c.r = dataptr[0]; - c.g = dataptr[1]; - c.b = dataptr[2]; - c.a = dataptr[3]; + uint16_t raw_data[4]; + memcpy(raw_data, dataptr, 2 * 4); + c.r = Math::half_to_float(raw_data[0]); + c.g = Math::half_to_float(raw_data[1]); + c.b = Math::half_to_float(raw_data[2]); + c.a = Math::half_to_float(raw_data[3]); } return c; @@ -1342,19 +1338,66 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND(!multimesh); - ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); - { + if (multimesh->uses_colors || multimesh->uses_custom_data) { + // Color and custom need to be packed so copy buffer to data_cache and pack. + + _multimesh_make_local(multimesh); + multimesh->data_cache = p_buffer; + + float *w = multimesh->data_cache.ptrw(); + uint32_t old_stride = multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; + old_stride += multimesh->uses_colors ? 4 : 0; + old_stride += multimesh->uses_custom_data ? 4 : 0; + for (int i = 0; i < multimesh->instances; i++) { + { + float *dataptr = w + i * old_stride; + float *newptr = w + i * multimesh->stride_cache; + float vals[8] = { dataptr[0], dataptr[1], dataptr[2], dataptr[3], dataptr[4], dataptr[5], dataptr[6], dataptr[7] }; + memcpy(newptr, vals, 8 * 4); + } + + if (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_3D) { + float *dataptr = w + i * old_stride + 8; + float *newptr = w + i * multimesh->stride_cache + 8; + float vals[8] = { dataptr[0], dataptr[1], dataptr[2], dataptr[3] }; + memcpy(newptr, vals, 4 * 4); + } + + if (multimesh->uses_colors) { + float *dataptr = w + i * old_stride + (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12); + float *newptr = w + i * multimesh->stride_cache + multimesh->color_offset_cache; + uint16_t val[4] = { Math::make_half_float(dataptr[0]), Math::make_half_float(dataptr[1]), Math::make_half_float(dataptr[2]), Math::make_half_float(dataptr[3]) }; + memcpy(newptr, val, 2 * 4); + } + if (multimesh->uses_custom_data) { + float *dataptr = w + i * old_stride + (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12) + (multimesh->uses_colors ? 4 : 0); + float *newptr = w + i * multimesh->stride_cache + multimesh->custom_data_offset_cache; + uint16_t val[4] = { Math::make_half_float(dataptr[0]), Math::make_half_float(dataptr[1]), Math::make_half_float(dataptr[2]), Math::make_half_float(dataptr[3]) }; + memcpy(newptr, val, 2 * 4); + } + } + + multimesh->data_cache.resize(multimesh->instances * (int)multimesh->stride_cache); + const float *r = multimesh->data_cache.ptr(); + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); + glBufferData(GL_ARRAY_BUFFER, multimesh->data_cache.size() * sizeof(float), r, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + } else { + // Only Transform is being used, so we can upload directly. + ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); const float *r = p_buffer.ptr(); glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); glBufferData(GL_ARRAY_BUFFER, p_buffer.size() * sizeof(float), r, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); - multimesh->buffer_set = true; } - if (multimesh->data_cache.size()) { + multimesh->buffer_set = true; + + if (multimesh->data_cache.size() || multimesh->uses_colors || multimesh->uses_custom_data) { //if we have a data cache, just update it - multimesh->data_cache = p_buffer; + multimesh->data_cache = multimesh->data_cache; { //clear dirty since nothing will be dirty anymore uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; @@ -1370,29 +1413,78 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b const float *data = p_buffer.ptr(); _multimesh_re_create_aabb(multimesh, data, multimesh->instances); - multimesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_AABB); + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } } Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, Vector<float>()); - if (multimesh->buffer == 0) { + Vector<float> ret; + if (multimesh->buffer == 0 || multimesh->instances == 0) { return Vector<float>(); } else if (multimesh->data_cache.size()) { - return multimesh->data_cache; + ret = multimesh->data_cache; } else { - //get from memory + // Buffer not cached, so fetch from GPU memory. This can be a stalling operation, avoid whenever possible. - //Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); - Vector<float> ret; + Vector<uint8_t> buffer = Utilities::buffer_get_data(GL_ARRAY_BUFFER, multimesh->buffer, multimesh->instances * multimesh->stride_cache * sizeof(float)); ret.resize(multimesh->instances * multimesh->stride_cache); - //{ - // float *w = ret.ptrw(); - // const uint8_t *r = buffer.ptr(); - // memcpy(w, r, buffer.size()); - //} + { + float *w = ret.ptrw(); + const uint8_t *r = buffer.ptr(); + memcpy(w, r, buffer.size()); + } + } + if (multimesh->uses_colors || multimesh->uses_custom_data) { + // Need to decompress buffer. + uint32_t new_stride = multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12; + new_stride += multimesh->uses_colors ? 4 : 0; + new_stride += multimesh->uses_custom_data ? 4 : 0; + + Vector<float> decompressed; + decompressed.resize(multimesh->instances * (int)new_stride); + float *w = decompressed.ptrw(); + const float *r = ret.ptr(); + for (int i = 0; i < multimesh->instances; i++) { + { + float *newptr = w + i * new_stride; + const float *oldptr = r + i * multimesh->stride_cache; + float vals[8] = { oldptr[0], oldptr[1], oldptr[2], oldptr[3], oldptr[4], oldptr[5], oldptr[6], oldptr[7] }; + memcpy(newptr, vals, 8 * 4); + } + + if (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_3D) { + float *newptr = w + i * new_stride + 8; + const float *oldptr = r + i * multimesh->stride_cache + 8; + float vals[8] = { oldptr[0], oldptr[1], oldptr[2], oldptr[3] }; + memcpy(newptr, vals, 4 * 4); + } + + if (multimesh->uses_colors) { + float *newptr = w + i * new_stride + (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12); + const float *oldptr = r + i * multimesh->stride_cache + multimesh->color_offset_cache; + uint16_t raw_data[4]; + memcpy(raw_data, oldptr, 2 * 4); + newptr[0] = Math::half_to_float(raw_data[0]); + newptr[1] = Math::half_to_float(raw_data[1]); + newptr[2] = Math::half_to_float(raw_data[2]); + newptr[3] = Math::half_to_float(raw_data[3]); + } + if (multimesh->uses_custom_data) { + float *newptr = w + i * new_stride + (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_2D ? 8 : 12) + (multimesh->uses_colors ? 4 : 0); + const float *oldptr = r + i * multimesh->stride_cache + multimesh->custom_data_offset_cache; + uint16_t raw_data[4]; + memcpy(raw_data, oldptr, 2 * 4); + newptr[0] = Math::half_to_float(raw_data[0]); + newptr[1] = Math::half_to_float(raw_data[1]); + newptr[2] = Math::half_to_float(raw_data[2]); + newptr[3] = Math::half_to_float(raw_data[3]); + } + } + return decompressed; + } else { return ret; } } @@ -1412,7 +1504,7 @@ void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible multimesh->visible_instances = p_visible; - multimesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); } int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { @@ -1439,7 +1531,7 @@ void MeshStorage::_update_dirty_multimeshes() { if (multimesh->data_cache_used_dirty_regions > 32 || multimesh->data_cache_used_dirty_regions > visible_region_count / 2) { // If there too many dirty regions, or represent the majority of regions, just copy all, else transfer cost piles up too much glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); - glBufferData(GL_ARRAY_BUFFER, MIN(visible_region_count * region_size, multimesh->instances * (uint32_t)multimesh->stride_cache * (uint32_t)sizeof(float)), data, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, MIN(visible_region_count * region_size, multimesh->instances * multimesh->stride_cache * sizeof(float)), data, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } else { // Not that many regions? update them all @@ -1463,11 +1555,10 @@ void MeshStorage::_update_dirty_multimeshes() { multimesh->data_cache_used_dirty_regions = 0; } - if (multimesh->aabb_dirty) { - //aabb is dirty.. + if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) { _multimesh_re_create_aabb(multimesh, data, visible_instances); multimesh->aabb_dirty = false; - multimesh->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_AABB); + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); } } @@ -1516,7 +1607,12 @@ Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bo return Transform2D(); } -void MeshStorage::skeleton_update_dependency(RID p_base, RendererStorage::DependencyTracker *p_instance) { +void MeshStorage::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) { +} + +/* OCCLUDER */ + +void MeshStorage::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { } #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 068aa2fe40..a31db24f2d 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -37,6 +37,7 @@ #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" #include "servers/rendering/storage/mesh_storage.h" +#include "servers/rendering/storage/utilities.h" #include "platform_config.h" #ifndef OPENGL_INCLUDE_H @@ -74,7 +75,7 @@ struct Mesh { // Cache vertex arrays so they can be created struct Version { uint32_t input_mask = 0; - GLuint vertex_array; + GLuint vertex_array = 0; Attrib attribs[RS::ARRAY_MAX]; }; @@ -90,7 +91,8 @@ struct Mesh { struct LOD { float edge_length = 0.0; uint32_t index_count = 0; - GLuint index_buffer; + uint32_t index_buffer_size = 0; + GLuint index_buffer = 0; }; LOD *lods = nullptr; @@ -125,7 +127,7 @@ struct Mesh { RID shadow_mesh; HashSet<Mesh *> shadow_owners; - RendererStorage::Dependency dependency; + Dependency dependency; }; /* Mesh Instance */ @@ -173,12 +175,12 @@ struct MultiMesh { bool *data_cache_dirty_regions = nullptr; uint32_t data_cache_used_dirty_regions = 0; - GLuint buffer; + GLuint buffer = 0; bool dirty = false; MultiMesh *dirty_list = nullptr; - RendererStorage::Dependency dependency; + Dependency dependency; }; struct Skeleton { @@ -193,7 +195,7 @@ struct Skeleton { uint64_t version = 1; - RendererStorage::Dependency dependency; + Dependency dependency; }; class MeshStorage : public RendererMeshStorage { @@ -323,13 +325,12 @@ public: return s->index_count ? s->index_count : s->vertex_count; } - _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const { + _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); int32_t current_lod = -1; - if (r_index_count) { - *r_index_count = s->index_count; - } + r_index_count = s->index_count; + for (uint32_t i = 0; i < s->lod_count; i++) { float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold; if (screen_size > p_mesh_lod_threshold) { @@ -340,9 +341,7 @@ public: if (current_lod == -1) { return 0; } else { - if (r_index_count) { - *r_index_count = s->lods[current_lod].index_count; - } + r_index_count = s->lods[current_lod].index_count; return current_lod + 1; } } @@ -360,7 +359,7 @@ public: _FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); - return s->vertex_count <= 65536 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + return (s->vertex_count <= 65536 && s->vertex_count > 0) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; } // Use this to cache Vertex Array Objects so they are only generated once @@ -530,11 +529,15 @@ public: virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override; virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override; - virtual void skeleton_update_dependency(RID p_base, RendererStorage::DependencyTracker *p_instance) override; + virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override; + + /* OCCLUDER */ + + void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); }; } // namespace GLES3 #endif // GLES3_ENABLED -#endif // !MESH_STORAGE_GLES3_H +#endif // MESH_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h index cf47ada5d5..84d1f94d8c 100644 --- a/drivers/gles3/storage/particles_storage.h +++ b/drivers/gles3/storage/particles_storage.h @@ -137,4 +137,4 @@ public: #endif // GLES3_ENABLED -#endif // !PARTICLES_STORAGE_GLES3_H +#endif // PARTICLES_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp new file mode 100644 index 0000000000..e0e78de728 --- /dev/null +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* render_scene_buffers_gles3.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "render_scene_buffers_gles3.h" +#include "texture_storage.h" + +RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { + free_render_buffer_data(); +} + +void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + //internal_size.x = p_internal_size.x; // ignore for now + //internal_size.y = p_internal_size.y; + width = p_target_size.x; + height = p_target_size.y; + //fsr_sharpness = p_fsr_sharpness; + //texture_mipmap_bias = p_texture_mipmap_bias; + render_target = p_render_target; + //msaa = p_msaa; + //screen_space_aa = p_screen_space_aa; + //use_debanding = p_use_debanding; + view_count = p_view_count; + + free_render_buffer_data(); + + GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); + + is_transparent = rt->is_transparent; +} + +void RenderSceneBuffersGLES3::free_render_buffer_data() { +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h new file mode 100644 index 0000000000..092c14e1b8 --- /dev/null +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* render_scene_buffers_gles3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RENDER_SCENE_BUFFERS_GLES3_H +#define RENDER_SCENE_BUFFERS_GLES3_H + +#ifdef GLES3_ENABLED + +#include "servers/rendering/storage/render_scene_buffers.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +class RenderSceneBuffersGLES3 : public RenderSceneBuffers { + GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers); + +public: + // Original implementation, need to investigate which ones we'll keep like this and what we'll change... + + int internal_width = 0; + int internal_height = 0; + int width = 0; + int height = 0; + //float fsr_sharpness = 0.2f; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + //bool use_debanding = false; + uint32_t view_count = 1; + + bool is_transparent = false; + + RID render_target; + + //built-in textures used for ping pong image processing and blurring + struct Blur { + RID texture; + + struct Mipmap { + RID texture; + int width; + int height; + GLuint fbo; + }; + + Vector<Mipmap> mipmaps; + }; + + Blur blur[2]; //the second one starts from the first mipmap + +private: +public: + virtual ~RenderSceneBuffersGLES3(); + virtual void configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) override; + + virtual void set_fsr_sharpness(float p_fsr_sharpness) override{}; + virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{}; + virtual void set_use_debanding(bool p_use_debanding) override{}; + + void free_render_buffer_data(); +}; + +#endif // GLES3_ENABLED + +#endif // RENDER_SCENE_BUFFERS_GLES3_H diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 42c80da39a..51e22fe779 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -34,6 +34,10 @@ #include "config.h" #include "drivers/gles3/effects/copy_effects.h" +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#endif + using namespace GLES3; TextureStorage *TextureStorage::singleton = nullptr; @@ -59,9 +63,7 @@ TextureStorage::TextureStorage() { { //create default textures { // White Textures - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1, 1, 1, 1)); image->generate_mipmaps(); @@ -90,9 +92,7 @@ TextureStorage::TextureStorage() { } { // black - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 1)); image->generate_mipmaps(); @@ -115,10 +115,17 @@ TextureStorage::TextureStorage() { texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_BLACK], images, RS::TEXTURE_LAYERED_CUBEMAP); } + { // transparent black + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); + image->fill(Color(0, 0, 0, 0)); + image->generate_mipmaps(); + + default_gl_textures[DEFAULT_GL_TEXTURE_TRANSPARENT] = texture_allocate(); + texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_TRANSPARENT], image); + } + { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0.5, 0.5, 1, 1)); image->generate_mipmaps(); @@ -127,9 +134,7 @@ TextureStorage::TextureStorage() { } { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1.0, 0.5, 1, 1)); image->generate_mipmaps(); @@ -183,6 +188,33 @@ TextureStorage::TextureStorage() { texture.gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); } } + + glBindTexture(GL_TEXTURE_2D, 0); + + { // Atlas Texture initialize. + uint8_t pixel_data[4 * 4 * 4]; + for (int i = 0; i < 16; i++) { + pixel_data[i * 4 + 0] = 0; + pixel_data[i * 4 + 1] = 0; + pixel_data[i * 4 + 2] = 0; + pixel_data[i * 4 + 3] = 255; + } + + glGenTextures(1, &texture_atlas.texture); + glBindTexture(GL_TEXTURE_2D, texture_atlas.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data); + } + + glBindTexture(GL_TEXTURE_2D, 0); + + { + sdf_shader.shader.initialize(); + sdf_shader.shader_version = sdf_shader.shader.version_create(); + } + +#ifdef GLES_OVER_GL + glEnable(GL_PROGRAM_POINT_SIZE); +#endif } TextureStorage::~TextureStorage() { @@ -190,6 +222,12 @@ TextureStorage::~TextureStorage() { for (int i = 0; i < DEFAULT_GL_TEXTURE_MAX; i++) { texture_free(default_gl_textures[i]); } + + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; + sdf_shader.shader.version_free(sdf_shader.shader_version); } //TODO, move back to storage @@ -576,7 +614,9 @@ void TextureStorage::texture_free(RID p_texture) { } if (t->tex_id != 0) { - glDeleteTextures(1, &t->tex_id); + if (!t->is_external) { + glDeleteTextures(1, &t->tex_id); + } t->tex_id = 0; } @@ -587,7 +627,7 @@ void TextureStorage::texture_free(RID p_texture) { } } - //decal_atlas_remove_texture(p_texture); + texture_atlas_remove_texture(p_texture); for (int i = 0; i < t->proxies.size(); i++) { Texture *p = texture_owner.get_or_null(t->proxies[i]); @@ -600,6 +640,8 @@ void TextureStorage::texture_free(RID p_texture) { } void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) { + ERR_FAIL_COND(p_image.is_null()); + Texture texture; texture.width = p_image->get_width(); texture.height = p_image->get_height(); @@ -640,9 +682,37 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { texture_owner.initialize_rid(p_texture, proxy_tex); } +RID TextureStorage::texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) { + Texture texture; + texture.active = true; + texture.is_external = true; + texture.type = p_type; + + switch (p_type) { + case Texture::TYPE_2D: { + texture.target = GL_TEXTURE_2D; + } break; + case Texture::TYPE_3D: { + texture.target = GL_TEXTURE_3D; + } break; + case Texture::TYPE_LAYERED: { + texture.target = GL_TEXTURE_2D_ARRAY; + } break; + } + + texture.real_format = texture.format = p_format; + texture.tex_id = p_image; + texture.alloc_width = texture.width = p_width; + texture.alloc_height = texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.layered_type = p_layered_type; + + return texture_owner.make_rid(texture); +} + void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) { - // only 1 layer so far - texture_set_data(p_texture, p_image); + texture_set_data(p_texture, p_image, p_layer); #ifdef TOOLS_ENABLED Texture *tex = texture_owner.get_or_null(p_texture); @@ -651,14 +721,32 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, } void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!tex); + ERR_FAIL_COND(!tex->is_proxy); + Texture *proxy_to = texture_owner.get_or_null(p_proxy_to); + ERR_FAIL_COND(!proxy_to); + ERR_FAIL_COND(proxy_to->is_proxy); + + if (tex->proxy_to.is_valid()) { + Texture *prev_tex = texture_owner.get_or_null(tex->proxy_to); + ERR_FAIL_COND(!prev_tex); + prev_tex->proxies.erase(p_texture); + } + + *tex = *proxy_to; + + tex->proxy_to = p_proxy_to; + tex->is_render_target = false; + tex->is_proxy = true; + tex->proxies.clear(); + proxy_to->proxies.push_back(p_texture); } void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); @@ -667,9 +755,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -688,9 +774,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, Re void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -714,6 +798,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { #ifdef GLES_OVER_GL // OpenGL 3.3 supports glGetTexImage which is faster and simpler than glReadPixels. + // It also allows for reading compressed textures, mipmaps, and more formats. Vector<uint8_t> data; int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1); @@ -744,16 +829,71 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); + Ref<Image> image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (texture->format != texture->real_format) { image->convert(texture->format); } #else - // Support for Web and Mobile will come later. - Ref<Image> image; + + Vector<uint8_t> data; + + // On web and mobile we always read an RGBA8 image with no mipmaps. + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + uint8_t *w = data.ptrw(); + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->tex_id); + + glViewport(0, 0, texture->alloc_width, texture->alloc_height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + CopyEffects::get_singleton()->copy_to_rect(Rect2i(0, 0, 1.0, 1.0)); + + glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &w[0]); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &temp_color_texture); + glDeleteFramebuffers(1, &temp_framebuffer); + + data.resize(data_size); + + ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); + Ref<Image> image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data); + ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); + + if (texture->format != Image::FORMAT_RGBA8) { + image->convert(texture->format); + } + + if (texture->mipmaps > 1) { + image->generate_mipmaps(); + } + #endif #ifdef TOOLS_ENABLED @@ -807,7 +947,7 @@ void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) { //delete last, so proxies can be updated texture_owner.free(p_by_texture); - //decal_atlas_mark_dirty_on_texture(p_texture); + texture_atlas_mark_dirty_on_texture(p_texture); } void TextureStorage::texture_set_size_override(RID p_texture, int p_width, int p_height) { @@ -902,6 +1042,10 @@ Size2 TextureStorage::texture_size_with_proxy(RID p_texture) { } } +RID TextureStorage::texture_get_rd_texture_rid(RID p_texture, bool p_srgb) const { + return RID(); +} + void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { Texture *texture = texture_owner.get_or_null(p_texture); @@ -941,7 +1085,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, img->resize_to_po2(false); } - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target; Vector<uint8_t> read = img->get_data(); @@ -998,7 +1142,11 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + if (texture->target == GL_TEXTURE_2D_ARRAY) { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 0, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } } tsize += size; @@ -1075,6 +1223,217 @@ RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resoluti return RID(); } +/* TEXTURE ATLAS API */ + +void TextureStorage::texture_add_to_texture_atlas(RID p_texture) { + if (!texture_atlas.textures.has(p_texture)) { + TextureAtlas::Texture t; + t.users = 1; + texture_atlas.textures[p_texture] = t; + texture_atlas.dirty = true; + } else { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + t->users++; + } +} + +void TextureStorage::texture_remove_from_texture_atlas(RID p_texture) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + ERR_FAIL_COND(!t); + t->users--; + if (t->users == 0) { + texture_atlas.textures.erase(p_texture); + // Do not mark it dirty, there is no need to since it remains working. + } +} + +void TextureStorage::texture_atlas_mark_dirty_on_texture(RID p_texture) { + if (texture_atlas.textures.has(p_texture)) { + texture_atlas.dirty = true; // Mark it dirty since it was most likely modified. + } +} + +void TextureStorage::texture_atlas_remove_texture(RID p_texture) { + if (texture_atlas.textures.has(p_texture)) { + texture_atlas.textures.erase(p_texture); + // There is not much a point of making it dirty, texture can be removed next time the atlas is updated. + } +} + +GLuint TextureStorage::texture_atlas_get_texture() const { + return texture_atlas.texture; +} + +void TextureStorage::update_texture_atlas() { + CopyEffects *copy_effects = CopyEffects::get_singleton(); + ERR_FAIL_NULL(copy_effects); + + if (!texture_atlas.dirty) { + return; //nothing to do + } + + texture_atlas.dirty = false; + + if (texture_atlas.texture != 0) { + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; + } + + const int border = 2; + + if (texture_atlas.textures.size()) { + //generate atlas + Vector<TextureAtlas::SortItem> itemsv; + itemsv.resize(texture_atlas.textures.size()); + int base_size = 8; + + int idx = 0; + + for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) { + TextureAtlas::SortItem &si = itemsv.write[idx]; + + Texture *src_tex = get_texture(E.key); + + si.size.width = (src_tex->width / border) + 1; + si.size.height = (src_tex->height / border) + 1; + si.pixel_size = Size2i(src_tex->width, src_tex->height); + + if (base_size < si.size.width) { + base_size = nearest_power_of_2_templated(si.size.width); + } + + si.texture = E.key; + idx++; + } + + //sort items by size + itemsv.sort(); + + //attempt to create atlas + int item_count = itemsv.size(); + TextureAtlas::SortItem *items = itemsv.ptrw(); + + int atlas_height = 0; + + while (true) { + Vector<int> v_offsetsv; + v_offsetsv.resize(base_size); + + int *v_offsets = v_offsetsv.ptrw(); + memset(v_offsets, 0, sizeof(int) * base_size); + + int max_height = 0; + + for (int i = 0; i < item_count; i++) { + //best fit + TextureAtlas::SortItem &si = items[i]; + int best_idx = -1; + int best_height = 0x7FFFFFFF; + for (int j = 0; j <= base_size - si.size.width; j++) { + int height = 0; + for (int k = 0; k < si.size.width; k++) { + int h = v_offsets[k + j]; + if (h > height) { + height = h; + if (height > best_height) { + break; //already bad + } + } + } + + if (height < best_height) { + best_height = height; + best_idx = j; + } + } + + //update + for (int k = 0; k < si.size.width; k++) { + v_offsets[k + best_idx] = best_height + si.size.height; + } + + si.pos.x = best_idx; + si.pos.y = best_height; + + if (si.pos.y + si.size.height > max_height) { + max_height = si.pos.y + si.size.height; + } + } + + if (max_height <= base_size * 2) { + atlas_height = max_height; + break; //good ratio, break; + } + + base_size *= 2; + } + + texture_atlas.size.width = base_size * border; + texture_atlas.size.height = nearest_power_of_2_templated(atlas_height * border); + + for (int i = 0; i < item_count; i++) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(items[i].texture); + t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2); + t->uv_rect.size = items[i].pixel_size; + + t->uv_rect.position /= Size2(texture_atlas.size); + t->uv_rect.size /= Size2(texture_atlas.size); + } + } else { + texture_atlas.size.width = 4; + texture_atlas.size.height = 4; + } + + { // Atlas Texture initialize. + // TODO validate texture atlas size with maximum texture size + glGenTextures(1, &texture_atlas.texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_atlas.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture_atlas.size.width, texture_atlas.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + + glGenFramebuffers(1, &texture_atlas.framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, texture_atlas.framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_atlas.texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &texture_atlas.framebuffer); + texture_atlas.framebuffer = 0; + glDeleteTextures(1, &texture_atlas.texture); + texture_atlas.texture = 0; + WARN_PRINT("Could not create texture atlas, status: " + get_framebuffer_error(status)); + return; + } + glViewport(0, 0, texture_atlas.size.width, texture_atlas.size.height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glDisable(GL_BLEND); + + if (texture_atlas.textures.size()) { + for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(E.key); + Texture *src_tex = get_texture(E.key); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, src_tex->tex_id); + copy_effects->copy_to_rect(t->uv_rect); + } + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + /* DECAL API */ RID TextureStorage::decal_allocate() { @@ -1115,6 +1474,18 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const { return AABB(); } +/* DECAL INSTANCE API */ + +RID TextureStorage::decal_instance_create(RID p_decal) { + return RID(); +} + +void TextureStorage::decal_instance_free(RID p_decal_instance) { +} + +void TextureStorage::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) { +} + /* RENDER TARGET API */ GLuint TextureStorage::system_fbo = 0; @@ -1131,9 +1502,11 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { return; } + Config *config = Config::get_singleton(); + rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2; rt->color_format = GL_RGBA; - rt->color_type = rt->is_transparent ? GL_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; + rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; rt->image_format = Image::FORMAT_RGBA8; glDisable(GL_SCISSOR_TEST); @@ -1141,31 +1514,74 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { glDepthMask(GL_FALSE); { - /* Front FBO */ + Texture *texture; + bool use_multiview = rt->view_count > 1 && config->multiview_supported; + GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; - Texture *texture = get_texture(rt->texture); - ERR_FAIL_COND(!texture); + /* Front FBO */ - // framebuffer glGenFramebuffers(1, &rt->fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); // color - glGenTextures(1, &rt->color); - glBindTexture(GL_TEXTURE_2D, rt->color); + if (rt->overridden.color.is_valid()) { + texture = get_texture(rt->overridden.color); + ERR_FAIL_COND(!texture); - glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); + rt->color = texture->tex_id; + rt->size = Size2i(texture->width, texture->height); + } else { + texture = get_texture(rt->texture); + ERR_FAIL_COND(!texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glGenTextures(1, &rt->color); + glBindTexture(texture_target, rt->color); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (use_multiview) { + glTexImage3D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); + } else { + glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); + } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + } - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + // depth + if (rt->overridden.depth.is_valid()) { + texture = get_texture(rt->overridden.depth); + ERR_FAIL_COND(!texture); + + rt->depth = texture->tex_id; + } else { + glGenTextures(1, &rt->depth); + glBindTexture(texture_target, rt->depth); + if (use_multiview) { + glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } else { + glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + } + + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + if (use_multiview) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { glDeleteFramebuffers(1, &rt->fbo); glDeleteTextures(1, &rt->color); @@ -1173,25 +1589,38 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { rt->size.x = 0; rt->size.y = 0; rt->color = 0; - texture->tex_id = 0; - texture->active = false; + rt->depth = 0; + if (rt->overridden.color.is_null()) { + texture->tex_id = 0; + texture->active = false; + } WARN_PRINT("Could not create render target, status: " + get_framebuffer_error(status)); return; } - texture->format = rt->image_format; - texture->real_format = rt->image_format; - texture->type = Texture::TYPE_2D; - texture->target = GL_TEXTURE_2D; - texture->gl_format_cache = rt->color_format; - texture->gl_type_cache = GL_UNSIGNED_BYTE; - texture->gl_internal_format_cache = rt->color_internal_format; - texture->tex_id = rt->color; - texture->width = rt->size.x; - texture->alloc_width = rt->size.x; - texture->height = rt->size.y; - texture->alloc_height = rt->size.y; - texture->active = true; + if (rt->overridden.color.is_valid()) { + texture->is_render_target = true; + } else { + texture->format = rt->image_format; + texture->real_format = rt->image_format; + texture->target = texture_target; + if (rt->view_count > 1 && config->multiview_supported) { + texture->type = Texture::TYPE_LAYERED; + texture->layers = rt->view_count; + } else { + texture->type = Texture::TYPE_2D; + texture->layers = 1; + } + texture->gl_format_cache = rt->color_format; + texture->gl_type_cache = GL_UNSIGNED_BYTE; + texture->gl_internal_format_cache = rt->color_internal_format; + texture->tex_id = rt->color; + texture->width = rt->size.x; + texture->alloc_width = rt->size.x; + texture->height = rt->size.y; + texture->alloc_height = rt->size.y; + texture->active = true; + } } glClearColor(0, 0, 0, 0); @@ -1259,35 +1688,32 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { if (rt->fbo) { glDeleteFramebuffers(1, &rt->fbo); - glDeleteTextures(1, &rt->color); rt->fbo = 0; + } + + if (rt->overridden.color.is_null()) { + glDeleteTextures(1, &rt->color); rt->color = 0; } - /* - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); - // clean up our texture - Texture *t = get_texture(rt->external.texture); - t->alloc_height = 0; - t->alloc_width = 0; - t->width = 0; - t->height = 0; - t->active = false; - texture_free(rt->external.texture); - memdelete(t); + if (rt->overridden.depth.is_null()) { + glDeleteTextures(1, &rt->depth); + rt->depth = 0; + } - rt->external.fbo = 0; + if (rt->texture.is_valid()) { + Texture *tex = get_texture(rt->texture); + tex->alloc_height = 0; + tex->alloc_width = 0; + tex->width = 0; + tex->height = 0; + tex->active = false; } - */ - Texture *tex = get_texture(rt->texture); - tex->alloc_height = 0; - tex->alloc_width = 0; - tex->width = 0; - tex->height = 0; - tex->active = false; + if (rt->overridden.color.is_valid()) { + Texture *tex = get_texture(rt->overridden.color); + tex->is_render_target = false; + } if (rt->backbuffer_fbo != 0) { glDeleteFramebuffers(1, &rt->backbuffer_fbo); @@ -1295,6 +1721,16 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { rt->backbuffer = 0; rt->backbuffer_fbo = 0; } + _render_target_clear_sdf(rt); +} + +void TextureStorage::_clear_render_target_overridden_fbo_cache(RenderTarget *rt) { + // Dispose of the cached fbo's and the allocated textures + for (KeyValue<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) { + glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr()); + glDeleteFramebuffers(1, &E.value.fbo); + } + rt->overridden.fbo_cache.clear(); } RID TextureStorage::render_target_create() { @@ -1315,11 +1751,14 @@ RID TextureStorage::render_target_create() { void TextureStorage::render_target_free(RID p_rid) { RenderTarget *rt = render_target_owner.get_or_null(p_rid); _clear_render_target(rt); + _clear_render_target_overridden_fbo_cache(rt); Texture *t = get_texture(rt->texture); if (t) { t->is_render_target = false; - texture_free(rt->texture); + if (rt->overridden.color.is_null()) { + texture_free(rt->texture); + } //memdelete(t); } render_target_owner.free(p_rid); @@ -1332,132 +1771,126 @@ void TextureStorage::render_target_set_position(RID p_render_target, int p_x, in rt->position = Point2i(p_x, p_y); } +Point2i TextureStorage::render_target_get_position(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Point2i()); + + return rt->position; +}; + void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); - if (p_width == rt->size.x && p_height == rt->size.y) { + if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) { + return; + } + if (rt->overridden.color.is_valid()) { return; } _clear_render_target(rt); rt->size = Size2i(p_width, p_height); + rt->view_count = p_view_count; _update_render_target(rt); } // TODO: convert to Size2i internally -Size2i TextureStorage::render_target_get_size(RID p_render_target) { +Size2i TextureStorage::render_target_get_size(RID p_render_target) const { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, Size2()); + ERR_FAIL_COND_V(!rt, Size2i()); return rt->size; } -RID TextureStorage::render_target_get_texture(RID p_render_target) { +void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, RID()); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->direct_to_screen); - if (rt->external.fbo == 0) { - return rt->texture; - } else { - return rt->external.texture; + rt->overridden.velocity = p_velocity_texture; + + if (rt->overridden.color == p_color_texture && rt->overridden.depth == p_depth_texture) { + return; } -} -void TextureStorage::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); + if (p_color_texture.is_null() && p_depth_texture.is_null()) { + _clear_render_target(rt); + rt->overridden.is_overridden = false; + rt->overridden.color = RID(); + rt->overridden.depth = RID(); + rt->size = Size2i(); + _clear_render_target_overridden_fbo_cache(rt); + return; + } - if (p_texture_id == 0) { - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); + if (!rt->overridden.is_overridden) { + _clear_render_target(rt); + } - // and this - if (rt->external.depth != 0) { - glDeleteRenderbuffers(1, &rt->external.depth); - } + rt->overridden.color = p_color_texture; + rt->overridden.depth = p_depth_texture; + rt->overridden.is_overridden = true; - // clean up our texture - Texture *t = get_texture(rt->external.texture); - t->alloc_height = 0; - t->alloc_width = 0; - t->width = 0; - t->height = 0; - t->active = false; - texture_free(rt->external.texture); - //memdelete(t); - - rt->external.fbo = 0; - rt->external.color = 0; - rt->external.depth = 0; - } - } else { - Texture *t; - - if (rt->external.fbo == 0) { - // create our fbo - glGenFramebuffers(1, &rt->external.fbo); - glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); - - // allocate a texture - t = memnew(Texture); - - t->type = Texture::TYPE_2D; - t->width = 0; - t->height = 0; - t->alloc_height = 0; - t->alloc_width = 0; - t->format = Image::FORMAT_RGBA8; - t->target = GL_TEXTURE_2D; - t->gl_format_cache = 0; - t->gl_internal_format_cache = 0; - t->gl_type_cache = 0; - t->total_data_size = 0; - t->mipmaps = 1; - t->active = true; - t->tex_id = 0; - t->render_target = rt; - t->is_render_target = true; - - //rt->external.texture = make_rid(t); + uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id()); + hash_key = hash_murmur3_one_64(p_depth_texture.get_id(), hash_key); + hash_key = hash_fmix32(hash_key); - } else { - // bind our frame buffer - glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + RBMap<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry>::Element *cache; + if ((cache = rt->overridden.fbo_cache.find(hash_key)) != nullptr) { + rt->fbo = cache->get().fbo; + rt->size = cache->get().size; + rt->texture = p_color_texture; + return; + } - // find our texture - t = get_texture(rt->external.texture); - } + _update_render_target(rt); + + RenderTarget::RTOverridden::FBOCacheEntry new_entry; + new_entry.fbo = rt->fbo; + new_entry.size = rt->size; + // Keep track of any textures we had to allocate because they weren't overridden. + if (p_color_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->color); + } + if (p_depth_texture.is_null()) { + new_entry.allocated_textures.push_back(rt->depth); + } + rt->overridden.fbo_cache.insert(hash_key, new_entry); +} + +RID TextureStorage::render_target_get_override_color(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); - // set our texture - t->tex_id = p_texture_id; - rt->external.color = p_texture_id; + return rt->overridden.color; +} - // size shouldn't be different - t->width = rt->size.x; - t->height = rt->size.y; - t->alloc_height = rt->size.x; - t->alloc_width = rt->size.y; +RID TextureStorage::render_target_get_override_depth(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); - // Switch our texture on our frame buffer - { - // set our texture as the destination for our framebuffer - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0); - } + return rt->overridden.depth; +} - // check status and unbind - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); +RID TextureStorage::render_target_get_override_velocity(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); - if (status != GL_FRAMEBUFFER_COMPLETE) { - WARN_PRINT("framebuffer fail, status: " + get_framebuffer_error(status)); - } + return rt->overridden.velocity; +} - ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); +RID TextureStorage::render_target_get_texture(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RID()); + + if (rt->overridden.color.is_valid()) { + return rt->overridden.color; } + + return rt->texture; } void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) { @@ -1466,8 +1899,17 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t rt->is_transparent = p_transparent; - _clear_render_target(rt); - _update_render_target(rt); + if (rt->overridden.color.is_null()) { + _clear_render_target(rt); + _update_render_target(rt); + } +} + +bool TextureStorage::render_target_get_transparent(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->is_transparent; } void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) { @@ -1481,10 +1923,22 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo // those functions change how they operate depending on the value of DIRECT_TO_SCREEN _clear_render_target(rt); rt->direct_to_screen = p_direct_to_screen; + if (rt->direct_to_screen) { + rt->overridden.color = RID(); + rt->overridden.depth = RID(); + rt->overridden.velocity = RID(); + } _update_render_target(rt); } -bool TextureStorage::render_target_was_used(RID p_render_target) { +bool TextureStorage::render_target_get_direct_to_screen(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->direct_to_screen; +} + +bool TextureStorage::render_target_was_used(RID p_render_target) const { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND_V(!rt, false); @@ -1498,6 +1952,27 @@ void TextureStorage::render_target_clear_used(RID p_render_target) { rt->used_in_frame = false; } +void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + if (p_msaa == rt->msaa) { + return; + } + + WARN_PRINT("2D MSAA is not yet supported for GLES3."); + + _clear_render_target(rt); + rt->msaa = p_msaa; + _update_render_target(rt); +} + +RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, RS::VIEWPORT_MSAA_DISABLED); + + return rt->msaa; +} + void TextureStorage::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -1528,19 +2003,279 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { if (!rt->clear_requested) { return; } + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); glClearBufferfv(GL_COLOR, 0, rt->clear_color.components); rt->clear_requested = false; + glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); } void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + if (rt->sdf_oversize == p_size && rt->sdf_scale == p_scale) { + return; + } + + rt->sdf_oversize = p_size; + rt->sdf_scale = p_scale; + + _render_target_clear_sdf(rt); +} + +Rect2i TextureStorage::_render_target_get_sdf_rect(const RenderTarget *rt) const { + Size2i margin; + int scale; + switch (rt->sdf_oversize) { + case RS::VIEWPORT_SDF_OVERSIZE_100_PERCENT: { + scale = 100; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT: { + scale = 120; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_150_PERCENT: { + scale = 150; + } break; + case RS::VIEWPORT_SDF_OVERSIZE_200_PERCENT: { + scale = 200; + } break; + default: { + } + } + + margin = (rt->size * scale / 100) - rt->size; + + Rect2i r(Vector2i(), rt->size); + r.position -= margin; + r.size += margin * 2; + + return r; } Rect2i TextureStorage::render_target_get_sdf_rect(RID p_render_target) const { - return Rect2i(); + const RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Rect2i()); + + return _render_target_get_sdf_rect(rt); } void TextureStorage::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->sdf_enabled = p_enabled; +} + +bool TextureStorage::render_target_is_sdf_enabled(RID p_render_target) const { + const RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->sdf_enabled; +} + +GLuint TextureStorage::render_target_get_sdf_texture(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, 0); + if (rt->sdf_texture_read == 0) { + Texture *texture = texture_owner.get_or_null(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK]); + return texture->tex_id; + } + + return rt->sdf_texture_read; +} + +void TextureStorage::_render_target_allocate_sdf(RenderTarget *rt) { + ERR_FAIL_COND(rt->sdf_texture_write_fb != 0); + + Size2i size = _render_target_get_sdf_rect(rt).size; + + glGenTextures(1, &rt->sdf_texture_write); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size.width, size.height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenFramebuffers(1, &rt->sdf_texture_write_fb); + glBindFramebuffer(GL_FRAMEBUFFER, rt->sdf_texture_write_fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_write, 0); + + int scale; + switch (rt->sdf_scale) { + case RS::VIEWPORT_SDF_SCALE_100_PERCENT: { + scale = 100; + } break; + case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { + scale = 50; + } break; + case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { + scale = 25; + } break; + default: { + scale = 100; + } break; + } + + rt->process_size = size * scale / 100; + rt->process_size.x = MAX(rt->process_size.x, 1); + rt->process_size.y = MAX(rt->process_size.y, 1); + + glGenTextures(2, rt->sdf_texture_process); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glGenTextures(1, &rt->sdf_texture_read); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_read); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, rt->process_size.width, rt->process_size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void TextureStorage::_render_target_clear_sdf(RenderTarget *rt) { + if (rt->sdf_texture_write_fb != 0) { + glDeleteTextures(1, &rt->sdf_texture_read); + glDeleteTextures(1, &rt->sdf_texture_write); + glDeleteTextures(2, rt->sdf_texture_process); + glDeleteFramebuffers(1, &rt->sdf_texture_write_fb); + rt->sdf_texture_read = 0; + rt->sdf_texture_write = 0; + rt->sdf_texture_process[0] = 0; + rt->sdf_texture_process[1] = 0; + rt->sdf_texture_write_fb = 0; + } +} + +GLuint TextureStorage::render_target_get_sdf_framebuffer(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, 0); + + if (rt->sdf_texture_write_fb == 0) { + _render_target_allocate_sdf(rt); + } + + return rt->sdf_texture_write_fb; +} +void TextureStorage::render_target_sdf_process(RID p_render_target) { + CopyEffects *copy_effects = CopyEffects::get_singleton(); + + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->sdf_texture_write_fb == 0); + + Rect2i r = _render_target_get_sdf_rect(rt); + + Size2i size = r.size; + int32_t shift = 0; + + bool shrink = false; + + switch (rt->sdf_scale) { + case RS::VIEWPORT_SDF_SCALE_50_PERCENT: { + size[0] >>= 1; + size[1] >>= 1; + shift = 1; + shrink = true; + } break; + case RS::VIEWPORT_SDF_SCALE_25_PERCENT: { + size[0] >>= 2; + size[1] >>= 2; + shift = 2; + shrink = true; + } break; + default: { + }; + } + + GLuint temp_fb; + glGenFramebuffers(1, &temp_fb); + glBindFramebuffer(GL_FRAMEBUFFER, temp_fb); + + // Load + CanvasSdfShaderGLES3::ShaderVariant variant = shrink ? CanvasSdfShaderGLES3::MODE_LOAD_SHRINK : CanvasSdfShaderGLES3::MODE_LOAD; + sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, 0, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[0], 0); + glViewport(0, 0, size.width, size.height); + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, size.width, size.height); + + copy_effects->draw_screen_triangle(); + + // Process + + int stride = nearest_power_of_2_templated(MAX(size.width, size.height) / 2); + + variant = CanvasSdfShaderGLES3::MODE_PROCESS; + sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant); + + bool swap = false; + + //jumpflood + while (stride > 0) { + glBindTexture(GL_TEXTURE_2D, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 0 : 1], 0); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]); + + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant); + + copy_effects->draw_screen_triangle(); + + stride /= 2; + swap = !swap; + } + + // Store + variant = shrink ? CanvasSdfShaderGLES3::MODE_STORE_SHRINK : CanvasSdfShaderGLES3::MODE_STORE; + sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant); + sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant); + + glBindTexture(GL_TEXTURE_2D, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_read, 0); + glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]); + + copy_effects->draw_screen_triangle(); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); + glDeleteFramebuffers(1, &temp_fb); + glDisable(GL_SCISSOR_TEST); } void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index d6d04e45a1..7714e72f62 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -39,6 +39,8 @@ #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/storage/texture_storage.h" +#include "../shaders/canvas_sdf.glsl.gen.h" + // This must come first to avoid windows.h mess #include "platform_config.h" #ifndef OPENGL_INCLUDE_H @@ -84,18 +86,8 @@ namespace GLES3 { #define _GL_TEXTURE_EXTERNAL_OES 0x8D65 -#ifdef GLES_OVER_GL -#define _GL_HALF_FLOAT_OES 0x140B -#else -#define _GL_HALF_FLOAT_OES 0x8D61 -#endif - #define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F -#define _RED_OES 0x1903 - -#define _DEPTH_COMPONENT24_OES 0x81A6 - #ifndef GLES_OVER_GL #define glClearDepth glClearDepthf #endif //!GLES_OVER_GL @@ -103,6 +95,7 @@ namespace GLES3 { enum DefaultGLTexture { DEFAULT_GL_TEXTURE_WHITE, DEFAULT_GL_TEXTURE_BLACK, + DEFAULT_GL_TEXTURE_TRANSPARENT, DEFAULT_GL_TEXTURE_NORMAL, DEFAULT_GL_TEXTURE_ANISO, DEFAULT_GL_TEXTURE_DEPTH, @@ -125,11 +118,6 @@ struct CanvasTexture { RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; - - Size2i size_cache = Size2i(1, 1); - bool use_normal_cache = false; - bool use_specular_cache = false; - bool cleared_cache = true; }; struct RenderTarget; @@ -138,6 +126,7 @@ struct Texture { RID self; bool is_proxy = false; + bool is_external = false; bool is_render_target = false; RID proxy_to = RID(); @@ -199,6 +188,7 @@ struct Texture { void copy_from(const Texture &o) { proxy_to = o.proxy_to; is_proxy = o.is_proxy; + is_external = o.is_external; width = o.width; height = o.height; alloc_width = o.alloc_width; @@ -315,22 +305,14 @@ private: }; struct RenderTarget { - struct External { - GLuint fbo = 0; - GLuint color = 0; - GLuint depth = 0; - RID texture; - - External() { - } - } external; - Point2i position = Point2i(0, 0); Size2i size = Size2i(0, 0); + uint32_t view_count = 1; int mipmap_count = 1; RID self; GLuint fbo = 0; GLuint color = 0; + GLuint depth = 0; GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; @@ -339,12 +321,35 @@ struct RenderTarget { GLuint color_type = GL_UNSIGNED_BYTE; Image::Format image_format = Image::FORMAT_RGBA8; + GLuint sdf_texture_write = 0; + GLuint sdf_texture_write_fb = 0; + GLuint sdf_texture_process[2] = { 0, 0 }; + GLuint sdf_texture_read = 0; + RS::ViewportSDFOversize sdf_oversize = RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT; + RS::ViewportSDFScale sdf_scale = RS::VIEWPORT_SDF_SCALE_50_PERCENT; + Size2i process_size; + bool sdf_enabled = false; + bool is_transparent = false; bool direct_to_screen = false; bool used_in_frame = false; RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + struct RTOverridden { + bool is_overridden = false; + RID color; + RID depth; + RID velocity; + + struct FBOCacheEntry { + GLuint fbo; + Size2i size; + Vector<GLuint> allocated_textures; + }; + RBMap<uint32_t, FBOCacheEntry> fbo_cache; + } overridden; + RID texture; Color clear_color = Color(1, 1, 1, 1); @@ -370,13 +375,54 @@ private: Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const; + /* TEXTURE ATLAS API */ + + struct TextureAtlas { + struct Texture { + int users; + Rect2 uv_rect; + }; + + struct SortItem { + RID texture; + Size2i pixel_size; + Size2i size; + Point2i pos; + + bool operator<(const SortItem &p_item) const { + //sort larger to smaller + if (size.height == p_item.size.height) { + return size.width > p_item.size.width; + } else { + return size.height > p_item.size.height; + } + } + }; + + HashMap<RID, Texture> textures; + bool dirty = true; + + GLuint texture = 0; + GLuint framebuffer = 0; + Size2i size; + } texture_atlas; + /* Render Target API */ mutable RID_Owner<RenderTarget> render_target_owner; void _clear_render_target(RenderTarget *rt); + void _clear_render_target_overridden_fbo_cache(RenderTarget *rt); void _update_render_target(RenderTarget *rt); void _create_render_target_backbuffer(RenderTarget *rt); + void _render_target_allocate_sdf(RenderTarget *rt); + void _render_target_clear_sdf(RenderTarget *rt); + Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const; + + struct RenderTargetSDF { + CanvasSdfShaderGLES3 shader; + RID shader_version; + } sdf_shader; public: static TextureStorage *get_singleton(); @@ -426,6 +472,8 @@ public: virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override; virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent + RID texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY); + virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override{}; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; @@ -456,6 +504,8 @@ public: virtual Size2 texture_size_with_proxy(RID p_proxy) override; + virtual RID texture_get_rd_texture_rid(RID p_texture, bool p_srgb = false) const override; + void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0); 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, int p_layer = 0); //Ref<Image> texture_get_data(RID p_texture, int p_layer = 0) const; @@ -468,6 +518,25 @@ public: void texture_bind(RID p_texture, uint32_t p_texture_no); RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; + /* TEXTURE ATLAS API */ + + void update_texture_atlas(); + + GLuint texture_atlas_get_texture() const; + _FORCE_INLINE_ Rect2 texture_atlas_get_texture_rect(RID p_texture) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + if (!t) { + return Rect2(); + } + + return t->uv_rect; + } + + void texture_add_to_texture_atlas(RID p_texture); + void texture_remove_from_texture_atlas(RID p_texture); + void texture_atlas_mark_dirty_on_texture(RID p_texture); + void texture_atlas_remove_texture(RID p_texture); + /* DECAL API */ virtual RID decal_allocate() override; @@ -489,6 +558,12 @@ public: virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} + /* DECAL INSTANCE */ + + virtual RID decal_instance_create(RID p_decal) override; + virtual void decal_instance_free(RID p_decal_instance) override; + virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; + /* RENDER TARGET API */ static GLuint system_fbo; @@ -498,16 +573,19 @@ public: virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; + virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override; + virtual Point2i render_target_get_position(RID p_render_target) const override; virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override; - Size2i render_target_get_size(RID p_render_target); - virtual RID render_target_get_texture(RID p_render_target) override; - virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; - + virtual Size2i render_target_get_size(RID p_render_target) const override; virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override; + virtual bool render_target_get_transparent(RID p_render_target) const override; virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override; - virtual bool render_target_was_used(RID p_render_target) override; + virtual bool render_target_get_direct_to_screen(RID p_render_target) const override; + virtual bool render_target_was_used(RID p_render_target) const override; void render_target_clear_used(RID p_render_target); + virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; + virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; // new void render_target_set_as_unused(RID p_render_target) override { @@ -520,14 +598,38 @@ public: void render_target_disable_clear_request(RID p_render_target) override; void render_target_do_clear_request(RID p_render_target) override; - void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; - Rect2i render_target_get_sdf_rect(RID p_render_target) const override; - void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override; + virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override; + virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override; + GLuint render_target_get_sdf_texture(RID p_render_target); + GLuint render_target_get_sdf_framebuffer(RID p_render_target); + void render_target_sdf_process(RID p_render_target); + virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override; + bool render_target_is_sdf_enabled(RID p_render_target) const; void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps); void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color); void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region); + virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {} + virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; } + virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} + virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } + + virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) override; + virtual RID render_target_get_override_color(RID p_render_target) const override; + virtual RID render_target_get_override_depth(RID p_render_target) const override; + virtual RID render_target_get_override_velocity(RID p_render_target) const override; + + virtual RID render_target_get_texture(RID p_render_target) override; + + void bind_framebuffer(GLuint framebuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + } + + void bind_framebuffer_system() { + glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); + } + String get_framebuffer_error(GLenum p_status); }; @@ -548,6 +650,6 @@ inline String TextureStorage::get_framebuffer_error(GLenum p_status) { } // namespace GLES3 -#endif // !GLES3_ENABLED +#endif // GLES3_ENABLED -#endif // !TEXTURE_STORAGE_GLES3_H +#endif // TEXTURE_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp new file mode 100644 index 0000000000..8e7e218bb9 --- /dev/null +++ b/drivers/gles3/storage/utilities.cpp @@ -0,0 +1,374 @@ +/*************************************************************************/ +/* utilities.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "utilities.h" +#include "config.h" +#include "light_storage.h" +#include "material_storage.h" +#include "mesh_storage.h" +#include "particles_storage.h" +#include "texture_storage.h" + +#include "servers/rendering/rendering_server_globals.h" + +using namespace GLES3; + +Utilities *Utilities::singleton = nullptr; + +Utilities::Utilities() { + singleton = this; + frame = 0; + for (int i = 0; i < FRAME_COUNT; i++) { + frames[i].index = 0; + glGenQueries(max_timestamp_query_elements, frames[i].queries); + + frames[i].timestamp_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); + frames[i].timestamp_count = 0; + + frames[i].timestamp_result_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_count = 0; + } +} + +Utilities::~Utilities() { + singleton = nullptr; + for (int i = 0; i < FRAME_COUNT; i++) { + glDeleteQueries(max_timestamp_query_elements, frames[i].queries); + } +} + +Vector<uint8_t> Utilities::buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size) { + Vector<uint8_t> ret; + + if (p_buffer_size == 0) { + return ret; + } + + ret.resize(p_buffer_size); + glBindBuffer(p_target, p_buffer); + +#if defined(__EMSCRIPTEN__) + { + uint8_t *w = ret.ptrw(); + glGetBufferSubData(p_target, 0, p_buffer_size, w); + } +#else + void *data = glMapBufferRange(p_target, 0, p_buffer_size, GL_MAP_READ_BIT); + ERR_FAIL_NULL_V(data, Vector<uint8_t>()); + { + uint8_t *w = ret.ptrw(); + memcpy(w, data, p_buffer_size); + } + glUnmapBuffer(p_target); +#endif + glBindBuffer(p_target, 0); + return ret; +} + +/* INSTANCES */ + +RS::InstanceType Utilities::get_base_type(RID p_rid) const { + if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) { + return RS::INSTANCE_MESH; + } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { + return RS::INSTANCE_MULTIMESH; + } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { + return RS::INSTANCE_LIGHT; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + return RS::INSTANCE_LIGHTMAP; + } + return RS::INSTANCE_NONE; +} + +bool Utilities::free(RID p_rid) { + if (GLES3::TextureStorage::get_singleton()->owns_render_target(p_rid)) { + GLES3::TextureStorage::get_singleton()->render_target_free(p_rid); + return true; + } else if (GLES3::TextureStorage::get_singleton()->owns_texture(p_rid)) { + GLES3::TextureStorage::get_singleton()->texture_free(p_rid); + return true; + } else if (GLES3::TextureStorage::get_singleton()->owns_canvas_texture(p_rid)) { + GLES3::TextureStorage::get_singleton()->canvas_texture_free(p_rid); + return true; + } else if (GLES3::MaterialStorage::get_singleton()->owns_shader(p_rid)) { + GLES3::MaterialStorage::get_singleton()->shader_free(p_rid); + return true; + } else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) { + GLES3::MaterialStorage::get_singleton()->material_free(p_rid); + return true; + } else if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) { + GLES3::MeshStorage::get_singleton()->mesh_free(p_rid); + return true; + } else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) { + GLES3::MeshStorage::get_singleton()->multimesh_free(p_rid); + return true; + } else if (GLES3::MeshStorage::get_singleton()->owns_mesh_instance(p_rid)) { + GLES3::MeshStorage::get_singleton()->mesh_instance_free(p_rid); + return true; + } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { + GLES3::LightStorage::get_singleton()->light_free(p_rid); + return true; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + GLES3::LightStorage::get_singleton()->lightmap_free(p_rid); + return true; + } else { + return false; + } + /* + else if (reflection_probe_owner.owns(p_rid)) { + // delete the texture + ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid); + reflection_probe->instance_remove_deps(); + + reflection_probe_owner.free(p_rid); + memdelete(reflection_probe); + + return true; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + // delete the texture + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get_or_null(p_rid); + lightmap_capture->instance_remove_deps(); + + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); + return true; + + } else if (canvas_occluder_owner.owns(p_rid)) { + CanvasOccluder *co = canvas_occluder_owner.get_or_null(p_rid); + if (co->index_id) { + glDeleteBuffers(1, &co->index_id); + } + if (co->vertex_id) { + glDeleteBuffers(1, &co->vertex_id); + } + + canvas_occluder_owner.free(p_rid); + memdelete(co); + + return true; + + } else if (canvas_light_shadow_owner.owns(p_rid)) { + CanvasLightShadow *cls = canvas_light_shadow_owner.get_or_null(p_rid); + glDeleteFramebuffers(1, &cls->fbo); + glDeleteRenderbuffers(1, &cls->depth); + glDeleteTextures(1, &cls->distance); + canvas_light_shadow_owner.free(p_rid); + memdelete(cls); + + return true; + } + */ +} + +/* DEPENDENCIES */ + +void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance) { + if (MeshStorage::get_singleton()->owns_mesh(p_base)) { + Mesh *mesh = MeshStorage::get_singleton()->get_mesh(p_base); + p_instance->update_dependency(&mesh->dependency); + } else if (MeshStorage::get_singleton()->owns_multimesh(p_base)) { + MultiMesh *multimesh = MeshStorage::get_singleton()->get_multimesh(p_base); + p_instance->update_dependency(&multimesh->dependency); + if (multimesh->mesh.is_valid()) { + base_update_dependency(multimesh->mesh, p_instance); + } + } else if (LightStorage::get_singleton()->owns_light(p_base)) { + Light *l = LightStorage::get_singleton()->get_light(p_base); + p_instance->update_dependency(&l->dependency); + } +} + +/* VISIBILITY NOTIFIER */ + +RID Utilities::visibility_notifier_allocate() { + return RID(); +} + +void Utilities::visibility_notifier_initialize(RID p_notifier) { +} + +void Utilities::visibility_notifier_free(RID p_notifier) { +} + +void Utilities::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) { +} + +void Utilities::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) { +} + +AABB Utilities::visibility_notifier_get_aabb(RID p_notifier) const { + return AABB(); +} + +void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) { +} + +/* TIMING */ + +void Utilities::capture_timestamps_begin() { + capture_timestamp("Frame Begin"); +} + +void Utilities::capture_timestamp(const String &p_name) { + ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); + +#ifdef GLES_OVER_GL + glQueryCounter(frames[frame].queries[frames[frame].timestamp_count], GL_TIMESTAMP); +#endif + + frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name; + frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec(); + frames[frame].timestamp_count++; +} + +void Utilities::_capture_timestamps_begin() { + // frame is incremented at the end of the frame so this gives us the queries for frame - 2. By then they should be ready. + if (frames[frame].timestamp_count) { +#ifdef GLES_OVER_GL + for (uint32_t i = 0; i < frames[frame].timestamp_count; i++) { + uint64_t temp = 0; + glGetQueryObjectui64v(frames[frame].queries[i], GL_QUERY_RESULT, &temp); + frames[frame].timestamp_result_values[i] = temp; + } +#endif + SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); + SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); + } + + frames[frame].timestamp_result_count = frames[frame].timestamp_count; + frames[frame].timestamp_count = 0; + frames[frame].index = Engine::get_singleton()->get_frames_drawn(); + capture_timestamp("Internal Begin"); +} + +void Utilities::capture_timestamps_end() { + capture_timestamp("Internal End"); + frame = (frame + 1) % FRAME_COUNT; +} + +uint32_t Utilities::get_captured_timestamps_count() const { + return frames[frame].timestamp_result_count; +} + +uint64_t Utilities::get_captured_timestamps_frame() const { + return frames[frame].index; +} + +uint64_t Utilities::get_captured_timestamp_gpu_time(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return frames[frame].timestamp_result_values[p_index]; +} + +uint64_t Utilities::get_captured_timestamp_cpu_time(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); + return frames[frame].timestamp_cpu_result_values[p_index]; +} + +String Utilities::get_captured_timestamp_name(uint32_t p_index) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String()); + return frames[frame].timestamp_result_names[p_index]; +} + +/* MISC */ + +void Utilities::update_dirty_resources() { + MaterialStorage::get_singleton()->_update_global_shader_uniforms(); + MaterialStorage::get_singleton()->_update_queued_materials(); + //MeshStorage::get_singleton()->_update_dirty_skeletons(); + MeshStorage::get_singleton()->_update_dirty_multimeshes(); + TextureStorage::get_singleton()->update_texture_atlas(); +} + +void Utilities::set_debug_generate_wireframes(bool p_generate) { +} + +bool Utilities::has_os_feature(const String &p_feature) const { + Config *config = Config::get_singleton(); + if (!config) { + return false; + } + + if (p_feature == "rgtc") { + return config->rgtc_supported; + } + + if (p_feature == "s3tc") { + return config->s3tc_supported; + } + + if (p_feature == "bptc") { + return config->bptc_supported; + } + + if (p_feature == "etc" || p_feature == "etc2") { + return config->etc2_supported; + } + + return false; +} + +void Utilities::update_memory_info() { +} + +uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) { + return 0; +} + +String Utilities::get_video_adapter_name() const { + return (const char *)glGetString(GL_RENDERER); +} + +String Utilities::get_video_adapter_vendor() const { + return (const char *)glGetString(GL_VENDOR); +} + +RenderingDevice::DeviceType Utilities::get_video_adapter_type() const { + return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER; +} + +String Utilities::get_video_adapter_api_version() const { + return (const char *)glGetString(GL_VERSION); +} + +Size2i Utilities::get_maximum_viewport_size() const { + Config *config = Config::get_singleton(); + if (!config) { + return Size2i(); + } + + return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h new file mode 100644 index 0000000000..55a875958e --- /dev/null +++ b/drivers/gles3/storage/utilities.h @@ -0,0 +1,134 @@ +/*************************************************************************/ +/* utilities.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef UTILITIES_GLES3_H +#define UTILITIES_GLES3_H + +#ifdef GLES3_ENABLED + +#include "servers/rendering/storage/utilities.h" + +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +namespace GLES3 { + +class Utilities : public RendererUtilities { +private: + static Utilities *singleton; + +public: + static Utilities *get_singleton() { return singleton; } + + Utilities(); + ~Utilities(); + + // Buffer size is specified in bytes + static Vector<uint8_t> buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size); + + /* INSTANCES */ + + virtual RS::InstanceType get_base_type(RID p_rid) const override; + virtual bool free(RID p_rid) override; + + /* DEPENDENCIES */ + + virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override; + + /* VISIBILITY NOTIFIER */ + virtual RID visibility_notifier_allocate() override; + virtual void visibility_notifier_initialize(RID p_notifier) override; + virtual void visibility_notifier_free(RID p_notifier) override; + + virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override; + virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override; + + virtual AABB visibility_notifier_get_aabb(RID p_notifier) const override; + virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override; + + /* TIMING */ + +#define MAX_QUERIES 256 +#define FRAME_COUNT 3 + + struct Frame { + GLuint queries[MAX_QUERIES]; + TightLocalVector<String> timestamp_names; + TightLocalVector<uint64_t> timestamp_cpu_values; + uint32_t timestamp_count = 0; + TightLocalVector<String> timestamp_result_names; + TightLocalVector<uint64_t> timestamp_cpu_result_values; + TightLocalVector<uint64_t> timestamp_result_values; + uint32_t timestamp_result_count = 0; + uint64_t index = 0; + }; + + const uint32_t max_timestamp_query_elements = MAX_QUERIES; + + Frame frames[FRAME_COUNT]; // Frames for capturing timestamps. We use 3 so we don't need to wait for commands to complete + uint32_t frame = 0; + + virtual void capture_timestamps_begin() override; + virtual void capture_timestamp(const String &p_name) override; + virtual uint32_t get_captured_timestamps_count() const override; + virtual uint64_t get_captured_timestamps_frame() const override; + virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override; + virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override; + virtual String get_captured_timestamp_name(uint32_t p_index) const override; + void _capture_timestamps_begin(); + void capture_timestamps_end(); + + /* MISC */ + + virtual void update_dirty_resources() override; + virtual void set_debug_generate_wireframes(bool p_generate) override; + + virtual bool has_os_feature(const String &p_feature) const override; + + virtual void update_memory_info() override; + + virtual uint64_t get_rendering_info(RS::RenderingInfo p_info) override; + virtual String get_video_adapter_name() const override; + virtual String get_video_adapter_vendor() const override; + virtual RenderingDevice::DeviceType get_video_adapter_type() const override; + virtual String get_video_adapter_api_version() const override; + + virtual Size2i get_maximum_viewport_size() const override; +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // UTILITIES_GLES3_H diff --git a/drivers/png/SCsub b/drivers/png/SCsub index 39d296e7cf..fe8c8fa8cc 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -30,14 +30,14 @@ if env["builtin_libpng"]: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_png.Prepend(CPPPATH=[thirdparty_dir]) - # Needed for drivers includes and in platform/javascript + # Needed for drivers includes and in platform/web. env.Prepend(CPPPATH=[thirdparty_dir]) # Currently .ASM filter_neon.S does not compile on NT. import os # Enable ARM NEON instructions on 32-bit Android to compile more optimized code. - use_neon = "android_arch" in env and env["android_arch"] == "armv7" and os.name != "nt" + use_neon = env["platform"] == "android" and env["arch"] == "arm32" and os.name != "nt" if use_neon: env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 2)]) else: diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 917bfec574..165de34c71 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -36,7 +36,7 @@ #include <string.h> -Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { +Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { const uint64_t buffer_size = f->get_length(); Vector<uint8_t> file_buffer; Error err = file_buffer.resize(buffer_size); @@ -48,7 +48,7 @@ Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_f f->get_buffer(writer, buffer_size); } const uint8_t *reader = file_buffer.ptr(); - return PNGDriverCommon::png_to_image(reader, buffer_size, p_force_linear, p_image); + return PNGDriverCommon::png_to_image(reader, buffer_size, p_flags & FLAG_FORCE_LINEAR, p_image); } void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const { diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index 522cc901d4..a247d77310 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -40,9 +40,9 @@ private: static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderPNG(); }; -#endif +#endif // IMAGE_LOADER_PNG_H diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index bc4bb3782b..79641464d8 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -119,7 +119,7 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); //print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height)); - p_image->create(png_img.width, png_img.height, false, dest_format, buffer); + p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer); return OK; } diff --git a/drivers/png/png_driver_common.h b/drivers/png/png_driver_common.h index 62302bbdbb..24546d4402 100644 --- a/drivers/png/png_driver_common.h +++ b/drivers/png/png_driver_common.h @@ -43,4 +43,4 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer); } // namespace PNGDriverCommon -#endif +#endif // PNG_DRIVER_COMMON_H diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp index 704ddca726..275f3240d7 100644 --- a/drivers/png/resource_saver_png.cpp +++ b/drivers/png/resource_saver_png.cpp @@ -35,7 +35,7 @@ #include "drivers/png/png_driver_common.h" #include "scene/resources/texture.h" -Error ResourceSaverPNG::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { +Error ResourceSaverPNG::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { Ref<ImageTexture> texture = p_resource; ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as PNG."); diff --git a/drivers/png/resource_saver_png.h b/drivers/png/resource_saver_png.h index 1a681faaec..260a643a1b 100644 --- a/drivers/png/resource_saver_png.h +++ b/drivers/png/resource_saver_png.h @@ -39,7 +39,7 @@ public: static Error save_image(const String &p_path, const Ref<Image> &p_img); static Vector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img); - virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index a6c35b6837..b25cf1d5b4 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -178,7 +178,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { Error AudioDriverPulseAudio::init_device() { // If there is a specified device check that it is really present if (device_name != "Default") { - Array list = get_device_list(); + PackedStringArray list = get_device_list(); if (list.find(device_name) == -1) { device_name = "Default"; new_device = "Default"; @@ -285,9 +285,8 @@ Error AudioDriverPulseAudio::init() { return ERR_CANT_OPEN; } - active = false; - thread_exited = false; - exit_thread = false; + active.clear(); + exit_thread.clear(); mix_rate = GLOBAL_GET("audio/driver/mix_rate"); @@ -384,7 +383,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { size_t avail_bytes = 0; uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec(); - while (!ad->exit_thread) { + while (!ad->exit_thread.is_set()) { size_t read_bytes = 0; size_t written_bytes = 0; @@ -392,7 +391,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ad->lock(); ad->start_counting_ticks(); - if (!ad->active) { + if (!ad->active.is_set()) { ad->samples_out.fill(0); } else { ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); @@ -462,8 +461,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { err = ad->init_device(); if (err != OK) { - ad->active = false; - ad->exit_thread = true; + ad->active.clear(); + ad->exit_thread.set(); break; } } @@ -501,8 +500,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { Error err = ad->init_device(); if (err != OK) { ERR_PRINT("PulseAudio: init_device error"); - ad->active = false; - ad->exit_thread = true; + ad->active.clear(); + ad->exit_thread.set(); break; } @@ -555,8 +554,8 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { err = ad->capture_init_device(); if (err != OK) { - ad->active = false; - ad->exit_thread = true; + ad->active.clear(); + ad->exit_thread.set(); break; } } @@ -571,12 +570,10 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { OS::get_singleton()->delay_usec(1000); } } - - ad->thread_exited = true; } void AudioDriverPulseAudio::start() { - active = true; + active.set(); } int AudioDriverPulseAudio::get_mix_rate() const { @@ -599,7 +596,7 @@ void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, ad->pa_status++; } -Array AudioDriverPulseAudio::get_device_list() { +PackedStringArray AudioDriverPulseAudio::get_device_list() { pa_devices.clear(); pa_devices.push_back("Default"); @@ -661,7 +658,7 @@ void AudioDriverPulseAudio::finish() { return; } - exit_thread = true; + exit_thread.set(); thread.wait_to_finish(); finish_device(); @@ -681,7 +678,7 @@ void AudioDriverPulseAudio::finish() { Error AudioDriverPulseAudio::capture_init_device() { // If there is a specified device check that it is really present if (capture_device_name != "Default") { - Array list = capture_get_device_list(); + PackedStringArray list = capture_get_device_list(); if (list.find(capture_device_name) == -1) { capture_device_name = "Default"; capture_new_device = "Default"; @@ -785,7 +782,7 @@ void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info ad->pa_status++; } -Array AudioDriverPulseAudio::capture_get_device_list() { +PackedStringArray AudioDriverPulseAudio::capture_get_device_list() { pa_rec_devices.clear(); pa_rec_devices.push_back("Default"); diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index af96489972..ae6e0acc97 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -35,6 +35,7 @@ #include "core/os/mutex.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "servers/audio_server.h" #include "pulse-so_wrap.h" @@ -47,8 +48,8 @@ class AudioDriverPulseAudio : public AudioDriver { pa_context *pa_ctx = nullptr; pa_stream *pa_str = nullptr; pa_stream *pa_rec_str = nullptr; - pa_channel_map pa_map; - pa_channel_map pa_rec_map; + pa_channel_map pa_map = {}; + pa_channel_map pa_rec_map = {}; String device_name = "Default"; String new_device = "Default"; @@ -67,12 +68,11 @@ class AudioDriverPulseAudio : public AudioDriver { int channels = 0; int pa_ready = 0; int pa_status = 0; - Array pa_devices; - Array pa_rec_devices; + PackedStringArray pa_devices; + PackedStringArray pa_rec_devices; - bool active = false; - bool thread_exited = false; - mutable bool exit_thread = false; + SafeFlag active; + SafeFlag exit_thread; float latency = 0; @@ -103,11 +103,11 @@ public: virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; - virtual Array get_device_list(); + virtual PackedStringArray get_device_list(); virtual String get_device(); virtual void set_device(String device); - virtual Array capture_get_device_list(); + virtual PackedStringArray capture_get_device_list(); virtual void capture_set_device(const String &p_name); virtual String capture_get_device(); diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 504ef9843a..53a7f7aa4f 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -34,11 +34,11 @@ #include "drivers/png/image_loader_png.h" #include "drivers/png/resource_saver_png.h" -static ImageLoaderPNG *image_loader_png; +static Ref<ImageLoaderPNG> image_loader_png; static Ref<ResourceSaverPNG> resource_saver_png; void register_core_driver_types() { - image_loader_png = memnew(ImageLoaderPNG); + image_loader_png.instantiate(); ImageLoader::add_image_format_loader(image_loader_png); resource_saver_png.instantiate(); @@ -46,9 +46,8 @@ void register_core_driver_types() { } void unregister_core_driver_types() { - if (image_loader_png) { - memdelete(image_loader_png); - } + ImageLoader::remove_image_format_loader(image_loader_png); + image_loader_png.unref(); ResourceSaver::remove_resource_format_saver(resource_saver_png); resource_saver_png.unref(); diff --git a/drivers/register_driver_types.h b/drivers/register_driver_types.h index c008d93185..41f95b57e7 100644 --- a/drivers/register_driver_types.h +++ b/drivers/register_driver_types.h @@ -37,4 +37,4 @@ void unregister_core_driver_types(); void register_driver_types(); void unregister_driver_types(); -#endif +#endif // REGISTER_DRIVER_TYPES_H diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 7e6105f033..d894ceba59 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -30,9 +30,10 @@ #include "dir_access_unix.h" -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/os/memory.h" +#include "core/os/os.h" #include "core/string/print_string.h" #include "core/templates/list.h" @@ -40,19 +41,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> - -#ifndef ANDROID_ENABLED #include <sys/statvfs.h> -#endif #ifdef HAVE_MNTENT #include <mntent.h> #endif -Ref<DirAccess> DirAccessUnix::create_fs() { - return memnew(DirAccessUnix); -} - Error DirAccessUnix::list_dir_begin() { list_dir_end(); //close any previous dir opening! @@ -72,12 +66,12 @@ bool DirAccessUnix::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION if (p_file.is_relative_path()) { - p_file = current_dir.plus_file(p_file); + p_file = current_dir.path_join(p_file); } p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_file.utf8().get_data(), &flags) == 0); if (success && S_ISDIR(flags.st_mode)) { @@ -91,12 +85,12 @@ bool DirAccessUnix::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION if (p_dir.is_relative_path()) { - p_dir = get_current_dir().plus_file(p_dir); + p_dir = get_current_dir().path_join(p_dir); } p_dir = fix_path(p_dir); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_dir.utf8().get_data(), &flags) == 0); return (success && S_ISDIR(flags.st_mode)); @@ -106,7 +100,7 @@ bool DirAccessUnix::is_readable(String p_dir) { GLOBAL_LOCK_FUNCTION if (p_dir.is_relative_path()) { - p_dir = get_current_dir().plus_file(p_dir); + p_dir = get_current_dir().path_join(p_dir); } p_dir = fix_path(p_dir); @@ -117,7 +111,7 @@ bool DirAccessUnix::is_writable(String p_dir) { GLOBAL_LOCK_FUNCTION if (p_dir.is_relative_path()) { - p_dir = get_current_dir().plus_file(p_dir); + p_dir = get_current_dir().path_join(p_dir); } p_dir = fix_path(p_dir); @@ -126,12 +120,12 @@ bool DirAccessUnix::is_writable(String p_dir) { uint64_t DirAccessUnix::get_modified_time(String p_file) { if (p_file.is_relative_path()) { - p_file = current_dir.plus_file(p_file); + p_file = current_dir.path_join(p_file); } p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_file.utf8().get_data(), &flags) == 0); if (success) { @@ -162,9 +156,9 @@ String DirAccessUnix::get_next() { // known if it points to a directory. stat() will resolve the link // for us. if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) { - String f = current_dir.plus_file(fname); + String f = current_dir.path_join(fname); - struct stat flags; + struct stat flags = {}; if (stat(f.utf8().get_data(), &flags) == 0) { _cisdir = S_ISDIR(flags.st_mode); } else { @@ -216,10 +210,11 @@ static bool _filter_drive(struct mntent *mnt) { #endif static void _get_drives(List<String> *list) { + // Add root. list->push_back("/"); #if defined(HAVE_MNTENT) && defined(X11_ENABLED) - // Check /etc/mtab for the list of mounted partitions + // Check /etc/mtab for the list of mounted partitions. FILE *mtab = setmntent("/etc/mtab", "r"); if (mtab) { struct mntent mnt; @@ -239,7 +234,7 @@ static void _get_drives(List<String> *list) { } #endif - // Add $HOME + // Add $HOME. const char *home = getenv("HOME"); if (home) { // Only add if it's not a duplicate @@ -248,7 +243,8 @@ static void _get_drives(List<String> *list) { list->push_back(home_name); } - // Check $HOME/.config/gtk-3.0/bookmarks + // Check GTK+3 bookmarks for both XDG locations (Documents, Downloads, etc.) + // and potential user-defined bookmarks. char path[1024]; snprintf(path, 1024, "%s/.config/gtk-3.0/bookmarks", home); FILE *fd = fopen(path, "r"); @@ -257,7 +253,7 @@ static void _get_drives(List<String> *list) { while (fgets(string, 1024, fd)) { // Parse only file:// links if (strncmp(string, "file://", 7) == 0) { - // Strip any unwanted edges on the strings and push_back if it's not a duplicate + // Strip any unwanted edges on the strings and push_back if it's not a duplicate. String fpath = String::utf8(string + 7).strip_edges().split_spaces()[0].uri_decode(); if (!list->find(fpath)) { list->push_back(fpath); @@ -267,6 +263,12 @@ static void _get_drives(List<String> *list) { fclose(fd); } + + // Add Desktop dir. + String dpath = OS::get_singleton()->get_system_dir(OS::SystemDir::SYSTEM_DIR_DESKTOP); + if (dpath.length() > 0 && !list->find(dpath)) { + list->push_back(dpath); + } } list->sort(); @@ -310,7 +312,7 @@ Error DirAccessUnix::make_dir(String p_dir) { GLOBAL_LOCK_FUNCTION if (p_dir.is_relative_path()) { - p_dir = get_current_dir().plus_file(p_dir); + p_dir = get_current_dir().path_join(p_dir); } p_dir = fix_path(p_dir); @@ -338,14 +340,14 @@ Error DirAccessUnix::change_dir(String p_dir) { String prev_dir; char real_current_dir_name[2048]; ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == nullptr, ERR_BUG); - if (prev_dir.parse_utf8(real_current_dir_name)) { + if (prev_dir.parse_utf8(real_current_dir_name) != OK) { prev_dir = real_current_dir_name; //no utf8, maybe latin? } // try_dir is the directory we are trying to change into String try_dir = ""; if (p_dir.is_relative_path()) { - String next_dir = current_dir.plus_file(p_dir); + String next_dir = current_dir.path_join(p_dir); next_dir = next_dir.simplify_path(); try_dir = next_dir; } else { @@ -389,13 +391,13 @@ String DirAccessUnix::get_current_dir(bool p_include_drive) const { Error DirAccessUnix::rename(String p_path, String p_new_path) { if (p_path.is_relative_path()) { - p_path = get_current_dir().plus_file(p_path); + p_path = get_current_dir().path_join(p_path); } p_path = fix_path(p_path); if (p_new_path.is_relative_path()) { - p_new_path = get_current_dir().plus_file(p_new_path); + p_new_path = get_current_dir().path_join(p_new_path); } p_new_path = fix_path(p_new_path); @@ -405,12 +407,12 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) { Error DirAccessUnix::remove(String p_path) { if (p_path.is_relative_path()) { - p_path = get_current_dir().plus_file(p_path); + p_path = get_current_dir().path_join(p_path); } p_path = fix_path(p_path); - struct stat flags; + struct stat flags = {}; if ((stat(p_path.utf8().get_data(), &flags) != 0)) { return FAILED; } @@ -424,12 +426,12 @@ Error DirAccessUnix::remove(String p_path) { bool DirAccessUnix::is_link(String p_file) { if (p_file.is_relative_path()) { - p_file = get_current_dir().plus_file(p_file); + p_file = get_current_dir().path_join(p_file); } p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; if ((lstat(p_file.utf8().get_data(), &flags) != 0)) { return FAILED; } @@ -439,7 +441,7 @@ bool DirAccessUnix::is_link(String p_file) { String DirAccessUnix::read_link(String p_file) { if (p_file.is_relative_path()) { - p_file = get_current_dir().plus_file(p_file); + p_file = get_current_dir().path_join(p_file); } p_file = fix_path(p_file); @@ -456,7 +458,7 @@ String DirAccessUnix::read_link(String p_file) { Error DirAccessUnix::create_link(String p_source, String p_target) { if (p_target.is_relative_path()) { - p_target = get_current_dir().plus_file(p_target); + p_target = get_current_dir().path_join(p_target); } p_source = fix_path(p_source); @@ -470,17 +472,12 @@ Error DirAccessUnix::create_link(String p_source, String p_target) { } uint64_t DirAccessUnix::get_space_left() { -#ifndef NO_STATVFS struct statvfs vfs; if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) { return 0; } return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize; -#else - // FIXME: Implement this. - return 0; -#endif } String DirAccessUnix::get_filesystem_type() const { @@ -500,7 +497,7 @@ DirAccessUnix::DirAccessUnix() { // set current directory to an absolute path of the current directory char real_current_dir_name[2048]; ERR_FAIL_COND(getcwd(real_current_dir_name, 2048) == nullptr); - if (current_dir.parse_utf8(real_current_dir_name)) { + if (current_dir.parse_utf8(real_current_dir_name) != OK) { current_dir = real_current_dir_name; } @@ -511,4 +508,4 @@ DirAccessUnix::~DirAccessUnix() { list_dir_end(); } -#endif //posix_enabled +#endif // UNIX_ENABLED diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index 69530de337..4db24a27b9 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -31,7 +31,7 @@ #ifndef DIR_ACCESS_UNIX_H #define DIR_ACCESS_UNIX_H -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/io/dir_access.h" @@ -43,54 +43,53 @@ class DirAccessUnix : public DirAccess { DIR *dir_stream = nullptr; - static Ref<DirAccess> create_fs(); - - String current_dir; bool _cisdir = false; bool _cishidden = false; protected: + String current_dir; virtual String fix_unicode_name(const char *p_name) const { return String::utf8(p_name); } virtual bool is_hidden(const String &p_name); public: - virtual Error list_dir_begin(); ///< This starts dir listing - virtual String get_next(); - virtual bool current_is_dir() const; - virtual bool current_is_hidden() const; + virtual Error list_dir_begin() override; ///< This starts dir listing + virtual String get_next() override; + virtual bool current_is_dir() const override; + virtual bool current_is_hidden() const override; - virtual void list_dir_end(); ///< + virtual void list_dir_end() override; ///< - virtual int get_drive_count(); - virtual String get_drive(int p_drive); - virtual int get_current_drive(); - virtual bool drives_are_shortcuts(); + virtual int get_drive_count() override; + virtual String get_drive(int p_drive) override; + virtual int get_current_drive() override; + virtual bool drives_are_shortcuts() override; - virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location - virtual Error make_dir(String p_dir); + virtual Error change_dir(String p_dir) override; ///< can be relative or absolute, return false on success + virtual String get_current_dir(bool p_include_drive = true) const override; ///< return current dir location + virtual Error make_dir(String p_dir) override; - virtual bool file_exists(String p_file); - virtual bool dir_exists(String p_dir); - virtual bool is_readable(String p_dir); - virtual bool is_writable(String p_dir); + virtual bool file_exists(String p_file) override; + virtual bool dir_exists(String p_dir) override; + virtual bool is_readable(String p_dir) override; + virtual bool is_writable(String p_dir) override; virtual uint64_t get_modified_time(String p_file); - virtual Error rename(String p_path, String p_new_path); - virtual Error remove(String p_path); + virtual Error rename(String p_path, String p_new_path) override; + virtual Error remove(String p_path) override; - virtual bool is_link(String p_file); - virtual String read_link(String p_file); - virtual Error create_link(String p_source, String p_target); + virtual bool is_link(String p_file) override; + virtual String read_link(String p_file) override; + virtual Error create_link(String p_source, String p_target) override; - virtual uint64_t get_space_left(); + virtual uint64_t get_space_left() override; - virtual String get_filesystem_type() const; + virtual String get_filesystem_type() const override; DirAccessUnix(); ~DirAccessUnix(); }; -#endif //UNIX ENABLED -#endif +#endif // UNIX_ENABLED + +#endif // DIR_ACCESS_UNIX_H diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index e0b2994b63..0b80fb1491 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -30,24 +30,20 @@ #include "file_access_unix.h" -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/os/os.h" #include "core/string/print_string.h" +#include <errno.h> +#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> -#include <errno.h> - #if defined(UNIX_ENABLED) #include <unistd.h> #endif -#ifndef ANDROID_ENABLED -#include <sys/statvfs.h> -#endif - #ifdef MSVC #define S_ISREG(m) ((m)&_S_IFREG) #include <io.h> @@ -56,12 +52,6 @@ #define S_ISREG(m) ((m)&S_IFREG) #endif -#ifndef NO_FCNTL -#include <fcntl.h> -#else -#include <sys/ioctl.h> -#endif - void FileAccessUnix::check_errors() const { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); @@ -70,7 +60,7 @@ void FileAccessUnix::check_errors() const { } } -Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { +Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) { _close(); path_src = p_path; @@ -96,7 +86,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { backend (unix-compatible mostly) supports utf8 encoding */ //printf("opening %s as %s\n", p_path.utf8().get_data(), path.utf8().get_data()); - struct stat st; + struct stat st = {}; int err = stat(path.utf8().get_data(), &st); if (!err) { switch (st.st_mode & S_IFMT) { @@ -131,13 +121,8 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { int fd = fileno(f); if (fd != -1) { -#if defined(NO_FCNTL) - unsigned long par = 0; - ioctl(fd, FIOCLEX, &par); -#else int opts = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, opts | FD_CLOEXEC); -#endif } last_error = OK; @@ -267,7 +252,7 @@ void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { bool FileAccessUnix::file_exists(const String &p_path) { int err; - struct stat st; + struct stat st = {}; String filename = fix_path(p_path); // Does the name exist at all? @@ -299,7 +284,7 @@ bool FileAccessUnix::file_exists(const String &p_path) { uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; int err = stat(file.utf8().get_data(), &flags); if (!err) { @@ -312,7 +297,7 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { String file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; int err = stat(file.utf8().get_data(), &flags); if (!err) { @@ -333,14 +318,10 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_per return FAILED; } -Ref<FileAccess> FileAccessUnix::create_libc() { - return memnew(FileAccessUnix); -} - CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr; FileAccessUnix::~FileAccessUnix() { _close(); } -#endif +#endif // UNIX_ENABLED diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 4340bbbc82..8c9afe75e7 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -36,7 +36,7 @@ #include <stdio.h> -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags); @@ -49,43 +49,43 @@ class FileAccessUnix : public FileAccess { String path; String path_src; - static Ref<FileAccess> create_libc(); void _close(); public: static CloseNotificationFunc close_notification_func; - virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual bool is_open() const; ///< true when file is open + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file + virtual bool is_open() const override; ///< true when file is open - virtual String get_path() const; /// returns the path for the current open file - virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual String get_path() const override; /// returns the path for the current open file + virtual String get_path_absolute() const override; /// returns the absolute path for the current open file - virtual void seek(uint64_t p_position); ///< seek to a given position - virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual uint64_t get_position() const; ///< get position in the file - virtual uint64_t get_length() const; ///< get size of the file + virtual void seek(uint64_t p_position) override; ///< seek to a given position + virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file + virtual uint64_t get_position() const override; ///< get position in the file + virtual uint64_t get_length() const override; ///< get size of the file - virtual bool eof_reached() const; ///< reading passed EOF + virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const; ///< get a byte - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; + virtual uint8_t get_8() const override; ///< get a byte + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; - virtual Error get_error() const; ///< get last error + virtual Error get_error() const override; ///< get last error - virtual void flush(); - virtual void store_8(uint8_t p_dest); ///< store a byte - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes + virtual void flush() override; + virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes - virtual bool file_exists(const String &p_path); ///< return true if a file exists + virtual bool file_exists(const String &p_path) override; ///< return true if a file exists - virtual uint64_t _get_modified_time(const String &p_file); - virtual uint32_t _get_unix_permissions(const String &p_file); - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); + virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint32_t _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; FileAccessUnix() {} virtual ~FileAccessUnix(); }; -#endif -#endif +#endif // UNIX_ENABLED + +#endif // FILE_ACCESS_UNIX_H diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index 2deeb79957..0dc2efedc1 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -95,12 +95,12 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos int s = getaddrinfo(p_hostname.utf8().get_data(), nullptr, &hints, &result); if (s != 0) { - ERR_PRINT("getaddrinfo failed! Cannot resolve hostname."); + print_verbose("getaddrinfo failed! Cannot resolve hostname."); return; } if (result == nullptr || result->ai_addr == nullptr) { - ERR_PRINT("Invalid response from getaddrinfo"); + print_verbose("Invalid response from getaddrinfo"); if (result) { freeaddrinfo(result); } diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index 06fcdb6e17..798d02095c 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -50,4 +50,5 @@ public: }; #endif + #endif // IP_UNIX_H diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index f172f31b24..c6b327eeee 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -30,32 +30,30 @@ #include "net_socket_posix.h" +// Some proprietary Unix-derived platforms don't expose Unix sockets +// so this allows skipping this file to reimplement this API differently. #ifndef UNIX_SOCKET_UNAVAILABLE + #if defined(UNIX_ENABLED) #include <errno.h> +#include <fcntl.h> #include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> +#include <sys/socket.h> #include <sys/types.h> #include <unistd.h> -#ifndef NO_FCNTL -#include <fcntl.h> -#else -#include <sys/ioctl.h> -#endif -#include <netinet/in.h> -#include <sys/socket.h> -#ifdef JAVASCRIPT_ENABLED +#ifdef WEB_ENABLED #include <arpa/inet.h> #endif -#include <netinet/tcp.h> - // BSD calls this flag IPV6_JOIN_GROUP #if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP) #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -93,7 +91,7 @@ #define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) #endif -#endif +#endif // UNIX_ENABLED size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); @@ -181,8 +179,8 @@ NetSocketPosix::~NetSocketPosix() { close(); } -// Silent a warning reported in #27594 - +// Silence a warning reported in GH-27594. +// EAGAIN and EWOULDBLOCK have the same value on most platforms, but it's not guaranteed. #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" @@ -309,14 +307,9 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_ void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { #ifndef WINDOWS_ENABLED // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. -#if defined(NO_FCNTL) - unsigned long par = p_enabled ? 1 : 0; - SOCK_IOCTL(_sock, FIOCLEX, &par); -#else int opts = fcntl(_sock, F_GETFD); fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); #endif -#endif } Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -658,7 +651,7 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int ret = 0; -#if defined(WINDOWS_ENABLED) || defined(NO_FCNTL) +#if defined(WINDOWS_ENABLED) unsigned long par = p_enabled ? 0 : 1; ret = SOCK_IOCTL(_sock, FIONBIO, &par); #else @@ -781,4 +774,5 @@ Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, Str Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, false); } -#endif + +#endif // UNIX_SOCKET_UNAVAILABLE diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index 867513099a..5558114cb1 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef NET_SOCKET_UNIX_H -#define NET_SOCKET_UNIX_H +#ifndef NET_SOCKET_POSIX_H +#define NET_SOCKET_POSIX_H #include "core/io/net_socket.h" @@ -104,4 +104,4 @@ public: ~NetSocketPosix(); }; -#endif +#endif // NET_SOCKET_POSIX_H diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 52a4d538e1..161706489f 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -51,7 +51,6 @@ #include <sys/sysctl.h> #endif -#include <assert.h> #include <dlfcn.h> #include <errno.h> #include <poll.h> @@ -65,7 +64,7 @@ #include <time.h> #include <unistd.h> -#if defined(OSX_ENABLED) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) +#if defined(MACOS_ENABLED) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) // Random location for getentropy. Fitting. #include <sys/random.h> #define UNIX_GET_ENTROPY @@ -92,7 +91,7 @@ static void _setup_clock() { _clock_start = mach_absolute_time() * _clock_scale; } #else -#if defined(CLOCK_MONOTONIC_RAW) && !defined(JAVASCRIPT_ENABLED) // This is a better clock on Linux. +#if defined(CLOCK_MONOTONIC_RAW) && !defined(WEB_ENABLED) // This is a better clock on Linux. #define GODOT_CLOCK CLOCK_MONOTONIC_RAW #else #define GODOT_CLOCK CLOCK_MONOTONIC @@ -104,10 +103,6 @@ static void _setup_clock() { } #endif -void OS_Unix::debug_break() { - assert(false); -} - static void handle_interrupt(int sig) { if (!EngineDebugger::is_active()) { return; @@ -131,9 +126,7 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) { } void OS_Unix::initialize_core() { -#if !defined(NO_THREADS) init_thread_posix(); -#endif FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); @@ -142,10 +135,8 @@ void OS_Unix::initialize_core() { DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); -#ifndef NO_NETWORK NetSocketPosix::make_default(); IPUnix::make_default(); -#endif _setup_clock(); } @@ -154,6 +145,10 @@ void OS_Unix::finalize_core() { NetSocketPosix::cleanup(); } +Vector<String> OS_Unix::get_video_adapter_driver_info() const { + return Vector<String>(); +} + String OS_Unix::get_stdin_string(bool p_block) { if (p_block) { char buff[1024]; @@ -175,6 +170,7 @@ Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) { left -= chunk; ofs += chunk; } while (left > 0); +// Define this yourself if you don't want to fall back to /dev/urandom. #elif !defined(NO_URANDOM) int r = open("/dev/urandom", O_RDONLY); ERR_FAIL_COND_V(r < 0, FAILED); @@ -194,13 +190,21 @@ String OS_Unix::get_name() const { return "Unix"; } +String OS_Unix::get_distribution_name() const { + return ""; +} + +String OS_Unix::get_version() const { + return ""; +} + double OS_Unix::get_unix_time() const { struct timeval tv_now; gettimeofday(&tv_now, nullptr); return (double)tv_now.tv_sec + double(tv_now.tv_usec) / 1000000; } -OS::Date OS_Unix::get_date(bool p_utc) const { +OS::DateTime OS_Unix::get_datetime(bool p_utc) const { time_t t = time(nullptr); struct tm lt; if (p_utc) { @@ -208,7 +212,7 @@ OS::Date OS_Unix::get_date(bool p_utc) const { } else { localtime_r(&t, <); } - Date ret; + DateTime ret; ret.year = 1900 + lt.tm_year; // Index starting at 1 to match OS_Unix::get_date // and Windows SYSTEMTIME and tm_mon follows the typical structure @@ -216,24 +220,11 @@ OS::Date OS_Unix::get_date(bool p_utc) const { ret.month = (Month)(lt.tm_mon + 1); ret.day = lt.tm_mday; ret.weekday = (Weekday)lt.tm_wday; - ret.dst = lt.tm_isdst; - - return ret; -} - -OS::Time OS_Unix::get_time(bool p_utc) const { - time_t t = time(nullptr); - struct tm lt; - if (p_utc) { - gmtime_r(&t, <); - } else { - localtime_r(&t, <); - } - Time ret; ret.hour = lt.tm_hour; ret.minute = lt.tm_min; ret.second = lt.tm_sec; - get_time_zone_info(); + ret.dst = lt.tm_isdst; + return ret; } @@ -292,7 +283,7 @@ uint64_t OS_Unix::get_ticks_usec() const { Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { #ifdef __EMSCRIPTEN__ // Don't compile this code at all to avoid undefined references. - // Actual virtual call goes to OS_JavaScript. + // Actual virtual call goes to OS_Web. ERR_FAIL_V(ERR_BUG); #else if (r_pipe) { @@ -313,7 +304,12 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St if (p_pipe_mutex) { p_pipe_mutex->lock(); } - (*r_pipe) += String::utf8(buf); + String pipe_out; + if (pipe_out.parse_utf8(buf) == OK) { + (*r_pipe) += pipe_out; + } else { + (*r_pipe) += String(buf); // If not valid UTF-8 try decode as Latin-1 + } if (p_pipe_mutex) { p_pipe_mutex->unlock(); } @@ -361,7 +357,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { #ifdef __EMSCRIPTEN__ // Don't compile this code at all to avoid undefined references. - // Actual virtual call goes to OS_JavaScript. + // Actual virtual call goes to OS_Web. ERR_FAIL_V(ERR_BUG); #else pid_t pid = fork(); @@ -449,12 +445,12 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle if (!FileAccess::exists(path)) { // This code exists so GDExtension can load .so files from within the executable path. - path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); + path = get_executable_path().get_base_dir().path_join(p_path.get_file()); } if (!FileAccess::exists(path)) { // This code exists so GDExtension can load .so files from a standard unix location. - path = get_executable_path().get_base_dir().plus_file("../lib").plus_file(p_path.get_file()); + path = get_executable_path().get_base_dir().path_join("../lib").path_join(p_path.get_file()); } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); @@ -508,26 +504,22 @@ bool OS_Unix::set_environment(const String &p_var, const String &p_value) const return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0; } -int OS_Unix::get_processor_count() const { - return sysconf(_SC_NPROCESSORS_CONF); -} - String OS_Unix::get_user_data_dir() const { - String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + String appname = get_safe_dir_name(GLOBAL_GET("application/config/name")); if (!appname.is_empty()) { - bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); + bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir"); if (use_custom_dir) { - String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); + String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true); if (custom_dir.is_empty()) { custom_dir = appname; } - return get_data_path().plus_file(custom_dir); + return get_data_path().path_join(custom_dir); } else { - return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file(appname); + return get_data_path().path_join(get_godot_dir_name()).path_join("app_userdata").path_join(appname); } } - return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("[unnamed project]"); + return get_data_path().path_join(get_godot_dir_name()).path_join("app_userdata").path_join("[unnamed project]"); } String OS_Unix::get_executable_path() const { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index a1ed4bd501..ce06a52a95 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -51,9 +51,11 @@ protected: public: OS_Unix(); + virtual Vector<String> get_video_adapter_driver_info() const override; + virtual String get_stdin_string(bool p_block) override; - virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; // Should return cryptographycally-safe random bytes. + virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; @@ -62,9 +64,10 @@ public: virtual Error set_cwd(const String &p_cwd) override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; - virtual Date get_date(bool p_utc) const override; - virtual Time get_time(bool p_utc) const override; + virtual DateTime get_datetime(bool p_utc) const override; virtual TimeZoneInfo get_time_zone_info() const override; virtual double get_unix_time() const override; @@ -83,9 +86,6 @@ public: virtual bool set_environment(const String &p_var, const String &p_value) const override; virtual String get_locale() const override; - virtual int get_processor_count() const override; - - virtual void debug_break() override; virtual void initialize_debugging() override; virtual String get_executable_path() const override; @@ -98,6 +98,6 @@ public: virtual ~UnixTerminalLogger(); }; -#endif +#endif // UNIX_ENABLED -#endif +#endif // OS_UNIX_H diff --git a/drivers/unix/syslog_logger.h b/drivers/unix/syslog_logger.h index 697a96a6f9..cc6617eb25 100644 --- a/drivers/unix/syslog_logger.h +++ b/drivers/unix/syslog_logger.h @@ -43,6 +43,6 @@ public: virtual ~SyslogLogger(); }; -#endif +#endif // UNIX_ENABLED -#endif +#endif // SYSLOG_LOGGER_H diff --git a/drivers/unix/thread_posix.cpp b/drivers/unix/thread_posix.cpp index cb5f261e6e..5154feb478 100644 --- a/drivers/unix/thread_posix.cpp +++ b/drivers/unix/thread_posix.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS) +#if defined(UNIX_ENABLED) #include "thread_posix.h" @@ -70,7 +70,7 @@ static Error set_name(const String &p_name) { } void init_thread_posix() { - Thread::_set_platform_funcs(&set_name, nullptr); + Thread::_set_platform_functions({ .set_name = set_name }); } -#endif +#endif // UNIX_ENABLED diff --git a/drivers/unix/thread_posix.h b/drivers/unix/thread_posix.h index 9cd3ecbe90..87e42b3870 100644 --- a/drivers/unix/thread_posix.h +++ b/drivers/unix/thread_posix.h @@ -31,8 +31,6 @@ #ifndef THREAD_POSIX_H #define THREAD_POSIX_H -#if !defined(NO_THREADS) void init_thread_posix(); -#endif -#endif +#endif // THREAD_POSIX_H diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub index b6ceb1cdea..a076c0ac54 100644 --- a/drivers/vulkan/SCsub +++ b/drivers/vulkan/SCsub @@ -15,11 +15,11 @@ if env["use_volk"]: if env["platform"] == "android": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"]) -elif env["platform"] == "iphone": +elif env["platform"] == "ios": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"]) -elif env["platform"] == "linuxbsd": +elif env["platform"] == "linuxbsd" and env["x11"]: env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"]) -elif env["platform"] == "osx": +elif env["platform"] == "macos": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"]) elif env["platform"] == "windows": env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_WIN32_KHR"]) @@ -40,7 +40,7 @@ elif env["platform"] == "android": # Our current NDK version only provides old Vulkan headers, # so we have to limit VMA. env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1000000"]) -elif env["platform"] == "osx" or env["platform"] == "iphone": +elif env["platform"] == "macos" or env["platform"] == "ios": # MoltenVK supports only Vulkan 1.1 API, limit VMA to the same version. env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1001000"]) diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 77aab72d40..0ff4ca31e6 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -36,6 +36,7 @@ #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/templates/hashfuncs.h" +#include "core/version.h" #include "drivers/vulkan/vulkan_context.h" #include "thirdparty/misc/smolv.h" @@ -45,7 +46,7 @@ static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096; -// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values) +// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values). RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &r_stage_mask, VkAccessFlags &r_access_mask, uint32_t p_post_barrier) { Buffer *buffer = nullptr; if (vertex_buffer_owner.owns(p_buffer)) { @@ -106,9 +107,9 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID return buffer; } -static void update_external_dependency_for_store(VkSubpassDependency &dependency, bool is_sampled, bool is_storage, bool is_depth) { - // Transitioning from write to read, protect the shaders that may use this next - // Allow for copies/image layout transitions +static void update_external_dependency_for_store(VkSubpassDependency2KHR &dependency, bool is_sampled, bool is_storage, bool is_depth) { + // Transitioning from write to read, protect the shaders that may use this next. + // Allow for copies/image layout transitions. dependency.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; dependency.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; @@ -124,7 +125,7 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency } if (is_depth) { - // Depth resources have additional stages that may be interested in them + // Depth resources have additional stages that may be interested in them. dependency.dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } @@ -145,7 +146,7 @@ void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { } void RenderingDeviceVulkan::_free_dependencies(RID p_id) { - //direct dependencies must be freed + // Direct dependencies must be freed. HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); if (E) { @@ -155,7 +156,7 @@ void RenderingDeviceVulkan::_free_dependencies(RID p_id) { dependency_map.remove(E); } - //reverse dependencies must be unreferenced + // Reverse dependencies must be unreferenced. E = reverse_dependency_map.find(p_id); if (E) { @@ -859,7 +860,7 @@ uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format) case DATA_FORMAT_D24_UNORM_S8_UINT: return 4; case DATA_FORMAT_D32_SFLOAT_S8_UINT: - return 5; //? + return 5; // ? case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: @@ -994,7 +995,7 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor case DATA_FORMAT_EAC_R11_SNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: //again, not sure about astc + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: @@ -1072,7 +1073,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK: case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK: return 16; - case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: //again, not sure about astc + case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc. case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK: @@ -1100,7 +1101,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK: case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK: case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK: - return 8; //wrong + return 8; // Wrong. default: { } } @@ -1109,7 +1110,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFormat p_format) { switch (p_format) { - case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: //these formats are half byte size, so rshift is 1 + case DATA_FORMAT_BC1_RGB_UNORM_BLOCK: // These formats are half byte size, so rshift is 1. case DATA_FORMAT_BC1_RGB_SRGB_BLOCK: case DATA_FORMAT_BC1_RGBA_UNORM_BLOCK: case DATA_FORMAT_BC1_RGBA_SRGB_BLOCK: @@ -1183,7 +1184,7 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form } uint32_t RenderingDeviceVulkan::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) { - //formats and block size don't really matter here since they can all go down to 1px (even if block is larger) + // Formats and block size don't really matter here since they can all go down to 1px (even if block is larger). uint32_t w = p_width; uint32_t h = p_height; uint32_t d = p_depth; @@ -1400,17 +1401,17 @@ Error RenderingDeviceVulkan::_insert_staging_block() { return OK; } -Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment, bool p_on_draw_command_buffer) { - //determine a block to use +Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment) { + // Determine a block to use. r_alloc_size = p_amount; while (true) { r_alloc_offset = 0; - //see if we can use current block + // See if we can use current block. if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { - //we used this block this frame, let's see if there is still room + // We used this block this frame, let's see if there is still room. uint32_t write_from = staging_buffer_blocks[staging_buffer_current].fill_amount; @@ -1424,107 +1425,107 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ int32_t available_bytes = int32_t(staging_buffer_block_size) - int32_t(write_from); if ((int32_t)p_amount < available_bytes) { - //all is good, we should be ok, all will fit + // All is good, we should be ok, all will fit. r_alloc_offset = write_from; } else if (p_can_segment && available_bytes >= (int32_t)p_required_align) { - //ok all won't fit but at least we can fit a chunkie - //all is good, update what needs to be written to + // Ok all won't fit but at least we can fit a chunkie. + // All is good, update what needs to be written to. r_alloc_offset = write_from; r_alloc_size = available_bytes - (available_bytes % p_required_align); } else { - //can't fit it into this buffer. - //will need to try next buffer + // Can't fit it into this buffer. + // Will need to try next buffer. staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); - // before doing anything, though, let's check that we didn't manage to fill all blocks - // possible in a single frame + // Before doing anything, though, let's check that we didn't manage to fill all blocks. + // Possible in a single frame. if (staging_buffer_blocks[staging_buffer_current].frame_used == frames_drawn) { - //guess we did.. ok, let's see if we can insert a new block.. + // Guess we did.. ok, let's see if we can insert a new block. if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { - //we can, so we are safe + // We can, so we are safe. Error err = _insert_staging_block(); if (err) { return err; } - //claim for this frame + // Claim for this frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } else { // Ok, worst case scenario, all the staging buffers belong to this frame // and this frame is not even done. - // If this is the main thread, it means the user is likely loading a lot of resources at once, - // otherwise, the thread should just be blocked until the next frame (currently unimplemented) + // If this is the main thread, it means the user is likely loading a lot of resources at once,. + // Otherwise, the thread should just be blocked until the next frame (currently unimplemented). - if (false) { //separate thread from render + if (false) { // Separate thread from render. //block_until_next_frame() continue; } else { - //flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands + // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. _flush(true); - //clear the whole staging buffer + // Clear the whole staging buffer. for (int i = 0; i < staging_buffer_blocks.size(); i++) { staging_buffer_blocks.write[i].frame_used = 0; staging_buffer_blocks.write[i].fill_amount = 0; } - //claim current + // Claim current. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } } } else { - //not from current frame, so continue and try again + // Not from current frame, so continue and try again. continue; } } } else if (staging_buffer_blocks[staging_buffer_current].frame_used <= frames_drawn - frame_count) { - //this is an old block, which was already processed, let's reuse + // This is an old block, which was already processed, let's reuse. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; staging_buffer_blocks.write[staging_buffer_current].fill_amount = 0; } else { - //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? + // This block may still be in use, let's not touch it unless we have to, so.. can we create a new one? if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) { - //we are still allowed to create a new block, so let's do that and insert it for current pos + // We are still allowed to create a new block, so let's do that and insert it for current pos. Error err = _insert_staging_block(); if (err) { return err; } - //claim for this frame + // Claim for this frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } else { - // oops, we are out of room and we can't create more. - // let's flush older frames. + // Oops, we are out of room and we can't create more. + // Let's flush older frames. // The logic here is that if a game is loading a lot of data from the main thread, it will need to be stalled anyway. // If loading from a separate thread, we can block that thread until next frame when more room is made (not currently implemented, though). if (false) { - //separate thread from render + // Separate thread from render. //block_until_next_frame() - continue; //and try again + continue; // And try again. } else { _flush(false); for (int i = 0; i < staging_buffer_blocks.size(); i++) { - //clear all blocks but the ones from this frame + // Clear all blocks but the ones from this frame. int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { - break; //ok, we reached something from this frame, abort + break; // Ok, we reached something from this frame, abort. } staging_buffer_blocks.write[block_idx].frame_used = 0; staging_buffer_blocks.write[block_idx].fill_amount = 0; } - //claim for current frame + // Claim for current frame. staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; } } } - //all was good, break + // All was good, break. break; } @@ -1534,7 +1535,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_ } Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer, uint32_t p_required_align) { - //submitting may get chunked for various reasons, so convert this to a task + // Submitting may get chunked for various reasons, so convert this to a task. size_t to_submit = p_data_size; size_t submit_from = 0; @@ -1542,12 +1543,12 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c uint32_t block_write_offset; uint32_t block_write_amount; - Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount, p_use_draw_command_buffer); + Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount); if (err) { return err; } - //map staging buffer (It's CPU and coherent) + // Map staging buffer (It's CPU and coherent). void *data_ptr = nullptr; { @@ -1555,12 +1556,12 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); } - //copy to staging buffer + // Copy to staging buffer. memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount); - //unmap + // Unmap. vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); - //insert a command to copy this + // Insert a command to copy this. VkBufferCopy region; region.srcOffset = block_write_offset; @@ -1586,13 +1587,13 @@ void RenderingDeviceVulkan::_memory_barrier(VkPipelineStageFlags p_src_stage_mas mem_barrier.dstAccessMask = p_dst_sccess; if (p_src_stage_mask == 0 || p_dst_stage_mask == 0) { - return; //no barrier, since this is invalid + return; // No barrier, since this is invalid. } vkCmdPipelineBarrier(p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, p_src_stage_mask, p_dst_stage_mask, 0, 1, &mem_barrier, 0, nullptr, 0, nullptr); } void RenderingDeviceVulkan::_full_barrier(bool p_sync_with_draw) { - //used for debug + // Used for debug. _memory_barrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT | @@ -1654,15 +1655,13 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.pNext = nullptr; image_create_info.flags = 0; -#ifndef _MSC_VER -#warning TODO check for support via RenderingDevice to enable on mobile when possible -#endif + // TODO: Check for support via RenderingDevice to enable on mobile when possible. #ifndef ANDROID_ENABLED // vkCreateImage fails with format list on Android (VK_ERROR_OUT_OF_HOST_MEMORY) - VkImageFormatListCreateInfoKHR format_list_create_info; //keep out of the if, needed for creation - Vector<VkFormat> allowed_formats; //keep out of the if, needed for creation + VkImageFormatListCreateInfoKHR format_list_create_info; // Keep out of the if, needed for creation. + Vector<VkFormat> allowed_formats; // Keep out of the if, needed for creation. #endif if (p_format.shareable_formats.size()) { image_create_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; @@ -1732,10 +1731,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T ERR_FAIL_INDEX_V(p_format.samples, TEXTURE_SAMPLES_MAX, RID()); - image_create_info.samples = rasterization_sample_count[p_format.samples]; + image_create_info.samples = _ensure_supported_sample_count(p_format.samples); image_create_info.tiling = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; - //usage + // Usage. image_create_info.usage = 0; if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { @@ -1758,6 +1757,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } + if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + image_create_info.usage |= VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR; + } + if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) { image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } @@ -1795,7 +1798,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T } { - //validate that this image is supported for the intended use + // Validate that this image is supported for the intended use. VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), image_create_info.format, &properties); VkFormatFeatureFlags flags; @@ -1829,9 +1832,14 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_ATOMIC_BIT && !(flags & VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT)) { ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as atomic storage image."); } + + // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. + if (p_format.usage_bits & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && p_format.format != DATA_FORMAT_R8_UINT) { + ERR_FAIL_V_MSG(RID(), "Format " + format_text + " does not support usage as VRS attachment."); + } } - //some view validation + // Some view validation. if (p_view.format_override != DATA_FORMAT_MAX) { ERR_FAIL_INDEX_V(p_view.format_override, DATA_FORMAT_MAX, RID()); @@ -1841,7 +1849,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T ERR_FAIL_INDEX_V(p_view.swizzle_b, TEXTURE_SWIZZLE_MAX, RID()); ERR_FAIL_INDEX_V(p_view.swizzle_a, TEXTURE_SWIZZLE_MAX, RID()); - //allocate memory + // Allocate memory. uint32_t width, height; uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height); @@ -1874,23 +1882,24 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T texture.mipmaps = image_create_info.mipLevels; texture.base_mipmap = 0; texture.base_layer = 0; + texture.is_resolve_buffer = p_format.is_resolve_buffer; texture.usage_flags = p_format.usage_bits; texture.samples = p_format.samples; texture.allowed_shared_formats = p_format.shareable_formats; - //set base layout based on usage priority + // Set base layout based on usage priority. if (p_format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT) { - //first priority, readable + // First priority, readable. texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } else if (p_format.usage_bits & TEXTURE_USAGE_STORAGE_BIT) { - //second priority, storage + // Second priority, storage. texture.layout = VK_IMAGE_LAYOUT_GENERAL; } else if (p_format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - //third priority, color or depth + // Third priority, color or depth. texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; @@ -1915,7 +1924,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T texture.bound = false; - //create view + // Create view. VkImageViewCreateInfo image_view_create_info; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -1972,7 +1981,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); } - //barrier to set layout + // Barrier to set layout. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -1994,6 +2003,9 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T } RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif if (p_data.size()) { for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) { @@ -2009,13 +2021,13 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); - if (src_texture->owner.is_valid()) { //ahh this is a share + if (src_texture->owner.is_valid()) { // Ahh this is a share. p_with_texture = src_texture->owner; src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. } - //create view + // Create view. Texture texture = *src_texture; @@ -2076,7 +2088,7 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID usage_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO; usage_info.pNext = nullptr; if (p_view.format_override != DATA_FORMAT_MAX) { - //need to validate usage with vulkan + // Need to validate usage with vulkan. usage_info.usage = 0; @@ -2123,6 +2135,9 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID texture.owner = p_with_texture; RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif _add_dependency(id, p_with_texture); return id; @@ -2135,9 +2150,9 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat Texture texture; texture.image = image; - // if we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? - // also leave texture.allocation_info alone - // we'll set texture.view later on + // If we leave texture.allocation as a nullptr, would that be enough to detect we don't "own" the image? + // Also leave texture.allocation_info alone. + // We'll set texture.view later on. texture.type = p_type; texture.format = p_format; texture.samples = p_samples; @@ -2145,14 +2160,35 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat texture.height = p_height; texture.depth = p_depth; texture.layers = p_layers; - texture.mipmaps = 0; // maybe make this settable too? + texture.mipmaps = 1; texture.usage_flags = p_flags; texture.base_mipmap = 0; texture.base_layer = 0; texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); - // Do we need to do something with texture.layout ? + // Set base layout based on usage priority. + + if (texture.usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { + // First priority, readable. + texture.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + } else if (texture.usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + // Second priority, storage. + + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + + } else if (texture.usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + // Third priority, color or depth. + + texture.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + } else if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + texture.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + } else { + texture.layout = VK_IMAGE_LAYOUT_GENERAL; + } if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; @@ -2166,7 +2202,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; } - // Create a view for us to use + // Create a view for us to use. VkImageViewCreateInfo image_view_create_info; image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -2197,7 +2233,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat VK_COMPONENT_SWIZZLE_A }; - // hardcode for now, maybe make this settable from outside.. + // Hardcode for now, maybe make this settable from outside. image_view_create_info.components.r = component_swizzles[TEXTURE_SWIZZLE_R]; image_view_create_info.components.g = component_swizzles[TEXTURE_SWIZZLE_G]; image_view_create_info.components.b = component_swizzles[TEXTURE_SWIZZLE_B]; @@ -2220,7 +2256,7 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); } - //barrier to set layout + // Barrier to set layout. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -2242,6 +2278,9 @@ RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, Dat } RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif return id; } @@ -2252,10 +2291,10 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p Texture *src_texture = texture_owner.get_or_null(p_with_texture); ERR_FAIL_COND_V(!src_texture, RID()); - if (src_texture->owner.is_valid()) { //ahh this is a share + if (src_texture->owner.is_valid()) { // Ahh this is a share. p_with_texture = src_texture->owner; src_texture = texture_owner.get_or_null(src_texture->owner); - ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug + ERR_FAIL_COND_V(!src_texture, RID()); // This is a bug. } ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_CUBEMAP && (src_texture->type != TEXTURE_TYPE_CUBE && src_texture->type != TEXTURE_TYPE_CUBE_ARRAY), RID(), @@ -2267,7 +2306,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p ERR_FAIL_COND_V_MSG(p_slice_type == TEXTURE_SLICE_2D_ARRAY && (src_texture->type != TEXTURE_TYPE_2D_ARRAY), RID(), "Can only create an array slice from a 2D array mipmap"); - //create view + // Create view. ERR_FAIL_UNSIGNED_INDEX_V(p_mipmap, src_texture->mipmaps, RID()); ERR_FAIL_COND_V(p_mipmap + p_mipmaps > src_texture->mipmaps, RID()); @@ -2314,6 +2353,12 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; } + if (p_slice_type == TEXTURE_SLICE_2D) { + texture.type = TEXTURE_TYPE_2D; + } else if (p_slice_type == TEXTURE_SLICE_3D) { + texture.type = TEXTURE_TYPE_3D; + } + if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) { image_view_create_info.format = vulkan_formats[texture.format]; } else { @@ -2361,6 +2406,9 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p texture.owner = p_with_texture; RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif _add_dependency(id, p_with_texture); return id; @@ -2370,6 +2418,22 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); } +static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_unit_size) { + uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size; + uint32_t dst_offset = 0; + for (uint32_t y = p_src_h; y > 0; y--) { + uint8_t const *__restrict src = p_src + src_offset; + uint8_t *__restrict dst = p_dst + dst_offset; + for (uint32_t x = p_src_w * p_unit_size; x > 0; x--) { + *dst = *src; + src++; + dst++; + } + src_offset += p_src_full_w * p_unit_size; + dst_offset += p_src_w * p_unit_size; + } +} + Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier, bool p_use_setup_queue) { _THREAD_SAFE_METHOD_ @@ -2382,7 +2446,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co if (texture->owner != RID()) { p_texture = texture->owner; texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, ERR_BUG); //this is a bug + ERR_FAIL_COND_V(!texture, ERR_BUG); // This is a bug. } ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, @@ -2404,7 +2468,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co if (required_align == 1) { required_align = get_image_format_pixel_size(texture->format); } - if ((required_align % 4) != 0) { //alignment rules are really strange + if ((required_align % 4) != 0) { // Alignment rules are really strange. required_align *= 4; } @@ -2417,7 +2481,7 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; - //barrier to transfer + // Barrier to transfer. { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -2451,12 +2515,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co const uint8_t *read_ptr_mipmap = r + mipmap_offset; image_size = image_total - mipmap_offset; - for (uint32_t z = 0; z < depth; z++) { //for 3D textures, depth may be > 0 + for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 0. const uint8_t *read_ptr = read_ptr_mipmap + image_size * z / depth; - for (uint32_t x = 0; x < width; x += region_size) { - for (uint32_t y = 0; y < height; y += region_size) { + for (uint32_t y = 0; y < height; y += region_size) { + for (uint32_t x = 0; x < width; x += region_size) { uint32_t region_w = MIN(region_size, width - x); uint32_t region_h = MIN(region_size, height - y); @@ -2468,12 +2532,12 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co to_allocate >>= get_compressed_image_format_pixel_rshift(texture->format); uint32_t alloc_offset, alloc_size; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, !p_use_setup_queue); + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); uint8_t *write_ptr; - { //map + { // Map. void *data_ptr = nullptr; VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr); ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + "."); @@ -2488,52 +2552,32 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co ERR_FAIL_COND_V(region_h % block_h, ERR_BUG); if (block_w != 1 || block_h != 1) { - //compressed image (blocks) - //must copy a block region + // Compressed image (blocks). + // Must copy a block region. uint32_t block_size = get_compressed_image_format_block_byte_size(texture->format); - //re-create current variables in blocky format + // Re-create current variables in blocky format. uint32_t xb = x / block_w; uint32_t yb = y / block_h; uint32_t wb = width / block_w; //uint32_t hb = height / block_h; uint32_t region_wb = region_w / block_w; uint32_t region_hb = region_h / block_h; - for (uint32_t xr = 0; xr < region_wb; xr++) { - for (uint32_t yr = 0; yr < region_hb; yr++) { - uint32_t src_offset = ((yr + yb) * wb + xr + xb) * block_size; - uint32_t dst_offset = (yr * region_wb + xr) * block_size; - //copy block - for (uint32_t i = 0; i < block_size; i++) { - write_ptr[dst_offset + i] = read_ptr[src_offset + i]; - } - } - } - + _copy_region(read_ptr, write_ptr, xb, yb, region_wb, region_hb, wb, block_size); } else { - //regular image (pixels) - //must copy a pixel region - - for (uint32_t xr = 0; xr < region_w; xr++) { - for (uint32_t yr = 0; yr < region_h; yr++) { - uint32_t src_offset = ((yr + y) * width + xr + x) * pixel_size; - uint32_t dst_offset = (yr * region_w + xr) * pixel_size; - //copy block - for (uint32_t i = 0; i < pixel_size; i++) { - write_ptr[dst_offset + i] = read_ptr[src_offset + i]; - } - } - } + // Regular image (pixels). + // Must copy a pixel region. + _copy_region(read_ptr, write_ptr, x, y, region_w, region_h, width, pixel_size); } - { //unmap + { // Unmap. vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation); } VkBufferImageCopy buffer_image_copy; buffer_image_copy.bufferOffset = alloc_offset; - buffer_image_copy.bufferRowLength = 0; //tightly packed - buffer_image_copy.bufferImageHeight = 0; //tightly packed + buffer_image_copy.bufferRowLength = 0; // Tightly packed. + buffer_image_copy.bufferImageHeight = 0; // Tightly packed. buffer_image_copy.imageSubresource.aspectMask = texture->read_aspect_mask; buffer_image_copy.imageSubresource.mipLevel = mm_i; @@ -2560,17 +2604,17 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co logic_height = MAX(1u, logic_height >> 1); } - //barrier to restore layout + // Barrier to restore layout. { uint32_t barrier_flags = 0; uint32_t access_flags = 0; if (p_post_barrier & BARRIER_MASK_COMPUTE) { barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier & BARRIER_MASK_RASTER) { barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - access_flags |= VK_ACCESS_SHADER_READ_BIT; + access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } if (p_post_barrier & BARRIER_MASK_TRANSFER) { barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; @@ -2647,7 +2691,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex const uint8_t *slice_read_ptr = ((uint8_t *)img_mem) + layout.offset + z * layout.depthPitch; if (block_size > 1) { - //compressed + // Compressed. uint32_t line_width = (block_size * (width / blockw)); for (uint32_t y = 0; y < height / blockh; y++) { const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; @@ -2657,7 +2701,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex } } else { - //uncompressed + // Uncompressed. for (uint32_t y = 0; y < height; y++) { const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch; uint8_t *wptr = write_ptr + y * pixel_size * width; @@ -2693,19 +2737,19 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t ERR_FAIL_COND_V(p_layer >= layer_count, Vector<uint8_t>()); if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) { - //does not need anything fancy, map and read. + // Does not need anything fancy, map and read. return _texture_get_data_from_image(tex, tex->image, tex->allocation, p_layer); } else { - //compute total image size + // Compute total image size. uint32_t width, height, depth; uint32_t buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps, &width, &height, &depth); - //allocate buffer - VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; //makes more sense to retrieve + // Allocate buffer. + VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; // Makes more sense to retrieve. Buffer tmp_buffer; _buffer_allocate(&tmp_buffer, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_HOST, VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT); - { //Source image barrier + { // Source image barrier. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2761,7 +2805,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t offset += size; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2876,9 +2920,9 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; { - //PRE Copy the image + // PRE Copy the image. - { //Source + { // Source. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2898,7 +2942,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //Dest + { // Dest. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2919,7 +2963,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - //COPY + // COPY. { VkImageCopy image_copy_region; @@ -2946,7 +2990,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, vkCmdCopyImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); } - // RESTORE LAYOUT for SRC and DST + // RESTORE LAYOUT for SRC and DST. uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -2967,7 +3011,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -2984,10 +3028,10 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //make dst readable + { // Make dst readable. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -3010,6 +3054,13 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, } } + if (dst_tex->used_in_frame != frames_drawn) { + dst_tex->used_in_raster = false; + dst_tex->used_in_compute = false; + dst_tex->used_in_frame = frames_drawn; + } + dst_tex->used_in_transfer = true; + return OK; } @@ -3047,9 +3098,9 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; { - //PRE Copy the image + // PRE Copy the image. - { //Source + { // Source. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3069,7 +3120,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //Dest + { // Dest. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3090,7 +3141,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - //COPY + // COPY. { VkImageResolve image_copy_region; @@ -3117,7 +3168,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdResolveImage(command_buffer, src_tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy_region); } - // RESTORE LAYOUT for SRC and DST + // RESTORE LAYOUT for SRC and DST. uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -3138,7 +3189,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } - { //restore src + { // Restore src. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3158,7 +3209,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } - { //make dst readable + { // Make dst readable. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -3211,13 +3262,13 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, VkImageLayout clear_layout = (src_tex->layout == VK_IMAGE_LAYOUT_GENERAL) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner) + // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner.) const VkPipelineStageFlags valid_texture_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; constexpr VkAccessFlags read_access = VK_ACCESS_SHADER_READ_BIT; constexpr VkAccessFlags read_write_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; const VkAccessFlags valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; - { // Barrier from previous access with optional layout change (see clear_layout logic above) + { // Barrier from previous access with optional layout change (see clear_layout logic above). VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -3253,7 +3304,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, vkCmdClearColorImage(command_buffer, src_tex->image, clear_layout, &clear_color, 1, &range); - { // Barrier to post clear accesses (changing back the layout if needed) + { // Barrier to post clear accesses (changing back the layout if needed). uint32_t barrier_flags = 0; uint32_t access_flags = 0; @@ -3309,7 +3360,7 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f _THREAD_SAFE_METHOD_ - //validate that this image is supported for the intended use + // Validate that this image is supported for the intended use. VkFormatProperties properties; vkGetPhysicalDeviceFormatProperties(context->get_physical_device(), vulkan_formats[p_format], &properties); VkFormatFeatureFlags flags; @@ -3340,6 +3391,11 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f return false; } + // Validation via VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR fails if VRS attachment is not supported. + if (p_usage & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && p_format != DATA_FORMAT_R8_UINT) { + return false; + } + return true; } @@ -3348,22 +3404,39 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f /********************/ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { - // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them + // Set up dependencies from/to external equivalent to the default (implicit) one, and then amend them. const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // From Section 7.1 of Vulkan API Spec v1.1.148 + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | // From Section 7.1 of Vulkan API Spec v1.1.148. + VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR; VkPipelineStageFlags reading_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; - VkSubpassDependency dependencies[2] = { { VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0 }, - { 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0 } }; - VkSubpassDependency &dependency_from_external = dependencies[0]; - VkSubpassDependency &dependency_to_external = dependencies[1]; + VkSubpassDependency2KHR dependencies[2] = { + { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0, 0 }, + { VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, nullptr, 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0, 0 } + }; + VkSubpassDependency2KHR &dependency_from_external = dependencies[0]; + VkSubpassDependency2KHR &dependency_to_external = dependencies[1]; LocalVector<int32_t> attachment_last_pass; attachment_last_pass.resize(p_attachments.size()); - Vector<VkAttachmentDescription> attachments; + if (p_view_count > 1) { + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); + + // This only works with multiview! + ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); + + // Make sure we limit this to the number of views we support. + ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + } + + // These are only used if we use multiview but we need to define them in scope. + const uint32_t view_mask = (1 << p_view_count) - 1; + const uint32_t correlation_mask = (1 << p_view_count) - 1; + + Vector<VkAttachmentDescription2KHR> attachments; Vector<int> attachment_remap; for (int i = 0; i < p_attachments.size(); i++) { @@ -3374,95 +3447,109 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF ERR_FAIL_INDEX_V(p_attachments[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); ERR_FAIL_INDEX_V(p_attachments[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); - ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)), - VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth, input or stencil) bit set."); + ERR_FAIL_COND_V_MSG(!(p_attachments[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT)), + VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (color, depth-stencil, input or VRS) bit set."); - VkAttachmentDescription description = {}; + VkAttachmentDescription2KHR description = {}; + description.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR; + description.pNext = nullptr; description.flags = 0; description.format = vulkan_formats[p_attachments[i].format]; - description.samples = rasterization_sample_count[p_attachments[i].samples]; + description.samples = _ensure_supported_sample_count(p_attachments[i].samples); bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write - // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs. - // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that - // stage - - switch (is_depth ? p_initial_depth_action : p_initial_action) { - case INITIAL_ACTION_CLEAR_REGION: - case INITIAL_ACTION_CLEAR: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_KEEP: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_DROP: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - dependency_from_external.srcStageMask |= reading_stages; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; - } - } break; - case INITIAL_ACTION_CLEAR_REGION_CONTINUE: - case INITIAL_ACTION_CONTINUE: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - dependency_from_external.srcStageMask |= reading_stages; + // We can setup a framebuffer where we write to our VRS texture to set it up. + // We make the assumption here that if our texture is actually used as our VRS attachment. + // It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses. + bool is_vrs = p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT && i == p_passes[0].vrs_attachment; + + if (is_vrs) { + // For VRS we only read, there is no writing to this texture. + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + } else { + // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write. + // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs + // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that + // stage. + + switch (is_depth ? p_initial_depth_action : p_initial_action) { + case INITIAL_ACTION_CLEAR_REGION: + case INITIAL_ACTION_CLEAR: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_KEEP: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_DROP: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + dependency_from_external.srcStageMask |= reading_stages; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + case INITIAL_ACTION_CLEAR_REGION_CONTINUE: + case INITIAL_ACTION_CONTINUE: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + description.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + dependency_from_external.srcStageMask |= reading_stages; + } + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. } - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here } } @@ -3472,20 +3559,25 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF int last_pass = p_passes.size() - 1; if (is_depth) { - //likely missing depth resolve? + // Likely missing depth resolve? if (p_passes[last_pass].depth_attachment == i) { used_last = true; } + } else if (is_vrs) { + if (p_passes[last_pass].vrs_attachment == i) { + used_last = true; + } } else { if (p_passes[last_pass].resolve_attachments.size()) { - //if using resolve attachments, check resolve attachments + // If using resolve attachments, check resolve attachments. for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { if (p_passes[last_pass].resolve_attachments[j] == i) { used_last = true; break; } } - } else { + } + if (!used_last) { for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) { if (p_passes[last_pass].color_attachments[j] == i) { used_last = true; @@ -3517,58 +3609,69 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } } - switch (is_depth ? final_depth_action : final_action) { - case FINAL_ACTION_READ: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - // TODO: What does this mean about the next usage (and thus appropriate dependency masks - } - } break; - case FINAL_ACTION_DISCARD: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - } - } break; - case FINAL_ACTION_CONTINUE: { - if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; - description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } else { - description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there - } + if (is_vrs) { + // We don't change our VRS texture during this process. - } break; - default: { - ERR_FAIL_V(VK_NULL_HANDLE); //should never reach here + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // TODO: Do we need to update our external dependency? + // update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); + } else { + switch (is_depth ? final_depth_action : final_action) { + case FINAL_ACTION_READ: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); + } else { + description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + // TODO: What does this mean about the next usage (and thus appropriate dependency masks. + } + } break; + case FINAL_ACTION_DISCARD: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + } else { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + } + } break; + case FINAL_ACTION_CONTINUE: { + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + description.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. + } + + } break; + default: { + ERR_FAIL_V(VK_NULL_HANDLE); // Should never reach here. + } } } @@ -3577,12 +3680,14 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF attachments.push_back(description); } - LocalVector<VkSubpassDescription> subpasses; - LocalVector<LocalVector<VkAttachmentReference>> color_reference_array; - LocalVector<LocalVector<VkAttachmentReference>> input_reference_array; - LocalVector<LocalVector<VkAttachmentReference>> resolve_reference_array; + LocalVector<VkSubpassDescription2KHR> subpasses; + LocalVector<LocalVector<VkAttachmentReference2KHR>> color_reference_array; + LocalVector<LocalVector<VkAttachmentReference2KHR>> input_reference_array; + LocalVector<LocalVector<VkAttachmentReference2KHR>> resolve_reference_array; LocalVector<LocalVector<uint32_t>> preserve_reference_array; - LocalVector<VkAttachmentReference> depth_reference_array; + LocalVector<VkAttachmentReference2KHR> depth_reference_array; + LocalVector<VkAttachmentReference2KHR> vrs_reference_array; + LocalVector<VkFragmentShadingRateAttachmentInfoKHR> vrs_attachment_info_array; subpasses.resize(p_passes.size()); color_reference_array.resize(p_passes.size()); @@ -3590,20 +3695,25 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF resolve_reference_array.resize(p_passes.size()); preserve_reference_array.resize(p_passes.size()); depth_reference_array.resize(p_passes.size()); + vrs_reference_array.resize(p_passes.size()); + vrs_attachment_info_array.resize(p_passes.size()); - LocalVector<VkSubpassDependency> subpass_dependencies; + LocalVector<VkSubpassDependency2KHR> subpass_dependencies; for (int i = 0; i < p_passes.size(); i++) { const FramebufferPass *pass = &p_passes[i]; - LocalVector<VkAttachmentReference> &color_references = color_reference_array[i]; + LocalVector<VkAttachmentReference2KHR> &color_references = color_reference_array[i]; TextureSamples texture_samples = TEXTURE_SAMPLES_1; bool is_multisample_first = true; + void *subpass_nextptr = nullptr; for (int j = 0; j < pass->color_attachments.size(); j++) { int32_t attachment = pass->color_attachments[j]; - VkAttachmentReference reference; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3622,14 +3732,17 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; color_references.push_back(reference); } - LocalVector<VkAttachmentReference> &input_references = input_reference_array[i]; + LocalVector<VkAttachmentReference2KHR> &input_references = input_reference_array[i]; for (int j = 0; j < pass->input_attachments.size(); j++) { int32_t attachment = pass->input_attachments[j]; - VkAttachmentReference reference; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3641,10 +3754,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; input_references.push_back(reference); } - LocalVector<VkAttachmentReference> &resolve_references = resolve_reference_array[i]; + LocalVector<VkAttachmentReference2KHR> &resolve_references = resolve_reference_array[i]; if (pass->resolve_attachments.size() > 0) { ERR_FAIL_COND_V_MSG(pass->resolve_attachments.size() != pass->color_attachments.size(), VK_NULL_HANDLE, "The amount of resolve attachments (" + itos(pass->resolve_attachments.size()) + ") must match the number of color attachments (" + itos(pass->color_attachments.size()) + ")."); @@ -3652,7 +3766,9 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } for (int j = 0; j < pass->resolve_attachments.size(); j++) { int32_t attachment = pass->resolve_attachments[j]; - VkAttachmentReference reference; + VkAttachmentReference2KHR reference; + reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + reference.pNext = nullptr; if (attachment == FramebufferPass::ATTACHMENT_UNUSED) { reference.attachment = VK_ATTACHMENT_UNUSED; reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -3664,13 +3780,16 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF bool multisample = p_attachments[attachment].samples > TEXTURE_SAMPLES_1; ERR_FAIL_COND_V_MSG(multisample, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachments can't be multisample."); reference.attachment = attachment_remap[attachment]; - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL attachment_last_pass[attachment] = i; } + reference.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; resolve_references.push_back(reference); } - VkAttachmentReference &depth_stencil_reference = depth_reference_array[i]; + VkAttachmentReference2KHR &depth_stencil_reference = depth_reference_array[i]; + depth_stencil_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + depth_stencil_reference.pNext = nullptr; if (pass->depth_attachment != FramebufferPass::ATTACHMENT_UNUSED) { int32_t attachment = pass->depth_attachment; @@ -3679,6 +3798,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); depth_stencil_reference.attachment = attachment_remap[attachment]; depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depth_stencil_reference.aspectMask = VK_IMAGE_ASPECT_NONE; attachment_last_pass[attachment] = i; if (is_multisample_first) { @@ -3693,6 +3813,32 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED; } + if (context->get_vrs_capabilities().attachment_vrs_supported && pass->vrs_attachment != FramebufferPass::ATTACHMENT_UNUSED) { + int32_t attachment = pass->vrs_attachment; + ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), VRS attachment."); + ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer VRS format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as VRS, but it's not a VRS attachment."); + ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer VRS attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); + + VkAttachmentReference2KHR &vrs_reference = vrs_reference_array[i]; + vrs_reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + vrs_reference.pNext = nullptr; + vrs_reference.attachment = attachment_remap[attachment]; + vrs_reference.layout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR; + vrs_reference.aspectMask = VK_IMAGE_ASPECT_NONE; + + Size2i texel_size = context->get_vrs_capabilities().max_texel_size; + + VkFragmentShadingRateAttachmentInfoKHR &vrs_attachment_info = vrs_attachment_info_array[i]; + vrs_attachment_info.sType = VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR; + vrs_attachment_info.pNext = nullptr; + vrs_attachment_info.pFragmentShadingRateAttachment = &vrs_reference; + vrs_attachment_info.shadingRateAttachmentTexelSize = { uint32_t(texel_size.x), uint32_t(texel_size.y) }; + + attachment_last_pass[attachment] = i; + + subpass_nextptr = &vrs_attachment_info; + } + LocalVector<uint32_t> &preserve_references = preserve_reference_array[i]; for (int j = 0; j < pass->preserve_attachments.size(); j++) { @@ -3703,15 +3849,23 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + ")."); if (attachment_last_pass[attachment] != i) { - //preserve can still be used to keep depth or color from being discarded after use + // Preserve can still be used to keep depth or color from being discarded after use. attachment_last_pass[attachment] = i; preserve_references.push_back(attachment); } } - VkSubpassDescription &subpass = subpasses[i]; + VkSubpassDescription2KHR &subpass = subpasses[i]; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + subpass.pNext = subpass_nextptr; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + if (p_view_count == 1) { + // VUID-VkSubpassDescription2-multiview-06558: If the multiview feature is not enabled, viewMask must be 0. + subpass.viewMask = 0; + } else { + subpass.viewMask = view_mask; + } subpass.inputAttachmentCount = input_references.size(); if (input_references.size()) { subpass.pInputAttachments = input_references.ptr(); @@ -3748,7 +3902,9 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } if (i > 0) { - VkSubpassDependency dependency; + VkSubpassDependency2KHR dependency; + dependency.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR; + dependency.pNext = nullptr; dependency.srcSubpass = i - 1; dependency.dstSubpass = i; dependency.srcStageMask = 0; @@ -3758,27 +3914,29 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependency.viewOffset = 0; subpass_dependencies.push_back(dependency); } /* - // NOTE: Big Mallet Approach -- any layout transition causes a full barrier + // NOTE: Big Mallet Approach -- any layout transition causes a full barrier. if (reference.layout != description.initialLayout) { - // NOTE: this should be smarter based on the texture's knowledge of its previous role + // NOTE: This should be smarter based on the texture's knowledge of its previous role. dependency_from_external.srcStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_from_external.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } if (reference.layout != description.finalLayout) { - // NOTE: this should be smarter based on the texture's knowledge of its subsequent role + // NOTE: This should be smarter based on the texture's knowledge of its subsequent role. dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dependency_to_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; } */ } - VkRenderPassCreateInfo render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + VkRenderPassCreateInfo2KHR render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; + render_pass_create_info.attachmentCount = attachments.size(); render_pass_create_info.pAttachments = attachments.ptr(); render_pass_create_info.subpassCount = subpasses.size(); @@ -3795,22 +3953,23 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF render_pass_create_info.pDependencies = nullptr; } - // These are only used if we use multiview but we need to define them in scope. - const uint32_t view_mask = (1 << p_view_count) - 1; - const uint32_t correlation_mask = (1 << p_view_count) - 1; + if (p_view_count == 1) { + // VUID-VkRenderPassCreateInfo2-viewMask-03057: If the VkSubpassDescription2::viewMask member of all elements of pSubpasses is 0, correlatedViewMaskCount must be 0. + render_pass_create_info.correlatedViewMaskCount = 0; + render_pass_create_info.pCorrelatedViewMasks = nullptr; + } else { + render_pass_create_info.correlatedViewMaskCount = 1; + render_pass_create_info.pCorrelatedViewMasks = &correlation_mask; + } + Vector<uint32_t> view_masks; VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info; - if (p_view_count > 1) { - const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); - - // For now this only works with multiview! - ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported"); - - // Make sure we limit this to the number of views we support. - ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass"); + if ((p_view_count > 1) && !context->supports_renderpass2()) { + // This is only required when using vkCreateRenderPass, we add it if vkCreateRenderPass2KHR is not supported + // resulting this in being passed to our vkCreateRenderPass fallback. - // Set view masks for each subpass + // Set view masks for each subpass. for (uint32_t i = 0; i < subpasses.size(); i++) { view_masks.push_back(view_mask); } @@ -3828,8 +3987,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } VkRenderPass render_pass; - VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); - ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); + VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); + ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass2KHR failed with error " + itos(res) + "."); return render_pass; } @@ -3848,7 +4007,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c passes.push_back(pass); return framebuffer_format_create_multipass(p_format, passes, p_view_count); } -RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { +RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ FramebufferFormatKey key; @@ -3858,14 +4017,14 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { - //exists, return + // Exists, return. return E->get(); } Vector<TextureSamples> samples; - VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); //actions don't matter for this use case + VkRenderPass render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); // Actions don't matter for this use case. - if (render_pass == VK_NULL_HANDLE) { //was likely invalid + if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. return INVALID_ID; } FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); @@ -3886,14 +4045,17 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); if (E) { - //exists, return + // Exists, return. return E->get(); } - VkSubpassDescription subpass; + VkSubpassDescription2KHR subpass; + subpass.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + subpass.pNext = nullptr; subpass.flags = 0; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.inputAttachmentCount = 0; //unsupported for now + subpass.viewMask = 0; + subpass.inputAttachmentCount = 0; // Unsupported for now. subpass.pInputAttachments = nullptr; subpass.colorAttachmentCount = 0; subpass.pColorAttachments = nullptr; @@ -3902,8 +4064,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; - VkRenderPassCreateInfo render_pass_create_info; - render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + VkRenderPassCreateInfo2KHR render_pass_create_info; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; render_pass_create_info.pNext = nullptr; render_pass_create_info.flags = 0; render_pass_create_info.attachmentCount = 0; @@ -3912,13 +4074,15 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c render_pass_create_info.pSubpasses = &subpass; render_pass_create_info.dependencyCount = 0; render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.correlatedViewMaskCount = 0; + render_pass_create_info.pCorrelatedViewMasks = nullptr; VkRenderPass render_pass; - VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); + VkResult res = context->vkCreateRenderPass2KHR(device, &render_pass_create_info, nullptr, &render_pass); - ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass for empty fb failed with error " + itos(res) + "."); + ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass2KHR for empty fb failed with error " + itos(res) + "."); - if (render_pass == VK_NULL_HANDLE) { //was likely invalid + if (render_pass == VK_NULL_HANDLE) { // Was likely invalid. return INVALID_ID; } @@ -3954,7 +4118,11 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur framebuffer.size = p_size; framebuffer.view_count = 1; - return framebuffer_owner.make_rid(framebuffer); + RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) { @@ -3969,8 +4137,14 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac if (texture && texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { pass.depth_attachment = i; + } else if (texture && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + pass.vrs_attachment = i; } else { - pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); + if (texture && texture->is_resolve_buffer) { + pass.resolve_attachments.push_back(i); + } else { + pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); + } } } @@ -3980,7 +4154,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac return framebuffer_create_multipass(p_texture_attachments, passes, p_format_check, p_view_count); } -RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { +RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ Vector<AttachmentFormat> attachments; @@ -3999,6 +4173,10 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex size.width = texture->width; size.height = texture->height; size_set = true; + } else if (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { + // If this is not the first attachment we assume this is used as the VRS attachment. + // In this case this texture will be 1/16th the size of the color attachment. + // So we skip the size check. } else { ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), "All textures in a framebuffer should be the same size."); @@ -4028,6 +4206,9 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex framebuffer.view_count = p_view_count; RID id = framebuffer_owner.make_rid(framebuffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif for (int i = 0; i < p_texture_attachments.size(); i++) { if (p_texture_attachments[i].is_valid()) { @@ -4047,6 +4228,22 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_form return framebuffer->format_id; } +bool RenderingDeviceVulkan::framebuffer_is_valid(RID p_framebuffer) const { + _THREAD_SAFE_METHOD_ + + return framebuffer_owner.owns(p_framebuffer); +} + +void RenderingDeviceVulkan::framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata) { + _THREAD_SAFE_METHOD_ + + Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); + ERR_FAIL_COND(!framebuffer); + + framebuffer->invalidated_callback = p_callback; + framebuffer->invalidated_callback_userdata = p_userdata; +} + /*****************/ /**** SAMPLER ****/ /*****************/ @@ -4089,7 +4286,11 @@ RID RenderingDeviceVulkan::sampler_create(const SamplerState &p_state) { VkResult res = vkCreateSampler(device, &sampler_create_info, nullptr, &sampler); ERR_FAIL_COND_V_MSG(res, RID(), "vkCreateSampler failed with error " + itos(res) + "."); - return sampler_owner.make_rid(sampler); + RID id = sampler_owner.make_rid(sampler); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } /**********************/ @@ -4118,10 +4319,14 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, false); } - return vertex_buffer_owner.make_rid(buffer); + RID id = vertex_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } -// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated +// Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. RenderingDevice::VertexFormatID RenderingDeviceVulkan::vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats) { _THREAD_SAFE_METHOD_ @@ -4133,7 +4338,7 @@ RenderingDevice::VertexFormatID RenderingDeviceVulkan::vertex_format_create(cons return *idptr; } - //does not exist, create one and cache it + // Does not exist, create one and cache it. VertexDescriptionCache vdcache; vdcache.bindings = memnew_arr(VkVertexInputBindingDescription, p_vertex_formats.size()); vdcache.attributes = memnew_arr(VkVertexInputAttributeDescription, p_vertex_formats.size()); @@ -4189,25 +4394,25 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo vertex_array.vertex_count = p_vertex_count; vertex_array.description = p_vertex_format; - vertex_array.max_instances_allowed = 0xFFFFFFFF; //by default as many as you want + vertex_array.max_instances_allowed = 0xFFFFFFFF; // By default as many as you want. for (int i = 0; i < p_src_buffers.size(); i++) { Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]); - //validate with buffer + // Validate with buffer. { const VertexAttribute &atf = vd.vertex_formats[i]; uint32_t element_size = get_format_vertex_size(atf.format); - ERR_FAIL_COND_V(element_size == 0, RID()); //should never happens since this was prevalidated + ERR_FAIL_COND_V(element_size == 0, RID()); // Should never happens since this was prevalidated. if (atf.frequency == VERTEX_FREQUENCY_VERTEX) { - //validate size for regular drawing + // Validate size for regular drawing. uint64_t total_size = uint64_t(atf.stride) * (p_vertex_count - 1) + atf.offset + element_size; ERR_FAIL_COND_V_MSG(total_size > buffer->size, RID(), "Attachment (" + itos(i) + ") will read past the end of the buffer."); } else { - //validate size for instances drawing + // Validate size for instances drawing. uint64_t available = buffer->size - atf.offset; ERR_FAIL_COND_V_MSG(available < element_size, RID(), "Attachment (" + itos(i) + ") uses instancing, but it's just too small."); @@ -4218,7 +4423,7 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo } vertex_array.buffers.push_back(buffer->buffer); - vertex_array.offsets.push_back(0); //offset unused, but passing anyway + vertex_array.offsets.push_back(0); // Offset unused, but passing anyway. } RID id = vertex_array_owner.make_rid(vertex_array); @@ -4253,7 +4458,7 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff const uint16_t *index16 = (const uint16_t *)r; for (uint32_t i = 0; i < p_index_count; i++) { if (p_use_restart_indices && index16[i] == 0xFFFF) { - continue; //restart index, ignore + continue; // Restart index, ignore. } index_buffer.max_index = MAX(index16[i], index_buffer.max_index); } @@ -4261,7 +4466,7 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff const uint32_t *index32 = (const uint32_t *)r; for (uint32_t i = 0; i < p_index_count; i++) { if (p_use_restart_indices && index32[i] == 0xFFFFFFFF) { - continue; //restart index, ignore + continue; // Restart index, ignore. } index_buffer.max_index = MAX(index32[i], index_buffer.max_index); } @@ -4279,7 +4484,11 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff _buffer_update(&index_buffer, 0, r, data_size); _buffer_memory_barrier(index_buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_INDEX_READ_BIT, false); } - return index_buffer_owner.make_rid(index_buffer); + RID id = index_buffer_owner.make_rid(index_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_index_offset, uint32_t p_index_count) { @@ -4342,7 +4551,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { if (!ret.is_empty()) { ret += "\n"; } - ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Length: " + itos(ui.length); + ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Writable: " + (ui.writable ? "Y" : "N") + " Length: " + itos(ui.length); } } return ret; @@ -4356,7 +4565,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa case glslang::EbtSampler: { //print_line("DEBUG: IsSampler"); if (reflection.getType()->getSampler().dim == glslang::EsdBuffer) { - //texture buffers + // Texture buffers. if (reflection.getType()->getSampler().isCombined()) { layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER; @@ -4456,7 +4665,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } break;*/ default: { if (reflection.getType()->getQualifier().hasOffset() || reflection.name.find(".") != std::string::npos) { - //member of uniform block? + // Member of uniform block? return true; } @@ -4493,10 +4702,10 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa uint32_t binding = reflection.getType()->getQualifier().layoutBinding; if (set < (uint32_t)bindings.size()) { - //check if this already exists + // Check if this already exists. for (int i = 0; i < bindings[set].size(); i++) { if (bindings[set][i].binding == binding) { - //already exists, verify that it's the same type + // Already exists, verify that it's the same type. if (bindings[set][i].descriptorType != layout_binding.descriptorType) { if (r_error) { *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform type."; @@ -4504,7 +4713,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa return false; } - //also, verify that it's the same size + // Also, verify that it's the same size. if (bindings[set][i].descriptorCount != layout_binding.descriptorCount || uniform_infos[set][i].length != info.length) { if (r_error) { *r_error = "On shader stage '" + String(shader_stage_names[p_stage]) + "', uniform '" + reflection.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(binding) + " with different uniform size."; @@ -4512,7 +4721,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa return false; } - //just append stage mask and return + // Just append stage mask and return. bindings.write[set].write[i].stageFlags |= shader_stage_masks[p_stage]; uniform_infos.write[set].write[i].stages |= 1 << p_stage; return true; @@ -4521,7 +4730,7 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } layout_binding.binding = binding; layout_binding.stageFlags = shader_stage_masks[p_stage]; - layout_binding.pImmutableSamplers = nullptr; //no support for this yet + layout_binding.pImmutableSamplers = nullptr; // No support for this yet. info.stages = 1 << p_stage; info.binding = binding; @@ -4540,20 +4749,22 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa } #endif -//version 1: initial -//version 2: Added shader name +// Version 1: initial. +// Version 2: Added shader name. +// Version 3: Added writable. -#define SHADER_BINARY_VERSION 2 +#define SHADER_BINARY_VERSION 3 String RenderingDeviceVulkan::shader_get_binary_cache_key() const { - return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); + return "Vulkan-SV" + itos(SHADER_BINARY_VERSION) + "-" + String(VERSION_NUMBER) + "-" + String(VERSION_HASH); } struct RenderingDeviceVulkanShaderBinaryDataBinding { uint32_t type; uint32_t binding; uint32_t stages; - uint32_t length; //size of arrays (in total elements), or ubos (in bytes * total elements) + uint32_t length; // Size of arrays (in total elements), or ubos (in bytes * total elements). + uint32_t writable; }; struct RenderingDeviceVulkanShaderBinarySpecializationConstant { @@ -4593,7 +4804,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve binary_data.push_constant_size = 0; binary_data.push_constants_vk_stage = 0; - Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; //set bindings + Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; // Set bindings. Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants; uint32_t stages_processed = 0; @@ -4627,7 +4838,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve uint32_t stage = p_spirv[i].shader_stage; if (binding_count > 0) { - //Parse bindings + // Parse bindings. Vector<SpvReflectDescriptorBinding *> bindings; bindings.resize(binding_count); @@ -4639,10 +4850,11 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve for (uint32_t j = 0; j < binding_count; j++) { const SpvReflectDescriptorBinding &binding = *bindings[j]; - RenderingDeviceVulkanShaderBinaryDataBinding info; + RenderingDeviceVulkanShaderBinaryDataBinding info{}; bool need_array_dimensions = false; bool need_block_size = false; + bool may_be_writable = false; switch (binding.descriptor_type) { case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: { @@ -4660,6 +4872,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: { info.type = UNIFORM_TYPE_IMAGE; need_array_dimensions = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: { info.type = UNIFORM_TYPE_TEXTURE_BUFFER; @@ -4668,6 +4881,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { info.type = UNIFORM_TYPE_IMAGE_BUFFER; need_array_dimensions = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { info.type = UNIFORM_TYPE_UNIFORM_BUFFER; @@ -4676,6 +4890,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: { info.type = UNIFORM_TYPE_STORAGE_BUFFER; need_block_size = true; + may_be_writable = true; } break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: { ERR_PRINT("Dynamic uniform buffer not supported."); @@ -4714,6 +4929,12 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve info.length = 0; } + if (may_be_writable) { + info.writable = !(binding.type_description->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) && !(binding.block.decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE); + } else { + info.writable = false; + } + info.binding = binding.binding; uint32_t set = binding.set; @@ -4724,26 +4945,31 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported by the hardware (" + itos(limits.maxBoundDescriptorSets) + ")."); if (set < (uint32_t)uniform_info.size()) { - //check if this already exists + // Check if this already exists. bool exists = false; for (int k = 0; k < uniform_info[set].size(); k++) { if (uniform_info[set][k].binding == (uint32_t)info.binding) { - //already exists, verify that it's the same type + // Already exists, verify that it's the same type. ERR_FAIL_COND_V_MSG(uniform_info[set][k].type != info.type, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform type."); - //also, verify that it's the same size + // Also, verify that it's the same size. ERR_FAIL_COND_V_MSG(uniform_info[set][k].length != info.length, Vector<uint8_t>(), "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different uniform size."); - //just append stage mask and return + // Also, verify that it has the same writability. + ERR_FAIL_COND_V_MSG(uniform_info[set][k].writable != info.writable, Vector<uint8_t>(), + "On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' trying to re-use location for set=" + itos(set) + ", binding=" + itos(info.binding) + " with different writability."); + + // Just append stage mask and return. uniform_info.write[set].write[k].stages |= 1 << stage; exists = true; + break; } } if (exists) { - continue; //merged + continue; // Merged. } } @@ -4758,7 +4984,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve } { - //specialization constants + // Specialization constants. uint32_t sc_count = 0; result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr); @@ -4775,11 +5001,11 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve for (uint32_t j = 0; j < sc_count; j++) { int32_t existing = -1; - RenderingDeviceVulkanShaderBinarySpecializationConstant sconst; + RenderingDeviceVulkanShaderBinarySpecializationConstant sconst{}; SpvReflectSpecializationConstant *spc = spec_constants[j]; sconst.constant_id = spc->constant_id; - sconst.int_value = 0.0; // clear previous value JIC + sconst.int_value = 0.0; // Clear previous value JIC. switch (spc->constant_type) { case SPV_REFLECT_SPECIALIZATION_CONSTANT_BOOL: { sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_BOOL; @@ -4829,7 +5055,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining input variables."); for (uint32_t j = 0; j < iv_count; j++) { - if (input_vars[j] && input_vars[j]->decoration_flags == 0) { //regular input + if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input. binary_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location)); } } @@ -4898,7 +5124,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve Vector<Vector<uint8_t>> compressed_stages; Vector<uint32_t> smolv_size; - Vector<uint32_t> zstd_size; //if 0, stdno t used + Vector<uint32_t> zstd_size; // If 0, zstd not used. uint32_t stages_binary_size = 0; @@ -4910,7 +5136,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve ERR_FAIL_V_MSG(Vector<uint8_t>(), "Error compressing shader stage :" + String(shader_stage_names[p_spirv[i].shader_stage])); } else { smolv_size.push_back(smolv.size()); - { //zstd + { // zstd. Vector<uint8_t> zstd; zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD)); int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD); @@ -4923,7 +5149,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve Vector<uint8_t> smv; smv.resize(smolv.size()); memcpy(smv.ptrw(), &smolv[0], smolv.size()); - zstd_size.push_back(0); //not using zstd + zstd_size.push_back(0); // Not using zstd. compressed_stages.push_back(smv); } } @@ -4943,12 +5169,12 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve binary_data.shader_name_len = shader_name_utf.length(); - uint32_t total_size = sizeof(uint32_t) * 3; //header + version + main datasize; + uint32_t total_size = sizeof(uint32_t) * 3; // Header + version + main datasize;. total_size += sizeof(RenderingDeviceVulkanShaderBinaryData); total_size += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. total_size += 4 - (binary_data.shader_name_len % 4); } @@ -4959,7 +5185,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve total_size += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size(); - total_size += compressed_stages.size() * sizeof(uint32_t) * 3; //sizes + total_size += compressed_stages.size() * sizeof(uint32_t) * 3; // Sizes. total_size += stages_binary_size; Vector<uint8_t> ret; @@ -4968,9 +5194,9 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve uint32_t offset = 0; uint8_t *binptr = ret.ptrw(); binptr[0] = 'G'; - binptr[1] = 'V'; + binptr[1] = 'S'; binptr[2] = 'B'; - binptr[3] = 'D'; //godot vulkan binary data + binptr[3] = 'D'; // Godot Shader Binary Data. offset += 4; encode_uint32(SHADER_BINARY_VERSION, binptr + offset); offset += sizeof(uint32_t); @@ -4981,7 +5207,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve memcpy(binptr + offset, shader_name_utf.ptr(), binary_data.shader_name_len); offset += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. offset += 4 - (binary_data.shader_name_len % 4); } @@ -5029,12 +5255,12 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ uint32_t binsize = p_shader_binary.size(); uint32_t read_offset = 0; - //consistency check + // Consistency check. ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(RenderingDeviceVulkanShaderBinaryData), RID()); - ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'V' || binptr[2] != 'B' || binptr[3] != 'D', RID()); + ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'S' || binptr[2] != 'B' || binptr[3] != 'D', RID()); uint32_t bin_version = decode_uint32(binptr + 4); - ERR_FAIL_COND_V(bin_version > SHADER_BINARY_VERSION, RID()); + ERR_FAIL_COND_V(bin_version != SHADER_BINARY_VERSION, RID()); uint32_t bin_data_size = decode_uint32(binptr + 8); @@ -5059,7 +5285,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ if (binary_data.shader_name_len) { name.parse_utf8((const char *)(binptr + read_offset), binary_data.shader_name_len); read_offset += binary_data.shader_name_len; - if ((binary_data.shader_name_len % 4) != 0) { //alignment rules are really strange + if ((binary_data.shader_name_len % 4) != 0) { // Alignment rules are really strange. read_offset += 4 - (binary_data.shader_name_len % 4); } } @@ -5081,6 +5307,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ for (uint32_t j = 0; j < set_count; j++) { UniformInfo info; info.type = UniformType(set_ptr[j].type); + info.writable = set_ptr[j].writable; info.length = set_ptr[j].length; info.binding = set_ptr[j].binding; info.stages = set_ptr[j].stages; @@ -5175,7 +5402,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ const uint8_t *src_smolv = nullptr; if (zstd_size > 0) { - //decompress to smolv + // Decompress to smolv. smolv.resize(smolv_size); int dec_smolv_size = Compression::decompress(smolv.ptrw(), smolv.size(), binptr + read_offset, zstd_size, Compression::MODE_ZSTD); ERR_FAIL_COND_V(dec_smolv_size != (int32_t)smolv_size, RID()); @@ -5204,7 +5431,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_COND_V(read_offset != binsize, RID()); - //all good, let's create modules + // All good, let's create modules. _THREAD_SAFE_METHOD_ @@ -5260,11 +5487,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ shader.pipeline_stages.push_back(shader_stage); } - //proceed to create descriptor sets + // Proceed to create descriptor sets. if (success) { for (int i = 0; i < set_bindings.size(); i++) { - //empty ones are fine if they were not used according to spec (binding count will be 0) + // Empty ones are fine if they were not used according to spec (binding count will be 0). VkDescriptorSetLayoutCreateInfo layout_create_info; layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_create_info.pNext = nullptr; @@ -5283,13 +5510,13 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ Shader::Set set; set.descriptor_set_layout = layout; set.uniform_info = uniform_info[i]; - //sort and hash + // Sort and hash. set.uniform_info.sort(); - uint32_t format = 0; //no format, default + uint32_t format = 0; // No format, default. if (set.uniform_info.size()) { - //has data, needs an actual format; + // Has data, needs an actual format. UniformSetFormat usformat; usformat.uniform_info = set.uniform_info; RBMap<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); @@ -5307,7 +5534,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } if (success) { - //create pipeline layout + // Create pipeline layout. VkPipelineLayoutCreateInfo pipeline_layout_create_info; pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_create_info.pNext = nullptr; @@ -5346,7 +5573,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ } if (!success) { - //clean up if failed + // Clean up if failed. for (int i = 0; i < shader.pipeline_stages.size(); i++) { vkDestroyShaderModule(device, shader.pipeline_stages[i].module, nullptr); } @@ -5358,7 +5585,11 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_V_MSG(RID(), error_text); } - return shader_owner.make_rid(shader); + RID id = shader_owner.make_rid(shader); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) { @@ -5391,7 +5622,11 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve _buffer_update(&buffer, 0, r, data_size); _buffer_memory_barrier(buffer.buffer, 0, data_size, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT, false); } - return uniform_buffer_owner.make_rid(buffer); + RID id = uniform_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, uint32_t p_usage) { @@ -5404,7 +5639,6 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); Buffer buffer; - buffer.usage = p_usage; uint32_t flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; if (p_usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT) { flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; @@ -5462,8 +5696,12 @@ RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataF ERR_FAIL_V_MSG(RID(), "Unable to create buffer view, error " + itos(res) + "."); } - //allocate the view - return texture_buffer_owner.make_rid(texture_buffer); + // Allocate the view. + RID id = texture_buffer_owner.make_rid(texture_buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_allocate(const DescriptorPoolKey &p_key) { @@ -5481,17 +5719,17 @@ RenderingDeviceVulkan::DescriptorPool *RenderingDeviceVulkan::_descriptor_pool_a } if (!pool) { - //create a new one + // Create a new one. pool = memnew(DescriptorPool); pool->usage = 0; VkDescriptorPoolCreateInfo descriptor_pool_create_info; descriptor_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool_create_info.pNext = nullptr; - descriptor_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // can't think how somebody may NOT need this flag.. + descriptor_pool_create_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; // Can't think how somebody may NOT need this flag. descriptor_pool_create_info.maxSets = max_descriptors_per_pool; Vector<VkDescriptorPoolSize> sizes; - //here comes more vulkan API strangeness + // Here comes more vulkan API strangeness. if (p_key.uniform_type[UNIFORM_TYPE_SAMPLER]) { VkDescriptorPoolSize s; @@ -5591,7 +5829,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, RID(), "Desired set (" + itos(p_shader_set) + ") not used by shader."); - //see that all sets in shader are satisfied + // See that all sets in shader are satisfied. const Shader::Set &set = shader->sets[p_shader_set]; @@ -5604,11 +5842,11 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, Vector<VkWriteDescriptorSet> writes; DescriptorPoolKey pool_key; - //to keep them alive until update call + // To keep them alive until update call. List<Vector<VkDescriptorBufferInfo>> buffer_infos; List<Vector<VkBufferView>> buffer_views; List<Vector<VkDescriptorImageInfo>> image_infos; - //used for verification to make sure a uniform set does not use a framebuffer bound texture + // Used for verification to make sure a uniform set does not use a framebuffer bound texture. LocalVector<UniformSet::AttachableTexture> attachable_textures; Vector<Texture *> mutable_sampled_textures; Vector<Texture *> mutable_storage_textures; @@ -5629,14 +5867,14 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, ERR_FAIL_COND_V_MSG(uniform.uniform_type != set_uniform.type, RID(), "Mismatch uniform type for binding (" + itos(set_uniform.binding) + "), set (" + itos(p_shader_set) + "). Expected '" + shader_uniform_names[set_uniform.type] + "', supplied: '" + shader_uniform_names[uniform.uniform_type] + "'."); - VkWriteDescriptorSet write; //common header + VkWriteDescriptorSet write; // Common header. write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write.pNext = nullptr; - write.dstSet = VK_NULL_HANDLE; //will assign afterwards when everything is valid + write.dstSet = VK_NULL_HANDLE; // Will assign afterwards when everything is valid. write.dstBinding = set_uniform.binding; write.dstArrayElement = 0; write.descriptorCount = 0; - write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; //Invalid value. + write.descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM; // Invalid value. write.pImageInfo = nullptr; write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; @@ -5709,12 +5947,12 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_sampled_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -5762,13 +6000,13 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } if (texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_sampled_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -5810,13 +6048,13 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, img_info.imageView = texture->view; if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - //can also be used as storage, add to mutable sampled + // Can also be used as storage, add to mutable sampled. mutable_storage_textures.push_back(texture); } if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; @@ -5906,7 +6144,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_IMAGE_BUFFER: { - //todo + // Todo. } break; case UNIFORM_TYPE_UNIFORM_BUFFER: { @@ -5942,7 +6180,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } ERR_FAIL_COND_V_MSG(!buffer, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); - //if 0, then it's sized on link time + // If 0, then it's sized on link time. ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); @@ -5981,7 +6219,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, if (texture->owner.is_valid()) { texture = texture_owner.get_or_null(texture->owner); - ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen + ERR_FAIL_COND_V(!texture, RID()); // Bug, should never happen. } img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6009,7 +6247,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, pool_key.uniform_type[set_uniform.type] += type_size; } - //need a descriptor pool + // Need a descriptor pool. DescriptorPool *pool = _descriptor_pool_allocate(pool_key); ERR_FAIL_COND_V(!pool, RID()); @@ -6026,7 +6264,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, VkResult res = vkAllocateDescriptorSets(device, &descriptor_set_allocate_info, &descriptor_set); if (res) { - _descriptor_pool_free(pool_key, pool); // meh + _descriptor_pool_free(pool_key, pool); // Meh. ERR_FAIL_V_MSG(RID(), "Cannot allocate descriptor sets, error " + itos(res) + "."); } @@ -6042,7 +6280,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, uniform_set.shader_id = p_shader; RID id = uniform_set_owner.make_rid(uniform_set); - //add dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Add dependencies. _add_dependency(id, p_shader); for (uint32_t i = 0; i < uniform_count; i++) { const Uniform &uniform = uniforms[i]; @@ -6052,7 +6293,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } } - //write the contents + // Write the contents. if (writes.size()) { for (int i = 0; i < writes.size(); i++) { writes.write[i].dstSet = descriptor_set; @@ -6067,7 +6308,7 @@ bool RenderingDeviceVulkan::uniform_set_is_valid(RID p_uniform_set) { return uniform_set_owner.owns(p_uniform_set); } -void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) { +void RenderingDeviceVulkan::uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata) { UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set); ERR_FAIL_COND(!us); us->invalidated_callback = p_callback; @@ -6085,7 +6326,7 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint VkPipelineStageFlags dst_stage_mask = 0; VkAccessFlags dst_access = 0; if (p_post_barrier & BARRIER_MASK_TRANSFER) { - // Protect subsequent updates... + // Protect subsequent updates. dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; } @@ -6097,7 +6338,7 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - // no barrier should be needed here + // No barrier should be needed here. // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, true); Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_post_barrier); @@ -6133,7 +6374,7 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 VkPipelineStageFlags dst_stage_mask = 0; VkAccessFlags dst_access = 0; if (p_post_barrier & BARRIER_MASK_TRANSFER) { - // Protect subsequent updates... + // Protect subsequent updates. dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; } @@ -6146,7 +6387,7 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - // should not be needed + // Should not be needed. // _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, p_post_barrier); vkCmdFillBuffer(frames[frame].draw_command_buffer, buffer->buffer, p_offset, p_size, 0); @@ -6167,10 +6408,10 @@ Error RenderingDeviceVulkan::buffer_clear(RID p_buffer, uint32_t p_offset, uint3 Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { _THREAD_SAFE_METHOD_ - // It could be this buffer was just created + // It could be this buffer was just created. VkPipelineShaderStageCreateFlags src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; VkAccessFlags src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; - // Get the vulkan buffer and the potential stage/access possible + // Get the vulkan buffer and the potential stage/access possible. Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stage_mask, src_access_mask, BARRIER_MASK_ALL); if (!buffer) { ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); @@ -6187,8 +6428,8 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { region.srcOffset = 0; region.dstOffset = 0; region.size = buffer->size; - vkCmdCopyBuffer(command_buffer, buffer->buffer, tmp_buffer.buffer, 1, ®ion); //dst buffer is in CPU, but I wonder if src buffer needs a barrier for this.. - //flush everything so memory can be safely mapped + vkCmdCopyBuffer(command_buffer, buffer->buffer, tmp_buffer.buffer, 1, ®ion); // Dst buffer is in CPU, but I wonder if src buffer needs a barrier for this. + // Flush everything so memory can be safely mapped. _flush(true); void *buffer_mem; @@ -6216,7 +6457,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const PipelineRasterizationState &p_rasterization_state, const PipelineMultisampleState &p_multisample_state, const PipelineDepthStencilState &p_depth_stencil_state, const PipelineColorBlendState &p_blend_state, int p_dynamic_state_flags, uint32_t p_for_render_pass, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ - //needs a shader + // Needs a shader. Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); @@ -6224,13 +6465,13 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma "Compute shaders can't be used in render pipelines"); if (p_framebuffer_format == INVALID_ID) { - //if nothing provided, use an empty one (no attachments) + // If nothing provided, use an empty one (no attachments). p_framebuffer_format = framebuffer_format_create(Vector<AttachmentFormat>()); } ERR_FAIL_COND_V(!framebuffer_formats.has(p_framebuffer_format), RID()); const FramebufferFormat &fb_format = framebuffer_formats[p_framebuffer_format]; - { //validate shader vs framebuffer + { // Validate shader vs framebuffer. ERR_FAIL_COND_V_MSG(p_for_render_pass >= uint32_t(fb_format.E->key().passes.size()), RID(), "Render pass requested for pipeline creation (" + itos(p_for_render_pass) + ") is out of bounds"); const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; @@ -6243,17 +6484,17 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_COND_V_MSG(shader->fragment_output_mask != output_mask, RID(), "Mismatch fragment shader output mask (" + itos(shader->fragment_output_mask) + ") and framebuffer color output mask (" + itos(output_mask) + ") when binding both in render pipeline."); } - //vertex + // Vertex. VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info; if (p_vertex_format != INVALID_ID) { - //uses vertices, else it does not + // Uses vertices, else it does not. ERR_FAIL_COND_V(!vertex_formats.has(p_vertex_format), RID()); const VertexDescriptionCache &vd = vertex_formats[p_vertex_format]; pipeline_vertex_input_state_create_info = vd.create_info; - //validate with inputs + // Validate with inputs. for (uint32_t i = 0; i < 32; i++) { if (!(shader->vertex_input_mask & (1UL << i))) { continue; @@ -6270,7 +6511,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma } } else { - //does not use vertices + // Does not use vertices. pipeline_vertex_input_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; pipeline_vertex_input_state_create_info.pNext = nullptr; pipeline_vertex_input_state_create_info.flags = 0; @@ -6282,7 +6523,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_COND_V_MSG(shader->vertex_input_mask != 0, RID(), "Shader contains vertex inputs, but no vertex input description was provided for pipeline creation."); } - //input assembly + // Input assembly. ERR_FAIL_INDEX_V(p_render_primitive, RENDER_PRIMITIVE_MAX, RID()); @@ -6308,7 +6549,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma input_assembly_create_info.topology = topology_list[p_render_primitive]; input_assembly_create_info.primitiveRestartEnable = (p_render_primitive == RENDER_PRIMITIVE_TRIANGLE_STRIPS_WITH_RESTART_INDEX); - //tessellation + // Tessellation. VkPipelineTessellationStateCreateInfo tessellation_create_info; tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; tessellation_create_info.pNext = nullptr; @@ -6320,12 +6561,12 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state_create_info.pNext = nullptr; viewport_state_create_info.flags = 0; - viewport_state_create_info.viewportCount = 1; //if VR extensions are supported at some point, this will have to be customizable in the framebuffer format + viewport_state_create_info.viewportCount = 1; // If VR extensions are supported at some point, this will have to be customizable in the framebuffer format. viewport_state_create_info.pViewports = nullptr; viewport_state_create_info.scissorCount = 1; viewport_state_create_info.pScissors = nullptr; - //rasterization + // Rasterization. VkPipelineRasterizationStateCreateInfo rasterization_state_create_info; rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterization_state_create_info.pNext = nullptr; @@ -6342,24 +6583,24 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); rasterization_state_create_info.cullMode = cull_mode[p_rasterization_state.cull_mode]; rasterization_state_create_info.frontFace = (p_rasterization_state.front_face == POLYGON_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); - rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enable; + rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enabled; rasterization_state_create_info.depthBiasConstantFactor = p_rasterization_state.depth_bias_constant_factor; rasterization_state_create_info.depthBiasClamp = p_rasterization_state.depth_bias_clamp; rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; rasterization_state_create_info.lineWidth = p_rasterization_state.line_width; - //multisample + // Multisample. VkPipelineMultisampleStateCreateInfo multisample_state_create_info; multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisample_state_create_info.pNext = nullptr; multisample_state_create_info.flags = 0; - multisample_state_create_info.rasterizationSamples = rasterization_sample_count[p_multisample_state.sample_count]; + multisample_state_create_info.rasterizationSamples = _ensure_supported_sample_count(p_multisample_state.sample_count); multisample_state_create_info.sampleShadingEnable = p_multisample_state.enable_sample_shading; multisample_state_create_info.minSampleShading = p_multisample_state.min_sample_shading; Vector<VkSampleMask> sample_mask; if (p_multisample_state.sample_mask.size()) { - //use sample mask + // Use sample mask. const int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { 1, 2, 4, 8, 16, 32, 64 }; @@ -6377,7 +6618,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma multisample_state_create_info.alphaToCoverageEnable = p_multisample_state.enable_alpha_to_coverage; multisample_state_create_info.alphaToOneEnable = p_multisample_state.enable_alpha_to_one; - //depth stencil + // Depth stencil. VkPipelineDepthStencilStateCreateInfo depth_stencil_state_create_info; depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; @@ -6417,7 +6658,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma depth_stencil_state_create_info.minDepthBounds = p_depth_stencil_state.depth_range_min; depth_stencil_state_create_info.maxDepthBounds = p_depth_stencil_state.depth_range_max; - //blend state + // Blend state. VkPipelineColorBlendStateCreateInfo color_blend_state_create_info; color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; color_blend_state_create_info.pNext = nullptr; @@ -6488,15 +6729,15 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma color_blend_state_create_info.blendConstants[2] = p_blend_state.blend_constant.b; color_blend_state_create_info.blendConstants[3] = p_blend_state.blend_constant.a; - //dynamic state + // Dynamic state. VkPipelineDynamicStateCreateInfo dynamic_state_create_info; dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_create_info.pNext = nullptr; dynamic_state_create_info.flags = 0; - Vector<VkDynamicState> dynamic_states; //vulkan is weird.. + Vector<VkDynamicState> dynamic_states; // Vulkan is weird. - dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); //viewport and scissor are always dynamic + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); // Viewport and scissor are always dynamic. dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); if (p_dynamic_state_flags & DYNAMIC_STATE_LINE_WIDTH) { @@ -6530,11 +6771,28 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma dynamic_state_create_info.dynamicStateCount = dynamic_states.size(); dynamic_state_create_info.pDynamicStates = dynamic_states.ptr(); - //finally, pipeline create info + void *graphics_pipeline_nextptr = nullptr; + + VkPipelineFragmentShadingRateStateCreateInfoKHR vrs_create_info; + if (context->get_vrs_capabilities().attachment_vrs_supported) { + // If VRS is used, this defines how the different VRS types are combined. + // combinerOps[0] decides how we use the output of pipeline and primitive (drawcall) VRS. + // combinerOps[1] decides how we use the output of combinerOps[0] and our attachment VRS. + + vrs_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR; + vrs_create_info.pNext = nullptr; + vrs_create_info.fragmentSize = { 4, 4 }; + vrs_create_info.combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; // We don't use pipeline/primitive VRS so this really doesn't matter. + vrs_create_info.combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR; // Always use the outcome of attachment VRS if enabled. + + graphics_pipeline_nextptr = &vrs_create_info; + } + + // Finally, pipeline create info. VkGraphicsPipelineCreateInfo graphics_pipeline_create_info; graphics_pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - graphics_pipeline_create_info.pNext = nullptr; + graphics_pipeline_create_info.pNext = graphics_pipeline_nextptr; graphics_pipeline_create_info.flags = 0; Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages; @@ -6548,9 +6806,9 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma specialization_info.resize(pipeline_stages.size()); specialization_map_entries.resize(pipeline_stages.size()); for (int i = 0; i < shader->specialization_constants.size(); i++) { - //see if overridden + // See if overridden. const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + data_ptr[i] = sc.constant.int_value; // Just copy the 32 bits. for (int j = 0; j < p_specialization_constants.size(); j++) { const PipelineSpecializationConstant &psc = p_specialization_constants[j]; @@ -6645,9 +6903,12 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma }; pipeline.validation.primitive_minimum = primitive_minimum[p_render_primitive]; #endif - //create ID to associate with this pipeline + // Create ID to associate with this pipeline. RID id = render_pipeline_owner.make_rid(pipeline); - //now add all the dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. _add_dependency(id, p_shader); return id; } @@ -6664,14 +6925,14 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) { RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) { _THREAD_SAFE_METHOD_ - //needs a shader + // Needs a shader. Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, RID()); ERR_FAIL_COND_V_MSG(!shader->is_compute, RID(), "Non-compute shaders can't be used in compute pipelines"); - //finally, pipeline create info + // Finally, pipeline create info. VkComputePipelineCreateInfo compute_pipeline_create_info; compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; @@ -6691,15 +6952,15 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<Pi specialization_constant_data.resize(shader->specialization_constants.size()); uint32_t *data_ptr = specialization_constant_data.ptrw(); for (int i = 0; i < shader->specialization_constants.size(); i++) { - //see if overridden + // See if overridden. const Shader::SpecializationConstant &sc = shader->specialization_constants[i]; - data_ptr[i] = sc.constant.int_value; //just copy the 32 bits + data_ptr[i] = sc.constant.int_value; // Just copy the 32 bits. for (int j = 0; j < p_specialization_constants.size(); j++) { const PipelineSpecializationConstant &psc = p_specialization_constants[j]; if (psc.constant_id == sc.constant.constant_id) { ERR_FAIL_COND_V_MSG(psc.type != sc.constant.type, RID(), "Specialization constant provided for id (" + itos(sc.constant.constant_id) + ") is of the wrong type."); - data_ptr[i] = sc.constant.int_value; + data_ptr[i] = psc.int_value; break; } } @@ -6734,9 +6995,12 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<Pi pipeline.local_group_size[1] = shader->compute_local_size[1]; pipeline.local_group_size[2] = shader->compute_local_size[2]; - //create ID to associate with this pipeline + // Create ID to associate with this pipeline. RID id = compute_pipeline_owner.make_rid(pipeline); - //now add all the dependencies +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + // Now add all the dependencies. _add_dependency(id, p_shader); return id; } @@ -6766,7 +7030,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::screen_get_framebuff _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(local_device.is_valid(), INVALID_ID, "Local devices have no screen"); - //very hacky, but not used often per frame so I guess ok + // Very hacky, but not used often per frame so I guess ok. VkFormat vkformat = context->get_screen_format(); DataFormat format = DATA_FORMAT_MAX; for (int i = 0; i < DATA_FORMAT_MAX; i++) { @@ -6868,7 +7132,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu vk.view_count = p_framebuffer->view_count; if (!p_framebuffer->framebuffers.has(vk)) { - //need to create this version + // Need to create this version. Framebuffer::Version version; version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); @@ -6883,8 +7147,10 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); if (texture) { attachments.push_back(texture->view); - ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); - ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + if (!(texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { // VRS attachment will be a different size. + ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); + ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); + } } } framebuffer_create_info.attachmentCount = attachments.size(); @@ -6942,7 +7208,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff } if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); //a bug + ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. Color clear_color = p_clear_colors[color_index]; clear_value.color.float32[0] = clear_color.r; clear_value.color.float32[1] = clear_color.g; @@ -6973,7 +7239,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - //must change layout to general + // Must change layout to general. VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; @@ -7001,7 +7267,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff vkCmdBeginRenderPass(command_buffer, &render_pass_begin, subpass_contents); - //mark textures as bound + // Mark textures as bound. draw_list_bound_textures.clear(); draw_list_unbind_color_textures = p_final_color_action != FINAL_ACTION_CONTINUE; draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; @@ -7018,13 +7284,12 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff return OK; } -void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { - ERR_FAIL_COND_MSG(p_clear_color && p_clear_colors.size() != framebuffer->texture_ids.size(), "Clear color values supplied (" + itos(p_clear_colors.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(framebuffer->texture_ids.size()) + ")."); +void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { Vector<VkClearAttachment> clear_attachments; int color_index = 0; int texture_index = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); if (!texture) { texture_index++; @@ -7057,12 +7322,12 @@ void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list, VkClearRect cr; cr.baseArrayLayer = 0; cr.layerCount = 1; - cr.rect.offset.x = viewport_offset.x; - cr.rect.offset.y = viewport_offset.y; - cr.rect.extent.width = viewport_size.width; - cr.rect.extent.height = viewport_size.height; + cr.rect.offset.x = p_viewport_offset.x; + cr.rect.offset.y = p_viewport_offset.y; + cr.rect.extent.width = p_viewport_size.width; + cr.rect.extent.height = p_viewport_size.height; - vkCmdClearAttachments(draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); + vkCmdClearAttachments(p_draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); } RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { @@ -7079,7 +7344,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu bool needs_clear_color = false; bool needs_clear_depth = false; - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { //check custom region + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. Rect2i viewport(viewport_offset, viewport_size); Rect2i regioni = p_region; if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && @@ -7108,12 +7373,17 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu } } - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. int color_count = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - color_count++; + // We only check for our VRS usage bit if this is not the first texture id. + // If it is the first we're likely populating our VRS texture. + // Bit dirty but... + if (!texture || (!(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) { + if (!texture || !texture->is_resolve_buffer) { + color_count++; + } } } ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); @@ -7169,6 +7439,9 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != nullptr, ERR_BUSY, "Only one draw list can be active at the same time."); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, ERR_BUSY, "Only one draw/compute list can be active at the same time."); + ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); @@ -7180,7 +7453,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p bool needs_clear_color = false; bool needs_clear_depth = false; - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { //check custom region + if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. Rect2i viewport(viewport_offset, viewport_size); Rect2i regioni = p_region; if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && @@ -7202,7 +7475,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p } } - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values + if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. int color_count = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { @@ -7288,7 +7561,7 @@ RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(DrawL return nullptr; } - uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); //mask + uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); // Mask. if (index >= draw_list_count) { return nullptr; @@ -7300,6 +7573,16 @@ RenderingDeviceVulkan::DrawList *RenderingDeviceVulkan::_get_draw_list_ptr(DrawL } } +void RenderingDeviceVulkan::draw_list_set_blend_constants(DrawListID p_list, const Color &p_color) { + DrawList *dl = _get_draw_list_ptr(p_list); + ERR_FAIL_COND(!dl); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); +#endif + + vkCmdSetBlendConstants(dl->command_buffer, p_color.components); +} + void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) { DrawList *dl = _get_draw_list_ptr(p_list); ERR_FAIL_COND(!dl); @@ -7314,7 +7597,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI #endif if (p_render_pipeline == dl->state.pipeline) { - return; //redundant state, return. + return; // Redundant state, return. } dl->state.pipeline = p_render_pipeline; @@ -7323,17 +7606,17 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI vkCmdBindPipeline(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline); if (dl->state.pipeline_shader != pipeline->shader) { - // shader changed, so descriptor sets may become incompatible. + // Shader changed, so descriptor sets may become incompatible. - //go through ALL sets, and unbind them (and all those above) if the format is different + // Go through ALL sets, and unbind them (and all those above) if the format is different. - uint32_t pcount = pipeline->set_formats.size(); //formats count in this pipeline + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. dl->state.set_count = MAX(dl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - bool sets_valid = true; //once invalid, all above become invalid + bool sets_valid = true; // Once invalid, all above become invalid. for (uint32_t i = 0; i < pcount; i++) { - //if a part of the format is different, invalidate it (and the rest) + // If a part of the format is different, invalidate it (and the rest). if (!sets_valid || dl->state.sets[i].pipeline_expected_format != pformats[i]) { dl->state.sets[i].bound = false; dl->state.sets[i].pipeline_expected_format = pformats[i]; @@ -7342,11 +7625,11 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI } for (uint32_t i = pcount; i < dl->state.set_count; i++) { - //unbind the ones above (not used) if exist + // Unbind the ones above (not used) if exist. dl->state.sets[i].bound = false; } - dl->state.set_count = pcount; //update set count + dl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { dl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; @@ -7359,7 +7642,7 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI } #ifdef DEBUG_ENABLED - //update render pass pipeline info + // Update render pass pipeline info. dl->validation.pipeline_active = true; dl->validation.pipeline_dynamic_state = pipeline->validation.dynamic_state; dl->validation.pipeline_vertex_format = pipeline->validation.vertex_format; @@ -7389,8 +7672,8 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_ dl->state.set_count = p_index; } - dl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; //update set pointer - dl->state.sets[p_index].bound = false; //needs rebind + dl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. + dl->state.sets[p_index].bound = false; // Needs rebind. dl->state.sets[p_index].uniform_set_format = uniform_set->format; dl->state.sets[p_index].uniform_set = p_uniform_set; @@ -7408,7 +7691,7 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_ } #ifdef DEBUG_ENABLED - { //validate that textures bound are not attached as framebuffer bindings + { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); const UniformSet::AttachableTexture *attachable_ptr = uniform_set->attachable_textures.ptr(); uint32_t bound_count = draw_list_bound_textures.size(); @@ -7434,7 +7717,7 @@ void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p ERR_FAIL_COND(!vertex_array); if (dl->state.vertex_array == p_vertex_array) { - return; //already set + return; // Already set. } dl->state.vertex_array = p_vertex_array; @@ -7458,7 +7741,7 @@ void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_ ERR_FAIL_COND(!index_array); if (dl->state.index_array == p_index_array) { - return; //already set + return; // Already set. } dl->state.index_array = p_index_array; @@ -7468,7 +7751,7 @@ void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_ dl->validation.index_array_size = index_array->indices; dl->validation.index_array_offset = index_array->offset; - vkCmdBindIndexBuffer(dl->command_buffer, index_array->buffer, index_array->offset, index_array->index_type); + vkCmdBindIndexBuffer(dl->command_buffer, index_array->buffer, 0, index_array->index_type); } void RenderingDeviceVulkan::draw_list_set_line_width(DrawListID p_list, float p_width) { @@ -7510,30 +7793,30 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices ERR_FAIL_COND_MSG(!dl->validation.pipeline_active, "No render pipeline was set before attempting to draw."); if (dl->validation.pipeline_vertex_format != INVALID_ID) { - //pipeline uses vertices, validate format + // Pipeline uses vertices, validate format. ERR_FAIL_COND_MSG(dl->validation.vertex_format == INVALID_ID, "No vertex array was bound, and render pipeline expects vertices."); - //make sure format is right + // Make sure format is right. ERR_FAIL_COND_MSG(dl->validation.pipeline_vertex_format != dl->validation.vertex_format, "The vertex format used to create the pipeline does not match the vertex format bound."); - //make sure number of instances is valid + // Make sure number of instances is valid. ERR_FAIL_COND_MSG(p_instances > dl->validation.vertex_max_instances_allowed, "Number of instances requested (" + itos(p_instances) + " is larger than the maximum number supported by the bound vertex array (" + itos(dl->validation.vertex_max_instances_allowed) + ")."); } if (dl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!dl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < dl->state.set_count; i++) { if (dl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (dl->state.sets[i].pipeline_expected_format != dl->state.sets[i].uniform_set_format) { @@ -7548,7 +7831,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices } #endif if (!dl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(dl->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, dl->state.pipeline_layout, i, 1, &dl->state.sets[i].descriptor_set, 0, nullptr); dl->state.sets[i].bound = true; } @@ -7562,12 +7845,6 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices ERR_FAIL_COND_MSG(!dl->validation.index_array_size, "Draw command requested indices, but no index buffer was set."); - if (dl->validation.pipeline_vertex_format != INVALID_ID) { - //uses vertices, do some vertex validations - ERR_FAIL_COND_MSG(dl->validation.vertex_array_size < dl->validation.index_array_max_index, - "Index array references (max index: " + itos(dl->validation.index_array_max_index) + ") indices beyond the vertex array size (" + itos(dl->validation.vertex_array_size) + ")."); - } - ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); #endif @@ -7689,7 +7966,7 @@ Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_spli } Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { - // Lock while draw_list is active + // Lock while draw_list is active. _THREAD_SAFE_LOCK_ if (p_splits == 0) { @@ -7716,7 +7993,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 VkCommandBuffer command_buffer; VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. + // No command buffer exists, create it. cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdbuf.pNext = nullptr; cmdbuf.commandPool = split_draw_list_allocators[i].command_pool; @@ -7735,7 +8012,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 draw_list_split = true; for (uint32_t i = 0; i < p_splits; i++) { - //take a command buffer and initialize it + // Take a command buffer and initialize it. VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame]; VkCommandBufferInheritanceInfo inheritance_info; @@ -7745,7 +8022,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 inheritance_info.subpass = p_subpass; inheritance_info.framebuffer = draw_list_vkframebuffer; inheritance_info.occlusionQueryEnable = false; - inheritance_info.queryFlags = 0; //? + inheritance_info.queryFlags = 0; // ? inheritance_info.pipelineStatistics = 0; VkCommandBufferBeginInfo cmdbuf_begin; @@ -7778,7 +8055,7 @@ Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint3 void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (draw_list_split) { - //send all command buffers + // Send all command buffers. VkCommandBuffer *command_buffers = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer) * draw_list_count); for (uint32_t i = 0; i < draw_list_count; i++) { vkEndCommandBuffer(draw_list[i].command_buffer); @@ -7798,12 +8075,12 @@ void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) { if (r_last_viewport) { *r_last_viewport = draw_list->viewport; } - //just end the list + // Just end the list. memdelete(draw_list); draw_list = nullptr; } - // draw_list is no longer active + // Draw_list is no longer active. _THREAD_SAFE_UNLOCK_ } @@ -7818,7 +8095,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { for (int i = 0; i < draw_list_bound_textures.size(); i++) { Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); - ERR_CONTINUE(!texture); //wtf + ERR_CONTINUE(!texture); // Wtf. if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { texture->bound = false; } @@ -7890,12 +8167,8 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { draw_list_storage_textures.clear(); // To ensure proper synchronization, we must make sure rendering is done before: - // * Some buffer is copied - // * Another render pass happens (since we may be done) - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else + // * Some buffer is copied. + // * Another render pass happens (since we may be done). VkMemoryBarrier mem_barrier; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; @@ -7907,6 +8180,8 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) { vkCmdPipelineBarrier(frames[frame].draw_command_buffer, src_stage, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); } +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); #endif } @@ -7918,7 +8193,7 @@ RenderingDevice::ComputeListID RenderingDeviceVulkan::compute_list_begin(bool p_ ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); - // Lock while compute_list is active + // Lock while compute_list is active. _THREAD_SAFE_LOCK_ compute_list = memnew(ComputeList); @@ -7938,7 +8213,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l ERR_FAIL_COND(!pipeline); if (p_compute_pipeline == cl->state.pipeline) { - return; //redundant state, return. + return; // Redundant state, return. } cl->state.pipeline = p_compute_pipeline; @@ -7947,17 +8222,17 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l vkCmdBindPipeline(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline); if (cl->state.pipeline_shader != pipeline->shader) { - // shader changed, so descriptor sets may become incompatible. + // Shader changed, so descriptor sets may become incompatible. - //go through ALL sets, and unbind them (and all those above) if the format is different + // Go through ALL sets, and unbind them (and all those above) if the format is different. - uint32_t pcount = pipeline->set_formats.size(); //formats count in this pipeline + uint32_t pcount = pipeline->set_formats.size(); // Formats count in this pipeline. cl->state.set_count = MAX(cl->state.set_count, pcount); - const uint32_t *pformats = pipeline->set_formats.ptr(); //pipeline set formats + const uint32_t *pformats = pipeline->set_formats.ptr(); // Pipeline set formats. - bool sets_valid = true; //once invalid, all above become invalid + bool sets_valid = true; // Once invalid, all above become invalid. for (uint32_t i = 0; i < pcount; i++) { - //if a part of the format is different, invalidate it (and the rest) + // If a part of the format is different, invalidate it (and the rest). if (!sets_valid || cl->state.sets[i].pipeline_expected_format != pformats[i]) { cl->state.sets[i].bound = false; cl->state.sets[i].pipeline_expected_format = pformats[i]; @@ -7966,11 +8241,11 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l } for (uint32_t i = pcount; i < cl->state.set_count; i++) { - //unbind the ones above (not used) if exist + // Unbind the ones above (not used) if exist. cl->state.sets[i].bound = false; } - cl->state.set_count = pcount; //update set count + cl->state.set_count = pcount; // Update set count. if (pipeline->push_constant_size) { cl->state.pipeline_push_constant_stages = pipeline->push_constant_stages; @@ -7986,7 +8261,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l } #ifdef DEBUG_ENABLED - //update compute pass pipeline info + // Update compute pass pipeline info. cl->validation.pipeline_active = true; cl->validation.pipeline_push_constant_size = pipeline->push_constant_size; #endif @@ -8014,8 +8289,8 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, cl->state.set_count = p_index; } - cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; //update set pointer - cl->state.sets[p_index].bound = false; //needs rebind + cl->state.sets[p_index].descriptor_set = uniform_set->descriptor_set; // Update set pointer. + cl->state.sets[p_index].bound = false; // Needs rebind. cl->state.sets[p_index].uniform_set_format = uniform_set->format; cl->state.sets[p_index].uniform_set = p_uniform_set; @@ -8118,7 +8393,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, textures_to_storage[i]->layout = VK_IMAGE_LAYOUT_GENERAL; - cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); //needs to go back to sampled layout afterwards + cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); // Needs to go back to sampled layout afterwards. } } @@ -8131,7 +8406,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, } #if 0 - { //validate that textures bound are not attached as framebuffer bindings + { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); const RID *attachable_ptr = uniform_set->attachable_textures.ptr(); uint32_t bound_count = draw_list_bound_textures.size(); @@ -8191,18 +8466,18 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { @@ -8217,7 +8492,7 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t } #endif if (!cl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); cl->state.sets[i].bound = true; } @@ -8243,7 +8518,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_threads(ComputeListID p_list, ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } @@ -8261,7 +8536,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, Buffer *buffer = storage_buffer_owner.get_or_null(p_buffer); ERR_FAIL_COND(!buffer); - ERR_FAIL_COND_MSG(!(buffer->usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT), "Buffer provided was not created to do indirect dispatch."); + ERR_FAIL_COND_MSG(!(buffer->usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT), "Buffer provided was not created to do indirect dispatch."); ERR_FAIL_COND_MSG(p_offset + 12 > buffer->size, "Offset provided (+12) is past the end of buffer."); @@ -8274,18 +8549,18 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw."); if (cl->validation.pipeline_push_constant_size > 0) { - //using push constants, check that they were supplied + // Using push constants, check that they were supplied. ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied, "The shader in this pipeline requires a push constant to be set before drawing, but it's not present."); } #endif - //Bind descriptor sets + // Bind descriptor sets. for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { - continue; //nothing expected by this pipeline + continue; // Nothing expected by this pipeline. } #ifdef DEBUG_ENABLED if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) { @@ -8300,7 +8575,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, } #endif if (!cl->state.sets[i].bound) { - //All good, see if this requires re-binding + // All good, see if this requires re-binding. vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr); cl->state.sets[i].bound = true; } @@ -8377,9 +8652,6 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { } } -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else VkMemoryBarrier mem_barrier; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; mem_barrier.pNext = nullptr; @@ -8390,12 +8662,14 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, barrier_flags, 0, 1, &mem_barrier, 0, nullptr, image_barrier_count, image_barriers); } +#ifdef FORCE_FULL_BARRIER + _full_barrier(true); #endif memdelete(compute_list); compute_list = nullptr; - // compute_list is no longer active + // Compute_list is no longer active. _THREAD_SAFE_UNLOCK_ } @@ -8488,7 +8762,7 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame "Draw list index (" + itos(i) + ") is created with a framebuffer format incompatible with this render pass."); if (dl->validation.active) { - //needs to be closed, so close it. + // Needs to be closed, so close it. vkEndCommandBuffer(dl->command_buffer); dl->validation.active = false; } @@ -8505,7 +8779,15 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame #endif void RenderingDeviceVulkan::_free_internal(RID p_id) { - //push everything so it's disposed of next time this frame index is processed (means, it's safe to do it) +#ifdef DEV_ENABLED + String resource_name; + if (resource_names.has(p_id)) { + resource_name = resource_names[p_id]; + resource_names.erase(p_id); + } +#endif + + // Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it). if (texture_owner.owns(p_id)) { Texture *texture = texture_owner.get_or_null(p_id); frames[frame].textures_to_dispose_of.push_back(*texture); @@ -8513,6 +8795,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (framebuffer_owner.owns(p_id)) { Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer); + + if (framebuffer->invalidated_callback != nullptr) { + framebuffer->invalidated_callback(framebuffer->invalidated_callback_userdata); + } + framebuffer_owner.free(p_id); } else if (sampler_owner.owns(p_id)) { VkSampler *sampler = sampler_owner.get_or_null(p_id); @@ -8569,30 +8856,34 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline); compute_pipeline_owner.free(p_id); } else { +#ifdef DEV_ENABLED + ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id()) + " " + resource_name); +#else ERR_PRINT("Attempted to free invalid ID: " + itos(p_id.get_id())); +#endif } } void RenderingDeviceVulkan::free(RID p_id) { _THREAD_SAFE_METHOD_ - _free_dependencies(p_id); //recursively erase dependencies first, to avoid potential API problems + _free_dependencies(p_id); // Recursively erase dependencies first, to avoid potential API problems. _free_internal(p_id); } -// The full list of resources that can be named is in the VkObjectType enum +// The full list of resources that can be named is in the VkObjectType enum. // We just expose the resources that are owned and can be accessed easily. void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { if (texture_owner.owns(p_id)) { Texture *texture = texture_owner.get_or_null(p_id); if (texture->owner.is_null()) { - // Don't set the source texture's name when calling on a texture view + // Don't set the source texture's name when calling on a texture view. context->set_object_name(VK_OBJECT_TYPE_IMAGE, uint64_t(texture->image), p_name); } context->set_object_name(VK_OBJECT_TYPE_IMAGE_VIEW, uint64_t(texture->view), p_name + " View"); } else if (framebuffer_owner.owns(p_id)) { //Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id); - // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex + // Not implemented for now as the relationship between Framebuffer and RenderPass is very complex. } else if (sampler_owner.owns(p_id)) { VkSampler *sampler = sampler_owner.get_or_null(p_id); context->set_object_name(VK_OBJECT_TYPE_SAMPLER, uint64_t(*sampler), p_name); @@ -8631,7 +8922,11 @@ void RenderingDeviceVulkan::set_resource_name(RID p_id, const String p_name) { context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout"); } else { ERR_PRINT("Attempted to name invalid ID: " + itos(p_id.get_id())); + return; } +#ifdef DEV_ENABLED + resource_names[p_id] = p_name; +#endif } void RenderingDeviceVulkan::draw_command_begin_label(String p_label_name, const Color p_color) { @@ -8675,17 +8970,17 @@ void RenderingDeviceVulkan::_finalize_command_bufers() { ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); } - { //complete the setup buffer (that needs to be processed before anything else) + { // Complete the setup buffer (that needs to be processed before anything else). vkEndCommandBuffer(frames[frame].setup_command_buffer); vkEndCommandBuffer(frames[frame].draw_command_buffer); } } void RenderingDeviceVulkan::_begin_frame() { - //erase pending resources + // Erase pending resources. _free_pending_resources(frame); - //create setup command buffer and set as the setup buffer + // Create setup command buffer and set as the setup buffer. { VkCommandBufferBeginInfo cmdbuf_begin; @@ -8704,20 +8999,20 @@ void RenderingDeviceVulkan::_begin_frame() { if (local_device.is_null()) { context->append_command_buffer(frames[frame].draw_command_buffer); - context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. } } - //advance current frame + // Advance current frame. frames_drawn++; - //advance staging buffer if used + // Advance staging buffer if used. if (staging_buffer_used) { staging_buffer_current = (staging_buffer_current + 1) % staging_buffer_blocks.size(); staging_buffer_used = false; } if (frames[frame].timestamp_count) { - vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); + vkGetQueryPoolResults(device, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count, sizeof(uint64_t) * max_timestamp_query_elements, frames[frame].timestamp_result_values.ptr(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT); vkCmdResetQueryPool(frames[frame].setup_command_buffer, frames[frame].timestamp_pool, 0, frames[frame].timestamp_count); SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names); SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values); @@ -8728,6 +9023,25 @@ void RenderingDeviceVulkan::_begin_frame() { frames[frame].index = Engine::get_singleton()->get_frames_drawn(); } +VkSampleCountFlagBits RenderingDeviceVulkan::_ensure_supported_sample_count(TextureSamples p_requested_sample_count) const { + VkSampleCountFlags sample_count_flags = limits.framebufferColorSampleCounts & limits.framebufferDepthSampleCounts; + + if (sample_count_flags & rasterization_sample_count[p_requested_sample_count]) { + // The requested sample count is supported. + return rasterization_sample_count[p_requested_sample_count]; + } else { + // Find the closest lower supported sample count. + VkSampleCountFlagBits sample_count = rasterization_sample_count[p_requested_sample_count]; + while (sample_count > VK_SAMPLE_COUNT_1_BIT) { + if (sample_count_flags & sample_count) { + return sample_count; + } + sample_count = (VkSampleCountFlagBits)(sample_count >> 1); + } + } + return VK_SAMPLE_COUNT_1_BIT; +} + void RenderingDeviceVulkan::swap_buffers() { ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); _THREAD_SAFE_METHOD_ @@ -8735,7 +9049,7 @@ void RenderingDeviceVulkan::swap_buffers() { _finalize_command_bufers(); screen_prepared = false; - //swap buffers + // Swap buffers. context->swap_buffers(); frame = (frame + 1) % frame_count; @@ -8781,15 +9095,15 @@ VmaPool RenderingDeviceVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_ pci.pMemoryAllocateNext = nullptr; VmaPool pool = VK_NULL_HANDLE; VkResult res = vmaCreatePool(allocator, &pci, &pool); - small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time + small_allocs_pools[p_mem_type_index] = pool; // Don't try to create it again if failed the first time. ERR_FAIL_COND_V_MSG(res, pool, "vmaCreatePool failed with error " + itos(res) + "."); return pool; } void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { - //free in dependency usage order, so nothing weird happens - //pipelines + // Free in dependency usage order, so nothing weird happens. + // Pipelines. while (frames[p_frame].render_pipelines_to_dispose_of.front()) { RenderPipeline *pipeline = &frames[p_frame].render_pipelines_to_dispose_of.front()->get(); @@ -8806,7 +9120,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].compute_pipelines_to_dispose_of.pop_front(); } - //uniform sets + // Uniform sets. while (frames[p_frame].uniform_sets_to_dispose_of.front()) { UniformSet *uniform_set = &frames[p_frame].uniform_sets_to_dispose_of.front()->get(); @@ -8816,7 +9130,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].uniform_sets_to_dispose_of.pop_front(); } - //buffer views + // Buffer views. while (frames[p_frame].buffer_views_to_dispose_of.front()) { VkBufferView buffer_view = frames[p_frame].buffer_views_to_dispose_of.front()->get(); @@ -8825,19 +9139,19 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].buffer_views_to_dispose_of.pop_front(); } - //shaders + // Shaders. while (frames[p_frame].shaders_to_dispose_of.front()) { Shader *shader = &frames[p_frame].shaders_to_dispose_of.front()->get(); - //descriptor set layout for each set + // Descriptor set layout for each set. for (int i = 0; i < shader->sets.size(); i++) { vkDestroyDescriptorSetLayout(device, shader->sets[i].descriptor_set_layout, nullptr); } - //pipeline layout + // Pipeline layout. vkDestroyPipelineLayout(device, shader->pipeline_layout, nullptr); - //shaders themselves + // Shaders themselves. for (int i = 0; i < shader->pipeline_stages.size(); i++) { vkDestroyShaderModule(device, shader->pipeline_stages[i].module, nullptr); } @@ -8845,7 +9159,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].shaders_to_dispose_of.pop_front(); } - //samplers + // Samplers. while (frames[p_frame].samplers_to_dispose_of.front()) { VkSampler sampler = frames[p_frame].samplers_to_dispose_of.front()->get(); @@ -8854,12 +9168,12 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].samplers_to_dispose_of.pop_front(); } - //framebuffers + // Framebuffers. while (frames[p_frame].framebuffers_to_dispose_of.front()) { Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) { - //first framebuffer, then render pass because it depends on it + // First framebuffer, then render pass because it depends on it. vkDestroyFramebuffer(device, E.value.framebuffer, nullptr); vkDestroyRenderPass(device, E.value.render_pass, nullptr); } @@ -8867,7 +9181,7 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { frames[p_frame].framebuffers_to_dispose_of.pop_front(); } - //textures + // Textures. while (frames[p_frame].textures_to_dispose_of.front()) { Texture *texture = &frames[p_frame].textures_to_dispose_of.front()->get(); @@ -8876,14 +9190,14 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) { } vkDestroyImageView(device, texture->view, nullptr); if (texture->owner.is_null()) { - //actually owns the image and the allocation too + // Actually owns the image and the allocation too. image_memory -= texture->allocation_info.size; vmaDestroyImage(allocator, texture->image, texture->allocation); } frames[p_frame].textures_to_dispose_of.pop_front(); } - //buffers + // Buffers. while (frames[p_frame].buffers_to_dispose_of.front()) { _buffer_free(&frames[p_frame].buffers_to_dispose_of.front()->get()); @@ -8915,9 +9229,9 @@ uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { void RenderingDeviceVulkan::_flush(bool p_current_frame) { if (local_device.is_valid() && !p_current_frame) { - return; //flushing previous frames has no effect with local device + return; // Flushing previous frames has no effect with local device. } - //not doing this crashes RADV (undefined behavior) + // Not doing this crashes RADV (undefined behavior). if (p_current_frame) { vkEndCommandBuffer(frames[frame].setup_command_buffer); vkEndCommandBuffer(frames[frame].draw_command_buffer); @@ -8941,7 +9255,7 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } else { context->flush(p_current_frame, p_current_frame); - //re-create the setup command + // Re-create the setup command. if (p_current_frame) { VkCommandBufferBeginInfo cmdbuf_begin; cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -8951,7 +9265,7 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { VkResult err = vkBeginCommandBuffer(frames[frame].setup_command_buffer, &cmdbuf_begin); ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); - context->set_setup_buffer(frames[frame].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[frame].setup_command_buffer); // Append now so it's added before everything else. } if (p_current_frame) { @@ -8969,21 +9283,10 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) { } void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) { - // get our device capabilities + // Get our device capabilities. { device_capabilities.version_major = p_context->get_vulkan_major(); device_capabilities.version_minor = p_context->get_vulkan_minor(); - - // get info about subgroups - VulkanContext::SubgroupCapabilities subgroup_capabilities = p_context->get_subgroup_capabilities(); - device_capabilities.subgroup_size = subgroup_capabilities.size; - device_capabilities.subgroup_in_shaders = subgroup_capabilities.supported_stages_flags_rd(); - device_capabilities.subgroup_operations = subgroup_capabilities.supported_operations_flags_rd(); - - // get info about further features - VulkanContext::MultiviewCapabilities multiview_capabilies = p_context->get_multiview_capabilities(); - device_capabilities.supports_multiview = multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; - device_capabilities.supports_fsr_half_float = p_context->get_shader_capabilities().shader_float16_is_supported && p_context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; } context = p_context; @@ -8993,12 +9296,12 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de local_device = p_context->local_device_create(); device = p_context->local_device_get_vk_device(local_device); } else { - frame_count = p_context->get_swapchain_image_count() + 1; //always need one extra to ensure it's unused at any time, without having to use a fence for this. + frame_count = p_context->get_swapchain_image_count() + 1; // Always need one extra to ensure it's unused at any time, without having to use a fence for this. } limits = p_context->get_device_limits(); max_timestamp_query_elements = 256; - { //initialize allocator + { // Initialize allocator. VmaAllocatorCreateInfo allocatorInfo; memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo)); @@ -9008,13 +9311,13 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de vmaCreateAllocator(&allocatorInfo, &allocator); } - frames = memnew_arr(Frame, frame_count); + frames.resize(frame_count); frame = 0; - //create setup and frame buffers + // Create setup and frame buffers. for (int i = 0; i < frame_count; i++) { frames[i].index = 0; - { //create command pool, one per frame is recommended + { // Create command pool, one per frame is recommended. VkCommandPoolCreateInfo cmd_pool_info; cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cmd_pool_info.pNext = nullptr; @@ -9025,10 +9328,10 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de ERR_FAIL_COND_MSG(res, "vkCreateCommandPool failed with error " + itos(res) + "."); } - { //create command buffers + { // Create command buffers. VkCommandBufferAllocateInfo cmdbuf; - //no command buffer exists, create it. + // No command buffer exists, create it. cmdbuf.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdbuf.pNext = nullptr; cmdbuf.commandPool = frames[i].command_pool; @@ -9043,7 +9346,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de } { - //create query pool + // Create query pool. VkQueryPoolCreateInfo query_pool_create_info; query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; query_pool_create_info.flags = 0; @@ -9054,19 +9357,19 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de vkCreateQueryPool(device, &query_pool_create_info, nullptr, &frames[i].timestamp_pool); - frames[i].timestamp_names = memnew_arr(String, max_timestamp_query_elements); - frames[i].timestamp_cpu_values = memnew_arr(uint64_t, max_timestamp_query_elements); + frames[i].timestamp_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements); frames[i].timestamp_count = 0; - frames[i].timestamp_result_names = memnew_arr(String, max_timestamp_query_elements); - frames[i].timestamp_cpu_result_values = memnew_arr(uint64_t, max_timestamp_query_elements); - frames[i].timestamp_result_values = memnew_arr(uint64_t, max_timestamp_query_elements); + frames[i].timestamp_result_names.resize(max_timestamp_query_elements); + frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements); + frames[i].timestamp_result_values.resize(max_timestamp_query_elements); frames[i].timestamp_result_count = 0; } } { - //begin the first command buffer for the first frame, so - //setting up things can be done in the meantime until swap_buffers(), which is called before advance. + // Begin the first command buffer for the first frame, so + // setting up things can be done in the meantime until swap_buffers(), which is called before advance. VkCommandBufferBeginInfo cmdbuf_begin; cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdbuf_begin.pNext = nullptr; @@ -9079,42 +9382,40 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de err = vkBeginCommandBuffer(frames[0].draw_command_buffer, &cmdbuf_begin); ERR_FAIL_COND_MSG(err, "vkBeginCommandBuffer failed with error " + itos(err) + "."); if (local_device.is_null()) { - context->set_setup_buffer(frames[0].setup_command_buffer); //append now so it's added before everything else + context->set_setup_buffer(frames[0].setup_command_buffer); // Append now so it's added before everything else. context->append_command_buffer(frames[0].draw_command_buffer); } } - // Note: If adding new project settings here, also duplicate their definition in - // rendering_server.cpp for headless doctool. - staging_buffer_block_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/block_size_kb", 256); + staging_buffer_block_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/block_size_kb"); staging_buffer_block_size = MAX(4u, staging_buffer_block_size); - staging_buffer_block_size *= 1024; //kb -> bytes - staging_buffer_max_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/max_size_mb", 128); + staging_buffer_block_size *= 1024; // Kb -> bytes. + staging_buffer_max_size = GLOBAL_GET("rendering/rendering_device/staging_buffer/max_size_mb"); staging_buffer_max_size = MAX(1u, staging_buffer_max_size); staging_buffer_max_size *= 1024 * 1024; if (staging_buffer_max_size < staging_buffer_block_size * 4) { - //validate enough blocks + // Validate enough blocks. staging_buffer_max_size = staging_buffer_block_size * 4; } - texture_upload_region_size_px = GLOBAL_DEF("rendering/vulkan/staging_buffer/texture_upload_region_size_px", 64); + texture_upload_region_size_px = GLOBAL_GET("rendering/rendering_device/staging_buffer/texture_upload_region_size_px"); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); - frames_drawn = frame_count; //start from frame count, so everything else is immediately old + frames_drawn = frame_count; // Start from frame count, so everything else is immediately old. - //ensure current staging block is valid and at least one per frame exists + // Ensure current staging block is valid and at least one per frame exists. staging_buffer_current = 0; staging_buffer_used = false; for (int i = 0; i < frame_count; i++) { - //staging was never used, create a block + // Staging was never used, create a block. Error err = _insert_staging_block(); ERR_CONTINUE(err != OK); } - max_descriptors_per_pool = GLOBAL_DEF("rendering/vulkan/descriptor_pools/max_descriptors_per_pool", 64); + max_descriptors_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool"); - //check to make sure DescriptorPoolKey is good + // Check to make sure DescriptorPoolKey is good. static_assert(sizeof(uint64_t) * 3 >= UNIFORM_TYPE_MAX * sizeof(uint16_t)); draw_list = nullptr; @@ -9135,6 +9436,11 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) { WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); } for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif free(E); } } @@ -9144,7 +9450,7 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) { ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); - //this should be optional for profiling, else it will slow things down + // This should be optional for profiling, else it will slow things down. { VkMemoryBarrier memoryBarrier; @@ -9270,7 +9576,7 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R return uint64_t(render_pipeline->pipeline); } break; default: { - // not supported for this driver + // Not supported for this driver. return 0; } break; } @@ -9307,9 +9613,9 @@ static void mult64to128(uint64_t u, uint64_t v, uint64_t &h, uint64_t &l) { uint64_t RenderingDeviceVulkan::get_captured_timestamp_gpu_time(uint32_t p_index) const { ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0); - // this sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. - // so, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible - // need to do 128 bits fixed point multiplication to get the right value + // This sucks because timestampPeriod multiplier is a float, while the timestamp is 64 bits nanosecs. + // So, in cases like nvidia which give you enormous numbers and 1 as multiplier, multiplying is next to impossible. + // Need to do 128 bits fixed point multiplication to get the right value. uint64_t shift_bits = 16; @@ -9332,7 +9638,7 @@ String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) cons return frames[frame].timestamp_result_names[p_index]; } -uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { +uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const { switch (p_limit) { case LIMIT_MAX_BOUND_UNIFORM_SETS: return limits.maxBoundDescriptorSets; @@ -9402,7 +9708,22 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { return limits.maxComputeWorkGroupSize[1]; case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; - + case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: + return limits.maxViewportDimensions[0]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y: + return limits.maxViewportDimensions[1]; + case LIMIT_SUBGROUP_SIZE: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.size; + } + case LIMIT_SUBGROUP_IN_SHADERS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_stages_flags_rd(); + } + case LIMIT_SUBGROUP_OPERATIONS: { + VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); + return subgroup_capabilities.supported_operations_flags_rd(); + } default: ERR_FAIL_V(0); } @@ -9411,7 +9732,7 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) { } void RenderingDeviceVulkan::finalize() { - //free all resources + // Free all resources. _flush(false); @@ -9429,7 +9750,7 @@ void RenderingDeviceVulkan::finalize() { _free_rids(framebuffer_owner, "Framebuffer"); _free_rids(sampler_owner, "Sampler"); { - //for textures it's a bit more difficult because they may be shared + // For textures it's a bit more difficult because they may be shared. List<RID> owned; texture_owner.get_owned_list(&owned); if (owned.size()) { @@ -9438,40 +9759,45 @@ void RenderingDeviceVulkan::finalize() { } else { WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size())); } - //free shared first + // Free shared first. for (List<RID>::Element *E = owned.front(); E;) { List<RID>::Element *N = E->next(); if (texture_is_shared(E->get())) { +#ifdef DEV_ENABLED + if (resource_names.has(E->get())) { + print_line(String(" - ") + resource_names[E->get()]); + } +#endif free(E->get()); owned.erase(E); } E = N; } - //free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. + // Free non shared second, this will avoid an error trying to free unexisting textures due to dependencies. for (const RID &E : owned) { +#ifdef DEV_ENABLED + if (resource_names.has(E)) { + print_line(String(" - ") + resource_names[E]); + } +#endif free(E); } } } - //free everything pending + // Free everything pending. for (int i = 0; i < frame_count; i++) { int f = (frame + i) % frame_count; _free_pending_resources(f); vkDestroyCommandPool(device, frames[i].command_pool, nullptr); vkDestroyQueryPool(device, frames[i].timestamp_pool, nullptr); - memdelete_arr(frames[i].timestamp_names); - memdelete_arr(frames[i].timestamp_cpu_values); - memdelete_arr(frames[i].timestamp_result_names); - memdelete_arr(frames[i].timestamp_result_values); - memdelete_arr(frames[i].timestamp_cpu_result_values); } for (int i = 0; i < split_draw_list_allocators.size(); i++) { vkDestroyCommandPool(device, split_draw_list_allocators[i].command_pool, nullptr); } - memdelete_arr(frames); + frames.clear(); for (int i = 0; i < staging_buffer_blocks.size(); i++) { vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation); @@ -9495,7 +9821,7 @@ void RenderingDeviceVulkan::finalize() { } framebuffer_formats.clear(); - //all these should be clear at this point + // All these should be clear at this point. ERR_FAIL_COND(descriptor_pools.size()); ERR_FAIL_COND(dependency_map.size()); ERR_FAIL_COND(reverse_dependency_map.size()); @@ -9507,6 +9833,25 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() { return rd; } +bool RenderingDeviceVulkan::has_feature(const Features p_feature) const { + switch (p_feature) { + case SUPPORTS_MULTIVIEW: { + VulkanContext::MultiviewCapabilities multiview_capabilies = context->get_multiview_capabilities(); + return multiview_capabilies.is_supported && multiview_capabilies.max_view_count > 1; + } break; + case SUPPORTS_FSR_HALF_FLOAT: { + return context->get_shader_capabilities().shader_float16_is_supported && context->get_storage_buffer_capabilities().storage_buffer_16_bit_access_is_supported; + } break; + case SUPPORTS_ATTACHMENT_VRS: { + VulkanContext::VRSCapabilities vrs_capabilities = context->get_vrs_capabilities(); + return vrs_capabilities.attachment_vrs_supported; + } break; + default: { + return false; + } + } +} + RenderingDeviceVulkan::RenderingDeviceVulkan() { device_capabilities.device_family = DEVICE_VULKAN; } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 903a39b3d0..7c75e9bb2e 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -96,13 +96,13 @@ class RenderingDeviceVulkan : public RenderingDevice { ID_TYPE_SPLIT_DRAW_LIST, ID_TYPE_COMPUTE_LIST, ID_TYPE_MAX, - ID_BASE_SHIFT = 58 //5 bits for ID types + ID_BASE_SHIFT = 58 // 5 bits for ID types. }; VkDevice device = VK_NULL_HANDLE; - HashMap<RID, HashSet<RID>> dependency_map; //IDs to IDs that depend on it - HashMap<RID, HashSet<RID>> reverse_dependency_map; //same as above, but in reverse + HashMap<RID, HashSet<RID>> dependency_map; // IDs to IDs that depend on it. + HashMap<RID, HashSet<RID>> reverse_dependency_map; // Same as above, but in reverse. void _add_dependency(RID p_id, RID p_depends_on); void _free_dependencies(RID p_id); @@ -150,9 +150,11 @@ class RenderingDeviceVulkan : public RenderingDevice { bool used_in_raster = false; bool used_in_compute = false; + bool is_resolve_buffer = false; + uint32_t read_aspect_mask = 0; uint32_t barrier_aspect_mask = 0; - bool bound = false; //bound to framebffer + bool bound = false; // Bound to framebffer. RID owner; }; @@ -206,7 +208,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint64_t staging_buffer_max_size = 0; bool staging_buffer_used = false; - Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true, bool p_on_draw_command_buffer = false); + Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true); Error _insert_staging_block(); struct Buffer { @@ -214,7 +216,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t usage = 0; VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; - VkDescriptorBufferInfo buffer_info; //used for binding + VkDescriptorBufferInfo buffer_info; // Used for binding. Buffer() { } }; @@ -241,6 +243,7 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector<AttachmentFormat> attachments; Vector<FramebufferPass> passes; uint32_t view_count = 1; + bool operator<(const FramebufferFormatKey &p_key) const { if (view_count != p_key.view_count) { return view_count < p_key.view_count; @@ -255,7 +258,7 @@ class RenderingDeviceVulkan : public RenderingDevice { const FramebufferPass *key_pass_ptr = p_key.passes.ptr(); for (uint32_t i = 0; i < pass_size; i++) { - { //compare color attachments + { // Compare color attachments. uint32_t attachment_size = pass_ptr[i].color_attachments.size(); uint32_t key_attachment_size = key_pass_ptr[i].color_attachments.size(); if (attachment_size != key_attachment_size) { @@ -270,7 +273,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare input attachments + { // Compare input attachments. uint32_t attachment_size = pass_ptr[i].input_attachments.size(); uint32_t key_attachment_size = key_pass_ptr[i].input_attachments.size(); if (attachment_size != key_attachment_size) { @@ -285,7 +288,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare resolve attachments + { // Compare resolve attachments. uint32_t attachment_size = pass_ptr[i].resolve_attachments.size(); uint32_t key_attachment_size = key_pass_ptr[i].resolve_attachments.size(); if (attachment_size != key_attachment_size) { @@ -300,7 +303,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } } - { //compare preserve attachments + { // Compare preserve attachments. uint32_t attachment_size = pass_ptr[i].preserve_attachments.size(); uint32_t key_attachment_size = key_pass_ptr[i].preserve_attachments.size(); if (attachment_size != key_attachment_size) { @@ -342,7 +345,7 @@ class RenderingDeviceVulkan : public RenderingDevice { } } - return false; //equal + return false; // Equal. } }; @@ -352,9 +355,9 @@ class RenderingDeviceVulkan : public RenderingDevice { RBMap<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; struct FramebufferFormat { const RBMap<FramebufferFormatKey, FramebufferFormatID>::Element *E; - VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) + VkRenderPass render_pass = VK_NULL_HANDLE; // Here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec). Vector<TextureSamples> pass_samples; - uint32_t view_count = 1; // number of views + uint32_t view_count = 1; // Number of views. }; HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; @@ -391,10 +394,12 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t storage_mask = 0; Vector<RID> texture_ids; + InvalidationCallback invalidated_callback = nullptr; + void *invalidated_callback_userdata = nullptr; struct Version { VkFramebuffer framebuffer = VK_NULL_HANDLE; - VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned + VkRenderPass render_pass = VK_NULL_HANDLE; // This one is owned. uint32_t subpass_count = 1; }; @@ -451,23 +456,23 @@ class RenderingDeviceVulkan : public RenderingDevice { return false; } } - return true; //they are equal + return true; // They are equal. } } uint32_t hash() const { int vdc = vertex_formats.size(); - uint32_t h = hash_djb2_one_32(vdc); + uint32_t h = hash_murmur3_one_32(vdc); const VertexAttribute *ptr = vertex_formats.ptr(); for (int i = 0; i < vdc; i++) { const VertexAttribute &vd = ptr[i]; - h = hash_djb2_one_32(vd.location, h); - h = hash_djb2_one_32(vd.offset, h); - h = hash_djb2_one_32(vd.format, h); - h = hash_djb2_one_32(vd.stride, h); - h = hash_djb2_one_32(vd.frequency, h); + h = hash_murmur3_one_32(vd.location, h); + h = hash_murmur3_one_32(vd.offset, h); + h = hash_murmur3_one_32(vd.format, h); + h = hash_murmur3_one_32(vd.stride, h); + h = hash_murmur3_one_32(vd.frequency, h); } - return h; + return hash_fmix32(h); } }; @@ -496,14 +501,14 @@ class RenderingDeviceVulkan : public RenderingDevice { int vertex_count = 0; uint32_t max_instances_allowed = 0; - Vector<VkBuffer> buffers; //not owned, just referenced + Vector<VkBuffer> buffers; // Not owned, just referenced. Vector<VkDeviceSize> offsets; }; RID_Owner<VertexArray, true> vertex_array_owner; struct IndexBuffer : public Buffer { - uint32_t max_index = 0; //used for validation + uint32_t max_index = 0; // Used for validation. uint32_t index_count = 0; VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; bool supports_restart_indices = false; @@ -512,8 +517,8 @@ class RenderingDeviceVulkan : public RenderingDevice { RID_Owner<IndexBuffer, true> index_buffer_owner; struct IndexArray { - uint32_t max_index = 0; //remember the maximum index here too, for validation - VkBuffer buffer; //not owned, inherited from index buffer + uint32_t max_index = 0; // Remember the maximum index here too, for validation. + VkBuffer buffer; // Not owned, inherited from index buffer. uint32_t offset = 0; uint32_t indices = 0; VkIndexType index_type = VK_INDEX_TYPE_NONE_NV; @@ -544,12 +549,13 @@ class RenderingDeviceVulkan : public RenderingDevice { struct UniformInfo { UniformType type = UniformType::UNIFORM_TYPE_MAX; + bool writable = false; int binding = 0; uint32_t stages = 0; - int length = 0; //size of arrays (in total elements), or ubos (in bytes * total elements) + int length = 0; // Size of arrays (in total elements), or ubos (in bytes * total elements). bool operator!=(const UniformInfo &p_info) const { - return (binding != p_info.binding || type != p_info.type || stages != p_info.stages || length != p_info.length); + return (binding != p_info.binding || type != p_info.type || writable != p_info.writable || stages != p_info.stages || length != p_info.length); } bool operator<(const UniformInfo &p_info) const { @@ -559,6 +565,9 @@ class RenderingDeviceVulkan : public RenderingDevice { if (type != p_info.type) { return type < p_info.type; } + if (writable != p_info.writable) { + return writable < p_info.writable; + } if (stages != p_info.stages) { return stages < p_info.stages; } @@ -615,7 +624,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; }; - uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation + uint32_t vertex_input_mask = 0; // Inputs used, this is mostly for validation. uint32_t fragment_output_mask = 0; struct PushConstant { @@ -633,13 +642,12 @@ class RenderingDeviceVulkan : public RenderingDevice { }; bool is_compute = false; - int max_output = 0; Vector<Set> sets; Vector<uint32_t> set_formats; Vector<VkPipelineShaderStageCreateInfo> pipeline_stages; Vector<SpecializationConstant> specialization_constants; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; - String name; //used for debug + String name; // Used for debug. }; String _shader_uniform_debug(RID p_shader, int p_set = -1); @@ -711,7 +719,7 @@ class RenderingDeviceVulkan : public RenderingDevice { RID_Owner<Buffer, true> uniform_buffer_owner; RID_Owner<Buffer, true> storage_buffer_owner; - //texture buffer needs a view + // Texture buffer needs a view. struct TextureBuffer { Buffer buffer; VkBufferView view = VK_NULL_HANDLE; @@ -734,16 +742,16 @@ class RenderingDeviceVulkan : public RenderingDevice { DescriptorPool *pool = nullptr; DescriptorPoolKey pool_key; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; - //VkPipelineLayout pipeline_layout; //not owned, inherited from shader + //VkPipelineLayout pipeline_layout; // Not owned, inherited from shader. struct AttachableTexture { uint32_t bind; RID texture; }; - LocalVector<AttachableTexture> attachable_textures; //used for validation - Vector<Texture *> mutable_sampled_textures; //used for layout change - Vector<Texture *> mutable_storage_textures; //used for layout change - UniformSetInvalidatedCallback invalidated_callback = nullptr; + LocalVector<AttachableTexture> attachable_textures; // Used for validation. + Vector<Texture *> mutable_sampled_textures; // Used for layout change. + Vector<Texture *> mutable_storage_textures; // Used for layout change. + InvalidationCallback invalidated_callback = nullptr; void *invalidated_callback_userdata = nullptr; }; @@ -765,7 +773,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // was not supplied as intended. struct RenderPipeline { - //Cached values for validation + // Cached values for validation. #ifdef DEBUG_ENABLED struct Validation { FramebufferFormatID framebuffer_format = 0; @@ -777,10 +785,10 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t primitive_divisor = 0; } validation; #endif - //Actual pipeline + // Actual pipeline. RID shader; Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // not owned, needed for push constants + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; uint32_t push_constant_stages = 0; @@ -791,7 +799,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct ComputePipeline { RID shader; Vector<uint32_t> set_formats; - VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // not owned, needed for push constants + VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; // Not owned, needed for push constants. VkPipeline pipeline = VK_NULL_HANDLE; uint32_t push_constant_size = 0; uint32_t push_constant_stages = 0; @@ -817,7 +825,7 @@ class RenderingDeviceVulkan : public RenderingDevice { struct SplitDrawListAllocator { VkCommandPool command_pool = VK_NULL_HANDLE; - Vector<VkCommandBuffer> command_buffers; //one for each frame + Vector<VkCommandBuffer> command_buffers; // One for each frame. }; Vector<SplitDrawListAllocator> split_draw_list_allocators; @@ -866,11 +874,9 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t pipeline_dynamic_state = 0; VertexFormatID pipeline_vertex_format = INVALID_ID; RID pipeline_shader; - uint32_t invalid_set_from = 0; bool pipeline_uses_restart_indices = false; uint32_t pipeline_primitive_divisor = 0; uint32_t pipeline_primitive_minimum = 0; - Vector<uint32_t> pipeline_set_formats; uint32_t pipeline_push_constant_size = 0; bool pipeline_push_constant_supplied = false; } validation; @@ -899,7 +905,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool draw_list_unbind_color_textures = false; bool draw_list_unbind_depth_textures = false; - void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); + void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); @@ -944,7 +950,6 @@ class RenderingDeviceVulkan : public RenderingDevice { bool pipeline_active = false; RID pipeline_shader; uint32_t invalid_set_from = 0; - Vector<uint32_t> pipeline_set_formats; uint32_t pipeline_push_constant_size = 0; bool pipeline_push_constant_supplied = false; } validation; @@ -972,7 +977,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // when the frame is cycled. struct Frame { - //list in usage order, from last to free to first to free + // List in usage order, from last to free to first to free. List<Buffer> buffers_to_dispose_of; List<Texture> textures_to_dispose_of; List<Framebuffer> framebuffers_to_dispose_of; @@ -984,8 +989,8 @@ class RenderingDeviceVulkan : public RenderingDevice { List<ComputePipeline> compute_pipelines_to_dispose_of; VkCommandPool command_pool = VK_NULL_HANDLE; - VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; //used at the beginning of every frame for set-up - VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; //used at the beginning of every frame for set-up + VkCommandBuffer setup_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. + VkCommandBuffer draw_command_buffer = VK_NULL_HANDLE; // Used at the beginning of every frame for set-up. struct Timestamp { String description; @@ -994,21 +999,21 @@ class RenderingDeviceVulkan : public RenderingDevice { VkQueryPool timestamp_pool; - String *timestamp_names = nullptr; - uint64_t *timestamp_cpu_values = nullptr; + TightLocalVector<String> timestamp_names; + TightLocalVector<uint64_t> timestamp_cpu_values; uint32_t timestamp_count = 0; - String *timestamp_result_names = nullptr; - uint64_t *timestamp_cpu_result_values = nullptr; - uint64_t *timestamp_result_values = nullptr; + TightLocalVector<String> timestamp_result_names; + TightLocalVector<uint64_t> timestamp_cpu_result_values; + TightLocalVector<uint64_t> timestamp_result_values; uint32_t timestamp_result_count = 0; uint64_t index = 0; }; uint32_t max_timestamp_query_elements = 0; - Frame *frames = nullptr; //frames available, for main device they are cycled (usually 3), for local devices only 1 - int frame = 0; //current frame - int frame_count = 0; //total amount of frames + TightLocalVector<Frame> frames; // Frames available, for main device they are cycled (usually 3), for local devices only 1. + int frame = 0; // Current frame. + int frame_count = 0; // Total amount of frames. uint64_t frames_drawn = 0; RID local_device; bool local_device_processing = false; @@ -1035,6 +1040,12 @@ class RenderingDeviceVulkan : public RenderingDevice { void _finalize_command_bufers(); void _begin_frame(); +#ifdef DEV_ENABLED + HashMap<RID, String> resource_names; +#endif + + VkSampleCountFlagBits _ensure_supported_sample_count(TextureSamples p_requested_sample_count) const; + public: virtual RID texture_create(const TextureFormat &p_format, const TextureView &p_view, const Vector<Vector<uint8_t>> &p_data = Vector<Vector<uint8_t>>()); virtual RID texture_create_shared(const TextureView &p_view, RID p_with_texture); @@ -1058,13 +1069,15 @@ public: /*********************/ virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1); - virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); + virtual FramebufferFormatID framebuffer_format_create_multipass(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, uint32_t p_view_count = 1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass = 0); virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); - virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); + virtual RID framebuffer_create_multipass(const Vector<RID> &p_texture_attachments, const Vector<FramebufferPass> &p_passes, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); + virtual bool framebuffer_is_valid(RID p_framebuffer) const; + virtual void framebuffer_set_invalidation_callback(RID p_framebuffer, InvalidationCallback p_callback, void *p_userdata); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); @@ -1080,7 +1093,7 @@ public: virtual RID vertex_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), bool p_use_as_storage = false); - // Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated + // Internally reference counted, this ID is warranted to be unique for the same description, but needs to be freed as many times as it was allocated. virtual VertexFormatID vertex_format_create(const Vector<VertexAttribute> &p_vertex_formats); virtual RID vertex_array_create(uint32_t p_vertex_count, VertexFormatID p_vertex_format, const Vector<RID> &p_src_buffers); @@ -1109,9 +1122,9 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); virtual bool uniform_set_is_valid(RID p_uniform_set); - virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata); + virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, InvalidationCallback p_callback, void *p_userdata); - virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); //works for any buffer + virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); // Works for any buffer. virtual Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, uint32_t p_post_barrier = BARRIER_MASK_ALL); virtual Vector<uint8_t> buffer_get_data(RID p_buffer); @@ -1146,6 +1159,7 @@ public: virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); + virtual void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index); virtual void draw_list_bind_vertex_array(DrawListID p_list, RID p_vertex_array); @@ -1203,16 +1217,16 @@ public: /**** Limits ****/ /****************/ - virtual uint64_t limit_get(Limit p_limit); + virtual uint64_t limit_get(Limit p_limit) const; virtual void prepare_screen_for_drawing(); void initialize(VulkanContext *p_context, bool p_local_device = false); void finalize(); - virtual void swap_buffers(); //for main device + virtual void swap_buffers(); // For main device. - virtual void submit(); //for local device - virtual void sync(); //for local device + virtual void submit(); // For local device. + virtual void sync(); // For local device. virtual uint32_t get_frame_delay() const; @@ -1234,6 +1248,8 @@ public: virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0); + virtual bool has_feature(const Features p_feature) const; + RenderingDeviceVulkan(); ~RenderingDeviceVulkan(); }; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 7944057041..464ab474e1 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -48,6 +48,122 @@ VulkanHooks *VulkanContext::vulkan_hooks = nullptr; +Vector<VkAttachmentReference> VulkanContext::_convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs) { + Vector<VkAttachmentReference> att_refs; + + if (p_refs != nullptr) { + for (uint32_t i = 0; i < p_count; i++) { + // We lose aspectMask in this conversion but we don't use it currently. + + VkAttachmentReference ref = { + p_refs[i].attachment, /* attachment */ + p_refs[i].layout /* layout */ + }; + + att_refs.push_back(ref); + } + } + + return att_refs; +} + +VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { + if (has_renderpass2_ext) { + if (fpCreateRenderPass2KHR == nullptr) { + fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetDeviceProcAddr(p_device, "vkCreateRenderPass2KHR"); + } + + if (fpCreateRenderPass2KHR == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } else { + return (fpCreateRenderPass2KHR)(p_device, p_create_info, p_allocator, p_render_pass); + } + } else { + // need to fall back on vkCreateRenderPass + + const void *next = p_create_info->pNext; // ATM we only support multiview which should work if supported. + + Vector<VkAttachmentDescription> attachments; + for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { + // Basically the old layout just misses type and next. + VkAttachmentDescription att = { + p_create_info->pAttachments[i].flags, /* flags */ + p_create_info->pAttachments[i].format, /* format */ + p_create_info->pAttachments[i].samples, /* samples */ + p_create_info->pAttachments[i].loadOp, /* loadOp */ + p_create_info->pAttachments[i].storeOp, /* storeOp */ + p_create_info->pAttachments[i].stencilLoadOp, /* stencilLoadOp */ + p_create_info->pAttachments[i].stencilStoreOp, /* stencilStoreOp */ + p_create_info->pAttachments[i].initialLayout, /* initialLayout */ + p_create_info->pAttachments[i].finalLayout /* finalLayout */ + }; + + attachments.push_back(att); + } + + Vector<VkSubpassDescription> subpasses; + for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { + // Here we need to do more, again it's just stripping out type and next + // but we have VkAttachmentReference2 to convert to VkAttachmentReference. + // Also viewmask is not supported but we don't use it outside of multiview. + + Vector<VkAttachmentReference> input_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].inputAttachmentCount, p_create_info->pSubpasses[i].pInputAttachments); + Vector<VkAttachmentReference> color_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pColorAttachments); + Vector<VkAttachmentReference> resolve_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pResolveAttachments); + Vector<VkAttachmentReference> depth_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pDepthStencilAttachment); + + VkSubpassDescription subpass = { + p_create_info->pSubpasses[i].flags, /* flags */ + p_create_info->pSubpasses[i].pipelineBindPoint, /* pipelineBindPoint */ + p_create_info->pSubpasses[i].inputAttachmentCount, /* inputAttachmentCount */ + input_attachments.size() == 0 ? nullptr : input_attachments.ptr(), /* pInputAttachments */ + p_create_info->pSubpasses[i].colorAttachmentCount, /* colorAttachmentCount */ + color_attachments.size() == 0 ? nullptr : color_attachments.ptr(), /* pColorAttachments */ + resolve_attachments.size() == 0 ? nullptr : resolve_attachments.ptr(), /* pResolveAttachments */ + depth_attachments.size() == 0 ? nullptr : depth_attachments.ptr(), /* pDepthStencilAttachment */ + p_create_info->pSubpasses[i].preserveAttachmentCount, /* preserveAttachmentCount */ + p_create_info->pSubpasses[i].pPreserveAttachments /* pPreserveAttachments */ + }; + + subpasses.push_back(subpass); + } + + Vector<VkSubpassDependency> dependencies; + for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { + // We lose viewOffset here but again I don't believe we use this anywhere. + VkSubpassDependency dep = { + p_create_info->pDependencies[i].srcSubpass, /* srcSubpass */ + p_create_info->pDependencies[i].dstSubpass, /* dstSubpass */ + p_create_info->pDependencies[i].srcStageMask, /* srcStageMask */ + p_create_info->pDependencies[i].dstStageMask, /* dstStageMask */ + p_create_info->pDependencies[i].srcAccessMask, /* srcAccessMask */ + p_create_info->pDependencies[i].dstAccessMask, /* dstAccessMask */ + p_create_info->pDependencies[i].dependencyFlags, /* dependencyFlags */ + }; + + dependencies.push_back(dep); + } + + // CorrelatedViewMask is not supported in vkCreateRenderPass but we + // currently only use this for multiview. + // We'll need to look into this. + + VkRenderPassCreateInfo create_info = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, /* sType */ + next, /* pNext*/ + p_create_info->flags, /* flags */ + (uint32_t)attachments.size(), /* attachmentCount */ + attachments.ptr(), /* pAttachments */ + (uint32_t)subpasses.size(), /* subpassCount */ + subpasses.ptr(), /* pSubpasses */ + (uint32_t)dependencies.size(), /* */ + dependencies.ptr(), /* */ + }; + + return vkCreateRenderPass(device, &create_info, p_allocator, p_render_pass); + } +} + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -72,20 +188,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( strstr(pCallbackData->pMessage, "must be a memory object") != nullptr) { return VK_FALSE; } - /* - // This is a valid warning because its illegal in Vulkan, but in practice it should work according to VK_KHR_maintenance2 - if (strstr(pCallbackData->pMessage, "VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT") != nullptr) { - return VK_FALSE; - } - if (strstr(pCallbackData->pMessage, "VK_FORMAT_R4G4B4A4_UNORM_PACK16 with tiling VK_IMAGE_TILING_OPTIMAL does not support usage that includes VK_IMAGE_USAGE_STORAGE_BIT") != nullptr) { - return VK_FALSE; - } -*/ - // Workaround for Vulkan-Loader usability bug: https://github.com/KhronosGroup/Vulkan-Loader/issues/262. - if (strstr(pCallbackData->pMessage, "wrong ELF class: ELFCLASS32") != nullptr) { - return VK_FALSE; - } if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { return VK_FALSE; } @@ -215,17 +318,17 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *c Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { static const LocalVector<LocalVector<const char *>> instance_validation_layers_alt{ - // Preferred set of validation layers + // Preferred set of validation layers. { "VK_LAYER_KHRONOS_validation" }, - // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers + // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers. { "VK_LAYER_LUNARG_standard_validation" }, - // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers + // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers. { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" } }; - // Clear out-arguments + // Clear out-arguments. *count = 0; if (names != nullptr) { *names = nullptr; @@ -269,7 +372,7 @@ typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); Error VulkanContext::_obtain_vulkan_version() { // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description - // for Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. + // For Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion"); if (func != nullptr) { uint32_t api_version; @@ -279,15 +382,15 @@ Error VulkanContext::_obtain_vulkan_version() { vulkan_minor = VK_API_VERSION_MINOR(api_version); vulkan_patch = VK_API_VERSION_PATCH(api_version); } else { - // according to the documentation this shouldn't fail with anything except a memory allocation error - // in which case we're in deep trouble anyway + // According to the documentation this shouldn't fail with anything except a memory allocation error + // in which case we're in deep trouble anyway. ERR_FAIL_V(ERR_CANT_CREATE); } } else { print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0."); } - // we don't go above 1.2 + // We don't go above 1.2. if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) { vulkan_major = 1; vulkan_minor = 2; @@ -303,11 +406,21 @@ Error VulkanContext::_initialize_extensions() { enabled_extension_count = 0; enabled_debug_utils = false; enabled_debug_report = false; - /* Look for instance extensions */ + // Look for instance extensions. VkBool32 surfaceExtFound = 0; VkBool32 platformSurfaceExtFound = 0; memset(extension_names, 0, sizeof(extension_names)); + // Only enable debug utils in verbose mode or DEV_ENABLED. + // End users would get spammed with messages of varying verbosity due to the + // mess that thirdparty layers/extensions and drivers seem to leave in their + // wake, making the Windows registry a bottomless pit of broken layer JSON. +#ifdef DEV_ENABLED + bool want_debug_utils = true; +#else + bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); +#endif + VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); @@ -335,8 +448,10 @@ Error VulkanContext::_initialize_extensions() { } } if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - enabled_debug_utils = true; + if (want_debug_utils) { + extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + enabled_debug_utils = true; + } } if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; @@ -403,7 +518,7 @@ String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { res += ", STAGE_COMPUTE"; } - /* these are not defined on Android GRMBL */ + // These are not defined on Android GRMBL. if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { res += ", STAGE_RAYGEN_KHR"; } @@ -429,7 +544,7 @@ String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { res += ", STAGE_MESH_NV"; } - return res.substr(2); // remove first ", " + return res.substr(2); // Remove first ", ". } uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { @@ -494,19 +609,22 @@ String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { res += ", FEATURE_PARTITIONED_NV"; } - return res.substr(2); // remove first ", " + return res.substr(2); // Remove first ", ". } Error VulkanContext::_check_capabilities() { // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html // https://www.khronos.org/blog/vulkan-subgroup-tutorial - // for Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. + // For Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. - // so we check if the functions are accessible by getting their function pointers and skipping if not - // (note that the desktop loader does a better job here but the android loader doesn't) + // So we check if the functions are accessible by getting their function pointers and skipping if not + // (note that the desktop loader does a better job here but the android loader doesn't.) - // assume not supported until proven otherwise + // Assume not supported until proven otherwise. + vrs_capabilities.pipeline_vrs_supported = false; + vrs_capabilities.primitive_vrs_supported = false; + vrs_capabilities.attachment_vrs_supported = false; multiview_capabilities.is_supported = false; multiview_capabilities.geometry_shader_is_supported = false; multiview_capabilities.tessellation_shader_is_supported = false; @@ -523,17 +641,25 @@ Error VulkanContext::_check_capabilities() { storage_buffer_capabilities.storage_push_constant_16_is_supported = false; storage_buffer_capabilities.storage_input_output_16 = false; - // check for extended features + // Check for extended features. PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); if (vkGetPhysicalDeviceFeatures2_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name + // In Vulkan 1.0 might be accessible under its original extension name. vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); } if (vkGetPhysicalDeviceFeatures2_func != nullptr) { - // check our extended features + // Check our extended features. + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, + /*pNext*/ nullptr, + /*pipelineFragmentShadingRate*/ false, + /*primitiveFragmentShadingRate*/ false, + /*attachmentFragmentShadingRate*/ false, + }; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, - /*pNext*/ nullptr, + /*pNext*/ &vrs_features, /*shaderFloat16*/ false, /*shaderInt8*/ false, }; @@ -561,54 +687,114 @@ Error VulkanContext::_check_capabilities() { vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); - multiview_capabilities.is_supported = multiview_features.multiview; - multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; - multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + // We must check that the relative extension is present before assuming a + // feature as enabled. Actually, according to the spec we shouldn't add the + // structs in pNext at all, but this works fine. + // See also: https://github.com/godotengine/godot/issues/65409 + for (uint32_t i = 0; i < enabled_extension_count; ++i) { + if (!strcmp(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, extension_names[i])) { + vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; + vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; + vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; - shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; - shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + continue; + } - storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; - storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; - storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; - storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; + if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, extension_names[i])) { + multiview_capabilities.is_supported = multiview_features.multiview; + multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; + multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; + + continue; + } + + if (!strcmp(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, extension_names[i])) { + shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; + shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; + + continue; + } + + if (!strcmp(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, extension_names[i])) { + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; + storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; + storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; + + continue; + } + } } - // check extended properties + // Check extended properties. PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2"); if (device_properties_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name + // In Vulkan 1.0 might be accessible under its original extension name. device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); } if (device_properties_func != nullptr) { - VkPhysicalDeviceMultiviewProperties multiviewProperties; - VkPhysicalDeviceSubgroupProperties subgroupProperties; - VkPhysicalDeviceProperties2 physicalDeviceProperties; + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties{}; + VkPhysicalDeviceMultiviewProperties multiviewProperties{}; + VkPhysicalDeviceSubgroupProperties subgroupProperties{}; + VkPhysicalDeviceProperties2 physicalDeviceProperties{}; + void *nextptr = nullptr; - subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nullptr; + if (!(vulkan_major == 1 && vulkan_minor == 0)) { + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nextptr; - physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + nextptr = &subgroupProperties; + } if (multiview_capabilities.is_supported) { multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - multiviewProperties.pNext = &subgroupProperties; + multiviewProperties.pNext = nextptr; - physicalDeviceProperties.pNext = &multiviewProperties; - } else { - physicalDeviceProperties.pNext = &subgroupProperties; + nextptr = &multiviewProperties; } + if (vrs_capabilities.attachment_vrs_supported) { + vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; + vrsProperties.pNext = nextptr; + + nextptr = &vrsProperties; + } + + physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + physicalDeviceProperties.pNext = nextptr; + device_properties_func(gpu, &physicalDeviceProperties); subgroup_capabilities.size = subgroupProperties.subgroupSize; subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; // Note: quadOperationsInAllStages will be true if: - // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT - // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT + // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT. + // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT. subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + print_verbose("- Vulkan Variable Rate Shading supported:"); + if (vrs_capabilities.pipeline_vrs_supported) { + print_verbose(" Pipeline fragment shading rate"); + } + if (vrs_capabilities.primitive_vrs_supported) { + print_verbose(" Primitive fragment shading rate"); + } + if (vrs_capabilities.attachment_vrs_supported) { + // TODO expose these somehow to the end user. + vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; + vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; + vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; + + print_verbose(String(" Attachment fragment shading rate") + String(", min texel size: (") + itos(vrs_capabilities.min_texel_size.x) + String(", ") + itos(vrs_capabilities.min_texel_size.y) + String(")") + String(", max texel size: (") + itos(vrs_capabilities.max_texel_size.x) + String(", ") + itos(vrs_capabilities.max_texel_size.y) + String(")")); + } + + } else { + print_verbose("- Vulkan Variable Rate Shading not supported"); + } + if (multiview_capabilities.is_supported) { multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; @@ -635,10 +821,10 @@ Error VulkanContext::_check_capabilities() { } Error VulkanContext::_create_instance() { - /* obtain version */ + // Obtain Vulkan version. _obtain_vulkan_version(); - /* initialise extensions */ + // Initialize extensions. { Error err = _initialize_extensions(); if (err != OK) { @@ -646,7 +832,7 @@ Error VulkanContext::_create_instance() { } } - CharString cs = ProjectSettings::get_singleton()->get("application/config/name").operator String().utf8(); + CharString cs = GLOBAL_GET("application/config/name").operator String().utf8(); const VkApplicationInfo app = { /*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO, /*pNext*/ nullptr, @@ -673,7 +859,7 @@ Error VulkanContext::_create_instance() { VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info; VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{}; if (enabled_debug_utils) { - // VK_EXT_debug_utils style + // VK_EXT_debug_utils style. dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; dbg_messenger_create_info.pNext = nullptr; dbg_messenger_create_info.flags = 0; @@ -726,8 +912,7 @@ Error VulkanContext::_create_instance() { #endif if (enabled_debug_utils) { - // Setup VK_EXT_debug_utils function pointers always (we use them for - // debug labels and names). + // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). CreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT"); DestroyDebugUtilsMessengerEXT = @@ -800,7 +985,7 @@ Error VulkanContext::_create_instance() { } Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { - /* Make initial call to query gpu_count, then second call for gpu info*/ + // Make initial call to query gpu_count, then second call for gpu info. uint32_t gpu_count = 0; VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -836,7 +1021,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { return ERR_CANT_CREATE; } - // not really needed but nice to print the correct entry + // Not really needed but nice to print the correct entry. for (uint32_t i = 0; i < gpu_count; ++i) { if (physical_devices[i] == gpu) { device_index = i; @@ -845,8 +1030,8 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { } } else { // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. - // The device should really be a preference, but for now choosing a discrete GPU over the - // integrated one is better than the default. + // The device should really be a preference, but for now choosing a discrete GPU over the + // integrated one is better than the default. int type_selected = -1; print_verbose("Vulkan devices:"); @@ -948,13 +1133,13 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { free(physical_devices); - /* Look for device extensions */ + // Look for device extensions. uint32_t device_extension_count = 0; VkBool32 swapchainExtFound = 0; enabled_extension_count = 0; memset(extension_names, 0, sizeof(extension_names)); - /* Get identifier properties */ + // Get identifier properties. vkGetPhysicalDeviceProperties(gpu, &gpu_props); device_name = gpu_props.deviceName; @@ -996,9 +1181,17 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; } if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) { - // if multiview is supported, enable it + // If multiview is supported, enable it. extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME; } + if (!strcmp(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, device_extensions[i].extensionName)) { + // if shading rate image is supported, enable it + extension_names[enabled_extension_count++] = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, device_extensions[i].extensionName)) { + has_renderpass2_ext = true; + extension_names[enabled_extension_count++] = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; + } if (enabled_extension_count >= MAX_EXTENSIONS) { free(device_extensions); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); @@ -1049,19 +1242,18 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n" "vkCreateInstance Failure"); - /* Call with nullptr data to get count */ + // Call with nullptr data to get count. vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); queue_props = (VkQueueFamilyProperties *)malloc(queue_family_count * sizeof(VkQueueFamilyProperties)); vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, queue_props); - // Query fine-grained feature support for this device. // If app has specific feature requirements it should check supported // features based on this query vkGetPhysicalDeviceFeatures(gpu, &physical_device_features); - physical_device_features.robustBufferAccess = false; //turn off robust buffer access, which can hamper performance on some hardware + physical_device_features.robustBufferAccess = false; // Turn off robust buffer access, which can hamper performance on some hardware. #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ { \ @@ -1076,7 +1268,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); - // get info about what our vulkan driver is capable off + // Gets capability info for current Vulkan driver. { Error res = _check_capabilities(); if (res != OK) { @@ -1110,11 +1302,23 @@ Error VulkanContext::_create_device() { }; nextptr = &shader_features; + VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features; + if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { + // Insert into our chain to enable these features if they are available. + vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; + vrs_features.pNext = nextptr; + vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; + vrs_features.primitiveFragmentShadingRate = vrs_capabilities.primitive_vrs_supported; + vrs_features.attachmentFragmentShadingRate = vrs_capabilities.attachment_vrs_supported; + + nextptr = &vrs_features; + } + VkPhysicalDeviceVulkan11Features vulkan11features; VkPhysicalDevice16BitStorageFeaturesKHR storage_feature; VkPhysicalDeviceMultiviewFeatures multiview_features; if (vulkan_major > 1 || vulkan_minor >= 2) { - // In Vulkan 1.2 and newer we use a newer struct to enable various features + // In Vulkan 1.2 and newer we use a newer struct to enable various features. vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; vulkan11features.pNext = nextptr; @@ -1132,7 +1336,7 @@ Error VulkanContext::_create_device() { vulkan11features.shaderDrawParameters = 0; nextptr = &vulkan11features; } else { - // On Vulkan 1.0 and 1.1 we use our older structs to initialise these features + // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; storage_feature.pNext = nextptr; storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; @@ -1161,7 +1365,7 @@ Error VulkanContext::_create_device() { /*ppEnabledLayerNames*/ nullptr, /*enabledExtensionCount*/ enabled_extension_count, /*ppEnabledExtensionNames*/ (const char *const *)extension_names, - /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here + /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here. }; if (separate_present_queue) { queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -1193,7 +1397,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { } // Search for a graphics and a present queue in the array of queue - // families, try to find one that supports both + // families, try to find one that supports both. uint32_t graphicsQueueFamilyIndex = UINT32_MAX; uint32_t presentQueueFamilyIndex = UINT32_MAX; for (uint32_t i = 0; i < queue_family_count; i++) { @@ -1223,7 +1427,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { free(supportsPresent); - // Generate error if could not find both a graphics and a present queue + // Generate error if could not find both a graphics and a present queue. ERR_FAIL_COND_V_MSG(graphicsQueueFamilyIndex == UINT32_MAX || presentQueueFamilyIndex == UINT32_MAX, ERR_CANT_CREATE, "Could not find both graphics and present queues\n"); @@ -1279,7 +1483,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { color_space = surfFormats[0].colorSpace; } else { // These should be ordered with the ones we want to use on top and fallback modes further down - // we want an 32bit RGBA unsigned normalised buffer or similar + // we want a 32bit RGBA unsigned normalized buffer or similar. const VkFormat allowed_formats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM @@ -1291,7 +1495,7 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1"); } - // Find the first format that we support + // Find the first format that we support. format = VK_FORMAT_UNDEFINED; for (uint32_t af = 0; af < allowed_formats_count && format == VK_FORMAT_UNDEFINED; af++) { for (uint32_t sf = 0; sf < formatCount && format == VK_FORMAT_UNDEFINED; sf++) { @@ -1323,7 +1527,7 @@ Error VulkanContext::_create_semaphores() { VkResult err; // Create semaphores to synchronize acquiring presentable buffers before - // rendering and waiting for drawing to be complete before presenting + // rendering and waiting for drawing to be complete before presenting. VkSemaphoreCreateInfo semaphoreCreateInfo = { /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, /*pNext*/ nullptr, @@ -1331,7 +1535,7 @@ Error VulkanContext::_create_semaphores() { }; // Create fences that we can use to throttle if we get too far - // ahead of the image presents + // ahead of the image presents. VkFenceCreateInfo fence_ci = { /*sType*/ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, /*pNext*/ nullptr, @@ -1351,7 +1555,7 @@ Error VulkanContext::_create_semaphores() { } frame_index = 0; - // Get Memory information and properties + // Get Memory information and properties. vkGetPhysicalDeviceMemoryProperties(gpu, &memory_properties); return OK; @@ -1361,6 +1565,24 @@ bool VulkanContext::_use_validation_layers() { return Engine::get_singleton()->is_validation_layers_enabled(); } +VkExtent2D VulkanContext::_compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const { + // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + if (p_surf_capabilities.currentExtent.width == 0xFFFFFFFF) { + // If the surface size is undefined, the size is set to the size + // of the images requested, which must fit within the minimum and + // maximum values. + VkExtent2D extent = {}; + extent.width = CLAMP((uint32_t)(*p_window_width), p_surf_capabilities.minImageExtent.width, p_surf_capabilities.maxImageExtent.width); + extent.height = CLAMP((uint32_t)(*p_window_height), p_surf_capabilities.minImageExtent.height, p_surf_capabilities.maxImageExtent.height); + return extent; + } else { + // If the surface size is defined, the swap chain size must match. + *p_window_width = p_surf_capabilities.currentExtent.width; + *p_window_height = p_surf_capabilities.currentExtent.height; + return p_surf_capabilities.currentExtent; + } +} + Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); @@ -1426,7 +1648,7 @@ bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) VkRenderPass VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); Window *w = &windows[p_window]; - //vulkan use of currentbuffer + // Vulkan use of currentbuffer. return w->render_pass; } @@ -1434,7 +1656,7 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); ERR_FAIL_COND_V(!buffers_prepared, VK_NULL_HANDLE); Window *w = &windows[p_window]; - //vulkan use of currentbuffer + // Vulkan use of currentbuffer. if (w->swapchain_image_resources != VK_NULL_HANDLE) { return w->swapchain_image_resources[w->current_buffer].framebuffer; } else { @@ -1459,7 +1681,7 @@ Error VulkanContext::_clean_up_swap_chain(Window *window) { } vkDeviceWaitIdle(device); - //this destroys images associated it seems + // This destroys images associated it seems. fpDestroySwapchainKHR(device, window->swapchain, nullptr); window->swapchain = VK_NULL_HANDLE; vkDestroyRenderPass(device, window->render_pass, nullptr); @@ -1485,7 +1707,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { _clean_up_swap_chain(window); } - // Check the surface capabilities and formats + // Check the surface capabilities and formats. VkSurfaceCapabilitiesKHR surfCapabilities; err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); @@ -1501,52 +1723,27 @@ Error VulkanContext::_update_swap_chain(Window *window) { ERR_FAIL_V(ERR_CANT_CREATE); } - VkExtent2D swapchainExtent; - // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. - if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) { - // If the surface size is undefined, the size is set to the size - // of the images requested, which must fit within the minimum and - // maximum values. - swapchainExtent.width = window->width; - swapchainExtent.height = window->height; - - if (swapchainExtent.width < surfCapabilities.minImageExtent.width) { - swapchainExtent.width = surfCapabilities.minImageExtent.width; - } else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) { - swapchainExtent.width = surfCapabilities.maxImageExtent.width; - } - - if (swapchainExtent.height < surfCapabilities.minImageExtent.height) { - swapchainExtent.height = surfCapabilities.minImageExtent.height; - } else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) { - swapchainExtent.height = surfCapabilities.maxImageExtent.height; - } - } else { - // If the surface size is defined, the swap chain size must match - swapchainExtent = surfCapabilities.currentExtent; - window->width = surfCapabilities.currentExtent.width; - window->height = surfCapabilities.currentExtent.height; - } + VkExtent2D swapchainExtent = _compute_swapchain_extent(surfCapabilities, &window->width, &window->height); if (window->width == 0 || window->height == 0) { free(presentModes); - //likely window minimized, no swapchain created + // Likely window minimized, no swapchain created. return OK; } // The FIFO present mode is guaranteed by the spec to be supported // and to have no tearing. It's a great default present mode to use. - // There are times when you may wish to use another present mode. The - // following code shows how to select them, and the comments provide some - // reasons you may wish to use them. + // There are times when you may wish to use another present mode. The + // following code shows how to select them, and the comments provide some + // reasons you may wish to use them. // // It should be noted that Vulkan 1.0 doesn't provide a method for - // synchronizing rendering with the presentation engine's display. There + // synchronizing rendering with the presentation engine's display. There // is a method provided for throttling rendering with the display, but // there are some presentation engines for which this method will not work. // If an application doesn't throttle its rendering, and if it renders much // faster than the refresh rate of the display, this can waste power on - // mobile devices. That is because power is being spent rendering images + // mobile devices. That is because power is being spent rendering images // that may never be seen. // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about @@ -1591,8 +1788,23 @@ Error VulkanContext::_update_swap_chain(Window *window) { if (present_mode_available) { window->presentMode = requested_present_mode; } else { - WARN_PRINT("Requested VSync mode is not available!"); - window->vsync_mode = DisplayServer::VSYNC_ENABLED; //Set to default + String present_mode_string; + switch (window->vsync_mode) { + case DisplayServer::VSYNC_MAILBOX: + present_mode_string = "Mailbox"; + break; + case DisplayServer::VSYNC_ADAPTIVE: + present_mode_string = "Adaptive"; + break; + case DisplayServer::VSYNC_ENABLED: + present_mode_string = "Enabled"; + break; + case DisplayServer::VSYNC_DISABLED: + present_mode_string = "Disabled"; + break; + } + WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_string)); + window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default. } print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); @@ -1601,15 +1813,15 @@ Error VulkanContext::_update_swap_chain(Window *window) { // Determine the number of VkImages to use in the swap chain. // Application desires to acquire 3 images at a time for triple - // buffering + // buffering. uint32_t desiredNumOfSwapchainImages = 3; if (desiredNumOfSwapchainImages < surfCapabilities.minImageCount) { desiredNumOfSwapchainImages = surfCapabilities.minImageCount; } // If maxImageCount is 0, we can ask for as many images as we want; - // otherwise we're limited to maxImageCount + // otherwise we're limited to maxImageCount. if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { - // Application must settle for fewer images than desired: + // Application must settle for fewer images than desired. desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; } @@ -1620,18 +1832,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - }; - for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { - if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { - compositeAlpha = compositeAlphaFlags[i]; - break; + + if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { + // Find a supported composite alpha mode - one of these is guaranteed to be set. + VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + }; + + for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { + if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { + compositeAlpha = compositeAlphaFlags[i]; + break; + } } } @@ -1667,7 +1883,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { ERR_FAIL_COND_V(err, ERR_CANT_CREATE); if (swapchainImageCount == 0) { - //assign here for the first time. + // Assign here for the first time. swapchainImageCount = sp_image_count; } else { ERR_FAIL_COND_V(swapchainImageCount != sp_image_count, ERR_BUG); @@ -1725,7 +1941,9 @@ Error VulkanContext::_update_swap_chain(Window *window) { /******** FRAMEBUFFER ************/ { - const VkAttachmentDescription attachment = { + const VkAttachmentDescription2KHR attachment = { + /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, + /*pNext*/ nullptr, /*flags*/ 0, /*format*/ format, /*samples*/ VK_SAMPLE_COUNT_1_BIT, @@ -1737,14 +1955,20 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*finalLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, }; - const VkAttachmentReference color_reference = { + const VkAttachmentReference2KHR color_reference = { + /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, + /*pNext*/ nullptr, /*attachment*/ 0, /*layout*/ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + /*aspectMask*/ 0, }; - const VkSubpassDescription subpass = { + const VkSubpassDescription2KHR subpass = { + /*sType*/ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, + /*pNext*/ nullptr, /*flags*/ 0, /*pipelineBindPoint*/ VK_PIPELINE_BIND_POINT_GRAPHICS, + /*viewMask*/ 0, /*inputAttachmentCount*/ 0, /*pInputAttachments*/ nullptr, /*colorAttachmentCount*/ 1, @@ -1754,8 +1978,9 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*preserveAttachmentCount*/ 0, /*pPreserveAttachments*/ nullptr, }; - const VkRenderPassCreateInfo rp_info = { - /*sTyp*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + + const VkRenderPassCreateInfo2KHR rp_info = { + /*sType*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, /*pNext*/ nullptr, /*flags*/ 0, /*attachmentCount*/ 1, @@ -1764,9 +1989,11 @@ Error VulkanContext::_update_swap_chain(Window *window) { /*pSubpasses*/ &subpass, /*dependencyCount*/ 0, /*pDependencies*/ nullptr, + /*correlatedViewMaskCount*/ 0, + /*pCorrelatedViewMasks*/ nullptr, }; - err = vkCreateRenderPass(device, &rp_info, nullptr, &window->render_pass); + err = vkCreateRenderPass2KHR(device, &rp_info, nullptr, &window->render_pass); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); for (uint32_t i = 0; i < swapchainImageCount; i++) { @@ -1839,7 +2066,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { } } - //reset current buffer + // Reset current buffer. window->current_buffer = 0; return OK; @@ -1860,27 +2087,30 @@ Error VulkanContext::initialize() { return OK; } -void VulkanContext::set_setup_buffer(const VkCommandBuffer &pCommandBuffer) { - command_buffer_queue.write[0] = pCommandBuffer; +void VulkanContext::set_setup_buffer(VkCommandBuffer p_command_buffer) { + command_buffer_queue.write[0] = p_command_buffer; } -void VulkanContext::append_command_buffer(const VkCommandBuffer &pCommandBuffer) { +void VulkanContext::append_command_buffer(VkCommandBuffer p_command_buffer) { if (command_buffer_queue.size() <= command_buffer_count) { command_buffer_queue.resize(command_buffer_count + 1); } - command_buffer_queue.write[command_buffer_count] = pCommandBuffer; + command_buffer_queue.write[command_buffer_count] = p_command_buffer; command_buffer_count++; } void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { - // ensure everything else pending is executed + // Ensure everything else pending is executed. vkDeviceWaitIdle(device); - //flush the pending setup buffer + // Flush the pending setup buffer. - if (p_flush_setup && command_buffer_queue[0]) { - //use a fence to wait for everything done + bool setup_flushable = p_flush_setup && command_buffer_queue[0]; + bool pending_flushable = p_flush_pending && command_buffer_count > 1; + + if (setup_flushable) { + // Use a fence to wait for everything done. VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; @@ -1889,33 +2119,33 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { submit_info.pWaitSemaphores = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = command_buffer_queue.ptr(); - submit_info.signalSemaphoreCount = 0; - submit_info.pSignalSemaphores = nullptr; + submit_info.signalSemaphoreCount = pending_flushable ? 1 : 0; + submit_info.pSignalSemaphores = pending_flushable ? &draw_complete_semaphores[frame_index] : nullptr; VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); command_buffer_queue.write[0] = nullptr; ERR_FAIL_COND(err); - vkDeviceWaitIdle(device); } - if (p_flush_pending && command_buffer_count > 1) { - //use a fence to wait for everything done + if (pending_flushable) { + // Use a fence to wait for everything to finish. VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = nullptr; - submit_info.waitSemaphoreCount = 0; - submit_info.pWaitSemaphores = nullptr; + VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + submit_info.pWaitDstStageMask = setup_flushable ? &wait_stage_mask : nullptr; + submit_info.waitSemaphoreCount = setup_flushable ? 1 : 0; + submit_info.pWaitSemaphores = setup_flushable ? &draw_complete_semaphores[frame_index] : nullptr; submit_info.commandBufferCount = command_buffer_count - 1; submit_info.pCommandBuffers = command_buffer_queue.ptr() + 1; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - ERR_FAIL_COND(err); - vkDeviceWaitIdle(device); - command_buffer_count = 1; + ERR_FAIL_COND(err); } + + vkDeviceWaitIdle(device); } Error VulkanContext::prepare_buffers() { @@ -1925,7 +2155,7 @@ Error VulkanContext::prepare_buffers() { VkResult err; - // Ensure no more than FRAME_LAG renderings are outstanding + // Ensure no more than FRAME_LAG renderings are outstanding. vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX); vkResetFences(device, 1, &fences[frame_index]); @@ -1939,19 +2169,19 @@ Error VulkanContext::prepare_buffers() { } do { - // Get the index of the next available swapchain image: + // Get the index of the next available swapchain image. err = fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) { - // swapchain is out of date (e.g. the window was resized) and - // must be recreated: + // Swapchain is out of date (e.g. the window was resized) and + // must be recreated. print_verbose("Vulkan: Early out of date swapchain, recreating."); - //resize_notify(); + // resize_notify(); _update_swap_chain(w); } else if (err == VK_SUBOPTIMAL_KHR) { - // swapchain is not as optimal as it could be, but the platform's + // Swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. print_verbose("Vulkan: Early suboptimal swapchain."); break; @@ -1979,7 +2209,7 @@ Error VulkanContext::swap_buffers() { #if 0 if (VK_GOOGLE_display_timing_enabled) { // Look at what happened to previous presents, and make appropriate - // adjustments in timing: + // adjustments in timing. DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in @@ -1998,7 +2228,7 @@ Error VulkanContext::swap_buffers() { uint32_t commands_to_submit = 0; if (command_buffer_queue[0] == nullptr) { - //no setup command, but commands to submit, submit from the first and skip command + // No setup command, but commands to submit, submit from the first and skip command. if (command_buffer_count > 1) { commands_ptr = command_buffer_queue.ptr() + 1; commands_to_submit = command_buffer_count - 1; @@ -2041,7 +2271,7 @@ Error VulkanContext::swap_buffers() { if (separate_present_queue) { // If we are using separate queues, change image ownership to the // present queue before presenting, waiting for the draw complete - // semaphore and signalling the ownership released semaphore when finished + // semaphore and signalling the ownership released semaphore when finished. VkFence nullFence = VK_NULL_HANDLE; pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; @@ -2068,7 +2298,7 @@ Error VulkanContext::swap_buffers() { } // If we are using separate queues, we have to wait for image ownership, - // otherwise wait for draw complete + // otherwise wait for draw complete. VkPresentInfoKHR present = { /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, /*pNext*/ nullptr, @@ -2142,7 +2372,7 @@ Error VulkanContext::swap_buffers() { uint64_t curtime = getTimeInNanoseconds(); if (curtime == 0) { // Since we didn't find out the current time, don't give a - // desiredPresentTime: + // desiredPresentTime. ptime.desiredPresentTime = 0; } else { ptime.desiredPresentTime = curtime + (target_IPD >> 1); @@ -2164,8 +2394,6 @@ Error VulkanContext::swap_buffers() { } } #endif - static int total_frames = 0; - total_frames++; // print_line("current buffer: " + itos(current_buffer)); err = fpQueuePresentKHR(present_queue, &present); @@ -2173,12 +2401,12 @@ Error VulkanContext::swap_buffers() { frame_index %= FRAME_LAG; if (err == VK_ERROR_OUT_OF_DATE_KHR) { - // swapchain is out of date (e.g. the window was resized) and - // must be recreated: + // Swapchain is out of date (e.g. the window was resized) and + // must be recreated. print_verbose("Vulkan: Swapchain is out of date, recreating."); resize_notify(); } else if (err == VK_SUBOPTIMAL_KHR) { - // swapchain is not as optimal as it could be, but the platform's + // Swapchain is not as optimal as it could be, but the platform's // presentation engine will still present the image correctly. print_verbose("Vulkan: Swapchain is suboptimal."); } else { @@ -2223,7 +2451,7 @@ VkPhysicalDeviceLimits VulkanContext::get_device_limits() const { RID VulkanContext::local_device_create() { LocalDevice ld; - { //create device + { // Create device. VkResult err; float queue_priorities[1] = { 0.0 }; VkDeviceQueueCreateInfo queues[2]; @@ -2244,13 +2472,13 @@ RID VulkanContext::local_device_create() { /*ppEnabledLayerNames */ nullptr, /*enabledExtensionCount */ enabled_extension_count, /*ppEnabledExtensionNames */ (const char *const *)extension_names, - /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here + /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here. }; err = vkCreateDevice(gpu, &sdevice, nullptr, &ld.device); ERR_FAIL_COND_V(err, RID()); } - { //create graphics queue + { // Create graphics queue. vkGetDeviceQueue(ld.device, graphics_queue_family_index, 0, &ld.queue); } @@ -2312,7 +2540,7 @@ void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String return; } - CharString cs = p_label_name.utf8().get_data(); + CharString cs = p_label_name.utf8(); VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; @@ -2328,7 +2556,7 @@ void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, Strin if (!enabled_debug_utils) { return; } - CharString cs = p_label_name.utf8().get_data(); + CharString cs = p_label_name.utf8(); VkDebugUtilsLabelEXT label; label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; label.pNext = nullptr; @@ -2382,12 +2610,12 @@ String VulkanContext::get_device_pipeline_cache_uuid() const { } DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { - ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); return windows[p_window].vsync_mode; } void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { - ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set VSync mode for window with WindowID " + itos(p_window) + " because it does not exist."); + ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist."); windows[p_window].vsync_mode = p_mode; _update_swap_chain(&windows[p_window]); } diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 236e3bf35f..d6a25c5cd7 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -69,6 +69,15 @@ public: uint32_t max_instance_count; }; + struct VRSCapabilities { + bool pipeline_vrs_supported; // We can specify our fragment rate on a pipeline level. + bool primitive_vrs_supported; // We can specify our fragment rate on each drawcall. + bool attachment_vrs_supported; // We can provide a density map attachment on our framebuffer. + + Size2i min_texel_size; + Size2i max_texel_size; + }; + struct ShaderCapabilities { bool shader_float16_is_supported; bool shader_int8_is_supported; @@ -98,12 +107,13 @@ private: bool device_initialized = false; bool inst_initialized = false; - // Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise + // Vulkan 1.0 doesn't return version info so we assume this by default until we know otherwise. uint32_t vulkan_major = 1; uint32_t vulkan_minor = 0; uint32_t vulkan_patch = 0; SubgroupCapabilities subgroup_capabilities; MultiviewCapabilities multiview_capabilities; + VRSCapabilities vrs_capabilities; ShaderCapabilities shader_capabilities; StorageBufferCapabilities storage_buffer_capabilities; @@ -117,8 +127,8 @@ private: // Present queue. bool queues_initialized = false; - uint32_t graphics_queue_family_index = 0; - uint32_t present_queue_family_index = 0; + uint32_t graphics_queue_family_index = UINT32_MAX; + uint32_t present_queue_family_index = UINT32_MAX; bool separate_present_queue = false; VkQueue graphics_queue = VK_NULL_HANDLE; VkQueue present_queue = VK_NULL_HANDLE; @@ -178,6 +188,7 @@ private: uint32_t enabled_extension_count = 0; const char *extension_names[MAX_EXTENSIONS]; bool enabled_debug_utils = false; + bool has_renderpass2_ext = false; /** * True if VK_EXT_debug_report extension is used. VK_EXT_debug_report is deprecated but it is @@ -206,6 +217,7 @@ private: PFN_vkQueuePresentKHR fpQueuePresentKHR = nullptr; PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE = nullptr; PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE = nullptr; + PFN_vkCreateRenderPass2KHR fpCreateRenderPass2KHR = nullptr; VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE; VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE; @@ -246,6 +258,8 @@ private: Error _create_swap_chain(); Error _create_semaphores(); + Vector<VkAttachmentReference> _convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs); + protected: virtual const char *_get_platform_surface_extension() const = 0; @@ -255,13 +269,20 @@ protected: Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); + virtual VkExtent2D _compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const; + public: + // Extension calls. + bool supports_renderpass2() const { return has_renderpass2_ext; } + VkResult vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass); + uint32_t get_vulkan_major() const { return vulkan_major; }; uint32_t get_vulkan_minor() const { return vulkan_minor; }; - SubgroupCapabilities get_subgroup_capabilities() const { return subgroup_capabilities; }; - MultiviewCapabilities get_multiview_capabilities() const { return multiview_capabilities; }; - ShaderCapabilities get_shader_capabilities() const { return shader_capabilities; }; - StorageBufferCapabilities get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; + const SubgroupCapabilities &get_subgroup_capabilities() const { return subgroup_capabilities; }; + const MultiviewCapabilities &get_multiview_capabilities() const { return multiview_capabilities; }; + const VRSCapabilities &get_vrs_capabilities() const { return vrs_capabilities; }; + const ShaderCapabilities &get_shader_capabilities() const { return shader_capabilities; }; + const StorageBufferCapabilities &get_storage_buffer_capabilities() const { return storage_buffer_capabilities; }; VkDevice get_device(); VkPhysicalDevice get_physical_device(); @@ -289,8 +310,8 @@ public: VkFormat get_screen_format() const; VkPhysicalDeviceLimits get_device_limits() const; - void set_setup_buffer(const VkCommandBuffer &pCommandBuffer); - void append_command_buffer(const VkCommandBuffer &pCommandBuffer); + void set_setup_buffer(VkCommandBuffer p_command_buffer); + void append_command_buffer(VkCommandBuffer p_command_buffer); void resize_notify(); void flush(bool p_flush_setup = false, bool p_flush_pending = false); Error prepare_buffers(); @@ -315,4 +336,4 @@ public: virtual ~VulkanContext(); }; -#endif // VULKAN_DEVICE_H +#endif // VULKAN_CONTEXT_H diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h index 3f244b4d45..dec2042d0d 100644 --- a/drivers/vulkan/vulkan_hooks.h +++ b/drivers/vulkan/vulkan_hooks.h @@ -45,4 +45,4 @@ public: virtual ~VulkanHooks(){}; }; -#endif +#endif // VULKAN_HOOKS_H diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index 3a62850339..f196530e51 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -201,7 +201,7 @@ public: static CMMNotificationClient notif_client; -Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { +Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; IMMDevice *device = nullptr; @@ -267,7 +267,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } } - if (reinit) { + if (p_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. if (hr != S_OK) { @@ -285,6 +285,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + + if (p_no_audio_client_3) { + using_audio_client_3 = false; + } + if (using_audio_client_3) { hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { @@ -302,7 +307,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c SAFE_RELEASE(device) - if (reinit) { + if (p_reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; } @@ -413,7 +418,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c &fundamental_period_frames, &min_period_frames, &max_period_frames); - ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + "."); + if (hr != S_OK) { + print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); + CoTaskMemFree(pwfex); + SAFE_RELEASE(device) + return audio_device_init(p_device, p_capture, p_reinit, true); + } // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail, // so we need to select the closest multiple to the user-specified latency. @@ -430,12 +440,25 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c buffer_frames = period_frames; hr = device_audio_client_3->InitializeSharedAudioStream(0, period_frames, pwfex, nullptr); - ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + "."); - uint32_t output_latency_in_frames; - WAVEFORMATEX *current_pwfex; - device_audio_client_3->GetCurrentSharedModeEnginePeriod(¤t_pwfex, &output_latency_in_frames); - real_latency = (float)output_latency_in_frames / (float)current_pwfex->nSamplesPerSec; - CoTaskMemFree(current_pwfex); + if (hr != S_OK) { + print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); + CoTaskMemFree(pwfex); + SAFE_RELEASE(device); + return audio_device_init(p_device, p_capture, p_reinit, true); + } else { + uint32_t output_latency_in_frames; + WAVEFORMATEX *current_pwfex; + hr = device_audio_client_3->GetCurrentSharedModeEnginePeriod(¤t_pwfex, &output_latency_in_frames); + if (hr == OK) { + real_latency = (float)output_latency_in_frames / (float)current_pwfex->nSamplesPerSec; + CoTaskMemFree(current_pwfex); + } else { + print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); + CoTaskMemFree(pwfex); + SAFE_RELEASE(device); + return audio_device_init(p_device, p_capture, p_reinit, true); + } + } } if (p_capture) { @@ -452,8 +475,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c return OK; } -Error AudioDriverWASAPI::init_render_device(bool reinit) { - Error err = audio_device_init(&audio_output, false, reinit); +Error AudioDriverWASAPI::init_render_device(bool p_reinit) { + Error err = audio_device_init(&audio_output, false, p_reinit); if (err != OK) { return err; } @@ -484,8 +507,8 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { return OK; } -Error AudioDriverWASAPI::init_capture_device(bool reinit) { - Error err = audio_device_init(&audio_input, true, reinit); +Error AudioDriverWASAPI::init_capture_device(bool p_reinit) { + Error err = audio_device_init(&audio_input, true, p_reinit); if (err != OK) { return err; } @@ -501,11 +524,11 @@ Error AudioDriverWASAPI::init_capture_device(bool reinit) { } Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { - if (p_device->active) { + if (p_device->active.is_set()) { if (p_device->audio_client) { p_device->audio_client->Stop(); } - p_device->active = false; + p_device->active.clear(); } SAFE_RELEASE(p_device->audio_client) @@ -533,8 +556,7 @@ Error AudioDriverWASAPI::init() { ERR_PRINT("WASAPI: init_render_device error"); } - exit_thread = false; - thread_exited = false; + exit_thread.clear(); thread.start(thread_func, this); @@ -553,8 +575,8 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } -Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { - Array list; +PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { + PackedStringArray list; IMMDeviceCollection *devices = nullptr; IMMDeviceEnumerator *enumerator = nullptr; @@ -563,14 +585,14 @@ Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { CoInitialize(nullptr); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); - ERR_FAIL_COND_V(hr != S_OK, Array()); + ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); - ERR_FAIL_COND_V(hr != S_OK, Array()); + ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); UINT count = 0; hr = devices->GetCount(&count); - ERR_FAIL_COND_V(hr != S_OK, Array()); + ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); for (ULONG i = 0; i < count; i++) { IMMDevice *device = nullptr; @@ -600,7 +622,7 @@ Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { return list; } -Array AudioDriverWASAPI::get_device_list() { +PackedStringArray AudioDriverWASAPI::get_device_list() { return audio_device_get_list(false); } @@ -684,7 +706,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { uint32_t avail_frames = 0; uint32_t write_ofs = 0; - while (!ad->exit_thread) { + while (!ad->exit_thread.is_set()) { uint32_t read_frames = 0; uint32_t written_frames = 0; @@ -692,7 +714,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { ad->lock(); ad->start_counting_ticks(); - if (ad->audio_output.active) { + if (ad->audio_output.active.is_set()) { ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw()); } else { for (int i = 0; i < ad->samples_in.size(); i++) { @@ -758,7 +780,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } else { ERR_PRINT("WASAPI: Get buffer error"); - ad->exit_thread = true; + ad->exit_thread.set(); } } } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { @@ -807,7 +829,7 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { write_ofs = 0; } - if (ad->audio_input.active) { + if (ad->audio_input.active.is_set()) { UINT32 packet_length = 0; BYTE *data; UINT32 num_frames_available; @@ -886,8 +908,6 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { OS::get_singleton()->delay_usec(1000); } } - - ad->thread_exited = true; } void AudioDriverWASAPI::start() { @@ -896,7 +916,7 @@ void AudioDriverWASAPI::start() { if (hr != S_OK) { ERR_PRINT("WASAPI: Start failed"); } else { - audio_output.active = true; + audio_output.active.set(); } } } @@ -910,7 +930,7 @@ void AudioDriverWASAPI::unlock() { } void AudioDriverWASAPI::finish() { - exit_thread = true; + exit_thread.set(); thread.wait_to_finish(); finish_capture_device(); @@ -924,19 +944,19 @@ Error AudioDriverWASAPI::capture_start() { return err; } - if (audio_input.active) { + if (audio_input.active.is_set()) { return FAILED; } audio_input.audio_client->Start(); - audio_input.active = true; + audio_input.active.set(); return OK; } Error AudioDriverWASAPI::capture_stop() { - if (audio_input.active) { + if (audio_input.active.is_set()) { audio_input.audio_client->Stop(); - audio_input.active = false; + audio_input.active.clear(); return OK; } @@ -950,7 +970,7 @@ void AudioDriverWASAPI::capture_set_device(const String &p_name) { unlock(); } -Array AudioDriverWASAPI::capture_get_device_list() { +PackedStringArray AudioDriverWASAPI::capture_get_device_list() { return audio_device_get_list(true); } diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 89ed90e97b..fb6a55734d 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -35,6 +35,7 @@ #include "core/os/mutex.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "servers/audio_server.h" #include <audioclient.h> @@ -48,7 +49,7 @@ class AudioDriverWASAPI : public AudioDriver { IAudioClient *audio_client = nullptr; IAudioRenderClient *render_client = nullptr; IAudioCaptureClient *capture_client = nullptr; - bool active = false; + SafeFlag active; WORD format_tag = 0; WORD bits_per_sample = 0; @@ -76,22 +77,21 @@ class AudioDriverWASAPI : public AudioDriver { float real_latency = 0.0; bool using_audio_client_3 = false; - bool thread_exited = false; - mutable bool exit_thread = false; + SafeFlag exit_thread; static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample); static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); - Error init_render_device(bool reinit = false); - Error init_capture_device(bool reinit = false); + Error init_render_device(bool p_reinit = false); + Error init_capture_device(bool p_reinit = false); Error finish_render_device(); Error finish_capture_device(); - Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit); + Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3 = false); Error audio_device_finish(AudioDeviceWASAPI *p_device); - Array audio_device_get_list(bool p_capture); + PackedStringArray audio_device_get_list(bool p_capture); public: virtual const char *get_name() const { @@ -103,7 +103,7 @@ public: virtual int get_mix_rate() const; virtual float get_latency(); virtual SpeakerMode get_speaker_mode() const; - virtual Array get_device_list(); + virtual PackedStringArray get_device_list(); virtual String get_device(); virtual void set_device(String device); virtual void lock(); @@ -112,7 +112,7 @@ public: virtual Error capture_start(); virtual Error capture_stop(); - virtual Array capture_get_device_list(); + virtual PackedStringArray capture_get_device_list(); virtual void capture_set_device(const String &p_name); virtual String capture_get_device(); @@ -120,4 +120,5 @@ public: }; #endif // WASAPI_ENABLED + #endif // AUDIO_DRIVER_WASAPI_H diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 881575d245..c085ba372b 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -157,10 +157,10 @@ Error DirAccessWindows::make_dir(String p_dir) { p_dir = fix_path(p_dir); if (p_dir.is_relative_path()) { - p_dir = current_dir.plus_file(p_dir); + p_dir = current_dir.path_join(p_dir); } - p_dir = p_dir.replace("/", "\\"); + p_dir = p_dir.simplify_path().replace("/", "\\"); bool success; int err; @@ -200,9 +200,9 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const { return current_dir; } else { if (_get_root_string().is_empty()) { - int p = current_dir.find(":"); - if (p != -1) { - return current_dir.substr(p + 1); + int pos = current_dir.find(":"); + if (pos != -1) { + return current_dir.substr(pos + 1); } } return current_dir; @@ -213,7 +213,7 @@ bool DirAccessWindows::file_exists(String p_file) { GLOBAL_LOCK_FUNCTION if (!p_file.is_absolute_path()) { - p_file = get_current_dir().plus_file(p_file); + p_file = get_current_dir().path_join(p_file); } p_file = fix_path(p_file); @@ -232,7 +232,7 @@ bool DirAccessWindows::dir_exists(String p_dir) { GLOBAL_LOCK_FUNCTION if (p_dir.is_relative_path()) { - p_dir = get_current_dir().plus_file(p_dir); + p_dir = get_current_dir().path_join(p_dir); } p_dir = fix_path(p_dir); @@ -247,13 +247,13 @@ bool DirAccessWindows::dir_exists(String p_dir) { Error DirAccessWindows::rename(String p_path, String p_new_path) { if (p_path.is_relative_path()) { - p_path = get_current_dir().plus_file(p_path); + p_path = get_current_dir().path_join(p_path); } p_path = fix_path(p_path); if (p_new_path.is_relative_path()) { - p_new_path = get_current_dir().plus_file(p_new_path); + p_new_path = get_current_dir().path_join(p_new_path); } p_new_path = fix_path(p_new_path); @@ -291,7 +291,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) { Error DirAccessWindows::remove(String p_path) { if (p_path.is_relative_path()) { - p_path = get_current_dir().plus_file(p_path); + p_path = get_current_dir().path_join(p_path); } p_path = fix_path(p_path); @@ -309,39 +309,13 @@ Error DirAccessWindows::remove(String p_path) { } } -/* - -FileType DirAccessWindows::get_file_type(const String& p_file) const { - WCHAR real_current_dir_name[2048]; - GetCurrentDirectoryW(2048, real_current_dir_name); - String prev_dir = Strong::utf16((const char16_t *)real_current_dir_name); - - bool worked = SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data())); - - DWORD attr; - if (worked) { - WIN32_FILE_ATTRIBUTE_DATA fileInfo; - attr = GetFileAttributesExW((LPCWSTR)(p_file.utf16().get_data()), GetFileExInfoStandard, &fileInfo); - } - - SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data())); - - if (!worked) { - return FILE_TYPE_NONE; - } - - return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FILE_TYPE_ -} - -*/ - uint64_t DirAccessWindows::get_space_left() { uint64_t bytes = 0; if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) { return 0; } - //this is either 0 or a value in bytes. + // This is either 0 or a value in bytes. return bytes; } @@ -402,6 +376,8 @@ DirAccessWindows::DirAccessWindows() { } DirAccessWindows::~DirAccessWindows() { + list_dir_end(); + memdelete(p); } diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index fbb07ddef8..c2835b3347 100644 --- a/drivers/windows/dir_access_windows.h +++ b/drivers/windows/dir_access_windows.h @@ -54,33 +54,33 @@ class DirAccessWindows : public DirAccess { bool _cishidden = false; public: - virtual Error list_dir_begin(); ///< This starts dir listing - virtual String get_next(); - virtual bool current_is_dir() const; - virtual bool current_is_hidden() const; - virtual void list_dir_end(); ///< + virtual Error list_dir_begin() override; ///< This starts dir listing + virtual String get_next() override; + virtual bool current_is_dir() const override; + virtual bool current_is_hidden() const override; + virtual void list_dir_end() override; ///< - virtual int get_drive_count(); - virtual String get_drive(int p_drive); + virtual int get_drive_count() override; + virtual String get_drive(int p_drive) override; - virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success - virtual String get_current_dir(bool p_include_drive = true) const; ///< return current dir location + virtual Error change_dir(String p_dir) override; ///< can be relative or absolute, return false on success + virtual String get_current_dir(bool p_include_drive = true) const override; ///< return current dir location - virtual bool file_exists(String p_file); - virtual bool dir_exists(String p_dir); + virtual bool file_exists(String p_file) override; + virtual bool dir_exists(String p_dir) override; - virtual Error make_dir(String p_dir); + virtual Error make_dir(String p_dir) override; - virtual Error rename(String p_path, String p_new_path); - virtual Error remove(String p_path); + virtual Error rename(String p_path, String p_new_path) override; + virtual Error remove(String p_path) override; - virtual bool is_link(String p_file) { return false; }; - virtual String read_link(String p_file) { return p_file; }; - virtual Error create_link(String p_source, String p_target) { return FAILED; }; + virtual bool is_link(String p_file) override { return false; }; + virtual String read_link(String p_file) override { return p_file; }; + virtual Error create_link(String p_source, String p_target) override { return FAILED; }; - uint64_t get_space_left(); + uint64_t get_space_left() override; - virtual String get_filesystem_type() const; + virtual String get_filesystem_type() const override; DirAccessWindows(); ~DirAccessWindows(); diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 1a66d19373..4e8b327ffe 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -58,7 +58,7 @@ void FileAccessWindows::check_errors() const { } } -Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { +Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { _close(); path_src = p_path; @@ -95,8 +95,8 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { // platforms). if (p_mode_flags == READ) { WIN32_FIND_DATAW d; - HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); - if (f != INVALID_HANDLE_VALUE) { + HANDLE fnd = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); + if (fnd != INVALID_HANDLE_VALUE) { String fname = String::utf16((const char16_t *)(d.cFileName)); if (!fname.is_empty()) { String base_file = path.get_file(); @@ -104,7 +104,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { WARN_PRINT("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); + FindClose(fnd); } } #endif diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 5d67b6ca4f..d84c400775 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -51,33 +51,33 @@ class FileAccessWindows : public FileAccess { void _close(); public: - virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual bool is_open() const; ///< true when file is open + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file + virtual bool is_open() const override; ///< true when file is open - virtual String get_path() const; /// returns the path for the current open file - virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual String get_path() const override; /// returns the path for the current open file + virtual String get_path_absolute() const override; /// returns the absolute path for the current open file - virtual void seek(uint64_t p_position); ///< seek to a given position - virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual uint64_t get_position() const; ///< get position in the file - virtual uint64_t get_length() const; ///< get size of the file + virtual void seek(uint64_t p_position) override; ///< seek to a given position + virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file + virtual uint64_t get_position() const override; ///< get position in the file + virtual uint64_t get_length() const override; ///< get size of the file - virtual bool eof_reached() const; ///< reading passed EOF + virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const; ///< get a byte - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; + virtual uint8_t get_8() const override; ///< get a byte + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; - virtual Error get_error() const; ///< get last error + virtual Error get_error() const override; ///< get last error - virtual void flush(); - virtual void store_8(uint8_t p_dest); ///< store a byte - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes + virtual void flush() override; + virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes - virtual bool file_exists(const String &p_name); ///< return true if a file exists + virtual bool file_exists(const String &p_name) override; ///< return true if a file exists - uint64_t _get_modified_time(const String &p_file); - virtual uint32_t _get_unix_permissions(const String &p_file); - virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); + uint64_t _get_modified_time(const String &p_file) override; + virtual uint32_t _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override; FileAccessWindows() {} virtual ~FileAccessWindows(); diff --git a/drivers/winmidi/midi_driver_winmidi.h b/drivers/winmidi/midi_driver_winmidi.h index 6572ba0c16..e42f3df4cf 100644 --- a/drivers/winmidi/midi_driver_winmidi.h +++ b/drivers/winmidi/midi_driver_winmidi.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WINMIDI_ENABLED - #ifndef MIDI_DRIVER_WINMIDI_H #define MIDI_DRIVER_WINMIDI_H +#ifdef WINMIDI_ENABLED + #include "core/os/midi_driver.h" #include "core/templates/vector.h" @@ -57,5 +57,6 @@ public: virtual ~MIDIDriverWinMidi(); }; -#endif // MIDI_DRIVER_WINMIDI_H #endif // WINMIDI_ENABLED + +#endif // MIDI_DRIVER_WINMIDI_H diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp index c32c7cf1e5..6c48c1a844 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -38,9 +38,8 @@ const char *AudioDriverXAudio2::get_name() const { } Error AudioDriverXAudio2::init() { - active = false; - thread_exited = false; - exit_thread = false; + active.clear(); + exit_thread.clear(); pcm_open = false; samples_in = nullptr; @@ -86,17 +85,19 @@ Error AudioDriverXAudio2::init() { void AudioDriverXAudio2::thread_func(void *p_udata) { AudioDriverXAudio2 *ad = static_cast<AudioDriverXAudio2 *>(p_udata); - while (!ad->exit_thread) { - if (!ad->active) { + while (!ad->exit_thread.is_set()) { + if (!ad->active.is_set()) { for (int i = 0; i < AUDIO_BUFFERS; i++) { ad->xaudio_buffer[i].Flags = XAUDIO2_END_OF_STREAM; } } else { ad->lock(); + ad->start_counting_ticks(); ad->audio_server_process(ad->buffer_size, ad->samples_in); + ad->stop_counting_ticks(); ad->unlock(); for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) { @@ -117,12 +118,10 @@ void AudioDriverXAudio2::thread_func(void *p_udata) { } } } - - ad->thread_exited = true; } void AudioDriverXAudio2::start() { - active = true; + active.set(); HRESULT hr = source_voice->Start(0); ERR_FAIL_COND_MSG(hr != S_OK, "Error starting XAudio2 driver. Error code: " + itos(hr) + "."); } @@ -154,7 +153,7 @@ void AudioDriverXAudio2::unlock() { } void AudioDriverXAudio2::finish() { - exit_thread = true; + exit_thread.set(); thread.wait_to_finish(); if (source_voice) { diff --git a/drivers/xaudio2/audio_driver_xaudio2.h b/drivers/xaudio2/audio_driver_xaudio2.h index 9072269a0e..0f64d54a1f 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.h +++ b/drivers/xaudio2/audio_driver_xaudio2.h @@ -33,6 +33,7 @@ #include "core/os/mutex.h" #include "core/os/thread.h" +#include "core/templates/safe_refcount.h" #include "servers/audio_server.h" #include <mmsystem.h> @@ -77,9 +78,8 @@ class AudioDriverXAudio2 : public AudioDriver { int channels = 0; - bool active = false; - bool thread_exited = false; - mutable bool exit_thread = false; + SafeFlag active; + SafeFlag exit_thread; bool pcm_open = false; WAVEFORMATEX wave_format = { 0 }; @@ -106,4 +106,4 @@ public: ~AudioDriverXAudio2() {} }; -#endif +#endif // AUDIO_DRIVER_XAUDIO2_H |