diff options
Diffstat (limited to 'drivers')
88 files changed, 18328 insertions, 10346 deletions
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp index 7884269103..f86c4d82ef 100644 --- a/drivers/alsa/audio_driver_alsa.cpp +++ b/drivers/alsa/audio_driver_alsa.cpp @@ -181,7 +181,7 @@ Error AudioDriverALSA::init() { } void AudioDriverALSA::thread_func(void *p_udata) { - AudioDriverALSA *ad = (AudioDriverALSA *)p_udata; + AudioDriverALSA *ad = static_cast<AudioDriverALSA *>(p_udata); while (!ad->exit_thread) { ad->lock(); diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp index 0674c90cd6..c334146dd2 100644 --- a/drivers/alsamidi/midi_driver_alsamidi.cpp +++ b/drivers/alsamidi/midi_driver_alsamidi.cpp @@ -73,7 +73,7 @@ static int get_message_size(uint8_t message) { } void MIDIDriverALSAMidi::thread_func(void *p_udata) { - MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata; + MIDIDriverALSAMidi *md = static_cast<MIDIDriverALSAMidi *>(p_udata); uint64_t timestamp = 0; uint8_t buffer[256]; int expected_size = 255; diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index e37a53fede..cc38c2352f 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -38,11 +38,11 @@ #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) { - AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); // If our selected device is the Default call set_device to update the // kAudioOutputUnitProperty_CurrentDevice property @@ -56,7 +56,7 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) { - AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; + AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); // If our selected device is the Default call set_device to update the // kAudioOutputUnitProperty_CurrentDevice property @@ -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 @@ -168,15 +168,15 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon); if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { AudioBuffer *abuf = &ioData->mBuffers[i]; memset(abuf->mData, 0, abuf->mDataByteSize); - }; + } return 0; - }; + } ad->start_counting_ticks(); @@ -195,21 +195,21 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, frames_left -= frames; out += frames * ad->channels; - }; - }; + } + } ad->stop_counting_ticks(); ad->unlock(); return 0; -}; +} OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; + AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon); if (!ad->active) { return 0; } @@ -251,7 +251,7 @@ void AudioDriverCoreAudio::start() { active = true; } } -}; +} void AudioDriverCoreAudio::stop() { if (active) { @@ -266,19 +266,19 @@ void AudioDriverCoreAudio::stop() { int AudioDriverCoreAudio::get_mix_rate() const { return mix_rate; -}; +} AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); -}; +} void AudioDriverCoreAudio::lock() { mutex.lock(); -}; +} void AudioDriverCoreAudio::unlock() { mutex.unlock(); -}; +} bool AudioDriverCoreAudio::try_lock() { return mutex.try_lock() == OK; @@ -313,7 +313,7 @@ void AudioDriverCoreAudio::finish() { ERR_PRINT("AudioUnitUninitialize failed"); } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -339,7 +339,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 +352,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 +370,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 +447,7 @@ void AudioDriverCoreAudio::capture_finish() { ERR_PRINT("AudioUnitUninitialize failed"); } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; prop.mScope = kAudioObjectPropertyScopeGlobal; @@ -491,7 +491,7 @@ Error AudioDriverCoreAudio::capture_stop() { return OK; } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED Array AudioDriverCoreAudio::_get_device_list(bool capture) { Array list; @@ -521,8 +521,9 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) { AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); UInt32 channelCount = 0; - for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) { channelCount += bufferList->mBuffers[j].mNumberChannels; + } memfree(bufferList); @@ -579,8 +580,9 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); UInt32 channelCount = 0; - for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) + for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) { channelCount += bufferList->mBuffers[j].mNumberChannels; + } memfree(bufferList); diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index b19f133d89..7fac8a99ed 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -28,16 +28,16 @@ /* 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" -#include <AudioUnit/AudioUnit.h> -#ifdef OSX_ENABLED -#include <CoreAudio/AudioHardware.h> +#import <AudioUnit/AudioUnit.h> +#ifdef MACOS_ENABLED +#import <CoreAudio/AudioHardware.h> #endif class AudioDriverCoreAudio : public AudioDriver { @@ -58,7 +58,7 @@ class AudioDriverCoreAudio : public AudioDriver { Vector<int32_t> samples_in; Vector<int16_t> input_buf; -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED Array _get_device_list(bool capture = false); void _set_device(const String &device, bool capture = false); @@ -106,7 +106,7 @@ public: bool try_lock(); void stop(); -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED virtual Array get_device_list(); virtual String get_device(); virtual void set_device(String device); @@ -120,6 +120,6 @@ public: ~AudioDriverCoreAudio() {} }; -#endif +#endif // COREAUDIO_ENABLED -#endif +#endif // AUDIO_DRIVER_COREAUDIO_H diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp index ecd10f900b..dc69ab9472 100644 --- a/drivers/coremidi/midi_driver_coremidi.cpp +++ b/drivers/coremidi/midi_driver_coremidi.cpp @@ -34,8 +34,8 @@ #include "core/string/print_string.h" -#include <CoreAudio/HostTime.h> -#include <CoreServices/CoreServices.h> +#import <CoreAudio/HostTime.h> +#import <CoreServices/CoreServices.h> void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) { MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet); diff --git a/drivers/coremidi/midi_driver_coremidi.h b/drivers/coremidi/midi_driver_coremidi.h index be0a9f610e..ac2ad99eb3 100644 --- a/drivers/coremidi/midi_driver_coremidi.h +++ b/drivers/coremidi/midi_driver_coremidi.h @@ -28,15 +28,15 @@ /* 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" -#include <CoreMIDI/CoreMIDI.h> +#import <CoreMIDI/CoreMIDI.h> #include <stdio.h> class MIDIDriverCoreMidi : public MIDIDriver { @@ -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 987ddcd16e..506312df80 100644 --- a/drivers/gles3/SCsub +++ b/drivers/gles3/SCsub @@ -5,3 +5,6 @@ Import("env") 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/SCsub b/drivers/gles3/effects/SCsub new file mode 100644 index 0000000000..91e1140b75 --- /dev/null +++ b/drivers/gles3/effects/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp new file mode 100644 index 0000000000..de0181f887 --- /dev/null +++ b/drivers/gles3/effects/copy_effects.cpp @@ -0,0 +1,165 @@ +/*************************************************************************/ +/* copy_effects.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 "copy_effects.h" + +using namespace GLES3; + +CopyEffects *CopyEffects::singleton = nullptr; + +CopyEffects *CopyEffects::get_singleton() { + return singleton; +} + +CopyEffects::CopyEffects() { + singleton = this; + + copy.shader.initialize(); + copy.shader_version = copy.shader.version_create(); + copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); + + { // Screen Triangle. + glGenBuffers(1, &screen_triangle); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + + const float qv[6] = { + -1.0f, + -1.0f, + 3.0f, + -1.0f, + -1.0f, + 3.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &screen_triangle_array); + glBindVertexArray(screen_triangle_array); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } + + { // Screen Quad + + glGenBuffers(1, &quad); + glBindBuffer(GL_ARRAY_BUFFER, quad); + + const float qv[12] = { + -1.0f, + -1.0f, + 1.0f, + -1.0f, + 1.0f, + 1.0f, + -1.0f, + -1.0f, + 1.0f, + 1.0f, + -1.0f, + 1.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &quad_array); + glBindVertexArray(quad_array); + glBindBuffer(GL_ARRAY_BUFFER, quad); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } +} + +CopyEffects::~CopyEffects() { + singleton = nullptr; + glDeleteBuffers(1, &screen_triangle); + 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) { + 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); +} + +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); +} + +void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) { + GLuint framebuffers[2]; + glGenFramebuffers(2, framebuffers); + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, 0); + + Rect2i source_region = p_region; + Rect2i dest_region = p_region; + for (int i = 1; i < p_mipmap_count; i++) { + dest_region.position.x >>= 1; + dest_region.position.y >>= 1; + dest_region.size.x = MAX(1, dest_region.size.x >> 1); + dest_region.size.y = MAX(1, dest_region.size.y >> 1); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[i % 2]); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i); + glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.size.x, source_region.size.y, + dest_region.position.x, dest_region.position.y, dest_region.size.x, dest_region.size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[i % 2]); + source_region = dest_region; + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDeleteFramebuffers(2, framebuffers); +} + +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); + 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 new file mode 100644 index 0000000000..b863e76579 --- /dev/null +++ b/drivers/gles3/effects/copy_effects.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* copy_effects.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 COPY_EFFECTS_GLES3_H +#define COPY_EFFECTS_GLES3_H + +#ifdef GLES3_ENABLED + +#include "../shaders/copy.glsl.gen.h" + +namespace GLES3 { + +class CopyEffects { +private: + struct Copy { + CopyShaderGLES3 shader; + RID shader_version; + } copy; + + static CopyEffects *singleton; + + // Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal. + GLuint screen_triangle = 0; + GLuint screen_triangle_array = 0; + + // Use for rect-based effects. + GLuint quad = 0; + GLuint quad_array = 0; + +public: + static CopyEffects *get_singleton(); + + CopyEffects(); + ~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_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); +}; + +} //namespace GLES3 + +#endif // GLES3_ENABLED + +#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/texture_loader_gles3.h b/drivers/gles3/environment/fog.cpp index 54ddf80a96..02d88f6871 100644 --- a/drivers/gles3/texture_loader_gles3.h +++ b/drivers/gles3/environment/fog.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* texture_loader_gles3.h */ +/* fog.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,24 +28,39 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTURE_LOADER_OPENGL_H -#define TEXTURE_LOADER_OPENGL_H - #ifdef GLES3_ENABLED -#include "core/io/resource_loader.h" -#include "scene/resources/texture.h" +#include "fog.h" -class ResourceFormatGLES2Texture : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; +using namespace GLES3; - virtual ~ResourceFormatGLES2Texture() {} -}; +/* FOG */ -#endif // GLES3_ENABLED +RID Fog::fog_volume_allocate() { + return RID(); +} + +void Fog::fog_volume_initialize(RID p_rid) { +} + +void Fog::fog_free(RID p_rid) { +} + +void Fog::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { +} -#endif // TEXTURE_LOADER_OPENGL_H +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..350eb459cf --- /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_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..84cdb81d35 --- /dev/null +++ b/drivers/gles3/environment/gi.cpp @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* 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_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..7a0634f22b --- /dev/null +++ b/drivers/gles3/environment/gi.h @@ -0,0 +1,96 @@ +/*************************************************************************/ +/* 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_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 451960d772..83154acd51 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -34,10 +34,13 @@ #include "core/os/os.h" #include "rasterizer_scene_gles3.h" -#include "rasterizer_storage_gles3.h" #include "core/config/project_settings.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 @@ -54,57 +57,57 @@ //}; void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) { - p_mat4[0] = p_transform.elements[0][0]; - p_mat4[1] = p_transform.elements[0][1]; + p_mat4[0] = p_transform.columns[0][0]; + p_mat4[1] = p_transform.columns[0][1]; p_mat4[2] = 0; p_mat4[3] = 0; - p_mat4[4] = p_transform.elements[1][0]; - p_mat4[5] = p_transform.elements[1][1]; + p_mat4[4] = p_transform.columns[1][0]; + p_mat4[5] = p_transform.columns[1][1]; p_mat4[6] = 0; p_mat4[7] = 0; p_mat4[8] = 0; p_mat4[9] = 0; p_mat4[10] = 1; p_mat4[11] = 0; - p_mat4[12] = p_transform.elements[2][0]; - p_mat4[13] = p_transform.elements[2][1]; + p_mat4[12] = p_transform.columns[2][0]; + p_mat4[13] = p_transform.columns[2][1]; p_mat4[14] = 0; p_mat4[15] = 1; } void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4) { - p_mat2x4[0] = p_transform.elements[0][0]; - p_mat2x4[1] = p_transform.elements[1][0]; + p_mat2x4[0] = p_transform.columns[0][0]; + p_mat2x4[1] = p_transform.columns[1][0]; p_mat2x4[2] = 0; - p_mat2x4[3] = p_transform.elements[2][0]; + p_mat2x4[3] = p_transform.columns[2][0]; - p_mat2x4[4] = p_transform.elements[0][1]; - p_mat2x4[5] = p_transform.elements[1][1]; + p_mat2x4[4] = p_transform.columns[0][1]; + p_mat2x4[5] = p_transform.columns[1][1]; p_mat2x4[6] = 0; - p_mat2x4[7] = p_transform.elements[2][1]; + p_mat2x4[7] = p_transform.columns[2][1]; } void RasterizerCanvasGLES3::_update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3) { - p_mat2x3[0] = p_transform.elements[0][0]; - p_mat2x3[1] = p_transform.elements[0][1]; - p_mat2x3[2] = p_transform.elements[1][0]; - p_mat2x3[3] = p_transform.elements[1][1]; - p_mat2x3[4] = p_transform.elements[2][0]; - p_mat2x3[5] = p_transform.elements[2][1]; + p_mat2x3[0] = p_transform.columns[0][0]; + p_mat2x3[1] = p_transform.columns[0][1]; + p_mat2x3[2] = p_transform.columns[1][0]; + p_mat2x3[3] = p_transform.columns[1][1]; + p_mat2x3[4] = p_transform.columns[2][0]; + p_mat2x3[5] = p_transform.columns[2][1]; } void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4) { - p_mat4[0] = p_transform.basis.elements[0][0]; - p_mat4[1] = p_transform.basis.elements[1][0]; - p_mat4[2] = p_transform.basis.elements[2][0]; + p_mat4[0] = p_transform.basis.rows[0][0]; + p_mat4[1] = p_transform.basis.rows[1][0]; + p_mat4[2] = p_transform.basis.rows[2][0]; p_mat4[3] = 0; - p_mat4[4] = p_transform.basis.elements[0][1]; - p_mat4[5] = p_transform.basis.elements[1][1]; - p_mat4[6] = p_transform.basis.elements[2][1]; + p_mat4[4] = p_transform.basis.rows[0][1]; + p_mat4[5] = p_transform.basis.rows[1][1]; + p_mat4[6] = p_transform.basis.rows[2][1]; p_mat4[7] = 0; - p_mat4[8] = p_transform.basis.elements[0][2]; - p_mat4[9] = p_transform.basis.elements[1][2]; - p_mat4[10] = p_transform.basis.elements[2][2]; + p_mat4[8] = p_transform.basis.rows[0][2]; + p_mat4[9] = p_transform.basis.rows[1][2]; + p_mat4[10] = p_transform.basis.rows[2][2]; p_mat4[11] = 0; p_mat4[12] = p_transform.origin.x; p_mat4[13] = p_transform.origin.y; @@ -113,12 +116,14 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans } 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) { - storage->frame.current_rt = nullptr; - - storage->_set_current_render_target(p_to_render_target); + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse(); + // Clear out any state that may have been left from the 3D pass. + reset_canvas(); + // TODO: Setup Directional Lights // TODO: Setup lights @@ -127,18 +132,18 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ //update canvas state uniform buffer StateBuffer state_buffer; - Size2i ssize = storage->render_target_get_size(p_to_render_target); + 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); Transform2D normal_transform = p_canvas_transform; - normal_transform.elements[0].normalize(); - normal_transform.elements[1].normalize(); - normal_transform.elements[2] = Vector2(); + normal_transform.columns[0].normalize(); + normal_transform.columns[1].normalize(); + normal_transform.columns[2] = Vector2(); _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform); state_buffer.canvas_modulate[0] = p_modulate.r; @@ -146,11 +151,14 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.canvas_modulate[2] = p_modulate.b; state_buffer.canvas_modulate[3] = p_modulate.a; - Size2 render_target_size = storage->render_target_get_size(p_to_render_target); + Size2 render_target_size = texture_storage->render_target_get_size(p_to_render_target); state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - state_buffer.time = storage->frame.time; + // 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; @@ -163,7 +171,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.screen_to_sdf[0] = 1.0 / state_buffer.sdf_to_screen[0]; state_buffer.screen_to_sdf[1] = 1.0 / state_buffer.sdf_to_screen[1]; - Rect2 sdf_rect = storage->render_target_get_sdf_rect(p_to_render_target); + Rect2 sdf_rect = texture_storage->render_target_get_sdf_rect(p_to_render_target); Rect2 sdf_tex_rect(sdf_rect.position / canvas_scale, sdf_rect.size / canvas_scale); state_buffer.sdf_to_tex[0] = 1.0 / sdf_tex_rect.size.width; @@ -171,10 +179,13 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.sdf_to_tex[2] = -sdf_tex_rect.position.x / sdf_tex_rect.size.width; state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; - //print_line("w: " + itos(ssize.width) + " s: " + rtos(canvas_scale)); state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_state_buffer); + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_state_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); + + GLuint global_buffer = material_storage->global_shader_uniforms_get_uniform_buffer(); + + glBindBufferBase(GL_UNIFORM_BUFFER, GLOBAL_UNIFORM_LOCATION, global_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); } @@ -183,19 +194,100 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state.default_repeat = p_default_repeat; } - state.current_tex = RID(); - state.current_tex_ptr = nullptr; - state.current_normal = RID(); - state.current_specular = RID(); - state.canvas_texscreen_used = false; - r_sdf_used = false; int item_count = 0; + bool backbuffer_cleared = false; + bool time_used = false; + bool material_screen_texture_found = false; + Rect2 back_buffer_rect; + bool backbuffer_copy = false; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); Item *ci = p_item_list; + Item *canvas_group_owner = nullptr; + while (ci) { + if (ci->copy_back_buffer && canvas_group_owner == nullptr) { + backbuffer_copy = true; + + if (ci->copy_back_buffer->full) { + back_buffer_rect = Rect2(); + } else { + back_buffer_rect = ci->copy_back_buffer->rect; + } + } + + // Check material for something that may change flow of rendering, but do not bind for now. + RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; + if (material.is_valid()) { + 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) { + backbuffer_copy = true; + back_buffer_rect = Rect2(); + } + } + + if (md->shader_data->uses_sdf) { + r_sdf_used = true; + } + if (md->shader_data->uses_time) { + time_used = true; + } + } + } + + 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); + 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) { + texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + } else if (!backbuffer_cleared) { + texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); + backbuffer_cleared = true; + } + + backbuffer_copy = false; + canvas_group_owner = ci->canvas_group_owner; //continue until owner found + } + + ci->canvas_group_owner = nullptr; //must be cleared + } + + if (!backbuffer_cleared && canvas_group_owner == nullptr && ci->canvas_group != nullptr && !backbuffer_copy) { + texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); + backbuffer_cleared = true; + } + + if (ci == canvas_group_owner) { + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true); + item_count = 0; + + if (ci->canvas_group->blur_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, ci->global_rect_cache); + } + + canvas_group_owner = nullptr; + } + + 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); + item_count = 0; + + texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, true); + + backbuffer_copy = false; + material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies + } + // just add all items for now items[item_count++] = ci; @@ -207,118 +299,155 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ ci = ci->next; } + + if (time_used) { + RenderingServerDefault::redraw_request(); + } + + // Clear out state used in 2D pass + reset_canvas(); } 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(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); Item *current_clip = nullptr; Transform2D canvas_transform_inverse = p_canvas_transform_inverse; - RID framebuffer; - Vector<Color> clear_colors; - - canvas_begin(); + canvas_begin(p_to_render_target, p_to_backbuffer); RID prev_material; uint32_t index = 0; + GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; + GLES3::CanvasShaderData *shader_data_cache = nullptr; + + 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; for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; + if (current_clip != ci->final_clip_owner) { + _render_batch(index); + + 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; - RasterizerStorageGLES3::Material *material_ptr = storage->material_owner.get_or_null(material); if (material.is_null() && ci->canvas_group != nullptr) { material = default_canvas_group_material; } if (material != prev_material) { - RasterizerStorageGLES3::Shader *shader_ptr = nullptr; - - if (material_ptr) { - shader_ptr = material_ptr->shader; - - if (shader_ptr && shader_ptr->mode != RS::SHADER_CANVAS_ITEM) { - shader_ptr = nullptr; // not a canvas item shader, don't use. - } + _render_batch(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)); } - - if (shader_ptr) { - if (true) { //check that shader has changed - if (shader_ptr->canvas_item.uses_time) { - RenderingServerDefault::redraw_request(); - } - //state.canvas_shader.version_bind_shader(shader_ptr->version, CanvasShaderGLES3::MODE_QUAD); - state.current_shader_version = shader_ptr->version; + 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; + } - int tc = material_ptr->textures.size(); - Pair<StringName, RID> *textures = material_ptr->textures.ptrw(); - - ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_ptr->texture_uniforms.ptrw(); - - for (int ti = 0; ti < tc; i++) { - glActiveTexture(GL_TEXTURE0 + ti); - - RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(textures[ti].second); - - if (!t) { - switch (texture_uniforms[i].hint) { - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_ANISOTROPY: { - glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); - } break; - default: { - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - } break; - } + GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX; - continue; - } + if (last_blend_mode != blend_mode) { + if (last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { + // re-enable it + glEnable(GL_BLEND); + } else if (blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { + // disable it + glDisable(GL_BLEND); + } - //Set texture filter and repeat texture_uniforms[i].filter texture_uniforms[i].repeat + switch (blend_mode) { + case GLES3::CanvasShaderData::BLEND_MODE_DISABLED: { + // Nothing to do here. - if (t->redraw_if_visible) { - RenderingServerDefault::redraw_request(); + } break; + case GLES3::CanvasShaderData::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); } - t = t->get_ptr(); + } break; + case GLES3::CanvasShaderData::BLEND_MODE_ADD: { + glBlendEquation(GL_FUNC_ADD); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_SRC_ALPHA, GL_ONE); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); + } -#ifdef TOOLS_ENABLED - if (t->detect_normal && texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) { - t->detect_normal(t->detect_normal_ud); + } break; + case GLES3::CanvasShaderData::BLEND_MODE_SUB: { + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_SRC_ALPHA, GL_ONE); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); } -#endif - if (t->render_target) { - t->render_target->used_in_frame = true; + } break; + case GLES3::CanvasShaderData::BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); + } else { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); } - glBindTexture(t->target, t->tex_id); - } + } break; + case GLES3::CanvasShaderData::BLEND_MODE_PMALPHA: { + glBlendEquation(GL_FUNC_ADD); + if (state.transparent_render_target) { + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + } - } else { - //state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); - state.current_shader_version = state.canvas_shader_default_version; + } break; } - prev_material = material; + last_blend_mode = blend_mode; } _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index); } // Render last command - state.end_batch = true; _render_batch(index); - - canvas_end(); } 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 }; + RS::CanvasItemTextureFilter current_filter = state.default_filter; RS::CanvasItemTextureRepeat current_repeat = state.default_repeat; @@ -337,8 +466,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item uint32_t base_flags = 0; - RID last_texture; - Size2 texpixel_size; + bool reclip = false; bool skipping = false; @@ -349,7 +477,10 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item continue; } - _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + if (c->type != Item::Command::TYPE_MESH) { + // For Meshes, this gets updated below. + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + } for (int i = 0; i < 4; i++) { state.instance_data_array[r_index].modulation[i] = 0.0; @@ -358,7 +489,6 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[i] = 0.0; state.instance_data_array[r_index].lights[i] = uint32_t(0); } - state.instance_data_array[r_index].flags = base_flags; 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; @@ -375,21 +505,20 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } - if (rect->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) { - state.end_batch = true; + 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; } - _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); - state.canvas_shader.version_bind_shader(state.current_shader_version, 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); Rect2 src_rect; Rect2 dst_rect; if (rect->texture != RID()) { - src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); + 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); dst_rect = Rect2(rect->rect.position, rect->rect.size); if (dst_rect.size.width < 0) { @@ -454,39 +583,37 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[1] = dst_rect.position.y; 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; - //_render_batch(r_index); + r_index++; if (r_index >= state.max_instances_per_batch - 1) { - //r_index--; - state.end_batch = true; _render_batch(r_index); } } break; case Item::Command::TYPE_NINEPATCH: { - /* const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); - //bind pipeline - { - RID pipeline = pipeline_variants->variants[light_mode][PIPELINE_VARIANT_NINEPATCH].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format); - RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline); + 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; } //bind textures - - _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, index, last_texture, texpixel_size); + _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); 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()) { - texpixel_size = Size2(1, 1); + state.current_pixel_size = Size2(1, 1); src_rect = Rect2(0, 0, 1, 1); } else { if (np->source != Rect2()) { - 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); + 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); 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; @@ -522,14 +649,14 @@ 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]; - RD::get_singleton()->draw_list_set_state.instance_data_array[r_index](p_draw_list, &state.instance_data_array[r_index], sizeof(PushConstant)); - RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array); - RD::get_singleton()->draw_list_draw(p_draw_list, true); + r_index++; + if (r_index >= state.max_instances_per_batch - 1) { + _render_batch(r_index); + } // Restore if overridden. - 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; -*/ + 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; } break; case Item::Command::TYPE_POLYGON: { @@ -538,15 +665,14 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); ERR_CONTINUE(!pb); - if (polygon->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { - state.end_batch = true; + if (polygon->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { _render_batch(r_index); state.current_primitive_points = 0; state.current_command = Item::Command::TYPE_POLYGON; } - _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index, last_texture, texpixel_size); - state.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); + _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); state.current_primitive = polygon->primitive; state.instance_data_array[r_index].modulation[0] = base_color.r; @@ -560,27 +686,12 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].ninepatch_margins[j] = 0; } - // If the previous operation is not done yet, allocated a new buffer - GLint syncStatus; - glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); - if (syncStatus == GL_UNSIGNALED) { - _allocate_instance_data_buffer(); - } else { - glDeleteSync(state.fences[state.current_buffer]); - } - - glBindBufferBase(GL_UNIFORM_BUFFER, 3, 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), &state.instance_data_array[0], GL_DYNAMIC_DRAW); -#else - void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(ubo, &state.instance_data_array[0], sizeof(InstanceData)); - glUnmapBuffer(GL_UNIFORM_BUFFER); -#endif + _bind_instance_data_buffer(1); glBindVertexArray(pb->vertex_array); - static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + if (pb->color_disabled) { + 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); @@ -591,21 +702,25 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item 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(); + + if (pb->color_disabled) { + // 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: { const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); - if (last_texture != default_canvas_texture || state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) { - state.end_batch = true; + 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; } - _bind_canvas_texture(RID(), current_filter, current_repeat, r_index, last_texture, texpixel_size); - state.canvas_shader.version_bind_shader(state.current_shader_version, 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); - for (uint32_t j = 0; j < MIN(3, primitive->point_count); j++) { + 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; state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j].y; state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j].x; @@ -636,8 +751,6 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item r_index++; } if (r_index >= state.max_instances_per_batch - 1) { - //r_index--; - state.end_batch = true; _render_batch(r_index); } } break; @@ -645,12 +758,17 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item 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); - int instance_count = 1; + 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 (c->type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); @@ -662,26 +780,24 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } else if (c->type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); RID multimesh = mm->multimesh; - mesh = storage->multimesh_get_mesh(multimesh); + mesh = mesh_storage->multimesh_get_mesh(multimesh); texture = mm->texture; - if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { break; } - instance_count = storage->multimesh_get_instances_to_draw(multimesh); + instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); if (instance_count == 0) { break; } - state.instance_data_array[r_index].flags |= 1; //multimesh, trails disabled - if (storage->multimesh_uses_colors(multimesh)) { - state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS; - } - if (storage->multimesh_uses_custom_data(multimesh)) { - state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA; - } + 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); } // TODO: implement particles here @@ -690,16 +806,20 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item break; } - if (texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) { - state.end_batch = true; + 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, last_texture, texpixel_size); + _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 { + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED); + } - uint32_t surf_count = storage->mesh_get_surface_count(mesh); + uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); 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; @@ -711,19 +831,74 @@ 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++) { - RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j); + void *surface = mesh_storage->mesh_get_surface(mesh, j); - RS::PrimitiveType primitive = storage->mesh_surface_get_primitive(surface); + RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - glBindVertexArray(surface->vertex_array); - static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + 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); + } - // Draw directly, no need to batch + 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 (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); + } } - */ } break; case Item::Command::TYPE_TRANSFORM: { const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); @@ -731,20 +906,19 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } break; case Item::Command::TYPE_CLIP_IGNORE: { - /* const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); if (current_clip) { if (ci->ignore != reclip) { if (ci->ignore) { - RD::get_singleton()->draw_list_disable_scissor(p_draw_list); + glDisable(GL_SCISSOR_TEST); reclip = true; } else { - RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect); + // Scissor area is already set + glEnable(GL_SCISSOR_TEST); reclip = false; } } } - */ } break; case Item::Command::TYPE_ANIMATION_SLICE: { /* @@ -760,28 +934,16 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item c = c->next; } + + if (current_clip && reclip) { + //will make it re-enable clipping if needed afterwards + current_clip = nullptr; + } } void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { - if (state.end_batch && r_index > 0) { - // If the previous operation is not done yet, allocate a new buffer - GLint syncStatus; - glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); - if (syncStatus == GL_UNSIGNALED) { - _allocate_instance_data_buffer(); - } else { - glDeleteSync(state.fences[state.current_buffer]); - } - - glBindBufferBase(GL_UNIFORM_BUFFER, 3, 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) * r_index, state.instance_data_array, GL_DYNAMIC_DRAW); -#else - void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * r_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * r_index); - glUnmapBuffer(GL_UNIFORM_BUFFER); -#endif + 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); @@ -793,7 +955,6 @@ void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { 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(); - state.end_batch = false; //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]; @@ -816,25 +977,30 @@ void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { } } -// TODO maybe dont use -void RasterizerCanvasGLES3::_end_batch(uint32_t &r_index) { - 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; - state.instance_data_array[r_index].src_rect[i] = 0.0; - state.instance_data_array[r_index].dst_rect[i] = 0.0; +void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) { + if (p_max_index == 0) { + 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(); + } else { + glDeleteSync(state.fences[state.current_buffer]); + } } - state.instance_data_array[r_index].flags = uint32_t(0); - 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].pad[0] = 0.0; - state.instance_data_array[r_index].pad[1] = 0.0; - state.instance_data_array[r_index].lights[0] = uint32_t(0); - state.instance_data_array[r_index].lights[1] = uint32_t(0); - state.instance_data_array[r_index].lights[2] = uint32_t(0); - state.instance_data_array[r_index].lights[3] = uint32_t(0); + 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() { @@ -876,64 +1042,76 @@ bool RasterizerCanvasGLES3::free(RID p_rid) { void RasterizerCanvasGLES3::update() { } -void RasterizerCanvasGLES3::canvas_begin() { - state.using_transparent_rt = false; +void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backbuffer) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + GLES3::RenderTarget *render_target = texture_storage->get_render_target(p_to_render_target); + + if (p_to_backbuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, render_target->backbuffer_fbo); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); + 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 { + glBindFramebuffer(GL_FRAMEBUFFER, render_target->fbo); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, render_target->backbuffer); + } - if (storage->frame.current_rt) { - storage->bind_framebuffer(storage->frame.current_rt->fbo); - state.using_transparent_rt = storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]; + if (render_target->is_transparent) { + 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); } - if (storage->frame.current_rt && storage->frame.current_rt->clear_requested) { - const Color &col = storage->frame.current_rt->clear_color; + if (render_target && render_target->clear_requested) { + const Color &col = render_target->clear_color; glClearColor(col.r, col.g, col.b, col.a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - storage->frame.current_rt->clear_requested = false; + render_target->clear_requested = false; } - reset_canvas(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + 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); } -void RasterizerCanvasGLES3::canvas_end() { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} +void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); -void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, RID &r_last_texture, Size2 &r_texpixel_size) { if (p_texture == RID()) { - p_texture = default_canvas_texture; + p_texture = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); } - if (r_last_texture == p_texture) { + if (state.current_tex == p_texture) { return; //nothing to do, its the same } + state.current_tex = p_texture; - state.end_batch = true; - _render_batch(r_index); - - RasterizerStorageGLES3::CanvasTexture *ct = nullptr; + GLES3::CanvasTexture *ct = nullptr; - RasterizerStorageGLES3::Texture *t = storage->texture_owner.get_or_null(p_texture); + GLES3::Texture *t = texture_storage->get_texture(p_texture); if (t) { //regular texture if (!t->canvas_texture) { - t->canvas_texture = memnew(RasterizerStorageGLES3::CanvasTexture); + t->canvas_texture = memnew(GLES3::CanvasTexture); t->canvas_texture->diffuse = p_texture; } ct = t->canvas_texture; } else { - ct = storage->canvas_texture_owner.get_or_null(p_texture); + ct = texture_storage->get_canvas_texture(p_texture); } if (!ct) { // Invalid Texture RID. - _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_last_texture, r_texpixel_size); + _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index); return; } @@ -943,66 +1121,60 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat; ERR_FAIL_COND(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT); - RasterizerStorageGLES3::Texture *texture = storage->texture_owner.get_or_null(ct->diffuse); + GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); if (!texture) { - state.current_tex = RID(); - state.current_tex_ptr = nullptr; - ct->size_cache = Size2i(1, 1); - + 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); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - + glBindTexture(GL_TEXTURE_2D, tex->tex_id); } else { - texture = texture->get_ptr(); - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture->tex_id); - state.current_tex = ct->diffuse; + state.current_tex = p_texture; state.current_tex_ptr = texture; ct->size_cache = Size2i(texture->width, texture->height); - texture->GLSetFilter(GL_TEXTURE_2D, filter); - texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); } - RasterizerStorageGLES3::Texture *normal_map = storage->texture_owner.get_or_null(ct->normal_map); + 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 + storage->config.max_texture_image_units - 6); - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->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 { - normal_map = normal_map->get_ptr(); - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); + 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->GLSetFilter(GL_TEXTURE_2D, filter); - texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); } - RasterizerStorageGLES3::Texture *specular_map = storage->texture_owner.get_or_null(ct->specular); + 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 + storage->config.max_texture_image_units - 7); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - + 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 { - specular_map = specular_map->get_ptr(); - - glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 7); + 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->GLSetFilter(GL_TEXTURE_2D, filter); - texture->GLSetRepeat(GL_TEXTURE_2D, repeat); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); } if (ct->use_specular_cache) { @@ -1022,32 +1194,19 @@ 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)); - r_texpixel_size.x = 1.0 / float(ct->size_cache.x); - r_texpixel_size.y = 1.0 / float(ct->size_cache.y); + state.current_pixel_size.x = 1.0 / float(ct->size_cache.x); + state.current_pixel_size.y = 1.0 / float(ct->size_cache.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; - - r_last_texture = p_texture; -} - -void RasterizerCanvasGLES3::_set_uniforms() { + 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; } void RasterizerCanvasGLES3::reset_canvas() { glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); - glDisable(GL_DITHER); glEnable(GL_BLEND); - - // Default to Mix. - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - } + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -1056,7 +1215,7 @@ void RasterizerCanvasGLES3::reset_canvas() { 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::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, Projection *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) { @@ -1090,8 +1249,8 @@ RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vec { 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 - const uint8_t *r = polygon_buffer.ptr(); - float *fptr = (float *)r; + uint8_t *r = polygon_buffer.ptrw(); + float *fptr = reinterpret_cast<float *>(r); uint32_t *uptr = (uint32_t *)r; uint32_t base_offset = 0; { @@ -1109,11 +1268,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))); @@ -1128,7 +1283,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) { @@ -1248,7 +1404,21 @@ void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { glBindBuffer(GL_UNIFORM_BUFFER, 0); } -void RasterizerCanvasGLES3::initialize() { +void RasterizerCanvasGLES3::set_time(double p_time) { + state.time = p_time; +} + +RasterizerCanvasGLES3 *RasterizerCanvasGLES3::singleton = nullptr; + +RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() { + return singleton; +} + +RasterizerCanvasGLES3::RasterizerCanvasGLES3() { + singleton = this; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + // quad buffer { glGenBuffers(1, &data.canvas_quad_vertices); @@ -1370,10 +1540,7 @@ void RasterizerCanvasGLES3::initialize() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - //state.canvas_shadow_shader.init(); - - int uniform_max_size; - glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &uniform_max_size); + 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; @@ -1387,6 +1554,7 @@ void RasterizerCanvasGLES3::initialize() { 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); } @@ -1400,27 +1568,19 @@ void RasterizerCanvasGLES3::initialize() { glBindBuffer(GL_UNIFORM_BUFFER, 0); 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 += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n"; global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n"; - state.canvas_shader.initialize(global_defines); - state.canvas_shader_default_version = state.canvas_shader.version_create(); - state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); - - //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_RGBA_SHADOWS, storage->config.use_rgba_2d_shadows); - - //state.canvas_shader.bind(); - - //state.lens_shader.init(); - - //state.canvas_shader.set_conditional(CanvasOldShaderGLES3::USE_PIXEL_SNAP, GLOBAL_DEF("rendering/quality/2d/use_pixel_snap", false)); + 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); { - default_canvas_group_shader = storage->shader_allocate(); - storage->shader_initialize(default_canvas_group_shader); + default_canvas_group_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_canvas_group_shader); - storage->shader_set_code(default_canvas_group_shader, R"( + material_storage->shader_set_code(default_canvas_group_shader, R"( // Default CanvasGroup shader. shader_type canvas_item; @@ -1435,31 +1595,26 @@ void fragment() { COLOR *= c; } )"); - default_canvas_group_material = storage->material_allocate(); - storage->material_initialize(default_canvas_group_material); + default_canvas_group_material = material_storage->material_allocate(); + material_storage->material_initialize(default_canvas_group_material); - storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); + material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); } - default_canvas_texture = storage->canvas_texture_allocate(); - storage->canvas_texture_initialize(default_canvas_texture); - - state.using_light = nullptr; - state.using_transparent_rt = false; - state.using_skeleton = false; state.current_shader_version = state.canvas_shader_default_version; + state.time = 0.0; } -RasterizerCanvasGLES3::RasterizerCanvasGLES3() { -} RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { - state.canvas_shader.version_free(state.canvas_shader_default_version); - storage->free(default_canvas_group_material); - storage->free(default_canvas_group_shader); - storage->free(default_canvas_texture); -} + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + memdelete_arr(state.instance_data_array); + + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(state.canvas_shader_default_version); + material_storage->material_free(default_canvas_group_material); + material_storage->shader_free(default_canvas_group_shader); + singleton = nullptr; -void RasterizerCanvasGLES3::finalize() { glDeleteBuffers(1, &data.canvas_quad_vertices); glDeleteVertexArrays(1, &data.canvas_quad_array); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 908d79f9f8..f920e37130 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -28,21 +28,24 @@ /* 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" class RasterizerSceneGLES3; class RasterizerCanvasGLES3 : public RendererCanvasRender { + static RasterizerCanvasGLES3 *singleton; + _FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4); _FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3); @@ -50,13 +53,6 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { _FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4); enum { - BASE_UNIFORM_BUFFER_OBJECT = 0, - MATERIAL_UNIFORM_BUFFER_OBJECT = 1, - TRANSFORMS_UNIFORM_BUFFER_OBJECT = 2, - CANVAS_TEXTURE_UNIFORM_BUFFER_OBJECT = 3, - }; - - enum { FLAGS_INSTANCING_MASK = 0x7F, FLAGS_INSTANCING_HAS_COLORS = (1 << 7), @@ -100,6 +96,14 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { }; public: + enum { + BASE_UNIFORM_LOCATION = 0, + GLOBAL_UNIFORM_LOCATION = 1, + LIGHT_UNIFORM_LOCATION = 2, + INSTANCE_UNIFORM_LOCATION = 3, + MATERIAL_UNIFORM_LOCATION = 4, + }; + struct StateBuffer { float canvas_transform[16]; float screen_transform[16]; @@ -164,40 +168,24 @@ public: LocalVector<GLsync> fences; uint32_t current_buffer = 0; - InstanceData *instance_data_array; + InstanceData *instance_data_array = nullptr; bool canvas_texscreen_used; - CanvasShaderGLES3 canvas_shader; RID canvas_shader_current_version; RID canvas_shader_default_version; - //CanvasShadowShaderGLES3 canvas_shadow_shader; - //LensDistortedShaderGLES3 lens_shader; - - bool using_texture_rect; - - bool using_ninepatch; - bool using_skeleton; - - Transform2D skeleton_transform; - Transform2D skeleton_transform_inverse; - Size2i skeleton_texture_size; RID current_tex = RID(); + Size2 current_pixel_size = Size2(); RID current_normal = RID(); RID current_specular = RID(); - RasterizerStorageGLES3::Texture *current_tex_ptr; + 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; - bool end_batch = false; + bool transparent_render_target = false; - Transform3D vp; - Light *using_light; - bool using_shadow; - bool using_transparent_rt; - - // FROM RD Renderer + double time = 0.0; uint32_t max_lights_per_render; uint32_t max_lights_per_item; @@ -215,20 +203,13 @@ public: typedef void Texture; - RasterizerSceneGLES3 *scene_render; - - RasterizerStorageGLES3 *storage; - - void _set_uniforms(); - - void canvas_begin(); - void canvas_end(); + 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); - virtual void reset_canvas(); - virtual 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); + 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, Projection *p_xform_cache); virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override; @@ -247,13 +228,15 @@ 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, RID &r_last_texture, Size2 &r_texpixel_size); + 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; + bool color_disabled = false; + Color color; }; struct { @@ -268,14 +251,16 @@ public: 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 _end_batch(uint32_t &p_max_index); + void _bind_instance_data_buffer(uint32_t p_max_index); void _allocate_instance_data_buffer(); - void initialize(); - void finalize(); + void set_time(double p_time); + + static RasterizerCanvasGLES3 *get_singleton(); 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 087bf36473..33303b1e38 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -29,11 +29,13 @@ /*************************************************************************/ #include "rasterizer_gles3.h" +#include "storage/utilities.h" #ifdef GLES3_ENABLED #include "core/config/project_settings.h" #include "core/os/os.h" +#include "storage/texture_storage.h" #define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 @@ -67,7 +69,7 @@ #endif #endif -#if !defined(IPHONE_ENABLED) && !defined(JAVASCRIPT_ENABLED) +#if !defined(IOS_ENABLED) && !defined(JAVASCRIPT_ENABLED) // We include EGL below to get debug callback on GLES2 platforms, // but EGL is not available on iOS. #define CAN_DEBUG @@ -95,14 +97,12 @@ void RasterizerGLES3::begin_frame(double frame_step) { double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs"); time_total = Math::fmod(time_total, time_roll_over); - storage.frame.time = time_total; - storage.frame.count++; - storage.frame.delta = frame_step; + canvas->set_time(time_total); + scene->set_time(time_total, frame_step); - storage.update_dirty_resources(); - - storage.info.render_final = storage.info.render; - storage.info.render.reset(); + GLES3::Utilities *utilities = GLES3::Utilities::get_singleton(); + utilities->info.render_final = utilities->info.render; + utilities->info.render.reset(); //scene->iteration(); } @@ -193,13 +193,31 @@ typedef void (*DEBUGPROCARB)(GLenum source, typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam); void RasterizerGLES3::initialize() { - print_verbose("Using OpenGL video driver"); + print_line("OpenGL Renderer: " + RS::get_singleton()->get_video_adapter_name()); +} - storage._main_thread_id = Thread::get_caller_id(); +void RasterizerGLES3::finalize() { + memdelete(scene); + memdelete(canvas); + 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); +} +RasterizerGLES3::RasterizerGLES3() { #ifdef GLAD_ENABLED if (!gladLoadGL()) { ERR_PRINT("Error initializing GLAD"); + // FIXME this is an early return from a constructor. Any other code using this instance will crash or the finalizer will crash, because none of + // the members of this instance are initialized, so this just makes debugging harder. It should either crash here intentionally, + // or we need to actually test for this situation before constructing this. return; } #endif @@ -248,50 +266,53 @@ void RasterizerGLES3::initialize() { #endif // GLES_OVER_GL #endif // CAN_DEBUG - print_line("OpenGL Renderer: " + RS::get_singleton()->get_video_adapter_name()); - storage.initialize(); - canvas.initialize(); - // scene.initialize(); - - // make sure the OS knows to only access the renderer from the main thread - OS::get_singleton()->set_render_main_thread_mode(OS::RENDER_MAIN_THREAD_ONLY); + // 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); + gi = memnew(GLES3::GI); + fog = memnew(GLES3::Fog); + canvas = memnew(RasterizerCanvasGLES3()); + scene = memnew(RasterizerSceneGLES3()); } -RasterizerGLES3::RasterizerGLES3() { - canvas.storage = &storage; - canvas.scene_render = &scene; - storage.canvas = &canvas; - //scene.storage = &storage; - storage.scene = &scene; +RasterizerGLES3::~RasterizerGLES3() { } void RasterizerGLES3::prepare_for_blitting_render_targets() { } void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { - ERR_FAIL_COND(storage.frame.current_rt); + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); - RasterizerStorageGLES3::RenderTarget *rt = storage.render_target_owner.get_or_null(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); } else { glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); } glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); - glBlitFramebuffer(0, 0, rt->width, rt->height, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + 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); } // 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) { - // do this once off for all blits - storage.bind_framebuffer_system(); - - storage.frame.current_rt = nullptr; + // 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]; @@ -322,12 +343,8 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c } glClear(GL_COLOR_BUFFER_BIT); - canvas.canvas_begin(); - - RID texture = storage.texture_create(); - //storage.texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_use_filter ? VS::TEXTURE_FLAG_FILTER : 0); - storage._texture_allocate_internal(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D); - storage.texture_set_data(texture, p_image); + RID texture = texture_storage->texture_allocate(); + texture_storage->texture_2d_initialize(texture, p_image); Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); Rect2 screenrect; @@ -349,13 +366,12 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); } - RasterizerStorageGLES3::Texture *t = storage.texture_owner.get_or_null(texture); - glActiveTexture(GL_TEXTURE0 + storage.config.max_texture_image_units - 1); + GLES3::Texture *t = texture_storage->get_texture(texture); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, t->tex_id); glBindTexture(GL_TEXTURE_2D, 0); - canvas.canvas_end(); - storage.free(texture); + texture_storage->texture_free(texture); end_frame(true); } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index a641e189c5..97543af0d5 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -28,15 +28,24 @@ /* 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" +#include "storage/material_storage.h" +#include "storage/mesh_storage.h" +#include "storage/particles_storage.h" +#include "storage/texture_storage.h" +#include "storage/utilities.h" class RasterizerGLES3 : public RendererCompositor { private: @@ -46,16 +55,32 @@ private: double time_total = 0.0; protected: - RasterizerStorageGLES3 storage; - RasterizerCanvasGLES3 canvas; - RasterizerSceneGLES3 scene; + 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; + 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); public: - RendererStorage *get_storage() { return &storage; } - RendererCanvasRender *get_canvas() { return &canvas; } - RendererSceneRender *get_scene() { return &scene; } + 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; } + RendererGI *get_gi() { return gi; } + RendererFog *get_fog() { return fog; } + RendererCanvasRender *get_canvas() { return canvas; } + RendererSceneRender *get_scene() { return scene; } void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true); @@ -67,7 +92,7 @@ public: void end_frame(bool p_swap_buffers); - void finalize() {} + void finalize(); static RendererCompositor *_create_current() { return memnew(RasterizerGLES3); @@ -75,16 +100,16 @@ public: static void make_current() { _create_func = _create_current; + low_end = true; } - virtual bool is_low_end() const { return true; } uint64_t get_frame_number() const { return frame; } double get_frame_delta_time() const { return delta; } RasterizerGLES3(); - ~RasterizerGLES3() {} + ~RasterizerGLES3(); }; #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 121dc86fb2..3e416bc615 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -29,85 +29,374 @@ /*************************************************************************/ #include "rasterizer_scene_gles3.h" +#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/light_storage.h" +#include "storage/mesh_storage.h" +#include "storage/texture_storage.h" + #ifdef GLES3_ENABLED -// TODO: 3D support not implemented yet. +RasterizerSceneGLES3 *RasterizerSceneGLES3::singleton = nullptr; -RasterizerSceneGLES3::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) { - return nullptr; -} +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); -void RasterizerSceneGLES3::geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) { -} + GeometryInstanceGLES3 *ginstance = geometry_instance_alloc.alloc(); + ginstance->data = memnew(GeometryInstanceGLES3::Data); -void RasterizerSceneGLES3::geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) { -} + ginstance->data->base = p_base; + ginstance->data->base_type = type; -void RasterizerSceneGLES3::geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) { -} + ginstance->_mark_dirty(); -void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) { + return ginstance; } -void RasterizerSceneGLES3::geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) { -} +uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() { + return (1 << RS::INSTANCE_LIGHT); +} + +void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) { + GLES3::Config *config = GLES3::Config::get_singleton(); + + omni_light_count = 0; + spot_light_count = 0; + omni_lights.clear(); + spot_lights.clear(); -void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) { + for (uint32_t i = 0; i < p_light_instance_count; i++) { + RS::LightType type = RasterizerSceneGLES3::get_singleton()->light_instance_get_type(p_light_instances[i]); + switch (type) { + case RS::LIGHT_OMNI: { + 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 (spot_light_count < (uint32_t)config->max_lights_per_object) { + spot_lights.push_back(p_light_instances[i]); + spot_light_count++; + } + } break; + default: + break; + } + } +} + +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; + while (surf) { + GeometryInstanceSurface *next = surf->next; + geometry_instance_surface_alloc.free(surf); + surf = next; + } + memdelete(ginstance->data); + geometry_instance_alloc.free(ginstance); +} + +void RasterizerSceneGLES3::GeometryInstanceGLES3::_mark_dirty() { + if (dirty_list_element.in_list()) { + return; + } + + //clear surface caches + GeometryInstanceSurface *surf = surface_caches; + + while (surf) { + GeometryInstanceSurface *next = surf->next; + RasterizerSceneGLES3::get_singleton()->geometry_instance_surface_alloc.free(surf); + surf = next; + } + + 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) { +} + +void RasterizerSceneGLES3::GeometryInstanceGLES3::set_lightmap_capture(const Color *p_sh9) { +} + +void RasterizerSceneGLES3::_update_dirty_geometry_instances() { + while (geometry_instance_dirty_list.first()) { + _geometry_instance_update(geometry_instance_dirty_list.first()->self()); + } +} + +void RasterizerSceneGLES3::_geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker) { + switch (p_notification) { + 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 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); + } + } break; + default: { + //rest of notifications of no interest + } break; + } +} + +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_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) { -} +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) { + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + + bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture; + bool has_base_alpha = ((p_material->shader_data->uses_alpha && !p_material->shader_data->uses_alpha_clip) || has_read_screen_alpha); + bool has_blend_alpha = p_material->shader_data->uses_blend_alpha; + bool has_alpha = has_base_alpha || has_blend_alpha; + + uint32_t flags = 0; + + if (p_material->shader_data->uses_screen_texture) { + flags |= GeometryInstanceSurface::FLAG_USES_SCREEN_TEXTURE; + } + + if (p_material->shader_data->uses_depth_texture) { + flags |= GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE; + } -void RasterizerSceneGLES3::geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) { -} + if (p_material->shader_data->uses_normal_texture) { + flags |= GeometryInstanceSurface::FLAG_USES_NORMAL_TEXTURE; + } -void RasterizerSceneGLES3::geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) { -} + if (ginstance->data->cast_double_sided_shadows) { + flags |= GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS; + } -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) { -} + if (has_alpha || has_read_screen_alpha || p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) { + //material is only meant for alpha pass + flags |= GeometryInstanceSurface::FLAG_PASS_ALPHA; + if (p_material->shader_data->uses_depth_pre_pass && !(p_material->shader_data->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_DISABLED || p_material->shader_data->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED)) { + flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH; + flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW; + } + } else { + flags |= GeometryInstanceSurface::FLAG_PASS_OPAQUE; + flags |= GeometryInstanceSurface::FLAG_PASS_DEPTH; + flags |= GeometryInstanceSurface::FLAG_PASS_SHADOW; + } -void RasterizerSceneGLES3::geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) { -} + GLES3::SceneMaterialData *material_shadow = nullptr; + void *surface_shadow = nullptr; + if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass && !p_material->shader_data->uses_alpha_clip) { + flags |= GeometryInstanceSurface::FLAG_USES_SHARED_SHADOW_MATERIAL; + material_shadow = static_cast<GLES3::SceneMaterialData *>(GLES3::MaterialStorage::get_singleton()->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL)); -void RasterizerSceneGLES3::geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) { -} + RID shadow_mesh = mesh_storage->mesh_get_shadow_mesh(p_mesh); -void RasterizerSceneGLES3::geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) { -} + if (shadow_mesh.is_valid()) { + surface_shadow = mesh_storage->mesh_get_surface(shadow_mesh, p_surface); + } -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) { -} + } else { + material_shadow = p_material; + } -void RasterizerSceneGLES3::geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) { -} + GeometryInstanceSurface *sdcache = geometry_instance_surface_alloc.alloc(); -void RasterizerSceneGLES3::geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) { -} + sdcache->flags = flags; -void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) { -} + sdcache->shader = p_material->shader_data; + sdcache->material = p_material; + sdcache->surface = mesh_storage->mesh_get_surface(p_mesh, p_surface); + sdcache->primitive = mesh_storage->mesh_surface_get_primitive(sdcache->surface); + sdcache->surface_index = p_surface; -uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() { - return 0; -} + if (ginstance->data->dirty_dependencies) { + RSG::utilities->base_update_dependency(p_mesh, &ginstance->data->dependency_tracker); + } + + //shadow + sdcache->shader_shadow = material_shadow->shader_data; + sdcache->material_shadow = material_shadow; + + sdcache->surface_shadow = surface_shadow ? surface_shadow : sdcache->surface; + + sdcache->owner = ginstance; + + sdcache->next = ginstance->surface_caches; + ginstance->surface_caches = sdcache; + + //sortkey + + sdcache->sort.sort_key1 = 0; + sdcache->sort.sort_key2 = 0; -void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) { + sdcache->sort.surface_index = p_surface; + sdcache->sort.material_id_low = p_material_id & 0x0000FFFF; + sdcache->sort.material_id_hi = p_material_id >> 16; + sdcache->sort.shader_id = p_shader_id; + sdcache->sort.geometry_id = p_mesh.get_local_index(); + sdcache->sort.priority = p_material->priority; } -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_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material_data, RID p_mat_src, RID p_mesh) { + GLES3::SceneMaterialData *material_data = p_material_data; + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + + _geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, p_mat_src.get_local_index(), material_storage->material_get_shader_id(p_mat_src), p_mesh); -void RasterizerSceneGLES3::geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) { -} + while (material_data->next_pass.is_valid()) { + RID next_pass = material_data->next_pass; + material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(next_pass, RS::SHADER_SPATIAL)); + if (!material_data || !material_data->shader_data->valid) { + break; + } + if (ginstance->data->dirty_dependencies) { + material_storage->material_update_dependency(next_pass, &ginstance->data->dependency_tracker); + } + _geometry_instance_add_surface_with_material(ginstance, p_surface, material_data, next_pass.get_local_index(), material_storage->material_get_shader_id(next_pass), p_mesh); + } +} + +void RasterizerSceneGLES3::_geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + RID m_src; + + m_src = ginstance->data->material_override.is_valid() ? ginstance->data->material_override : p_material; + + GLES3::SceneMaterialData *material_data = nullptr; + + if (m_src.is_valid()) { + material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(m_src, RS::SHADER_SPATIAL)); + if (!material_data || !material_data->shader_data->valid) { + material_data = nullptr; + } + } + + if (material_data) { + if (ginstance->data->dirty_dependencies) { + material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker); + } + } else { + material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL)); + m_src = scene_globals.default_material; + } + + ERR_FAIL_COND(!material_data); -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) { -} + _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material_data, m_src, p_mesh); -void RasterizerSceneGLES3::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) { + if (ginstance->data->material_overlay.is_valid()) { + m_src = ginstance->data->material_overlay; + + material_data = static_cast<GLES3::SceneMaterialData *>(material_storage->material_get_data(m_src, RS::SHADER_SPATIAL)); + if (material_data && material_data->shader_data->valid) { + if (ginstance->data->dirty_dependencies) { + material_storage->material_update_dependency(m_src, &ginstance->data->dependency_tracker); + } + + _geometry_instance_add_surface_with_material_chain(ginstance, p_surface, material_data, m_src, p_mesh); + } + } } -void RasterizerSceneGLES3::geometry_instance_free(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); + + if (ginstance->data->dirty_dependencies) { + ginstance->data->dependency_tracker.update_begin(); + } + + //add geometry for drawing + switch (ginstance->data->base_type) { + case RS::INSTANCE_MESH: { + const RID *materials = nullptr; + uint32_t surface_count; + RID mesh = ginstance->data->base; + + materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); + if (materials) { + //if no materials, no surfaces. + const RID *inst_materials = ginstance->data->surface_materials.ptr(); + uint32_t surf_mat_count = ginstance->data->surface_materials.size(); + + for (uint32_t j = 0; j < surface_count; j++) { + RID material = (j < surf_mat_count && inst_materials[j].is_valid()) ? inst_materials[j] : materials[j]; + _geometry_instance_add_surface(ginstance, j, material, mesh); + } + } + + ginstance->instance_count = -1; + + } break; + + case RS::INSTANCE_MULTIMESH: { + RID mesh = mesh_storage->multimesh_get_mesh(ginstance->data->base); + if (mesh.is_valid()) { + const RID *materials = nullptr; + uint32_t surface_count; + + materials = mesh_storage->mesh_get_surface_count_and_materials(mesh, surface_count); + if (materials) { + for (uint32_t j = 0; j < surface_count; j++) { + _geometry_instance_add_surface(ginstance, j, materials[j], mesh); + } + } + + ginstance->instance_count = mesh_storage->multimesh_get_instances_to_draw(ginstance->data->base); + } + + } break; + case RS::INSTANCE_PARTICLES: { + } break; + + default: { + } + } + + bool store_transform = true; + ginstance->base_flags = 0; + + if (ginstance->data->base_type == RS::INSTANCE_MULTIMESH) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH; + if (mesh_storage->multimesh_get_transform_format(ginstance->data->base) == RS::MULTIMESH_TRANSFORM_2D) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D; + } + if (mesh_storage->multimesh_uses_colors(ginstance->data->base)) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR; + } + if (mesh_storage->multimesh_uses_custom_data(ginstance->data->base)) { + ginstance->base_flags |= INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA; + } + + //ginstance->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(ginstance->data->base, scene_globals.default_shader_rd, TRANSFORMS_UNIFORM_SET); + + } else if (ginstance->data->base_type == RS::INSTANCE_PARTICLES) { + } else if (ginstance->data->base_type == RS::INSTANCE_MESH) { + } + + ginstance->store_transform_cache = store_transform; + + if (ginstance->data->dirty_dependencies) { + ginstance->data->dependency_tracker.update_end(); + ginstance->data->dirty_dependencies = false; + } + + ginstance->dirty_list_element.remove_from_list(); } /* SHADOW ATLAS API */ @@ -136,107 +425,624 @@ int RasterizerSceneGLES3::get_directional_light_shadow_size(RID p_light_intance) void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) { } -/* SDFGI UPDATE */ +/* SKY API */ -void RasterizerSceneGLES3::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) { +void RasterizerSceneGLES3::_free_sky_data(Sky *p_sky) { + if (p_sky->radiance != 0) { + glDeleteTextures(1, &p_sky->radiance); + p_sky->radiance = 0; + glDeleteFramebuffers(1, &p_sky->radiance_framebuffer); + p_sky->radiance_framebuffer = 0; + } } -int RasterizerSceneGLES3::sdfgi_get_pending_region_count(RID p_render_buffers) const { - return 0; +RID RasterizerSceneGLES3::sky_allocate() { + return sky_owner.allocate_rid(); } -AABB RasterizerSceneGLES3::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const { - return AABB(); +void RasterizerSceneGLES3::sky_initialize(RID p_rid) { + sky_owner.initialize_rid(p_rid); } -uint32_t RasterizerSceneGLES3::sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const { - return 0; -} +void RasterizerSceneGLES3::sky_set_radiance_size(RID p_sky, int p_radiance_size) { + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND(!sky); + ERR_FAIL_COND_MSG(p_radiance_size < 32 || p_radiance_size > 2048, "Sky radiance size must be between 32 and 2048"); -/* SKY API */ + if (sky->radiance_size == p_radiance_size) { + return; // No need to update + } -RID RasterizerSceneGLES3::sky_allocate() { - return RID(); -} + sky->radiance_size = p_radiance_size; -void RasterizerSceneGLES3::sky_initialize(RID p_rid) { + _free_sky_data(sky); + _invalidate_sky(sky); } -void RasterizerSceneGLES3::sky_set_radiance_size(RID p_sky, int p_radiance_size) { -} +void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_mode) { + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND(!sky); -void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_samples) { + if (sky->mode == p_mode) { + return; + } + + sky->mode = p_mode; + _invalidate_sky(sky); } void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { -} + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND(!sky); + + if (sky->material == p_material) { + return; + } + + sky->material = p_material; + _invalidate_sky(sky); +} + +void RasterizerSceneGLES3::_invalidate_sky(Sky *p_sky) { + if (!p_sky->dirty) { + p_sky->dirty = true; + p_sky->dirty_list = dirty_sky_list; + dirty_sky_list = p_sky; + } +} + +void RasterizerSceneGLES3::_update_dirty_skys() { + Sky *sky = dirty_sky_list; -Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) { - return Ref<Image>(); -} + while (sky) { + if (sky->radiance == 0) { + 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); + + GLenum internal_format = GL_RGB10_A2; + + glGenTextures(1, &sky->radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->radiance); + +#ifdef GLES_OVER_GL + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_INT_2_10_10_10_REV; + //TODO, on low-end compare this to allocating each face of each mip individually + // see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexStorage2D.xhtml + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, sky->radiance_size, sky->radiance_size, 0, format, type, nullptr); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#else + glTexStorage2D(GL_TEXTURE_CUBE_MAP, sky->mipmap_count, internal_format, sky->radiance_size, sky->radiance_size); +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + 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 - 1); + + glGenTextures(1, &sky->raw_radiance); + glBindTexture(GL_TEXTURE_CUBE_MAP, sky->raw_radiance); + +#ifdef GLES_OVER_GL + //TODO, on low-end compare this to allocating each face of each mip individually + // see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexStorage2D.xhtml + for (int i = 0; i < 6; i++) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internal_format, sky->radiance_size, sky->radiance_size, 0, format, type, nullptr); + } + + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); +#else + glTexStorage2D(GL_TEXTURE_CUBE_MAP, sky->mipmap_count, internal_format, sky->radiance_size, sky->radiance_size); +#endif + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + 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 - 1); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + } + + sky->reflection_dirty = true; + sky->processing_layer = 0; + + Sky *next = sky->dirty_list; + sky->dirty_list = nullptr; + sky->dirty = false; + sky = next; + } + + dirty_sky_list = nullptr; +} + +void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, 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.is_null()); + + GLES3::SkyMaterialData *material = nullptr; + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); + + RID sky_material; + + GLES3::SkyShaderData *shader_data = nullptr; + + if (sky) { + sky_material = sky->material; + + if (sky_material.is_valid()) { + material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + if (!material || !material->shader_data->valid) { + material = nullptr; + } + } + + if (!material) { + sky_material = sky_globals.default_material; + material = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + + ERR_FAIL_COND(!material); + + shader_data = material->shader_data; + + ERR_FAIL_COND(!shader_data); + + if (shader_data->uses_time && time - sky->prev_time > 0.00001) { + sky->prev_time = time; + sky->reflection_dirty = true; + RenderingServerDefault::redraw_request(); + } + + if (material != sky->prev_material) { + sky->prev_material = material; + sky->reflection_dirty = true; + } + + if (material->uniform_set_updated) { + material->uniform_set_updated = false; + sky->reflection_dirty = true; + } + + if (!p_transform.origin.is_equal_approx(sky->prev_position) && shader_data->uses_position) { + sky->prev_position = p_transform.origin; + sky->reflection_dirty = true; + } + + 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]); + if (!li) { + continue; + } + RID base = li->light; + + ERR_CONTINUE(base.is_null()); + + RS::LightType type = light_storage->light_get_type(base); + if (type == RS::LIGHT_DIRECTIONAL && light_storage->light_directional_get_sky_mode(base) != RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_ONLY) { + DirectionalLightData &sky_light_data = sky_globals.directional_lights[sky_globals.directional_light_count]; + Transform3D light_transform = li->transform; + Vector3 world_direction = light_transform.basis.xform(Vector3(0, 0, 1)).normalized(); + + sky_light_data.direction[0] = world_direction.x; + sky_light_data.direction[1] = world_direction.y; + sky_light_data.direction[2] = world_direction.z; + + 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); + + 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; + sky_light_data.color[2] = linear_col.b; + + sky_light_data.enabled = true; + + 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)); + } else { + angular_diameter = 0.0; + } + sky_light_data.size = angular_diameter; + sky_globals.directional_light_count++; + if (sky_globals.directional_light_count >= sky_globals.max_directional_lights) { + break; + } + } + } + // Check whether the directional_light_buffer changes + bool light_data_dirty = false; + + // Light buffer is dirty if we have fewer or more lights + // If we have fewer lights, make sure that old lights are disabled + if (sky_globals.directional_light_count != sky_globals.last_frame_directional_light_count) { + 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; + } + } + + if (!light_data_dirty) { + for (uint32_t i = 0; i < sky_globals.directional_light_count; i++) { + if (sky_globals.directional_lights[i].direction[0] != sky_globals.last_frame_directional_lights[i].direction[0] || + sky_globals.directional_lights[i].direction[1] != sky_globals.last_frame_directional_lights[i].direction[1] || + sky_globals.directional_lights[i].direction[2] != sky_globals.last_frame_directional_lights[i].direction[2] || + sky_globals.directional_lights[i].energy != sky_globals.last_frame_directional_lights[i].energy || + sky_globals.directional_lights[i].color[0] != sky_globals.last_frame_directional_lights[i].color[0] || + sky_globals.directional_lights[i].color[1] != sky_globals.last_frame_directional_lights[i].color[1] || + sky_globals.directional_lights[i].color[2] != sky_globals.last_frame_directional_lights[i].color[2] || + sky_globals.directional_lights[i].enabled != sky_globals.last_frame_directional_lights[i].enabled || + sky_globals.directional_lights[i].size != sky_globals.last_frame_directional_lights[i].size) { + light_data_dirty = true; + break; + } + } + } + + 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); + + DirectionalLightData *temp = sky_globals.last_frame_directional_lights; + sky_globals.last_frame_directional_lights = sky_globals.directional_lights; + sky_globals.directional_lights = temp; + sky_globals.last_frame_directional_light_count = sky_globals.directional_light_count; + sky->reflection_dirty = true; + } + } + + if (!sky->radiance) { + _invalidate_sky(sky); + _update_dirty_skys(); + } + } +} + +void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + ERR_FAIL_COND(p_env.is_null()); + + 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 = environment_get_background(p_env); + + if (sky) { + ERR_FAIL_COND(!sky); + sky_material = sky->material; + + if (sky_material.is_valid()) { + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + if (!material_data || !material_data->shader_data->valid) { + material_data = nullptr; + } + } + + if (!material_data) { + sky_material = sky_globals.default_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + } else if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) { + sky_material = sky_globals.fog_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + + ERR_FAIL_COND(!material_data); + material_data->bind_uniforms(); + + GLES3::SkyShaderData *shader_data = material_data->shader_data; + + ERR_FAIL_COND(!shader_data); + + // Camera + Projection camera; + + 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(environment_get_sky_custom_fov(p_env), aspect, near_plane, far_plane); + } else { + camera = p_projection; + } + 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::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); + + glBindVertexArray(sky_globals.screen_triangle_array); + glDrawArrays(GL_TRIANGLES, 0, 3); +} + +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + ERR_FAIL_COND(p_env.is_null()); + + 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 = environment_get_background(p_env); + + if (sky) { + ERR_FAIL_COND(!sky); + sky_material = sky->material; + + if (sky_material.is_valid()) { + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + if (!material_data || !material_data->shader_data->valid) { + material_data = nullptr; + } + } + + if (!material_data) { + sky_material = sky_globals.default_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + } else if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) { + sky_material = sky_globals.fog_material; + material_data = static_cast<GLES3::SkyMaterialData *>(material_storage->material_get_data(sky_material, RS::SHADER_SKY)); + } + + ERR_FAIL_COND(!material_data); + material_data->bind_uniforms(); + + GLES3::SkyShaderData *shader_data = material_data->shader_data; + + ERR_FAIL_COND(!shader_data); + + bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY; + RS::SkyMode sky_mode = sky->mode; + + if (sky_mode == RS::SKY_MODE_AUTOMATIC) { + if (shader_data->uses_time || shader_data->uses_position) { + update_single_frame = true; + sky_mode = RS::SKY_MODE_REALTIME; + } else if (shader_data->uses_light || shader_data->ubo_size > 0) { + update_single_frame = false; + sky_mode = RS::SKY_MODE_INCREMENTAL; + } else { + update_single_frame = true; + sky_mode = RS::SKY_MODE_QUALITY; + } + } + + if (sky->processing_layer == 0 && sky_mode == RS::SKY_MODE_INCREMENTAL) { + // On the first frame after creating sky, rebuild in single frame + update_single_frame = true; + sky_mode = RS::SKY_MODE_QUALITY; + } + + int max_processing_layer = sky->mipmap_count; + + // Update radiance cubemap + 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), + Vector3(0, +1, 0), + Vector3(0, -1, 0), + Vector3(0, 0, +1), + Vector3(0, 0, -1) + }; + static const Vector3 view_up[6] = { + Vector3(0, -1, 0), + Vector3(0, -1, 0), + Vector3(0, 0, +1), + Vector3(0, 0, -1), + Vector3(0, -1, 0), + Vector3(0, -1, 0) + }; + + Projection cm; + cm.set_perspective(90, 1, 0.01, 10.0); + Projection correction; + correction.set_depth_correction(true); + 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); + + // 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); + glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer); + + for (int i = 0; i < 6; i++) { + Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, local_view, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, sky->raw_radiance, 0); + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + if (update_single_frame) { + for (int i = 0; i < max_processing_layer; i++) { + _filter_sky_radiance(sky, i); + } + } else { + _filter_sky_radiance(sky, 0); //Just copy over the first mipmap + } + sky->processing_layer = 1; + + sky->reflection_dirty = false; + } else { + if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { + _filter_sky_radiance(sky, sky->processing_layer); + sky->processing_layer++; + } + } +} + +// 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(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, p_sky->raw_radiance); + glBindFramebuffer(GL_FRAMEBUFFER, p_sky->radiance_framebuffer); -/* ENVIRONMENT API */ + CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT; -RID RasterizerSceneGLES3::environment_allocate() { - return RID(); -} + if (p_base_layer == 0) { + glGenerateMipmap(GL_TEXTURE_CUBE_MAP); + // Copy over base layer without filtering. + mode = CubemapFilterShaderGLES3::MODE_COPY; + } -void RasterizerSceneGLES3::environment_initialize(RID p_rid) { -} + int size = p_sky->radiance_size >> p_base_layer; + glViewport(0, 0, size, size); + glBindVertexArray(sky_globals.screen_triangle_array); -void RasterizerSceneGLES3::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) { -} + material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode); -void RasterizerSceneGLES3::environment_set_sky(RID p_env, RID p_sky) { -} + 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)]; -void RasterizerSceneGLES3::environment_set_sky_custom_fov(RID p_env, float p_scale) { -} + float roughness = float(p_base_layer) / (p_sky->mipmap_count); + float roughness4 = roughness * roughness; + roughness4 *= roughness4; -void RasterizerSceneGLES3::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) { -} + float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size); -void RasterizerSceneGLES3::environment_set_bg_color(RID p_env, const Color &p_color) { -} + LocalVector<float> sample_directions; + sample_directions.resize(4 * sample_count); -void RasterizerSceneGLES3::environment_set_bg_energy(RID p_env, float p_energy) { -} + 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)); -void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { -} + 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); -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) { + 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); + 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); + + glDrawArrays(GL_TRIANGLES, 0, 3); + } + glBindVertexArray(0); + glViewport(0, 0, p_sky->screen_size.x, p_sky->screen_size.y); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } -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) { +Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) { + return Ref<Image>(); } +/* ENVIRONMENT API */ + void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool p_enable) { + glow_bicubic_upscale = p_enable; } void RasterizerSceneGLES3::environment_glow_set_use_high_quality(bool 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) { + glow_high_quality = p_enable; } 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) { -} - 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) { } @@ -246,18 +1052,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) { -} - -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) { -} - -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) { -} - -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) { } @@ -268,18 +1062,6 @@ Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bak return Ref<Image>(); } -bool RasterizerSceneGLES3::is_environment(RID p_env) const { - return false; -} - -RS::EnvironmentBG RasterizerSceneGLES3::environment_get_background(RID p_env) const { - return RS::ENV_BG_KEEP; -} - -int RasterizerSceneGLES3::environment_get_canvas_max_layer(RID p_env) const { - return 0; -} - RID RasterizerSceneGLES3::camera_effects_allocate() { return RID(); } @@ -299,23 +1081,39 @@ void RasterizerSceneGLES3::camera_effects_set_dof_blur(RID p_camera_effects, boo 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::positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } -void RasterizerSceneGLES3::directional_shadow_quality_set(RS::ShadowQuality p_quality) { +void RasterizerSceneGLES3::directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } RID RasterizerSceneGLES3::light_instance_create(RID p_light) { - return RID(); + 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::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 RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) { @@ -401,35 +1199,1172 @@ 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) { } -void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_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::_fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append) { + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + + if (p_render_list == RENDER_LIST_OPAQUE) { + scene_state.used_screen_texture = false; + scene_state.used_normal_texture = false; + scene_state.used_depth_texture = false; + } + + Plane near_plane; + if (p_render_data->cam_orthogonal) { + near_plane = Plane(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z), p_render_data->cam_transform.origin); + near_plane.d += p_render_data->cam_projection.get_z_near(); + } + float z_max = p_render_data->cam_projection.get_z_far() - p_render_data->cam_projection.get_z_near(); + + RenderList *rl = &render_list[p_render_list]; + + // Parse any updates on our geometry, updates surface caches and such + _update_dirty_geometry_instances(); + + if (!p_append) { + rl->clear(); + if (p_render_list == RENDER_LIST_OPAQUE) { + render_list[RENDER_LIST_ALPHA].clear(); //opaque fills alpha too + } + } + + //fill list + + for (int i = 0; i < (int)p_render_data->instances->size(); i++) { + GeometryInstanceGLES3 *inst = static_cast<GeometryInstanceGLES3 *>((*p_render_data->instances)[i]); + + if (p_render_data->cam_orthogonal) { + Vector3 support_min = inst->transformed_aabb.get_support(-near_plane.normal); + inst->depth = near_plane.distance_to(support_min); + } else { + Vector3 aabb_center = inst->transformed_aabb.position + (inst->transformed_aabb.size * 0.5); + inst->depth = p_render_data->cam_transform.origin.distance_to(aabb_center); + } + uint32_t depth_layer = CLAMP(int(inst->depth * 16 / z_max), 0, 15); + + uint32_t flags = inst->base_flags; //fill flags if appropriate + + if (inst->non_uniform_scale) { + flags |= INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE; + } + + // Sets the index values for lookup in the shader + // This has to be done after _setup_lights was called this frame + // TODO, check shadow status of lights here, if using shadows, skip here and add below + if (p_pass_mode == PASS_MODE_COLOR) { + 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]); + } + } + 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->flags_cache = flags; + + GeometryInstanceSurface *surf = inst->surface_caches; + + while (surf) { + // 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); + + 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); + + float distance = 0.0; + + if (distance_min * distance_max < 0.0) { + //crossing plane + distance = 0.0; + } else if (distance_min >= 0.0) { + distance = distance_min; + } else if (distance_max <= 0.0) { + distance = -distance_max; + } + + if (p_render_data->cam_orthogonal) { + 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); + /* + if (p_render_data->render_info) { + indices = _indices_to_primitives(surf->primitive, indices); + if (p_render_list == RENDER_LIST_OPAQUE) { //opaque + p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices; + } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow + p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += indices; + } + } + */ + } else { + surf->lod_index = 0; + /* + if (p_render_data->render_info) { + uint32_t to_draw = mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface); + to_draw = _indices_to_primitives(surf->primitive, to_draw); + to_draw *= inst->instance_count; + if (p_render_list == RENDER_LIST_OPAQUE) { //opaque + p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface); + } else if (p_render_list == RENDER_LIST_SECONDARY) { //shadow + p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW][RS::VIEWPORT_RENDER_INFO_PRIMITIVES_IN_FRAME] += mesh_storage->mesh_surface_get_vertices_drawn_count(surf->surface); + } + } + */ + } + + // ADD Element + if (p_pass_mode == PASS_MODE_COLOR) { +#ifdef DEBUG_ENABLED + bool force_alpha = unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW); +#else + bool force_alpha = false; +#endif + if (!force_alpha && (surf->flags & GeometryInstanceSurface::FLAG_PASS_OPAQUE)) { + rl->add_element(surf); + } + if (force_alpha || (surf->flags & GeometryInstanceSurface::FLAG_PASS_ALPHA)) { + render_list[RENDER_LIST_ALPHA].add_element(surf); + } + + if (surf->flags & GeometryInstanceSurface::FLAG_USES_SCREEN_TEXTURE) { + scene_state.used_screen_texture = true; + } + if (surf->flags & GeometryInstanceSurface::FLAG_USES_NORMAL_TEXTURE) { + scene_state.used_normal_texture = true; + } + if (surf->flags & GeometryInstanceSurface::FLAG_USES_DEPTH_TEXTURE) { + scene_state.used_depth_texture = true; + } + + /* + Add elements here if there are shadows + */ + + } else if (p_pass_mode == PASS_MODE_SHADOW) { + if (surf->flags & GeometryInstanceSurface::FLAG_PASS_SHADOW) { + rl->add_element(surf); + } + } else { + if (surf->flags & (GeometryInstanceSurface::FLAG_PASS_DEPTH | GeometryInstanceSurface::FLAG_PASS_OPAQUE)) { + rl->add_element(surf); + } + } + + surf->sort.depth_layer = depth_layer; + + surf = surf->next; + } + } +} + +// 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) { + Projection correction; + correction.set_depth_correction(p_flip_y); + Projection projection = correction * p_render_data->cam_projection; + //store camera into ubo + 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); + + scene_state.ubo.directional_light_count = p_render_data->directional_light_count; + + scene_state.ubo.z_far = p_render_data->z_far; + scene_state.ubo.z_near = p_render_data->z_near; + + scene_state.ubo.viewport_size[0] = p_screen_size.x; + scene_state.ubo.viewport_size[1] = p_screen_size.y; + + Size2 screen_pixel_size = Vector2(1.0, 1.0) / Size2(p_screen_size); + scene_state.ubo.screen_pixel_size[0] = screen_pixel_size.x; + scene_state.ubo.screen_pixel_size[1] = screen_pixel_size.y; + + //time global variables + scene_state.ubo.time = time; + + if (is_environment(p_render_data->environment)) { + 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 = environment_get_bg_energy(p_render_data->environment); + scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + + 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 : 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.use_ambient_light = true; + scene_state.ubo.use_ambient_cubemap = false; + } else { + 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 = environment_get_sky_orientation(p_render_data->environment); + sky_transform = sky_transform.inverse() * p_render_data->cam_transform.basis; + 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 = 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 = 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 = 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 = environment_get_fog_sun_scatter(p_render_data->environment); + + } else { + } + + 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); +} + +// Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer +void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count) { + GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + const Transform3D inverse_transform = p_render_data->inv_cam_transform; + + const PagedArray<RID> &lights = *p_render_data->lights; + + r_directional_light_count = 0; + r_omni_light_count = 0; + r_spot_light_count = 0; + + int num_lights = lights.size(); + + for (int i = 0; i < num_lights; i++) { + LightInstance *li = light_instance_owner.get_or_null(lights[i]); + if (!li) { + continue; + } + RID base = li->light; + + ERR_CONTINUE(base.is_null()); + + RS::LightType type = light_storage->light_get_type(base); + switch (type) { + case RS::LIGHT_DIRECTIONAL: { + if (r_directional_light_count >= RendererSceneRender::MAX_DIRECTIONAL_LIGHTS || light_storage->light_directional_get_sky_mode(base) == RS::LIGHT_DIRECTIONAL_SKY_MODE_SKY_ONLY) { + continue; + } + + DirectionalLightData &light_data = scene_state.directional_lights[r_directional_light_count]; + + Transform3D light_transform = li->transform; + + Vector3 direction = inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, 1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + 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; + + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); + light_data.color[0] = linear_col.r; + light_data.color[1] = linear_col.g; + 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.specular = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR); -void RasterizerSceneGLES3::render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { -} + r_directional_light_count++; + } break; + case RS::LIGHT_OMNI: { + if (r_omni_light_count >= (uint32_t)config->max_renderable_lights) { + continue; + } -void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) { -} + const real_t distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (light_storage->light_is_distance_fade_enabled(li->light)) { + const float fade_begin = light_storage->light_get_distance_fade_begin(li->light); + const float fade_length = light_storage->light_get_distance_fade_length(li->light); + + if (distance > fade_begin) { + if (distance > fade_begin + fade_length) { + // Out of range, don't draw this light to improve performance. + continue; + } + } + } + + li->gl_id = r_omni_light_count; -void RasterizerSceneGLES3::set_scene_pass(uint64_t p_pass) { + scene_state.omni_light_sort[r_omni_light_count].instance = li; + scene_state.omni_light_sort[r_omni_light_count].depth = distance; + r_omni_light_count++; + } break; + case RS::LIGHT_SPOT: { + if (r_spot_light_count >= (uint32_t)config->max_renderable_lights) { + continue; + } + + const real_t distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (light_storage->light_is_distance_fade_enabled(li->light)) { + const float fade_begin = light_storage->light_get_distance_fade_begin(li->light); + const float fade_length = light_storage->light_get_distance_fade_length(li->light); + + if (distance > fade_begin) { + if (distance > fade_begin + fade_length) { + // Out of range, don't draw this light to improve performance. + continue; + } + } + } + + li->gl_id = r_spot_light_count; + + scene_state.spot_light_sort[r_spot_light_count].instance = li; + scene_state.spot_light_sort[r_spot_light_count].depth = distance; + r_spot_light_count++; + } break; + } + } + + if (r_omni_light_count) { + SortArray<InstanceSort<LightInstance>> sorter; + sorter.sort(scene_state.omni_light_sort, r_omni_light_count); + } + + if (r_spot_light_count) { + SortArray<InstanceSort<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; + RID base = li->light; + + Transform3D light_transform = li->transform; + Vector3 pos = inverse_transform.xform(light_transform.origin); + + light_data.position[0] = pos.x; + light_data.position[1] = pos.y; + light_data.position[2] = pos.z; + + float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); + light_data.inv_radius = 1.0 / radius; + + Vector3 direction = inverse_transform.basis.xform(light_transform.basis.xform(Vector3(0, 0, -1))).normalized(); + + light_data.direction[0] = direction.x; + light_data.direction[1] = direction.y; + light_data.direction[2] = direction.z; + + float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); + + light_data.size = size; + + float sign = light_storage->light_is_negative(base) ? -1 : 1; + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); + + // Reuse fade begin, fade length and distance for shadow LOD determination later. + float fade_begin = 0.0; + float fade_length = 0.0; + real_t distance = 0.0; + + float fade = 1.0; + if (light_storage->light_is_distance_fade_enabled(li->light)) { + fade_begin = light_storage->light_get_distance_fade_begin(li->light); + fade_length = light_storage->light_get_distance_fade_length(li->light); + distance = p_render_data->cam_transform.origin.distance_to(li->transform.origin); + + if (distance > fade_begin) { + // Use `smoothstep()` to make opacity changes more gradual and less noticeable to the player. + fade = Math::smoothstep(0.0f, 1.0f, 1.0f - float(distance - fade_begin) / fade_length); + } + } + + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + + light_data.color[0] = linear_col.r * energy; + light_data.color[1] = linear_col.g * energy; + light_data.color[2] = linear_col.b * energy; + + light_data.attenuation = light_storage->light_get_param(base, RS::LIGHT_PARAM_ATTENUATION); + + 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.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0; + + 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) { + 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) { + 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) { + 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<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_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) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + RENDER_TIMESTAMP("Setup 3D Scene"); + + RenderBuffers *rb = nullptr; + if (p_render_buffers.is_valid()) { + rb = render_buffers_owner.get_or_null(p_render_buffers); + ERR_FAIL_COND(!rb); + } + + // 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; + // 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.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]; + } + + render_data.z_near = p_camera_data->main_projection.get_z_near(); + render_data.z_far = p_camera_data->main_projection.get_z_far(); + + render_data.instances = &p_instances; + 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.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; + } else { + render_data.screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; + } + render_data.render_info = r_render_info; + } + + PagedArray<RID> empty; + + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { + render_data.lights = ∅ + render_data.reflection_probes = ∅ + } + + bool reverse_cull = false; + + /////////// + // Fill Light lists here + ////////// + + GLuint global_buffer = GLES3::MaterialStorage::get_singleton()->global_shader_uniforms_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 = texture_storage->get_default_clear_color(); + } + + bool fb_cleared = false; + + Size2i screen_size; + screen_size.x = rb->width; + screen_size.y = rb->height; + + bool use_wireframe = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME; + + SceneState::TonemapUBO tonemap_ubo; + 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) { + // Only create if using 3D + glGenBuffers(1, &scene_state.tonemap_buffer); + } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW); + + _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); + + _fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR); + render_list[RENDER_LIST_OPAQUE].sort_by_key(); + render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); + + bool draw_sky = false; + bool draw_sky_fog_only = false; + bool keep_color = false; + + 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 (render_data.environment.is_valid()) { + RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment); + float bg_energy = environment_get_bg_energy(render_data.environment); + 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 (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 = environment_get_bg_color(render_data.environment); + clear_color.r *= bg_energy; + clear_color.g *= bg_energy; + clear_color.b *= bg_energy; + 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_SKY: { + draw_sky = true; + } break; + case RS::ENV_BG_CANVAS: { + keep_color = true; + } break; + case RS::ENV_BG_KEEP: { + keep_color = true; + } break; + case RS::ENV_BG_CAMERA_FEED: { + } break; + default: { + } + } + // setup sky if used for ambient, reflections, or background + 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"); + Projection projection = render_data.cam_projection; + if (render_data.reflection_probe.is_valid()) { + Projection correction; + correction.set_depth_correction(true); + projection = correction * render_data.cam_projection; + } + + _setup_sky(render_data.environment, p_render_buffers, *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); + } + } else { + // do not try to draw sky if invalid + draw_sky = false; + } + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, rb->framebuffer); + glViewport(0, 0, rb->width, rb->height); + + // Do depth prepass if it's explicitly enabled + bool use_depth_prepass = config->use_depth_prepass; + + // Don't do depth prepass we are rendering overdraw + use_depth_prepass = use_depth_prepass && get_debug_draw_mode() != RS::VIEWPORT_DEBUG_DRAW_OVERDRAW; + + if (use_depth_prepass) { + RENDER_TIMESTAMP("Depth Prepass"); + //pre z pass + + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDisable(GL_SCISSOR_TEST); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + + glColorMask(0, 0, 0, 0); + glClearDepth(1.0f); + glClear(GL_DEPTH_BUFFER_BIT); + + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, 0, use_wireframe); + _render_list_template<PASS_MODE_DEPTH>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); + + glColorMask(1, 1, 1, 1); + + fb_cleared = true; + scene_state.used_depth_prepass = true; + } else { + scene_state.used_depth_prepass = false; + } + + glBlendEquation(GL_FUNC_ADD); + + if (render_data.transparent_bg) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + glDisable(GL_BLEND); + } + scene_state.current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; + scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; + + if (!fb_cleared) { + glClearDepth(1.0f); + glClear(GL_DEPTH_BUFFER_BIT); + } + + if (!keep_color) { + glClearBufferfv(GL_COLOR, 0, clear_color.components); + } + RENDER_TIMESTAMP("Render Opaque Pass"); + uint32_t spec_constant_base_flags = 0; + + { + // Specialization Constants that apply for entire rendering pass. + if (render_data.directional_light_count == 0) { + spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS; + } + + 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; + } + } + // Render Opaque Objects. + RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); + + _render_list_template<PASS_MODE_COLOR>(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); + + if (draw_sky) { + RENDER_TIMESTAMP("Render Sky"); + if (scene_state.current_depth_test != GLES3::SceneShaderData::DEPTH_TEST_ENABLED) { + glEnable(GL_DEPTH_TEST); + scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; + } + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; + scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; + scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + + _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform); + } + + RENDER_TIMESTAMP("Render 3D Transparent Pass"); + glEnable(GL_BLEND); + + //Render transparent pass + RenderListParameters render_list_params_alpha(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); + + _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); + } + glDisable(GL_BLEND); + texture_storage->render_target_disable_clear_request(rb->render_target); +} + +template <PassMode p_pass_mode> +void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass) { + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + GLuint prev_vertex_array_gl = 0; + GLuint prev_index_array_gl = 0; + + 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. + + switch (p_pass_mode) { + case PASS_MODE_COLOR: + case PASS_MODE_COLOR_TRANSPARENT: { + } break; + case PASS_MODE_COLOR_ADDITIVE: { + shader_variant = SceneShaderGLES3::MODE_ADDITIVE; + } break; + case PASS_MODE_SHADOW: + case PASS_MODE_DEPTH: { + shader_variant = SceneShaderGLES3::MODE_DEPTH; + } break; + } + + if (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 (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; + } + glBindTexture(GL_TEXTURE_CUBE_MAP, texture_to_bind); + } + } + + for (uint32_t i = p_from_element; i < p_to_element; i++) { + const GeometryInstanceSurface *surf = p_params->elements[i]; + GeometryInstanceGLES3 *inst = surf->owner; + + if (p_pass_mode == PASS_MODE_COLOR && !(surf->flags & GeometryInstanceSurface::FLAG_PASS_OPAQUE)) { + continue; // Objects with "Depth-prepass" transparency are included in both render lists, but should only be rendered in the transparent pass + } + + if (inst->instance_count == 0) { + 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) { + shader = surf->shader_shadow; + material_data = surf->material_shadow; + mesh_surface = surf->surface_shadow; + } else { + shader = surf->shader; + material_data = surf->material; + mesh_surface = surf->surface; + } + + if (!mesh_surface) { + continue; + } + + if (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); + } else { + glEnable(GL_DEPTH_TEST); + } + scene_state.current_depth_test = shader->depth_test; + } + } + + if (scene_state.current_depth_draw != shader->depth_draw) { + switch (shader->depth_draw) { + case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: { + glDepthMask(p_pass_mode == PASS_MODE_COLOR); + } break; + case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: { + glDepthMask(GL_TRUE); + } break; + case GLES3::SceneShaderData::DEPTH_DRAW_DISABLED: { + glDepthMask(GL_FALSE); + } break; + } + + scene_state.current_depth_draw = shader->depth_draw; + } + + if (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) { + desired_blend_mode = GLES3::SceneShaderData::BLEND_MODE_ADD; + } else { + desired_blend_mode = shader->blend_mode; + } + + if (desired_blend_mode != scene_state.current_blend_mode) { + switch (desired_blend_mode) { + case GLES3::SceneShaderData::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + if (p_render_data->transparent_bg) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + } + + } break; + case GLES3::SceneShaderData::BLEND_MODE_ADD: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(p_pass_mode == PASS_MODE_COLOR_TRANSPARENT ? GL_SRC_ALPHA : GL_ONE, GL_ONE); + + } break; + case GLES3::SceneShaderData::BLEND_MODE_SUB: { + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + } break; + case GLES3::SceneShaderData::BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + if (p_render_data->transparent_bg) { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO); + } else { + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); + } + + } break; + case GLES3::SceneShaderData::BLEND_MODE_ALPHA_TO_COVERAGE: { + // Do nothing for now. + } break; + } + scene_state.current_blend_mode = desired_blend_mode; + } + } + + //find cull variant + GLES3::SceneShaderData::Cull cull_mode = shader->cull_mode; + + if ((surf->flags & GeometryInstanceSurface::FLAG_USES_DOUBLE_SIDED_SHADOWS)) { + cull_mode = GLES3::SceneShaderData::CULL_DISABLED; + } else { + bool mirror = inst->mirror; + if (p_params->reverse_cull) { + mirror = !mirror; + } + if (cull_mode == GLES3::SceneShaderData::CULL_FRONT && mirror) { + cull_mode = GLES3::SceneShaderData::CULL_BACK; + } else if (cull_mode == GLES3::SceneShaderData::CULL_BACK && mirror) { + cull_mode = GLES3::SceneShaderData::CULL_FRONT; + } + } + + if (scene_state.cull_mode != cull_mode) { + if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) { + glDisable(GL_CULL_FACE); + } else { + if (scene_state.cull_mode == GLES3::SceneShaderData::CULL_DISABLED) { + // Last time was disabled, so enable and set proper face. + glEnable(GL_CULL_FACE); + } + glCullFace(cull_mode == GLES3::SceneShaderData::CULL_FRONT ? GL_FRONT : GL_BACK); + } + scene_state.cull_mode = cull_mode; + } + + RS::PrimitiveType primitive = surf->primitive; + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + GLenum primitive_gl = prim[int(primitive)]; + + GLuint vertex_array_gl = 0; + GLuint index_array_gl = 0; + + //skeleton and blend shape + if (surf->owner->mesh_instance.is_valid()) { + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(surf->owner->mesh_instance, surf->surface_index, shader->vertex_input_mask, vertex_array_gl); + } else { + mesh_storage->mesh_surface_get_vertex_arrays_and_format(mesh_surface, shader->vertex_input_mask, vertex_array_gl); + } + + 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); + prev_vertex_array_gl = vertex_array_gl; + } + + 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); + } + prev_index_array_gl = index_array_gl; + } + + Transform3D world_transform; + if (inst->store_transform_cache) { + world_transform = inst->transform; + } + + if (prev_material_data != material_data) { + material_data->bind_uniforms(); + prev_material_data = material_data; + } + + 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); + float opaque_prepass_threshold = 0.0; + if (p_pass_mode == PASS_MODE_DEPTH) { + opaque_prepass_threshold = 0.99; + } else if (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, instance_variant); + + prev_shader = shader; + prev_variant = instance_variant; + } + + 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, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant); + + if (inst->omni_light_count) { + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant), 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, instance_variant), 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, instance_variant); + 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 { + // 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); + } + } +} + +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<RenderGeometryInstance *> &p_instances) { } void RasterizerSceneGLES3::set_time(double p_time, double p_step) { + time = p_time; + time_step = p_step; } void RasterizerSceneGLES3::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) { + debug_draw = p_debug_draw; } RID RasterizerSceneGLES3::render_buffers_create() { - return RID(); + 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_debanding, uint32_t p_view_count) { +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_texture_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_COMPONENT24, 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; + } +} + +//clear render buffers +/* + + + if (rt->copy_screen_effect.color) { + glDeleteFramebuffers(1, &rt->copy_screen_effect.fbo); + rt->copy_screen_effect.fbo = 0; + + glDeleteTextures(1, &rt->copy_screen_effect.color); + rt->copy_screen_effect.color = 0; + } + + if (rt->multisample_active) { + glDeleteFramebuffers(1, &rt->multisample_fbo); + rt->multisample_fbo = 0; + + glDeleteRenderbuffers(1, &rt->multisample_depth); + rt->multisample_depth = 0; + + glDeleteRenderbuffers(1, &rt->multisample_color); + + rt->multisample_color = 0; + } +*/ + +void RasterizerSceneGLES3::_render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer) { } void RasterizerSceneGLES3::gi_set_use_half_resolution(bool p_enable) { @@ -453,10 +2388,31 @@ TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const Vector } bool RasterizerSceneGLES3::free(RID p_rid) { - return false; + 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 { + return false; + } + return true; } void RasterizerSceneGLES3::update() { + _update_dirty_skys(); } void RasterizerSceneGLES3::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { @@ -469,6 +2425,209 @@ void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter } RasterizerSceneGLES3::RasterizerSceneGLES3() { + singleton = this; + + GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + { + // Setup Lights + + config->max_renderable_lights = MIN(config->max_renderable_lights, config->max_uniform_buffer_size / (int)sizeof(RasterizerSceneGLES3::LightData)); + config->max_lights_per_object = MIN(config->max_lights_per_object, config->max_renderable_lights); + + 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); + 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); + 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); + + uint32_t directional_light_buffer_size = MAX_DIRECTIONAL_LIGHTS * sizeof(DirectionalLightData); + scene_state.directional_lights = memnew_arr(DirectionalLightData, MAX_DIRECTIONAL_LIGHTS); + glGenBuffers(1, &scene_state.directional_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, scene_state.directional_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, directional_light_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + { + sky_globals.max_directional_lights = 4; + uint32_t directional_light_buffer_size = sky_globals.max_directional_lights * sizeof(DirectionalLightData); + sky_globals.directional_lights = memnew_arr(DirectionalLightData, sky_globals.max_directional_lights); + sky_globals.last_frame_directional_lights = memnew_arr(DirectionalLightData, sky_globals.max_directional_lights); + sky_globals.last_frame_directional_light_count = sky_globals.max_directional_lights + 1; + glGenBuffers(1, &sky_globals.directional_light_buffer); + glBindBuffer(GL_UNIFORM_BUFFER, sky_globals.directional_light_buffer); + glBufferData(GL_UNIFORM_BUFFER, directional_light_buffer_size, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + { + String global_defines; + 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"; + material_storage->shaders.scene_shader.initialize(global_defines); + scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create(); + material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR); + } + + { + //default material and shader + 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). + +shader_type spatial; + +void vertex() { + ROUGHNESS = 0.8; +} + +void fragment() { + ALBEDO = vec3(0.6); + ROUGHNESS = 0.8; + METALLIC = 0.2; +} +)"); + scene_globals.default_material = material_storage->material_allocate(); + material_storage->material_initialize(scene_globals.default_material); + material_storage->material_set_shader(scene_globals.default_material, scene_globals.default_shader); + } + + { + // Initialize Sky stuff + sky_globals.roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers"); + sky_globals.ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples"); + + String global_defines; + 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); + } + + { + 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); + } + + { + sky_globals.default_shader = material_storage->shader_allocate(); + + material_storage->shader_initialize(sky_globals.default_shader); + + material_storage->shader_set_code(sky_globals.default_shader, R"( +// Default sky shader. + +shader_type sky; + +void sky() { + COLOR = vec3(0.0); +} +)"); + sky_globals.default_material = material_storage->material_allocate(); + material_storage->material_initialize(sky_globals.default_material); + + material_storage->material_set_shader(sky_globals.default_material, sky_globals.default_shader); + } + { + sky_globals.fog_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(sky_globals.fog_shader); + + material_storage->shader_set_code(sky_globals.fog_shader, R"( +// Default clear color sky shader. + +shader_type sky; + +uniform vec4 clear_color; + +void sky() { + COLOR = clear_color.rgb; +} +)"); + sky_globals.fog_material = material_storage->material_allocate(); + material_storage->material_initialize(sky_globals.fog_material); + + material_storage->material_set_shader(sky_globals.fog_material, sky_globals.fog_shader); + } + + { + glGenBuffers(1, &sky_globals.screen_triangle); + glBindBuffer(GL_ARRAY_BUFFER, sky_globals.screen_triangle); + + const float qv[6] = { + -1.0f, + -1.0f, + 3.0f, + -1.0f, + -1.0f, + 3.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &sky_globals.screen_triangle_array); + glBindVertexArray(sky_globals.screen_triangle_array); + glBindBuffer(GL_ARRAY_BUFFER, sky_globals.screen_triangle); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } + +#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() { + glDeleteBuffers(1, &scene_state.directional_light_buffer); + glDeleteBuffers(1, &scene_state.omni_light_buffer); + glDeleteBuffers(1, &scene_state.spot_light_buffer); + memdelete_arr(scene_state.directional_lights); + memdelete_arr(scene_state.omni_lights); + memdelete_arr(scene_state.spot_lights); + memdelete_arr(scene_state.omni_light_sort); + memdelete_arr(scene_state.spot_light_sort); + + // 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); + 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); + 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 12bb21a5a0..a54d87a3a3 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -28,52 +28,610 @@ /* 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 "scene/resources/mesh.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/renderer_scene_render.h" #include "servers/rendering_server.h" +#include "shader_gles3.h" +#include "shaders/cubemap_filter.glsl.gen.h" +#include "shaders/sky.glsl.gen.h" +#include "storage/material_storage.h" +#include "storage/utilities.h" + +enum RenderListType { + RENDER_LIST_OPAQUE, //used for opaque objects + RENDER_LIST_ALPHA, //used for transparent objects + RENDER_LIST_SECONDARY, //used for shadows and other objects + RENDER_LIST_MAX +}; + +enum PassMode { + PASS_MODE_COLOR, + PASS_MODE_COLOR_TRANSPARENT, + PASS_MODE_COLOR_ADDITIVE, + PASS_MODE_SHADOW, + PASS_MODE_DEPTH, +}; + +// These should share as much as possible with SkyUniform Location +enum SceneUniformLocation { + SCENE_TONEMAP_UNIFORM_LOCATION, + SCENE_GLOBALS_UNIFORM_LOCATION, + SCENE_DATA_UNIFORM_LOCATION, + SCENE_MATERIAL_UNIFORM_LOCATION, + SCENE_EMPTY, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION. + SCENE_OMNILIGHT_UNIFORM_LOCATION, + SCENE_SPOTLIGHT_UNIFORM_LOCATION, + SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, +}; + +enum SkyUniformLocation { + SKY_TONEMAP_UNIFORM_LOCATION, + SKY_GLOBALS_UNIFORM_LOCATION, + SKY_EMPTY, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION. + SKY_MATERIAL_UNIFORM_LOCATION, + SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, +}; + +enum { + SPEC_CONSTANT_DISABLE_LIGHTMAP = 0, + SPEC_CONSTANT_DISABLE_DIRECTIONAL_LIGHTS = 1, + SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, + SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, + SPEC_CONSTANT_DISABLE_FOG = 4, +}; + +struct RenderDataGLES3 { + RID render_buffers = RID(); + bool transparent_bg = false; + + Transform3D cam_transform = Transform3D(); + Transform3D inv_cam_transform = Transform3D(); + Projection cam_projection = Projection(); + bool cam_orthogonal = false; + + // For stereo rendering + uint32_t view_count = 1; + 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<RenderGeometryInstance *> *instances = nullptr; + const PagedArray<RID> *lights = nullptr; + const PagedArray<RID> *reflection_probes = nullptr; + RID environment = RID(); + RID camera_effects = 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; +}; + +class RasterizerCanvasGLES3; class RasterizerSceneGLES3 : public RendererSceneRender { +private: + static RasterizerSceneGLES3 *singleton; + RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; + uint64_t scene_pass = 0; + + template <class T> + struct InstanceSort { + float depth; + T *instance = nullptr; + bool operator<(const InstanceSort &p_sort) const { + return depth < p_sort.depth; + } + }; + + struct SceneGlobals { + RID shader_default_version; + RID default_material; + RID default_shader; + RID cubemap_filter_shader_version; + } scene_globals; + + /* LIGHT INSTANCE */ + + struct LightData { + float position[3]; + float inv_radius; + + float direction[3]; // Only used by SpotLight + float size; + + float color[3]; + float attenuation; + + float inv_spot_attenuation; + float cos_spot_angle; + float specular_amount; + float shadow_opacity; + }; + static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes"); + + struct DirectionalLightData { + float direction[3]; + float energy; + + float color[3]; + float size; + + uint32_t enabled; // For use by SkyShaders + float pad[2]; + float specular; + }; + 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; + + class GeometryInstanceGLES3; + + // Cached data for drawing surfaces + struct GeometryInstanceSurface { + enum { + FLAG_PASS_DEPTH = 1, + FLAG_PASS_OPAQUE = 2, + FLAG_PASS_ALPHA = 4, + FLAG_PASS_SHADOW = 8, + FLAG_USES_SHARED_SHADOW_MATERIAL = 128, + FLAG_USES_SCREEN_TEXTURE = 2048, + FLAG_USES_DEPTH_TEXTURE = 4096, + FLAG_USES_NORMAL_TEXTURE = 8192, + FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384, + }; + + union { + struct { + uint64_t lod_index : 8; + uint64_t surface_index : 8; + uint64_t geometry_id : 32; + uint64_t material_id_low : 16; + + uint64_t material_id_hi : 16; + uint64_t shader_id : 32; + uint64_t uses_softshadow : 1; + uint64_t uses_projector : 1; + uint64_t uses_forward_gi : 1; + uint64_t uses_lightmap : 1; + uint64_t depth_layer : 4; + uint64_t priority : 8; + }; + struct { + uint64_t sort_key1; + uint64_t sort_key2; + }; + } sort; + + RS::PrimitiveType primitive = RS::PRIMITIVE_MAX; + uint32_t flags = 0; + uint32_t surface_index = 0; + uint32_t lod_index = 0; + + void *surface = nullptr; + GLES3::SceneShaderData *shader = nullptr; + GLES3::SceneMaterialData *material = nullptr; + + void *surface_shadow = nullptr; + GLES3::SceneShaderData *shader_shadow = nullptr; + GLES3::SceneMaterialData *material_shadow = nullptr; + + GeometryInstanceSurface *next = nullptr; + GeometryInstanceGLES3 *owner = nullptr; + }; + + class GeometryInstanceGLES3 : public RenderGeometryInstanceBase { + public: + //used during rendering + bool store_transform_cache = true; + + int32_t instance_count = 0; + + bool can_sdfgi = false; + bool using_projectors = false; + bool using_softshadows = false; + + uint32_t omni_light_count = 0; + LocalVector<RID> omni_lights; + uint32_t spot_light_count = 0; + LocalVector<RID> spot_lights; + LocalVector<uint32_t> omni_light_gl_cache; + LocalVector<uint32_t> spot_light_gl_cache; + + //used during setup + GeometryInstanceSurface *surface_caches = nullptr; + SelfList<GeometryInstanceGLES3> dirty_list_element; + + GeometryInstanceGLES3() : + dirty_list_element(this) {} + + 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; + + 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 {} + + 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_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(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; + + // Use PagedAllocator instead of RID to maximize performance + PagedAllocator<GeometryInstanceGLES3> geometry_instance_alloc; + PagedAllocator<GeometryInstanceSurface> geometry_instance_surface_alloc; + + 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_update(RenderGeometryInstance *p_geometry_instance); + void _update_dirty_geometry_instances(); + + struct SceneState { + struct UBO { + float projection_matrix[16]; + float inv_projection_matrix[16]; + float inv_view_matrix[16]; + float view_matrix[16]; + + float viewport_size[2]; + float screen_pixel_size[2]; + + float ambient_light_color_energy[4]; + + float ambient_color_sky_mix; + uint32_t material_uv2_mode; + float pad2; + uint32_t use_ambient_light = 0; + + uint32_t use_ambient_cubemap = 0; + uint32_t use_reflection_cubemap = 0; + float fog_aerial_perspective; + float time; + + float radiance_inverse_xform[12]; + + uint32_t directional_light_count; + float z_far; + float z_near; + float pad1; + + uint32_t fog_enabled; + float fog_density; + float fog_height; + float fog_height_density; + + float fog_light_color[3]; + float fog_sun_scatter; + }; + static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + + struct TonemapUBO { + float exposure = 1.0; + float white = 1.0; + int32_t tonemapper = 0; + int32_t pad = 0; + }; + static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes"); + + UBO ubo; + GLuint ubo_buffer = 0; + GLuint tonemap_buffer = 0; + + bool used_depth_prepass = false; + + GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; + GLES3::SceneShaderData::DepthDraw current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; + GLES3::SceneShaderData::DepthTest current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; + GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK; + + bool texscreen_copied = false; + bool used_screen_texture = false; + bool used_normal_texture = false; + bool used_depth_texture = false; + + LightData *omni_lights = nullptr; + LightData *spot_lights = nullptr; + + InstanceSort<LightInstance> *omni_light_sort; + InstanceSort<LightInstance> *spot_light_sort; + GLuint omni_light_buffer = 0; + GLuint spot_light_buffer = 0; + uint32_t omni_light_count = 0; + uint32_t spot_light_count = 0; + + DirectionalLightData *directional_lights = nullptr; + GLuint directional_light_buffer = 0; + } scene_state; + + struct RenderListParameters { + GeometryInstanceSurface **elements = nullptr; + int element_count = 0; + bool reverse_cull = false; + uint32_t spec_constant_base_flags = 0; + bool force_wireframe = false; + + RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint32_t p_spec_constant_base_flags, bool p_force_wireframe = false) { + elements = p_elements; + element_count = p_element_count; + reverse_cull = p_reverse_cull; + spec_constant_base_flags = p_spec_constant_base_flags; + force_wireframe = p_force_wireframe; + } + }; + + struct RenderList { + LocalVector<GeometryInstanceSurface *> elements; + + void clear() { + elements.clear(); + } + + //should eventually be replaced by radix + + struct SortByKey { + _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const { + return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2); + } + }; + + void sort_by_key() { + SortArray<GeometryInstanceSurface *, SortByKey> sorter; + sorter.sort(elements.ptr(), elements.size()); + } + + void sort_by_key_range(uint32_t p_from, uint32_t p_size) { + SortArray<GeometryInstanceSurface *, SortByKey> sorter; + sorter.sort(elements.ptr() + p_from, p_size); + } + + struct SortByDepth { + _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const { + return (A->owner->depth < B->owner->depth); + } + }; + + void sort_by_depth() { //used for shadows + + SortArray<GeometryInstanceSurface *, SortByDepth> sorter; + sorter.sort(elements.ptr(), elements.size()); + } + + struct SortByReverseDepthAndPriority { + _FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const { + return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority); + } + }; + + void sort_by_reverse_depth_and_priority() { //used for alpha + + SortArray<GeometryInstanceSurface *, SortByReverseDepthAndPriority> sorter; + sorter.sort(elements.ptr(), elements.size()); + } + + _FORCE_INLINE_ void add_element(GeometryInstanceSurface *p_element) { + elements.push_back(p_element); + } + }; + + RenderList render_list[RENDER_LIST_MAX]; + + void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count); + void _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); + void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false); + + template <PassMode p_pass_mode> + _FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false); + +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 _free_render_buffer_data(RenderBuffers *rb); + void _allocate_blur_textures(RenderBuffers *rb); + void _allocate_depth_backbuffer_textures(RenderBuffers *rb); + + void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); + + /* Environment */ + + RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM; + bool ssao_half_size = false; + float ssao_adaptive_target = 0.5; + int ssao_blur_passes = 2; + float ssao_fadeout_from = 50.0; + float ssao_fadeout_to = 300.0; + + bool glow_bicubic_upscale = false; + bool glow_high_quality = false; + RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; + + /* Sky */ + + struct SkyGlobals { + float fog_aerial_perspective = 0.0; + Color fog_light_color; + float fog_sun_scatter = 0.0; + bool fog_enabled = false; + float fog_density = 0.0; + float z_far = 0.0; + uint32_t directional_light_count = 0; + + DirectionalLightData *directional_lights = nullptr; + DirectionalLightData *last_frame_directional_lights = nullptr; + uint32_t last_frame_directional_light_count = 0; + GLuint directional_light_buffer = 0; + + RID shader_default_version; + RID default_material; + RID default_shader; + RID fog_material; + RID fog_shader; + GLuint screen_triangle = 0; + GLuint screen_triangle_array = 0; + GLuint radical_inverse_vdc_cache_tex = 0; + uint32_t max_directional_lights = 4; + uint32_t roughness_layers = 8; + uint32_t ggx_samples = 128; + } sky_globals; + + struct Sky { + // Screen Buffers + GLuint half_res_pass = 0; + GLuint half_res_framebuffer = 0; + GLuint quarter_res_pass = 0; + GLuint quarter_res_framebuffer = 0; + Size2i screen_size = Size2i(0, 0); + + // Radiance Cubemap + GLuint radiance = 0; + GLuint radiance_framebuffer = 0; + GLuint raw_radiance = 0; + + RID material; + GLuint uniform_buffer; + + int radiance_size = 256; + int mipmap_count = 1; + + RS::SkyMode mode = RS::SKY_MODE_AUTOMATIC; + + //ReflectionData reflection; + bool reflection_dirty = false; + bool dirty = false; + int processing_layer = 0; + Sky *dirty_list = nullptr; + + //State to track when radiance cubemap needs updating + GLES3::SkyMaterialData *prev_material; + Vector3 prev_position = Vector3(0.0, 0.0, 0.0); + float prev_time = 0.0f; + }; + + Sky *dirty_sky_list = nullptr; + mutable RID_Owner<Sky, true> sky_owner; + + void _setup_sky(RID p_env, RID p_render_buffers, 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(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _filter_sky_radiance(Sky *p_sky, int p_base_layer); + void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _free_sky_data(Sky *p_sky); + public: - struct State { - //SceneShaderGLES3 scene_shader; - } state; - - 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; + RasterizerCanvasGLES3 *canvas; + + RenderGeometryInstance *geometry_instance_create(RID p_base) override; + void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override; - void geometry_instance_free(GeometryInstance *p_geometry_instance) override; + uint32_t geometry_instance_get_pair_mask() override; /* SHADOW ATLAS API */ @@ -88,65 +646,46 @@ public: /* 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; - AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const override; - uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const override; + 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 { + return 0; + } + AABB sdfgi_get_pending_region_bounds(RID 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 { + return 0; + } /* SKY API */ RID sky_allocate() override; void sky_initialize(RID p_rid) override; void sky_set_radiance_size(RID p_sky, int p_radiance_size) override; - void sky_set_mode(RID p_sky, RS::SkyMode p_samples) override; + 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; /* 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; @@ -155,15 +694,24 @@ public: 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; + 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 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_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; 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; + } + 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; @@ -191,20 +739,30 @@ public: 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 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_ortogonal, 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(RID 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_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 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; + } + + _FORCE_INLINE_ uint64_t get_scene_pass() { + return scene_pass; + } - void set_scene_pass(uint64_t p_pass) override; void set_time(double p_time, double p_step) override; void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override; + _FORCE_INLINE_ RS::ViewportDebugDraw get_debug_draw_mode() const { + 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_debanding, uint32_t p_view_count) 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_texture_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; 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; @@ -223,8 +781,9 @@ public: void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; 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 fda208b812..0000000000 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ /dev/null @@ -1,4515 +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" - -GLuint RasterizerStorageGLES3::system_fbo = 0; - -/* TEXTURE API */ - -#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 -#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 -#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 - -#define _EXT_COMPRESSED_RED_RGTC1_EXT 0x8DBB -#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB -#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC -#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD -#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE -#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC -#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD -#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE -#define _EXT_ETC1_RGB8_OES 0x8D64 - -#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C -#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D -#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E -#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F - -#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 - -void RasterizerStorageGLES3::bind_quad_array() const { - //glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); - //glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); - //glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8)); - - //glEnableVertexAttribArray(RS::ARRAY_VERTEX); - //glEnableVertexAttribArray(RS::ARRAY_TEX_UV); -} - -bool RasterizerStorageGLES3::can_create_resources_async() const { - return false; -} - -Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, 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 { - r_gl_format = 0; - Ref<Image> image = p_image; - r_compressed = false; - r_real_format = p_format; - - bool need_decompress = false; - - switch (p_format) { - case Image::FORMAT_L8: { -#ifdef GLES_OVER_GL - r_gl_internal_format = GL_R8; - r_gl_format = GL_RED; - r_gl_type = GL_UNSIGNED_BYTE; -#else - r_gl_internal_format = GL_LUMINANCE; - r_gl_format = GL_LUMINANCE; - r_gl_type = GL_UNSIGNED_BYTE; -#endif - } break; - case Image::FORMAT_LA8: { -#ifdef GLES_OVER_GL - r_gl_internal_format = GL_RG8; - r_gl_format = GL_RG; - r_gl_type = GL_UNSIGNED_BYTE; -#else - r_gl_internal_format = GL_LUMINANCE_ALPHA; - r_gl_format = GL_LUMINANCE_ALPHA; - r_gl_type = GL_UNSIGNED_BYTE; -#endif - } break; - case Image::FORMAT_R8: { - r_gl_internal_format = GL_R8; - r_gl_format = GL_RED; - r_gl_type = GL_UNSIGNED_BYTE; - - } break; - case Image::FORMAT_RG8: { - r_gl_internal_format = GL_RG8; - r_gl_format = GL_RG; - r_gl_type = GL_UNSIGNED_BYTE; - - } break; - case Image::FORMAT_RGB8: { - r_gl_internal_format = GL_RGB8; - r_gl_format = GL_RGB; - r_gl_type = GL_UNSIGNED_BYTE; - //r_srgb = true; - - } break; - case Image::FORMAT_RGBA8: { - r_gl_format = GL_RGBA; - r_gl_internal_format = GL_RGBA8; - r_gl_type = GL_UNSIGNED_BYTE; - //r_srgb = true; - - } break; - case Image::FORMAT_RGBA4444: { - r_gl_internal_format = GL_RGBA4; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_SHORT_4_4_4_4; - - } break; - //case Image::FORMAT_RGBA5551: { - // r_gl_internal_format = GL_RGB5_A1; - // r_gl_format = GL_RGBA; - // r_gl_type = GL_UNSIGNED_SHORT_5_5_5_1; - // - //} break; - case Image::FORMAT_RF: { - r_gl_internal_format = GL_R32F; - r_gl_format = GL_RED; - r_gl_type = GL_FLOAT; - - } break; - case Image::FORMAT_RGF: { - r_gl_internal_format = GL_RG32F; - r_gl_format = GL_RG; - r_gl_type = GL_FLOAT; - - } break; - case Image::FORMAT_RGBF: { - r_gl_internal_format = GL_RGB32F; - r_gl_format = GL_RGB; - r_gl_type = GL_FLOAT; - - } break; - case Image::FORMAT_RGBAF: { - r_gl_internal_format = GL_RGBA32F; - r_gl_format = GL_RGBA; - r_gl_type = GL_FLOAT; - - } break; - case Image::FORMAT_RH: { - r_gl_internal_format = GL_R16F; - r_gl_format = GL_RED; - r_gl_type = GL_HALF_FLOAT; - } break; - case Image::FORMAT_RGH: { - r_gl_internal_format = GL_RG16F; - r_gl_format = GL_RG; - r_gl_type = GL_HALF_FLOAT; - - } break; - case Image::FORMAT_RGBH: { - r_gl_internal_format = GL_RGB16F; - r_gl_format = GL_RGB; - r_gl_type = GL_HALF_FLOAT; - - } break; - case Image::FORMAT_RGBAH: { - r_gl_internal_format = GL_RGBA16F; - r_gl_format = GL_RGBA; - r_gl_type = GL_HALF_FLOAT; - - } break; - case Image::FORMAT_RGBE9995: { - r_gl_internal_format = GL_RGB9_E5; - r_gl_format = GL_RGB; - r_gl_type = GL_UNSIGNED_INT_5_9_9_9_REV; - - } break; - case Image::FORMAT_DXT1: { - if (config.s3tc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_DXT3: { - if (config.s3tc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_DXT5: { - if (config.s3tc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_RGTC_R: { - if (config.rgtc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RED_RGTC1_EXT; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_RGTC_RG: { - if (config.rgtc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_BPTC_RGBA: { - if (config.bptc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGBA_BPTC_UNORM; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_BPTC_RGBF: { - if (config.bptc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; - r_gl_format = GL_RGB; - r_gl_type = GL_FLOAT; - r_compressed = true; - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_BPTC_RGBFU: { - if (config.bptc_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; - r_gl_format = GL_RGB; - r_gl_type = GL_FLOAT; - r_compressed = true; - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC: { - if (config.etc_supported) { - r_gl_internal_format = _EXT_ETC1_RGB8_OES; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - - } break; - /* - case Image::FORMAT_ETC2_R11: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_R11_EAC; - r_gl_format = GL_RED; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_R11S: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_SIGNED_R11_EAC; - r_gl_format = GL_RED; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_RG11: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RG11_EAC; - r_gl_format = GL_RG; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_RG11S: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_SIGNED_RG11_EAC; - r_gl_format = GL_RG; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_RGB8: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGB8_ETC2; - r_gl_format = GL_RGB; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_RGBA8: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGBA8_ETC2_EAC; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - case Image::FORMAT_ETC2_RGB8A1: { - if (config.etc2_supported) { - r_gl_internal_format = _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; - r_gl_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_compressed = true; - //r_srgb = true; - - } else { - need_decompress = true; - } - } break; - */ - default: { - ERR_FAIL_V(Ref<Image>()); - } - } - - if (need_decompress || p_force_decompress) { - if (!image.is_null()) { - image = image->duplicate(); - image->decompress(); - ERR_FAIL_COND_V(image->is_compressed(), image); - switch (image->get_format()) { - case Image::FORMAT_RGB8: { - r_gl_format = GL_RGB; - r_gl_internal_format = GL_RGB; - r_gl_type = GL_UNSIGNED_BYTE; - r_real_format = Image::FORMAT_RGB8; - r_compressed = false; - } break; - case Image::FORMAT_RGBA8: { - r_gl_format = GL_RGBA; - r_gl_internal_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_real_format = Image::FORMAT_RGBA8; - r_compressed = false; - } break; - default: { - image->convert(Image::FORMAT_RGBA8); - r_gl_format = GL_RGBA; - r_gl_internal_format = GL_RGBA; - r_gl_type = GL_UNSIGNED_BYTE; - r_real_format = Image::FORMAT_RGBA8; - r_compressed = false; - - } break; - } - } - - return image; - } - - return p_image; -} - -static const GLenum _cube_side_enum[6] = { - GL_TEXTURE_CUBE_MAP_NEGATIVE_X, - GL_TEXTURE_CUBE_MAP_POSITIVE_X, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, -}; - -RID RasterizerStorageGLES3::texture_allocate() { - RID id = texture_create(); - ERR_FAIL_COND_V(id == RID(), id); - return id; -} - -void RasterizerStorageGLES3::texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) { - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!tex); - - int w = p_image->get_width(); - int h = p_image->get_height(); - - _texture_allocate_internal(p_texture, w, h, 1, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D, 0); - texture_set_data(p_texture, p_image); -} - -void RasterizerStorageGLES3::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { -} - -void RasterizerStorageGLES3::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) { -} - -void RasterizerStorageGLES3::texture_proxy_initialize(RID p_texture, RID p_base) { - texture_set_proxy(p_texture, p_base); -} - -//RID RasterizerStorageGLES3::texture_2d_create(const Ref<Image> &p_image) { -// RID id = texture_create(); -// ERR_FAIL_COND_V(id == RID(), id); - -// int w = p_image->get_width(); -// int h = p_image->get_height(); - -// texture_allocate(id, w, h, 1, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D, 0); - -// texture_set_data(id, p_image); - -// return id; -//} - -//RID RasterizerStorageGLES3::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { -// return RID(); -//} - -//void RasterizerStorageGLES3::texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer) { -// // only 1 layer so far -// texture_set_data(p_texture, p_image); -//} -void RasterizerStorageGLES3::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); -} - -void RasterizerStorageGLES3::texture_2d_placeholder_initialize(RID p_texture) { -} - -void RasterizerStorageGLES3::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { -} - -void RasterizerStorageGLES3::texture_3d_placeholder_initialize(RID p_texture) { -} - -Ref<Image> RasterizerStorageGLES3::texture_2d_get(RID p_texture) const { - Texture *tex = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND_V(!tex, Ref<Image>()); - - /* -#ifdef TOOLS_ENABLED - if (tex->image_cache_2d.is_valid()) { - return tex->image_cache_2d; - } -#endif - Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); - ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instance(); - image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); - ERR_FAIL_COND_V(image->empty(), Ref<Image>()); - if (tex->format != tex->validated_format) { - image->convert(tex->format); - } - -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - tex->image_cache_2d = image; - } -#endif -*/ - ERR_FAIL_COND_V(!tex->images.size(), Ref<Image>()); - - return tex->images[0]; - - // return image; - - // return Ref<Image>(); -} - -void RasterizerStorageGLES3::texture_replace(RID p_texture, RID p_by_texture) { - Texture *tex_to = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!tex_to); - Texture *tex_from = texture_owner.get_or_null(p_by_texture); - ERR_FAIL_COND(!tex_from); - - tex_to->destroy(); - tex_to->copy_from(*tex_from); - - // copy image data and upload to GL - tex_to->images.resize(tex_from->images.size()); - - for (int n = 0; n < tex_from->images.size(); n++) { - texture_set_data(p_texture, tex_from->images[n], n); - } - - free(p_by_texture); -} - -bool RasterizerStorageGLES3::_is_main_thread() { - //#if defined DEBUG_ENABLED && defined TOOLS_ENABLED - // must be called from main thread in OpenGL - bool is_main_thread = _main_thread_id == Thread::get_caller_id(); - //#endif - return is_main_thread; -} - -RID RasterizerStorageGLES3::texture_create() { - ERR_FAIL_COND_V(!_is_main_thread(), RID()); - - Texture *texture = memnew(Texture); - ERR_FAIL_COND_V(!texture, RID()); - glGenTextures(1, &texture->tex_id); - texture->active = false; - texture->total_data_size = 0; - - return texture_owner.make_rid(texture); -} - -void RasterizerStorageGLES3::_texture_allocate_internal(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, RenderingDevice::TextureType p_type, uint32_t p_flags) { - // GLenum format; - // GLenum internal_format; - // GLenum type; - - // bool compressed = false; - - if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { - p_flags &= ~TEXTURE_FLAG_MIPMAPS; // no mipies for video - } - - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - texture->width = p_width; - texture->height = p_height; - texture->format = p_format; - texture->flags = p_flags; - texture->stored_cube_sides = 0; - texture->type = p_type; - - switch (p_type) { - case RenderingDevice::TEXTURE_TYPE_2D: { - texture->target = GL_TEXTURE_2D; - texture->images.resize(1); - } break; - // case RenderingDevice::TEXTURE_TYPE_EXTERNAL: { - //#ifdef ANDROID_ENABLED - // texture->target = _GL_TEXTURE_EXTERNAL_OES; - //#else - // texture->target = GL_TEXTURE_2D; - //#endif - // texture->images.resize(0); - // } break; - case RenderingDevice::TEXTURE_TYPE_CUBE: { - texture->target = GL_TEXTURE_CUBE_MAP; - texture->images.resize(6); - } break; - case RenderingDevice::TEXTURE_TYPE_2D_ARRAY: - case RenderingDevice::TEXTURE_TYPE_3D: { - texture->target = GL_TEXTURE_3D; - ERR_PRINT("3D textures and Texture Arrays are not supported in OpenGL. Please switch to the Vulkan backend."); - return; - } break; - default: { - ERR_PRINT("Unknown texture type!"); - return; - } - } - -#if 0 - // if (p_type != RS::TEXTURE_TYPE_EXTERNAL) { - if (p_type == RenderingDevice::TEXTURE_TYPE_2D) { - texture->alloc_width = texture->width; - texture->alloc_height = texture->height; - texture->resize_to_po2 = false; - if (!config.support_npot_repeat_mipmap) { - int po2_width = next_power_of_2(p_width); - int po2_height = next_power_of_2(p_height); - - bool is_po2 = p_width == po2_width && p_height == po2_height; - - if (!is_po2 && (p_flags & TEXTURE_FLAG_REPEAT || p_flags & TEXTURE_FLAG_MIPMAPS)) { - if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { - //not supported - ERR_PRINT("Streaming texture for non power of 2 or has mipmaps on this hardware: " + texture->path + "'. Mipmaps and repeat disabled."); - texture->flags &= ~(TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS); - } else { - texture->alloc_height = po2_height; - texture->alloc_width = po2_width; - texture->resize_to_po2 = true; - } - } - } - - GLenum format; - GLenum internal_format; - GLenum type; - bool compressed = false; - - Image::Format real_format; - _get_gl_image_and_format(Ref<Image>(), - texture->format, - texture->flags, - real_format, - format, - internal_format, - type, - compressed, - texture->resize_to_po2); - - texture->gl_format_cache = format; - texture->gl_type_cache = type; - texture->gl_internal_format_cache = internal_format; - texture->data_size = 0; - texture->mipmaps = 1; - - texture->compressed = compressed; - } -#endif - - glActiveTexture(GL_TEXTURE0); - glBindTexture(texture->target, texture->tex_id); - - // if (p_type == RS::TEXTURE_TYPE_EXTERNAL) { - // glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - // glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - // glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // } else if (p_flags & TEXTURE_FLAG_USED_FOR_STREAMING) { - // //prealloc if video - // glTexImage2D(texture->target, 0, internal_format, texture->alloc_width, texture->alloc_height, 0, format, type, NULL); - // } - - texture->active = true; -} - -void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND(!_is_main_thread()); - - ERR_FAIL_COND(!texture); - if (texture->target == GL_TEXTURE_3D) { - // Target is set to a 3D texture or array texture, exit early to avoid spamming errors - return; - } - ERR_FAIL_COND(!texture->active); - ERR_FAIL_COND(texture->render_target); - ERR_FAIL_COND(p_image.is_null()); - ERR_FAIL_COND(texture->format != p_image->get_format()); - - ERR_FAIL_COND(!p_image->get_width()); - ERR_FAIL_COND(!p_image->get_height()); - - // ERR_FAIL_COND(texture->type == RS::TEXTURE_TYPE_EXTERNAL); - - GLenum type; - GLenum format; - GLenum internal_format; - bool compressed = false; - - if (config.keep_original_textures && !(texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->images.write[p_layer] = p_image; - } - - // print_line("texture_set_data width " + itos (p_image->get_width()) + " height " + itos(p_image->get_height())); - - Image::Format real_format; - Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), texture->flags, real_format, format, internal_format, type, compressed, texture->resize_to_po2); - - if (texture->resize_to_po2) { - if (p_image->is_compressed()) { - ERR_PRINT("Texture '" + texture->path + "' is required to be a power of 2 because it uses either mipmaps or repeat, so it was decompressed. This will hurt performance and memory usage."); - } - - if (img == p_image) { - img = img->duplicate(); - } - img->resize_to_po2(false); - } - - if (config.shrink_textures_x2 && (p_image->has_mipmaps() || !p_image->is_compressed()) && !(texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING)) { - texture->alloc_height = MAX(1, texture->alloc_height / 2); - texture->alloc_width = MAX(1, texture->alloc_width / 2); - - if (texture->alloc_width == img->get_width() / 2 && texture->alloc_height == img->get_height() / 2) { - img->shrink_x2(); - } else if (img->get_format() <= Image::FORMAT_RGBA8) { - img->resize(texture->alloc_width, texture->alloc_height, Image::INTERPOLATE_BILINEAR); - } - } - - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; - - texture->data_size = img->get_data().size(); - Vector<uint8_t> read = img->get_data(); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(texture->target, texture->tex_id); - - texture->ignore_mipmaps = compressed && !img->has_mipmaps(); - - // set filtering and repeat state - _texture_set_state_from_flags(texture); - - //set swizle for older format compatibility -#ifdef GLES_OVER_GL - switch (texture->format) { - case Image::FORMAT_L8: { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); - - } break; - case Image::FORMAT_LA8: { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); - } break; - default: { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); - - } break; - } -#endif - - int mipmaps = ((texture->flags & TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1; - - int w = img->get_width(); - int h = img->get_height(); - - int tsize = 0; - - for (int i = 0; i < mipmaps; i++) { - int size, ofs; - img->get_mipmap_offset_and_size(i, ofs, size); - - if (compressed) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - - int bw = w; - int bh = h; - - glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); - } else { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - if (texture->flags & TEXTURE_FLAG_USED_FOR_STREAMING) { - glTexSubImage2D(blit_target, i, 0, 0, w, h, format, type, &read[ofs]); - } else { - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); - } - } - - tsize += size; - - w = MAX(1, w >> 1); - h = MAX(1, h >> 1); - } - - info.texture_mem -= texture->total_data_size; - texture->total_data_size = tsize; - info.texture_mem += texture->total_data_size; - - // printf("texture: %i x %i - size: %i - total: %i\n", texture->width, texture->height, tsize, info.texture_mem); - - texture->stored_cube_sides |= (1 << p_layer); - - if ((texture->flags & TEXTURE_FLAG_MIPMAPS) && mipmaps == 1 && !texture->ignore_mipmaps && (texture->type != RenderingDevice::TEXTURE_TYPE_CUBE || texture->stored_cube_sides == (1 << 6) - 1)) { - //generate mipmaps if they were requested and the image does not contain them - glGenerateMipmap(texture->target); - } - - texture->mipmaps = mipmaps; -} - -void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { - // TODO - ERR_PRINT("Not implemented (ask Karroffel to do it :p)"); -} - -/* -Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, Ref<Image>()); - ERR_FAIL_COND_V(!texture->active, Ref<Image>()); - ERR_FAIL_COND_V(texture->data_size == 0 && !texture->render_target, Ref<Image>()); - - if (texture->type == RS::TEXTURE_TYPE_CUBEMAP && p_layer < 6 && p_layer >= 0 && !texture->images[p_layer].is_null()) { - return texture->images[p_layer]; - } - -#ifdef GLES_OVER_GL - - Image::Format real_format; - GLenum gl_format; - GLenum gl_internal_format; - GLenum gl_type; - bool compressed; - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, false); - - PoolVector<uint8_t> data; - - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); - - data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers - PoolVector<uint8_t>::Write wb = data.write(); - - glActiveTexture(GL_TEXTURE0); - - glBindTexture(texture->target, texture->tex_id); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - - for (int i = 0; i < texture->mipmaps; i++) { - int ofs = Image::get_image_mipmap_offset(texture->alloc_width, texture->alloc_height, real_format, i); - - if (texture->compressed) { - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glGetCompressedTexImage(texture->target, i, &wb[ofs]); - } else { - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &wb[ofs]); - } - } - - wb.release(); - - data.resize(data_size); - - Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, texture->mipmaps > 1, real_format, data)); - - return Ref<Image>(img); -#else - - Image::Format real_format; - GLenum gl_format; - GLenum gl_internal_format; - GLenum gl_type; - bool compressed; - _get_gl_image_and_format(Ref<Image>(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, texture->resize_to_po2); - - PoolVector<uint8_t> data; - - 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 - PoolVector<uint8_t>::Write wb = data.write(); - - 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); - - shaders.copy.bind(); - - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - bind_quad_array(); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]); - - glDeleteTextures(1, &temp_color_texture); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &temp_framebuffer); - - wb.release(); - - data.resize(data_size); - - Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data)); - if (!texture->compressed) { - img->convert(real_format); - } - - return Ref<Image>(img); - -#endif -} -*/ - -void RasterizerStorageGLES3::_texture_set_state_from_flags(Texture *p_tex) { - if ((p_tex->flags & TEXTURE_FLAG_MIPMAPS) && !p_tex->ignore_mipmaps) { - if (p_tex->flags & TEXTURE_FLAG_FILTER) { - // these do not exactly correspond ... - p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS); - //texture->glTexParam_MinFilter(texture->target, config.use_fast_texture_filter ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR); - } else { - p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); - //texture->glTexParam_MinFilter(texture->target, config.use_fast_texture_filter ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR); - } - } else { - if (p_tex->flags & TEXTURE_FLAG_FILTER) { - p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR); - //texture->glTexParam_MinFilter(texture->target, GL_LINEAR); - } else { - p_tex->GLSetFilter(p_tex->target, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); - // texture->glTexParam_MinFilter(texture->target, GL_NEAREST); - } - } - - if (((p_tex->flags & TEXTURE_FLAG_REPEAT) || (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT)) && p_tex->target != GL_TEXTURE_CUBE_MAP) { - if (p_tex->flags & TEXTURE_FLAG_MIRRORED_REPEAT) { - p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); - } else { - p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); - } - } else { - p_tex->GLSetRepeat(p_tex->target, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - } -} - -void RasterizerStorageGLES3::texture_set_flags(RID p_texture, uint32_t p_flags) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - bool had_mipmaps = texture->flags & TEXTURE_FLAG_MIPMAPS; - - texture->flags = p_flags; - - glActiveTexture(GL_TEXTURE0); - glBindTexture(texture->target, texture->tex_id); - - // set filtering and repeat state - _texture_set_state_from_flags(texture); - - if ((texture->flags & TEXTURE_FLAG_MIPMAPS) && !texture->ignore_mipmaps) { - if (!had_mipmaps && texture->mipmaps == 1) { - glGenerateMipmap(texture->target); - } - } -} - -uint32_t RasterizerStorageGLES3::texture_get_flags(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, 0); - - return texture->flags; -} - -Image::Format RasterizerStorageGLES3::texture_get_format(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, Image::FORMAT_L8); - - return texture->format; -} - -RenderingDevice::TextureType RasterizerStorageGLES3::texture_get_type(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, RenderingDevice::TEXTURE_TYPE_2D); - - return texture->type; -} - -uint32_t RasterizerStorageGLES3::texture_get_texid(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, 0); - - return texture->tex_id; -} - -void RasterizerStorageGLES3::texture_bind(RID p_texture, uint32_t p_texture_no) { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND(!texture); - - glActiveTexture(GL_TEXTURE0 + p_texture_no); - glBindTexture(texture->target, texture->tex_id); -} - -uint32_t RasterizerStorageGLES3::texture_get_width(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, 0); - - return texture->width; -} - -uint32_t RasterizerStorageGLES3::texture_get_height(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, 0); - - return texture->height; -} - -uint32_t RasterizerStorageGLES3::texture_get_depth(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND_V(!texture, 0); - - return texture->depth; -} - -void RasterizerStorageGLES3::texture_set_size_override(RID p_texture, int p_width, int p_height) { - Texture *texture = texture_owner.get_or_null(p_texture); - - ERR_FAIL_COND(!texture); - ERR_FAIL_COND(texture->render_target); - - ERR_FAIL_COND(p_width <= 0 || p_width > 16384); - ERR_FAIL_COND(p_height <= 0 || p_height > 16384); - //real texture size is in alloc width and height - texture->width = p_width; - texture->height = p_height; -} - -void RasterizerStorageGLES3::texture_set_path(RID p_texture, const String &p_path) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - texture->path = p_path; -} - -String RasterizerStorageGLES3::texture_get_path(RID p_texture) const { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND_V(!texture, ""); - - return texture->path; -} - -void RasterizerStorageGLES3::texture_debug_usage(List<RS::TextureInfo> *r_info) { - List<RID> textures; - texture_owner.get_owned_list(&textures); - - for (List<RID>::Element *E = textures.front(); E; E = E->next()) { - Texture *t = texture_owner.get_or_null(E->get()); - if (!t) { - continue; - } - RS::TextureInfo tinfo; - tinfo.path = t->path; - tinfo.format = t->format; - tinfo.width = t->alloc_width; - tinfo.height = t->alloc_height; - tinfo.depth = 0; - tinfo.bytes = t->total_data_size; - r_info->push_back(tinfo); - } -} - -void RasterizerStorageGLES3::texture_set_shrink_all_x2_on_set_data(bool p_enable) { - config.shrink_textures_x2 = p_enable; -} - -void RasterizerStorageGLES3::textures_keep_original(bool p_enable) { - config.keep_original_textures = p_enable; -} - -Size2 RasterizerStorageGLES3::texture_size_with_proxy(RID p_texture) { - const Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND_V(!texture, Size2()); - if (texture->proxy) { - return Size2(texture->proxy->width, texture->proxy->height); - } else { - return Size2(texture->width, texture->height); - } -} - -// example use in 3.2 -// VS::get_singleton()->texture_set_proxy(default_texture->proxy, texture_rid); - -// p_proxy is the source (pre-existing) texture? -// and p_texture is the one that is being made into a proxy? -//This naming is confusing. Comments!!! - -// The naming of the parameters seemed to be reversed? -// The p_proxy is the source texture -// and p_texture is actually the proxy???? - -void RasterizerStorageGLES3::texture_set_proxy(RID p_texture, RID p_proxy) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - if (texture->proxy) { - texture->proxy->proxy_owners.erase(texture); - texture->proxy = nullptr; - } - - if (p_proxy.is_valid()) { - Texture *proxy = texture_owner.get_or_null(p_proxy); - ERR_FAIL_COND(!proxy); - ERR_FAIL_COND(proxy == texture); - proxy->proxy_owners.insert(texture); - texture->proxy = proxy; - } -} - -void RasterizerStorageGLES3::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - texture->redraw_if_visible = p_enable; -} - -void RasterizerStorageGLES3::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - texture->detect_3d = p_callback; - texture->detect_3d_ud = p_userdata; -} - -void RasterizerStorageGLES3::texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - texture->detect_srgb = p_callback; - texture->detect_srgb_ud = p_userdata; -} - -void RasterizerStorageGLES3::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { - Texture *texture = texture_owner.get_or_null(p_texture); - ERR_FAIL_COND(!texture); - - texture->detect_normal = p_callback; - texture->detect_normal_ud = p_userdata; -} - -RID RasterizerStorageGLES3::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { - return RID(); -} - -RID RasterizerStorageGLES3::canvas_texture_allocate() { - return canvas_texture_owner.allocate_rid(); -} - -void RasterizerStorageGLES3::canvas_texture_initialize(RID p_rid) { - canvas_texture_owner.initialize_rid(p_rid); -} - -void RasterizerStorageGLES3::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { - CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); - switch (p_channel) { - case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { - ct->diffuse = p_texture; - } break; - case RS::CANVAS_TEXTURE_CHANNEL_NORMAL: { - ct->normal_map = p_texture; - } break; - case RS::CANVAS_TEXTURE_CHANNEL_SPECULAR: { - ct->specular = p_texture; - } break; - } -} - -void RasterizerStorageGLES3::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { - CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); - ct->specular_color.r = p_specular_color.r; - ct->specular_color.g = p_specular_color.g; - ct->specular_color.b = p_specular_color.b; - ct->specular_color.a = p_shininess; -} - -void RasterizerStorageGLES3::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { - CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); - ct->texture_filter = p_filter; -} -void RasterizerStorageGLES3::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { - CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); - ct->texture_repeat = p_repeat; -} - -RID RasterizerStorageGLES3::sky_create() { - Sky *sky = memnew(Sky); - sky->radiance = 0; - return sky_owner.make_rid(sky); -} - -void RasterizerStorageGLES3::sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size) { -} - -/* SHADER API */ - -RID RasterizerStorageGLES3::shader_allocate() { - Shader *shader = memnew(Shader); - shader->mode = RS::SHADER_CANVAS_ITEM; - //shader->shader = &scene->state.scene_shader; - RID rid = shader_owner.make_rid(shader); - _shader_make_dirty(shader); - shader->self = rid; - - return rid; -} - -void RasterizerStorageGLES3::shader_initialize(RID p_rid) { - // noop -} - -//RID RasterizerStorageGLES3::shader_create() { -// Shader *shader = memnew(Shader); -// shader->mode = RS::SHADER_SPATIAL; -// shader->shader = &scene->state.scene_shader; -// RID rid = shader_owner.make_rid(shader); -// _shader_make_dirty(shader); -// shader->self = rid; - -// return rid; -//} - -void RasterizerStorageGLES3::_shader_make_dirty(Shader *p_shader) { - if (p_shader->dirty_list.in_list()) { - return; - } - - _shader_dirty_list.add(&p_shader->dirty_list); -} - -void RasterizerStorageGLES3::shader_set_code(RID p_shader, const String &p_code) { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - - shader->code = p_code; - - String mode_string = ShaderLanguage::get_shader_type(p_code); - RS::ShaderMode mode; - - if (mode_string == "canvas_item") { - mode = RS::SHADER_CANVAS_ITEM; - } else if (mode_string == "particles") { - mode = RS::SHADER_PARTICLES; - } else if (mode_string == "sky") { - mode = RS::SHADER_SKY; - } else if (mode_string == "spatial") { - mode = RS::SHADER_SPATIAL; - } else { - mode = RS::SHADER_MAX; - ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer"); - } - - if (shader->version.is_valid() && mode != shader->mode) { - shader->shader->version_free(shader->version); - shader->version = RID(); - } - - shader->mode = mode; - - // TODO handle all shader types - if (mode == RS::SHADER_CANVAS_ITEM) { - shader->shader = &canvas->state.canvas_shader; - - } else if (mode == RS::SHADER_SPATIAL) { - //shader->shader = &scene->state.scene_shader; - } else if (mode == RS::SHADER_PARTICLES) { - } else if (mode == RS::SHADER_SKY) { - } else { - return; - } - - if (shader->version.is_null() && shader->shader) { - shader->version = shader->shader->version_create(); - } - - _shader_make_dirty(shader); -} - -String RasterizerStorageGLES3::shader_get_code(RID p_shader) const { - const Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND_V(!shader, ""); - - return shader->code; -} - -void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { - _shader_dirty_list.remove(&p_shader->dirty_list); - - p_shader->valid = false; - - p_shader->uniforms.clear(); - - if (p_shader->code.is_empty()) { - return; //just invalid, but no error - } - - ShaderCompiler::GeneratedCode gen_code; - ShaderCompiler::IdentifierActions *actions = nullptr; - - switch (p_shader->mode) { - case RS::SHADER_CANVAS_ITEM: { - p_shader->canvas_item.light_mode = Shader::CanvasItem::LIGHT_MODE_NORMAL; - p_shader->canvas_item.blend_mode = Shader::CanvasItem::BLEND_MODE_MIX; - - p_shader->canvas_item.uses_screen_texture = false; - p_shader->canvas_item.uses_screen_uv = false; - p_shader->canvas_item.uses_time = false; - p_shader->canvas_item.uses_modulate = false; - p_shader->canvas_item.uses_color = false; - p_shader->canvas_item.uses_vertex = false; - - p_shader->canvas_item.uses_world_matrix = false; - p_shader->canvas_item.uses_extra_matrix = false; - p_shader->canvas_item.uses_projection_matrix = false; - p_shader->canvas_item.uses_instance_custom = false; - - shaders.actions_canvas.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD); - shaders.actions_canvas.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX); - shaders.actions_canvas.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_SUB); - shaders.actions_canvas.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MUL); - shaders.actions_canvas.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_PMALPHA); - - shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED); - shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY); - - shaders.actions_canvas.usage_flag_pointers["SCREEN_UV"] = &p_shader->canvas_item.uses_screen_uv; - shaders.actions_canvas.usage_flag_pointers["SCREEN_PIXEL_SIZE"] = &p_shader->canvas_item.uses_screen_uv; - shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture; - shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time; - shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate; - shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color; - - shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex; - - shaders.actions_canvas.usage_flag_pointers["WORLD_MATRIX"] = &p_shader->canvas_item.uses_world_matrix; - shaders.actions_canvas.usage_flag_pointers["EXTRA_MATRIX"] = &p_shader->canvas_item.uses_extra_matrix; - shaders.actions_canvas.usage_flag_pointers["PROJECTION_MATRIX"] = &p_shader->canvas_item.uses_projection_matrix; - shaders.actions_canvas.usage_flag_pointers["INSTANCE_CUSTOM"] = &p_shader->canvas_item.uses_instance_custom; - - actions = &shaders.actions_canvas; - actions->uniforms = &p_shader->uniforms; - } break; - - case RS::SHADER_SPATIAL: { - // TODO remove once 3D is added back - return; - p_shader->spatial.blend_mode = Shader::Spatial::BLEND_MODE_MIX; - p_shader->spatial.depth_draw_mode = Shader::Spatial::DEPTH_DRAW_OPAQUE; - p_shader->spatial.cull_mode = Shader::Spatial::CULL_MODE_BACK; - p_shader->spatial.uses_alpha = false; - p_shader->spatial.uses_alpha_scissor = false; - p_shader->spatial.uses_discard = false; - p_shader->spatial.unshaded = false; - p_shader->spatial.no_depth_test = false; - p_shader->spatial.uses_sss = false; - p_shader->spatial.uses_time = false; - p_shader->spatial.uses_vertex_lighting = false; - p_shader->spatial.uses_screen_texture = false; - p_shader->spatial.uses_depth_texture = false; - p_shader->spatial.uses_vertex = false; - p_shader->spatial.uses_tangent = false; - p_shader->spatial.uses_ensure_correct_normals = false; - p_shader->spatial.writes_modelview_or_projection = false; - p_shader->spatial.uses_world_coordinates = false; - - shaders.actions_scene.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_ADD); - shaders.actions_scene.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MIX); - shaders.actions_scene.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_SUB); - shaders.actions_scene.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->spatial.blend_mode, Shader::Spatial::BLEND_MODE_MUL); - - shaders.actions_scene.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_OPAQUE); - shaders.actions_scene.render_mode_values["depth_draw_always"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALWAYS); - shaders.actions_scene.render_mode_values["depth_draw_never"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_NEVER); - shaders.actions_scene.render_mode_values["depth_draw_alpha_prepass"] = Pair<int *, int>(&p_shader->spatial.depth_draw_mode, Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS); - - shaders.actions_scene.render_mode_values["cull_front"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_FRONT); - shaders.actions_scene.render_mode_values["cull_back"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_BACK); - shaders.actions_scene.render_mode_values["cull_disabled"] = Pair<int *, int>(&p_shader->spatial.cull_mode, Shader::Spatial::CULL_MODE_DISABLED); - - shaders.actions_scene.render_mode_flags["unshaded"] = &p_shader->spatial.unshaded; - shaders.actions_scene.render_mode_flags["depth_test_disable"] = &p_shader->spatial.no_depth_test; - - shaders.actions_scene.render_mode_flags["vertex_lighting"] = &p_shader->spatial.uses_vertex_lighting; - - shaders.actions_scene.render_mode_flags["world_vertex_coords"] = &p_shader->spatial.uses_world_coordinates; - - shaders.actions_scene.render_mode_flags["ensure_correct_normals"] = &p_shader->spatial.uses_ensure_correct_normals; - - shaders.actions_scene.usage_flag_pointers["ALPHA"] = &p_shader->spatial.uses_alpha; - shaders.actions_scene.usage_flag_pointers["ALPHA_SCISSOR"] = &p_shader->spatial.uses_alpha_scissor; - - shaders.actions_scene.usage_flag_pointers["SSS_STRENGTH"] = &p_shader->spatial.uses_sss; - shaders.actions_scene.usage_flag_pointers["DISCARD"] = &p_shader->spatial.uses_discard; - shaders.actions_scene.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->spatial.uses_screen_texture; - shaders.actions_scene.usage_flag_pointers["DEPTH_TEXTURE"] = &p_shader->spatial.uses_depth_texture; - shaders.actions_scene.usage_flag_pointers["TIME"] = &p_shader->spatial.uses_time; - - // Use of any of these BUILTINS indicate the need for transformed tangents. - // This is needed to know when to transform tangents in software skinning. - shaders.actions_scene.usage_flag_pointers["TANGENT"] = &p_shader->spatial.uses_tangent; - shaders.actions_scene.usage_flag_pointers["NORMALMAP"] = &p_shader->spatial.uses_tangent; - - shaders.actions_scene.write_flag_pointers["MODELVIEW_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; - shaders.actions_scene.write_flag_pointers["PROJECTION_MATRIX"] = &p_shader->spatial.writes_modelview_or_projection; - shaders.actions_scene.write_flag_pointers["VERTEX"] = &p_shader->spatial.uses_vertex; - - actions = &shaders.actions_scene; - actions->uniforms = &p_shader->uniforms; - } break; - - default: { - return; - } break; - } - - Error err = shaders.compiler.compile(p_shader->mode, p_shader->code, actions, p_shader->path, gen_code); - if (err != OK) { - return; - } - - Vector<StringName> texture_uniform_names; - for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { - texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); - } - - p_shader->shader->version_set_code(p_shader->version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); - - p_shader->texture_uniforms = gen_code.texture_uniforms; - - p_shader->uses_vertex_time = gen_code.uses_vertex_time; - p_shader->uses_fragment_time = gen_code.uses_fragment_time; - - for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { - _material_make_dirty(E->self()); - } - - p_shader->valid = true; -} - -void RasterizerStorageGLES3::update_dirty_shaders() { - while (_shader_dirty_list.first()) { - _update_shader(_shader_dirty_list.first()->self()); - } -} - -void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - - if (shader->dirty_list.in_list()) { - _update_shader(shader); - } - - Map<int, StringName> order; - - for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) { - if (E->get().texture_order >= 0) { - order[E->get().texture_order + 100000] = E->key(); - } else { - order[E->get().order] = E->key(); - } - } - - for (Map<int, StringName>::Element *E = order.front(); E; E = E->next()) { - PropertyInfo pi; - ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[E->get()]; - - pi.name = E->get(); - - switch (u.type) { - case ShaderLanguage::TYPE_VOID: { - pi.type = Variant::NIL; - } break; - - case ShaderLanguage::TYPE_BOOL: { - pi.type = Variant::BOOL; - } break; - - // bool vectors - case ShaderLanguage::TYPE_BVEC2: { - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y"; - } break; - case ShaderLanguage::TYPE_BVEC3: { - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y,z"; - } break; - case ShaderLanguage::TYPE_BVEC4: { - pi.type = Variant::INT; - pi.hint = PROPERTY_HINT_FLAGS; - pi.hint_string = "x,y,z,w"; - } break; - - // int stuff - case ShaderLanguage::TYPE_UINT: - case ShaderLanguage::TYPE_INT: { - pi.type = Variant::INT; - - if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { - pi.hint = PROPERTY_HINT_RANGE; - pi.hint_string = rtos(u.hint_range[0]) + "," + rtos(u.hint_range[1]) + "," + rtos(u.hint_range[2]); - } - } break; - - case ShaderLanguage::TYPE_IVEC2: - case ShaderLanguage::TYPE_UVEC2: - case ShaderLanguage::TYPE_IVEC3: - case ShaderLanguage::TYPE_UVEC3: - case ShaderLanguage::TYPE_IVEC4: - case ShaderLanguage::TYPE_UVEC4: { - // not sure what this should be in godot 4 - // pi.type = Variant::POOL_INT_ARRAY; - pi.type = Variant::PACKED_INT32_ARRAY; - } break; - - case ShaderLanguage::TYPE_FLOAT: { - pi.type = Variant::FLOAT; - if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { - pi.hint = PROPERTY_HINT_RANGE; - pi.hint_string = rtos(u.hint_range[0]) + "," + rtos(u.hint_range[1]) + "," + rtos(u.hint_range[2]); - } - } break; - - case ShaderLanguage::TYPE_VEC2: { - pi.type = Variant::VECTOR2; - } break; - case ShaderLanguage::TYPE_VEC3: { - pi.type = Variant::VECTOR3; - } break; - - case ShaderLanguage::TYPE_VEC4: { - if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { - pi.type = Variant::COLOR; - } else { - pi.type = Variant::PLANE; - } - } break; - - case ShaderLanguage::TYPE_MAT2: { - pi.type = Variant::TRANSFORM2D; - } break; - - case ShaderLanguage::TYPE_MAT3: { - pi.type = Variant::BASIS; - } break; - - case ShaderLanguage::TYPE_MAT4: { - pi.type = Variant::TRANSFORM3D; - } break; - - case ShaderLanguage::TYPE_SAMPLER2D: - // case ShaderLanguage::TYPE_SAMPLEREXT: - case ShaderLanguage::TYPE_ISAMPLER2D: - case ShaderLanguage::TYPE_USAMPLER2D: { - pi.type = Variant::OBJECT; - pi.hint = PROPERTY_HINT_RESOURCE_TYPE; - pi.hint_string = "Texture"; - } break; - - case ShaderLanguage::TYPE_SAMPLERCUBE: { - pi.type = Variant::OBJECT; - pi.hint = PROPERTY_HINT_RESOURCE_TYPE; - pi.hint_string = "CubeMap"; - } break; - - case ShaderLanguage::TYPE_SAMPLER2DARRAY: - case ShaderLanguage::TYPE_ISAMPLER2DARRAY: - case ShaderLanguage::TYPE_USAMPLER2DARRAY: - case ShaderLanguage::TYPE_SAMPLER3D: - case ShaderLanguage::TYPE_ISAMPLER3D: - case ShaderLanguage::TYPE_USAMPLER3D: { - // Not implemented in OpenGL - } break; - // new for godot 4 - case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: - case ShaderLanguage::TYPE_STRUCT: - case ShaderLanguage::TYPE_MAX: { - } break; - } - - p_param_list->push_back(pi); - } -} - -void RasterizerStorageGLES3::shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) { - Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND(!shader); - ERR_FAIL_COND(p_texture.is_valid() && !texture_owner.owns(p_texture)); - - if (!p_texture.is_valid()) { - if (shader->default_textures.has(p_name) && shader->default_textures[p_name].has(p_index)) { - shader->default_textures[p_name].erase(p_index); - - if (shader->default_textures[p_name].is_empty()) { - shader->default_textures.erase(p_name); - } - } - } else { - if (!shader->default_textures.has(p_name)) { - shader->default_textures[p_name] = Map<int, RID>(); - } - shader->default_textures[p_name][p_index] = p_texture; - } - - _shader_make_dirty(shader); -} - -RID RasterizerStorageGLES3::shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const { - const Shader *shader = shader_owner.get_or_null(p_shader); - ERR_FAIL_COND_V(!shader, RID()); - - if (shader->default_textures.has(p_name) && shader->default_textures[p_name].has(p_index)) { - return shader->default_textures[p_name][p_index]; - } - - return RID(); -} - -/* COMMON MATERIAL API */ - -void RasterizerStorageGLES3::_material_make_dirty(Material *p_material) const { - if (p_material->dirty_list.in_list()) { - return; - } - - _material_dirty_list.add(&p_material->dirty_list); -} - -RID RasterizerStorageGLES3::material_allocate() { - Material *material = memnew(Material); - return material_owner.make_rid(material); -} - -void RasterizerStorageGLES3::material_initialize(RID p_rid) { -} - -//RID RasterizerStorageGLES3::material_create() { -// Material *material = memnew(Material); - -// return material_owner.make_rid(material); -//} - -void RasterizerStorageGLES3::material_set_shader(RID p_material, RID p_shader) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - Shader *shader = shader_owner.get_or_null(p_shader); - - if (material->shader) { - // if a shader is present, remove the old shader - material->shader->materials.remove(&material->list); - } - - material->shader = shader; - - if (shader) { - shader->materials.add(&material->list); - } - - _material_make_dirty(material); -} - -RID RasterizerStorageGLES3::material_get_shader(RID p_material) const { - const Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, RID()); - - if (material->shader) { - return material->shader->self; - } - - return RID(); -} - -void RasterizerStorageGLES3::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - if (p_value.get_type() == Variant::NIL) { - material->params.erase(p_param); - } else { - material->params[p_param] = p_value; - } - - _material_make_dirty(material); -} - -Variant RasterizerStorageGLES3::material_get_param(RID p_material, const StringName &p_param) const { - const Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, RID()); - - if (material->params.has(p_param)) { - return material->params[p_param]; - } - - return material_get_param_default(p_material, p_param); -} - -Variant RasterizerStorageGLES3::material_get_param_default(RID p_material, const StringName &p_param) const { - const Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, Variant()); - - if (material->shader) { - if (material->shader->uniforms.has(p_param)) { - ShaderLanguage::ShaderNode::Uniform uniform = material->shader->uniforms[p_param]; - Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; - return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.hint); - } - } - return Variant(); -} - -void RasterizerStorageGLES3::material_set_line_width(RID p_material, float p_width) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - material->line_width = p_width; -} - -void RasterizerStorageGLES3::material_set_next_pass(RID p_material, RID p_next_material) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - material->next_pass = p_next_material; -} - -bool RasterizerStorageGLES3::material_is_animated(RID p_material) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, false); - if (material->dirty_list.in_list()) { - _update_material(material); - } - - bool animated = material->is_animated_cache; - if (!animated && material->next_pass.is_valid()) { - animated = material_is_animated(material->next_pass); - } - return animated; -} - -bool RasterizerStorageGLES3::material_casts_shadows(RID p_material) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, false); - if (material->dirty_list.in_list()) { - _update_material(material); - } - - bool casts_shadows = material->can_cast_shadow_cache; - - if (!casts_shadows && material->next_pass.is_valid()) { - casts_shadows = material_casts_shadows(material->next_pass); - } - - return casts_shadows; -} - -bool RasterizerStorageGLES3::material_uses_tangents(RID p_material) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, false); - - if (!material->shader) { - return false; - } - - if (material->shader->dirty_list.in_list()) { - _update_shader(material->shader); - } - - return material->shader->spatial.uses_tangent; -} - -bool RasterizerStorageGLES3::material_uses_ensure_correct_normals(RID p_material) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND_V(!material, false); - - if (!material->shader) { - return false; - } - - if (material->shader->dirty_list.in_list()) { - _update_shader(material->shader); - } - - return material->shader->spatial.uses_ensure_correct_normals; -} - -void RasterizerStorageGLES3::material_add_instance_owner(RID p_material, DependencyTracker *p_instance) { - /* - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - Map<InstanceBaseDependency *, int>::Element *E = material->instance_owners.find(p_instance); - if (E) { - E->get()++; - } else { - material->instance_owners[p_instance] = 1; - } -*/ -} - -void RasterizerStorageGLES3::material_remove_instance_owner(RID p_material, DependencyTracker *p_instance) { - /* - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - Map<InstanceBaseDependency *, int>::Element *E = material->instance_owners.find(p_instance); - ERR_FAIL_COND(!E); - - E->get()--; - - if (E->get() == 0) { - material->instance_owners.erase(E); - } -*/ -} - -void RasterizerStorageGLES3::material_set_render_priority(RID p_material, int priority) { - ERR_FAIL_COND(priority < RS::MATERIAL_RENDER_PRIORITY_MIN); - ERR_FAIL_COND(priority > RS::MATERIAL_RENDER_PRIORITY_MAX); - - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - material->render_priority = priority; -} - -void RasterizerStorageGLES3::_update_material(Material *p_material) { - if (p_material->dirty_list.in_list()) { - _material_dirty_list.remove(&p_material->dirty_list); - } - - if (p_material->shader && p_material->shader->dirty_list.in_list()) { - _update_shader(p_material->shader); - } - - if (p_material->shader && !p_material->shader->valid) { - return; - } - - { - bool can_cast_shadow = false; - bool is_animated = false; - - if (p_material->shader && p_material->shader->mode == RS::SHADER_SPATIAL) { - if (p_material->shader->spatial.blend_mode == Shader::Spatial::BLEND_MODE_MIX && - (!p_material->shader->spatial.uses_alpha || p_material->shader->spatial.depth_draw_mode == Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) { - can_cast_shadow = true; - } - - if (p_material->shader->spatial.uses_discard && p_material->shader->uses_fragment_time) { - is_animated = true; - } - - if (p_material->shader->spatial.uses_vertex && p_material->shader->uses_vertex_time) { - is_animated = true; - } - - if (can_cast_shadow != p_material->can_cast_shadow_cache || is_animated != p_material->is_animated_cache) { - p_material->can_cast_shadow_cache = can_cast_shadow; - p_material->is_animated_cache = is_animated; - - /* - for (Map<Geometry *, int>::Element *E = p_material->geometry_owners.front(); E; E = E->next()) { - E->key()->material_changed_notify(); - } - - for (Map<InstanceBaseDependency *, int>::Element *E = p_material->instance_owners.front(); E; E = E->next()) { - E->key()->base_changed(false, true); - } - */ - } - } - } - - // uniforms and other things will be set in the use_material method in ShaderGLES3 - - if (p_material->shader && p_material->shader->texture_uniforms.size() > 0) { - p_material->textures.resize(p_material->shader->texture_uniforms.size()); - - for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = p_material->shader->uniforms.front(); E; E = E->next()) { - if (E->get().texture_order < 0) { - continue; // not a texture, does not go here - } - - RID texture; - - Map<StringName, Variant>::Element *V = p_material->params.find(E->key()); - - if (V) { - texture = V->get(); - } - - if (!texture.is_valid()) { - Map<StringName, Map<int, RID>>::Element *W = p_material->shader->default_textures.find(E->key()); - - // TODO: make texture uniform array properly works with GLES3 - if (W && W->get().has(0)) { - texture = W->get()[0]; - } - } - - p_material->textures.write[E->get().texture_order] = Pair<StringName, RID>(E->key(), texture); - } - } else { - p_material->textures.clear(); - } -} -/* -void RasterizerStorageGLES3::_material_add_geometry(RID p_material, Geometry *p_geometry) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); - - if (I) { - I->get()++; - } else { - material->geometry_owners[p_geometry] = 1; - } -} - -void RasterizerStorageGLES3::_material_remove_geometry(RID p_material, Geometry *p_geometry) { - Material *material = material_owner.get_or_null(p_material); - ERR_FAIL_COND(!material); - - Map<Geometry *, int>::Element *I = material->geometry_owners.find(p_geometry); - ERR_FAIL_COND(!I); - - I->get()--; - - if (I->get() == 0) { - material->geometry_owners.erase(I); - } -} -*/ -void RasterizerStorageGLES3::update_dirty_materials() { - while (_material_dirty_list.first()) { - Material *material = _material_dirty_list.first()->self(); - _update_material(material); - } -} - -/* MESH API */ - -RID RasterizerStorageGLES3::mesh_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::mesh_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) { -} - -bool RasterizerStorageGLES3::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { - return false; -} - -RID RasterizerStorageGLES3::mesh_instance_create(RID p_base) { - return RID(); -} - -void RasterizerStorageGLES3::mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) { -} - -void RasterizerStorageGLES3::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) { -} - -void RasterizerStorageGLES3::mesh_instance_check_for_update(RID p_mesh_instance) { -} - -void RasterizerStorageGLES3::update_mesh_instances() { -} - -void RasterizerStorageGLES3::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) { -} - -float RasterizerStorageGLES3::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { - return 0.0; -} - -void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { -} - -int RasterizerStorageGLES3::mesh_get_blend_shape_count(RID p_mesh) const { - return 0; -} - -void RasterizerStorageGLES3::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { -} - -RS::BlendShapeMode RasterizerStorageGLES3::mesh_get_blend_shape_mode(RID p_mesh) const { - return RS::BLEND_SHAPE_MODE_NORMALIZED; -} - -void RasterizerStorageGLES3::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { -} - -void RasterizerStorageGLES3::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { -} - -void RasterizerStorageGLES3::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { -} - -void RasterizerStorageGLES3::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { -} - -RID RasterizerStorageGLES3::mesh_surface_get_material(RID p_mesh, int p_surface) const { - return RID(); -} - -RS::SurfaceData RasterizerStorageGLES3::mesh_get_surface(RID p_mesh, int p_surface) const { - return RS::SurfaceData(); -} - -int RasterizerStorageGLES3::mesh_get_surface_count(RID p_mesh) const { - return 1; -} - -void RasterizerStorageGLES3::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { -} - -AABB RasterizerStorageGLES3::mesh_get_custom_aabb(RID p_mesh) const { - return AABB(); -} - -AABB RasterizerStorageGLES3::mesh_get_aabb(RID p_mesh, RID p_skeleton) { - return AABB(); -} - -void RasterizerStorageGLES3::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { -} - -void RasterizerStorageGLES3::mesh_clear(RID p_mesh) { -} - -/* MULTIMESH API */ - -RID RasterizerStorageGLES3::multimesh_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::multimesh_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { -} - -int RasterizerStorageGLES3::multimesh_get_instance_count(RID p_multimesh) const { - return 0; -} - -void RasterizerStorageGLES3::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { -} - -void RasterizerStorageGLES3::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { -} - -void RasterizerStorageGLES3::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { -} - -void RasterizerStorageGLES3::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { -} - -void RasterizerStorageGLES3::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { -} - -RID RasterizerStorageGLES3::multimesh_get_mesh(RID p_multimesh) const { - return RID(); -} - -AABB RasterizerStorageGLES3::multimesh_get_aabb(RID p_multimesh) const { - return AABB(); -} - -Transform3D RasterizerStorageGLES3::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { - return Transform3D(); -} - -Transform2D RasterizerStorageGLES3::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { - return Transform2D(); -} - -Color RasterizerStorageGLES3::multimesh_instance_get_color(RID p_multimesh, int p_index) const { - return Color(); -} - -Color RasterizerStorageGLES3::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { - return Color(); -} - -void RasterizerStorageGLES3::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) { -} - -Vector<float> RasterizerStorageGLES3::multimesh_get_buffer(RID p_multimesh) const { - return Vector<float>(); -} - -void RasterizerStorageGLES3::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { -} - -int RasterizerStorageGLES3::multimesh_get_visible_instances(RID p_multimesh) const { - return 0; -} - -/* SKELETON API */ - -RID RasterizerStorageGLES3::skeleton_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::skeleton_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { -} - -void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { -} - -int RasterizerStorageGLES3::skeleton_get_bone_count(RID p_skeleton) const { - return 0; -} - -void RasterizerStorageGLES3::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { -} - -Transform3D RasterizerStorageGLES3::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { - return Transform3D(); -} - -void RasterizerStorageGLES3::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { -} - -Transform2D RasterizerStorageGLES3::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { - return Transform2D(); -} - -/* Light API */ - -RID RasterizerStorageGLES3::directional_light_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::directional_light_initialize(RID p_rid) { -} - -RID RasterizerStorageGLES3::omni_light_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::omni_light_initialize(RID p_rid) { -} - -RID RasterizerStorageGLES3::spot_light_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::spot_light_initialize(RID p_rid) { -} - -RID RasterizerStorageGLES3::reflection_probe_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::reflection_probe_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::light_set_color(RID p_light, const Color &p_color) { -} - -void RasterizerStorageGLES3::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { -} - -void RasterizerStorageGLES3::light_set_shadow(RID p_light, bool p_enabled) { -} - -void RasterizerStorageGLES3::light_set_shadow_color(RID p_light, const Color &p_color) { -} - -void RasterizerStorageGLES3::light_set_projector(RID p_light, RID p_texture) { -} - -void RasterizerStorageGLES3::light_set_negative(RID p_light, bool p_enable) { -} - -void RasterizerStorageGLES3::light_set_cull_mask(RID p_light, uint32_t p_mask) { -} - -void RasterizerStorageGLES3::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { -} - -void RasterizerStorageGLES3::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { -} - -void RasterizerStorageGLES3::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) { -} - -void RasterizerStorageGLES3::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { -} - -void RasterizerStorageGLES3::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { -} - -void RasterizerStorageGLES3::light_directional_set_blend_splits(RID p_light, bool p_enable) { -} - -bool RasterizerStorageGLES3::light_directional_get_blend_splits(RID p_light) const { - return false; -} - -void RasterizerStorageGLES3::light_directional_set_sky_only(RID p_light, bool p_sky_only) { -} - -bool RasterizerStorageGLES3::light_directional_is_sky_only(RID p_light) const { - return false; -} - -RS::LightDirectionalShadowMode RasterizerStorageGLES3::light_directional_get_shadow_mode(RID p_light) { - return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; -} - -RS::LightOmniShadowMode RasterizerStorageGLES3::light_omni_get_shadow_mode(RID p_light) { - return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; -} - -bool RasterizerStorageGLES3::light_has_shadow(RID p_light) const { - return false; -} - -bool RasterizerStorageGLES3::light_has_projector(RID p_light) const { - return false; -} - -RS::LightType RasterizerStorageGLES3::light_get_type(RID p_light) const { - return RS::LIGHT_OMNI; -} - -AABB RasterizerStorageGLES3::light_get_aabb(RID p_light) const { - return AABB(); -} - -float RasterizerStorageGLES3::light_get_param(RID p_light, RS::LightParam p_param) { - return 0.0; -} - -Color RasterizerStorageGLES3::light_get_color(RID p_light) { - return Color(); -} - -RS::LightBakeMode RasterizerStorageGLES3::light_get_bake_mode(RID p_light) { - return RS::LIGHT_BAKE_DISABLED; -} - -uint32_t RasterizerStorageGLES3::light_get_max_sdfgi_cascade(RID p_light) { - return 0; -} - -uint64_t RasterizerStorageGLES3::light_get_version(RID p_light) const { - return 0; -} - -/* PROBE API */ - -void RasterizerStorageGLES3::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { -} - -void RasterizerStorageGLES3::reflection_probe_set_intensity(RID p_probe, float p_intensity) { -} - -void RasterizerStorageGLES3::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) { -} - -void RasterizerStorageGLES3::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) { -} - -void RasterizerStorageGLES3::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) { -} - -void RasterizerStorageGLES3::reflection_probe_set_max_distance(RID p_probe, float p_distance) { -} - -void RasterizerStorageGLES3::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { -} - -void RasterizerStorageGLES3::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { -} - -void RasterizerStorageGLES3::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { -} - -void RasterizerStorageGLES3::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { -} - -void RasterizerStorageGLES3::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { -} - -void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { -} - -void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) { -} - -AABB RasterizerStorageGLES3::reflection_probe_get_aabb(RID p_probe) const { - return AABB(); -} - -RS::ReflectionProbeUpdateMode RasterizerStorageGLES3::reflection_probe_get_update_mode(RID p_probe) const { - return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; -} - -uint32_t RasterizerStorageGLES3::reflection_probe_get_cull_mask(RID p_probe) const { - return 0; -} - -Vector3 RasterizerStorageGLES3::reflection_probe_get_extents(RID p_probe) const { - return Vector3(); -} - -Vector3 RasterizerStorageGLES3::reflection_probe_get_origin_offset(RID p_probe) const { - return Vector3(); -} - -float RasterizerStorageGLES3::reflection_probe_get_origin_max_distance(RID p_probe) const { - return 0.0; -} - -bool RasterizerStorageGLES3::reflection_probe_renders_shadows(RID p_probe) const { - return false; -} - -void RasterizerStorageGLES3::base_update_dependency(RID p_base, DependencyTracker *p_instance) { -} - -void RasterizerStorageGLES3::skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) { -} - -/* DECAL API */ - -RID RasterizerStorageGLES3::decal_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::decal_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::decal_set_extents(RID p_decal, const Vector3 &p_extents) { -} - -void RasterizerStorageGLES3::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) { -} - -void RasterizerStorageGLES3::decal_set_emission_energy(RID p_decal, float p_energy) { -} - -void RasterizerStorageGLES3::decal_set_albedo_mix(RID p_decal, float p_mix) { -} - -void RasterizerStorageGLES3::decal_set_modulate(RID p_decal, const Color &p_modulate) { -} - -void RasterizerStorageGLES3::decal_set_cull_mask(RID p_decal, uint32_t p_layers) { -} - -void RasterizerStorageGLES3::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) { -} - -void RasterizerStorageGLES3::decal_set_fade(RID p_decal, float p_above, float p_below) { -} - -void RasterizerStorageGLES3::decal_set_normal_fade(RID p_decal, float p_fade) { -} - -AABB RasterizerStorageGLES3::decal_get_aabb(RID p_decal) const { - return AABB(); -} - -/* 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; -} - -/* LIGHTMAP CAPTURE */ -RID RasterizerStorageGLES3::lightmap_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::lightmap_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { -} - -void RasterizerStorageGLES3::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { -} - -void RasterizerStorageGLES3::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) { -} - -void RasterizerStorageGLES3::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) { -} - -PackedVector3Array RasterizerStorageGLES3::lightmap_get_probe_capture_points(RID p_lightmap) const { - return PackedVector3Array(); -} - -PackedColorArray RasterizerStorageGLES3::lightmap_get_probe_capture_sh(RID p_lightmap) const { - return PackedColorArray(); -} - -PackedInt32Array RasterizerStorageGLES3::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { - return PackedInt32Array(); -} - -PackedInt32Array RasterizerStorageGLES3::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { - return PackedInt32Array(); -} - -AABB RasterizerStorageGLES3::lightmap_get_aabb(RID p_lightmap) const { - return AABB(); -} - -void RasterizerStorageGLES3::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { -} - -bool RasterizerStorageGLES3::lightmap_is_interior(RID p_lightmap) const { - return false; -} - -void RasterizerStorageGLES3::lightmap_set_probe_capture_update_speed(float p_speed) { -} - -float RasterizerStorageGLES3::lightmap_get_probe_capture_update_speed() const { - return 0; -} - -/* OCCLUDER */ - -void RasterizerStorageGLES3::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) { -} - -/* PARTICLES */ - -RID RasterizerStorageGLES3::particles_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::particles_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { -} - -void RasterizerStorageGLES3::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { -} - -void RasterizerStorageGLES3::particles_set_emitting(RID p_particles, bool p_emitting) { -} - -void RasterizerStorageGLES3::particles_set_amount(RID p_particles, int p_amount) { -} - -void RasterizerStorageGLES3::particles_set_lifetime(RID p_particles, double p_lifetime) { -} - -void RasterizerStorageGLES3::particles_set_one_shot(RID p_particles, bool p_one_shot) { -} - -void RasterizerStorageGLES3::particles_set_pre_process_time(RID p_particles, double p_time) { -} - -void RasterizerStorageGLES3::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { -} - -void RasterizerStorageGLES3::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { -} - -void RasterizerStorageGLES3::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { -} - -void RasterizerStorageGLES3::particles_set_speed_scale(RID p_particles, double p_scale) { -} - -void RasterizerStorageGLES3::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { -} - -void RasterizerStorageGLES3::particles_set_process_material(RID p_particles, RID p_material) { -} - -void RasterizerStorageGLES3::particles_set_fixed_fps(RID p_particles, int p_fps) { -} - -void RasterizerStorageGLES3::particles_set_interpolate(RID p_particles, bool p_enable) { -} - -void RasterizerStorageGLES3::particles_set_fractional_delta(RID p_particles, bool p_enable) { -} - -void RasterizerStorageGLES3::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { -} - -void RasterizerStorageGLES3::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { -} - -void RasterizerStorageGLES3::particles_set_collision_base_size(RID p_particles, real_t p_size) { -} - -void RasterizerStorageGLES3::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { -} - -void RasterizerStorageGLES3::particles_set_trails(RID p_particles, bool p_enable, double p_length) { -} - -void RasterizerStorageGLES3::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { -} - -void RasterizerStorageGLES3::particles_restart(RID p_particles) { -} - -void RasterizerStorageGLES3::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { -} - -void RasterizerStorageGLES3::particles_set_draw_passes(RID p_particles, int p_count) { -} - -void RasterizerStorageGLES3::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { -} - -void RasterizerStorageGLES3::particles_request_process(RID p_particles) { -} - -AABB RasterizerStorageGLES3::particles_get_current_aabb(RID p_particles) { - return AABB(); -} - -AABB RasterizerStorageGLES3::particles_get_aabb(RID p_particles) const { - return AABB(); -} - -void RasterizerStorageGLES3::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { -} - -bool RasterizerStorageGLES3::particles_get_emitting(RID p_particles) { - return false; -} - -int RasterizerStorageGLES3::particles_get_draw_passes(RID p_particles) const { - return 0; -} - -RID RasterizerStorageGLES3::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { - return RID(); -} - -void RasterizerStorageGLES3::particles_add_collision(RID p_particles, RID p_instance) { -} - -void RasterizerStorageGLES3::particles_remove_collision(RID p_particles, RID p_instance) { -} - -void RasterizerStorageGLES3::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { -} - -void RasterizerStorageGLES3::update_particles() { -} - -/* PARTICLES COLLISION */ - -RID RasterizerStorageGLES3::particles_collision_allocate() { - return RID(); -} - -void RasterizerStorageGLES3::particles_collision_initialize(RID p_rid) { -} - -void RasterizerStorageGLES3::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { -} - -void RasterizerStorageGLES3::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { -} - -void RasterizerStorageGLES3::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { -} - -void RasterizerStorageGLES3::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { -} - -void RasterizerStorageGLES3::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { -} - -void RasterizerStorageGLES3::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { -} - -void RasterizerStorageGLES3::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { -} - -void RasterizerStorageGLES3::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { -} - -void RasterizerStorageGLES3::particles_collision_height_field_update(RID p_particles_collision) { -} - -void RasterizerStorageGLES3::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { -} - -AABB RasterizerStorageGLES3::particles_collision_get_aabb(RID p_particles_collision) const { - return AABB(); -} - -bool RasterizerStorageGLES3::particles_collision_is_heightfield(RID p_particles_collision) const { - return false; -} - -RID RasterizerStorageGLES3::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { - return RID(); -} - -RID RasterizerStorageGLES3::particles_collision_instance_create(RID p_collision) { - return RID(); -} - -void RasterizerStorageGLES3::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { -} - -void RasterizerStorageGLES3::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { -} - -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) { -} - -/* GLOBAL VARIABLES */ - -void RasterizerStorageGLES3::global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) { -} - -void RasterizerStorageGLES3::global_variable_remove(const StringName &p_name) { -} - -Vector<StringName> RasterizerStorageGLES3::global_variable_get_list() const { - return Vector<StringName>(); -} - -void RasterizerStorageGLES3::global_variable_set(const StringName &p_name, const Variant &p_value) { -} - -void RasterizerStorageGLES3::global_variable_set_override(const StringName &p_name, const Variant &p_value) { -} - -Variant RasterizerStorageGLES3::global_variable_get(const StringName &p_name) const { - return Variant(); -} - -RS::GlobalVariableType RasterizerStorageGLES3::global_variable_get_type(const StringName &p_name) const { - return RS::GLOBAL_VAR_TYPE_MAX; -} - -void RasterizerStorageGLES3::global_variables_load_settings(bool p_load_textures) { -} - -void RasterizerStorageGLES3::global_variables_clear() { -} - -int32_t RasterizerStorageGLES3::global_variables_instance_allocate(RID p_instance) { - return 0; -} - -void RasterizerStorageGLES3::global_variables_instance_free(RID p_instance) { -} - -void RasterizerStorageGLES3::global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) { -} - -bool RasterizerStorageGLES3::particles_is_inactive(RID p_particles) const { - return false; -} - -/* RENDER TARGET */ - -void RasterizerStorageGLES3::_set_current_render_target(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - - if (rt) { - if (rt->allocate_is_dirty) { - rt->allocate_is_dirty = false; - _render_target_allocate(rt); - } - - frame.current_rt = rt; - ERR_FAIL_COND(!rt); - frame.clear_request = false; - - glViewport(0, 0, rt->width, rt->height); - - _dims.rt_width = rt->width; - _dims.rt_height = rt->height; - _dims.win_width = rt->width; - _dims.win_height = rt->height; - - } else { - frame.current_rt = nullptr; - frame.clear_request = false; - bind_framebuffer_system(); - } -} - -void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { - // do not allocate a render target with no size - if (rt->width <= 0 || rt->height <= 0) { - return; - } - - // do not allocate a render target that is attached to the screen - if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { - rt->fbo = RasterizerStorageGLES3::system_fbo; - return; - } - - GLuint color_internal_format; - GLuint color_format; - GLuint color_type = GL_UNSIGNED_BYTE; - Image::Format image_format; - - if (rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { -#ifdef GLES_OVER_GL - color_internal_format = GL_RGBA8; -#else - color_internal_format = GL_RGBA; -#endif - color_format = GL_RGBA; - image_format = Image::FORMAT_RGBA8; - } else { -#ifdef GLES_OVER_GL - color_internal_format = GL_RGB8; -#else - color_internal_format = GL_RGB; -#endif - color_format = GL_RGB; - image_format = Image::FORMAT_RGB8; - } - - rt->used_dof_blur_near = false; - rt->mip_maps_allocated = false; - - { - /* Front FBO */ - - Texture *texture = texture_owner.get_or_null(rt->texture); - ERR_FAIL_COND(!texture); - - // framebuffer - glGenFramebuffers(1, &rt->fbo); - bind_framebuffer(rt->fbo); - - // color - glGenTextures(1, &rt->color); - glBindTexture(GL_TEXTURE_2D, rt->color); - - glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, rt->width, rt->height, 0, color_format, color_type, nullptr); - - if (texture->flags & TEXTURE_FLAG_FILTER) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - 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, rt->color, 0); - - // depth - - if (config.support_depth_texture) { - glGenTextures(1, &rt->depth); - glBindTexture(GL_TEXTURE_2D, rt->depth); - glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, rt->width, rt->height, 0, GL_DEPTH_COMPONENT, config.depth_type, 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, rt->depth, 0); - } else { - glGenRenderbuffers(1, &rt->depth); - glBindRenderbuffer(GL_RENDERBUFFER, rt->depth); - - glRenderbufferStorage(GL_RENDERBUFFER, config.depth_buffer_internalformat, rt->width, rt->height); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - glDeleteFramebuffers(1, &rt->fbo); - if (config.support_depth_texture) { - glDeleteTextures(1, &rt->depth); - } else { - glDeleteRenderbuffers(1, &rt->depth); - } - - glDeleteTextures(1, &rt->color); - rt->fbo = 0; - rt->width = 0; - rt->height = 0; - rt->color = 0; - rt->depth = 0; - texture->tex_id = 0; - texture->active = false; - WARN_PRINT("Could not create framebuffer!!"); - return; - } - - texture->format = image_format; - texture->gl_format_cache = color_format; - texture->gl_type_cache = GL_UNSIGNED_BYTE; - texture->gl_internal_format_cache = color_internal_format; - texture->tex_id = rt->color; - texture->width = rt->width; - texture->alloc_width = rt->width; - texture->height = rt->height; - texture->alloc_height = rt->height; - texture->active = true; - - texture_set_flags(rt->texture, texture->flags); - } - - /* BACK FBO */ - /* For MSAA */ - -#ifndef JAVASCRIPT_ENABLED - if (rt->msaa >= RS::VIEWPORT_MSAA_2X && rt->msaa <= RS::VIEWPORT_MSAA_8X) { - rt->multisample_active = true; - - static const int msaa_value[] = { 0, 2, 4, 8, 16 }; - int msaa = msaa_value[rt->msaa]; - - int max_samples = 0; - glGetIntegerv(GL_MAX_SAMPLES, &max_samples); - if (msaa > max_samples) { - WARN_PRINT("MSAA must be <= GL_MAX_SAMPLES, falling-back to GL_MAX_SAMPLES = " + itos(max_samples)); - msaa = max_samples; - } - - //regular fbo - glGenFramebuffers(1, &rt->multisample_fbo); - bind_framebuffer(rt->multisample_fbo); - - glGenRenderbuffers(1, &rt->multisample_depth); - glBindRenderbuffer(GL_RENDERBUFFER, rt->multisample_depth); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, config.depth_buffer_internalformat, rt->width, rt->height); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->multisample_depth); - - glGenRenderbuffers(1, &rt->multisample_color); - glBindRenderbuffer(GL_RENDERBUFFER, rt->multisample_color); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, color_internal_format, rt->width, rt->height); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rt->multisample_color); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - // Delete allocated resources and default to no MSAA - WARN_PRINT_ONCE("Cannot allocate back framebuffer for MSAA"); - printf("err status: %x\n", status); - rt->multisample_active = false; - - glDeleteFramebuffers(1, &rt->multisample_fbo); - rt->multisample_fbo = 0; - - glDeleteRenderbuffers(1, &rt->multisample_depth); - rt->multisample_depth = 0; - - glDeleteRenderbuffers(1, &rt->multisample_color); - rt->multisample_color = 0; - } - - glBindRenderbuffer(GL_RENDERBUFFER, 0); - bind_framebuffer(0); - - } else -#endif // JAVASCRIPT_ENABLED - { - rt->multisample_active = false; - } - - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // copy texscreen buffers - // if (!(rt->flags[RendererStorage::RENDER_TARGET_NO_SAMPLING])) { - if (true) { - glGenTextures(1, &rt->copy_screen_effect.color); - glBindTexture(GL_TEXTURE_2D, rt->copy_screen_effect.color); - - if (rt->flags[RendererStorage::RENDER_TARGET_TRANSPARENT]) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rt->width, rt->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, rt->width, rt->height, 0, GL_RGB, 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); - - glGenFramebuffers(1, &rt->copy_screen_effect.fbo); - bind_framebuffer(rt->copy_screen_effect.fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->copy_screen_effect.color, 0); - - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - _render_target_clear(rt); - ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); - } - } - - // Allocate mipmap chains for post_process effects - // if (!rt->flags[RendererStorage::RENDER_TARGET_NO_3D] && rt->width >= 2 && rt->height >= 2) { - if (rt->width >= 2 && rt->height >= 2) { - for (int i = 0; i < 2; i++) { - ERR_FAIL_COND(rt->mip_maps[i].sizes.size()); - int w = rt->width; - int h = rt->height; - - if (i > 0) { - w >>= 1; - h >>= 1; - } - - int level = 0; - int fb_w = w; - int fb_h = h; - - while (true) { - RenderTarget::MipMaps::Size mm; - mm.width = w; - mm.height = h; - rt->mip_maps[i].sizes.push_back(mm); - - w >>= 1; - h >>= 1; - - if (w < 2 || h < 2) { - break; - } - - level++; - } - - GLsizei width = fb_w; - GLsizei height = fb_h; - - if (config.render_to_mipmap_supported) { - glGenTextures(1, &rt->mip_maps[i].color); - glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].color); - - for (int l = 0; l < level + 1; l++) { - glTexImage2D(GL_TEXTURE_2D, l, color_internal_format, width, height, 0, color_format, color_type, nullptr); - width = MAX(1, (width / 2)); - height = MAX(1, (height / 2)); - } -#ifdef GLES_OVER_GL - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); -#endif - } else { - // Can't render to specific levels of a mipmap in ES 2.0 or Webgl so create a texture for each level - for (int l = 0; l < level + 1; l++) { - glGenTextures(1, &rt->mip_maps[i].sizes.write[l].color); - glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].sizes[l].color); - glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, width, height, 0, color_format, color_type, nullptr); - width = MAX(1, (width / 2)); - height = MAX(1, (height / 2)); - 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); - } - } - - glDisable(GL_SCISSOR_TEST); - glColorMask(1, 1, 1, 1); - glDepthMask(GL_TRUE); - - for (int j = 0; j < rt->mip_maps[i].sizes.size(); j++) { - RenderTarget::MipMaps::Size &mm = rt->mip_maps[i].sizes.write[j]; - - glGenFramebuffers(1, &mm.fbo); - bind_framebuffer(mm.fbo); - - if (config.render_to_mipmap_supported) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->mip_maps[i].color, j); - } else { - glBindTexture(GL_TEXTURE_2D, rt->mip_maps[i].sizes[j].color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->mip_maps[i].sizes[j].color, 0); - } - - bool used_depth = false; - if (j == 0 && i == 0) { //use always - if (config.support_depth_texture) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); - } else { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); - } - used_depth = true; - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - WARN_PRINT_ONCE("Cannot allocate mipmaps for 3D post processing effects"); - bind_framebuffer_system(); - return; - } - - glClearColor(1.0, 0.0, 1.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - if (used_depth) { - glClearDepth(1.0); - glClear(GL_DEPTH_BUFFER_BIT); - } - } - - rt->mip_maps[i].levels = level; - - if (config.render_to_mipmap_supported) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); - } - } - rt->mip_maps_allocated = true; - } - - bind_framebuffer_system(); -} - -void RasterizerStorageGLES3::_render_target_clear(RenderTarget *rt) { - // there is nothing to clear when DIRECT_TO_SCREEN is used - if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { - return; - } - - if (rt->fbo) { - glDeleteFramebuffers(1, &rt->fbo); - glDeleteTextures(1, &rt->color); - rt->fbo = 0; - } - - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); - - // clean up our texture - Texture *t = texture_owner.get_or_null(rt->external.texture); - t->alloc_height = 0; - t->alloc_width = 0; - t->width = 0; - t->height = 0; - t->active = false; - texture_owner.free(rt->external.texture); - memdelete(t); - - rt->external.fbo = 0; - } - - if (rt->depth) { - if (config.support_depth_texture) { - glDeleteTextures(1, &rt->depth); - } else { - glDeleteRenderbuffers(1, &rt->depth); - } - - rt->depth = 0; - } - - Texture *tex = texture_owner.get_or_null(rt->texture); - tex->alloc_height = 0; - tex->alloc_width = 0; - tex->width = 0; - tex->height = 0; - tex->active = false; - - if (rt->copy_screen_effect.color) { - glDeleteFramebuffers(1, &rt->copy_screen_effect.fbo); - rt->copy_screen_effect.fbo = 0; - - glDeleteTextures(1, &rt->copy_screen_effect.color); - rt->copy_screen_effect.color = 0; - } - - for (int i = 0; i < 2; i++) { - if (rt->mip_maps[i].sizes.size()) { - for (int j = 0; j < rt->mip_maps[i].sizes.size(); j++) { - glDeleteFramebuffers(1, &rt->mip_maps[i].sizes[j].fbo); - glDeleteTextures(1, &rt->mip_maps[i].sizes[j].color); - } - - glDeleteTextures(1, &rt->mip_maps[i].color); - rt->mip_maps[i].sizes.clear(); - rt->mip_maps[i].levels = 0; - rt->mip_maps[i].color = 0; - } - } - - if (rt->multisample_active) { - glDeleteFramebuffers(1, &rt->multisample_fbo); - rt->multisample_fbo = 0; - - glDeleteRenderbuffers(1, &rt->multisample_depth); - rt->multisample_depth = 0; - - glDeleteRenderbuffers(1, &rt->multisample_color); - - rt->multisample_color = 0; - } -} - -RID RasterizerStorageGLES3::render_target_create() { - RenderTarget *rt = memnew(RenderTarget); - Texture *t = memnew(Texture); - - t->type = RenderingDevice::TEXTURE_TYPE_2D; - t->flags = 0; - t->width = 0; - t->height = 0; - t->alloc_height = 0; - t->alloc_width = 0; - t->format = Image::FORMAT_R8; - t->target = GL_TEXTURE_2D; - t->gl_format_cache = 0; - t->gl_internal_format_cache = 0; - t->gl_type_cache = 0; - t->data_size = 0; - t->total_data_size = 0; - t->ignore_mipmaps = false; - t->compressed = false; - t->mipmaps = 1; - t->active = true; - t->tex_id = 0; - t->render_target = rt; - - rt->texture = texture_owner.make_rid(t); - return render_target_owner.make_rid(rt); -} - -void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int p_x, int p_y) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - - rt->x = p_x; - rt->y = p_y; -} - -void RasterizerStorageGLES3::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->width && p_height == rt->height) { - return; - } - - _render_target_clear(rt); - - rt->width = p_width; - rt->height = p_height; - - // print_line("render_target_set_size " + itos(p_render_target.get_id()) + ", w " + itos(p_width) + " h " + itos(p_height)); - - rt->allocate_is_dirty = true; - //_render_target_allocate(rt); -} - -// TODO: convert to Size2i internally -Size2i RasterizerStorageGLES3::render_target_get_size(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, Size2()); - - return Size2i(rt->width, rt->height); -} - -RID RasterizerStorageGLES3::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->external.fbo == 0) { - return rt->texture; - } else { - return rt->external.texture; - } -} - -void RasterizerStorageGLES3::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_texture_id == 0) { - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); - - // and this - if (rt->external.depth != 0) { - glDeleteRenderbuffers(1, &rt->external.depth); - } - - // clean up our texture - Texture *t = texture_owner.get_or_null(rt->external.texture); - t->alloc_height = 0; - t->alloc_width = 0; - t->width = 0; - t->height = 0; - t->active = false; - texture_owner.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); - bind_framebuffer(rt->external.fbo); - - // allocate a texture - t = memnew(Texture); - - t->type = RenderingDevice::TEXTURE_TYPE_2D; - t->flags = 0; - 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->data_size = 0; - t->compressed = false; - t->srgb = false; - t->total_data_size = 0; - t->ignore_mipmaps = false; - t->mipmaps = 1; - t->active = true; - t->tex_id = 0; - t->render_target = rt; - - rt->external.texture = texture_owner.make_rid(t); - - } else { - // bind our frame buffer - bind_framebuffer(rt->external.fbo); - - // find our texture - t = texture_owner.get_or_null(rt->external.texture); - } - - // set our texture - t->tex_id = p_texture_id; - rt->external.color = p_texture_id; - - // size shouldn't be different - t->width = rt->width; - t->height = rt->height; - t->alloc_height = rt->width; - t->alloc_width = rt->height; - - // 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); - - // seeing we're rendering into this directly, better also use our depth buffer, just use our existing one :) - if (config.support_depth_texture) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0); - } else { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); - } - } - - // check status and unbind - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - bind_framebuffer_system(); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - printf("framebuffer fail, status: %x\n", status); - } - - ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); - } -} - -void RasterizerStorageGLES3::render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - - // When setting DIRECT_TO_SCREEN, you need to clear before the value is set, but allocate after as - // those functions change how they operate depending on the value of DIRECT_TO_SCREEN - if (p_flag == RENDER_TARGET_DIRECT_TO_SCREEN && p_value != rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) { - _render_target_clear(rt); - rt->flags[p_flag] = p_value; - _render_target_allocate(rt); - } - - rt->flags[p_flag] = p_value; - - switch (p_flag) { - case RENDER_TARGET_TRANSPARENT: - /* - case RENDER_TARGET_HDR: - case RENDER_TARGET_NO_3D: - case RENDER_TARGET_NO_SAMPLING: - case RENDER_TARGET_NO_3D_EFFECTS: */ - { - //must reset for these formats - _render_target_clear(rt); - _render_target_allocate(rt); - } - break; - default: { - } - } -} - -bool RasterizerStorageGLES3::render_target_was_used(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, false); - - return rt->used_in_frame; -} - -void RasterizerStorageGLES3::render_target_clear_used(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - - rt->used_in_frame = false; -} - -void RasterizerStorageGLES3::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 (rt->msaa == p_msaa) { - return; - } - - _render_target_clear(rt); - rt->msaa = p_msaa; - _render_target_allocate(rt); -} - -//RasterizerStorageGLES3::RenderTarget * RasterizerStorageGLES3::render_target_get(RID p_render_target) -//{ -// return render_target_owner.get_or_null(p_render_target); -//} - -void RasterizerStorageGLES3::render_target_set_use_fxaa(RID p_render_target, bool p_fxaa) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - - rt->use_fxaa = p_fxaa; -} - -void RasterizerStorageGLES3::render_target_set_use_debanding(RID p_render_target, bool p_debanding) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - - if (p_debanding) { - WARN_PRINT_ONCE("Debanding is not supported in the OpenGL backend. Switch to the Vulkan backend and make sure HDR is enabled."); - } - - rt->use_debanding = p_debanding; -} - -void RasterizerStorageGLES3::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); - rt->clear_requested = true; - rt->clear_color = p_clear_color; - - // ERR_FAIL_COND(!frame.current_rt); - // frame.clear_request = true; - // frame.clear_request_color = p_color; -} - -bool RasterizerStorageGLES3::render_target_is_clear_requested(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, false); - return rt->clear_requested; -} -Color RasterizerStorageGLES3::render_target_get_clear_request_color(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND_V(!rt, Color()); - return rt->clear_color; -} - -void RasterizerStorageGLES3::render_target_disable_clear_request(RID p_render_target) { - RenderTarget *rt = render_target_owner.get_or_null(p_render_target); - ERR_FAIL_COND(!rt); - rt->clear_requested = false; -} - -void RasterizerStorageGLES3::render_target_do_clear_request(RID p_render_target) { -} - -void RasterizerStorageGLES3::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { -} - -Rect2i RasterizerStorageGLES3::render_target_get_sdf_rect(RID p_render_target) const { - return Rect2i(); -} - -void RasterizerStorageGLES3::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { -} - -/* 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); - bind_framebuffer(cls->fbo); - - glGenRenderbuffers(1, &cls->depth); - glBindRenderbuffer(GL_RENDERBUFFER, cls->depth); - glRenderbufferStorage(GL_RENDERBUFFER, config.depth_buffer_internalformat, 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_RGBA, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - } else { -#ifdef GLES_OVER_GL - glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, nullptr); -#else - glTexImage2D(GL_TEXTURE_2D, 0, GL_FLOAT, cls->size, cls->height, 0, _RED_OES, GL_FLOAT, NULL); -#endif - } - - 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); - bind_framebuffer_system(); - - 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 { - return RS::INSTANCE_NONE; - - /* - if (mesh_owner.owns(p_rid)) { - return RS::INSTANCE_MESH; - } else if (light_owner.owns(p_rid)) { - return RS::INSTANCE_LIGHT; - } else if (multimesh_owner.owns(p_rid)) { - return RS::INSTANCE_MULTIMESH; - } else if (immediate_owner.owns(p_rid)) { - return RS::INSTANCE_IMMEDIATE; - } else if (reflection_probe_owner.owns(p_rid)) { - return RS::INSTANCE_REFLECTION_PROBE; - } else if (lightmap_capture_data_owner.owns(p_rid)) { - return RS::INSTANCE_LIGHTMAP_CAPTURE; - } else { - return RS::INSTANCE_NONE; - } -*/ -} - -bool RasterizerStorageGLES3::free(RID p_rid) { - if (render_target_owner.owns(p_rid)) { - RenderTarget *rt = render_target_owner.get_or_null(p_rid); - _render_target_clear(rt); - - Texture *t = texture_owner.get_or_null(rt->texture); - if (t) { - texture_owner.free(rt->texture); - memdelete(t); - } - render_target_owner.free(p_rid); - memdelete(rt); - - return true; - } else if (texture_owner.owns(p_rid)) { - Texture *t = texture_owner.get_or_null(p_rid); - // can't free a render target texture - ERR_FAIL_COND_V(t->render_target, true); - if (t->canvas_texture) { - memdelete(t->canvas_texture); - } - - info.texture_mem -= t->total_data_size; - texture_owner.free(p_rid); - memdelete(t); - - return true; - } else if (canvas_texture_owner.owns(p_rid)) { - canvas_texture_owner.free(p_rid); - return true; - } else if (sky_owner.owns(p_rid)) { - Sky *sky = sky_owner.get_or_null(p_rid); - sky_set_texture(p_rid, RID(), 256); - sky_owner.free(p_rid); - memdelete(sky); - - return true; - } else if (shader_owner.owns(p_rid)) { - Shader *shader = shader_owner.get_or_null(p_rid); - - if (shader->shader && shader->version.is_valid()) { - shader->shader->version_free(shader->version); - } - - if (shader->dirty_list.in_list()) { - _shader_dirty_list.remove(&shader->dirty_list); - } - - while (shader->materials.first()) { - Material *m = shader->materials.first()->self(); - - m->shader = nullptr; - _material_make_dirty(m); - - shader->materials.remove(shader->materials.first()); - } - - shader_owner.free(p_rid); - memdelete(shader); - - return true; - } else if (material_owner.owns(p_rid)) { - Material *m = material_owner.get_or_null(p_rid); - - if (m->shader) { - m->shader->materials.remove(&m->list); - } - - /* - for (Map<Geometry *, int>::Element *E = m->geometry_owners.front(); E; E = E->next()) { - Geometry *g = E->key(); - g->material = RID(); - } - - for (Map<InstanceBaseDependency *, int>::Element *E = m->instance_owners.front(); E; E = E->next()) { - InstanceBaseDependency *ins = E->key(); - - if (ins->material_override == p_rid) { - ins->material_override = RID(); - } - - for (int i = 0; i < ins->materials.size(); i++) { - if (ins->materials[i] == p_rid) { - ins->materials.write[i] = RID(); - } - } - } -*/ - - material_owner.free(p_rid); - memdelete(m); - - return true; - - } else { - return false; - } - /* - } else if (skeleton_owner.owns(p_rid)) { - Skeleton *s = skeleton_owner.get_or_null(p_rid); - - if (s->update_list.in_list()) { - skeleton_update_list.remove(&s->update_list); - } - - for (Set<InstanceBaseDependency *>::Element *E = s->instances.front(); E; E = E->next()) { - E->get()->skeleton = RID(); - } - - skeleton_allocate(p_rid, 0, false); - - if (s->tex_id) { - glDeleteTextures(1, &s->tex_id); - } - - skeleton_owner.free(p_rid); - memdelete(s); - - return true; - } else if (mesh_owner.owns(p_rid)) { - Mesh *mesh = mesh_owner.get_or_null(p_rid); - - mesh->instance_remove_deps(); - mesh_clear(p_rid); - - while (mesh->multimeshes.first()) { - MultiMesh *multimesh = mesh->multimeshes.first()->self(); - multimesh->mesh = RID(); - multimesh->dirty_aabb = true; - - mesh->multimeshes.remove(mesh->multimeshes.first()); - - if (!multimesh->update_list.in_list()) { - multimesh_update_list.add(&multimesh->update_list); - } - } - - mesh_owner.free(p_rid); - memdelete(mesh); - - return true; - } else if (multimesh_owner.owns(p_rid)) { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); - multimesh->instance_remove_deps(); - - if (multimesh->mesh.is_valid()) { - Mesh *mesh = mesh_owner.get_or_null(multimesh->mesh); - if (mesh) { - mesh->multimeshes.remove(&multimesh->mesh_list); - } - } - - multimesh_allocate(p_rid, 0, RS::MULTIMESH_TRANSFORM_3D, RS::MULTIMESH_COLOR_NONE); - - update_dirty_multimeshes(); - - multimesh_owner.free(p_rid); - memdelete(multimesh); - - return true; - } else if (immediate_owner.owns(p_rid)) { - Immediate *im = immediate_owner.get_or_null(p_rid); - im->instance_remove_deps(); - - immediate_owner.free(p_rid); - memdelete(im); - - return true; - } else if (light_owner.owns(p_rid)) { - Light *light = light_owner.get_or_null(p_rid); - light->instance_remove_deps(); - - light_owner.free(p_rid); - memdelete(light); - - return true; - } 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 (p_feature == "s3tc") { - return config.s3tc_supported; - } - - if (p_feature == "etc") { - return config.etc_supported; - } - - if (p_feature == "skinning_fallback") { - return config.use_skeleton_software; - } - - 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; -} - -void RasterizerStorageGLES3::initialize() { - RasterizerStorageGLES3::system_fbo = 0; - - { - const GLubyte *extension_string = glGetString(GL_EXTENSIONS); - - Vector<String> extensions = String((const char *)extension_string).split(" "); - - for (int i = 0; i < extensions.size(); i++) { - config.extensions.insert(extensions[i]); - } - } - - config.keep_original_textures = true; // false - config.shrink_textures_x2 = false; - config.depth_internalformat = GL_DEPTH_COMPONENT; - config.depth_type = GL_UNSIGNED_INT; - -#ifdef GLES_OVER_GL - config.float_texture_supported = true; - config.s3tc_supported = true; - config.etc_supported = false; - config.support_npot_repeat_mipmap = true; - config.depth_buffer_internalformat = GL_DEPTH_COMPONENT24; -#else - config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float"); - config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc"); - config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") || config.extensions.has("WEBGL_compressed_texture_etc1"); - config.support_npot_repeat_mipmap = config.extensions.has("GL_OES_texture_npot"); - -#ifdef JAVASCRIPT_ENABLED - // RenderBuffer internal format must be 16 bits in WebGL, - // but depth_texture should default to 32 always - // if the implementation doesn't support 32, it should just quietly use 16 instead - // https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/ - config.depth_buffer_internalformat = GL_DEPTH_COMPONENT16; - config.depth_type = GL_UNSIGNED_INT; -#else - // on mobile check for 24 bit depth support for RenderBufferStorage - if (config.extensions.has("GL_OES_depth24")) { - config.depth_buffer_internalformat = _DEPTH_COMPONENT24_OES; - config.depth_type = GL_UNSIGNED_INT; - } else { - config.depth_buffer_internalformat = GL_DEPTH_COMPONENT16; - config.depth_type = GL_UNSIGNED_SHORT; - } -#endif -#endif - -#ifdef GLES_OVER_GL - //TODO: causes huge problems with desktop video drivers. Making false for now, needs to be true to render SCREEN_TEXTURE mipmaps - config.render_to_mipmap_supported = false; -#else - //check if mipmaps can be used for SCREEN_TEXTURE and Glow on Mobile and web platforms - config.render_to_mipmap_supported = config.extensions.has("GL_OES_fbo_render_mipmap") && config.extensions.has("GL_EXT_texture_lod"); -#endif - -#ifdef GLES_OVER_GL - config.use_rgba_2d_shadows = false; - config.support_depth_texture = true; - config.use_rgba_3d_shadows = false; - config.support_depth_cubemaps = true; -#else - config.use_rgba_2d_shadows = !(config.float_texture_supported && config.extensions.has("GL_EXT_texture_rg")); - config.support_depth_texture = config.extensions.has("GL_OES_depth_texture") || config.extensions.has("WEBGL_depth_texture"); - config.use_rgba_3d_shadows = !config.support_depth_texture; - config.support_depth_cubemaps = config.extensions.has("GL_OES_depth_texture_cube_map"); -#endif - -#ifdef GLES_OVER_GL - config.support_32_bits_indices = true; -#else - config.support_32_bits_indices = config.extensions.has("GL_OES_element_index_uint"); -#endif - -#ifdef GLES_OVER_GL - config.support_write_depth = true; -#elif defined(JAVASCRIPT_ENABLED) - config.support_write_depth = false; -#else - config.support_write_depth = config.extensions.has("GL_EXT_frag_depth"); -#endif - - config.support_half_float_vertices = true; -//every platform should support this except web, iOS has issues with their support, so add option to disable -#ifdef JAVASCRIPT_ENABLED - config.support_half_float_vertices = false; -#endif - bool disable_half_float = false; //GLOBAL_GET("rendering/opengl/compatibility/disable_half_float"); - if (disable_half_float) { - config.support_half_float_vertices = false; - } - - config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); - config.latc_supported = config.extensions.has("GL_EXT_texture_compression_latc"); - config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc"); - config.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc") || config.extensions.has("GL_ARB_texture_compression_rgtc") || config.extensions.has("EXT_texture_compression_rgtc"); - config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc") || config.extensions.has("EXT_texture_compression_bptc"); - config.srgb_decode_supported = config.extensions.has("GL_EXT_texture_sRGB_decode"); - //determine formats for depth textures (or renderbuffers) - if (config.support_depth_texture) { - // Will use texture for depth - // have to manually see if we can create a valid framebuffer texture using UNSIGNED_INT, - // as there is no extension to test for this. - GLuint fbo; - glGenFramebuffers(1, &fbo); - bind_framebuffer(fbo); - GLuint depth; - glGenTextures(1, &depth); - glBindTexture(GL_TEXTURE_2D, depth); - glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, config.depth_type, 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, depth, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - bind_framebuffer_system(); - glDeleteFramebuffers(1, &fbo); - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &depth); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - // If it fails, test to see if it supports a framebuffer texture using UNSIGNED_SHORT - // This is needed because many OSX devices don't support either UNSIGNED_INT or UNSIGNED_SHORT -#ifdef GLES_OVER_GL - config.depth_internalformat = GL_DEPTH_COMPONENT16; -#else - // OES_depth_texture extension only specifies GL_DEPTH_COMPONENT. - config.depth_internalformat = GL_DEPTH_COMPONENT; -#endif - config.depth_type = GL_UNSIGNED_SHORT; - - glGenFramebuffers(1, &fbo); - bind_framebuffer(fbo); - - glGenTextures(1, &depth); - glBindTexture(GL_TEXTURE_2D, depth); - glTexImage2D(GL_TEXTURE_2D, 0, config.depth_internalformat, 32, 32, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_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_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, depth, 0); - - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - //if it fails again depth textures aren't supported, use rgba shadows and renderbuffer for depth - config.support_depth_texture = false; - config.use_rgba_3d_shadows = true; - } - - bind_framebuffer_system(); - glDeleteFramebuffers(1, &fbo); - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &depth); - } - } - - //picky requirements for these - config.support_shadow_cubemaps = config.support_depth_texture && config.support_write_depth && config.support_depth_cubemaps; - - frame.count = 0; - frame.delta = 0; - frame.current_rt = nullptr; - frame.clear_request = false; - - glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &config.max_vertex_texture_image_units); - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); - - // the use skeleton software path should be used if either float texture is not supported, - // OR max_vertex_texture_image_units is zero - config.use_skeleton_software = (config.float_texture_supported == false) || (config.max_vertex_texture_image_units == 0); - - shaders.copy.initialize(); - shaders.copy_version = shaders.copy.version_create(); //TODO - shaders.copy.version_bind_shader(shaders.copy_version, CopyShaderGLES3::MODE_COPY_SECTION); - //shaders.cubemap_filter.init(); - //bool ggx_hq = GLOBAL_GET("rendering/quality/reflections/high_quality_ggx"); - //shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES3::LOW_QUALITY, !ggx_hq); - - { - // quad for copying stuff - - glGenBuffers(1, &resources.quadie); - glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); - { - const float qv[16] = { - -1, - -1, - 0, - 0, - -1, - 1, - 0, - 1, - 1, - 1, - 1, - 1, - 1, - -1, - 1, - 0, - }; - - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - { - //default textures - - glGenTextures(1, &resources.white_tex); - unsigned char whitetexdata[8 * 8 * 3]; - for (int i = 0; i < 8 * 8 * 3; i++) { - whitetexdata[i] = 255; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, resources.white_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, whitetexdata); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - - glGenTextures(1, &resources.black_tex); - unsigned char blacktexdata[8 * 8 * 3]; - for (int i = 0; i < 8 * 8 * 3; i++) { - blacktexdata[i] = 0; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, resources.black_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, blacktexdata); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - - glGenTextures(1, &resources.normal_tex); - unsigned char normaltexdata[8 * 8 * 3]; - for (int i = 0; i < 8 * 8 * 3; i += 3) { - normaltexdata[i + 0] = 128; - normaltexdata[i + 1] = 128; - normaltexdata[i + 2] = 255; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, resources.normal_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, normaltexdata); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - - glGenTextures(1, &resources.aniso_tex); - unsigned char anisotexdata[8 * 8 * 3]; - for (int i = 0; i < 8 * 8 * 3; i += 3) { - anisotexdata[i + 0] = 255; - anisotexdata[i + 1] = 128; - anisotexdata[i + 2] = 0; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, resources.aniso_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, anisotexdata); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - } - - // skeleton buffer - { - resources.skeleton_transform_buffer_size = 0; - glGenBuffers(1, &resources.skeleton_transform_buffer); - } - - // radical inverse vdc cache texture - // used for cubemap filtering - if (true /*||config.float_texture_supported*/) { //uint8 is similar and works everywhere - 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 - //this needs to be enabled manually in OpenGL 2.1 - - if (config.extensions.has("GL_ARB_seamless_cube_map")) { - glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); - } - glEnable(GL_POINT_SPRITE); - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); -#endif - - config.force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); - config.use_fast_texture_filter = false; //GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter"); - //config.should_orphan = GLOBAL_GET("rendering/options/api_usage_legacy/orphan_buffers"); -} - -void RasterizerStorageGLES3::finalize() { -} - -void RasterizerStorageGLES3::_copy_screen() { - bind_quad_array(); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); -} - -void RasterizerStorageGLES3::update_memory_info() { -} - -uint64_t RasterizerStorageGLES3::get_rendering_info(RS::RenderingInfo p_info) { - return 0; -} - -void RasterizerStorageGLES3::update_dirty_resources() { - update_dirty_shaders(); - update_dirty_materials(); - // update_dirty_skeletons(); - // update_dirty_multimeshes(); -} - -RasterizerStorageGLES3::RasterizerStorageGLES3() { - RasterizerStorageGLES3::system_fbo = 0; - config.should_orphan = true; -} - -RasterizerStorageGLES3::~RasterizerStorageGLES3() { - shaders.copy.version_free(shaders.copy_version); -} - -#endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h deleted file mode 100644 index 0dfc909777..0000000000 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ /dev/null @@ -1,1438 +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 "shaders/copy.glsl.gen.h" - -class RasterizerCanvasGLES3; -class RasterizerSceneGLES3; - -class RasterizerStorageGLES3 : public RendererStorage { - friend class RasterizerGLES3; - - Thread::ID _main_thread_id = 0; - bool _is_main_thread(); - -public: - RasterizerCanvasGLES3 *canvas; - RasterizerSceneGLES3 *scene; - - static GLuint system_fbo; - - struct Config { - bool shrink_textures_x2; - bool use_fast_texture_filter; - bool use_skeleton_software; - - int max_vertex_texture_image_units; - int max_texture_image_units; - int max_texture_size; - - // TODO implement wireframe in OpenGL - // bool generate_wireframes; - - Set<String> extensions; - - bool float_texture_supported; - bool s3tc_supported; - bool latc_supported; - bool rgtc_supported; - bool bptc_supported; - bool etc_supported; - bool etc2_supported; - bool srgb_decode_supported; - - bool keep_original_textures; - - bool force_vertex_shading; - - bool use_rgba_2d_shadows; - bool use_rgba_3d_shadows; - - bool support_32_bits_indices; - bool support_write_depth; - bool support_half_float_vertices; - bool support_npot_repeat_mipmap; - bool support_depth_texture; - bool support_depth_cubemaps; - - bool support_shadow_cubemaps; - - bool render_to_mipmap_supported; - - GLuint depth_internalformat; - GLuint depth_type; - GLuint depth_buffer_internalformat; - - // in some cases the legacy render didn't orphan. We will mark these - // so the user can switch orphaning off for them. - bool should_orphan; - } config; - - struct Resources { - GLuint white_tex; - GLuint black_tex; - GLuint normal_tex; - GLuint aniso_tex; - - GLuint mipmap_blur_fbo; - GLuint mipmap_blur_color; - - GLuint radical_inverse_vdc_cache_tex; - bool use_rgba_2d_shadows; - - GLuint quadie; - - size_t skeleton_transform_buffer_size; - GLuint skeleton_transform_buffer; - LocalVector<float> skeleton_transform_cpu_buffer; - - } resources; - - mutable struct Shaders { - ShaderCompiler compiler; - - CopyShaderGLES3 copy; - RID copy_version; - //CubemapFilterShaderGLES3 cubemap_filter; - - ShaderCompiler::IdentifierActions actions_canvas; - ShaderCompiler::IdentifierActions actions_scene; - ShaderCompiler::IdentifierActions actions_particles; - - } shaders; - - 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; - - void bind_quad_array() const; - - ///////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////API//////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////////// - - bool can_create_resources_async() const override; - - // TEXTURE API - - enum OpenGLTextureFlags { - TEXTURE_FLAG_MIPMAPS = 1, /// Enable automatic mipmap generation - when available - TEXTURE_FLAG_REPEAT = 2, /// Repeat texture (Tiling), otherwise Clamping - TEXTURE_FLAG_FILTER = 4, /// Create texture with linear (or available) filter - TEXTURE_FLAG_ANISOTROPIC_FILTER = 8, - TEXTURE_FLAG_CONVERT_TO_LINEAR = 16, - TEXTURE_FLAG_MIRRORED_REPEAT = 32, /// Repeat texture, with alternate sections mirrored - TEXTURE_FLAG_USED_FOR_STREAMING = 2048, - TEXTURE_FLAGS_DEFAULT = TEXTURE_FLAG_REPEAT | TEXTURE_FLAG_MIPMAPS | TEXTURE_FLAG_FILTER - }; - - /* CANVAS TEXTURE API (2D) */ - - struct CanvasTexture { - RID diffuse; - RID normal_map; - RID specular; - Color specular_color = Color(1, 1, 1, 1); - float shininess = 1.0; - - 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; - }; - - RID_Owner<CanvasTexture, true> canvas_texture_owner; - - struct RenderTarget; - - struct Texture { - RID self; - - Texture *proxy; - Set<Texture *> proxy_owners; - - String path; - uint32_t flags; - int width, height, depth; - int alloc_width, alloc_height; - Image::Format format; - RenderingDevice::TextureType type; - - GLenum target; - GLenum gl_format_cache; - GLenum gl_internal_format_cache; - GLenum gl_type_cache; - - int data_size; - int total_data_size; - bool ignore_mipmaps; - - bool compressed; - - bool srgb; - - int mipmaps; - - bool resize_to_po2; - - bool active; - GLenum tex_id; - - uint16_t stored_cube_sides; - - RenderTarget *render_target; - - Vector<Ref<Image>> images; - - bool redraw_if_visible; - - RS::TextureDetectCallback detect_3d; - void *detect_3d_ud; - - RS::TextureDetectCallback detect_srgb; - void *detect_srgb_ud; - - RS::TextureDetectCallback detect_normal; - void *detect_normal_ud; - - CanvasTexture *canvas_texture = nullptr; - - // some silly opengl shenanigans where - // texture coords start from bottom left, means we need to draw render target textures upside down - // to be compatible with vulkan etc. - bool is_upside_down() const { - if (proxy) { - return proxy->is_upside_down(); - } - - return render_target != nullptr; - } - - Texture() { - create(); - } - - _ALWAYS_INLINE_ Texture *get_ptr() { - if (proxy) { - return proxy; //->get_ptr(); only one level of indirection, else not inlining possible. - } else { - return this; - } - } - - ~Texture() { - destroy(); - - if (tex_id != 0) { - glDeleteTextures(1, &tex_id); - } - } - - void copy_from(const Texture &o) { - proxy = o.proxy; - flags = o.flags; - width = o.width; - height = o.height; - alloc_width = o.alloc_width; - alloc_height = o.alloc_height; - format = o.format; - type = o.type; - target = o.target; - data_size = o.data_size; - total_data_size = o.total_data_size; - ignore_mipmaps = o.ignore_mipmaps; - compressed = o.compressed; - mipmaps = o.mipmaps; - resize_to_po2 = o.resize_to_po2; - active = o.active; - tex_id = o.tex_id; - stored_cube_sides = o.stored_cube_sides; - render_target = o.render_target; - redraw_if_visible = o.redraw_if_visible; - detect_3d = o.detect_3d; - detect_3d_ud = o.detect_3d_ud; - detect_srgb = o.detect_srgb; - detect_srgb_ud = o.detect_srgb_ud; - detect_normal = o.detect_normal; - detect_normal_ud = o.detect_normal_ud; - - images.clear(); - } - - void create() { - proxy = nullptr; - flags = 0; - width = 0; - height = 0; - alloc_width = 0; - alloc_height = 0; - format = Image::FORMAT_L8; - type = RenderingDevice::TEXTURE_TYPE_2D; - target = 0; - data_size = 0; - total_data_size = 0; - ignore_mipmaps = false; - compressed = false; - mipmaps = 0; - resize_to_po2 = false; - active = false; - tex_id = 0; - stored_cube_sides = 0; - render_target = nullptr; - redraw_if_visible = false; - detect_3d = nullptr; - detect_3d_ud = nullptr; - detect_srgb = nullptr; - detect_srgb_ud = nullptr; - detect_normal = nullptr; - detect_normal_ud = nullptr; - } - void destroy() { - images.clear(); - - for (Set<Texture *>::Element *E = proxy_owners.front(); E; E = E->next()) { - E->get()->proxy = nullptr; - } - - if (proxy) { - proxy->proxy_owners.erase(this); - } - } - - // texture state - void GLSetFilter(GLenum p_target, RS::CanvasItemTextureFilter p_filter) { - if (p_filter == state_filter) { - return; - } - state_filter = p_filter; - GLint pmin = GL_LINEAR; // param min - GLint pmag = GL_LINEAR; // param mag - switch (state_filter) { - default: { - } break; - case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { - pmin = GL_LINEAR_MIPMAP_LINEAR; - pmag = GL_LINEAR; - } break; - case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { - pmin = GL_NEAREST; - pmag = GL_NEAREST; - } break; - case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { - pmin = GL_NEAREST_MIPMAP_NEAREST; - pmag = GL_NEAREST; - } break; - } - glTexParameteri(p_target, GL_TEXTURE_MIN_FILTER, pmin); - glTexParameteri(p_target, GL_TEXTURE_MAG_FILTER, pmag); - } - void GLSetRepeat(GLenum p_target, RS::CanvasItemTextureRepeat p_repeat) { - if (p_repeat == state_repeat) { - return; - } - state_repeat = p_repeat; - GLint prep = GL_CLAMP_TO_EDGE; // parameter repeat - switch (state_repeat) { - default: { - } break; - case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { - prep = GL_REPEAT; - } break; - case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { - prep = GL_MIRRORED_REPEAT; - } break; - } - glTexParameteri(p_target, GL_TEXTURE_WRAP_S, prep); - glTexParameteri(p_target, GL_TEXTURE_WRAP_T, prep); - } - - private: - RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; - RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; - }; - - mutable RID_PtrOwner<Texture> texture_owner; - - Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, 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; - - void _texture_set_state_from_flags(Texture *p_tex); - - // new - RID texture_allocate() override; - void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override; - void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override; - 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; - void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent - - void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override; - void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override {} - void texture_proxy_update(RID p_proxy, RID p_base) override {} - - void texture_2d_placeholder_initialize(RID p_texture) override; - void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override; - void texture_3d_placeholder_initialize(RID p_texture) override; - - Ref<Image> texture_2d_get(RID p_texture) const override; - Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref<Image>(); } - Vector<Ref<Image>> texture_3d_get(RID p_texture) const override { return Vector<Ref<Image>>(); } - - void texture_replace(RID p_texture, RID p_by_texture) override; - //void texture_set_size_override(RID p_texture, int p_width, int p_height) override {} - - void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} - void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} - - // old - uint32_t texture_get_width(RID p_texture) const; - uint32_t texture_get_height(RID p_texture) const; - -private: - RID texture_create(); - - //void texture_allocate(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, RenderingDevice::TextureType p_type, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT); - void _texture_allocate_internal(RID p_texture, int p_width, int p_height, int p_depth_3d, Image::Format p_format, RenderingDevice::TextureType p_type, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT); - - 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; - void texture_set_flags(RID p_texture, uint32_t p_flags); - uint32_t texture_get_flags(RID p_texture) const; - Image::Format texture_get_format(RID p_texture) const; - RenderingDevice::TextureType texture_get_type(RID p_texture) const; - uint32_t texture_get_texid(RID p_texture) const; - uint32_t texture_get_depth(RID p_texture) const; - void texture_set_size_override(RID p_texture, int p_width, int p_height) override; - - void texture_bind(RID p_texture, uint32_t p_texture_no); - - void texture_set_path(RID p_texture, const String &p_path) override; - String texture_get_path(RID p_texture) const override; - - void texture_set_shrink_all_x2_on_set_data(bool p_enable); - - void texture_debug_usage(List<RS::TextureInfo> *r_info) override; - - RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; - - void textures_keep_original(bool p_enable); - - void texture_set_proxy(RID p_texture, RID p_proxy); - Size2 texture_size_with_proxy(RID p_texture) override; - - void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; - void texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata); - void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; - void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override {} - - void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override; - -public: - RID canvas_texture_allocate() override; - void canvas_texture_initialize(RID p_rid) override; - - void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override; - void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) override; - - void canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) override; - void canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) override; - - /* SKY API */ - // not sure if used in godot 4? - struct Sky { - RID self; - RID panorama; - GLuint radiance; - int radiance_size; - }; - - mutable RID_PtrOwner<Sky> sky_owner; - - RID sky_create(); - void sky_set_texture(RID p_sky, RID p_panorama, int p_radiance_size); - - // SHADER API - - struct Material; - - struct Shader { - RID self; - - RS::ShaderMode mode; - ShaderGLES3 *shader; - String code; - SelfList<Material>::List materials; - - Map<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; - - RID version; - - SelfList<Shader> dirty_list; - - Map<StringName, Map<int, RID>> default_textures; - - Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; - - bool valid; - - String path; - - uint32_t index; - uint64_t last_pass; - - struct CanvasItem { - enum BlendMode { - BLEND_MODE_MIX, - BLEND_MODE_ADD, - BLEND_MODE_SUB, - BLEND_MODE_MUL, - BLEND_MODE_PMALPHA, - }; - - int blend_mode; - - enum LightMode { - LIGHT_MODE_NORMAL, - LIGHT_MODE_UNSHADED, - LIGHT_MODE_LIGHT_ONLY - }; - - int light_mode; - - bool uses_screen_texture; - bool uses_screen_uv; - bool uses_time; - bool uses_modulate; - bool uses_color; - bool uses_vertex; - - // all these should disable item joining if used in a custom shader - bool uses_world_matrix; - bool uses_extra_matrix; - bool uses_projection_matrix; - bool uses_instance_custom; - - } canvas_item; - - struct Spatial { - enum BlendMode { - BLEND_MODE_MIX, - BLEND_MODE_ADD, - BLEND_MODE_SUB, - BLEND_MODE_MUL, - }; - - int blend_mode; - - enum DepthDrawMode { - DEPTH_DRAW_OPAQUE, - DEPTH_DRAW_ALWAYS, - DEPTH_DRAW_NEVER, - DEPTH_DRAW_ALPHA_PREPASS, - }; - - int depth_draw_mode; - - enum CullMode { - CULL_MODE_FRONT, - CULL_MODE_BACK, - CULL_MODE_DISABLED, - }; - - int cull_mode; - - bool uses_alpha; - bool uses_alpha_scissor; - bool unshaded; - bool no_depth_test; - bool uses_vertex; - bool uses_discard; - bool uses_sss; - bool uses_screen_texture; - bool uses_depth_texture; - bool uses_time; - bool uses_tangent; - bool uses_ensure_correct_normals; - bool writes_modelview_or_projection; - bool uses_vertex_lighting; - bool uses_world_coordinates; - - } spatial; - - struct Particles { - } particles; - - bool uses_vertex_time; - bool uses_fragment_time; - - Shader() : - dirty_list(this) { - shader = nullptr; - valid = false; - version = RID(); - last_pass = 0; - } - }; - - mutable RID_PtrOwner<Shader> shader_owner; - mutable SelfList<Shader>::List _shader_dirty_list; - - void _shader_make_dirty(Shader *p_shader); - - RID shader_allocate() override; - void shader_initialize(RID p_rid) override; - - //RID shader_create() override; - - void shader_set_code(RID p_shader, const String &p_code) override; - String shader_get_code(RID p_shader) const override; - void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; - - void shader_set_default_texture_param(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override; - RID shader_get_default_texture_param(RID p_shader, const StringName &p_name, int p_index) const override; - - RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); }; - - void _update_shader(Shader *p_shader) const; - void update_dirty_shaders(); - - // new - Variant shader_get_param_default(RID p_material, const StringName &p_param) const override { return Variant(); } - - // COMMON MATERIAL API - - struct Material { - RID self; - Shader *shader; - Map<StringName, Variant> params; - SelfList<Material> list; - SelfList<Material> dirty_list; - Vector<Pair<StringName, RID>> textures; - float line_width; - int render_priority; - - RID next_pass; - - uint32_t index; - uint64_t last_pass; - - // Map<Geometry *, int> geometry_owners; - // Map<InstanceBaseDependency *, int> instance_owners; - - bool can_cast_shadow_cache; - bool is_animated_cache; - - Material() : - list(this), - dirty_list(this) { - can_cast_shadow_cache = false; - is_animated_cache = false; - shader = nullptr; - line_width = 1.0; - last_pass = 0; - render_priority = 0; - } - }; - - mutable SelfList<Material>::List _material_dirty_list; - void _material_make_dirty(Material *p_material) const; - - // void _material_add_geometry(RID p_material, Geometry *p_geometry); - // void _material_remove_geometry(RID p_material, Geometry *p_geometry); - - void _update_material(Material *p_material); - - mutable RID_PtrOwner<Material> material_owner; - - // new - void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override {} - void material_update_dependency(RID p_material, DependencyTracker *p_instance) override {} - - // old - RID material_allocate() override; - void material_initialize(RID p_rid) override; - - //RID material_create() override; - - void material_set_shader(RID p_material, RID p_shader) override; - RID material_get_shader(RID p_material) const; - - void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override; - Variant material_get_param(RID p_material, const StringName &p_param) const override; - Variant material_get_param_default(RID p_material, const StringName &p_param) const; - - void material_set_line_width(RID p_material, float p_width); - void material_set_next_pass(RID p_material, RID p_next_material) override; - - bool material_is_animated(RID p_material) override; - bool material_casts_shadows(RID p_material) override; - bool material_uses_tangents(RID p_material); - bool material_uses_ensure_correct_normals(RID p_material); - - void material_add_instance_owner(RID p_material, DependencyTracker *p_instance); - void material_remove_instance_owner(RID p_material, DependencyTracker *p_instance); - - void material_set_render_priority(RID p_material, int priority) override; - - void update_dirty_materials(); - - /* MESH API */ - - RID mesh_allocate() override; - void mesh_initialize(RID p_rid) override; - void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override; - bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override; - RID mesh_instance_create(RID p_base) override; - void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; - void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; - void mesh_instance_check_for_update(RID p_mesh_instance) override; - void update_mesh_instances() override; - void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; - float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override; - - void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override; - - int mesh_get_blend_shape_count(RID p_mesh) const override; - - void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override; - RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override; - - void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; - void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; - void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; - - void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override; - RID mesh_surface_get_material(RID p_mesh, int p_surface) const override; - - RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; - int mesh_get_surface_count(RID p_mesh) const override; - - void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; - AABB mesh_get_custom_aabb(RID p_mesh) const override; - - AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override; - void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override; - void mesh_clear(RID p_mesh) override; - - /* MULTIMESH API */ - - struct MultiMesh { - RID mesh; - int instances = 0; - RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D; - bool uses_colors = false; - bool uses_custom_data = false; - int visible_instances = -1; - AABB aabb; - bool aabb_dirty = false; - bool buffer_set = false; - uint32_t stride_cache = 0; - uint32_t color_offset_cache = 0; - uint32_t custom_data_offset_cache = 0; - - Vector<float> data_cache; //used if individual setting is used - bool *data_cache_dirty_regions = nullptr; - uint32_t data_cache_used_dirty_regions = 0; - - RID buffer; //storage buffer - RID uniform_set_3d; - RID uniform_set_2d; - - bool dirty = false; - MultiMesh *dirty_list = nullptr; - - Dependency dependency; - }; - - mutable RID_Owner<MultiMesh, true> multimesh_owner; - - MultiMesh *multimesh_dirty_list = nullptr; - - _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; - _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); - _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); - _FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances); - void _update_dirty_multimeshes(); - - RID multimesh_allocate() override; - void multimesh_initialize(RID p_rid) override; - void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; - int multimesh_get_instance_count(RID p_multimesh) const override; - - void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; - void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; - void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; - void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; - void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; - - RID multimesh_get_mesh(RID p_multimesh) const override; - AABB multimesh_get_aabb(RID p_multimesh) const override; - - Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; - Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; - Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; - Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; - void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; - Vector<float> multimesh_get_buffer(RID p_multimesh) const override; - - void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; - int multimesh_get_visible_instances(RID p_multimesh) const override; - - _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - return multimesh->xform_format; - } - - _FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - return multimesh->uses_colors; - } - - _FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - return multimesh->uses_custom_data; - } - - _FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const { - MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); - if (multimesh->visible_instances >= 0) { - return multimesh->visible_instances; - } - return multimesh->instances; - } - - /* SKELETON API */ - - RID skeleton_allocate() override; - void skeleton_initialize(RID p_rid) override; - void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override; - void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override; - int skeleton_get_bone_count(RID p_skeleton) const override; - void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override; - Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override; - void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override; - Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override; - - /* Light API */ - - RID directional_light_allocate() override; - void directional_light_initialize(RID p_rid) override; - RID omni_light_allocate() override; - void omni_light_initialize(RID p_rid) override; - RID spot_light_allocate() override; - void spot_light_initialize(RID p_rid) override; - RID reflection_probe_allocate() override; - void reflection_probe_initialize(RID p_rid) override; - - void light_set_color(RID p_light, const Color &p_color) override; - void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override; - void light_set_shadow(RID p_light, bool p_enabled) override; - void light_set_shadow_color(RID p_light, const Color &p_color) override; - void light_set_projector(RID p_light, RID p_texture) override; - void light_set_negative(RID p_light, bool p_enable) override; - void light_set_cull_mask(RID p_light, uint32_t p_mask) override; - void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override; - void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override; - void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override; - - void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override; - - void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override; - void light_directional_set_blend_splits(RID p_light, bool p_enable) override; - bool light_directional_get_blend_splits(RID p_light) const override; - void light_directional_set_sky_only(RID p_light, bool p_sky_only) override; - bool light_directional_is_sky_only(RID p_light) const override; - - RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override; - RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override; - - bool light_has_shadow(RID p_light) const override; - bool light_has_projector(RID p_light) const override; - - RS::LightType light_get_type(RID p_light) const override; - AABB light_get_aabb(RID p_light) const override; - float light_get_param(RID p_light, RS::LightParam p_param) override; - Color light_get_color(RID p_light) override; - RS::LightBakeMode light_get_bake_mode(RID p_light) override; - uint32_t light_get_max_sdfgi_cascade(RID p_light) override; - uint64_t light_get_version(RID p_light) const override; - - /* PROBE API */ - - void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override; - void reflection_probe_set_intensity(RID p_probe, float p_intensity) override; - void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override; - void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override; - void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override; - void reflection_probe_set_max_distance(RID p_probe, float p_distance) override; - void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) override; - void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override; - void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override; - void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override; - void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override; - void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override; - void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; - - AABB reflection_probe_get_aabb(RID p_probe) const override; - RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override; - uint32_t reflection_probe_get_cull_mask(RID p_probe) const override; - Vector3 reflection_probe_get_extents(RID p_probe) const override; - Vector3 reflection_probe_get_origin_offset(RID p_probe) const override; - float reflection_probe_get_origin_max_distance(RID p_probe) const override; - bool reflection_probe_renders_shadows(RID p_probe) const override; - - void base_update_dependency(RID p_base, DependencyTracker *p_instance) override; - void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override; - - /* DECAL API */ - - RID decal_allocate() override; - void decal_initialize(RID p_rid) override; - void decal_set_extents(RID p_decal, const Vector3 &p_extents) override; - void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override; - void decal_set_emission_energy(RID p_decal, float p_energy) override; - void decal_set_albedo_mix(RID p_decal, float p_mix) override; - void decal_set_modulate(RID p_decal, const Color &p_modulate) override; - void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override; - void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override; - void decal_set_fade(RID p_decal, float p_above, float p_below) override; - void decal_set_normal_fade(RID p_decal, float p_fade) override; - - AABB decal_get_aabb(RID p_decal) const 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; - - /* LIGHTMAP CAPTURE */ - RID lightmap_allocate() override; - void lightmap_initialize(RID p_rid) override; - void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override; - void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; - void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; - 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; - PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; - PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; - PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; - PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override; - AABB lightmap_get_aabb(RID p_lightmap) const override; - void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override; - bool lightmap_is_interior(RID p_lightmap) const override; - void lightmap_set_probe_capture_update_speed(float p_speed) override; - float lightmap_get_probe_capture_update_speed() const override; - - /* OCCLUDER */ - - void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices); - - /* PARTICLES */ - - RID particles_allocate() override; - void particles_initialize(RID p_rid) override; - void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override; - void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override; - void particles_set_emitting(RID p_particles, bool p_emitting) override; - void particles_set_amount(RID p_particles, int p_amount) override; - void particles_set_lifetime(RID p_particles, double p_lifetime) override; - void particles_set_one_shot(RID p_particles, bool p_one_shot) override; - void particles_set_pre_process_time(RID p_particles, double p_time) override; - void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override; - void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override; - void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override; - void particles_set_speed_scale(RID p_particles, double p_scale) override; - void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override; - void particles_set_process_material(RID p_particles, RID p_material) override; - void particles_set_fixed_fps(RID p_particles, int p_fps) override; - void particles_set_interpolate(RID p_particles, bool p_enable) override; - void particles_set_fractional_delta(RID p_particles, bool p_enable) override; - void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override; - void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override; - void particles_set_collision_base_size(RID p_particles, real_t p_size) override; - - void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override; - - void particles_set_trails(RID p_particles, bool p_enable, double p_length) override; - void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override; - - void particles_restart(RID p_particles) override; - - void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override; - - void particles_set_draw_passes(RID p_particles, int p_count) override; - void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override; - - void particles_request_process(RID p_particles) override; - AABB particles_get_current_aabb(RID p_particles) override; - AABB particles_get_aabb(RID p_particles) const override; - - void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override; - - bool particles_get_emitting(RID p_particles) override; - int particles_get_draw_passes(RID p_particles) const override; - RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override; - - void particles_add_collision(RID p_particles, RID p_instance) override; - void particles_remove_collision(RID p_particles, RID p_instance) override; - - void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; - - void update_particles() override; - - /* PARTICLES COLLISION */ - - RID particles_collision_allocate() override; - void particles_collision_initialize(RID p_rid) override; - void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override; - void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override; - void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override; - void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override; - void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override; - void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override; - void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override; - void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override; - void particles_collision_height_field_update(RID p_particles_collision) override; - void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; - AABB particles_collision_get_aabb(RID p_particles_collision) const override; - bool particles_collision_is_heightfield(RID p_particles_collision) const override; - RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; - - RID particles_collision_instance_create(RID p_collision) override; - void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override; - void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override; - - /* 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; - - /* GLOBAL VARIABLES */ - - void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) override; - void global_variable_remove(const StringName &p_name) override; - Vector<StringName> global_variable_get_list() const override; - - void global_variable_set(const StringName &p_name, const Variant &p_value) override; - void global_variable_set_override(const StringName &p_name, const Variant &p_value) override; - Variant global_variable_get(const StringName &p_name) const override; - RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override; - - void global_variables_load_settings(bool p_load_textures = true) override; - void global_variables_clear() override; - - int32_t global_variables_instance_allocate(RID p_instance) override; - void global_variables_instance_free(RID p_instance) override; - void global_variables_instance_update(RID p_instance, int p_index, const Variant &p_value) override; - - bool particles_is_inactive(RID p_particles) const override; - - // RENDER TARGET - - struct RenderTarget { - RID self; - GLuint fbo = 0; - GLuint color = 0; - GLuint depth = 0; - - GLuint multisample_fbo = 0; - GLuint multisample_color = 0; - GLuint multisample_depth = 0; - bool multisample_active = false; - - struct Effect { - GLuint fbo = 0; - int width = 0; - int height = 0; - - GLuint color = 0; - - Effect() { - } - }; - - Effect copy_screen_effect; - - struct MipMaps { - struct Size { - GLuint fbo; - GLuint color; - int width; - int height; - }; - - Vector<Size> sizes; - GLuint color = 0; - int levels = 0; - - MipMaps() { - } - }; - - MipMaps mip_maps[2]; - - struct External { - GLuint fbo = 0; - GLuint color = 0; - GLuint depth = 0; - RID texture; - - External() { - } - } external; - - int x = 0, y = 0, width = 0, height = 0; - - bool flags[RENDER_TARGET_FLAG_MAX]; - - // instead of allocating sized render targets immediately, - // defer this for faster startup - bool allocate_is_dirty = false; - bool used_in_frame = false; - RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; - - bool use_fxaa = false; - bool use_debanding = false; - - RID texture; - - bool used_dof_blur_near = false; - bool mip_maps_allocated = false; - - Color clear_color = Color(1, 1, 1, 1); - bool clear_requested = false; - - RenderTarget() { - for (int i = 0; i < RENDER_TARGET_FLAG_MAX; ++i) { - flags[i] = false; - } - external.fbo = 0; - } - }; - - mutable RID_PtrOwner<RenderTarget> render_target_owner; - - void _render_target_clear(RenderTarget *rt); - void _render_target_allocate(RenderTarget *rt); - void _set_current_render_target(RID p_render_target); - - RID render_target_create() override; - void render_target_set_position(RID p_render_target, int p_x, int p_y) override; - 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); - RID render_target_get_texture(RID p_render_target) override; - void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; - - void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override; - bool render_target_was_used(RID p_render_target) override; - void render_target_clear_used(RID p_render_target); - void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa); - void render_target_set_use_fxaa(RID p_render_target, bool p_fxaa); - void render_target_set_use_debanding(RID p_render_target, bool p_debanding); - - // new - void render_target_set_as_unused(RID p_render_target) override { - render_target_clear_used(p_render_target); - } - - void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override; - bool render_target_is_clear_requested(RID p_render_target) override; - Color render_target_get_clear_request_color(RID p_render_target) override; - 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; - - // access from canvas - // 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; - - struct Frame { - RenderTarget *current_rt; - - // these 2 may have been superseded by the equivalents in the render target. - // these may be able to be removed. - bool clear_request; - Color clear_request_color; - - float time; - float delta; - uint64_t count; - - Frame() { - // current_rt = nullptr; - // clear_request = false; - } - } frame; - - void initialize(); - void finalize(); - - void _copy_screen(); - - 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; - - 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(); - } - - // make access easier to these - struct Dimensions { - // render target - int rt_width; - int rt_height; - - // window - int win_width; - int win_height; - Dimensions() { - rt_width = 0; - rt_height = 0; - win_width = 0; - win_height = 0; - } - } _dims; - - void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false) const; - bool safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const; - - void bind_framebuffer(GLuint framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - } - - void bind_framebuffer_system() { - glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo); - } - - RasterizerStorageGLES3(); - ~RasterizerStorageGLES3(); -}; - -inline bool RasterizerStorageGLES3::safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const { - r_offset_after = p_offset + p_data_size; -#ifdef DEBUG_ENABLED - // we are trying to write across the edge of the buffer - if (r_offset_after > p_total_buffer_size) { - return false; - } -#endif - glBufferSubData(p_target, p_offset, p_data_size, p_data); - return true; -} - -// standardize the orphan / upload in one place so it can be changed per platform as necessary, and avoid future -// bugs causing pipeline stalls -inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) const { - // Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData - // Was previously #ifndef GLES_OVER_GL however this causes stalls on desktop mac also (and possibly other) - if (!p_optional_orphan || (config.should_orphan)) { - glBufferData(p_target, p_buffer_size, nullptr, p_usage); -#ifdef RASTERIZER_EXTRA_CHECKS - // fill with garbage off the end of the array - if (p_buffer_size) { - unsigned int start = p_offset + p_data_size; - unsigned int end = start + 1024; - if (end < p_buffer_size) { - uint8_t *garbage = (uint8_t *)alloca(1024); - for (int n = 0; n < 1024; n++) { - garbage[n] = Math::random(0, 255); - } - glBufferSubData(p_target, start, 1024, garbage); - } - } -#endif - } - glBufferSubData(p_target, p_offset, p_data_size, p_data); -} - -#endif // GLES3_ENABLED - -#endif // RASTERIZER_STORAGE_OPENGL_H diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 7ae8b4e3bf..21ccef3518 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "shader_gles3.h" + #ifdef GLES3_ENABLED #include "core/io/compression.h" @@ -164,11 +165,21 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("\n"); //make sure defines begin at newline builder.append(general_defines.get_data()); builder.append(variant_defines[p_variant]); + builder.append("\n"); for (int j = 0; j < p_version->custom_defines.size(); j++) { builder.append(p_version->custom_defines[j].get_data()); } builder.append("\n"); //make sure defines begin at newline + // Default to highp precision unless specified otherwise. + builder.append("precision highp float;\n"); + builder.append("precision highp int;\n"); +#ifndef GLES_OVER_GL + builder.append("precision highp sampler2D;\n"); + builder.append("precision highp samplerCube;\n"); + 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]; switch (chunk.type) { @@ -326,7 +337,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ glDeleteProgram(spec.id); spec.id = 0; - ERR_PRINT("No OpenGL program link log. What the frick?"); + ERR_PRINT("No OpenGL program link log. Something is wrong."); ERR_FAIL(); } @@ -463,8 +474,8 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) { String sha1 = _version_get_sha1(p_version); String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; - FileAccessRef f = FileAccess::open(path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); + if (f.is_null()) { return false; } @@ -529,8 +540,8 @@ void ShaderGLES3::_save_to_cache(Version *p_version) { String sha1 = _version_get_sha1(p_version); String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache"; - FileAccessRef f = FileAccess::open(path, FileAccess::WRITE); - ERR_FAIL_COND(!f); + Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE); + ERR_FAIL_COND(f.is_null()); f->store_buffer((const uint8_t *)shader_file_header, 4); f->store_32(cache_file_version); //file version uint32_t variant_count = variant_count; @@ -540,8 +551,6 @@ void ShaderGLES3::_save_to_cache(Version *p_version) { f->store_32(p_version->variant_data[i].size()); //stage count f->store_buffer(p_version->variant_data[i].ptr(), p_version->variant_data[i].size()); } - - f->close(); #endif } @@ -553,7 +562,7 @@ void ShaderGLES3::_clear_version(Version *p_version) { for (int i = 0; i < variant_count; i++) { for (OAHashMap<uint64_t, Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) { - if (it.valid) { + if (it.value->id != 0) { glDeleteShader(it.value->vert_id); glDeleteShader(it.value->frag_id); glDeleteProgram(it.value->id); @@ -576,7 +585,7 @@ void ShaderGLES3::_initialize_version(Version *p_version) { } } -void ShaderGLES3::version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize) { +void ShaderGLES3::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize) { Version *version = version_owner.get_or_null(p_version); ERR_FAIL_COND(!version); @@ -643,8 +652,8 @@ void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture base_sha256 = hash_build.as_string().sha256_text(); - DirAccessRef d = DirAccess::open(shader_cache_dir); - ERR_FAIL_COND(!d); + Ref<DirAccess> d = DirAccess::open(shader_cache_dir); + ERR_FAIL_COND(d.is_null()); if (d->change_dir(name) != OK) { Error err = d->make_dir(name); ERR_FAIL_COND(err != OK); @@ -664,7 +673,7 @@ void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture print_verbose("Shader '" + name + "' SHA256: " + base_sha256); } - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units); } void ShaderGLES3::set_shader_cache_dir(const String &p_dir) { diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index f344ea047f..2b72549b5b 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -28,14 +28,15 @@ /* 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/projection.h" #include "core/os/mutex.h" #include "core/string/string_builder.h" #include "core/templates/hash_map.h" #include "core/templates/local_vector.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "core/templates/rid_owner.h" #include "core/variant/variant.h" #include "servers/rendering_server.h" @@ -73,16 +74,17 @@ private: //versions CharString general_defines; - // A version is a high-level construct which is a combination of built-in and user-defined shader code - // Variants use #idefs to toggle behaviour on and off to change behaviour of the shader + // 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 + // 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) + // 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) struct Version { Vector<StringName> texture_uniforms; CharString uniforms; CharString vertex_globals; CharString fragment_globals; - Map<StringName, CharString> code_sections; + HashMap<StringName, CharString> code_sections; Vector<CharString> custom_defines; struct Specialization { @@ -91,7 +93,7 @@ private: GLuint frag_id; LocalVector<GLint> uniform_location; LocalVector<GLint> texture_uniform_locations; - Map<StringName, GLint> custom_uniform_locations; + HashMap<StringName, GLint> custom_uniform_locations; bool build_queued = false; bool ok = false; Specialization() { @@ -111,7 +113,7 @@ private: void _clear_version(Version *p_version); void _initialize_version(Version *p_version); - RID_Owner<Version> version_owner; + RID_Owner<Version, true> version_owner; struct StageTemplate { struct Chunk { @@ -141,7 +143,7 @@ private: static bool shader_cache_save_debug; bool shader_cache_dir_valid = false; - GLint max_image_units; + GLint max_image_units = 0; enum StageType { STAGE_TYPE_VERTEX, @@ -217,7 +219,11 @@ protected: ERR_FAIL_INDEX_V(p_which, uniform_count, -1); Version *version = version_owner.get_or_null(p_version); ERR_FAIL_COND_V(!version, -1); - return version->variants[p_variant].lookup_ptr(p_specialization)->uniform_location[p_which]; + ERR_FAIL_INDEX_V(p_variant, int(version->variants.size()), -1); + Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization); + ERR_FAIL_COND_V(!spec, -1); + ERR_FAIL_INDEX_V(p_which, int(spec->uniform_location.size()), -1); + return spec->uniform_location[p_which]; } virtual void _init() = 0; @@ -225,7 +231,7 @@ protected: public: RID version_create(); - void version_set_code(RID p_version, const Map<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize = false); + void version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const Vector<StringName> &p_texture_uniforms, bool p_initialize = false); bool version_is_valid(RID p_version); @@ -242,5 +248,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 2f56b77bdc..83ffe8b1e1 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -3,5 +3,17 @@ Import("env") if "GLES3_GLSL" in env["BUILDERS"]: + # find all include files + gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + + # find all shader code(all glsl files excluding our include files) + 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 + ["#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") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index a18c451858..4df818cd4c 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -5,6 +5,7 @@ mode_quad = mode_ninepatch = #define USE_NINEPATCH mode_primitive = #define USE_PRIMITIVE mode_attributes = #define USE_ATTRIBUTES +mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING #[specializations] @@ -20,6 +21,23 @@ layout(location = 4) in vec2 uv_attrib; layout(location = 10) in uvec4 bone_attrib; layout(location = 11) in vec4 weight_attrib; +#ifdef USE_INSTANCING + +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 + +#endif + +// This needs to be outside clang-format so the ubo comment is in the right place +#ifdef MATERIAL_UNIFORMS_USED +layout(std140) uniform MaterialUniforms{ //ubo:4 + +#MATERIAL_UNIFORMS + +}; #endif /* clang-format on */ #include "canvas_uniforms_inc.glsl" @@ -38,15 +56,6 @@ out vec2 pixel_size_interp; #endif -#ifdef MATERIAL_UNIFORMS_USED -layout(std140) uniform MaterialUniforms{ -//ubo:4 - -#MATERIAL_UNIFORMS - -}; -#endif - #GLOBALS void main() { @@ -77,13 +86,22 @@ void main() { vec4 bone_weights = vec4(0.0); #elif defined(USE_ATTRIBUTES) - +#ifdef USE_INSTANCING + draw_data_instance = 0; +#endif vec2 vertex = vertex_attrib; vec4 color = color_attrib * draw_data[draw_data_instance].modulation; vec2 uv = uv_attrib; uvec4 bones = bone_attrib; 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 = 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)); @@ -96,83 +114,12 @@ void main() { #endif - mat4 world_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0)); - - // MultiMeshes don't batch, so always read from draw_data[0] - uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK; + mat4 model_matrix = mat4(vec4(draw_data[draw_data_instance].world_x, 0.0, 0.0), vec4(draw_data[draw_data_instance].world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(draw_data[draw_data_instance].world_ofs, 0.0, 1.0)); -#ifdef USE_ATTRIBUTES -/* - if (instancing > 1) { - // trails - - uint stride = 2 + 1 + 1; //particles always uses this format - - uint trail_size = instancing; - - uint offset = trail_size * stride * gl_InstanceID; - - vec4 pcolor; - vec2 new_vertex; - { - uint boffset = offset + bone_attrib.x * stride; - new_vertex = (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.x; - pcolor = transforms.data[boffset + 2] * weight_attrib.x; - } - if (weight_attrib.y > 0.001) { - uint boffset = offset + bone_attrib.y * stride; - new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.y; - pcolor += transforms.data[boffset + 2] * weight_attrib.y; - } - if (weight_attrib.z > 0.001) { - uint boffset = offset + bone_attrib.z * stride; - new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.z; - pcolor += transforms.data[boffset + 2] * weight_attrib.z; - } - if (weight_attrib.w > 0.001) { - uint boffset = offset + bone_attrib.w * stride; - new_vertex += (vec4(vertex, 0.0, 1.0) * mat4(transforms.data[boffset + 0], transforms.data[boffset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy * weight_attrib.w; - pcolor += transforms.data[boffset + 2] * weight_attrib.w; - } - - instance_custom = transforms.data[offset + 3]; - - vertex = new_vertex; - color *= pcolor; - } else*/ -#endif // USE_ATTRIBUTES -/* - { - if (instancing == 1) { - uint stride = 2; - { - if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { - stride += 1; - } - if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - stride += 1; - } - } - - uint offset = stride * gl_InstanceID; - - mat4 matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); - offset += 2; +#ifdef USE_INSTANCING + model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))); +#endif // USE_INSTANCING - if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_COLORS)) { - color *= transforms.data[offset]; - offset += 1; - } - - if (bool(draw_data[0].flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) { - instance_custom = transforms.data[offset]; - } - - matrix = transpose(matrix); - world_matrix = world_matrix * matrix; - } - } -*/ #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE) if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) { //scale by texture size @@ -192,7 +139,7 @@ void main() { #endif #if !defined(SKIP_TRANSFORM_USED) - vertex = (world_matrix * vec4(vertex, 0.0, 1.0)).xy; + vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy; #endif color_interp = color; @@ -286,7 +233,7 @@ in vec2 pixel_size_interp; layout(location = 0) out vec4 frag_color; #ifdef MATERIAL_UNIFORMS_USED -uniform MaterialUniforms{ +layout(std140) uniform MaterialUniforms{ //ubo:4 #MATERIAL_UNIFORMS @@ -478,10 +425,6 @@ float msdf_median(float r, float g, float b, float a) { return min(max(min(r, g), min(max(r, g), b)), a); } -vec2 msdf_map(vec2 value, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max) { - return out_min + (out_max - out_min) * (value - in_min) / (in_max - in_min); -} - void main() { vec4 color = color_interp; vec2 uv = uv_interp; @@ -522,8 +465,8 @@ void main() { float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0); float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5; - if (outline_thickness > 0) { - float cr = clamp(outline_thickness, 0.0, px_range / 2) / px_range; + if (outline_thickness > 0.0) { + float cr = clamp(outline_thickness, 0.0, px_range / 2.0) / px_range; float a = clamp((d + cr) * px_size, 0.0, 1.0); color.a = a * color.a; } else { @@ -714,8 +657,8 @@ void main() { 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; - if (pos_rot.y > 0) { - if (pos_rot.x > 0) { + 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; } else { @@ -723,7 +666,7 @@ void main() { distance = shadow_pos.y; } } else { - if (pos_rot.x < 0) { + if (pos_rot.x < 0.0) { tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); distance = -shadow_pos.x; } else { diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl index 65389c211a..94485abd11 100644 --- a/drivers/gles3/shaders/canvas_shadow.glsl +++ b/drivers/gles3/shaders/canvas_shadow.glsl @@ -15,13 +15,13 @@ layout(location = 0) in highp vec3 vertex; uniform highp mat4 projection_matrix; /* clang-format on */ uniform highp mat4 light_matrix; -uniform highp mat4 world_matrix; +uniform highp mat4 model_matrix; uniform highp float distance_norm; out highp vec4 position_interp; void main() { - gl_Position = projection_matrix * (light_matrix * (world_matrix * vec4(vertex, 1.0))); + gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0))); position_interp = gl_Position; } diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index e08a15e59d..852dccf415 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -58,8 +58,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 diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index 62332a15a7..ca2fc7e36d 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -1,204 +1,59 @@ /* clang-format off */ #[modes] -mode_default = -mode_cubemap = #define USE_CUBEMAP -mode_panorama = #define USE_PANORAMA +mode_default = #define MODE_SIMPLE_COPY mode_copy_section = #define USE_COPY_SECTION -mode_asym_pano = #define USE_ASYM_PANO -mode_no_alpha = #define USE_NO_ALPHA -mode_custom_alpha = #define USE_CUSTOM_ALPHA -mode_multiplier = #define USE_MULTIPLIER -mode_sep_cbcr_texture = #define USE_SEP_CBCR_TEXTURE -mode_ycbcr_to_rgb = #define USE_YCBCR_TO_RGB +mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR +mode_mipmap = #define MODE_MIPMAP +mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION #[specializations] - #[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 vec4 vertex_attrib; -/* clang-format on */ - -#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) -layout(location = 4) in vec3 cube_in; -#else -layout(location = 4) in vec2 uv_in; -#endif - -layout(location = 5) in vec2 uv2_in; +layout(location = 0) in vec2 vertex_attrib; -#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) -out vec3 cube_interp; -#else out vec2 uv_interp; -#endif -out vec2 uv2_interp; +/* clang-format on */ #ifdef USE_COPY_SECTION uniform highp vec4 copy_section; -#elif defined(USE_DISPLAY_TRANSFORM) -uniform highp mat4 display_transform; #endif void main() { -#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) - cube_interp = cube_in; -#elif defined(USE_ASYM_PANO) - uv_interp = vertex_attrib.xy; -#else - uv_interp = uv_in; -#endif - - uv2_interp = uv2_in; - gl_Position = vertex_attrib; + uv_interp = vertex_attrib * 0.5 + 0.5; + 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 + (gl_Position.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0; -#elif defined(USE_DISPLAY_TRANSFORM) - uv_interp = (display_transform * vec4(uv_in, 1.0, 1.0)).xy; #endif } /* clang-format off */ #[fragment] -#define M_PI 3.14159265359 - -#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 - -#if defined(USE_CUBEMAP) || defined(USE_PANORAMA) -in vec3 cube_interp; -#else in vec2 uv_interp; -#endif /* clang-format on */ - -#ifdef USE_ASYM_PANO -uniform highp mat4 pano_transform; -uniform highp vec4 asym_proj; -#endif - -#ifdef USE_CUBEMAP -uniform samplerCube source_cube; // texunit:0 -#else -uniform sampler2D source; // texunit:0 -#endif - -#ifdef USE_SEP_CBCR_TEXTURE -uniform sampler2D CbCr; //texunit:1 -#endif - -in vec2 uv2_interp; - -#ifdef USE_MULTIPLIER -uniform float multiplier; +#ifdef MODE_SIMPLE_COLOR +uniform vec4 color_in; #endif -#ifdef USE_CUSTOM_ALPHA -uniform float custom_alpha; +#ifdef MODE_GAUSSIAN_BLUR +uniform highp vec2 pixel_size; #endif -#if defined(USE_PANORAMA) || defined(USE_ASYM_PANO) -uniform highp mat4 sky_transform; - -vec4 texturePanorama(sampler2D pano, vec3 normal) { - vec2 st = vec2( - atan(normal.x, normal.z), - acos(normal.y)); - - if (st.x < 0.0) - st.x += M_PI * 2.0; - - st /= vec2(M_PI * 2.0, M_PI); - - return texture(pano, st); -} - -#endif +uniform sampler2D source; // texunit:0 layout(location = 0) out vec4 frag_color; void main() { -#ifdef USE_PANORAMA - - vec3 cube_normal = normalize(cube_interp); - cube_normal.z = -cube_normal.z; - cube_normal = mat3(sky_transform) * cube_normal; - cube_normal.z = -cube_normal.z; - - vec4 color = texturePanorama(source, cube_normal); - -#elif defined(USE_ASYM_PANO) - - // When an asymmetrical projection matrix is used (applicable for stereoscopic rendering i.e. VR) we need to do this calculation per fragment to get a perspective correct result. - // Asymmetrical projection means the center of projection is no longer in the center of the screen but shifted. - // The Matrix[2][0] (= asym_proj.x) and Matrix[2][1] (= asym_proj.z) values are what provide the right shift in the image. - - vec3 cube_normal; - cube_normal.z = -1.0; - cube_normal.x = (cube_normal.z * (-uv_interp.x - asym_proj.x)) / asym_proj.y; - cube_normal.y = (cube_normal.z * (-uv_interp.y - asym_proj.z)) / asym_proj.a; - cube_normal = mat3(sky_transform) * mat3(pano_transform) * cube_normal; - cube_normal.z = -cube_normal.z; - - vec4 color = texturePanorama(source, normalize(cube_normal.xyz)); - -#elif defined(USE_CUBEMAP) - vec4 color = texture(source_cube, normalize(cube_interp)); -#elif defined(USE_SEP_CBCR_TEXTURE) - vec4 color; - color.r = texture(source, uv_interp).r; - color.gb = texture(CbCr, uv_interp).rg - vec2(0.5, 0.5); - color.a = 1.0; -#else +#ifdef MODE_SIMPLE_COPY vec4 color = texture(source, uv_interp); + frag_color = color; #endif -#ifdef USE_YCBCR_TO_RGB - // YCbCr -> RGB conversion - - // Using BT.601, which is the standard for SDTV is provided as a reference - color.rgb = mat3( - vec3(1.00000, 1.00000, 1.00000), - vec3(0.00000, -0.34413, 1.77200), - vec3(1.40200, -0.71414, 0.00000)) * - color.rgb; -#endif - -#ifdef USE_NO_ALPHA - color.a = 1.0; -#endif - -#ifdef USE_CUSTOM_ALPHA - color.a = custom_alpha; -#endif - -#ifdef USE_MULTIPLIER - color.rgb *= multiplier; +#ifdef MODE_SIMPLE_COLOR + frag_color = color_in; #endif - - frag_color = color; } diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl index 2081abfef6..88464876f1 100644 --- a/drivers/gles3/shaders/cubemap_filter.glsl +++ b/drivers/gles3/shaders/cubemap_filter.glsl @@ -1,214 +1,122 @@ /* clang-format off */ -[vertex] +#[modes] -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif +mode_default = +mode_copy = #define MODE_DIRECT_WRITE + +#[specializations] -layout(location = 0) in highp vec2 vertex; +#[vertex] + +layout(location = 0) in highp vec2 vertex_attrib; /* clang-format on */ -layout(location = 4) in highp vec2 uv; out highp vec2 uv_interp; void main() { - uv_interp = uv; - gl_Position = vec4(vertex, 0, 1); + uv_interp = vertex_attrib; + gl_Position = vec4(uv_interp, 0.0, 1.0); } /* clang-format off */ -[fragment] +#[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 +#define M_PI 3.14159265359 -#ifdef USE_SOURCE_PANORAMA -uniform sampler2D source_panorama; //texunit:0 -#else uniform samplerCube source_cube; //texunit:0 -#endif + /* clang-format on */ uniform int face_id; -uniform float roughness; -in highp vec2 uv_interp; - -uniform sampler2D radical_inverse_vdc_cache; // texunit:1 - -#define M_PI 3.14159265359 - -#ifdef LOW_QUALITY - -#define SAMPLE_COUNT 64 - -#else - -#define SAMPLE_COUNT 512 +#ifndef MODE_DIRECT_WRITE +uniform int sample_count; +uniform vec4 sample_directions_mip[MAX_SAMPLE_COUNT]; +uniform float weight; #endif -#ifdef USE_SOURCE_PANORAMA - -vec4 texturePanorama(sampler2D pano, vec3 normal) { - vec2 st = vec2( - atan(normal.x, normal.z), - acos(normal.y)); +in highp vec2 uv_interp; - if (st.x < 0.0) - st.x += M_PI * 2.0; +layout(location = 0) out vec4 frag_color; - st /= vec2(M_PI * 2.0, M_PI); +#define M_PI 3.14159265359 - return textureLod(pano, st, 0.0); +// Don't include tonemap_inc.glsl because all we want is these functions, we don't want the uniforms +vec3 linear_to_srgb(vec3 color) { + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); } -#endif +vec3 srgb_to_linear(vec3 color) { + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); +} vec3 texelCoordToVec(vec2 uv, int faceID) { mat3 faceUvVectors[6]; // -x - faceUvVectors[0][0] = vec3(0.0, 0.0, 1.0); // u -> +z - faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[0][2] = vec3(-1.0, 0.0, 0.0); // -x face + faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z + faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face // +x - faceUvVectors[1][0] = vec3(0.0, 0.0, -1.0); // u -> -z - faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[1][2] = vec3(1.0, 0.0, 0.0); // +x face + faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z + faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face // -y - faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[2][1] = vec3(0.0, 0.0, -1.0); // v -> -z - faceUvVectors[2][2] = vec3(0.0, -1.0, 0.0); // -y face + faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z + faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face // +y - faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[3][1] = vec3(0.0, 0.0, 1.0); // v -> +z - faceUvVectors[3][2] = vec3(0.0, 1.0, 0.0); // +y face + faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z + faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face // -z - faceUvVectors[4][0] = vec3(-1.0, 0.0, 0.0); // u -> -x - faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[4][2] = vec3(0.0, 0.0, -1.0); // -z face + faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x + faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face // +z - faceUvVectors[5][0] = vec3(1.0, 0.0, 0.0); // u -> +x - faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y - faceUvVectors[5][2] = vec3(0.0, 0.0, 1.0); // +z face + faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x + faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y + faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face // out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2]. - vec3 result; - for (int i = 0; i < 6; i++) { - if (i == faceID) { - result = (faceUvVectors[i][0] * uv.x) + (faceUvVectors[i][1] * uv.y) + faceUvVectors[i][2]; - break; - } - } + vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2]; return normalize(result); } -vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { - float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph] - - // Compute distribution direction - float Phi = 2.0 * M_PI * Xi.x; - float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 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; - - vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 TangentX = normalize(cross(UpVector, N)); - vec3 TangentY = cross(N, TangentX); - - // Tangent to world space - return TangentX * H.x + TangentY * H.y + N * H.z; -} - -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)); -} - -uniform bool z_flip; - -layout(location = 0) out vec4 frag_color; - void main() { vec3 color = vec3(0.0); - - vec2 uv = (uv_interp * 2.0) - 1.0; + vec2 uv = uv_interp; vec3 N = texelCoordToVec(uv, face_id); -#ifdef USE_DIRECT_WRITE - -#ifdef USE_SOURCE_PANORAMA - - frag_color = vec4(texturePanorama(source_panorama, N).rgb, 1.0); -#else - - frag_color = vec4(textureCube(source_cube, N).rgb, 1.0); -#endif //USE_SOURCE_PANORAMA - +#ifdef MODE_DIRECT_WRITE + frag_color = vec4(textureLod(source_cube, N, 0.0).rgb, 1.0); #else vec4 sum = vec4(0.0); - - for (int sample_num = 0; sample_num < SAMPLE_COUNT; sample_num++) { - vec2 xi = Hammersley(sample_num, SAMPLE_COUNT); - - vec3 H = ImportanceSampleGGX(xi, roughness, N); - vec3 V = N; - vec3 L = (2.0 * dot(V, H) * H - V); - - float NdotL = clamp(dot(N, L), 0.0, 1.0); - - if (NdotL > 0.0) { - -#ifdef USE_SOURCE_PANORAMA - vec3 val = texturePanorama(source_panorama, L).rgb; -#else - vec3 val = textureCubeLod(source_cube, L, 0.0).rgb; -#endif - //mix using Linear, to approximate high end back-end - val = mix(pow((val + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), val * (1.0 / 12.92), vec3(lessThan(val, vec3(0.04045)))); - - sum.rgb += val * NdotL; - - sum.a += NdotL; - } + 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)); + T[1] = cross(N, T[0]); + T[2] = N; + + for (int sample_num = 0; sample_num < sample_count; sample_num++) { + 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; - - vec3 a = vec3(0.055); - sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308)))); + sum /= weight; + sum.rgb = linear_to_srgb(sum.rgb); frag_color = vec4(sum.rgb, 1.0); #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 98c92a1d99..84daf839e9 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1,980 +1,532 @@ /* clang-format off */ -[vertex] +#[modes] -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif +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 -#define SHADER_IS_SRGB true //TODO remove +#[specializations] -#define M_PI 3.14159265359 +DISABLE_LIGHTMAP = false +DISABLE_LIGHT_DIRECTIONAL = false +DISABLE_LIGHT_OMNI = false +DISABLE_LIGHT_SPOT = false +DISABLE_FOG = false +USE_RADIANCE_MAP = true -// -// attributes -// -layout(location = 0) in highp vec4 vertex_attrib; -/* clang-format on */ -layout(location = 1) in vec3 normal_attrib; +#[vertex] -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) -layout(location = 2) in vec4 tangent_attrib; -#endif +#define M_PI 3.14159265359 -#if defined(ENABLE_COLOR_INTERP) -layout(location = 3) in vec4 color_attrib; -#endif +#define SHADER_IS_SRGB true -#if defined(ENABLE_UV_INTERP) -layout(location = 4) in vec2 uv_attrib; -#endif +#include "stdlib_inc.glsl" -#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) -layout(location = 5) in vec2 uv2_attrib; +#if !defined(MODE_RENDER_DEPTH) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) ||defined(LIGHT_CLEARCOAT_USED) +#ifndef NORMAL_USED +#define NORMAL_USED +#endif #endif -#ifdef USE_SKELETON - -#ifdef USE_SKELETON_SOFTWARE - -layout(location = 13) in highp vec4 bone_transform_row_0; -layout(location = 14) in highp vec4 bone_transform_row_1; -layout(location = 15) in highp vec4 bone_transform_row_2; - -#else +/* +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_COLOR = 3, // RGBA8 +ARRAY_TEX_UV = 4, // RG32F +ARRAY_TEX_UV2 = 5, // RG32F +ARRAY_CUSTOM0 = 6, // Depends on ArrayCustomFormat. +ARRAY_CUSTOM1 = 7, +ARRAY_CUSTOM2 = 8, +ARRAY_CUSTOM3 = 9, +ARRAY_BONES = 10, // RGBA16UI (x2 if 8 weights) +ARRAY_WEIGHTS = 11, // RGBA16UNORM (x2 if 8 weights) +*/ -layout(location = 6) in vec4 bone_ids; -layout(location = 7) in highp vec4 bone_weights; +/* INPUT ATTRIBS */ -uniform highp sampler2D bone_transforms; // texunit:-1 -uniform ivec2 skeleton_texture_size; +layout(location = 0) in highp vec3 vertex_attrib; +/* clang-format on */ +#ifdef NORMAL_USED +layout(location = 1) in vec3 normal_attrib; #endif +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +layout(location = 2) in vec4 tangent_attrib; #endif -#ifdef USE_INSTANCING - -layout(location = 8) in highp vec4 instance_xform_row_0; -layout(location = 9) in highp vec4 instance_xform_row_1; -layout(location = 10) in highp vec4 instance_xform_row_2; - -layout(location = 11) in highp vec4 instance_color; -layout(location = 12) in highp vec4 instance_custom_data; - +#if defined(COLOR_USED) +layout(location = 3) in vec4 color_attrib; #endif -// -// uniforms -// - -uniform highp mat4 camera_matrix; -uniform highp mat4 camera_inverse_matrix; -uniform highp mat4 projection_matrix; -uniform highp mat4 projection_inverse_matrix; - -uniform highp mat4 world_transform; - -uniform highp float time; - -uniform highp vec2 viewport_size; - -#ifdef RENDER_DEPTH -uniform float light_bias; -uniform float light_normal_bias; +#ifdef UV_USED +layout(location = 4) in vec2 uv_attrib; #endif -// -// varyings -// - -#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) -out highp vec4 position_interp; +#if defined(UV2_USED) || defined(USE_LIGHTMAP) +layout(location = 5) in vec2 uv2_attrib; #endif -out highp vec3 vertex_interp; -out vec3 normal_interp; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) -out vec3 tangent_interp; -out vec3 binormal_interp; +#if defined(CUSTOM0_USED) +layout(location = 6) in vec4 custom0_attrib; #endif -#if defined(ENABLE_COLOR_INTERP) -out vec4 color_interp; +#if defined(CUSTOM1_USED) +layout(location = 7) in vec4 custom1_attrib; #endif -#if defined(ENABLE_UV_INTERP) -out vec2 uv_interp; +#if defined(CUSTOM2_USED) +layout(location = 8) in vec4 custom2_attrib; #endif -#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) -out vec2 uv2_interp; +#if defined(CUSTOM3_USED) +layout(location = 9) in vec4 custom3_attrib; #endif -/* clang-format off */ - -VERTEX_SHADER_GLOBALS - -/* clang-format on */ - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -out highp float dp_clip; -uniform highp float shadow_dual_paraboloid_render_zfar; -uniform highp float shadow_dual_paraboloid_render_side; - +#if defined(BONES_USED) +layout(location = 10) in uvec4 bone_attrib; #endif -#if defined(USE_SHADOW) && defined(USE_LIGHTING) - -uniform highp mat4 light_shadow_matrix; -out highp vec4 shadow_coord; - -#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) -uniform highp mat4 light_shadow_matrix2; -out highp vec4 shadow_coord2; +#if defined(WEIGHTS_USED) +layout(location = 11) in vec4 weight_attrib; #endif -#if defined(LIGHT_USE_PSSM4) - -uniform highp mat4 light_shadow_matrix3; -uniform highp mat4 light_shadow_matrix4; -out highp vec4 shadow_coord3; -out highp vec4 shadow_coord4; - +#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 -#endif +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; +}; -#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) +layout(std140) uniform SceneData { // ubo:2 + highp mat4 projection_matrix; + highp mat4 inv_projection_matrix; + highp mat4 inv_view_matrix; + highp mat4 view_matrix; -out highp vec3 diffuse_interp; -out highp vec3 specular_interp; + vec2 viewport_size; + vec2 screen_pixel_size; -// general for all lights -uniform highp vec4 light_color; -uniform highp vec4 shadow_color; -uniform highp float light_specular; + mediump vec4 ambient_light_color_energy; -// directional -uniform highp vec3 light_direction; + mediump float ambient_color_sky_mix; + bool material_uv2_mode; + float pad2; + bool use_ambient_light; + bool use_ambient_cubemap; + bool use_reflection_cubemap; -// omni -uniform highp vec3 light_position; + float fog_aerial_perspective; + float time; -uniform highp float light_range; -uniform highp float light_attenuation; + mat3 radiance_inverse_xform; -// spot -uniform highp float light_spot_attenuation; -uniform highp float light_spot_range; -uniform highp float light_spot_angle; + uint directional_light_count; + float z_far; + float z_near; + float pad; -void light_compute( - vec3 N, - vec3 L, - vec3 V, - vec3 light_color, - vec3 attenuation, - float roughness) { -//this makes lights behave closer to linear, but then addition of lights looks bad -//better left disabled + bool fog_enabled; + float fog_density; + float fog_height; + float fog_height_density; -//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); -/* -#define SRGB_APPROX(m_var) {\ - float S1 = sqrt(m_var);\ - float S2 = sqrt(S1);\ - float S3 = sqrt(S2);\ - m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ - } -*/ -#define SRGB_APPROX(m_var) + vec3 fog_light_color; + float fog_sun_scatter; +} +scene_data; - float NdotL = dot(N, L); - float cNdotL = max(NdotL, 0.0); // clamped NdotL - float NdotV = dot(N, V); - float cNdotV = max(NdotV, 0.0); +uniform highp mat4 world_transform; -#if defined(DIFFUSE_OREN_NAYAR) - vec3 diffuse_brdf_NL; -#else - float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance +#ifdef USE_LIGHTMAP +uniform highp vec4 lightmap_uv_rect; #endif -#if defined(DIFFUSE_LAMBERT_WRAP) - // energy conserving lambert wrap shader - diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); - -#elif defined(DIFFUSE_OREN_NAYAR) +/* Varyings */ - { - // see http://mimosa-pudica.net/improved-oren-nayar.html - float LdotV = dot(L, V); - - float s = LdotV - NdotL * NdotV; - float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); - - float sigma2 = roughness * roughness; // TODO: this needs checking - vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13)); - float B = 0.45 * sigma2 / (sigma2 + 0.09); - - diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); - } -#else - // lambert by default for everything else - diffuse_brdf_NL = cNdotL * (1.0 / M_PI); +out highp vec3 vertex_interp; +#ifdef NORMAL_USED +out vec3 normal_interp; #endif - SRGB_APPROX(diffuse_brdf_NL) - - diffuse_interp += light_color * diffuse_brdf_NL * attenuation; - - if (roughness > 0.0) { - // D - float specular_brdf_NL = 0.0; - -#if !defined(SPECULAR_DISABLED) - //normalized blinn always unless disabled - vec3 H = normalize(V + L); - float cNdotH = max(dot(N, H), 0.0); - float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; - float blinn = pow(cNdotH, shininess) * cNdotL; - blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); - specular_brdf_NL = blinn; +#if defined(COLOR_USED) +out vec4 color_interp; #endif - SRGB_APPROX(specular_brdf_NL) - specular_interp += specular_brdf_NL * light_color * attenuation * (1.0 / M_PI); - } -} - +#if defined(UV_USED) +out vec2 uv_interp; #endif -#ifdef USE_VERTEX_LIGHTING - -#ifdef USE_REFLECTION_PROBE1 - -uniform highp mat4 refprobe1_local_matrix; -out mediump vec4 refprobe1_reflection_normal_blend; -uniform highp vec3 refprobe1_box_extents; - -#ifndef USE_LIGHTMAP -out mediump vec3 refprobe1_ambient_normal; +#if defined(UV2_USED) +out vec2 uv2_interp; +#else +#ifdef USE_LIGHTMAP +out vec2 uv2_interp; +#endif #endif -#endif //reflection probe1 - -#ifdef USE_REFLECTION_PROBE2 - -uniform highp mat4 refprobe2_local_matrix; -out mediump vec4 refprobe2_reflection_normal_blend; -uniform highp vec3 refprobe2_box_extents; - -#ifndef USE_LIGHTMAP -out mediump vec3 refprobe2_ambient_normal; +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +out vec3 tangent_interp; +out vec3 binormal_interp; #endif -#endif //reflection probe2 +#if defined(MATERIAL_UNIFORMS_USED) -#endif //vertex lighting for refprobes +/* clang-format off */ +layout(std140) uniform MaterialUniforms { // ubo:3 -#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) +#MATERIAL_UNIFORMS -out vec4 fog_interp; +}; +/* clang-format on */ -uniform mediump vec4 fog_color_base; -#ifdef LIGHT_MODE_DIRECTIONAL -uniform mediump vec4 fog_sun_color_amount; #endif -uniform bool fog_transmit_enabled; -uniform mediump float fog_transmit_curve; - -#ifdef FOG_DEPTH_ENABLED -uniform highp float fog_depth_begin; -uniform mediump float fog_depth_curve; -uniform mediump float fog_max_distance; -#endif +/* clang-format off */ -#ifdef FOG_HEIGHT_ENABLED -uniform highp float fog_height_min; -uniform highp float fog_height_max; -uniform mediump float fog_height_curve; -#endif +#GLOBALS -#endif //fog +/* clang-format on */ +invariant gl_Position; void main() { - highp vec4 vertex = vertex_attrib; - - mat4 world_matrix = world_transform; + highp vec3 vertex = vertex_attrib; + highp mat4 model_matrix = world_transform; #ifdef USE_INSTANCING - { - highp mat4 m = mat4( - instance_xform_row_0, - instance_xform_row_1, - instance_xform_row_2, - vec4(0.0, 0.0, 0.0, 1.0)); - world_matrix = world_matrix * transpose(m); - } - + 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 - vec3 normal = normal_attrib; +#ifdef NORMAL_USED + vec3 normal = normal_attrib * 2.0 - 1.0; +#endif + highp mat3 model_normal_matrix = mat3(model_matrix); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - vec3 tangent = tangent_attrib.xyz; - float binormalf = tangent_attrib.a; +#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; vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#if defined(ENABLE_COLOR_INTERP) +#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(ENABLE_UV_INTERP) +#if defined(UV_USED) uv_interp = uv_attrib; #endif -#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) +#ifdef USE_LIGHTMAP + uv2_interp = lightmap_uv_rect.zw * uv2_attrib + lightmap_uv_rect.xy; +#else +#if defined(UV2_USED) uv2_interp = uv2_attrib; #endif +#endif #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; -#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - vertex = world_matrix * vertex; - normal = normalize((world_matrix * vec4(normal, 0.0)).xyz); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - - tangent = normalize((world_matrix * vec4(tangent, 0.0)).xyz); - binormal = normalize((world_matrix * vec4(binormal, 0.0)).xyz); -#endif -#endif - -#ifdef USE_SKELETON - - highp mat4 bone_transform = mat4(0.0); - -#ifdef USE_SKELETON_SOFTWARE - // passing the transform as attributes - - bone_transform[0] = vec4(bone_transform_row_0.x, bone_transform_row_1.x, bone_transform_row_2.x, 0.0); - bone_transform[1] = vec4(bone_transform_row_0.y, bone_transform_row_1.y, bone_transform_row_2.y, 0.0); - bone_transform[2] = vec4(bone_transform_row_0.z, bone_transform_row_1.z, bone_transform_row_2.z, 0.0); - bone_transform[3] = vec4(bone_transform_row_0.w, bone_transform_row_1.w, bone_transform_row_2.w, 1.0); - +#ifdef USE_INSTANCING + vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w)); #else - // look up transform from the "pose texture" - { - for (int i = 0; i < 4; i++) { - ivec2 tex_ofs = ivec2(int(bone_ids[i]) * 3, 0); + vec4 instance_custom = vec4(0.0); +#endif - highp mat4 b = mat4( - texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(0, 0)), - texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(1, 0)), - texel2DFetch(bone_transforms, skeleton_texture_size, tex_ofs + ivec2(2, 0)), - vec4(0.0, 0.0, 0.0, 1.0)); + // Using world coordinates +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - bone_transform += transpose(b) * bone_weights[i]; - } - } + vertex = (model_matrix * vec4(vertex, 1.0)).xyz; +#ifdef NORMAL_USED + normal = model_normal_matrix * normal; #endif - world_matrix = world_matrix * bone_transform; +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) -#endif - -#ifdef USE_INSTANCING - vec4 instance_custom = instance_custom_data; -#else - vec4 instance_custom = vec4(0.0); + tangent = model_normal_matrix * tangent; + binormal = model_normal_matrix * binormal; #endif +#endif - mat4 local_projection_matrix = projection_matrix; - - mat4 modelview = camera_inverse_matrix * world_matrix; float roughness = 1.0; -#define projection_matrix local_projection_matrix -#define world_transform world_matrix + highp mat4 modelview = scene_data.view_matrix * model_matrix; + highp mat3 modelview_normal = mat3(scene_data.view_matrix) * model_normal_matrix; float point_size = 1.0; { - /* clang-format off */ - -VERTEX_SHADER_CODE - - /* clang-format on */ +#CODE : VERTEX } gl_PointSize = point_size; - vec4 outvec = vertex; - // use local coordinates + // Using local coordinates (default) #if !defined(SKIP_TRANSFORM_USED) && !defined(VERTEX_WORLD_COORDS_USED) - vertex = modelview * vertex; - normal = normalize((modelview * vec4(normal, 0.0)).xyz); - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - tangent = normalize((modelview * vec4(tangent, 0.0)).xyz); - binormal = normalize((modelview * vec4(binormal, 0.0)).xyz); -#endif -#endif - -#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - vertex = camera_inverse_matrix * vertex; - normal = normalize((camera_inverse_matrix * vec4(normal, 0.0)).xyz); -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - tangent = normalize((camera_inverse_matrix * vec4(tangent, 0.0)).xyz); - binormal = normalize((camera_inverse_matrix * vec4(binormal, 0.0)).xyz); -#endif -#endif - - vertex_interp = vertex.xyz; - normal_interp = normal; - -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - tangent_interp = tangent; - binormal_interp = binormal; -#endif - -#ifdef RENDER_DEPTH - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - - vertex_interp.z *= shadow_dual_paraboloid_render_side; - normal_interp.z *= shadow_dual_paraboloid_render_side; - - dp_clip = vertex_interp.z; //this attempts to avoid noise caused by objects sent to the other parabolloid side due to bias - - //for dual paraboloid shadow mapping, this is the fastest but least correct way, as it curves straight edges - - highp vec3 vtx = vertex_interp + normalize(vertex_interp) * light_bias; - highp float distance = length(vtx); - vtx = normalize(vtx); - vtx.xy /= 1.0 - vtx.z; - vtx.z = (distance / shadow_dual_paraboloid_render_zfar); - vtx.z = vtx.z * 2.0 - 1.0; - - vertex_interp = vtx; - -#else - float z_ofs = light_bias; - z_ofs += (1.0 - abs(normal_interp.z)) * light_normal_bias; - - vertex_interp.z -= z_ofs; -#endif //dual parabolloid - -#endif //depth - -//vertex lighting -#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING) - //vertex shaded version of lighting (more limited) - vec3 L; - vec3 light_att; - -#ifdef LIGHT_MODE_OMNI - vec3 light_vec = light_position - vertex_interp; - float light_length = length(light_vec); - - float normalized_distance = light_length / light_range; - - if (normalized_distance < 1.0) { - float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); - - vec3 attenuation = vec3(omni_attenuation); - light_att = vec3(omni_attenuation); - } else { - light_att = vec3(0.0); - } - - L = normalize(light_vec); - -#endif - -#ifdef LIGHT_MODE_SPOT - - vec3 light_rel_vec = light_position - vertex_interp; - float light_length = length(light_rel_vec); - float normalized_distance = light_length / light_range; - - if (normalized_distance < 1.0) { - float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); - vec3 spot_dir = light_direction; - - float spot_cutoff = light_spot_angle; - - float angle = dot(-normalize(light_rel_vec), spot_dir); - - if (angle > spot_cutoff) { - float scos = max(angle, spot_cutoff); - float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); - - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - - light_att = vec3(spot_attenuation); - } else { - light_att = vec3(0.0); - } - } else { - light_att = vec3(0.0); - } - - L = normalize(light_rel_vec); + vertex = (modelview * vec4(vertex, 1.0)).xyz; +#ifdef NORMAL_USED + normal = modelview_normal * normal; #endif -#ifdef LIGHT_MODE_DIRECTIONAL - vec3 light_vec = -light_direction; - light_att = vec3(1.0); //no base attenuation - L = normalize(light_vec); #endif - diffuse_interp = vec3(0.0); - specular_interp = vec3(0.0); - light_compute(normal_interp, L, -normalize(vertex_interp), light_color.rgb, light_att, roughness); +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) + binormal = modelview_normal * binormal; + tangent = modelview_normal * tangent; #endif -//shadows (for both vertex and fragment) -#if defined(USE_SHADOW) && defined(USE_LIGHTING) - - vec4 vi4 = vec4(vertex_interp, 1.0); - shadow_coord = light_shadow_matrix * vi4; - -#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) - shadow_coord2 = light_shadow_matrix2 * vi4; -#endif - -#if defined(LIGHT_USE_PSSM4) - shadow_coord3 = light_shadow_matrix3 * vi4; - shadow_coord4 = light_shadow_matrix4 * vi4; - -#endif - -#endif //use shadow and use lighting - -#ifdef USE_VERTEX_LIGHTING - -#ifdef USE_REFLECTION_PROBE1 - { - vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); - vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz; - vec3 inner_pos = abs(local_pos / refprobe1_box_extents); - float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); - - { - vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz; - refprobe1_reflection_normal_blend.xyz = local_ref_vec; - refprobe1_reflection_normal_blend.a = blend; - } -#ifndef USE_LIGHTMAP + // Using world coordinates +#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) - refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz; + vertex = (scene_data.view_matrix * vec4(vertex, 1.0)).xyz; +#ifdef NORMAL_USED + normal = (scene_data.view_matrix * vec4(normal, 0.0)).xyz; #endif - } - -#endif //USE_REFLECTION_PROBE1 -#ifdef USE_REFLECTION_PROBE2 - { - vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); - vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz; - vec3 inner_pos = abs(local_pos / refprobe2_box_extents); - float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); - - { - vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz; - refprobe2_reflection_normal_blend.xyz = local_ref_vec; - refprobe2_reflection_normal_blend.a = blend; - } -#ifndef USE_LIGHTMAP - - refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz; +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) + binormal = (scene_data.view_matrix * vec4(binormal, 0.0)).xyz; + tangent = (scene_data.view_matrix * vec4(tangent, 0.0)).xyz; #endif - } - -#endif //USE_REFLECTION_PROBE2 - -#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) - - float fog_amount = 0.0; - -#ifdef LIGHT_MODE_DIRECTIONAL - - vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(normalize(vertex_interp), light_direction), 0.0), 8.0)); -#else - vec3 fog_color = fog_color_base.rgb; #endif -#ifdef FOG_DEPTH_ENABLED - - { - float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); - - fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a; - } + vertex_interp = vertex; +#ifdef NORMAL_USED + normal_interp = normal; #endif -#ifdef FOG_HEIGHT_ENABLED - { - float y = (camera_matrix * vec4(vertex_interp, 1.0)).y; - fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve)); - } +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) + tangent_interp = tangent; + binormal_interp = binormal; #endif - fog_interp = vec4(fog_color, fog_amount); - -#endif //fog - -#endif //use vertex lighting #if defined(OVERRIDE_POSITION) gl_Position = position; #else gl_Position = projection_matrix * vec4(vertex_interp, 1.0); #endif - -#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) - position_interp = gl_Position; -#endif } /* clang-format off */ -[fragment] +#[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 -#define M_PI 3.14159265359 -#define SHADER_IS_SRGB true - -// -// uniforms -// - -uniform highp mat4 camera_matrix; -/* clang-format on */ -uniform highp mat4 camera_inverse_matrix; -uniform highp mat4 projection_matrix; -uniform highp mat4 projection_inverse_matrix; - -uniform highp mat4 world_transform; - -uniform highp float time; - -uniform highp vec2 viewport_size; - -#if defined(SCREEN_UV_USED) -uniform vec2 screen_pixel_size; +// Default to SPECULAR_SCHLICK_GGX. +#if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON) +#define SPECULAR_SCHLICK_GGX #endif -#if defined(SCREEN_TEXTURE_USED) -uniform highp sampler2D screen_texture; //texunit:-4 +#if !defined(MODE_RENDER_DEPTH) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) ||defined(LIGHT_CLEARCOAT_USED) +#ifndef NORMAL_USED +#define NORMAL_USED #endif -#if defined(DEPTH_TEXTURE_USED) -uniform highp sampler2D depth_texture; //texunit:-4 #endif -#ifdef USE_REFLECTION_PROBE1 +#include "tonemap_inc.glsl" +#include "stdlib_inc.glsl" -#ifdef USE_VERTEX_LIGHTING +/* texture unit usage, N is max_texture_unity-N -in mediump vec4 refprobe1_reflection_normal_blend; -#ifndef USE_LIGHTMAP -in mediump vec3 refprobe1_ambient_normal; -#endif +1-color correction // In tonemap_inc.glsl +2-radiance +3-directional_shadow +4-positional_shadow +5-screen +6-depth -#else - -uniform bool refprobe1_use_box_project; -uniform highp vec3 refprobe1_box_extents; -uniform vec3 refprobe1_box_offset; -uniform highp mat4 refprobe1_local_matrix; - -#endif //use vertex lighting - -uniform bool refprobe1_exterior; - -uniform highp samplerCube reflection_probe1; //texunit:-5 +*/ -uniform float refprobe1_intensity; -uniform vec4 refprobe1_ambient; +#define M_PI 3.14159265359 +/* clang-format on */ -#endif //USE_REFLECTION_PROBE1 +#define SHADER_IS_SRGB true -#ifdef USE_REFLECTION_PROBE2 +/* Varyings */ -#ifdef USE_VERTEX_LIGHTING +#if defined(COLOR_USED) +in vec4 color_interp; +#endif -in mediump vec4 refprobe2_reflection_normal_blend; -#ifndef USE_LIGHTMAP -in mediump vec3 refprobe2_ambient_normal; +#if defined(UV_USED) +in vec2 uv_interp; #endif +#if defined(UV2_USED) +in vec2 uv2_interp; #else - -uniform bool refprobe2_use_box_project; -uniform highp vec3 refprobe2_box_extents; -uniform vec3 refprobe2_box_offset; -uniform highp mat4 refprobe2_local_matrix; - -#endif //use vertex lighting - -uniform bool refprobe2_exterior; - -uniform highp samplerCube reflection_probe2; //texunit:-6 - -uniform float refprobe2_intensity; -uniform vec4 refprobe2_ambient; - -#endif //USE_REFLECTION_PROBE2 - -#define RADIANCE_MAX_LOD 6.0 - -#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) - -void reflection_process(samplerCube reflection_map, -#ifdef USE_VERTEX_LIGHTING - vec3 ref_normal, -#ifndef USE_LIGHTMAP - vec3 amb_normal, +#ifdef USE_LIGHTMAP +in vec2 uv2_interp; #endif - float ref_blend, - -#else //no vertex lighting - vec3 normal, vec3 vertex, - mat4 local_matrix, - bool use_box_project, vec3 box_extents, vec3 box_offset, -#endif //vertex lighting - bool exterior, float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) { - vec4 reflection; - -#ifdef USE_VERTEX_LIGHTING - - reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; - - float blend = ref_blend; //crappier blend formula for vertex - blend *= blend; - blend = max(0.0, 1.0 - blend); - -#else //fragment lighting - - vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz; - - if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box - return; - } - - vec3 inner_pos = abs(local_pos / box_extents); - float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); - blend = mix(length(inner_pos), blend, blend); - blend *= blend; - blend = max(0.0, 1.0 - blend); - - //reflect and make local - vec3 ref_normal = normalize(reflect(vertex, normal)); - ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz; - - if (use_box_project) { //box project - - vec3 nrdir = normalize(ref_normal); - vec3 rbmax = (box_extents - local_pos) / nrdir; - vec3 rbmin = (-box_extents - local_pos) / nrdir; - - vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0)))); - - float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); - vec3 posonbox = local_pos + nrdir * fa; - ref_normal = posonbox - box_offset.xyz; - } - - reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; #endif - if (exterior) { - reflection.rgb = mix(skybox, reflection.rgb, blend); - } - reflection.rgb *= intensity; - reflection.a = blend; - reflection.rgb *= blend; - - reflection_accum += reflection; - -#ifndef USE_LIGHTMAP - - vec4 ambient_out; -#ifndef USE_VERTEX_LIGHTING +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) +in vec3 tangent_interp; +in vec3 binormal_interp; +#endif - vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz; +#ifdef NORMAL_USED +in vec3 normal_interp; #endif - ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb; - ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a); - if (exterior) { - ambient_out.rgb = mix(ambient, ambient_out.rgb, blend); - } +in highp vec3 vertex_interp; - ambient_out.a = blend; - ambient_out.rgb *= blend; - ambient_accum += ambient_out; +#ifdef USE_RADIANCE_MAP -#endif -} +#define RADIANCE_MAX_LOD 5.0 -#endif //use refprobe 1 or 2 +uniform samplerCube radiance_map; // texunit:-2 -#ifdef USE_LIGHTMAP -uniform mediump sampler2D lightmap; //texunit:-4 -uniform mediump float lightmap_energy; #endif -#ifdef USE_LIGHTMAP_CAPTURE -uniform mediump vec4[12] lightmap_captures; -uniform bool lightmap_capture_sky; +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; +}; -#endif + /* Material Uniforms */ -#ifdef USE_RADIANCE_MAP +#if defined(MATERIAL_UNIFORMS_USED) -uniform samplerCube radiance_map; // texunit:-2 +/* clang-format off */ +layout(std140) uniform MaterialUniforms { // ubo:3 -uniform mat4 radiance_inverse_xform; +#MATERIAL_UNIFORMS -#endif - -uniform vec4 bg_color; -uniform float bg_energy; +}; +/* clang-format on */ -uniform float ambient_sky_contribution; -uniform vec4 ambient_color; -uniform float ambient_energy; +#endif -#ifdef USE_LIGHTING +layout(std140) uniform SceneData { // ubo:2 + highp mat4 projection_matrix; + highp mat4 inv_projection_matrix; + highp mat4 inv_view_matrix; + highp mat4 view_matrix; -uniform highp vec4 shadow_color; + vec2 viewport_size; + vec2 screen_pixel_size; -#ifdef USE_VERTEX_LIGHTING + mediump vec4 ambient_light_color_energy; -//get from vertex -in highp vec3 diffuse_interp; -in highp vec3 specular_interp; + mediump float ambient_color_sky_mix; + bool material_uv2_mode; + float pad2; + bool use_ambient_light; + bool use_ambient_cubemap; + bool use_reflection_cubemap; -uniform highp vec3 light_direction; //may be used by fog, so leave here + float fog_aerial_perspective; + float time; -#else -//done in fragment -// general for all lights -uniform highp vec4 light_color; + mat3 radiance_inverse_xform; -uniform highp float light_specular; + uint directional_light_count; + float z_far; + float z_near; + float pad; -// directional -uniform highp vec3 light_direction; -// omni -uniform highp vec3 light_position; + bool fog_enabled; + float fog_density; + float fog_height; + float fog_height_density; -uniform highp float light_attenuation; + vec3 fog_light_color; + float fog_sun_scatter; +} +scene_data; -// spot -uniform highp float light_spot_attenuation; -uniform highp float light_spot_range; -uniform highp float light_spot_angle; -#endif +/* clang-format off */ -//this is needed outside above if because dual paraboloid wants it -uniform highp float light_range; +#GLOBALS -#ifdef USE_SHADOW +/* clang-format on */ -uniform highp vec2 shadow_pixel_size; +//directional light data -#if defined(LIGHT_MODE_OMNI) || defined(LIGHT_MODE_SPOT) -uniform highp sampler2D light_shadow_atlas; //texunit:-3 -#endif +#ifndef DISABLE_LIGHT_DIRECTIONAL -#ifdef LIGHT_MODE_DIRECTIONAL -uniform highp sampler2D light_directional_shadow; // texunit:-3 -uniform highp vec4 light_split_offsets; -#endif +struct DirectionalLightData { + mediump vec3 direction; + mediump float energy; + mediump vec3 color; + mediump float size; + mediump vec3 pad; + mediump float specular; +}; -in highp vec4 shadow_coord; +layout(std140) uniform DirectionalLights { // ubo:7 + DirectionalLightData directional_lights[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +}; -#if defined(LIGHT_USE_PSSM2) || defined(LIGHT_USE_PSSM4) -in highp vec4 shadow_coord2; #endif -#if defined(LIGHT_USE_PSSM4) - -in highp vec4 shadow_coord3; -in highp vec4 shadow_coord4; - -#endif +// omni and spot +#if !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) +struct LightData { //this structure needs to be as packed as possible + highp vec3 position; + highp float inv_radius; -uniform vec4 light_clamp; + mediump vec3 direction; + highp float size; -#endif // light shadow + mediump vec3 color; + mediump float attenuation; -// directional shadow + mediump float cone_attenuation; + mediump float cone_angle; + mediump float specular_amount; + mediump float shadow_opacity; +}; +#ifndef DISABLE_LIGHT_OMNI +layout(std140) uniform OmniLightData { // ubo:5 + LightData omni_lights[MAX_LIGHT_DATA_STRUCTS]; +}; +uniform uint omni_light_indices[MAX_FORWARD_LIGHTS]; +uniform int omni_light_count; #endif -// -// varyings -// - -#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS) -in highp vec4 position_interp; -#endif +#ifndef DISABLE_LIGHT_SPOT -in highp vec3 vertex_interp; -in vec3 normal_interp; +layout(std140) uniform SpotLightData { // ubo:6 -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) -in vec3 tangent_interp; -in vec3 binormal_interp; + LightData spot_lights[MAX_LIGHT_DATA_STRUCTS]; +}; +uniform uint spot_light_indices[MAX_FORWARD_LIGHTS]; +uniform int spot_light_count; #endif -#if defined(ENABLE_COLOR_INTERP) -in vec4 color_interp; +#ifdef USE_ADDITIVE_LIGHTING +uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #endif -#if defined(ENABLE_UV_INTERP) -in vec2 uv_interp; -#endif +#endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) -#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) -in vec2 uv2_interp; -#endif +uniform highp sampler2D screen_texture; // texunit:-5 +uniform highp sampler2D depth_buffer; // texunit:-6 -in vec3 view_interp; +uniform highp mat4 world_transform; +uniform mediump float opaque_prepass_threshold; layout(location = 0) out vec4 frag_color; @@ -985,95 +537,31 @@ vec3 F0(float metallic, float specular, vec3 albedo) { return mix(vec3(dielectric), albedo, vec3(metallic)); } -/* clang-format off */ - -FRAGMENT_SHADER_GLOBALS - -/* clang-format on */ - -#ifdef RENDER_DEPTH_DUAL_PARABOLOID - -in highp float dp_clip; - -#endif - -#ifdef USE_LIGHTING - -// This returns the G_GGX function divided by 2 cos_theta_m, where in practice cos_theta_m is either N.L or N.V. -// We're dividing this factor off because the overall term we'll end up looks like -// (see, for example, the first unnumbered equation in B. Burley, "Physically Based Shading at Disney", SIGGRAPH 2012): -// -// F(L.V) D(N.H) G(N.L) G(N.V) / (4 N.L N.V) -// -// We're basically regouping this as -// -// F(L.V) D(N.H) [G(N.L)/(2 N.L)] [G(N.V) / (2 N.V)] -// -// and thus, this function implements the [G(N.m)/(2 N.m)] part with m = L or V. -// -// The contents of the D and G (G1) functions (GGX) are taken from -// E. Heitz, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs", J. Comp. Graph. Tech. 3 (2) (2014). -// Eqns 71-72 and 85-86 (see also Eqns 43 and 80). - -/* -float G_GGX_2cos(float cos_theta_m, float alpha) { - // Schlick's approximation - // C. Schlick, "An Inexpensive BRDF Model for Physically-based Rendering", Computer Graphics Forum. 13 (3): 233 (1994) - // Eq. (19), although see Heitz (2014) the about the problems with his derivation. - // It nevertheless approximates GGX well with k = alpha/2. - float k = 0.5 * alpha; - return 0.5 / (cos_theta_m * (1.0 - k) + k); - - // float cos2 = cos_theta_m * cos_theta_m; - // float sin2 = (1.0 - cos2); - // return 1.0 / (cos_theta_m + sqrt(cos2 + alpha * alpha * sin2)); -} -*/ - -// This approximates G_GGX_2cos(cos_theta_l, alpha) * G_GGX_2cos(cos_theta_v, alpha) -// See Filament docs, Specular G section. -float V_GGX(float cos_theta_l, float cos_theta_v, float alpha) { - return 0.5 / mix(2.0 * cos_theta_l * cos_theta_v, cos_theta_l + cos_theta_v, alpha); -} - +#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) float D_GGX(float cos_theta_m, float alpha) { - float alpha2 = alpha * alpha; - float d = 1.0 + (alpha2 - 1.0) * cos_theta_m * cos_theta_m; - return alpha2 / (M_PI * d * d); -} - -/* -float G_GGX_anisotropic_2cos(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { - float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0 - cos2); - float s_x = alpha_x * cos_phi; - float s_y = alpha_y * sin_phi; - return 1.0 / max(cos_theta_m + sqrt(cos2 + (s_x * s_x + s_y * s_y) * sin2), 0.001); + float a = cos_theta_m * alpha; + float k = alpha / (1.0 - cos_theta_m * cos_theta_m + a * a); + return k * k * (1.0 / M_PI); } -*/ -// This approximates G_GGX_anisotropic_2cos(cos_theta_l, ...) * G_GGX_anisotropic_2cos(cos_theta_v, ...) -// See Filament docs, Anisotropic specular BRDF section. -float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) { - float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV)); - float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL)); - return 0.5 / (Lambda_V + Lambda_L); +// From Earl Hammon, Jr. "PBR Diffuse Lighting for GGX+Smith Microsurfaces" https://www.gdcvault.com/play/1024478/PBR-Diffuse-Lighting-for-GGX +float V_GGX(float NdotL, float NdotV, float alpha) { + return 0.5 / mix(2.0 * NdotL * NdotV, NdotL + NdotV, alpha); } -float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) { +float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi) { float alpha2 = alpha_x * alpha_y; - highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH); + highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * cos_theta_m); highp float v2 = dot(v, v); float w2 = alpha2 / v2; float D = alpha2 * w2 * w2 * (1.0 / M_PI); return D; +} - /* float cos2 = cos_theta_m * cos_theta_m; - float sin2 = (1.0 - cos2); - float r_x = cos_phi / alpha_x; - float r_y = sin_phi / alpha_y; - float d = cos2 + sin2 * (r_x * r_x + r_y * r_y); - return 1.0 / max(M_PI * alpha_x * alpha_y * d * d, 0.001); */ +float V_GGX_anisotropic(float alpha_x, float alpha_y, float TdotV, float TdotL, float BdotV, float BdotL, float NdotV, float NdotL) { + float Lambda_V = NdotL * length(vec3(alpha_x * TdotV, alpha_y * BdotV, NdotV)); + float Lambda_L = NdotV * length(vec3(alpha_x * TdotL, alpha_y * BdotL, NdotL)); + return 0.5 / (Lambda_V + Lambda_L); } float SchlickFresnel(float u) { @@ -1082,109 +570,59 @@ float SchlickFresnel(float u) { return m2 * m2 * m; // pow(m,5) } -float GTR1(float NdotH, float a) { - if (a >= 1.0) - return 1.0 / M_PI; - float a2 = a * a; - float t = 1.0 + (a2 - 1.0) * NdotH * NdotH; - return (a2 - 1.0) / (M_PI * log(a2) * t); -} - -void light_compute( - vec3 N, - vec3 L, - vec3 V, - vec3 B, - vec3 T, - vec3 light_color, - vec3 attenuation, - vec3 diffuse_color, - vec3 transmission, - float specular_blob_intensity, - float roughness, - float metallic, - float specular, - float rim, - float rim_tint, - float clearcoat, - float clearcoat_gloss, - float anisotropy, - inout vec3 diffuse_light, - inout vec3 specular_light, - inout float alpha) { -//this makes lights behave closer to linear, but then addition of lights looks bad -//better left disabled - -//#define SRGB_APPROX(m_var) m_var = pow(m_var,0.4545454545); -/* -#define SRGB_APPROX(m_var) {\ - float S1 = sqrt(m_var);\ - float S2 = sqrt(S1);\ - float S3 = sqrt(S2);\ - m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ - } -*/ -#define SRGB_APPROX(m_var) +void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, +#ifdef LIGHT_BACKLIGHT_USED + vec3 backlight, +#endif +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_roughness, vec3 vertex_normal, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 B, vec3 T, float anisotropy, +#endif + inout vec3 diffuse_light, inout vec3 specular_light) { #if defined(USE_LIGHT_SHADER_CODE) // light is written by the light shader vec3 normal = N; - vec3 albedo = diffuse_color; vec3 light = L; vec3 view = V; /* clang-format off */ -LIGHT_SHADER_CODE + +#CODE : LIGHT /* clang-format on */ #else - float NdotL = dot(N, L); + float NdotL = min(A + dot(N, L), 1.0); float cNdotL = max(NdotL, 0.0); // clamped NdotL float NdotV = dot(N, V); - float cNdotV = max(abs(NdotV), 1e-6); + float cNdotV = max(NdotV, 1e-4); -#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) vec3 H = normalize(V + L); #endif -#if defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) - float cNdotH = max(dot(N, H), 0.0); +#if defined(SPECULAR_SCHLICK_GGX) + float cNdotH = clamp(A + dot(N, H), 0.0, 1.0); #endif -#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT) - float cLdotH = max(dot(L, H), 0.0); +#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_CLEARCOAT_USED) + float cLdotH = clamp(A + dot(L, H), 0.0, 1.0); #endif if (metallic < 1.0) { -#if defined(DIFFUSE_OREN_NAYAR) - vec3 diffuse_brdf_NL; -#else float diffuse_brdf_NL; // BRDF times N.L for calculating diffuse radiance -#endif #if defined(DIFFUSE_LAMBERT_WRAP) // energy conserving lambert wrap shader diffuse_brdf_NL = max(0.0, (NdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))); - -#elif defined(DIFFUSE_OREN_NAYAR) - - { - // see http://mimosa-pudica.net/improved-oren-nayar.html - float LdotV = dot(L, V); - - float s = LdotV - NdotL * NdotV; - float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); - - float sigma2 = roughness * roughness; // TODO: this needs checking - vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13)); - float B = 0.45 * sigma2 / (sigma2 + 0.09); - - diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI); - } - #elif defined(DIFFUSE_TOON) diffuse_brdf_NL = smoothstep(-roughness, max(roughness, 0.01), NdotL); @@ -1196,230 +634,253 @@ LIGHT_SHADER_CODE float FdV = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotV); float FdL = 1.0 + FD90_minus_1 * SchlickFresnel(cNdotL); diffuse_brdf_NL = (1.0 / M_PI) * FdV * FdL * cNdotL; - /* - float energyBias = mix(roughness, 0.0, 0.5); - float energyFactor = mix(roughness, 1.0, 1.0 / 1.51); - float fd90 = energyBias + 2.0 * VoH * VoH * roughness; - float f0 = 1.0; - float lightScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotL, 5.0); - float viewScatter = f0 + (fd90 - f0) * pow(1.0 - cNdotV, 5.0); - - diffuse_brdf_NL = lightScatter * viewScatter * energyFactor; - */ } #else // lambert diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif - SRGB_APPROX(diffuse_brdf_NL) - - diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; + diffuse_light += light_color * diffuse_brdf_NL * attenuation; -#if defined(TRANSMISSION_USED) - diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation; +#if defined(LIGHT_BACKLIGHT_USED) + diffuse_light += light_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * backlight * attenuation; #endif -#if defined(LIGHT_USE_RIM) +#if defined(LIGHT_RIM_USED) float rim_light = pow(max(0.0, 1.0 - cNdotV), max(0.0, (1.0 - roughness) * 16.0)); - diffuse_light += rim_light * rim * mix(vec3(1.0), diffuse_color, rim_tint) * light_color; + diffuse_light += rim_light * rim * mix(vec3(1.0), albedo, rim_tint) * light_color; #endif } - if (roughness > 0.0) { + if (roughness > 0.0) { // FIXME: roughness == 0 should not disable specular light entirely -#if defined(SPECULAR_SCHLICK_GGX) - vec3 specular_brdf_NL = vec3(0.0); -#else - float specular_brdf_NL = 0.0; -#endif - -#if defined(SPECULAR_BLINN) - - //normalized blinn - float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; - float blinn = pow(cNdotH, shininess) * cNdotL; - blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); - specular_brdf_NL = blinn; - -#elif defined(SPECULAR_PHONG) - - vec3 R = normalize(-reflect(L, N)); - float cRdotV = max(0.0, dot(R, V)); - float shininess = exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25; - float phong = pow(cRdotV, shininess); - phong *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); - specular_brdf_NL = (phong) / max(4.0 * cNdotV * cNdotL, 0.75); + // D -#elif defined(SPECULAR_TOON) +#if defined(SPECULAR_TOON) vec3 R = normalize(-reflect(L, N)); float RdotV = dot(R, V); float mid = 1.0 - roughness; mid *= mid; - specular_brdf_NL = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid; + float intensity = smoothstep(mid - roughness * 0.5, mid + roughness * 0.5, RdotV) * mid; + diffuse_light += light_color * intensity * attenuation * specular_amount; // write to diffuse_light, as in toon shading you generally want no reflection #elif defined(SPECULAR_DISABLED) // none.. + #elif defined(SPECULAR_SCHLICK_GGX) // shlick+ggx as default - -#if defined(LIGHT_USE_ANISOTROPY) float alpha_ggx = roughness * roughness; +#if defined(LIGHT_ANISOTROPY_USED) + float aspect = sqrt(1.0 - anisotropy * 0.9); float ax = alpha_ggx / aspect; float ay = alpha_ggx * aspect; float XdotH = dot(T, H); float YdotH = dot(B, H); - float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH, cNdotH); - //float G = G_GGX_anisotropic_2cos(cNdotL, ax, ay, XdotH, YdotH) * G_GGX_anisotropic_2cos(cNdotV, ax, ay, XdotH, YdotH); + float D = D_GGX_anisotropic(cNdotH, ax, ay, XdotH, YdotH); float G = V_GGX_anisotropic(ax, ay, dot(T, V), dot(T, L), dot(B, V), dot(B, L), cNdotV, cNdotL); - -#else - float alpha_ggx = roughness * roughness; +#else // LIGHT_ANISOTROPY_USED float D = D_GGX(cNdotH, alpha_ggx); - //float G = G_GGX_2cos(cNdotL, alpha_ggx) * G_GGX_2cos(cNdotV, alpha_ggx); float G = V_GGX(cNdotL, cNdotV, alpha_ggx); -#endif - // F - vec3 f0 = F0(metallic, specular, diffuse_color); +#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; - specular_brdf_NL = cNdotL * D * F * G; + vec3 specular_brdf_NL = cNdotL * D * F * G; + specular_light += specular_brdf_NL * light_color * attenuation * specular_amount; #endif - SRGB_APPROX(specular_brdf_NL) - specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; - -#if defined(LIGHT_USE_CLEARCOAT) +#if defined(LIGHT_CLEARCOAT_USED) + // Clearcoat ignores normal_map, use vertex normal instead + float ccNdotL = max(min(A + dot(vertex_normal, L), 1.0), 0.0); + float ccNdotH = clamp(A + dot(vertex_normal, H), 0.0, 1.0); + float ccNdotV = max(dot(vertex_normal, V), 1e-4); #if !defined(SPECULAR_SCHLICK_GGX) float cLdotH5 = SchlickFresnel(cLdotH); #endif - float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_gloss)); + float Dr = D_GGX(ccNdotH, mix(0.001, 0.1, clearcoat_roughness)); + float Gr = 0.25 / (cLdotH * cLdotH); float Fr = mix(.04, 1.0, cLdotH5); - //float Gr = G_GGX_2cos(cNdotL, .25) * G_GGX_2cos(cNdotV, .25); - float Gr = V_GGX(cNdotL, cNdotV, 0.25); - - float clearcoat_specular_brdf_NL = 0.25 * clearcoat * Gr * Fr * Dr * cNdotL; + float clearcoat_specular_brdf_NL = clearcoat * Gr * Fr * Dr * cNdotL; - specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation; -#endif + specular_light += clearcoat_specular_brdf_NL * light_color * attenuation * specular_amount; + // TODO: Clearcoat adds light to the scene right now (it is non-energy conserving), both diffuse and specular need to be scaled by (1.0 - FR) + // but to do so we need to rearrange this entire function +#endif // LIGHT_CLEARCOAT_USED } #ifdef USE_SHADOW_TO_OPACITY - alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0)); + alpha = min(alpha, clamp(1.0 - attenuation, 0.0, 1.0)); #endif -#endif //defined(USE_LIGHT_SHADER_CODE) +#endif //defined(LIGHT_CODE_USED) } -#endif -// shadows - -#ifdef USE_SHADOW - -#ifdef USE_RGBA_SHADOWS - -#define SHADOW_DEPTH(m_val) dot(m_val, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) - -#else - -#define SHADOW_DEPTH(m_val) (m_val).r +float get_omni_attenuation(float distance, float inv_range, float decay) { + float nd = distance * inv_range; + nd *= nd; + nd *= nd; // nd^4 + nd = max(1.0 - nd, 0.0); + nd *= nd; // nd^2 + return nd * pow(max(distance, 0.0001), -decay); +} +void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, +#ifdef LIGHT_BACKLIGHT_USED + vec3 backlight, #endif - -#define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, SHADOW_DEPTH(texture(p_shadow, p_pos))) -#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, SHADOW_DEPTH(textureProj(p_shadow, p_pos))) - -float sample_shadow(highp sampler2D shadow, highp vec4 spos) { -#ifdef SHADOW_MODE_PCF_13 - - spos.xyz /= spos.w; - vec2 pos = spos.xy; - float depth = spos.z; - - float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, -shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, -shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x * 2.0, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x * 2.0, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth); - return avg * (1.0 / 13.0); +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, #endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_roughness, vec3 vertex_normal, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 binormal, vec3 tangent, float anisotropy, +#endif + inout vec3 diffuse_light, inout vec3 specular_light) { + vec3 light_rel_vec = omni_lights[idx].position - vertex; + float light_length = length(light_rel_vec); + float omni_attenuation = get_omni_attenuation(light_length, omni_lights[idx].inv_radius, omni_lights[idx].attenuation); + vec3 color = omni_lights[idx].color; + float size_A = 0.0; -#ifdef SHADOW_MODE_PCF_5 - - spos.xyz /= spos.w; - vec2 pos = spos.xy; - float depth = spos.z; + 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.0 / sqrt(1.0 + t * t)); + } - float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, shadow_pixel_size.y), depth); - avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(0.0, -shadow_pixel_size.y), depth); - return avg * (1.0 / 5.0); + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, +#ifdef LIGHT_BACKLIGHT_USED + backlight, +#endif +#ifdef LIGHT_RIM_USED + rim * omni_attenuation, rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_roughness, vertex_normal, +#endif +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, +#endif + diffuse_light, + specular_light); +} +void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, +#ifdef LIGHT_BACKLIGHT_USED + vec3 backlight, +#endif +#ifdef LIGHT_RIM_USED + float rim, float rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + float clearcoat, float clearcoat_roughness, vec3 vertex_normal, +#endif +#ifdef LIGHT_ANISOTROPY_USED + vec3 binormal, vec3 tangent, float anisotropy, #endif + inout vec3 diffuse_light, + inout vec3 specular_light) { -#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) + vec3 light_rel_vec = spot_lights[idx].position - vertex; + float light_length = length(light_rel_vec); + float spot_attenuation = get_omni_attenuation(light_length, spot_lights[idx].inv_radius, spot_lights[idx].attenuation); + vec3 spot_dir = spot_lights[idx].direction; + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_lights[idx].cone_angle); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_lights[idx].cone_angle)); + spot_attenuation *= 1.0 - pow(spot_rim, spot_lights[idx].cone_attenuation); + vec3 color = spot_lights[idx].color; + + float size_A = 0.0; + + 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.0 / sqrt(1.0 + t * t)); + } - return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos); + light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, +#ifdef LIGHT_BACKLIGHT_USED + backlight, +#endif +#ifdef LIGHT_RIM_USED + rim * spot_attenuation, rim_tint, +#endif +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_roughness, vertex_normal, #endif +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, +#endif + diffuse_light, specular_light); } +#endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) -#endif +#ifndef MODE_RENDER_DEPTH +vec4 fog_process(vec3 vertex) { + vec3 fog_color = scene_data.fog_light_color; -#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) +#ifdef USE_RADIANCE_MAP +/* + if (scene_data.fog_aerial_perspective > 0.0) { + vec3 sky_fog_color = vec3(0.0); + vec3 cube_view = scene_data.radiance_inverse_xform * vertex; + // mip_level always reads from the second mipmap and higher so the fog is always slightly blurred + float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)); -#if defined(USE_VERTEX_LIGHTING) + sky_fog_color = textureLod(radiance_map, cube_view, mip_level * RADIANCE_MAX_LOD).rgb; -in vec4 fog_interp; + fog_color = mix(fog_color, sky_fog_color, scene_data.fog_aerial_perspective); + } + */ +#endif + +#ifndef DISABLE_LIGHT_DIRECTIONAL + if (scene_data.fog_sun_scatter > 0.001) { + vec4 sun_scatter = vec4(0.0); + float sun_total = 0.0; + vec3 view = normalize(vertex); + for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + vec3 light_color = directional_lights[i].color * directional_lights[i].energy; + float light_amount = pow(max(dot(view, directional_lights[i].direction), 0.0), 8.0); + fog_color += light_color * light_amount * scene_data.fog_sun_scatter; + } + } +#endif // !DISABLE_LIGHT_DIRECTIONAL -#else -uniform mediump vec4 fog_color_base; -#ifdef LIGHT_MODE_DIRECTIONAL -uniform mediump vec4 fog_sun_color_amount; -#endif + float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density)); -uniform bool fog_transmit_enabled; -uniform mediump float fog_transmit_curve; + if (abs(scene_data.fog_height_density) >= 0.0001) { + float y = (scene_data.inv_view_matrix * vec4(vertex, 1.0)).y; -#ifdef FOG_DEPTH_ENABLED -uniform highp float fog_depth_begin; -uniform mediump float fog_depth_curve; -uniform mediump float fog_max_distance; -#endif + float y_dist = y - scene_data.fog_height; -#ifdef FOG_HEIGHT_ENABLED -uniform highp float fog_height_min; -uniform highp float fog_height_max; -uniform mediump float fog_height_curve; -#endif + float vfog_amount = 1.0 - exp(min(0.0, y_dist * scene_data.fog_height_density)); -#endif //vertex lit -#endif //fog + fog_amount = max(vfog_amount, fog_amount); + } -void main() { -#ifdef RENDER_DEPTH_DUAL_PARABOLOID + return vec4(fog_color, fog_amount); +} - if (dp_clip > 0.0) - discard; -#endif - highp vec3 vertex = vertex_interp; +#endif // !MODE_RENDER_DEPTH + +void main() { + //lay out everything, whatever is unused is optimized away anyway + vec3 vertex = vertex_interp; vec3 view = -normalize(vertex_interp); vec3 albedo = vec3(1.0); - vec3 transmission = vec3(0.0); + vec3 backlight = vec3(0.0); + vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0); + float transmittance_depth = 0.0; + float transmittance_boost = 0.0; float metallic = 0.0; float specular = 0.5; vec3 emission = vec3(0.0); @@ -1427,620 +888,308 @@ void main() { float rim = 0.0; float rim_tint = 0.0; float clearcoat = 0.0; - float clearcoat_gloss = 0.0; + float clearcoat_roughness = 0.0; float anisotropy = 0.0; vec2 anisotropy_flow = vec2(1.0, 0.0); - float sss_strength = 0.0; //unused - // gl_FragDepth is not available in GLES2, so writing to DEPTH is not converted to gl_FragDepth by Godot compiler resulting in a - // compile error because DEPTH is not a variable. - float m_DEPTH = 0.0; - - float alpha = 1.0; - float side = 1.0; - - float specular_blob_intensity = 1.0; -#if defined(SPECULAR_TOON) - specular_blob_intensity *= specular * 2.0; + vec4 fog = vec4(0.0); +#if defined(CUSTOM_RADIANCE_USED) + vec4 custom_radiance = vec4(0.0); +#endif +#if defined(CUSTOM_IRRADIANCE_USED) + vec4 custom_irradiance = vec4(0.0); #endif -#if defined(ENABLE_AO) float ao = 1.0; float ao_light_affect = 0.0; -#endif -#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) - vec3 binormal = normalize(binormal_interp) * side; - vec3 tangent = normalize(tangent_interp) * side; + float alpha = 1.0; + +#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) + vec3 binormal = normalize(binormal_interp); + vec3 tangent = normalize(tangent_interp); #else vec3 binormal = vec3(0.0); vec3 tangent = vec3(0.0); #endif - vec3 normal = normalize(normal_interp) * side; -#if defined(ENABLE_NORMALMAP) - vec3 normalmap = vec3(0.5); +#ifdef NORMAL_USED + vec3 normal = normalize(normal_interp); + +#if defined(DO_SIDE_CHECK) + if (!gl_FrontFacing) { + normal = -normal; + } #endif - float normaldepth = 1.0; -#if defined(ALPHA_SCISSOR_USED) - float alpha_scissor = 0.5; +#endif //NORMAL_USED + +#ifdef UV_USED + vec2 uv = uv_interp; #endif -#if defined(SCREEN_UV_USED) - vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; +#if defined(UV2_USED) || defined(USE_LIGHTMAP) + vec2 uv2 = uv2_interp; #endif - { - /* clang-format off */ +#if defined(COLOR_USED) + vec4 color = color_interp; +#endif -FRAGMENT_SHADER_CODE +#if defined(NORMAL_MAP_USED) - /* clang-format on */ - } + vec3 normal_map = vec3(0.5); +#endif -#if defined(ENABLE_NORMALMAP) - normalmap.xy = normalmap.xy * 2.0 - 1.0; - normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); + float normal_map_depth = 1.0; - normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; - //normal = normalmap; -#endif + vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size; - normal = normalize(normal); + float sss_strength = 0.0; - vec3 N = normal; +#ifdef ALPHA_SCISSOR_USED + float alpha_scissor_threshold = 1.0; +#endif // ALPHA_SCISSOR_USED - vec3 specular_light = vec3(0.0, 0.0, 0.0); - vec3 diffuse_light = vec3(0.0, 0.0, 0.0); - vec3 ambient_light = vec3(0.0, 0.0, 0.0); +#ifdef ALPHA_HASH_USED + float alpha_hash_scale = 1.0; +#endif // ALPHA_HASH_USED - vec3 eye_position = view; +#ifdef ALPHA_ANTIALIASING_EDGE_USED + float alpha_antialiasing_edge = 0.0; + vec2 alpha_texture_coordinate = vec2(0.0, 0.0); +#endif // ALPHA_ANTIALIASING_EDGE_USED + { +#CODE : FRAGMENT + } -#if !defined(USE_SHADOW_TO_OPACITY) +#ifndef USE_SHADOW_TO_OPACITY #if defined(ALPHA_SCISSOR_USED) - if (alpha < alpha_scissor) { + if (alpha < alpha_scissor_threshold) { discard; } #endif // ALPHA_SCISSOR_USED -#ifdef USE_DEPTH_PREPASS - if (alpha < 0.1) { +#ifdef USE_OPAQUE_PREPASS +#if !defined(ALPHA_SCISSOR_USED) + + if (alpha < opaque_prepass_threshold) { discard; } -#endif // USE_DEPTH_PREPASS -#endif // !USE_SHADOW_TO_OPACITY - -#ifdef BASE_PASS +#endif // not ALPHA_SCISSOR_USED +#endif // USE_OPAQUE_PREPASS - // IBL precalculations - float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0); - vec3 f0 = F0(metallic, specular, albedo); - vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); +#endif // !USE_SHADOW_TO_OPACITY -#ifdef AMBIENT_LIGHT_DISABLED - ambient_light = vec3(0.0, 0.0, 0.0); -#else +#ifdef NORMAL_MAP_USED -#ifdef USE_RADIANCE_MAP + normal_map.xy = normal_map.xy * 2.0 - 1.0; + normal_map.z = sqrt(max(0.0, 1.0 - dot(normal_map.xy, normal_map.xy))); //always ignore Z, as it can be RG packed, Z may be pos/neg, etc. - vec3 ref_vec = reflect(-eye_position, N); - ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz); + normal = normalize(mix(normal, tangent * normal_map.x + binormal * normal_map.y + normal * normal_map.z, normal_map_depth)); - ref_vec.z *= -1.0; +#endif - specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; -#ifndef USE_LIGHTMAP - { - vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); - vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, 4.0).xyz * bg_energy; - env_ambient *= 1.0 - F; +#ifdef LIGHT_ANISOTROPY_USED - ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution); + if (anisotropy > 0.01) { + //rotation matrix + mat3 rot = mat3(tangent, binormal, normal); + //make local to space + tangent = normalize(rot * vec3(anisotropy_flow.x, anisotropy_flow.y, 0.0)); + binormal = normalize(rot * vec3(-anisotropy_flow.y, anisotropy_flow.x, 0.0)); } + #endif -#else +#ifndef MODE_RENDER_DEPTH - ambient_light = ambient_color.rgb; - specular_light = bg_color.rgb * bg_energy; +#ifndef CUSTOM_FOG_USED +#ifndef DISABLE_FOG + // fog must be processed as early as possible and then packed. + // to maximize VGPR usage -#endif -#endif // AMBIENT_LIGHT_DISABLED - ambient_light *= ambient_energy; + if (scene_data.fog_enabled) { + fog = fog_process(vertex); + } +#endif // !DISABLE_FOG +#endif //!CUSTOM_FOG_USED -#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + uint fog_rg = packHalf2x16(fog.rg); + uint fog_ba = packHalf2x16(fog.ba); - vec4 ambient_accum = vec4(0.0); - vec4 reflection_accum = vec4(0.0); +#endif //!MODE_RENDER_DEPTH -#ifdef USE_REFLECTION_PROBE1 +#ifndef MODE_RENDER_DEPTH - reflection_process(reflection_probe1, -#ifdef USE_VERTEX_LIGHTING - refprobe1_reflection_normal_blend.rgb, -#ifndef USE_LIGHTMAP - refprobe1_ambient_normal, -#endif - refprobe1_reflection_normal_blend.a, -#else - normal_interp, vertex_interp, refprobe1_local_matrix, - refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset, -#endif - refprobe1_exterior, refprobe1_intensity, refprobe1_ambient, roughness, - ambient_light, specular_light, reflection_accum, ambient_accum); + // Convert colors to linear + albedo = srgb_to_linear(albedo); + emission = srgb_to_linear(emission); + // TODO Backlight and transmittance when used +#ifndef MODE_UNSHADED + vec3 f0 = F0(metallic, specular, albedo); + vec3 specular_light = vec3(0.0, 0.0, 0.0); + vec3 diffuse_light = vec3(0.0, 0.0, 0.0); + vec3 ambient_light = vec3(0.0, 0.0, 0.0); -#endif // USE_REFLECTION_PROBE1 +#ifdef BASE_PASS + /////////////////////// LIGHTING ////////////////////////////// -#ifdef USE_REFLECTION_PROBE2 + // IBL precalculations + float ndotv = clamp(dot(normal, view), 0.0, 1.0); + vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0); - reflection_process(reflection_probe2, -#ifdef USE_VERTEX_LIGHTING - refprobe2_reflection_normal_blend.rgb, -#ifndef USE_LIGHTMAP - refprobe2_ambient_normal, -#endif - refprobe2_reflection_normal_blend.a, +#ifdef USE_RADIANCE_MAP + if (scene_data.use_reflection_cubemap) { +#ifdef LIGHT_ANISOTROPY_USED + // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy + vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; + vec3 anisotropic_tangent = cross(anisotropic_direction, view); + vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); + vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); + vec3 ref_vec = reflect(-view, bent_normal); #else - normal_interp, vertex_interp, refprobe2_local_matrix, - refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset, + 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; + specular_light = srgb_to_linear(specular_light); + specular_light *= horizon * horizon; + specular_light *= scene_data.ambient_light_color_energy.a; + } #endif - refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness, - ambient_light, specular_light, reflection_accum, ambient_accum); -#endif // USE_REFLECTION_PROBE2 + // Calculate Reflection probes + // Calculate Lightmaps - if (reflection_accum.a > 0.0) { - specular_light = reflection_accum.rgb / reflection_accum.a; - } +#if defined(CUSTOM_RADIANCE_USED) + specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); +#endif // CUSTOM_RADIANCE_USED #ifndef USE_LIGHTMAP - if (ambient_accum.a > 0.0) { - ambient_light = ambient_accum.rgb / ambient_accum.a; - } + //lightmap overrides everything + if (scene_data.use_ambient_light) { + ambient_light = scene_data.ambient_light_color_energy.rgb; +#ifdef USE_RADIANCE_MAP + if (scene_data.use_ambient_cubemap) { + vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; + vec3 cubemap_ambient = textureLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).rgb; + cubemap_ambient = srgb_to_linear(cubemap_ambient); + ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); + } #endif + } +#endif // USE_LIGHTMAP -#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) +#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; + + // convert ao to direct light ao + ao = mix(1.0, ao, ao_light_affect); - // environment BRDF approximation { #if defined(DIFFUSE_TOON) //simplify for toon, as specular_light *= specular * metallic * albedo * 2.0; #else - // scales the specular reflections, needs to be computed before lighting happens, - // but after environment and reflection probes are added - //TODO: this curve is not really designed for gammaspace, should be adjusted + // scales the specular reflections, needs to be be computed before lighting happens, + // but after environment, GI, and reflection probes are added + // Environment brdf approximation (Lazarov 2013) + // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); vec4 r = roughness * c0 + c1; + float ndotv = clamp(dot(normal, view), 0.0, 1.0); + 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 * F + env.y; - -#endif - } - -#ifdef USE_LIGHTMAP - //ambient light will come entirely from lightmap is lightmap is used - ambient_light = texture(lightmap, uv2_interp).rgb * lightmap_energy; -#endif - -#ifdef USE_LIGHTMAP_CAPTURE - { - vec3 cone_dirs[12]; - cone_dirs[0] = vec3(0.0, 0.0, 1.0); - cone_dirs[1] = vec3(0.866025, 0.0, 0.5); - cone_dirs[2] = vec3(0.267617, 0.823639, 0.5); - cone_dirs[3] = vec3(-0.700629, 0.509037, 0.5); - cone_dirs[4] = vec3(-0.700629, -0.509037, 0.5); - cone_dirs[5] = vec3(0.267617, -0.823639, 0.5); - cone_dirs[6] = vec3(0.0, 0.0, -1.0); - cone_dirs[7] = vec3(0.866025, 0.0, -0.5); - cone_dirs[8] = vec3(0.267617, 0.823639, -0.5); - cone_dirs[9] = vec3(-0.700629, 0.509037, -0.5); - cone_dirs[10] = vec3(-0.700629, -0.509037, -0.5); - cone_dirs[11] = vec3(0.267617, -0.823639, -0.5); - - vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz; - vec4 captured = vec4(0.0); - float sum = 0.0; - for (int i = 0; i < 12; i++) { - float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect - captured += lightmap_captures[i] * amount; - sum += amount; - } - - captured /= sum; - - if (lightmap_capture_sky) { - ambient_light = mix(ambient_light, captured.rgb, captured.a); - } else { - ambient_light = captured.rgb; - } - } + specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, 0.0, 1.0); #endif - -#endif //BASE PASS - -// -// Lighting -// -#ifdef USE_LIGHTING - -#ifndef USE_VERTEX_LIGHTING - vec3 L; -#endif - vec3 light_att = vec3(1.0); - -#ifdef LIGHT_MODE_OMNI - -#ifndef USE_VERTEX_LIGHTING - vec3 light_vec = light_position - vertex; - float light_length = length(light_vec); - - float normalized_distance = light_length / light_range; - if (normalized_distance < 1.0) { - float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); - - light_att = vec3(omni_attenuation); - } else { - light_att = vec3(0.0); - } - L = normalize(light_vec); - -#endif - -#if !defined(SHADOWS_DISABLED) - -#ifdef USE_SHADOW - { - highp vec4 splane = shadow_coord; - float shadow_len = length(splane.xyz); - - splane.xyz = normalize(splane.xyz); - - vec4 clamp_rect = light_clamp; - - if (splane.z >= 0.0) { - splane.z += 1.0; - - clamp_rect.y += clamp_rect.w; - } else { - splane.z = 1.0 - splane.z; - } - - splane.xy /= splane.z; - splane.xy = splane.xy * 0.5 + 0.5; - splane.z = shadow_len / light_range; - - splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; - splane.w = 1.0; - - float shadow = sample_shadow(light_shadow_atlas, splane); - - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); } -#endif - -#endif //SHADOWS_DISABLED - -#endif //type omni - -#ifdef LIGHT_MODE_DIRECTIONAL - -#ifndef USE_VERTEX_LIGHTING - vec3 light_vec = -light_direction; - L = normalize(light_vec); -#endif - float depth_z = -vertex.z; - -#if !defined(SHADOWS_DISABLED) - -#ifdef USE_SHADOW - -#ifdef USE_VERTEX_LIGHTING - //compute shadows in a mobile friendly way -#ifdef LIGHT_USE_PSSM4 - //take advantage of prefetch - float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); - float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); - float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3); - float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4); +#endif // BASE_PASS - if (depth_z < light_split_offsets.w) { - float pssm_fade = 0.0; - float shadow_att = 1.0; -#ifdef LIGHT_USE_PSSM_BLEND - float shadow_att2 = 1.0; - float pssm_blend = 0.0; - bool use_blend = true; +#ifndef DISABLE_LIGHT_DIRECTIONAL + //diffuse_light = normal; // + for (uint i = uint(0); i < scene_data.directional_light_count; i++) { + light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, +#ifdef LIGHT_BACKLIGHT_USED + backlight, #endif - if (depth_z < light_split_offsets.y) { - if (depth_z < light_split_offsets.x) { - shadow_att = shadow1; - -#ifdef LIGHT_USE_PSSM_BLEND - shadow_att2 = shadow2; - - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#ifdef LIGHT_RIM_USED + rim, rim_tint, #endif - } else { - shadow_att = shadow2; - -#ifdef LIGHT_USE_PSSM_BLEND - shadow_att2 = shadow3; - - pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_roughness, normalize(normal_interp), #endif - } - } else { - if (depth_z < light_split_offsets.z) { - shadow_att = shadow3; - -#if defined(LIGHT_USE_PSSM_BLEND) - shadow_att2 = shadow4; - pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#ifdef LIGHT_ANISOTROPY_USED + binormal, + tangent, anisotropy, #endif - - } else { - shadow_att = shadow4; - pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); - -#if defined(LIGHT_USE_PSSM_BLEND) - use_blend = false; -#endif - } - } -#if defined(LIGHT_USE_PSSM_BLEND) - if (use_blend) { - shadow_att = mix(shadow_att, shadow_att2, pssm_blend); - } -#endif - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att); + diffuse_light, + specular_light); } +#endif //!DISABLE_LIGHT_DIRECTIONAL -#endif //LIGHT_USE_PSSM4 - -#ifdef LIGHT_USE_PSSM2 - - //take advantage of prefetch - float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); - float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); - - if (depth_z < light_split_offsets.y) { - float shadow_att = 1.0; - float pssm_fade = 0.0; - -#ifdef LIGHT_USE_PSSM_BLEND - float shadow_att2 = 1.0; - float pssm_blend = 0.0; - bool use_blend = true; -#endif - if (depth_z < light_split_offsets.x) { - float pssm_fade = 0.0; - shadow_att = shadow1; - -#ifdef LIGHT_USE_PSSM_BLEND - shadow_att2 = shadow2; - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); -#endif - } else { - shadow_att = shadow2; - pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); -#ifdef LIGHT_USE_PSSM_BLEND - use_blend = false; -#endif - } -#ifdef LIGHT_USE_PSSM_BLEND - if (use_blend) { - shadow_att = mix(shadow_att, shadow_att2, pssm_blend); +#ifndef DISABLE_LIGHT_OMNI + for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= omni_light_count) { + break; } + light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha, +#ifdef LIGHT_BACKLIGHT_USED + backlight, #endif - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow_att); - } - -#endif //LIGHT_USE_PSSM2 - -#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) - - light_att *= mix(shadow_color.rgb, vec3(1.0), sample_shadow(light_directional_shadow, shadow_coord)); -#endif //orthogonal - -#else //fragment version of pssm - - { -#ifdef LIGHT_USE_PSSM4 - if (depth_z < light_split_offsets.w) { -#elif defined(LIGHT_USE_PSSM2) - if (depth_z < light_split_offsets.y) { -#else - if (depth_z < light_split_offsets.x) { -#endif //pssm2 - - highp vec4 pssm_coord; - float pssm_fade = 0.0; - -#ifdef LIGHT_USE_PSSM_BLEND - float pssm_blend; - highp vec4 pssm_coord2; - bool use_blend = true; +#ifdef LIGHT_RIM_USED + rim, + rim_tint, #endif - -#ifdef LIGHT_USE_PSSM4 - - if (depth_z < light_split_offsets.y) { - if (depth_z < light_split_offsets.x) { - pssm_coord = shadow_coord; - -#ifdef LIGHT_USE_PSSM_BLEND - pssm_coord2 = shadow_coord2; - - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_roughness, normalize(normal_interp), #endif - } else { - pssm_coord = shadow_coord2; - -#ifdef LIGHT_USE_PSSM_BLEND - pssm_coord2 = shadow_coord3; - - pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_ANISOTROPY_USED + binormal, tangent, anisotropy, #endif - } - } else { - if (depth_z < light_split_offsets.z) { - pssm_coord = shadow_coord3; - -#if defined(LIGHT_USE_PSSM_BLEND) - pssm_coord2 = shadow_coord4; - pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); -#endif - - } else { - pssm_coord = shadow_coord4; - pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); - -#if defined(LIGHT_USE_PSSM_BLEND) - use_blend = false; -#endif - } - } - -#endif // LIGHT_USE_PSSM4 - -#ifdef LIGHT_USE_PSSM2 - if (depth_z < light_split_offsets.x) { - pssm_coord = shadow_coord; + diffuse_light, specular_light); + } +#endif // !DISABLE_LIGHT_OMNI -#ifdef LIGHT_USE_PSSM_BLEND - pssm_coord2 = shadow_coord2; - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#ifndef DISABLE_LIGHT_SPOT + for (int i = 0; i < MAX_FORWARD_LIGHTS; i++) { + if (i >= spot_light_count) { + break; + } + light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 0.0, albedo, alpha, +#ifdef LIGHT_BACKLIGHT_USED + backlight, #endif - } else { - pssm_coord = shadow_coord2; - pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); -#ifdef LIGHT_USE_PSSM_BLEND - use_blend = false; +#ifdef LIGHT_RIM_USED + rim, + rim_tint, #endif - } - -#endif // LIGHT_USE_PSSM2 - -#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) - { - pssm_coord = shadow_coord; - } +#ifdef LIGHT_CLEARCOAT_USED + clearcoat, clearcoat_roughness, normalize(normal_interp), #endif - - float shadow = sample_shadow(light_directional_shadow, pssm_coord); - -#ifdef LIGHT_USE_PSSM_BLEND - if (use_blend) { - shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2), pssm_blend); - } +#ifdef LIGHT_ANISOTROPY_USED + tangent, + binormal, anisotropy, #endif - - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); - } + diffuse_light, specular_light); } -#endif //use vertex lighting - -#endif //use shadow - -#endif // SHADOWS_DISABLED -#endif - -#ifdef LIGHT_MODE_SPOT - - light_att = vec3(1.0); - -#ifndef USE_VERTEX_LIGHTING - - vec3 light_rel_vec = light_position - vertex; - float light_length = length(light_rel_vec); - float normalized_distance = light_length / light_range; - - if (normalized_distance < 1.0) { - float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); - vec3 spot_dir = light_direction; - - float spot_cutoff = light_spot_angle; - float angle = dot(-normalize(light_rel_vec), spot_dir); - - if (angle > spot_cutoff) { - float scos = max(angle, spot_cutoff); - float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - - light_att = vec3(spot_attenuation); - } else { - light_att = vec3(0.0); - } - } else { - light_att = vec3(0.0); - } - - L = normalize(light_rel_vec); - -#endif - -#if !defined(SHADOWS_DISABLED) - -#ifdef USE_SHADOW - { - highp vec4 splane = shadow_coord; - - float shadow = sample_shadow(light_shadow_atlas, splane); - light_att *= mix(shadow_color.rgb, vec3(1.0), shadow); - } -#endif - -#endif // SHADOWS_DISABLED - -#endif // LIGHT_MODE_SPOT - -#ifdef USE_VERTEX_LIGHTING - //vertex lighting - - specular_light += specular_interp * specular_blob_intensity * light_att; - diffuse_light += diffuse_interp * albedo * light_att; - -#else - //fragment lighting - light_compute( - normal, - L, - eye_position, - binormal, - tangent, - light_color.xyz, - light_att, - albedo, - transmission, - specular_blob_intensity * light_specular, - roughness, - metallic, - specular, - rim, - rim_tint, - clearcoat, - clearcoat_gloss, - anisotropy, - diffuse_light, - specular_light, - alpha); - -#endif //vertex lighting - -#endif //USE_LIGHTING - //compute and merge - -#ifdef USE_SHADOW_TO_OPACITY +#endif // !DISABLE_LIGHT_SPOT +#endif // !MODE_UNSHADED +#endif // !MODE_RENDER_DEPTH +#if defined(USE_SHADOW_TO_OPACITY) alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0)); #if defined(ALPHA_SCISSOR_USED) @@ -2049,105 +1198,60 @@ FRAGMENT_SHADER_CODE } #endif // ALPHA_SCISSOR_USED -#ifdef USE_DEPTH_PREPASS - if (alpha < 0.1) { +#ifdef USE_OPAQUE_PREPASS +#if !defined(ALPHA_SCISSOR_USED) + + if (alpha < opaque_prepass_threshold) { discard; } -#endif // USE_DEPTH_PREPASS -#endif // !USE_SHADOW_TO_OPACITY +#endif // not ALPHA_SCISSOR_USED +#endif // USE_OPAQUE_PREPASS -#ifndef RENDER_DEPTH +#endif // USE_SHADOW_TO_OPACITY -#ifdef SHADELESS +#ifdef MODE_RENDER_DEPTH +//nothing happens, so a tree-ssa optimizer will result in no fragment shader :) +#else // !MODE_RENDER_DEPTH +#ifdef MODE_UNSHADED frag_color = vec4(albedo, alpha); #else - ambient_light *= albedo; - -#if defined(ENABLE_AO) - ambient_light *= ao; - ao_light_affect = mix(1.0, ao, ao_light_affect); - specular_light *= ao_light_affect; - diffuse_light *= ao_light_affect; -#endif + diffuse_light *= albedo; diffuse_light *= 1.0 - metallic; ambient_light *= 1.0 - metallic; - frag_color = vec4(ambient_light + diffuse_light + specular_light, alpha); - - //add emission if in base pass + frag_color = vec4(diffuse_light + specular_light, alpha); #ifdef BASE_PASS - frag_color.rgb += emission; + frag_color.rgb += emission + ambient_light; #endif - // frag_color = vec4(normal, 1.0); - -//apply fog -#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) - -#if defined(USE_VERTEX_LIGHTING) +#endif //MODE_UNSHADED + fog = vec4(unpackHalf2x16(fog_rg), unpackHalf2x16(fog_ba)); -#if defined(BASE_PASS) - frag_color.rgb = mix(frag_color.rgb, fog_interp.rgb, fog_interp.a); +#ifndef DISABLE_FOG + if (scene_data.fog_enabled) { +#ifdef BASE_PASS + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); #else - frag_color.rgb *= (1.0 - fog_interp.a); + frag_color.rgb *= (1.0 - fog.a); #endif // BASE_PASS - -#else //pixel based fog - float fog_amount = 0.0; - -#ifdef LIGHT_MODE_DIRECTIONAL - - vec3 fog_color = mix(fog_color_base.rgb, fog_sun_color_amount.rgb, fog_sun_color_amount.a * pow(max(dot(eye_position, light_direction), 0.0), 8.0)); -#else - vec3 fog_color = fog_color_base.rgb; + } #endif -#ifdef FOG_DEPTH_ENABLED - - { - float fog_z = smoothstep(fog_depth_begin, fog_max_distance, length(vertex)); + // Tonemap before writing as we are writing to an sRGB framebuffer + frag_color.rgb *= exposure; + frag_color.rgb = apply_tonemapping(frag_color.rgb, white); + frag_color.rgb = linear_to_srgb(frag_color.rgb); - fog_amount = pow(fog_z, fog_depth_curve) * fog_color_base.a; - - if (fog_transmit_enabled) { - vec3 total_light = frag_color.rgb; - float transmit = pow(fog_z, fog_transmit_curve); - fog_color = mix(max(total_light, fog_color), fog_color, transmit); - } - } +#ifdef USE_BCS + frag_color.rgb = apply_bcs(frag_color.rgb, bcs); #endif -#ifdef FOG_HEIGHT_ENABLED - { - float y = (camera_matrix * vec4(vertex, 1.0)).y; - fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve)); - } +#ifdef USE_COLOR_CORRECTION + frag_color.rgb = apply_color_correction(frag_color.rgb, color_correction); #endif -#if defined(BASE_PASS) - frag_color.rgb = mix(frag_color.rgb, fog_color, fog_amount); -#else - frag_color.rgb *= (1.0 - fog_amount); -#endif // BASE_PASS - -#endif //use vertex lit - -#endif // defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED) - -#endif //unshaded - -#else // not RENDER_DEPTH -//depth render -#ifdef USE_RGBA_SHADOWS - - highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias - 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; - -#endif -#endif +#endif //!MODE_RENDER_DEPTH } diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl new file mode 100644 index 0000000000..eb1befe38e --- /dev/null +++ b/drivers/gles3/shaders/sky.glsl @@ -0,0 +1,168 @@ +/* clang-format off */ +#[modes] + +mode_background = +mode_half_res = #define USE_HALF_RES_PASS +mode_quarter_res = #define USE_QUARTER_RES_PASS +mode_cubemap = #define USE_CUBEMAP_PASS +mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS +mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS + +#[specializations] + +#[vertex] + +layout(location = 0) in vec2 vertex_attrib; + +out vec2 uv_interp; +/* clang-format on */ + +void main() { + uv_interp = vertex_attrib; + gl_Position = vec4(uv_interp, 1.0, 1.0); +} + +/* clang-format off */ +#[fragment] + +#define M_PI 3.14159265359 + +#include "tonemap_inc.glsl" + +in vec2 uv_interp; + +/* clang-format on */ + +uniform samplerCube radiance; //texunit:-1 +#ifdef USE_CUBEMAP_PASS +uniform samplerCube half_res; //texunit:-2 +uniform samplerCube quarter_res; //texunit:-3 +#else +uniform sampler2D half_res; //texunit:-2 +uniform sampler2D quarter_res; //texunit:-3 +#endif + +layout(std140) uniform GlobalShaderUniformData { //ubo:1 + vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; +}; + +struct DirectionalLightData { + vec4 direction_energy; + vec4 color_size; + bool enabled; +}; + +layout(std140) uniform DirectionalLights { //ubo:4 + DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +} +directional_lights; + +/* clang-format off */ + +#ifdef MATERIAL_UNIFORMS_USED +layout(std140) uniform MaterialUniforms{ //ubo:3 + +#MATERIAL_UNIFORMS + +}; +#endif +/* clang-format on */ +#GLOBALS + +#ifdef USE_CUBEMAP_PASS +#define AT_CUBEMAP_PASS true +#else +#define AT_CUBEMAP_PASS false +#endif + +#ifdef USE_HALF_RES_PASS +#define AT_HALF_RES_PASS true +#else +#define AT_HALF_RES_PASS false +#endif + +#ifdef USE_QUARTER_RES_PASS +#define AT_QUARTER_RES_PASS true +#else +#define AT_QUARTER_RES_PASS false +#endif + +// mat4 is a waste of space, but we don't have an easy way to set a mat3 uniform for now +uniform mat4 orientation; +uniform vec4 projection; +uniform vec3 position; +uniform float time; + +uniform float fog_aerial_perspective; +uniform vec3 fog_light_color; +uniform float fog_sun_scatter; +uniform bool fog_enabled; +uniform float fog_density; +uniform float z_far; +uniform uint directional_light_count; + +layout(location = 0) out vec4 frag_color; + +void main() { + vec3 cube_normal; + cube_normal.z = -1.0; + cube_normal.x = (uv_interp.x + projection.x) / projection.y; + cube_normal.y = (-uv_interp.y - projection.z) / projection.w; + cube_normal = mat3(orientation) * cube_normal; + cube_normal = normalize(cube_normal); + + vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5; + + vec2 panorama_coords = vec2(atan(cube_normal.x, -cube_normal.z), acos(cube_normal.y)); + + if (panorama_coords.x < 0.0) { + panorama_coords.x += M_PI * 2.0; + } + + panorama_coords /= vec2(M_PI * 2.0, M_PI); + + vec3 color = vec3(0.0, 0.0, 0.0); + float alpha = 1.0; // Only available to subpasses + vec4 half_res_color = vec4(1.0); + vec4 quarter_res_color = vec4(1.0); + vec4 custom_fog = vec4(0.0); + +#ifdef USE_CUBEMAP_PASS +#ifdef USES_HALF_RES_COLOR + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); +#endif +#ifdef USES_QUARTER_RES_COLOR + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal); +#endif +#else +#ifdef USES_HALF_RES_COLOR + half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); +#endif +#ifdef USES_QUARTER_RES_COLOR + quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0); +#endif +#endif + + { + +#CODE : SKY + + } + + // Convert to Linear for tonemapping so color matches scene shader better + color = srgb_to_linear(color); + color *= exposure; + color = apply_tonemapping(color, white); + color = linear_to_srgb(color); + +#ifdef USE_BCS + color = apply_bcs(color, bcs); +#endif + +#ifdef USE_COLOR_CORRECTION + color = apply_color_correction(color, color_correction); +#endif + + frag_color.rgb = color; + frag_color.a = alpha; +} diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl index 2eddf9d479..d5051760d7 100644 --- a/drivers/gles3/shaders/stdlib_inc.glsl +++ b/drivers/gles3/shaders/stdlib_inc.glsl @@ -1,5 +1,6 @@ -//TODO: only needed by GLES_OVER_GL +#ifdef USE_GLES_OVER_GL +// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile. uint float2half(uint f) { return ((f >> uint(16)) & uint(0x8000)) | ((((f & uint(0x7f800000)) - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) | @@ -37,14 +38,15 @@ vec2 unpackSnorm2x16(uint p) { vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16))); return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0)); } +#endif uint packUnorm4x8(vec4 v) { uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0)); - return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24); + return uv.x | (uv.y << uint(8)) | (uv.z << uint(16)) | (uv.w << uint(24)); } vec4 unpackUnorm4x8(uint p) { - return vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 + return vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0 } uint packSnorm4x8(vec4 v) { @@ -53,6 +55,6 @@ uint packSnorm4x8(vec4 v) { } vec4 unpackSnorm4x8(uint p) { - vec4 v = vec4(float(p & uint(0xffff)), float((p >> uint(8)) & uint(0xffff)), float((p >> uint(16)) & uint(0xffff)), float(p >> uint(24))); + vec4 v = vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))); return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0)); } diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index 4f962626a3..a478cf9170 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -231,10 +231,10 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { } void main() { - vec3 color = textureLod(source, uv_interp, 0.0).rgb; + vec4 color = textureLod(source, uv_interp, 0.0); #ifdef USE_FXAA - color = apply_fxaa(color, uv_interp, pixel_size); + color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); #endif // Glow @@ -296,18 +296,18 @@ void main() { #endif //USE_MULTI_TEXTURE_GLOW glow *= glow_intensity; - color = apply_glow(color, glow); + color.rgb = apply_glow(color.rgb, glow); #endif // Additional effects #ifdef USE_BCS - color = apply_bcs(color, bcs); + color.rgb = apply_bcs(color.rgb, bcs); #endif #ifdef USE_COLOR_CORRECTION - color = apply_color_correction(color, color_correction); + color.rgb = apply_color_correction(color.rgb, color_correction); #endif - frag_color = vec4(color, 1.0); + frag_color = color; } diff --git a/drivers/gles3/shaders/tonemap_inc.glsl b/drivers/gles3/shaders/tonemap_inc.glsl new file mode 100644 index 0000000000..f8f12760ec --- /dev/null +++ b/drivers/gles3/shaders/tonemap_inc.glsl @@ -0,0 +1,127 @@ +#ifdef USE_BCS +uniform vec3 bcs; +#endif + +#ifdef USE_COLOR_CORRECTION +#ifdef USE_1D_LUT +uniform sampler2D source_color_correction; //texunit:-1 +#else +uniform sampler3D source_color_correction; //texunit:-1 +#endif +#endif + +layout(std140) uniform TonemapData { //ubo:0 + float exposure; + float white; + int tonemapper; + int pad; +}; + +vec3 apply_bcs(vec3 color, vec3 bcs) { + color = mix(vec3(0.0), color, bcs.x); + color = mix(vec3(0.5), color, bcs.y); + color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, bcs.z); + + return color; +} +#ifdef USE_COLOR_CORRECTION +#ifdef USE_1D_LUT +vec3 apply_color_correction(vec3 color) { + color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r; + color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g; + color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b; + return color; +} +#else +vec3 apply_color_correction(vec3 color) { + return textureLod(source_color_correction, color, 0.0).rgb; +} +#endif +#endif + +vec3 tonemap_filmic(vec3 color, float p_white) { + // exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers + // also useful to scale the input to the range that the tonemapper is designed for (some require very high input values) + // has no effect on the curve's general shape or visual properties + const float exposure_bias = 2.0f; + const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance + const float B = 0.30f * exposure_bias; + const float C = 0.10f; + const float D = 0.20f; + const float E = 0.01f; + const float F = 0.30f; + + vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; + float p_white_tonemapped = ((p_white * (A * p_white + C * B) + D * E) / (p_white * (A * p_white + B) + D * F)) - E / F; + + return color_tonemapped / p_white_tonemapped; +} + +// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl +// (MIT License). +vec3 tonemap_aces(vec3 color, float p_white) { + const float exposure_bias = 1.8f; + const float A = 0.0245786f; + const float B = 0.000090537f; + const float C = 0.983729f; + const float D = 0.432951f; + const float E = 0.238081f; + + // Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias` + const mat3 rgb_to_rrt = mat3( + vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias), + vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias), + vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias)); + + const mat3 odt_to_rgb = mat3( + vec3(1.60475f, -0.53108f, -0.07367f), + vec3(-0.10208f, 1.10813f, -0.00605f), + vec3(-0.00327f, -0.07276f, 1.07602f)); + + color *= rgb_to_rrt; + vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E); + color_tonemapped *= odt_to_rgb; + + p_white *= exposure_bias; + float p_white_tonemapped = (p_white * (p_white + A) - B) / (p_white * (C * p_white + D) + E); + + return color_tonemapped / p_white_tonemapped; +} + +vec3 tonemap_reinhard(vec3 color, float p_white) { + return (p_white * color + color) / (color * p_white + p_white); +} + +// This expects 0-1 range input. +vec3 linear_to_srgb(vec3 color) { + //color = clamp(color, vec3(0.0), vec3(1.0)); + //const vec3 a = vec3(0.055f); + //return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f))); + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0)); +} + +// This expects 0-1 range input, outside that range it behaves poorly. +vec3 srgb_to_linear(vec3 color) { + // Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html + return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878); +} + +#define TONEMAPPER_LINEAR 0 +#define TONEMAPPER_REINHARD 1 +#define TONEMAPPER_FILMIC 2 +#define TONEMAPPER_ACES 3 + +vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color + // Ensure color values passed to tonemappers are positive. + // They can be negative in the case of negative lights, which leads to undesired behavior. + if (tonemapper == TONEMAPPER_LINEAR) { + return color; + } else if (tonemapper == TONEMAPPER_REINHARD) { + return tonemap_reinhard(max(vec3(0.0f), color), p_white); + } else if (tonemapper == TONEMAPPER_FILMIC) { + return tonemap_filmic(max(vec3(0.0f), color), p_white); + } else { // TONEMAPPER_ACES + return tonemap_aces(max(vec3(0.0f), color), p_white); + } +} diff --git a/drivers/gles3/storage/SCsub b/drivers/gles3/storage/SCsub new file mode 100644 index 0000000000..91e1140b75 --- /dev/null +++ b/drivers/gles3/storage/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp new file mode 100644 index 0000000000..6cc65e7bb2 --- /dev/null +++ b/drivers/gles3/storage/config.cpp @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* config.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 "config.h" +#include "core/config/project_settings.h" +#include "core/templates/vector.h" + +using namespace GLES3; + +#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +Config *Config::singleton = nullptr; + +Config::Config() { + singleton = this; + + { + GLint max_extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions); + for (int i = 0; i < max_extensions; i++) { + const GLubyte *s = glGetStringi(GL_EXTENSIONS, i); + if (!s) { + break; + } + extensions.insert((const char *)s); + } + } + + bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc"); +#ifdef GLES_OVER_GL + float_texture_supported = true; + etc2_supported = false; + 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"); + 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"); +#endif + 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")); +#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); + + // 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); + + 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); + } + + 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"); + + use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")); + if (use_depth_prepass) { + String vendors = GLOBAL_GET("rendering/driver/depth_prepass/disable_for_vendors"); + Vector<String> vendor_match = vendors.split(","); + String renderer = (const char *)glGetString(GL_RENDERER); + for (int i = 0; i < vendor_match.size(); i++) { + String v = vendor_match[i].strip_edges(); + if (v == String()) { + continue; + } + + if (renderer.findn(v) != -1) { + use_depth_prepass = false; + } + } + } + + max_renderable_elements = GLOBAL_GET("rendering/limits/opengl/max_renderable_elements"); + max_renderable_lights = GLOBAL_GET("rendering/limits/opengl/max_renderable_lights"); + max_lights_per_object = GLOBAL_GET("rendering/limits/opengl/max_lights_per_object"); +} + +Config::~Config() { + singleton = nullptr; +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/texture_loader_gles3.cpp b/drivers/gles3/storage/config.h index 1cbda02121..b83c83f425 100644 --- a/drivers/gles3/texture_loader_gles3.cpp +++ b/drivers/gles3/storage/config.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* texture_loader_gles3.cpp */ +/* config.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,84 +28,66 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "texture_loader_gles3.h" +#ifndef CONFIG_GLES3_H +#define CONFIG_GLES3_H + #ifdef GLES3_ENABLED -#include "core/io/file_access.h" -#include "core/string/print_string.h" - -#include <string.h> - -RES ResourceFormatGLES2Texture::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { - unsigned int width = 8; - unsigned int height = 8; - - //We just use some format - Image::Format fmt = Image::FORMAT_RGB8; - int rowsize = 3 * width; - - Vector<uint8_t> dstbuff; - - dstbuff.resize(rowsize * height); - - uint8_t **row_p = memnew_arr(uint8_t *, height); - - for (unsigned int i = 0; i < height; i++) { - row_p[i] = 0; //No colors any more, I want them to turn black - } - - memdelete_arr(row_p); - - Ref<Image> img = memnew(Image(width, height, 0, fmt, dstbuff)); - - Ref<ImageTexture> texture = memnew(ImageTexture); - texture->create_from_image(img); - - if (r_error) { - *r_error = OK; - } - - return texture; -} - -void ResourceFormatGLES2Texture::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("bmp"); - p_extensions->push_back("dds"); - p_extensions->push_back("exr"); - p_extensions->push_back("jpeg"); - p_extensions->push_back("jpg"); - p_extensions->push_back("hdr"); - p_extensions->push_back("pkm"); - p_extensions->push_back("png"); - p_extensions->push_back("pvr"); - p_extensions->push_back("svg"); - p_extensions->push_back("tga"); - p_extensions->push_back("webp"); -} - -bool ResourceFormatGLES2Texture::handles_type(const String &p_type) const { - return ClassDB::is_parent_class(p_type, "Texture2D"); -} - -String ResourceFormatGLES2Texture::get_resource_type(const String &p_path) const { - String extension = p_path.get_extension().to_lower(); - if ( - extension == "bmp" || - extension == "dds" || - extension == "exr" || - extension == "jpeg" || - extension == "jpg" || - extension == "hdr" || - extension == "pkm" || - extension == "png" || - extension == "pvr" || - extension == "svg" || - extension == "tga" || - extension == "webp") { - return "ImageTexture"; - } - - return ""; -} +#include "core/string/ustring.h" +#include "core/templates/hash_set.h" +// This must come first to avoid windows.h mess +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H #endif + +namespace GLES3 { + +class Config { +private: + static Config *singleton; + +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_uniform_buffer_size = 0; + int max_renderable_elements = 0; + int max_renderable_lights = 0; + int max_lights_per_object = 0; + + // TODO implement wireframe in OpenGL + // bool generate_wireframes; + + HashSet<String> extensions; + + bool float_texture_supported = false; + bool s3tc_supported = false; + bool rgtc_supported = false; + bool bptc_supported = false; + bool etc2_supported = false; + + bool force_vertex_shading = false; + + bool support_anisotropic_filter = false; + float anisotropic_level = 0.0f; + + static Config *get_singleton() { return singleton; }; + + Config(); + ~Config(); +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // CONFIG_GLES3_H diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp new file mode 100644 index 0000000000..2e4bfdc15b --- /dev/null +++ b/drivers/gles3/storage/light_storage.cpp @@ -0,0 +1,580 @@ +/*************************************************************************/ +/* light_storage.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 "light_storage.h" +#include "config.h" +#include "texture_storage.h" + +using namespace GLES3; + +LightStorage *LightStorage::singleton = nullptr; + +LightStorage *LightStorage::get_singleton() { + return singleton; +} + +LightStorage::LightStorage() { + singleton = this; +} + +LightStorage::~LightStorage() { + singleton = nullptr; +} + +/* Light API */ + +void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { + Light light; + light.type = p_type; + + light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_INDIRECT_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; + light.param[RS::LIGHT_PARAM_ATTENUATION] = 1.0; + light.param[RS::LIGHT_PARAM_SPOT_ANGLE] = 45; + light.param[RS::LIGHT_PARAM_SPOT_ATTENUATION] = 1.0; + light.param[RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE] = 0; + light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET] = 0.1; + light.param[RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET] = 0.3; + 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); +} + +RID LightStorage::directional_light_allocate() { + return light_owner.allocate_rid(); +} + +void LightStorage::directional_light_initialize(RID p_rid) { + _light_initialize(p_rid, RS::LIGHT_DIRECTIONAL); +} + +RID LightStorage::omni_light_allocate() { + return light_owner.allocate_rid(); +} + +void LightStorage::omni_light_initialize(RID p_rid) { + _light_initialize(p_rid, RS::LIGHT_OMNI); +} + +RID LightStorage::spot_light_allocate() { + return light_owner.allocate_rid(); +} + +void LightStorage::spot_light_initialize(RID p_rid) { + _light_initialize(p_rid, RS::LIGHT_SPOT); +} + +void LightStorage::light_free(RID p_rid) { + light_set_projector(p_rid, RID()); //clear projector + + // delete the texture + Light *light = light_owner.get_or_null(p_rid); + light->dependency.deleted_notify(p_rid); + light_owner.free(p_rid); +} + +void LightStorage::light_set_color(RID p_light, const Color &p_color) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->color = p_color; +} + +void LightStorage::light_set_param(RID p_light, RS::LightParam p_param, float p_value) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + ERR_FAIL_INDEX(p_param, RS::LIGHT_PARAM_MAX); + + if (light->param[p_param] == p_value) { + return; + } + + switch (p_param) { + case RS::LIGHT_PARAM_RANGE: + case RS::LIGHT_PARAM_SPOT_ANGLE: + case RS::LIGHT_PARAM_SHADOW_MAX_DISTANCE: + case RS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET: + case RS::LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET: + case RS::LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET: + case RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS: + case RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE: + case RS::LIGHT_PARAM_SHADOW_BIAS: { + light->version++; + 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(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); + } + } break; + default: { + } + } + + light->param[p_param] = p_value; +} + +void LightStorage::light_set_shadow(RID p_light, bool p_enabled) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + light->shadow = p_enabled; + + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +void LightStorage::light_set_projector(RID p_light, RID p_texture) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + if (light->projector == p_texture) { + return; + } + + if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { + texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); + } + + light->projector = p_texture; + + if (light->type != RS::LIGHT_DIRECTIONAL) { + if (light->projector.is_valid()) { + texture_storage->texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); + } + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT_SOFT_SHADOW_AND_PROJECTOR); + } +} + +void LightStorage::light_set_negative(RID p_light, bool p_enable) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->negative = p_enable; +} + +void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->cull_mask = p_mask; + + light->version++; + 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) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->distance_fade = p_enabled; + light->distance_fade_begin = p_begin; + light->distance_fade_shadow = p_shadow; + light->distance_fade_length = p_length; +} + +void LightStorage::light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->reverse_cull = p_enabled; + + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +void LightStorage::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->bake_mode = p_bake_mode; + + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +void LightStorage::light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->omni_shadow_mode = p_mode; + + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +RS::LightOmniShadowMode LightStorage::light_omni_get_shadow_mode(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_OMNI_SHADOW_CUBE); + + return light->omni_shadow_mode; +} + +void LightStorage::light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->directional_shadow_mode = p_mode; + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +void LightStorage::light_directional_set_blend_splits(RID p_light, bool p_enable) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->directional_blend_splits = p_enable; + light->version++; + light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT); +} + +bool LightStorage::light_directional_get_blend_splits(RID p_light) const { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, false); + + return light->directional_blend_splits; +} + +void LightStorage::light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) { + Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND(!light); + + light->directional_sky_mode = p_mode; +} + +RS::LightDirectionalSkyMode LightStorage::light_directional_get_sky_mode(RID p_light) const { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY); + + return light->directional_sky_mode; +} + +RS::LightDirectionalShadowMode LightStorage::light_directional_get_shadow_mode(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL); + + return light->directional_shadow_mode; +} + +RS::LightBakeMode LightStorage::light_get_bake_mode(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_BAKE_DISABLED); + + return light->bake_mode; +} + +uint64_t LightStorage::light_get_version(RID p_light) const { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->version; +} + +AABB LightStorage::light_get_aabb(RID p_light) const { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, AABB()); + + 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; + return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); + }; + case RS::LIGHT_OMNI: { + float r = light->param[RS::LIGHT_PARAM_RANGE]; + return AABB(-Vector3(r, r, r), Vector3(r, r, r) * 2); + }; + case RS::LIGHT_DIRECTIONAL: { + return AABB(); + }; + } + + ERR_FAIL_V(AABB()); +} + +/* PROBE API */ + +RID LightStorage::reflection_probe_allocate() { + return RID(); +} + +void LightStorage::reflection_probe_initialize(RID p_rid) { +} + +void LightStorage::reflection_probe_free(RID p_rid) { +} + +void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) { +} + +void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) { +} + +void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) { +} + +void LightStorage::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) { +} + +void LightStorage::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) { +} + +void LightStorage::reflection_probe_set_max_distance(RID p_probe, float p_distance) { +} + +void LightStorage::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { +} + +void LightStorage::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { +} + +void LightStorage::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { +} + +void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { +} + +void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { +} + +void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { +} + +void LightStorage::reflection_probe_set_resolution(RID p_probe, int p_resolution) { +} + +AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const { + return AABB(); +} + +RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const { + return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; +} + +uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const { + return 0; +} + +Vector3 LightStorage::reflection_probe_get_extents(RID p_probe) const { + return Vector3(); +} + +Vector3 LightStorage::reflection_probe_get_origin_offset(RID p_probe) const { + return Vector3(); +} + +float LightStorage::reflection_probe_get_origin_max_distance(RID p_probe) const { + return 0.0; +} + +bool LightStorage::reflection_probe_renders_shadows(RID p_probe) const { + return false; +} + +void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) { +} + +float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { + return 0.0; +} + +/* LIGHTMAP CAPTURE */ + +RID LightStorage::lightmap_allocate() { + return RID(); +} + +void LightStorage::lightmap_initialize(RID p_rid) { +} + +void LightStorage::lightmap_free(RID p_rid) { +} + +void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { +} + +void LightStorage::lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) { +} + +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) { +} + +PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { + return PackedVector3Array(); +} + +PackedColorArray LightStorage::lightmap_get_probe_capture_sh(RID p_lightmap) const { + return PackedColorArray(); +} + +PackedInt32Array LightStorage::lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const { + return PackedInt32Array(); +} + +PackedInt32Array LightStorage::lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const { + return PackedInt32Array(); +} + +AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { + return AABB(); +} + +void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) { +} + +bool LightStorage::lightmap_is_interior(RID p_lightmap) const { + return false; +} + +void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { +} + +float LightStorage::lightmap_get_probe_capture_update_speed() const { + return 0; +} + +/* 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; + } +} +*/ + +#endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h new file mode 100644 index 0000000000..857a0261fa --- /dev/null +++ b/drivers/gles3/storage/light_storage.h @@ -0,0 +1,347 @@ +/*************************************************************************/ +/* light_storage.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 LIGHT_STORAGE_GLES3_H +#define LIGHT_STORAGE_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/renderer_compositor.h" +#include "servers/rendering/storage/light_storage.h" +#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 { + +/* LIGHT */ + +struct Light { + RS::LightType type; + float param[RS::LIGHT_PARAM_MAX]; + Color color = Color(1, 1, 1, 1); + RID projector; + bool shadow = false; + bool negative = false; + bool reverse_cull = false; + RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC; + uint32_t max_sdfgi_cascade = 2; + uint32_t cull_mask = 0xFFFFFFFF; + bool distance_fade = false; + real_t distance_fade_begin = 40.0; + real_t distance_fade_shadow = 50.0; + real_t distance_fade_length = 10.0; + RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; + RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; + bool directional_blend_splits = false; + RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY; + uint64_t version = 0; + + Dependency dependency; +}; + +/* REFLECTION PROBE */ + +struct ReflectionProbe { + RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE; + int resolution = 256; + float intensity = 1.0; + RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT; + Color ambient_color; + float ambient_color_energy = 1.0; + float max_distance = 0; + Vector3 extents = Vector3(1, 1, 1); + Vector3 origin_offset; + bool interior = false; + bool box_projection = false; + bool enable_shadows = false; + uint32_t cull_mask = (1 << 20) - 1; + float mesh_lod_threshold = 0.01; + + Dependency dependency; +}; + +/* LIGHTMAP */ + +struct Lightmap { + RID light_texture; + bool uses_spherical_harmonics = false; + bool interior = false; + AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + int32_t array_index = -1; //unassigned + PackedVector3Array points; + PackedColorArray point_sh; + PackedInt32Array tetrahedra; + PackedInt32Array bsp_tree; + + struct BSP { + static const int32_t EMPTY_LEAF = INT32_MIN; + float plane[4]; + int32_t over = EMPTY_LEAF, under = EMPTY_LEAF; + }; + + Dependency dependency; +}; + +class LightStorage : public RendererLightStorage { +private: + static LightStorage *singleton; + + /* LIGHT */ + mutable RID_Owner<Light, true> light_owner; + + /* REFLECTION PROBE */ + mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner; + + /* LIGHTMAP */ + + Vector<RID> lightmap_textures; + + mutable RID_Owner<Lightmap, true> lightmap_owner; + +public: + static LightStorage *get_singleton(); + + LightStorage(); + virtual ~LightStorage(); + + /* Light API */ + + Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); }; + bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }; + + void _light_initialize(RID p_rid, RS::LightType p_type); + + virtual RID directional_light_allocate() override; + virtual void directional_light_initialize(RID p_rid) override; + virtual RID omni_light_allocate() override; + virtual void omni_light_initialize(RID p_rid) override; + virtual RID spot_light_allocate() override; + virtual void spot_light_initialize(RID p_rid) override; + + virtual void light_free(RID p_rid) override; + + virtual void light_set_color(RID p_light, const Color &p_color) override; + virtual void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override; + virtual void light_set_shadow(RID p_light, bool p_enabled) override; + virtual void light_set_projector(RID p_light, RID p_texture) override; + virtual void light_set_negative(RID p_light, bool p_enable) override; + virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override; + virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override; + virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override; + virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override; + virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {} + + virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override; + + virtual void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override; + virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) override; + virtual bool light_directional_get_blend_splits(RID p_light) const override; + virtual void light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) override; + virtual RS::LightDirectionalSkyMode light_directional_get_sky_mode(RID p_light) const override; + + virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override; + virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override; + virtual RS::LightType light_get_type(RID p_light) const override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->type; + } + virtual AABB light_get_aabb(RID p_light) const override; + + virtual float light_get_param(RID p_light, RS::LightParam p_param) override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->param[p_param]; + } + + _FORCE_INLINE_ RID light_get_projector(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RID()); + + return light->projector; + } + + virtual Color light_get_color(RID p_light) override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, Color()); + + return light->color; + } + + _FORCE_INLINE_ uint32_t light_get_cull_mask(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, 0); + + return light->cull_mask; + } + + _FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + return light->distance_fade; + } + + _FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + return light->distance_fade_begin; + } + + _FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + return light->distance_fade_shadow; + } + + _FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) { + const Light *light = light_owner.get_or_null(p_light); + return light->distance_fade_length; + } + + virtual bool light_has_shadow(RID p_light) const override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->shadow; + } + + virtual bool light_has_projector(RID p_light) const override { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light_owner.owns(light->projector); + } + + _FORCE_INLINE_ bool light_is_negative(RID p_light) const { + const Light *light = light_owner.get_or_null(p_light); + ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); + + return light->negative; + } + + _FORCE_INLINE_ float light_get_transmittance_bias(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_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; + + /* PROBE API */ + + virtual RID reflection_probe_allocate() override; + virtual void reflection_probe_initialize(RID p_rid) override; + virtual void reflection_probe_free(RID p_rid) override; + + virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override; + virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override; + virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override; + virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override; + virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override; + virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) override; + virtual void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) override; + virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override; + virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override; + virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override; + virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override; + virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; + virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; + virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override; + + virtual AABB reflection_probe_get_aabb(RID p_probe) const override; + virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override; + virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override; + virtual Vector3 reflection_probe_get_extents(RID p_probe) const override; + virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const override; + virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override; + virtual bool reflection_probe_renders_shadows(RID p_probe) const override; + + /* LIGHTMAP CAPTURE */ + + virtual RID lightmap_allocate() override; + virtual void lightmap_initialize(RID p_rid) override; + virtual void lightmap_free(RID p_rid) override; + + virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override; + 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 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; + virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override; + virtual AABB lightmap_get_aabb(RID p_lightmap) const override; + virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override; + 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); + */ +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // LIGHT_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp new file mode 100644 index 0000000000..26441fc726 --- /dev/null +++ b/drivers/gles3/storage/material_storage.cpp @@ -0,0 +1,3577 @@ +/*************************************************************************/ +/* material_storage.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 "core/config/project_settings.h" + +#include "config.h" +#include "material_storage.h" +#include "texture_storage.h" + +#include "drivers/gles3/rasterizer_canvas_gles3.h" + +using namespace GLES3; + +/////////////////////////////////////////////////////////////////////////// +// UBI helper functions + +_FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_array_size, const Variant &value, uint8_t *data) { + switch (type) { + case ShaderLanguage::TYPE_BOOL: { + uint32_t *gui = (uint32_t *)data; + + if (p_array_size > 0) { + const PackedInt32Array &ba = value; + int s = ba.size(); + const int *r = ba.ptr(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = (r[i] != 0) ? 1 : 0; + } else { + gui[j] = 0; + } + gui[j + 1] = 0; // ignored + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + bool v = value; + gui[0] = v ? 1 : 0; + } + } break; + case ShaderLanguage::TYPE_BVEC2: { + uint32_t *gui = (uint32_t *)data; + + if (p_array_size > 0) { + const PackedInt32Array &ba = value; + int s = ba.size(); + const int *r = ba.ptr(); + int count = 2 * p_array_size; + + for (int i = 0, j = 0; i < count; i += 2, j += 4) { + if (i < s) { + gui[j] = r[i] ? 1 : 0; + gui[j + 1] = r[i + 1] ? 1 : 0; + } else { + gui[j] = 0; + gui[j + 1] = 0; + } + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + int v = value; + gui[0] = v & 1 ? 1 : 0; + gui[1] = v & 2 ? 1 : 0; + } + } break; + case ShaderLanguage::TYPE_BVEC3: { + uint32_t *gui = (uint32_t *)data; + + if (p_array_size > 0) { + const PackedInt32Array &ba = value; + int s = ba.size(); + const int *r = ba.ptr(); + int count = 3 * p_array_size; + + for (int i = 0, j = 0; i < count; i += 3, j += 4) { + if (i < s) { + gui[j] = r[i] ? 1 : 0; + gui[j + 1] = r[i + 1] ? 1 : 0; + gui[j + 2] = r[i + 2] ? 1 : 0; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + } + gui[j + 3] = 0; // ignored + } + } else { + int v = value; + gui[0] = (v & 1) ? 1 : 0; + gui[1] = (v & 2) ? 1 : 0; + gui[2] = (v & 4) ? 1 : 0; + } + } break; + case ShaderLanguage::TYPE_BVEC4: { + uint32_t *gui = (uint32_t *)data; + + if (p_array_size > 0) { + const PackedInt32Array &ba = value; + int s = ba.size(); + const int *r = ba.ptr(); + int count = 4 * p_array_size; + + for (int i = 0; i < count; i += 4) { + if (i < s) { + gui[i] = r[i] ? 1 : 0; + gui[i + 1] = r[i + 1] ? 1 : 0; + gui[i + 2] = r[i + 2] ? 1 : 0; + gui[i + 3] = r[i + 3] ? 1 : 0; + } else { + gui[i] = 0; + gui[i + 1] = 0; + gui[i + 2] = 0; + gui[i + 3] = 0; + } + } + } else { + int v = value; + gui[0] = (v & 1) ? 1 : 0; + gui[1] = (v & 2) ? 1 : 0; + gui[2] = (v & 4) ? 1 : 0; + gui[3] = (v & 8) ? 1 : 0; + } + } break; + case ShaderLanguage::TYPE_INT: { + int32_t *gui = (int32_t *)data; + + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + const int *r = iv.ptr(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = r[i]; + } else { + gui[j] = 0; + } + gui[j + 1] = 0; // ignored + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + int v = value; + gui[0] = v; + } + } break; + case ShaderLanguage::TYPE_IVEC2: { + 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; + + 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 + } + } else { + Vector2i v = value; + gui[0] = v.x; + gui[1] = v.y; + } + } break; + case ShaderLanguage::TYPE_IVEC3: { + int32_t *gui = (int32_t *)data; + + 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 + } + } else { + Vector3i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + } + } break; + case ShaderLanguage::TYPE_IVEC4: { + int32_t *gui = (int32_t *)data; + + 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: { + uint32_t *gui = (uint32_t *)data; + + if (p_array_size > 0) { + Vector<int> iv = value; + int s = iv.size(); + const int *r = iv.ptr(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = r[i]; + } else { + gui[j] = 0; + } + gui[j + 1] = 0; // ignored + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + int v = value; + gui[0] = v; + } + } break; + case ShaderLanguage::TYPE_UVEC2: { + 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; + + 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 + } + } else { + Vector2i v = value; + gui[0] = v.x; + gui[1] = v.y; + } + } break; + case ShaderLanguage::TYPE_UVEC3: { + uint32_t *gui = (uint32_t *)data; + + 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 + } + } else { + Vector3i v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + } + } break; + case ShaderLanguage::TYPE_UVEC4: { + uint32_t *gui = (uint32_t *)data; + + 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: { + float *gui = (float *)data; + + if (p_array_size > 0) { + const PackedFloat32Array &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = a[i]; + } else { + gui[j] = 0; + } + gui[j + 1] = 0; // ignored + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + float v = value; + gui[0] = v; + } + } break; + case ShaderLanguage::TYPE_VEC2: { + float *gui = (float *)data; + + if (p_array_size > 0) { + const PackedVector2Array &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = a[i].x; + gui[j + 1] = a[i].y; + } else { + gui[j] = 0; + gui[j + 1] = 0; + } + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + } + } else { + Vector2 v = value; + gui[0] = v.x; + gui[1] = v.y; + } + } break; + case ShaderLanguage::TYPE_VEC3: { + float *gui = (float *)data; + + if (p_array_size > 0) { + if (value.get_type() == Variant::PACKED_COLOR_ARRAY) { + const PackedColorArray &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + Color color = a[i]; + gui[j] = color.r; + gui[j + 1] = color.g; + gui[j + 2] = color.b; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + } + gui[j + 3] = 0; // ignored + } + } else { + const PackedVector3Array &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + gui[j] = a[i].x; + gui[j + 1] = a[i].y; + gui[j + 2] = a[i].z; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + } + gui[j + 3] = 0; // ignored + } + } + } else { + if (value.get_type() == Variant::COLOR) { + Color v = value; + + gui[0] = v.r; + gui[1] = v.g; + gui[2] = v.b; + } else { + Vector3 v = value; + gui[0] = v.x; + gui[1] = v.y; + gui[2] = v.z; + } + } + } break; + case ShaderLanguage::TYPE_VEC4: { + float *gui = (float *)data; + + if (p_array_size > 0) { + if (value.get_type() == Variant::PACKED_COLOR_ARRAY) { + const PackedColorArray &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size; i++, j += 4) { + if (i < s) { + Color color = a[i]; + gui[j] = color.r; + gui[j + 1] = color.g; + gui[j + 2] = color.b; + gui[j + 3] = color.a; + } else { + gui[j] = 0; + gui[j + 1] = 0; + gui[j + 2] = 0; + gui[j + 3] = 0; + } + } + } else { + const PackedFloat32Array &a = value; + int s = a.size(); + int count = 4 * p_array_size; + + for (int i = 0; i < count; i += 4) { + if (i + 3 < s) { + gui[i] = a[i]; + gui[i + 1] = a[i + 1]; + gui[i + 2] = a[i + 2]; + gui[i + 3] = a[i + 3]; + } else { + gui[i] = 0; + gui[i + 1] = 0; + gui[i + 2] = 0; + gui[i + 3] = 0; + } + } + } + } else { + if (value.get_type() == Variant::COLOR) { + Color v = value; + + gui[0] = v.r; + gui[1] = v.g; + gui[2] = v.b; + gui[3] = v.a; + } else if (value.get_type() == Variant::RECT2) { + Rect2 v = value; + + gui[0] = v.position.x; + gui[1] = v.position.y; + gui[2] = v.size.x; + gui[3] = v.size.y; + } else if (value.get_type() == Variant::QUATERNION) { + Quaternion v = value; + + gui[0] = v.x; + 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; + + gui[0] = v.normal.x; + gui[1] = v.normal.y; + gui[2] = v.normal.z; + gui[3] = v.d; + } + } + } break; + case ShaderLanguage::TYPE_MAT2: { + float *gui = (float *)data; + + if (p_array_size > 0) { + const PackedFloat32Array &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size * 4; i += 4, j += 8) { + if (i + 3 < s) { + gui[j] = a[i]; + gui[j + 1] = a[i + 1]; + + gui[j + 4] = a[i + 2]; + gui[j + 5] = a[i + 3]; + } else { + gui[j] = 1; + gui[j + 1] = 0; + + gui[j + 4] = 0; + gui[j + 5] = 1; + } + gui[j + 2] = 0; // ignored + gui[j + 3] = 0; // ignored + gui[j + 6] = 0; // ignored + gui[j + 7] = 0; // ignored + } + } else { + Transform2D v = value; + + //in std140 members of mat2 are treated as vec4s + gui[0] = v.columns[0][0]; + gui[1] = v.columns[0][1]; + gui[2] = 0; // ignored + gui[3] = 0; // ignored + + gui[4] = v.columns[1][0]; + gui[5] = v.columns[1][1]; + gui[6] = 0; // ignored + gui[7] = 0; // ignored + } + } break; + case ShaderLanguage::TYPE_MAT3: { + float *gui = (float *)data; + + if (p_array_size > 0) { + const PackedFloat32Array &a = value; + int s = a.size(); + + for (int i = 0, j = 0; i < p_array_size * 9; i += 9, j += 12) { + if (i + 8 < s) { + gui[j] = a[i]; + gui[j + 1] = a[i + 1]; + gui[j + 2] = a[i + 2]; + + gui[j + 4] = a[i + 3]; + gui[j + 5] = a[i + 4]; + gui[j + 6] = a[i + 5]; + + gui[j + 8] = a[i + 6]; + gui[j + 9] = a[i + 7]; + gui[j + 10] = a[i + 8]; + } else { + gui[j] = 1; + gui[j + 1] = 0; + gui[j + 2] = 0; + + gui[j + 4] = 0; + gui[j + 5] = 1; + gui[j + 6] = 0; + + gui[j + 8] = 0; + gui[j + 9] = 0; + gui[j + 10] = 1; + } + gui[j + 3] = 0; // ignored + gui[j + 7] = 0; // ignored + gui[j + 11] = 0; // ignored + } + } else { + Basis v = value; + gui[0] = v.rows[0][0]; + gui[1] = v.rows[1][0]; + gui[2] = v.rows[2][0]; + gui[3] = 0; // ignored + + gui[4] = v.rows[0][1]; + gui[5] = v.rows[1][1]; + gui[6] = v.rows[2][1]; + gui[7] = 0; // ignored + + gui[8] = v.rows[0][2]; + gui[9] = v.rows[1][2]; + gui[10] = v.rows[2][2]; + gui[11] = 0; // ignored + } + } break; + case ShaderLanguage::TYPE_MAT4: { + float *gui = (float *)data; + + if (p_array_size > 0) { + const PackedFloat32Array &a = value; + int s = a.size(); + + for (int i = 0; i < p_array_size * 16; i += 16) { + if (i + 15 < s) { + gui[i] = a[i]; + gui[i + 1] = a[i + 1]; + gui[i + 2] = a[i + 2]; + gui[i + 3] = a[i + 3]; + + gui[i + 4] = a[i + 4]; + gui[i + 5] = a[i + 5]; + gui[i + 6] = a[i + 6]; + gui[i + 7] = a[i + 7]; + + gui[i + 8] = a[i + 8]; + gui[i + 9] = a[i + 9]; + gui[i + 10] = a[i + 10]; + gui[i + 11] = a[i + 11]; + + gui[i + 12] = a[i + 12]; + gui[i + 13] = a[i + 13]; + gui[i + 14] = a[i + 14]; + gui[i + 15] = a[i + 15]; + } else { + gui[i] = 1; + gui[i + 1] = 0; + gui[i + 2] = 0; + gui[i + 3] = 0; + + gui[i + 4] = 0; + gui[i + 5] = 1; + gui[i + 6] = 0; + gui[i + 7] = 0; + + gui[i + 8] = 0; + gui[i + 9] = 0; + gui[i + 10] = 1; + gui[i + 11] = 0; + + gui[i + 12] = 0; + gui[i + 13] = 0; + gui[i + 14] = 0; + gui[i + 15] = 1; + } + } + } else if (value.get_type() == Variant::TRANSFORM3D) { + Transform3D v = value; + gui[0] = v.basis.rows[0][0]; + gui[1] = v.basis.rows[1][0]; + gui[2] = v.basis.rows[2][0]; + gui[3] = 0; + + gui[4] = v.basis.rows[0][1]; + gui[5] = v.basis.rows[1][1]; + gui[6] = v.basis.rows[2][1]; + gui[7] = 0; + + gui[8] = v.basis.rows[0][2]; + gui[9] = v.basis.rows[1][2]; + gui[10] = v.basis.rows[2][2]; + gui[11] = 0; + + gui[12] = v.origin.x; + 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.matrix[i][j]; + } + } + } + } break; + default: { + } + } +} + +_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) { + switch (type) { + case ShaderLanguage::TYPE_BOOL: { + uint32_t *gui = (uint32_t *)data; + *gui = value[0].boolean ? 1 : 0; + } break; + case ShaderLanguage::TYPE_BVEC2: { + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC3: { + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + gui[2] = value[2].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_BVEC4: { + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].boolean ? 1 : 0; + gui[1] = value[1].boolean ? 1 : 0; + gui[2] = value[2].boolean ? 1 : 0; + gui[3] = value[3].boolean ? 1 : 0; + + } break; + case ShaderLanguage::TYPE_INT: { + int32_t *gui = (int32_t *)data; + gui[0] = value[0].sint; + + } break; + case ShaderLanguage::TYPE_IVEC2: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_IVEC3: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_IVEC4: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].sint; + } + + } break; + case ShaderLanguage::TYPE_UINT: { + uint32_t *gui = (uint32_t *)data; + gui[0] = value[0].uint; + + } break; + case ShaderLanguage::TYPE_UVEC2: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].uint; + } + } break; + case ShaderLanguage::TYPE_UVEC3: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].uint; + } + + } break; + case ShaderLanguage::TYPE_UVEC4: { + int32_t *gui = (int32_t *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].uint; + } + } break; + case ShaderLanguage::TYPE_FLOAT: { + float *gui = (float *)data; + gui[0] = value[0].real; + + } break; + case ShaderLanguage::TYPE_VEC2: { + float *gui = (float *)data; + + for (int i = 0; i < 2; i++) { + gui[i] = value[i].real; + } + + } break; + case ShaderLanguage::TYPE_VEC3: { + float *gui = (float *)data; + + for (int i = 0; i < 3; i++) { + gui[i] = value[i].real; + } + + } break; + case ShaderLanguage::TYPE_VEC4: { + float *gui = (float *)data; + + for (int i = 0; i < 4; i++) { + gui[i] = value[i].real; + } + } break; + case ShaderLanguage::TYPE_MAT2: { + float *gui = (float *)data; + + //in std140 members of mat2 are treated as vec4s + gui[0] = value[0].real; + gui[1] = value[1].real; + gui[2] = 0; + gui[3] = 0; + gui[4] = value[2].real; + gui[5] = value[3].real; + gui[6] = 0; + gui[7] = 0; + } break; + case ShaderLanguage::TYPE_MAT3: { + float *gui = (float *)data; + + gui[0] = value[0].real; + gui[1] = value[1].real; + gui[2] = value[2].real; + gui[3] = 0; + gui[4] = value[3].real; + gui[5] = value[4].real; + gui[6] = value[5].real; + gui[7] = 0; + gui[8] = value[6].real; + gui[9] = value[7].real; + gui[10] = value[8].real; + gui[11] = 0; + } break; + case ShaderLanguage::TYPE_MAT4: { + float *gui = (float *)data; + + for (int i = 0; i < 16; i++) { + gui[i] = value[i].real; + } + } break; + default: { + } + } +} + +_FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, int p_array_size, uint8_t *data) { + if (p_array_size <= 0) { + p_array_size = 1; + } + + switch (type) { + case ShaderLanguage::TYPE_BOOL: + case ShaderLanguage::TYPE_INT: + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_FLOAT: { + memset(data, 0, 4 * p_array_size); + } break; + case ShaderLanguage::TYPE_BVEC2: + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_VEC2: { + memset(data, 0, 8 * p_array_size); + } break; + case ShaderLanguage::TYPE_BVEC3: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_VEC3: + case ShaderLanguage::TYPE_BVEC4: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC4: + case ShaderLanguage::TYPE_VEC4: { + memset(data, 0, 16 * p_array_size); + } break; + case ShaderLanguage::TYPE_MAT2: { + memset(data, 0, 32 * p_array_size); + } break; + case ShaderLanguage::TYPE_MAT3: { + memset(data, 0, 48 * p_array_size); + } break; + case ShaderLanguage::TYPE_MAT4: { + memset(data, 0, 64 * p_array_size); + } break; + + default: { + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// MaterialData + +// Look up table to translate ShaderLanguage::DataType to GL_TEXTURE_* +static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = { + GL_TEXTURE_2D, // TYPE_VOID, + GL_TEXTURE_2D, // TYPE_BOOL, + GL_TEXTURE_2D, // TYPE_BVEC2, + GL_TEXTURE_2D, // TYPE_BVEC3, + GL_TEXTURE_2D, // TYPE_BVEC4, + GL_TEXTURE_2D, // TYPE_INT, + GL_TEXTURE_2D, // TYPE_IVEC2, + GL_TEXTURE_2D, // TYPE_IVEC3, + GL_TEXTURE_2D, // TYPE_IVEC4, + GL_TEXTURE_2D, // TYPE_UINT, + GL_TEXTURE_2D, // TYPE_UVEC2, + GL_TEXTURE_2D, // TYPE_UVEC3, + GL_TEXTURE_2D, // TYPE_UVEC4, + GL_TEXTURE_2D, // TYPE_FLOAT, + GL_TEXTURE_2D, // TYPE_VEC2, + GL_TEXTURE_2D, // TYPE_VEC3, + GL_TEXTURE_2D, // TYPE_VEC4, + GL_TEXTURE_2D, // TYPE_MAT2, + GL_TEXTURE_2D, // TYPE_MAT3, + GL_TEXTURE_2D, // TYPE_MAT4, + GL_TEXTURE_2D, // TYPE_SAMPLER2D, + GL_TEXTURE_2D, // TYPE_ISAMPLER2D, + GL_TEXTURE_2D, // TYPE_USAMPLER2D, + GL_TEXTURE_2D_ARRAY, // TYPE_SAMPLER2DARRAY, + GL_TEXTURE_2D_ARRAY, // TYPE_ISAMPLER2DARRAY, + GL_TEXTURE_2D_ARRAY, // TYPE_USAMPLER2DARRAY, + GL_TEXTURE_3D, // TYPE_SAMPLER3D, + GL_TEXTURE_3D, // TYPE_ISAMPLER3D, + GL_TEXTURE_3D, // TYPE_USAMPLER3D, + GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE, + GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY, + GL_TEXTURE_2D, // TYPE_STRUCT +}; + +void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color) { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + bool uses_global_buffer = false; + + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) { + if (E.value.order < 0) { + continue; // texture, does not go here + } + + if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; //instance uniforms don't appear in the buffer + } + + if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL) { + //this is a global variable, get the index to it + 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."); + } + + uint32_t offset = p_uniform_offsets[E.value.order]; + uint32_t *intptr = (uint32_t *)&p_buffer[offset]; + *intptr = index; + uses_global_buffer = true; + continue; + } + + //regular uniform + uint32_t offset = p_uniform_offsets[E.value.order]; +#ifdef DEBUG_ENABLED + uint32_t size = 0U; + // The following code enforces a 16-byte alignment of uniform arrays. + if (E.value.array_size > 0) { + size = ShaderLanguage::get_datatype_size(E.value.type) * E.value.array_size; + int m = (16 * E.value.array_size); + if ((size % m) != 0U) { + size += m - (size % m); + } + } else { + size = ShaderLanguage::get_datatype_size(E.value.type); + } + ERR_CONTINUE(offset + size > p_buffer_size); +#endif + uint8_t *data = &p_buffer[offset]; + HashMap<StringName, Variant>::ConstIterator V = p_parameters.find(E.key); + + if (V) { + //user provided + _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, V->value, data); + + } else if (E.value.default_value.size()) { + //default value + _fill_std140_ubo_value(E.value.type, E.value.default_value, data); + //value=E.value.default_value; + } else { + //zero because it was not provided + if ((E.value.type == ShaderLanguage::TYPE_VEC3 || E.value.type == ShaderLanguage::TYPE_VEC4) && E.value.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SOURCE_COLOR) { + //colors must be set as black, with alpha as 1.0 + _fill_std140_variant_ubo_value(E.value.type, E.value.array_size, Color(0, 0, 0, 1), data); + } else { + //else just zero it out + _fill_std140_ubo_empty(E.value.type, E.value.array_size, data); + } + } + } + + if (uses_global_buffer != (global_buffer_E != nullptr)) { + if (uses_global_buffer) { + global_buffer_E = material_storage->global_shader_uniforms.materials_using_buffer.push_back(self); + } else { + material_storage->global_shader_uniforms.materials_using_buffer.erase(global_buffer_E); + global_buffer_E = nullptr; + } + } +} + +MaterialData::~MaterialData() { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + + if (global_buffer_E) { + //unregister global buffers + 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) { + 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_shader_uniforms.materials_using_texture.erase(global_texture_E); + } + + if (uniform_buffer) { + glDeleteBuffers(1, &uniform_buffer); + uniform_buffer = 0; + } +} + +void MaterialData::update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color) { + TextureStorage *texture_storage = TextureStorage::get_singleton(); + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + +#ifdef TOOLS_ENABLED + Texture *roughness_detect_texture = nullptr; + RS::TextureDetectRoughnessChannel roughness_channel = RS::TEXTURE_DETECT_ROUGHNESS_R; + Texture *normal_detect_texture = nullptr; +#endif + + bool uses_global_textures = false; + global_textures_pass++; + + for (int i = 0, k = 0; i < p_texture_uniforms.size(); i++) { + const StringName &uniform_name = p_texture_uniforms[i].name; + int uniform_array_size = p_texture_uniforms[i].array_size; + + Vector<RID> textures; + + if (p_texture_uniforms[i].global) { + uses_global_textures = true; + + 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!."); + + } else { + HashMap<StringName, uint64_t>::Iterator E = used_global_textures.find(uniform_name); + if (!E) { + E = used_global_textures.insert(uniform_name, global_textures_pass); + v->texture_materials.insert(self); + } else { + E->value = global_textures_pass; + } + + textures.push_back(v->override.get_type() != Variant::NIL ? v->override : v->value); + } + + } else { + WARN_PRINT("Shader uses global uniform 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); + if (V) { + if (V->value.is_array()) { + Array array = (Array)V->value; + if (uniform_array_size > 0) { + for (int j = 0; j < array.size(); j++) { + textures.push_back(array[j]); + } + } else { + if (array.size() > 0) { + textures.push_back(array[0]); + } + } + } else { + textures.push_back(V->value); + } + } + + if (uniform_array_size > 0) { + if (textures.size() < uniform_array_size) { + HashMap<StringName, HashMap<int, RID>>::ConstIterator W = p_default_textures.find(uniform_name); + for (int j = textures.size(); j < uniform_array_size; j++) { + if (W && W->value.has(j)) { + textures.push_back(W->value[j]); + } else { + textures.push_back(RID()); + } + } + } + } else if (textures.is_empty()) { + HashMap<StringName, HashMap<int, RID>>::ConstIterator W = p_default_textures.find(uniform_name); + if (W && W->value.has(0)) { + textures.push_back(W->value[0]); + } + } + } + + RID gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_WHITE); + + if (textures.is_empty()) { + //check default usage + switch (p_texture_uniforms[i].type) { + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER2D: { + switch (p_texture_uniforms[i].hint) { + 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; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_NORMAL); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_NORMAL); + } break; + default: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_WHITE); + } break; + } + } break; + + case ShaderLanguage::TYPE_SAMPLERCUBE: { + switch (p_texture_uniforms[i].hint) { + case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_CUBEMAP_BLACK); + } break; + default: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_CUBEMAP_WHITE); + } break; + } + } break; + case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: { + ERR_PRINT_ONCE("Type: SamplerCubeArray not supported in OpenGL renderer, please use another type."); + } break; + + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER3D: { + switch (p_texture_uniforms[i].hint) { + case ShaderLanguage::ShaderNode::Uniform::HINT_DEFAULT_BLACK: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_3D_BLACK); + } break; + default: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_3D_WHITE); + } break; + } + } break; + + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: { + gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE); + } break; + + default: { + } + } +#ifdef TOOLS_ENABLED + if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) { + roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); + } +#endif + if (uniform_array_size > 0) { + for (int j = 0; j < uniform_array_size; j++) { + p_textures[k++] = gl_texture; + } + } else { + p_textures[k++] = gl_texture; + } + } else { + for (int j = 0; j < textures.size(); j++) { + Texture *tex = TextureStorage::get_singleton()->get_texture(textures[j]); + + if (tex) { + gl_texture = textures[j]; +#ifdef TOOLS_ENABLED + if (tex->detect_3d_callback && p_use_linear_color) { + tex->detect_3d_callback(tex->detect_3d_callback_ud); + } + if (tex->detect_normal_callback && (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL || p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL)) { + if (p_texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL) { + normal_detect_texture = tex; + } + tex->detect_normal_callback(tex->detect_normal_callback_ud); + } + if (tex->detect_roughness_callback && (p_texture_uniforms[i].hint >= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R || p_texture_uniforms[i].hint <= ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_GRAY)) { + //find the normal texture + roughness_detect_texture = tex; + roughness_channel = RS::TextureDetectRoughnessChannel(p_texture_uniforms[i].hint - ShaderLanguage::ShaderNode::Uniform::HINT_ROUGHNESS_R); + } +#endif + } +#ifdef TOOLS_ENABLED + if (roughness_detect_texture && normal_detect_texture && !normal_detect_texture->path.is_empty()) { + roughness_detect_texture->detect_roughness_callback(roughness_detect_texture->detect_roughness_callback_ud, normal_detect_texture->path, roughness_channel); + } +#endif + p_textures[k++] = gl_texture; + } + } + } + { + //for textures no longer used, unregister them + List<StringName> to_delete; + for (KeyValue<StringName, uint64_t> &E : used_global_textures) { + if (E.value != global_textures_pass) { + to_delete.push_back(E.key); + + GlobalShaderUniforms::Variable *v = material_storage->global_shader_uniforms.variables.getptr(E.key); + if (v) { + v->texture_materials.erase(self); + } + } + } + + while (to_delete.front()) { + used_global_textures.erase(to_delete.front()->get()); + to_delete.pop_front(); + } + //handle registering/unregistering global textures + if (uses_global_textures != (global_texture_E != nullptr)) { + if (uses_global_textures) { + global_texture_E = material_storage->global_shader_uniforms.materials_using_texture.push_back(self); + } else { + material_storage->global_shader_uniforms.materials_using_texture.erase(global_texture_E); + global_texture_E = nullptr; + } + } + } +} + +void MaterialData::update_parameters_internal(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size) { + if ((uint32_t)ubo_data.size() != p_ubo_size) { + p_uniform_dirty = true; + if (!uniform_buffer) { + glGenBuffers(1, &uniform_buffer); + } + + ubo_data.resize(p_ubo_size); + if (ubo_data.size()) { + memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear + } + } + + //check whether buffer changed + if (p_uniform_dirty && ubo_data.size()) { + update_uniform_buffer(p_uniforms, p_uniform_offsets, p_parameters, ubo_data.ptrw(), ubo_data.size(), true); + glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer); + glBufferData(GL_UNIFORM_BUFFER, ubo_data.size(), ubo_data.ptrw(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + + uint32_t tex_uniform_count = 0U; + for (int i = 0; i < p_texture_uniforms.size(); i++) { + tex_uniform_count += uint32_t(p_texture_uniforms[i].array_size > 0 ? p_texture_uniforms[i].array_size : 1); + } + + if ((uint32_t)texture_cache.size() != tex_uniform_count || p_textures_dirty) { + texture_cache.resize(tex_uniform_count); + p_textures_dirty = true; + } + + if (p_textures_dirty && tex_uniform_count) { + update_textures(p_parameters, p_default_texture_params, p_texture_uniforms, texture_cache.ptrw(), true); + } +} + +/////////////////////////////////////////////////////////////////////////// +// Material Storage + +MaterialStorage *MaterialStorage::singleton = nullptr; + +MaterialStorage *MaterialStorage::get_singleton() { + return singleton; +} + +MaterialStorage::MaterialStorage() { + singleton = this; + + shader_data_request_func[RS::SHADER_SPATIAL] = _create_scene_shader_func; + shader_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_shader_func; + shader_data_request_func[RS::SHADER_PARTICLES] = nullptr; + shader_data_request_func[RS::SHADER_SKY] = _create_sky_shader_func; + shader_data_request_func[RS::SHADER_FOG] = nullptr; + + material_data_request_func[RS::SHADER_SPATIAL] = _create_scene_material_func; + material_data_request_func[RS::SHADER_CANVAS_ITEM] = _create_canvas_material_func; + material_data_request_func[RS::SHADER_PARTICLES] = nullptr; + material_data_request_func[RS::SHADER_SKY] = _create_sky_material_func; + material_data_request_func[RS::SHADER_FOG] = nullptr; + + static_assert(sizeof(GlobalShaderUniforms::Value) == 16); + + 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_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); + + { + // Setup CanvasItem compiler + ShaderCompiler::DefaultIdentifierActions actions; + + actions.renames["VERTEX"] = "vertex"; + actions.renames["LIGHT_VERTEX"] = "light_vertex"; + actions.renames["SHADOW_VERTEX"] = "shadow_vertex"; + actions.renames["UV"] = "uv"; + 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["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); + actions.renames["AT_LIGHT_PASS"] = "false"; + actions.renames["INSTANCE_CUSTOM"] = "instance_custom"; + + actions.renames["COLOR"] = "color"; + actions.renames["NORMAL"] = "normal"; + 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["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["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_COLOR"] = "light_color"; + actions.renames["LIGHT_ENERGY"] = "light_energy"; + actions.renames["LIGHT"] = "light"; + actions.renames["SHADOW_MODULATE"] = "shadow_modulate"; + + actions.renames["texture_sdf"] = "texture_sdf"; + actions.renames["texture_sdf_normal"] = "texture_sdf_normal"; + actions.renames["sdf_to_screen_uv"] = "sdf_to_screen_uv"; + actions.renames["screen_uv_to_sdf"] = "screen_uv_to_sdf"; + + actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; + actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; + actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + actions.usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV"; + 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"; + actions.render_mode_defines["light_only"] = "#define MODE_LIGHT_ONLY\n"; + + shaders.compiler_canvas.initialize(actions); + } + + { + // Setup Scene compiler + + //shader compiler + ShaderCompiler::DefaultIdentifierActions actions; + + actions.renames["MODEL_MATRIX"] = "model_matrix"; + actions.renames["MODEL_NORMAL_MATRIX"] = "model_normal_matrix"; + actions.renames["VIEW_MATRIX"] = "scene_data.view_matrix"; + actions.renames["INV_VIEW_MATRIX"] = "scene_data.inv_view_matrix"; + actions.renames["PROJECTION_MATRIX"] = "projection_matrix"; + actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix"; + actions.renames["MODELVIEW_MATRIX"] = "modelview"; + actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal"; + + actions.renames["VERTEX"] = "vertex"; + actions.renames["NORMAL"] = "normal"; + actions.renames["TANGENT"] = "tangent"; + actions.renames["BINORMAL"] = "binormal"; + actions.renames["POSITION"] = "position"; + actions.renames["UV"] = "uv_interp"; + 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["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold"; + actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale"; + actions.renames["ALPHA_ANTIALIASING_EDGE"] = "alpha_antialiasing_edge"; + actions.renames["ALPHA_TEXTURE_COORDINATE"] = "alpha_texture_coordinate"; + + //builtins + + actions.renames["TIME"] = "scene_data.time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); + actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size"; + + actions.renames["FRAGCOORD"] = "gl_FragCoord"; + actions.renames["FRONT_FACING"] = "gl_FrontFacing"; + actions.renames["NORMAL_MAP"] = "normal_map"; + actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth"; + actions.renames["ALBEDO"] = "albedo"; + actions.renames["ALPHA"] = "alpha"; + actions.renames["METALLIC"] = "metallic"; + actions.renames["SPECULAR"] = "specular"; + actions.renames["ROUGHNESS"] = "roughness"; + actions.renames["RIM"] = "rim"; + actions.renames["RIM_TINT"] = "rim_tint"; + actions.renames["CLEARCOAT"] = "clearcoat"; + actions.renames["CLEARCOAT_ROUGHNESS"] = "clearcoat_roughness"; + actions.renames["ANISOTROPY"] = "anisotropy"; + actions.renames["ANISOTROPY_FLOW"] = "anisotropy_flow"; + actions.renames["SSS_STRENGTH"] = "sss_strength"; + actions.renames["SSS_TRANSMITTANCE_COLOR"] = "transmittance_color"; + actions.renames["SSS_TRANSMITTANCE_DEPTH"] = "transmittance_depth"; + actions.renames["SSS_TRANSMITTANCE_BOOST"] = "transmittance_boost"; + actions.renames["BACKLIGHT"] = "backlight"; + actions.renames["AO"] = "ao"; + actions.renames["AO_LIGHT_AFFECT"] = "ao_light_affect"; + actions.renames["EMISSION"] = "emission"; + 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["DEPTH"] = "gl_FragDepth"; + actions.renames["OUTPUT_IS_SRGB"] = "true"; + actions.renames["FOG"] = "fog"; + actions.renames["RADIANCE"] = "custom_radiance"; + actions.renames["IRRADIANCE"] = "custom_irradiance"; + actions.renames["BONE_INDICES"] = "bone_attrib"; + actions.renames["BONE_WEIGHTS"] = "weight_attrib"; + actions.renames["CUSTOM0"] = "custom0_attrib"; + actions.renames["CUSTOM1"] = "custom1_attrib"; + actions.renames["CUSTOM2"] = "custom2_attrib"; + 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"; + + //for light + actions.renames["VIEW"] = "view"; + actions.renames["LIGHT_COLOR"] = "light_color"; + actions.renames["LIGHT"] = "light"; + actions.renames["ATTENUATION"] = "attenuation"; + actions.renames["DIFFUSE_LIGHT"] = "diffuse_light"; + actions.renames["SPECULAR_LIGHT"] = "specular_light"; + + actions.usage_defines["NORMAL"] = "#define NORMAL_USED\n"; + actions.usage_defines["TANGENT"] = "#define TANGENT_USED\n"; + actions.usage_defines["BINORMAL"] = "@TANGENT"; + actions.usage_defines["RIM"] = "#define LIGHT_RIM_USED\n"; + actions.usage_defines["RIM_TINT"] = "@RIM"; + actions.usage_defines["CLEARCOAT"] = "#define LIGHT_CLEARCOAT_USED\n"; + actions.usage_defines["CLEARCOAT_ROUGHNESS"] = "@CLEARCOAT"; + actions.usage_defines["ANISOTROPY"] = "#define LIGHT_ANISOTROPY_USED\n"; + actions.usage_defines["ANISOTROPY_FLOW"] = "@ANISOTROPY"; + actions.usage_defines["AO"] = "#define AO_USED\n"; + actions.usage_defines["AO_LIGHT_AFFECT"] = "#define AO_USED\n"; + actions.usage_defines["UV"] = "#define UV_USED\n"; + actions.usage_defines["UV2"] = "#define UV2_USED\n"; + actions.usage_defines["BONE_INDICES"] = "#define BONES_USED\n"; + actions.usage_defines["BONE_WEIGHTS"] = "#define WEIGHTS_USED\n"; + actions.usage_defines["CUSTOM0"] = "#define CUSTOM0_USED\n"; + actions.usage_defines["CUSTOM1"] = "#define CUSTOM1_USED\n"; + actions.usage_defines["CUSTOM2"] = "#define CUSTOM2_USED\n"; + actions.usage_defines["CUSTOM3"] = "#define CUSTOM3_USED\n"; + actions.usage_defines["NORMAL_MAP"] = "#define NORMAL_MAP_USED\n"; + actions.usage_defines["NORMAL_MAP_DEPTH"] = "@NORMAL_MAP"; + actions.usage_defines["COLOR"] = "#define COLOR_USED\n"; + actions.usage_defines["INSTANCE_CUSTOM"] = "#define ENABLE_INSTANCE_CUSTOM\n"; + actions.usage_defines["POSITION"] = "#define OVERRIDE_POSITION\n"; + + actions.usage_defines["ALPHA_SCISSOR_THRESHOLD"] = "#define ALPHA_SCISSOR_USED\n"; + actions.usage_defines["ALPHA_HASH_SCALE"] = "#define ALPHA_HASH_USED\n"; + actions.usage_defines["ALPHA_ANTIALIASING_EDGE"] = "#define ALPHA_ANTIALIASING_EDGE_USED\n"; + actions.usage_defines["ALPHA_TEXTURE_COORDINATE"] = "@ALPHA_ANTIALIASING_EDGE"; + + actions.usage_defines["SSS_STRENGTH"] = "#define ENABLE_SSS\n"; + actions.usage_defines["SSS_TRANSMITTANCE_DEPTH"] = "#define ENABLE_TRANSMITTANCE\n"; + actions.usage_defines["BACKLIGHT"] = "#define LIGHT_BACKLIGHT_USED\n"; + actions.usage_defines["SCREEN_TEXTURE"] = "#define SCREEN_TEXTURE_USED\n"; + actions.usage_defines["SCREEN_UV"] = "#define SCREEN_UV_USED\n"; + + actions.usage_defines["DIFFUSE_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + actions.usage_defines["SPECULAR_LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n"; + + actions.usage_defines["FOG"] = "#define CUSTOM_FOG_USED\n"; + actions.usage_defines["RADIANCE"] = "#define CUSTOM_RADIANCE_USED\n"; + actions.usage_defines["IRRADIANCE"] = "#define CUSTOM_IRRADIANCE_USED\n"; + + actions.render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n"; + actions.render_mode_defines["world_vertex_coords"] = "#define VERTEX_WORLD_COORDS_USED\n"; + actions.render_mode_defines["ensure_correct_normals"] = "#define ENSURE_CORRECT_NORMALS\n"; + actions.render_mode_defines["cull_front"] = "#define DO_SIDE_CHECK\n"; + actions.render_mode_defines["cull_disabled"] = "#define DO_SIDE_CHECK\n"; + actions.render_mode_defines["particle_trails"] = "#define USE_PARTICLE_TRAILS\n"; + actions.render_mode_defines["depth_draw_opaque"] = "#define USE_OPAQUE_PREPASS\n"; + + bool force_lambert = GLOBAL_GET("rendering/shading/overrides/force_lambert_over_burley"); + + if (!force_lambert) { + actions.render_mode_defines["diffuse_burley"] = "#define DIFFUSE_BURLEY\n"; + } + + actions.render_mode_defines["diffuse_lambert_wrap"] = "#define DIFFUSE_LAMBERT_WRAP\n"; + actions.render_mode_defines["diffuse_toon"] = "#define DIFFUSE_TOON\n"; + + actions.render_mode_defines["sss_mode_skin"] = "#define SSS_MODE_SKIN\n"; + + actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_SCHLICK_GGX\n"; + actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n"; + actions.render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n"; + actions.render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n"; + actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n"; + actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n"; + actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n"; + + actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; + actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + + shaders.compiler_scene.initialize(actions); + } + + { + // Setup Particles compiler + /* +ShaderCompiler::DefaultIdentifierActions actions; + + actions.renames["COLOR"] = "PARTICLE.color"; + actions.renames["VELOCITY"] = "PARTICLE.velocity"; + //actions.renames["MASS"] = "mass"; ? + actions.renames["ACTIVE"] = "particle_active"; + actions.renames["RESTART"] = "restart"; + actions.renames["CUSTOM"] = "PARTICLE.custom"; + for (int i = 0; i < ParticlesShader::MAX_USERDATAS; i++) { + String udname = "USERDATA" + itos(i + 1); + actions.renames[udname] = "PARTICLE.userdata" + itos(i + 1); + actions.usage_defines[udname] = "#define USERDATA" + itos(i + 1) + "_USED\n"; + } + actions.renames["TRANSFORM"] = "PARTICLE.xform"; + actions.renames["TIME"] = "frame_history.data[0].time"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); + actions.renames["LIFETIME"] = "params.lifetime"; + actions.renames["DELTA"] = "local_delta"; + actions.renames["NUMBER"] = "particle_number"; + actions.renames["INDEX"] = "index"; + //actions.renames["GRAVITY"] = "current_gravity"; + actions.renames["EMISSION_TRANSFORM"] = "FRAME.emission_transform"; + actions.renames["RANDOM_SEED"] = "FRAME.random_seed"; + actions.renames["FLAG_EMIT_POSITION"] = "EMISSION_FLAG_HAS_POSITION"; + actions.renames["FLAG_EMIT_ROT_SCALE"] = "EMISSION_FLAG_HAS_ROTATION_SCALE"; + actions.renames["FLAG_EMIT_VELOCITY"] = "EMISSION_FLAG_HAS_VELOCITY"; + actions.renames["FLAG_EMIT_COLOR"] = "EMISSION_FLAG_HAS_COLOR"; + actions.renames["FLAG_EMIT_CUSTOM"] = "EMISSION_FLAG_HAS_CUSTOM"; + actions.renames["RESTART_POSITION"] = "restart_position"; + actions.renames["RESTART_ROT_SCALE"] = "restart_rotation_scale"; + actions.renames["RESTART_VELOCITY"] = "restart_velocity"; + actions.renames["RESTART_COLOR"] = "restart_color"; + actions.renames["RESTART_CUSTOM"] = "restart_custom"; + actions.renames["emit_subparticle"] = "emit_subparticle"; + actions.renames["COLLIDED"] = "collided"; + actions.renames["COLLISION_NORMAL"] = "collision_normal"; + actions.renames["COLLISION_DEPTH"] = "collision_depth"; + actions.renames["ATTRACTOR_FORCE"] = "attractor_force"; + + 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.sampler_array_name = "material_samplers"; + actions.base_texture_binding_index = 1; + actions.texture_layout_set = 3; + actions.base_uniform_string = "material."; + actions.base_varying_index = 10; + + actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; + actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + actions.global_buffer_array_variable = "global_shader_uniforms.data"; + + particles_shader.compiler.initialize(actions); + */ + } + + { + // Setup Sky compiler + ShaderCompiler::DefaultIdentifierActions actions; + + actions.renames["COLOR"] = "color"; + actions.renames["ALPHA"] = "alpha"; + actions.renames["EYEDIR"] = "cube_normal"; + actions.renames["POSITION"] = "position"; + actions.renames["SKY_COORDS"] = "panorama_coords"; + actions.renames["SCREEN_UV"] = "uv"; + actions.renames["TIME"] = "time"; + actions.renames["FRAGCOORD"] = "gl_FragCoord"; + actions.renames["PI"] = _MKSTR(Math_PI); + actions.renames["TAU"] = _MKSTR(Math_TAU); + actions.renames["E"] = _MKSTR(Math_E); + actions.renames["HALF_RES_COLOR"] = "half_res_color"; + actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color"; + actions.renames["RADIANCE"] = "radiance"; + actions.renames["FOG"] = "custom_fog"; + actions.renames["LIGHT0_ENABLED"] = "directional_lights.data[0].enabled"; + actions.renames["LIGHT0_DIRECTION"] = "directional_lights.data[0].direction_energy.xyz"; + actions.renames["LIGHT0_ENERGY"] = "directional_lights.data[0].direction_energy.w"; + actions.renames["LIGHT0_COLOR"] = "directional_lights.data[0].color_size.xyz"; + actions.renames["LIGHT0_SIZE"] = "directional_lights.data[0].color_size.w"; + actions.renames["LIGHT1_ENABLED"] = "directional_lights.data[1].enabled"; + actions.renames["LIGHT1_DIRECTION"] = "directional_lights.data[1].direction_energy.xyz"; + actions.renames["LIGHT1_ENERGY"] = "directional_lights.data[1].direction_energy.w"; + actions.renames["LIGHT1_COLOR"] = "directional_lights.data[1].color_size.xyz"; + actions.renames["LIGHT1_SIZE"] = "directional_lights.data[1].color_size.w"; + actions.renames["LIGHT2_ENABLED"] = "directional_lights.data[2].enabled"; + actions.renames["LIGHT2_DIRECTION"] = "directional_lights.data[2].direction_energy.xyz"; + actions.renames["LIGHT2_ENERGY"] = "directional_lights.data[2].direction_energy.w"; + actions.renames["LIGHT2_COLOR"] = "directional_lights.data[2].color_size.xyz"; + actions.renames["LIGHT2_SIZE"] = "directional_lights.data[2].color_size.w"; + actions.renames["LIGHT3_ENABLED"] = "directional_lights.data[3].enabled"; + actions.renames["LIGHT3_DIRECTION"] = "directional_lights.data[3].direction_energy.xyz"; + actions.renames["LIGHT3_ENERGY"] = "directional_lights.data[3].direction_energy.w"; + actions.renames["LIGHT3_COLOR"] = "directional_lights.data[3].color_size.xyz"; + actions.renames["LIGHT3_SIZE"] = "directional_lights.data[3].color_size.w"; + actions.renames["AT_CUBEMAP_PASS"] = "AT_CUBEMAP_PASS"; + actions.renames["AT_HALF_RES_PASS"] = "AT_HALF_RES_PASS"; + actions.renames["AT_QUARTER_RES_PASS"] = "AT_QUARTER_RES_PASS"; + 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.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP; + actions.default_repeat = ShaderLanguage::REPEAT_ENABLE; + + shaders.compiler_sky.initialize(actions); + } +} + +MaterialStorage::~MaterialStorage() { + //shaders.copy.version_free(shaders.copy_version); + + 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 SHADER UNIFORM API */ + +int32_t MaterialStorage::_global_shader_uniform_allocate(uint32_t p_elements) { + int32_t idx = 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_shader_uniforms.buffer_usage[idx + i].elements > 0) { + valid = false; + idx += i + global_shader_uniforms.buffer_usage[idx + i].elements; + break; + } + } + + if (!valid) { + continue; //if not valid, idx is in new position + } + + return idx; + } else { + idx += global_shader_uniforms.buffer_usage[idx].elements; + } + } + + return -1; +} + +void MaterialStorage::_global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderUniformType p_type, const Variant &p_value) { + switch (p_type) { + case RS::GLOBAL_VAR_TYPE_BOOL: { + 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; + bv.z = 0.0; + bv.w = 0.0; + + } break; + case RS::GLOBAL_VAR_TYPE_BVEC2: { + 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; + bv.z = 0.0; + bv.w = 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC3: { + 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; + bv.z = (bvec & 4) ? 1.0 : 0.0; + bv.w = 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_BVEC4: { + 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; + bv.z = (bvec & 4) ? 1.0 : 0.0; + bv.w = (bvec & 8) ? 1.0 : 0.0; + } break; + case RS::GLOBAL_VAR_TYPE_INT: { + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; + int32_t v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC2: { + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; + Vector2i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC3: { + GlobalShaderUniforms::ValueInt &bv = *(GlobalShaderUniforms::ValueInt *)&global_shader_uniforms.buffer_values[p_index]; + Vector3i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_IVEC4: { + 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; + bv.z = v.size() >= 3 ? v[2] : 0; + bv.w = v.size() >= 4 ? v[3] : 0; + } break; + case RS::GLOBAL_VAR_TYPE_RECT2I: { + 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; + bv.z = v.size.x; + bv.w = v.size.y; + } break; + case RS::GLOBAL_VAR_TYPE_UINT: { + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; + uint32_t v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC2: { + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; + Vector2i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC3: { + GlobalShaderUniforms::ValueUInt &bv = *(GlobalShaderUniforms::ValueUInt *)&global_shader_uniforms.buffer_values[p_index]; + Vector3i v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_UVEC4: { + 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; + bv.z = v.size() >= 3 ? v[2] : 0; + bv.w = v.size() >= 4 ? v[3] : 0; + } break; + case RS::GLOBAL_VAR_TYPE_FLOAT: { + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; + float v = p_value; + bv.x = v; + bv.y = 0; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC2: { + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; + Vector2 v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = 0; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC3: { + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; + Vector3 v = p_value; + bv.x = v.x; + bv.y = v.y; + bv.z = v.z; + bv.w = 0; + } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; + Plane v = p_value; + bv.x = v.normal.x; + bv.y = v.normal.y; + bv.z = v.normal.z; + bv.w = v.d; + } break; + case RS::GLOBAL_VAR_TYPE_COLOR: { + 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; + + 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; + bv_linear.z = v.b; + bv_linear.w = v.a; + + } break; + case RS::GLOBAL_VAR_TYPE_RECT2: { + GlobalShaderUniforms::Value &bv = global_shader_uniforms.buffer_values[p_index]; + Rect2 v = p_value; + bv.x = v.position.x; + bv.y = v.position.y; + bv.z = v.size.x; + bv.w = v.size.y; + } break; + case RS::GLOBAL_VAR_TYPE_MAT2: { + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; + Vector<float> m2 = p_value; + if (m2.size() < 4) { + m2.resize(4); + } + bv[0].x = m2[0]; + bv[0].y = m2[1]; + bv[0].z = 0; + bv[0].w = 0; + + bv[1].x = m2[2]; + bv[1].y = m2[3]; + bv[1].z = 0; + bv[1].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_MAT3: { + 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]; + bv[0].z = v.rows[2][0]; + bv[0].w = 0; + + bv[1].x = v.rows[0][1]; + bv[1].y = v.rows[1][1]; + bv[1].z = v.rows[2][1]; + bv[1].w = 0; + + bv[2].x = v.rows[0][2]; + bv[2].y = v.rows[1][2]; + bv[2].z = v.rows[2][2]; + bv[2].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_MAT4: { + GlobalShaderUniforms::Value *bv = &global_shader_uniforms.buffer_values[p_index]; + + Vector<float> m2 = p_value; + if (m2.size() < 16) { + m2.resize(16); + } + + bv[0].x = m2[0]; + bv[0].y = m2[1]; + bv[0].z = m2[2]; + bv[0].w = m2[3]; + + bv[1].x = m2[4]; + bv[1].y = m2[5]; + bv[1].z = m2[6]; + bv[1].w = m2[7]; + + bv[2].x = m2[8]; + bv[2].y = m2[9]; + bv[2].z = m2[10]; + bv[2].w = m2[11]; + + bv[3].x = m2[12]; + bv[3].y = m2[13]; + bv[3].z = m2[14]; + bv[3].w = m2[15]; + + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: { + 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]; + bv[0].z = 0; + bv[0].w = 0; + + bv[1].x = v.columns[1][0]; + bv[1].y = v.columns[1][1]; + bv[1].z = 0; + bv[1].w = 0; + + bv[2].x = v.columns[2][0]; + bv[2].y = v.columns[2][1]; + bv[2].z = 1; + bv[2].w = 0; + + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + 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]; + bv[0].z = v.basis.rows[2][0]; + bv[0].w = 0; + + bv[1].x = v.basis.rows[0][1]; + bv[1].y = v.basis.rows[1][1]; + bv[1].z = v.basis.rows[2][1]; + bv[1].w = 0; + + bv[2].x = v.basis.rows[0][2]; + bv[2].y = v.basis.rows[1][2]; + bv[2].z = v.basis.rows[2][2]; + bv[2].w = 0; + + bv[3].x = v.origin.x; + bv[3].y = v.origin.y; + bv[3].z = v.origin.z; + bv[3].w = 1; + + } break; + default: { + ERR_FAIL(); + } + } +} + +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) / GlobalShaderUniforms::BUFFER_DIRTY_REGION_SIZE; + if (chunk != prev_chunk) { + if (!global_shader_uniforms.buffer_dirty_regions[chunk]) { + global_shader_uniforms.buffer_dirty_regions[chunk] = true; + global_shader_uniforms.buffer_dirty_region_count++; + } + } + + prev_chunk = chunk; + } +} + +void MaterialStorage::global_shader_uniform_add(const StringName &p_name, RS::GlobalShaderUniformType 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_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) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 2; + } + if (p_type == RS::GLOBAL_VAR_TYPE_MAT3 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM_2D) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 3; + } + if (p_type == RS::GLOBAL_VAR_TYPE_MAT4 || p_type == RS::GLOBAL_VAR_TYPE_TRANSFORM) { + //color needs to elements to store srgb and linear + gv.buffer_elements = 4; + } + + //is vector, allocate in buffer and update index + 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_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_shader_uniforms.must_update_buffer_materials = true; //normally there are none + } + + global_shader_uniforms.variables[p_name] = gv; +} + +void MaterialStorage::global_shader_uniform_remove(const StringName &p_name) { + if (!global_shader_uniforms.variables.has(p_name)) { + return; + } + GlobalShaderUniforms::Variable &gv = global_shader_uniforms.variables[p_name]; + + if (gv.buffer_index >= 0) { + global_shader_uniforms.buffer_usage[gv.buffer_index].elements = 0; + global_shader_uniforms.must_update_buffer_materials = true; + } else { + global_shader_uniforms.must_update_texture_materials = true; + } + + global_shader_uniforms.variables.erase(p_name); +} + +Vector<StringName> MaterialStorage::global_shader_uniform_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, GlobalShaderUniforms::Variable> &E : global_shader_uniforms.variables) { + names.push_back(E.key); + } + names.sort_custom<StringName::AlphCompare>(); + return names; +} + +void MaterialStorage::global_shader_uniform_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_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(); + for (const RID &E : gv.texture_materials) { + Material *material = material_storage->get_material(E); + ERR_CONTINUE(!material); + material_storage->_material_queue_update(material, false, true); + } + } + } +} + +void MaterialStorage::global_shader_uniform_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); + + 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_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.value); + } else { + _global_shader_uniform_store_in_buffer(gv.buffer_index, gv.type, gv.override); + } + + _global_shader_uniform_mark_buffer_dirty(gv.buffer_index, gv.buffer_elements); + } else { + //texture + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + for (const RID &E : gv.texture_materials) { + Material *material = material_storage->get_material(E); + ERR_CONTINUE(!material); + material_storage->_material_queue_update(material, false, true); + } + } +} + +Variant MaterialStorage::global_shader_uniform_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_shader_uniforms.variables.has(p_name)) { + return Variant(); + } + + return global_shader_uniforms.variables[p_name].value; +} + +RS::GlobalShaderUniformType MaterialStorage::global_shader_uniform_get_type_internal(const StringName &p_name) const { + if (!global_shader_uniforms.variables.has(p_name)) { + return RS::GLOBAL_VAR_TYPE_MAX; + } + + return global_shader_uniforms.variables[p_name].type; +} + +RS::GlobalShaderUniformType MaterialStorage::global_shader_uniform_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_shader_uniform_get_type_internal(p_name); +} + +void MaterialStorage::global_shader_uniforms_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); + + ERR_CONTINUE(!d.has("type")); + ERR_CONTINUE(!d.has("value")); + + String type = d["type"]; + + static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { + "bool", + "bvec2", + "bvec3", + "bvec4", + "int", + "ivec2", + "ivec3", + "ivec4", + "rect2i", + "uint", + "uvec2", + "uvec3", + "uvec4", + "float", + "vec2", + "vec3", + "vec4", + "color", + "rect2", + "mat2", + "mat3", + "mat4", + "transform_2d", + "transform", + "sampler2D", + "sampler2DArray", + "sampler3D", + "samplerCube", + }; + + RS::GlobalShaderUniformType 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::GlobalShaderUniformType(i); + break; + } + } + + ERR_CONTINUE(gvtype == RS::GLOBAL_VAR_TYPE_MAX); //type invalid + + Variant value = d["value"]; + + if (gvtype >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) { + //textire + if (!p_load_textures) { + value = RID(); + continue; + } + + String path = value; + Ref<Resource> resource = ResourceLoader::load(path); + ERR_CONTINUE(resource.is_null()); + value = resource; + } + + if (global_shader_uniforms.variables.has(name)) { + //has it, update it + global_shader_uniform_set(name, value); + } else { + global_shader_uniform_add(name, gvtype, value); + } + } + } +} + +void MaterialStorage::global_shader_uniforms_clear() { + global_shader_uniforms.variables.clear(); +} + +GLuint MaterialStorage::global_shader_uniforms_get_uniform_buffer() const { + return global_shader_uniforms.buffer; +} + +int32_t MaterialStorage::global_shader_uniforms_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_shader_uniforms.buffer_usage[pos].elements = ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES; + return pos; +} + +void MaterialStorage::global_shader_uniforms_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_shader_uniforms.buffer_usage[pos].elements = 0; + } + global_shader_uniforms.instance_buffer_pos.erase(p_instance); +} + +void MaterialStorage::global_shader_uniforms_instance_update(RID p_instance, int p_index, const Variant &p_value) { + if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) { + return; //just not allocated, ignore + } + 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 + + ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { + ShaderLanguage::TYPE_MAX, //nil + ShaderLanguage::TYPE_BOOL, //bool + ShaderLanguage::TYPE_INT, //int + ShaderLanguage::TYPE_FLOAT, //float + ShaderLanguage::TYPE_MAX, //string + ShaderLanguage::TYPE_VEC2, //vec2 + ShaderLanguage::TYPE_IVEC2, //vec2i + ShaderLanguage::TYPE_VEC4, //rect2 + ShaderLanguage::TYPE_IVEC4, //rect2i + 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()]; + + 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 + + pos += p_index; + + _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_shader_uniforms() { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + 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_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_shader_uniforms.buffer_dirty_regions, 0, sizeof(bool) * total_regions); + } else { + 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_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_shader_uniforms.buffer_dirty_region_count = 0; + } + + 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_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_shader_uniforms.must_update_buffer_materials = false; + } + + 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_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_shader_uniforms.must_update_texture_materials = false; + } +} + +/* SHADER API */ + +RID MaterialStorage::shader_allocate() { + return shader_owner.allocate_rid(); +} + +void MaterialStorage::shader_initialize(RID p_rid) { + Shader shader; + shader.data = nullptr; + shader.mode = RS::SHADER_MAX; + + shader_owner.initialize_rid(p_rid, shader); +} + +void MaterialStorage::shader_free(RID p_rid) { + GLES3::Shader *shader = shader_owner.get_or_null(p_rid); + ERR_FAIL_COND(!shader); + + //make material unreference this + while (shader->owners.size()) { + material_set_shader((*shader->owners.begin())->self, RID()); + } + + //clear data if exists + if (shader->data) { + memdelete(shader->data); + } + shader_owner.free(p_rid); +} + +void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { + GLES3::Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + shader->code = p_code; + + String mode_string = ShaderLanguage::get_shader_type(p_code); + + RS::ShaderMode new_mode; + if (mode_string == "canvas_item") { + new_mode = RS::SHADER_CANVAS_ITEM; + //} else if (mode_string == "particles") { + // new_mode = RS::SHADER_PARTICLES; + } else if (mode_string == "spatial") { + new_mode = RS::SHADER_SPATIAL; + } else if (mode_string == "sky") { + new_mode = RS::SHADER_SKY; + //} else if (mode_string == "fog") { + // new_mode = RS::SHADER_FOG; + } else { + new_mode = RS::SHADER_MAX; + ERR_PRINT("shader type " + mode_string + " not supported in OpenGL renderer"); + } + + if (new_mode != shader->mode) { + if (shader->data) { + memdelete(shader->data); + shader->data = nullptr; + } + + for (Material *E : shader->owners) { + Material *material = E; + material->shader_mode = new_mode; + if (material->data) { + memdelete(material->data); + material->data = nullptr; + } + } + + shader->mode = new_mode; + + if (new_mode < RS::SHADER_MAX && shader_data_request_func[new_mode]) { + shader->data = shader_data_request_func[new_mode](); + } else { + shader->mode = RS::SHADER_MAX; //invalid + } + + for (Material *E : shader->owners) { + Material *material = E; + if (shader->data) { + material->data = material_data_request_func[new_mode](shader->data); + material->data->self = material->self; + material->data->set_next_pass(material->next_pass); + material->data->set_render_priority(material->priority); + } + material->shader_mode = new_mode; + } + + 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); + } + } + } + } + + if (shader->data) { + shader->data->set_code(p_code); + } + + for (Material *E : shader->owners) { + Material *material = E; + 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_shader_uniform_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_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) { + GLES3::Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + if (p_texture.is_valid() && TextureStorage::get_singleton()->owns_texture(p_texture)) { + if (!shader->default_texture_parameter.has(p_name)) { + shader->default_texture_parameter[p_name] = HashMap<int, RID>(); + } + shader->default_texture_parameter[p_name][p_index] = p_texture; + } else { + if (shader->default_texture_parameter.has(p_name) && shader->default_texture_parameter[p_name].has(p_index)) { + shader->default_texture_parameter[p_name].erase(p_index); + + if (shader->default_texture_parameter[p_name].is_empty()) { + shader->default_texture_parameter.erase(p_name); + } + } + } + if (shader->data) { + shader->data->set_default_texture_param(p_name, p_texture, p_index); + } + for (Material *E : shader->owners) { + Material *material = E; + _material_queue_update(material, false, true); + } +} + +RID MaterialStorage::shader_get_default_texture_param(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)) { + return shader->default_texture_parameter[p_name][p_index]; + } + + return RID(); +} + +Variant MaterialStorage::shader_get_param_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) { + return shader->data->get_default_parameter(p_param); + } + return Variant(); +} + +RS::ShaderNativeSourceCode MaterialStorage::shader_get_native_source_code(RID p_shader) const { + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND_V(!shader, RS::ShaderNativeSourceCode()); + if (shader->data) { + return shader->data->get_native_source_code(); + } + return RS::ShaderNativeSourceCode(); +} + +/* MATERIAL API */ + +void MaterialStorage::_material_queue_update(GLES3::Material *material, bool p_uniform, bool p_texture) { + material->uniform_dirty = material->uniform_dirty || p_uniform; + material->texture_dirty = material->texture_dirty || p_texture; + + if (material->update_element.in_list()) { + return; + } + + material_update_list.add(&material->update_element); +} + +void MaterialStorage::_update_queued_materials() { + while (material_update_list.first()) { + Material *material = material_update_list.first()->self(); + + if (material->data) { + material->data->update_parameters(material->params, material->uniform_dirty, material->texture_dirty); + } + material->texture_dirty = false; + material->uniform_dirty = false; + + material_update_list.remove(&material->update_element); + } +} + +RID MaterialStorage::material_allocate() { + return material_owner.allocate_rid(); +} + +void MaterialStorage::material_initialize(RID p_rid) { + material_owner.initialize_rid(p_rid); + Material *material = material_owner.get_or_null(p_rid); + material->self = p_rid; +} + +void MaterialStorage::material_free(RID p_rid) { + Material *material = material_owner.get_or_null(p_rid); + ERR_FAIL_COND(!material); + + material_set_shader(p_rid, RID()); //clean up shader + material->dependency.deleted_notify(p_rid); + + material_owner.free(p_rid); +} + +void MaterialStorage::material_set_shader(RID p_material, RID p_shader) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + if (material->data) { + memdelete(material->data); + material->data = nullptr; + } + + if (material->shader) { + material->shader->owners.erase(material); + material->shader = nullptr; + material->shader_mode = RS::SHADER_MAX; + } + + if (p_shader.is_null()) { + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); + material->shader_id = 0; + return; + } + + Shader *shader = get_shader(p_shader); + ERR_FAIL_COND(!shader); + material->shader = shader; + material->shader_mode = shader->mode; + material->shader_id = p_shader.get_local_index(); + shader->owners.insert(material); + + if (shader->mode == RS::SHADER_MAX) { + return; + } + + ERR_FAIL_COND(shader->data == nullptr); + + material->data = material_data_request_func[shader->mode](shader->data); + material->data->self = p_material; + material->data->set_next_pass(material->next_pass); + material->data->set_render_priority(material->priority); + //updating happens later + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); + _material_queue_update(material, true, true); +} + +void MaterialStorage::material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + if (p_value.get_type() == Variant::NIL) { + material->params.erase(p_param); + } else { + ERR_FAIL_COND(p_value.get_type() == Variant::OBJECT); //object not allowed + material->params[p_param] = p_value; + } + + if (material->shader && material->shader->data) { //shader is valid + bool is_texture = material->shader->data->is_param_texture(p_param); + _material_queue_update(material, !is_texture, is_texture); + } else { + _material_queue_update(material, true, true); + } +} + +Variant MaterialStorage::material_get_param(RID p_material, const StringName &p_param) const { + const GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, Variant()); + if (material->params.has(p_param)) { + return material->params[p_param]; + } else { + return Variant(); + } +} + +void MaterialStorage::material_set_next_pass(RID p_material, RID p_next_material) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + + if (material->next_pass == p_next_material) { + return; + } + + material->next_pass = p_next_material; + if (material->data) { + material->data->set_next_pass(p_next_material); + } + + material->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); +} + +void MaterialStorage::material_set_render_priority(RID p_material, int priority) { + ERR_FAIL_COND(priority < RS::MATERIAL_RENDER_PRIORITY_MIN); + ERR_FAIL_COND(priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + material->priority = priority; + if (material->data) { + material->data->set_render_priority(priority); + } +} + +bool MaterialStorage::material_is_animated(RID p_material) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, false); + if (material->shader && material->shader->data) { + if (material->shader->data->is_animated()) { + return true; + } else if (material->next_pass.is_valid()) { + return material_is_animated(material->next_pass); + } + } + return false; //by default nothing is animated +} + +bool MaterialStorage::material_casts_shadows(RID p_material) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND_V(!material, true); + if (material->shader && material->shader->data) { + if (material->shader->data->casts_shadows()) { + return true; + } else if (material->next_pass.is_valid()) { + return material_casts_shadows(material->next_pass); + } + } + return true; //by default everything casts shadows +} + +void MaterialStorage::material_get_instance_shader_uniforms(RID p_material, List<InstanceShaderParam> *r_parameters) { + GLES3::Material *material = material_owner.get_or_null(p_material); + ERR_FAIL_COND(!material); + if (material->shader && material->shader->data) { + material->shader->data->get_instance_param_list(r_parameters); + + if (material->next_pass.is_valid()) { + material_get_instance_shader_uniforms(material->next_pass, r_parameters); + } + } +} + +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); + if (material->next_pass.is_valid()) { + material_update_dependency(material->next_pass, p_instance); + } +} + +/* Canvas Shader Data */ + +void CanvasShaderData::set_code(const String &p_code) { + // compile the shader + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_screen_texture = false; + uses_sdf = false; + uses_time = false; + + if (code.is_empty()) { + return; //just invalid, but no error + } + + 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; + actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; + actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT; + + actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_modei, BLEND_MODE_ADD); + actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX); + actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB); + actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL); + actions.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&blend_modei, BLEND_MODE_PMALPHA); + actions.render_mode_values["blend_disabled"] = Pair<int *, int>(&blend_modei, BLEND_MODE_DISABLED); + + actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; + actions.usage_flag_pointers["texture_sdf"] = &uses_sdf; + actions.usage_flag_pointers["TIME"] = &uses_time; + + actions.uniforms = &uniforms; + Error err = MaterialStorage::get_singleton()->shaders.compiler_canvas.compile(RS::SHADER_CANVAS_ITEM, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + + if (version.is_null()) { + version = MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); + } + + blend_mode = BlendMode(blend_modei); + +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + 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); +#endif + + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + MaterialStorage::get_singleton()->shaders.canvas_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.canvas_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + valid = true; +} + +void CanvasShaderData::set_default_texture_param(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); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +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) { + continue; + } + if (E.value.texture_order >= 0) { + order[E.value.texture_order + 100000] = E.key; + } else { + order[E.value.order] = E.key; + } + } + + 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); + } +} + +void CanvasShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool CanvasShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool CanvasShaderData::is_animated() const { + return false; +} + +bool CanvasShaderData::casts_shadows() const { + return false; +} + +Variant CanvasShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +RS::ShaderNativeSourceCode CanvasShaderData::get_native_source_code() const { + return MaterialStorage::get_singleton()->shaders.canvas_shader.version_get_native_source_code(version); +} + +CanvasShaderData::CanvasShaderData() { + valid = false; + uses_screen_texture = false; + uses_sdf = false; +} + +CanvasShaderData::~CanvasShaderData() { + if (version.is_valid()) { + MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(version); + } +} + +GLES3::ShaderData *GLES3::_create_canvas_shader_func() { + CanvasShaderData *shader_data = memnew(CanvasShaderData); + return shader_data; +} + +void CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); +} + +void CanvasMaterialData::bind_uniforms() { + // Bind Material Uniforms + glBindBufferBase(GL_UNIFORM_BUFFER, RasterizerCanvasGLES3::MATERIAL_UNIFORM_LOCATION, uniform_buffer); + + RID *textures = texture_cache.ptrw(); + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); + for (int ti = 0; ti < texture_cache.size(); ti++) { + Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); + glActiveTexture(GL_TEXTURE1 + ti); // Start at GL_TEXTURE1 because texture slot 0 is used by the base texture + glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + + // Set sampler state here as the same texture can be used in multiple places with different flags + // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* + RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX); + RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); + } +} + +CanvasMaterialData::~CanvasMaterialData() { +} + +GLES3::MaterialData *GLES3::_create_canvas_material_func(ShaderData *p_shader) { + CanvasMaterialData *material_data = memnew(CanvasMaterialData); + material_data->shader_data = static_cast<CanvasShaderData *>(p_shader); + //update will happen later anyway so do nothing. + return material_data; +} + +//////////////////////////////////////////////////////////////////////////////// +// SKY SHADER + +void SkyShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + + if (code.is_empty()) { + return; //just invalid, but no error + } + + ShaderCompiler::GeneratedCode gen_code; + ShaderCompiler::IdentifierActions actions; + actions.entry_point_stages["sky"] = ShaderCompiler::STAGE_FRAGMENT; + + uses_time = false; + uses_half_res = false; + uses_quarter_res = false; + uses_position = false; + uses_light = false; + + actions.render_mode_flags["use_half_res_pass"] = &uses_half_res; + actions.render_mode_flags["use_quarter_res_pass"] = &uses_quarter_res; + + actions.usage_flag_pointers["TIME"] = &uses_time; + actions.usage_flag_pointers["POSITION"] = &uses_position; + actions.usage_flag_pointers["LIGHT0_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT0_SIZE"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT1_SIZE"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT2_SIZE"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_ENABLED"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_ENERGY"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_DIRECTION"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_COLOR"] = &uses_light; + actions.usage_flag_pointers["LIGHT3_SIZE"] = &uses_light; + + actions.uniforms = &uniforms; + + Error err = MaterialStorage::get_singleton()->shaders.compiler_sky.compile(RS::SHADER_SKY, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + + if (version.is_null()) { + version = MaterialStorage::get_singleton()->shaders.sky_shader.version_create(); + } + +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + 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); +#endif + + Vector<StringName> texture_uniform_names; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + MaterialStorage::get_singleton()->shaders.sky_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.sky_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + valid = true; +} + +void SkyShaderData::set_default_texture_param(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); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +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) { + if (E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL || E.value.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + if (E.value.texture_order >= 0) { + order[E.value.texture_order + 100000] = E.key; + } else { + order[E.value.order] = E.key; + } + } + + 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); + } +} + +void SkyShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool SkyShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool SkyShaderData::is_animated() const { + return false; +} + +bool SkyShaderData::casts_shadows() const { + return false; +} + +Variant SkyShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +RS::ShaderNativeSourceCode SkyShaderData::get_native_source_code() const { + return MaterialStorage::get_singleton()->shaders.sky_shader.version_get_native_source_code(version); +} + +SkyShaderData::SkyShaderData() { + valid = false; +} + +SkyShaderData::~SkyShaderData() { + if (version.is_valid()) { + MaterialStorage::get_singleton()->shaders.sky_shader.version_free(version); + } +} + +GLES3::ShaderData *GLES3::_create_sky_shader_func() { + SkyShaderData *shader_data = memnew(SkyShaderData); + return shader_data; +} + +//////////////////////////////////////////////////////////////////////////////// +// Sky material + +void SkyMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + uniform_set_updated = true; + return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); +} + +SkyMaterialData::~SkyMaterialData() { +} +GLES3::MaterialData *GLES3::_create_sky_material_func(ShaderData *p_shader) { + SkyMaterialData *material_data = memnew(SkyMaterialData); + material_data->shader_data = static_cast<SkyShaderData *>(p_shader); + //update will happen later anyway so do nothing. + return material_data; +} + +void SkyMaterialData::bind_uniforms() { + // Bind Material Uniforms + glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MATERIAL_UNIFORM_LOCATION, uniform_buffer); + + RID *textures = texture_cache.ptrw(); + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); + for (int ti = 0; ti < texture_cache.size(); ti++) { + Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); + glActiveTexture(GL_TEXTURE0 + ti); + glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + + // Set sampler state here as the same texture can be used in multiple places with different flags + // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* + RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX); + RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Scene SHADER + +void SceneShaderData::set_code(const String &p_code) { + //compile + + code = p_code; + valid = false; + ubo_size = 0; + uniforms.clear(); + uses_screen_texture = false; + + if (code.is_empty()) { + return; //just invalid, but no error + } + + ShaderCompiler::GeneratedCode gen_code; + + int blend_modei = BLEND_MODE_MIX; + int depth_testi = DEPTH_TEST_ENABLED; + int alpha_antialiasing_modei = ALPHA_ANTIALIASING_OFF; + int cull_modei = CULL_BACK; + int depth_drawi = DEPTH_DRAW_OPAQUE; + + uses_point_size = false; + uses_alpha = false; + uses_alpha_clip = false; + uses_blend_alpha = false; + uses_depth_pre_pass = false; + uses_discard = false; + uses_roughness = false; + uses_normal = false; + wireframe = false; + + unshaded = false; + uses_vertex = false; + uses_position = false; + uses_sss = false; + uses_transmittance = false; + uses_screen_texture = false; + uses_depth_texture = false; + uses_normal_texture = false; + uses_time = false; + writes_modelview_or_projection = false; + uses_world_coordinates = false; + uses_particle_trails = false; + + ShaderCompiler::IdentifierActions actions; + actions.entry_point_stages["vertex"] = ShaderCompiler::STAGE_VERTEX; + actions.entry_point_stages["fragment"] = ShaderCompiler::STAGE_FRAGMENT; + actions.entry_point_stages["light"] = ShaderCompiler::STAGE_FRAGMENT; + + actions.render_mode_values["blend_add"] = Pair<int *, int>(&blend_modei, BLEND_MODE_ADD); + actions.render_mode_values["blend_mix"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MIX); + actions.render_mode_values["blend_sub"] = Pair<int *, int>(&blend_modei, BLEND_MODE_SUB); + actions.render_mode_values["blend_mul"] = Pair<int *, int>(&blend_modei, BLEND_MODE_MUL); + + actions.render_mode_values["alpha_to_coverage"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE); + actions.render_mode_values["alpha_to_coverage_and_one"] = Pair<int *, int>(&alpha_antialiasing_modei, ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE); + + actions.render_mode_values["depth_draw_never"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_DISABLED); + actions.render_mode_values["depth_draw_opaque"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_OPAQUE); + actions.render_mode_values["depth_draw_always"] = Pair<int *, int>(&depth_drawi, DEPTH_DRAW_ALWAYS); + + actions.render_mode_values["depth_test_disabled"] = Pair<int *, int>(&depth_testi, DEPTH_TEST_DISABLED); + + actions.render_mode_values["cull_disabled"] = Pair<int *, int>(&cull_modei, CULL_DISABLED); + actions.render_mode_values["cull_front"] = Pair<int *, int>(&cull_modei, CULL_FRONT); + actions.render_mode_values["cull_back"] = Pair<int *, int>(&cull_modei, CULL_BACK); + + actions.render_mode_flags["unshaded"] = &unshaded; + actions.render_mode_flags["wireframe"] = &wireframe; + actions.render_mode_flags["particle_trails"] = &uses_particle_trails; + + actions.usage_flag_pointers["ALPHA"] = &uses_alpha; + actions.usage_flag_pointers["ALPHA_SCISSOR_THRESHOLD"] = &uses_alpha_clip; + actions.render_mode_flags["depth_prepass_alpha"] = &uses_depth_pre_pass; + + actions.usage_flag_pointers["SSS_STRENGTH"] = &uses_sss; + actions.usage_flag_pointers["SSS_TRANSMITTANCE_DEPTH"] = &uses_transmittance; + + actions.usage_flag_pointers["SCREEN_TEXTURE"] = &uses_screen_texture; + actions.usage_flag_pointers["DEPTH_TEXTURE"] = &uses_depth_texture; + actions.usage_flag_pointers["NORMAL_TEXTURE"] = &uses_normal_texture; + actions.usage_flag_pointers["DISCARD"] = &uses_discard; + actions.usage_flag_pointers["TIME"] = &uses_time; + actions.usage_flag_pointers["ROUGHNESS"] = &uses_roughness; + actions.usage_flag_pointers["NORMAL"] = &uses_normal; + actions.usage_flag_pointers["NORMAL_MAP"] = &uses_normal; + + actions.usage_flag_pointers["POINT_SIZE"] = &uses_point_size; + actions.usage_flag_pointers["POINT_COORD"] = &uses_point_size; + + actions.write_flag_pointers["MODELVIEW_MATRIX"] = &writes_modelview_or_projection; + actions.write_flag_pointers["PROJECTION_MATRIX"] = &writes_modelview_or_projection; + actions.write_flag_pointers["VERTEX"] = &uses_vertex; + actions.write_flag_pointers["POSITION"] = &uses_position; + + actions.usage_flag_pointers["TANGENT"] = &uses_tangent; + actions.usage_flag_pointers["BINORMAL"] = &uses_tangent; + actions.usage_flag_pointers["COLOR"] = &uses_color; + actions.usage_flag_pointers["UV"] = &uses_uv; + actions.usage_flag_pointers["UV2"] = &uses_uv2; + actions.usage_flag_pointers["CUSTOM0"] = &uses_custom0; + actions.usage_flag_pointers["CUSTOM1"] = &uses_custom1; + actions.usage_flag_pointers["CUSTOM2"] = &uses_custom2; + actions.usage_flag_pointers["CUSTOM3"] = &uses_custom3; + actions.usage_flag_pointers["BONE_INDICES"] = &uses_bones; + actions.usage_flag_pointers["BONE_WEIGHTS"] = &uses_weights; + + actions.uniforms = &uniforms; + + Error err = MaterialStorage::get_singleton()->shaders.compiler_scene.compile(RS::SHADER_SPATIAL, code, &actions, path, gen_code); + ERR_FAIL_COND_MSG(err != OK, "Shader compilation failed."); + + if (version.is_null()) { + version = MaterialStorage::get_singleton()->shaders.scene_shader.version_create(); + } + + depth_draw = DepthDraw(depth_drawi); + depth_test = DepthTest(depth_testi); + cull_mode = Cull(cull_modei); + blend_mode = BlendMode(blend_modei); + alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei); + vertex_input_mask = uint32_t(uses_normal); + vertex_input_mask |= uses_tangent << 1; + vertex_input_mask |= uses_color << 2; + vertex_input_mask |= uses_uv << 3; + vertex_input_mask |= uses_uv2 << 4; + vertex_input_mask |= uses_custom0 << 5; + vertex_input_mask |= uses_custom1 << 6; + vertex_input_mask |= uses_custom2 << 7; + vertex_input_mask |= uses_custom3 << 8; + vertex_input_mask |= uses_bones << 9; + vertex_input_mask |= uses_weights << 10; + +#if 0 + print_line("**compiling shader:"); + print_line("**defines:\n"); + for (int i = 0; i < gen_code.defines.size(); i++) { + print_line(gen_code.defines[i]); + } + + Map<String, String>::Element *el = gen_code.code.front(); + while (el) { + print_line("\n**code " + el->key() + ":\n" + el->value()); + + el = el->next(); + } + + print_line("\n**uniforms:\n" + gen_code.uniforms); + 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; + for (int i = 0; i < gen_code.texture_uniforms.size(); i++) { + texture_uniform_names.push_back(gen_code.texture_uniforms[i].name); + } + + MaterialStorage::get_singleton()->shaders.scene_shader.version_set_code(version, gen_code.code, gen_code.uniforms, gen_code.stage_globals[ShaderCompiler::STAGE_VERTEX], gen_code.stage_globals[ShaderCompiler::STAGE_FRAGMENT], gen_code.defines, texture_uniform_names); + ERR_FAIL_COND(!MaterialStorage::get_singleton()->shaders.scene_shader.version_is_valid(version)); + + ubo_size = gen_code.uniform_total_size; + ubo_offsets = gen_code.uniform_offsets; + texture_uniforms = gen_code.texture_uniforms; + + // if any form of Alpha Antialiasing is enabled, set the blend mode to alpha to coverage + if (alpha_antialiasing_mode != ALPHA_ANTIALIASING_OFF) { + blend_mode = BLEND_MODE_ALPHA_TO_COVERAGE; + } + + valid = true; +} + +void SceneShaderData::set_default_texture_param(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); + + if (default_texture_params[p_name].is_empty()) { + default_texture_params.erase(p_name); + } + } + } else { + if (!default_texture_params.has(p_name)) { + default_texture_params[p_name] = HashMap<int, RID>(); + } + default_texture_params[p_name][p_index] = p_texture; + } +} + +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) { + continue; + } + + if (E.value.texture_order >= 0) { + order[E.value.texture_order + 100000] = E.key; + } else { + order[E.value.order] = E.key; + } + } + + 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); + } +} + +void SceneShaderData::get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const { + for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : uniforms) { + if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_INSTANCE) { + continue; + } + + RendererMaterialStorage::InstanceShaderParam p; + p.info = ShaderLanguage::uniform_to_property_info(E.value); + p.info.name = E.key; //supply name + p.index = E.value.instance_index; + p.default_value = ShaderLanguage::constant_value_to_variant(E.value.default_value, E.value.type, E.value.array_size, E.value.hint); + p_param_list->push_back(p); + } +} + +bool SceneShaderData::is_param_texture(const StringName &p_param) const { + if (!uniforms.has(p_param)) { + return false; + } + + return uniforms[p_param].texture_order >= 0; +} + +bool SceneShaderData::is_animated() const { + return false; +} + +bool SceneShaderData::casts_shadows() const { + return false; +} + +Variant SceneShaderData::get_default_parameter(const StringName &p_parameter) const { + if (uniforms.has(p_parameter)) { + ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter]; + Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value; + return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint); + } + return Variant(); +} + +RS::ShaderNativeSourceCode SceneShaderData::get_native_source_code() const { + return MaterialStorage::get_singleton()->shaders.scene_shader.version_get_native_source_code(version); +} + +SceneShaderData::SceneShaderData() { + valid = false; + uses_screen_texture = false; +} + +SceneShaderData::~SceneShaderData() { + if (version.is_valid()) { + MaterialStorage::get_singleton()->shaders.scene_shader.version_free(version); + } +} + +GLES3::ShaderData *GLES3::_create_scene_shader_func() { + SceneShaderData *shader_data = memnew(SceneShaderData); + return shader_data; +} + +void SceneMaterialData::set_render_priority(int p_priority) { + priority = p_priority - RS::MATERIAL_RENDER_PRIORITY_MIN; //8 bits +} + +void SceneMaterialData::set_next_pass(RID p_pass) { + next_pass = p_pass; +} + +void SceneMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { + return update_parameters_internal(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size); +} + +SceneMaterialData::~SceneMaterialData() { +} + +GLES3::MaterialData *GLES3::_create_scene_material_func(ShaderData *p_shader) { + SceneMaterialData *material_data = memnew(SceneMaterialData); + material_data->shader_data = static_cast<SceneShaderData *>(p_shader); + //update will happen later anyway so do nothing. + return material_data; +} + +void SceneMaterialData::bind_uniforms() { + // Bind Material Uniforms + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MATERIAL_UNIFORM_LOCATION, uniform_buffer); + + RID *textures = texture_cache.ptrw(); + ShaderCompiler::GeneratedCode::Texture *texture_uniforms = shader_data->texture_uniforms.ptrw(); + for (int ti = 0; ti < texture_cache.size(); ti++) { + Texture *texture = TextureStorage::get_singleton()->get_texture(textures[ti]); + glActiveTexture(GL_TEXTURE0 + ti); + glBindTexture(target_from_type[texture_uniforms[ti].type], texture->tex_id); + + // Set sampler state here as the same texture can be used in multiple places with different flags + // Need to convert sampler state from ShaderLanguage::Texture* to RS::CanvasItemTexture* + RS::CanvasItemTextureFilter filter = RS::CanvasItemTextureFilter((int(texture_uniforms[ti].filter) + 1) % RS::CANVAS_ITEM_TEXTURE_FILTER_MAX); + RS::CanvasItemTextureRepeat repeat = RS::CanvasItemTextureRepeat((int(texture_uniforms[ti].repeat) + 1) % RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR); + texture->gl_set_filter(filter); + texture->gl_set_repeat(repeat); + } +} + +#endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h new file mode 100644 index 0000000000..2ca47351a4 --- /dev/null +++ b/drivers/gles3/storage/material_storage.h @@ -0,0 +1,602 @@ +/*************************************************************************/ +/* material_storage.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 MATERIAL_STORAGE_GLES3_H +#define MATERIAL_STORAGE_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/renderer_compositor.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" +#include "../shaders/scene.glsl.gen.h" +#include "../shaders/sky.glsl.gen.h" + +namespace GLES3 { + +/* Shader Structs */ + +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_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_animated() const = 0; + virtual bool casts_shadows() const = 0; + virtual Variant get_default_parameter(const StringName &p_parameter) const = 0; + virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); } + + virtual ~ShaderData() {} +}; + +typedef ShaderData *(*ShaderDataRequestFunction)(); + +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; +}; + +/* Material structs */ + +struct MaterialData { + void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size, bool p_use_linear_color); + void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color); + + virtual void set_render_priority(int p_priority) = 0; + virtual void set_next_pass(RID p_pass) = 0; + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) = 0; + virtual void bind_uniforms() = 0; + virtual ~MaterialData(); + + // Used internally by all Materials + void update_parameters_internal(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size); + +protected: + Vector<uint8_t> ubo_data; + GLuint uniform_buffer = GLuint(0); + Vector<RID> texture_cache; + +private: + friend class MaterialStorage; + RID self; + List<RID>::Element *global_buffer_E = nullptr; + List<RID>::Element *global_texture_E = nullptr; + uint64_t global_textures_pass = 0; + HashMap<StringName, uint64_t> used_global_textures; + + //internally by update_parameters_internal +}; + +typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *); + +struct Material { + RID self; + MaterialData *data = nullptr; + Shader *shader = nullptr; + //shortcut to shader data and type + RS::ShaderMode shader_mode = RS::SHADER_MAX; + uint32_t shader_id = 0; + bool uniform_dirty = false; + bool texture_dirty = false; + HashMap<StringName, Variant> params; + int32_t priority = 0; + RID next_pass; + SelfList<Material> update_element; + + Dependency dependency; + + Material() : + update_element(this) {} +}; + +/* CanvasItem Materials */ + +struct CanvasShaderData : public ShaderData { + enum BlendMode { //used internally + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_PMALPHA, + BLEND_MODE_DISABLED, + }; + + bool valid; + RID version; + String path; + BlendMode blend_mode = BLEND_MODE_MIX; + + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String code; + HashMap<StringName, HashMap<int, RID>> default_texture_params; + + bool uses_screen_texture = 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_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_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + + CanvasShaderData(); + virtual ~CanvasShaderData(); +}; + +ShaderData *_create_canvas_shader_func(); + +struct CanvasMaterialData : public MaterialData { + CanvasShaderData *shader_data = nullptr; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual void bind_uniforms(); + virtual ~CanvasMaterialData(); +}; + +MaterialData *_create_canvas_material_func(ShaderData *p_shader); + +/* Sky Materials */ + +struct SkyShaderData : public ShaderData { + bool valid; + RID version; + + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String path; + String code; + HashMap<StringName, HashMap<int, RID>> default_texture_params; + + bool uses_time; + bool uses_position; + bool uses_half_res; + bool uses_quarter_res; + 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_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_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + SkyShaderData(); + virtual ~SkyShaderData(); +}; + +ShaderData *_create_sky_shader_func(); + +struct SkyMaterialData : public MaterialData { + SkyShaderData *shader_data = nullptr; + bool uniform_set_updated = false; + + virtual void set_render_priority(int p_priority) {} + virtual void set_next_pass(RID p_pass) {} + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual void bind_uniforms(); + virtual ~SkyMaterialData(); +}; + +MaterialData *_create_sky_material_func(ShaderData *p_shader); + +/* Scene Materials */ + +struct SceneShaderData : public ShaderData { + enum BlendMode { //used internally + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_ALPHA_TO_COVERAGE + }; + + enum DepthDraw { + DEPTH_DRAW_DISABLED, + DEPTH_DRAW_OPAQUE, + DEPTH_DRAW_ALWAYS + }; + + enum DepthTest { + DEPTH_TEST_DISABLED, + DEPTH_TEST_ENABLED + }; + + enum Cull { + CULL_DISABLED, + CULL_FRONT, + CULL_BACK + }; + + enum AlphaAntiAliasing { + ALPHA_ANTIALIASING_OFF, + ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE, + ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE + }; + + bool valid; + RID version; + + String path; + + HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms; + Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms; + + Vector<uint32_t> ubo_offsets; + uint32_t ubo_size; + + String code; + HashMap<StringName, HashMap<int, RID>> default_texture_params; + + BlendMode blend_mode; + AlphaAntiAliasing alpha_antialiasing_mode; + DepthDraw depth_draw; + DepthTest depth_test; + Cull cull_mode; + + bool uses_point_size; + bool uses_alpha; + bool uses_blend_alpha; + bool uses_alpha_clip; + bool uses_depth_pre_pass; + bool uses_discard; + bool uses_roughness; + bool uses_normal; + bool uses_particle_trails; + bool wireframe; + + bool unshaded; + bool uses_vertex; + bool uses_position; + bool uses_sss; + bool uses_transmittance; + bool uses_screen_texture; + bool uses_depth_texture; + bool uses_normal_texture; + bool uses_time; + bool writes_modelview_or_projection; + bool uses_world_coordinates; + bool uses_tangent; + bool uses_color; + bool uses_uv; + bool uses_uv2; + bool uses_custom0; + bool uses_custom1; + bool uses_custom2; + bool uses_custom3; + bool uses_bones; + bool uses_weights; + + uint32_t vertex_input_mask = 0; + + uint64_t last_pass = 0; + 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_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_animated() const; + virtual bool casts_shadows() const; + virtual Variant get_default_parameter(const StringName &p_parameter) const; + virtual RS::ShaderNativeSourceCode get_native_source_code() const; + + SceneShaderData(); + virtual ~SceneShaderData(); +}; + +ShaderData *_create_scene_shader_func(); + +struct SceneMaterialData : public MaterialData { + SceneShaderData *shader_data = nullptr; + uint64_t last_pass = 0; + uint32_t index = 0; + RID next_pass; + uint8_t priority = 0; + virtual void set_render_priority(int p_priority); + virtual void set_next_pass(RID p_pass); + virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty); + virtual void bind_uniforms(); + virtual ~SceneMaterialData(); +}; + +MaterialData *_create_scene_material_func(ShaderData *p_shader); + +/* Global shader uniform structs */ +struct GlobalShaderUniforms { + enum { + BUFFER_DIRTY_REGION_SIZE = 1024 + }; + struct Variable { + HashSet<RID> texture_materials; // materials using this + + RS::GlobalShaderUniformType type; + Variant value; + Variant override; + int32_t buffer_index; //for vectors + int32_t buffer_elements; //for vectors + }; + + HashMap<StringName, Variable> variables; + + struct Value { + float x; + float y; + float z; + float w; + }; + + struct ValueInt { + int32_t x; + int32_t y; + int32_t z; + int32_t w; + }; + + struct ValueUInt { + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; + }; + + struct ValueUsage { + uint32_t elements = 0; + }; + + List<RID> materials_using_buffer; + List<RID> materials_using_texture; + + GLuint buffer = GLuint(0); + Value *buffer_values = nullptr; + ValueUsage *buffer_usage = nullptr; + bool *buffer_dirty_regions = nullptr; + uint32_t buffer_dirty_region_count = 0; + + uint32_t buffer_size; + + bool must_update_texture_materials = false; + bool must_update_buffer_materials = false; + + HashMap<RID, int32_t> instance_buffer_pos; +}; + +class MaterialStorage : public RendererMaterialStorage { +private: + friend struct MaterialData; + static MaterialStorage *singleton; + + /* GLOBAL SHADER UNIFORM API */ + + GlobalShaderUniforms global_shader_uniforms; + + int32_t _global_shader_uniform_allocate(uint32_t p_elements); + void _global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderUniformType p_type, const Variant &p_value); + void _global_shader_uniform_mark_buffer_dirty(int32_t p_index, int32_t p_elements); + + /* SHADER API */ + + ShaderDataRequestFunction shader_data_request_func[RS::SHADER_MAX]; + mutable RID_Owner<Shader, true> shader_owner; + + /* MATERIAL API */ + MaterialDataRequestFunction material_data_request_func[RS::SHADER_MAX]; + mutable RID_Owner<Material, true> material_owner; + + SelfList<Material>::List material_update_list; + +public: + static MaterialStorage *get_singleton(); + + 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.matrix[i][j]; + } + } + } + + struct Shaders { + CanvasShaderGLES3 canvas_shader; + SkyShaderGLES3 sky_shader; + SceneShaderGLES3 scene_shader; + CubemapFilterShaderGLES3 cubemap_filter_shader; + + ShaderCompiler compiler_canvas; + ShaderCompiler compiler_scene; + ShaderCompiler compiler_particles; + ShaderCompiler compiler_sky; + } shaders; + + /* GLOBAL SHADER UNIFORM API */ + + void _update_global_shader_uniforms(); + + virtual void global_shader_uniform_add(const StringName &p_name, RS::GlobalShaderUniformType p_type, const Variant &p_value) override; + virtual void global_shader_uniform_remove(const StringName &p_name) override; + virtual Vector<StringName> global_shader_uniform_get_list() const override; + + virtual void global_shader_uniform_set(const StringName &p_name, const Variant &p_value) override; + virtual void global_shader_uniform_set_override(const StringName &p_name, const Variant &p_value) override; + virtual Variant global_shader_uniform_get(const StringName &p_name) const override; + virtual RS::GlobalShaderUniformType global_shader_uniform_get_type(const StringName &p_name) const override; + RS::GlobalShaderUniformType global_shader_uniform_get_type_internal(const StringName &p_name) const; + + virtual void global_shader_uniforms_load_settings(bool p_load_textures = true) override; + virtual void global_shader_uniforms_clear() override; + + virtual int32_t global_shader_uniforms_instance_allocate(RID p_instance) override; + virtual void global_shader_uniforms_instance_free(RID p_instance) override; + virtual void global_shader_uniforms_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + + GLuint global_shader_uniforms_get_uniform_buffer() const; + + /* SHADER API */ + + Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }; + bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }; + + void _shader_make_dirty(Shader *p_shader); + + virtual RID shader_allocate() override; + virtual void shader_initialize(RID p_rid) override; + 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_shader_uniform_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 RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override; + + /* MATERIAL API */ + + Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }; + bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }; + + void _material_queue_update(Material *material, bool p_uniform, bool p_texture); + void _update_queued_materials(); + + virtual RID material_allocate() override; + virtual void material_initialize(RID p_rid) override; + virtual void material_free(RID p_rid) override; + + virtual void material_set_shader(RID p_material, RID p_shader) override; + + virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override; + virtual Variant material_get_param(RID p_material, const StringName &p_param) const override; + + virtual void material_set_next_pass(RID p_material, RID p_next_material) override; + virtual void material_set_render_priority(RID p_material, int priority) override; + + virtual bool material_is_animated(RID p_material) override; + virtual bool material_casts_shadows(RID p_material) override; + + virtual void material_get_instance_shader_uniforms(RID p_material, List<InstanceShaderParam> *r_parameters) 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); + return material->shader_id; + } + + _FORCE_INLINE_ MaterialData *material_get_data(RID p_material, RS::ShaderMode p_shader_mode) { + Material *material = material_owner.get_or_null(p_material); + if (!material || material->shader_mode != p_shader_mode) { + return nullptr; + } else { + return material->data; + } + } +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // MATERIAL_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp new file mode 100644 index 0000000000..d2c2aa0259 --- /dev/null +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -0,0 +1,1558 @@ +/*************************************************************************/ +/* mesh_storage.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 "mesh_storage.h" +#include "material_storage.h" +#include "utilities.h" + +using namespace GLES3; + +MeshStorage *MeshStorage::singleton = nullptr; + +MeshStorage *MeshStorage::get_singleton() { + return singleton; +} + +MeshStorage::MeshStorage() { + singleton = this; +} + +MeshStorage::~MeshStorage() { + singleton = nullptr; +} + +/* MESH API */ + +RID MeshStorage::mesh_allocate() { + return mesh_owner.allocate_rid(); +} + +void MeshStorage::mesh_initialize(RID p_rid) { + mesh_owner.initialize_rid(p_rid, Mesh()); +} + +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"); + } + if (mesh->shadow_owners.size()) { + for (Mesh *E : mesh->shadow_owners) { + Mesh *shadow_owner = E; + shadow_owner->shadow_mesh = RID(); + shadow_owner->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); + } + } + mesh_owner.free(p_rid); +} + +void MeshStorage::mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) { + ERR_FAIL_COND(p_blend_shape_count < 0); + + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(mesh->surface_count > 0); //surfaces already exist + WARN_PRINT_ONCE("blend shapes not supported by GLES3 renderer yet"); + mesh->blend_shape_count = p_blend_shape_count; +} + +bool MeshStorage::mesh_needs_instance(RID p_mesh, bool p_has_skeleton) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, false); + + return mesh->blend_shape_count > 0 || (mesh->has_bone_weights && p_has_skeleton); +} + +void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + + ERR_FAIL_COND(mesh->surface_count == RS::MAX_MESH_SURFACES); + +#ifdef DEBUG_ENABLED + //do a validation, to catch errors first + { + uint32_t stride = 0; + uint32_t attrib_stride = 0; + uint32_t skin_stride = 0; + + // TODO: I think this should be <=, but it is copied from RendererRD, will have to verify later + for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) { + if ((p_surface.format & (1 << i))) { + switch (i) { + case RS::ARRAY_VERTEX: { + if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + stride += sizeof(float) * 2; + } else { + stride += sizeof(float) * 3; + } + + } break; + case RS::ARRAY_NORMAL: { + stride += sizeof(int32_t); + + } break; + case RS::ARRAY_TANGENT: { + stride += sizeof(int32_t); + + } break; + case RS::ARRAY_COLOR: { + attrib_stride += sizeof(uint32_t); + } break; + case RS::ARRAY_TEX_UV: { + attrib_stride += sizeof(float) * 2; + + } break; + case RS::ARRAY_TEX_UV2: { + attrib_stride += sizeof(float) * 2; + + } break; + case RS::ARRAY_CUSTOM0: + case RS::ARRAY_CUSTOM1: + case RS::ARRAY_CUSTOM2: + case RS::ARRAY_CUSTOM3: { + int idx = i - RS::ARRAY_CUSTOM0; + uint32_t fmt_shift[RS::ARRAY_CUSTOM_COUNT] = { RS::ARRAY_FORMAT_CUSTOM0_SHIFT, RS::ARRAY_FORMAT_CUSTOM1_SHIFT, RS::ARRAY_FORMAT_CUSTOM2_SHIFT, RS::ARRAY_FORMAT_CUSTOM3_SHIFT }; + uint32_t fmt = (p_surface.format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK; + uint32_t fmtsize[RS::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 }; + attrib_stride += fmtsize[fmt]; + + } break; + case RS::ARRAY_WEIGHTS: + case RS::ARRAY_BONES: { + //uses a separate array + bool use_8 = p_surface.format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS; + skin_stride += sizeof(int16_t) * (use_8 ? 16 : 8); + } break; + } + } + } + + int expected_size = stride * p_surface.vertex_count; + ERR_FAIL_COND_MSG(expected_size != p_surface.vertex_data.size(), "Size of vertex data provided (" + itos(p_surface.vertex_data.size()) + ") does not match expected (" + itos(expected_size) + ")"); + + int bs_expected_size = expected_size * mesh->blend_shape_count; + + ERR_FAIL_COND_MSG(bs_expected_size != p_surface.blend_shape_data.size(), "Size of blend shape data provided (" + itos(p_surface.blend_shape_data.size()) + ") does not match expected (" + itos(bs_expected_size) + ")"); + + int expected_attrib_size = attrib_stride * p_surface.vertex_count; + ERR_FAIL_COND_MSG(expected_attrib_size != p_surface.attribute_data.size(), "Size of attribute data provided (" + itos(p_surface.attribute_data.size()) + ") does not match expected (" + itos(expected_attrib_size) + ")"); + + if ((p_surface.format & RS::ARRAY_FORMAT_WEIGHTS) && (p_surface.format & RS::ARRAY_FORMAT_BONES)) { + expected_size = skin_stride * p_surface.vertex_count; + ERR_FAIL_COND_MSG(expected_size != p_surface.skin_data.size(), "Size of skin data provided (" + itos(p_surface.skin_data.size()) + ") does not match expected (" + itos(expected_size) + ")"); + } + } + +#endif + + Mesh::Surface *s = memnew(Mesh::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.attribute_data.size()) { + glGenBuffers(1, &s->attribute_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->attribute_buffer); + glBufferData(GL_ARRAY_BUFFER, p_surface.attribute_data.size(), p_surface.attribute_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + s->attribute_buffer_size = p_surface.attribute_data.size(); + } + if (p_surface.skin_data.size()) { + glGenBuffers(1, &s->skin_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->skin_buffer); + glBufferData(GL_ARRAY_BUFFER, p_surface.skin_data.size(), p_surface.skin_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + s->skin_buffer_size = p_surface.skin_data.size(); + } + + s->vertex_count = p_surface.vertex_count; + + if (p_surface.format & RS::ARRAY_FORMAT_BONES) { + mesh->has_bone_weights = true; + } + + if (p_surface.index_count) { + bool is_index_16 = p_surface.vertex_count <= 65536; + 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); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind + s->index_count = p_surface.index_count; + s->index_buffer_size = p_surface.index_data.size(); + + if (p_surface.lods.size()) { + s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size()); + s->lod_count = p_surface.lods.size(); + + for (int i = 0; i < p_surface.lods.size(); i++) { + glGenBuffers(1, &s->lods[i].index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->lods[i].index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_surface.lods[i].index_data.size(), p_surface.lods[i].index_data.ptr(), GL_STATIC_DRAW); + 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(); + } + } + } + + s->aabb = p_surface.aabb; + s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. + + if (mesh->blend_shape_count > 0) { + //s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data); + } + + if (mesh->surface_count == 0) { + mesh->bone_aabbs = p_surface.bone_aabbs; + mesh->aabb = p_surface.aabb; + } else { + if (mesh->bone_aabbs.size() < p_surface.bone_aabbs.size()) { + // ArrayMesh::_surface_set_data only allocates bone_aabbs up to max_bone + // Each surface may affect different numbers of bones. + mesh->bone_aabbs.resize(p_surface.bone_aabbs.size()); + } + for (int i = 0; i < p_surface.bone_aabbs.size(); i++) { + AABB bone = p_surface.bone_aabbs[i]; + if (!bone.has_no_volume()) { + mesh->bone_aabbs.write[i].merge_with(p_surface.bone_aabbs[i]); + } + } + mesh->aabb.merge_with(p_surface.aabb); + } + + s->material = p_surface.material; + + mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count + 1)); + mesh->surfaces[mesh->surface_count] = s; + mesh->surface_count++; + + for (MeshInstance *mi : mesh->instances) { + _mesh_instance_add_surface(mi, mesh, mesh->surface_count - 1); + } + + 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(Dependency::DEPENDENCY_CHANGED_MESH); + } + + mesh->material_cache.clear(); +} + +int MeshStorage::mesh_get_blend_shape_count(RID p_mesh) const { + const Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, -1); + return mesh->blend_shape_count; +} + +void MeshStorage::mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_INDEX((int)p_mode, 2); + + mesh->blend_shape_mode = p_mode; +} + +RS::BlendShapeMode MeshStorage::mesh_get_blend_shape_mode(RID p_mesh) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, RS::BLEND_SHAPE_MODE_NORMALIZED); + return mesh->blend_shape_mode; +} + +void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { +} + +void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + mesh->surfaces[p_surface]->material = p_material; + + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MATERIAL); + mesh->material_cache.clear(); +} + +RID MeshStorage::mesh_surface_get_material(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RID()); + + return mesh->surfaces[p_surface]->material; +} + +RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, RS::SurfaceData()); + ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_surface, mesh->surface_count, RS::SurfaceData()); + + Mesh::Surface &s = *mesh->surfaces[p_surface]; + + RS::SurfaceData sd; + sd.format = s.format; + sd.vertex_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_buffer_size); + + if (s.attribute_buffer != 0) { + sd.attribute_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.attribute_buffer, s.attribute_buffer_size); + } + + sd.vertex_count = s.vertex_count; + sd.index_count = s.index_count; + sd.primitive = s.primitive; + + if (sd.index_count) { + 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 = Utilities::buffer_get_data(GL_ELEMENT_ARRAY_BUFFER, s.lods[i].index_buffer, s.lods[i].index_buffer_size); + sd.lods.push_back(lod); + } + + sd.bone_aabbs = s.bone_aabbs; + glBindBuffer(GL_ARRAY_BUFFER, 0); + + return sd; +} + +int MeshStorage::mesh_get_surface_count(RID p_mesh) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, 0); + return mesh->surface_count; +} + +void MeshStorage::mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + mesh->custom_aabb = p_aabb; +} + +AABB MeshStorage::mesh_get_custom_aabb(RID p_mesh) const { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + return mesh->custom_aabb; +} + +AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, AABB()); + + if (mesh->custom_aabb != AABB()) { + return mesh->custom_aabb; + } + + Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); + + if (!skeleton || skeleton->size == 0) { + return mesh->aabb; + } + + // Calculate AABB based on Skeleton + + AABB aabb; + + for (uint32_t i = 0; i < mesh->surface_count; i++) { + AABB laabb; + if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { + int bs = mesh->surfaces[i]->bone_aabbs.size(); + const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + + int sbs = skeleton->size; + ERR_CONTINUE(bs > sbs); + const float *baseptr = skeleton->data.ptr(); + + bool first = true; + + if (skeleton->use_2d) { + for (int j = 0; j < bs; j++) { + if (skbones[0].size == Vector3()) { + continue; //bone is unused + } + + const float *dataptr = baseptr + j * 8; + + Transform3D mtx; + + mtx.basis.rows[0].x = dataptr[0]; + mtx.basis.rows[1].x = dataptr[1]; + mtx.origin.x = dataptr[3]; + + mtx.basis.rows[0].y = dataptr[4]; + mtx.basis.rows[1].y = dataptr[5]; + mtx.origin.y = dataptr[7]; + + AABB baabb = mtx.xform(skbones[j]); + + if (first) { + laabb = baabb; + first = false; + } else { + laabb.merge_with(baabb); + } + } + } else { + for (int j = 0; j < bs; j++) { + if (skbones[0].size == Vector3()) { + continue; //bone is unused + } + + const float *dataptr = baseptr + j * 12; + + Transform3D mtx; + + mtx.basis.rows[0][0] = dataptr[0]; + mtx.basis.rows[0][1] = dataptr[1]; + mtx.basis.rows[0][2] = dataptr[2]; + mtx.origin.x = dataptr[3]; + mtx.basis.rows[1][0] = dataptr[4]; + mtx.basis.rows[1][1] = dataptr[5]; + mtx.basis.rows[1][2] = dataptr[6]; + mtx.origin.y = dataptr[7]; + mtx.basis.rows[2][0] = dataptr[8]; + mtx.basis.rows[2][1] = dataptr[9]; + mtx.basis.rows[2][2] = dataptr[10]; + mtx.origin.z = dataptr[11]; + + AABB baabb = mtx.xform(skbones[j]); + if (first) { + laabb = baabb; + first = false; + } else { + laabb.merge_with(baabb); + } + } + } + + if (laabb.size == Vector3()) { + laabb = mesh->surfaces[i]->aabb; + } + } else { + laabb = mesh->surfaces[i]->aabb; + } + + if (i == 0) { + aabb = laabb; + } else { + aabb.merge_with(laabb); + } + } + + return aabb; +} + +void MeshStorage::mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + + Mesh *shadow_mesh = mesh_owner.get_or_null(mesh->shadow_mesh); + if (shadow_mesh) { + shadow_mesh->shadow_owners.erase(mesh); + } + mesh->shadow_mesh = p_shadow_mesh; + + shadow_mesh = mesh_owner.get_or_null(mesh->shadow_mesh); + + if (shadow_mesh) { + shadow_mesh->shadow_owners.insert(mesh); + } + + mesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MESH); +} + +void MeshStorage::mesh_clear(RID p_mesh) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + for (uint32_t i = 0; i < mesh->surface_count; i++) { + Mesh::Surface &s = *mesh->surfaces[i]; + + if (s.vertex_buffer != 0) { + glDeleteBuffers(1, &s.vertex_buffer); + s.vertex_buffer = 0; + } + + if (s.version_count != 0) { + for (uint32_t j = 0; j < s.version_count; j++) { + glDeleteVertexArrays(1, &s.versions[j].vertex_array); + s.versions[j].vertex_array = 0; + } + } + + if (s.attribute_buffer != 0) { + glDeleteBuffers(1, &s.attribute_buffer); + s.attribute_buffer = 0; + } + + if (s.skin_buffer != 0) { + glDeleteBuffers(1, &s.skin_buffer); + s.skin_buffer = 0; + } + + if (s.index_buffer != 0) { + glDeleteBuffers(1, &s.index_buffer); + s.index_buffer = 0; + } + memdelete(mesh->surfaces[i]); + } + if (mesh->surfaces) { + memfree(mesh->surfaces); + } + + mesh->surfaces = nullptr; + mesh->surface_count = 0; + mesh->material_cache.clear(); + //clear instance data + for (MeshInstance *mi : mesh->instances) { + _mesh_instance_clear(mi); + } + mesh->has_bone_weights = false; + 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(Dependency::DEPENDENCY_CHANGED_MESH); + } +} + +void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis) { + Mesh::Surface::Attrib attribs[RS::ARRAY_MAX]; + + int attributes_stride = 0; + int vertex_stride = 0; + int skin_stride = 0; + + for (int i = 0; i < RS::ARRAY_INDEX; i++) { + if (!(s->format & (1 << i))) { + attribs[i].enabled = false; + attribs[i].integer = false; + continue; + } + + attribs[i].enabled = true; + attribs[i].integer = false; + + switch (i) { + case RS::ARRAY_VERTEX: { + attribs[i].offset = vertex_stride; + if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) { + attribs[i].size = 2; + } else { + attribs[i].size = 3; + } + attribs[i].type = GL_FLOAT; + vertex_stride += attribs[i].size * sizeof(float); + attribs[i].normalized = GL_FALSE; + } 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].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].normalized = GL_TRUE; + } break; + case RS::ARRAY_COLOR: { + attribs[i].offset = attributes_stride; + attribs[i].size = 4; + attribs[i].type = GL_UNSIGNED_BYTE; + attributes_stride += 4; + attribs[i].normalized = GL_TRUE; + } break; + case RS::ARRAY_TEX_UV: { + attribs[i].offset = attributes_stride; + attribs[i].size = 2; + attribs[i].type = GL_FLOAT; + attributes_stride += 2 * sizeof(float); + attribs[i].normalized = GL_FALSE; + } break; + case RS::ARRAY_TEX_UV2: { + attribs[i].offset = attributes_stride; + attribs[i].size = 2; + attribs[i].type = GL_FLOAT; + attributes_stride += 2 * sizeof(float); + attribs[i].normalized = GL_FALSE; + } break; + case RS::ARRAY_CUSTOM0: + case RS::ARRAY_CUSTOM1: + case RS::ARRAY_CUSTOM2: + case RS::ARRAY_CUSTOM3: { + attribs[i].offset = attributes_stride; + + int idx = i - RS::ARRAY_CUSTOM0; + uint32_t fmt_shift[RS::ARRAY_CUSTOM_COUNT] = { RS::ARRAY_FORMAT_CUSTOM0_SHIFT, RS::ARRAY_FORMAT_CUSTOM1_SHIFT, RS::ARRAY_FORMAT_CUSTOM2_SHIFT, RS::ARRAY_FORMAT_CUSTOM3_SHIFT }; + uint32_t fmt = (s->format >> fmt_shift[idx]) & RS::ARRAY_FORMAT_CUSTOM_MASK; + uint32_t fmtsize[RS::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 }; + GLenum gl_type[RS::ARRAY_CUSTOM_MAX] = { GL_UNSIGNED_BYTE, GL_BYTE, GL_HALF_FLOAT, GL_HALF_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT }; + GLboolean norm[RS::ARRAY_CUSTOM_MAX] = { GL_TRUE, GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE }; + attribs[i].type = gl_type[fmt]; + attributes_stride += fmtsize[fmt]; + attribs[i].size = fmtsize[fmt] / sizeof(float); + attribs[i].normalized = norm[fmt]; + } break; + case RS::ARRAY_BONES: { + attribs[i].offset = skin_stride; + attribs[i].size = 4; + attribs[i].type = GL_UNSIGNED_SHORT; + attributes_stride += 4 * sizeof(uint16_t); + attribs[i].normalized = GL_FALSE; + attribs[i].integer = true; + } break; + case RS::ARRAY_WEIGHTS: { + attribs[i].offset = skin_stride; + attribs[i].size = 4; + attribs[i].type = GL_UNSIGNED_SHORT; + attributes_stride += 4 * sizeof(uint16_t); + attribs[i].normalized = GL_TRUE; + } break; + } + } + + glGenVertexArrays(1, &v.vertex_array); + glBindVertexArray(v.vertex_array); + + for (int i = 0; i < RS::ARRAY_INDEX; i++) { + if (!attribs[i].enabled) { + glDisableVertexAttribArray(i); + continue; + } + if (i <= RS::ARRAY_TANGENT) { + attribs[i].stride = vertex_stride; + if (mis) { + glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffer); + } else { + glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); + } + } else if (i <= RS::ARRAY_CUSTOM3) { + attribs[i].stride = attributes_stride; + glBindBuffer(GL_ARRAY_BUFFER, s->attribute_buffer); + } else { + attribs[i].stride = skin_stride; + glBindBuffer(GL_ARRAY_BUFFER, s->skin_buffer); + } + + if (attribs[i].integer) { + glVertexAttribIPointer(i, attribs[i].size, attribs[i].type, attribs[i].stride, CAST_INT_TO_UCHAR_PTR(attribs[i].offset)); + } else { + glVertexAttribPointer(i, attribs[i].size, attribs[i].type, attribs[i].normalized, attribs[i].stride, CAST_INT_TO_UCHAR_PTR(attribs[i].offset)); + } + glEnableVertexAttribArray(i); + } + + // Do not bind index here as we want to switch between index buffers for LOD + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + v.input_mask = p_input_mask; +} + +/* MESH INSTANCE API */ + +RID MeshStorage::mesh_instance_create(RID p_base) { + Mesh *mesh = mesh_owner.get_or_null(p_base); + ERR_FAIL_COND_V(!mesh, RID()); + + RID rid = mesh_instance_owner.make_rid(); + MeshInstance *mi = mesh_instance_owner.get_or_null(rid); + + mi->mesh = mesh; + + for (uint32_t i = 0; i < mesh->surface_count; i++) { + _mesh_instance_add_surface(mi, mesh, i); + } + + mi->I = mesh->instances.push_back(mi); + + mi->dirty = true; + + return rid; +} + +void MeshStorage::mesh_instance_free(RID p_rid) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_rid); + _mesh_instance_clear(mi); + mi->mesh->instances.erase(mi->I); + mi->I = nullptr; + + mesh_instance_owner.free(p_rid); +} + +void MeshStorage::mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + if (mi->skeleton == p_skeleton) { + return; + } + mi->skeleton = p_skeleton; + mi->skeleton_version = 0; + mi->dirty = true; +} + +void MeshStorage::mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + ERR_FAIL_COND(!mi); + ERR_FAIL_INDEX(p_shape, (int)mi->blend_weights.size()); + mi->blend_weights[p_shape] = p_weight; + mi->weights_dirty = true; +} + +void MeshStorage::_mesh_instance_clear(MeshInstance *mi) { + for (uint32_t i = 0; i < mi->surfaces.size(); i++) { + if (mi->surfaces[i].version_count != 0) { + for (uint32_t j = 0; j < mi->surfaces[i].version_count; j++) { + glDeleteVertexArrays(1, &mi->surfaces[i].versions[j].vertex_array); + mi->surfaces[i].versions[j].vertex_array = 0; + } + memfree(mi->surfaces[i].versions); + } + if (mi->surfaces[i].vertex_buffer != 0) { + glDeleteBuffers(1, &mi->surfaces[i].vertex_buffer); + mi->surfaces[i].vertex_buffer = 0; + } + } + mi->surfaces.clear(); + + if (mi->blend_weights_buffer != 0) { + glDeleteBuffers(1, &mi->blend_weights_buffer); + mi->blend_weights_buffer = 0; + } + mi->blend_weights.clear(); + mi->weights_dirty = false; + mi->skeleton_version = 0; +} + +void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface) { + if (mesh->blend_shape_count > 0 && mi->blend_weights_buffer == 0) { + mi->blend_weights.resize(mesh->blend_shape_count); + for (uint32_t i = 0; i < mi->blend_weights.size(); i++) { + mi->blend_weights[i] = 0; + } + // Todo allocate buffer for blend_weights and copy data to it + //mi->blend_weights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(float) * mi->blend_weights.size(), mi->blend_weights.to_byte_array()); + + mi->weights_dirty = true; + } + + MeshInstance::Surface s; + if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) { + //surface warrants transform + //s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); + } + + mi->surfaces.push_back(s); + mi->dirty = true; +} + +void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + + bool needs_update = mi->dirty; + + if (mi->weights_dirty && !mi->weight_update_list.in_list()) { + dirty_mesh_instance_weights.add(&mi->weight_update_list); + needs_update = true; + } + + if (mi->array_update_list.in_list()) { + return; + } + + if (!needs_update && mi->skeleton.is_valid()) { + Skeleton *sk = skeleton_owner.get_or_null(mi->skeleton); + if (sk && sk->version != mi->skeleton_version) { + needs_update = true; + } + } + + if (needs_update) { + dirty_mesh_instance_arrays.add(&mi->array_update_list); + } +} + +void MeshStorage::update_mesh_instances() { + while (dirty_mesh_instance_weights.first()) { + MeshInstance *mi = dirty_mesh_instance_weights.first()->self(); + + if (mi->blend_weights_buffer != 0) { + //RD::get_singleton()->buffer_update(mi->blend_weights_buffer, 0, mi->blend_weights.size() * sizeof(float), mi->blend_weights.ptr()); + } + dirty_mesh_instance_weights.remove(&mi->weight_update_list); + mi->weights_dirty = false; + } + if (dirty_mesh_instance_arrays.first() == nullptr) { + return; //nothing to do + } + + // Process skeletons and blend shapes using transform feedback + // TODO: Implement when working on skeletons and blend shapes +} + +/* MULTIMESH API */ + +RID MeshStorage::multimesh_allocate() { + return multimesh_owner.allocate_rid(); +} + +void MeshStorage::multimesh_initialize(RID p_rid) { + multimesh_owner.initialize_rid(p_rid, MultiMesh()); +} + +void MeshStorage::multimesh_free(RID p_rid) { + _update_dirty_multimeshes(); + multimesh_allocate_data(p_rid, 0, RS::MULTIMESH_TRANSFORM_2D); + MultiMesh *multimesh = multimesh_owner.get_or_null(p_rid); + multimesh->dependency.deleted_notify(p_rid); + multimesh_owner.free(p_rid); +} + +void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors, bool p_use_custom_data) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + + if (multimesh->instances == p_instances && multimesh->xform_format == p_transform_format && multimesh->uses_colors == p_use_colors && multimesh->uses_custom_data == p_use_custom_data) { + return; + } + + if (multimesh->buffer) { + glDeleteBuffers(1, &multimesh->buffer); + multimesh->buffer = 0; + } + + if (multimesh->data_cache_dirty_regions) { + memdelete_arr(multimesh->data_cache_dirty_regions); + multimesh->data_cache_dirty_regions = nullptr; + multimesh->data_cache_used_dirty_regions = 0; + } + + multimesh->instances = p_instances; + multimesh->xform_format = p_transform_format; + 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 ? 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>(); + multimesh->aabb = AABB(); + multimesh->aabb_dirty = false; + multimesh->visible_instances = MIN(multimesh->visible_instances, multimesh->instances); + + if (multimesh->instances) { + glGenBuffers(1, &multimesh->buffer); + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); + glBufferData(GL_ARRAY_BUFFER, multimesh->instances * multimesh->stride_cache * sizeof(float), nullptr, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); +} + +int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + return multimesh->instances; +} + +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 || p_mesh.is_null()) { + return; + } + multimesh->mesh = p_mesh; + + if (multimesh->instances == 0) { + return; + } + + if (multimesh->data_cache.size()) { + //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. + if (multimesh->buffer_set) { + 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(Dependency::DEPENDENCY_CHANGED_MESH); +} + +#define MULTIMESH_DIRTY_REGION_SIZE 512 + +void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { + if (multimesh->data_cache.size() > 0) { + return; //already local + } + ERR_FAIL_COND(multimesh->data_cache.size() > 0); + // this means that the user wants to load/save individual elements, + // for this, the data must reside on CPU, so just copy it there. + multimesh->data_cache.resize(multimesh->instances * multimesh->stride_cache); + { + float *w = multimesh->data_cache.ptrw(); + + if (multimesh->buffer_set) { + 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()); + } + } else { + memset(w, 0, (size_t)multimesh->instances * multimesh->stride_cache * sizeof(float)); + } + } + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = false; + } + multimesh->data_cache_used_dirty_regions = 0; +} + +void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { + uint32_t region_index = p_index / MULTIMESH_DIRTY_REGION_SIZE; +#ifdef DEBUG_ENABLED + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + ERR_FAIL_UNSIGNED_INDEX(region_index, data_cache_dirty_region_count); //bug +#endif + if (!multimesh->data_cache_dirty_regions[region_index]) { + multimesh->data_cache_dirty_regions[region_index] = true; + multimesh->data_cache_used_dirty_regions++; + } + + if (p_aabb) { + multimesh->aabb_dirty = true; + } + + if (!multimesh->dirty) { + multimesh->dirty_list = multimesh_dirty_list; + multimesh_dirty_list = multimesh; + multimesh->dirty = true; + } +} + +void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb) { + if (p_data) { + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + if (!multimesh->data_cache_dirty_regions[i]) { + multimesh->data_cache_dirty_regions[i] = true; + multimesh->data_cache_used_dirty_regions++; + } + } + } + + if (p_aabb) { + multimesh->aabb_dirty = true; + } + + if (!multimesh->dirty) { + multimesh->dirty_list = multimesh_dirty_list; + multimesh_dirty_list = multimesh; + multimesh->dirty = true; + } +} + +void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) { + ERR_FAIL_COND(multimesh->mesh.is_null()); + AABB aabb; + AABB mesh_aabb = mesh_get_aabb(multimesh->mesh); + for (int i = 0; i < p_instances; i++) { + const float *data = p_data + multimesh->stride_cache * i; + Transform3D t; + + if (multimesh->xform_format == RS::MULTIMESH_TRANSFORM_3D) { + t.basis.rows[0][0] = data[0]; + t.basis.rows[0][1] = data[1]; + t.basis.rows[0][2] = data[2]; + t.origin.x = data[3]; + t.basis.rows[1][0] = data[4]; + t.basis.rows[1][1] = data[5]; + t.basis.rows[1][2] = data[6]; + t.origin.y = data[7]; + t.basis.rows[2][0] = data[8]; + t.basis.rows[2][1] = data[9]; + t.basis.rows[2][2] = data[10]; + t.origin.z = data[11]; + + } else { + t.basis.rows[0].x = data[0]; + t.basis.rows[1].x = data[1]; + t.origin.x = data[3]; + + t.basis.rows[0].y = data[4]; + t.basis.rows[1].y = data[5]; + t.origin.y = data[7]; + } + + if (i == 0) { + aabb = t.xform(mesh_aabb); + } else { + aabb.merge_with(t.xform(mesh_aabb)); + } + } + + multimesh->aabb = aabb; +} + +void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache; + + dataptr[0] = p_transform.basis.rows[0][0]; + dataptr[1] = p_transform.basis.rows[0][1]; + dataptr[2] = p_transform.basis.rows[0][2]; + dataptr[3] = p_transform.origin.x; + dataptr[4] = p_transform.basis.rows[1][0]; + dataptr[5] = p_transform.basis.rows[1][1]; + dataptr[6] = p_transform.basis.rows[1][2]; + dataptr[7] = p_transform.origin.y; + dataptr[8] = p_transform.basis.rows[2][0]; + dataptr[9] = p_transform.basis.rows[2][1]; + dataptr[10] = p_transform.basis.rows[2][2]; + dataptr[11] = p_transform.origin.z; + } + + _multimesh_mark_dirty(multimesh, p_index, true); +} + +void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache; + + dataptr[0] = p_transform.columns[0][0]; + dataptr[1] = p_transform.columns[1][0]; + dataptr[2] = 0; + dataptr[3] = p_transform.columns[2][0]; + dataptr[4] = p_transform.columns[0][1]; + dataptr[5] = p_transform.columns[1][1]; + dataptr[6] = 0; + dataptr[7] = p_transform.columns[2][1]; + } + + _multimesh_mark_dirty(multimesh, p_index, true); +} + +void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(!multimesh->uses_colors); + + _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; + 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); +} + +void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_INDEX(p_index, multimesh->instances); + ERR_FAIL_COND(!multimesh->uses_custom_data); + + _multimesh_make_local(multimesh); + + { + float *w = multimesh->data_cache.ptrw(); + + float *dataptr = w + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + 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); +} + +RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, RID()); + + return multimesh->mesh; +} + +AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, AABB()); + if (multimesh->aabb_dirty) { + const_cast<MeshStorage *>(this)->_update_dirty_multimeshes(); + } + return multimesh->aabb; +} + +Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform3D()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform3D()); + ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D, Transform3D()); + + _multimesh_make_local(multimesh); + + Transform3D t; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache; + + t.basis.rows[0][0] = dataptr[0]; + t.basis.rows[0][1] = dataptr[1]; + t.basis.rows[0][2] = dataptr[2]; + t.origin.x = dataptr[3]; + t.basis.rows[1][0] = dataptr[4]; + t.basis.rows[1][1] = dataptr[5]; + t.basis.rows[1][2] = dataptr[6]; + t.origin.y = dataptr[7]; + t.basis.rows[2][0] = dataptr[8]; + t.basis.rows[2][1] = dataptr[9]; + t.basis.rows[2][2] = dataptr[10]; + t.origin.z = dataptr[11]; + } + + return t; +} + +Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Transform2D()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Transform2D()); + ERR_FAIL_COND_V(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D, Transform2D()); + + _multimesh_make_local(multimesh); + + Transform2D t; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache; + + t.columns[0][0] = dataptr[0]; + t.columns[1][0] = dataptr[1]; + t.columns[2][0] = dataptr[3]; + t.columns[0][1] = dataptr[4]; + t.columns[1][1] = dataptr[5]; + t.columns[2][1] = dataptr[7]; + } + + return t; +} + +Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); + ERR_FAIL_COND_V(!multimesh->uses_colors, Color()); + + _multimesh_make_local(multimesh); + + Color c; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->color_offset_cache; + 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; +} + +Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, Color()); + ERR_FAIL_INDEX_V(p_index, multimesh->instances, Color()); + ERR_FAIL_COND_V(!multimesh->uses_custom_data, Color()); + + _multimesh_make_local(multimesh); + + Color c; + { + const float *r = multimesh->data_cache.ptr(); + + const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + 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; +} + +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); + + 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->uses_colors || multimesh->uses_custom_data) { + //if we have a data cache, just update it + 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; + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = false; + } + multimesh->data_cache_used_dirty_regions = 0; + } + + _multimesh_mark_all_dirty(multimesh, false, true); //update AABB + } else if (multimesh->mesh.is_valid()) { + //if we have a mesh set, we need to re-generate the AABB from the new data + const float *data = p_buffer.ptr(); + + _multimesh_re_create_aabb(multimesh, data, multimesh->instances); + 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>()); + Vector<float> ret; + if (multimesh->buffer == 0) { + return Vector<float>(); + } else if (multimesh->data_cache.size()) { + ret = multimesh->data_cache; + } else { + // Buffer not cached, so fetch from GPU memory. This can be a stalling operation, avoid whenever possible. + + 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()); + } + } + 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; + } +} + +void MeshStorage::multimesh_set_visible_instances(RID p_multimesh, int p_visible) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + ERR_FAIL_COND(p_visible < -1 || p_visible > multimesh->instances); + if (multimesh->visible_instances == p_visible) { + return; + } + + if (multimesh->data_cache.size()) { + //there is a data cache.. + _multimesh_mark_all_dirty(multimesh, false, true); + } + + multimesh->visible_instances = p_visible; + + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH_VISIBLE_INSTANCES); +} + +int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, 0); + return multimesh->visible_instances; +} + +void MeshStorage::_update_dirty_multimeshes() { + while (multimesh_dirty_list) { + MultiMesh *multimesh = multimesh_dirty_list; + + if (multimesh->data_cache.size()) { //may have been cleared, so only process if it exists + const float *data = multimesh->data_cache.ptr(); + + uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; + + if (multimesh->data_cache_used_dirty_regions) { + uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + + GLint region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); + + 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 * multimesh->stride_cache * sizeof(float)), data, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } else { + // Not that many regions? update them all + // TODO: profile the performance cost on low end + glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); + for (uint32_t i = 0; i < visible_region_count; i++) { + if (multimesh->data_cache_dirty_regions[i]) { + GLint offset = i * region_size; + GLint size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); + uint32_t region_start_index = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; + glBufferSubData(GL_ARRAY_BUFFER, offset, MIN(region_size, size - offset), &data[region_start_index]); + } + } + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { + multimesh->data_cache_dirty_regions[i] = false; + } + + multimesh->data_cache_used_dirty_regions = 0; + } + + if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) { + _multimesh_re_create_aabb(multimesh, data, visible_instances); + multimesh->aabb_dirty = false; + multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB); + } + } + + multimesh_dirty_list = multimesh->dirty_list; + + multimesh->dirty_list = nullptr; + multimesh->dirty = false; + } + + multimesh_dirty_list = nullptr; +} + +/* SKELETON API */ + +RID MeshStorage::skeleton_allocate() { + return RID(); +} + +void MeshStorage::skeleton_initialize(RID p_rid) { +} + +void MeshStorage::skeleton_free(RID p_rid) { +} + +void MeshStorage::skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton) { +} + +void MeshStorage::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) { +} + +int MeshStorage::skeleton_get_bone_count(RID p_skeleton) const { + return 0; +} + +void MeshStorage::skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) { +} + +Transform3D MeshStorage::skeleton_bone_get_transform(RID p_skeleton, int p_bone) const { + return Transform3D(); +} + +void MeshStorage::skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) { +} + +Transform2D MeshStorage::skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const { + return Transform2D(); +} + +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 new file mode 100644 index 0000000000..339380b3b0 --- /dev/null +++ b/drivers/gles3/storage/mesh_storage.h @@ -0,0 +1,546 @@ +/*************************************************************************/ +/* mesh_storage.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 MESH_STORAGE_GLES3_H +#define MESH_STORAGE_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/storage/mesh_storage.h" +#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 { + +struct MeshInstance; + +struct Mesh { + struct Surface { + struct Attrib { + bool enabled; + bool integer; + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + uint32_t offset; + }; + RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS; + uint32_t format = 0; + + GLuint vertex_buffer = 0; + GLuint attribute_buffer = 0; + GLuint skin_buffer = 0; + uint32_t vertex_count = 0; + uint32_t vertex_buffer_size = 0; + uint32_t attribute_buffer_size = 0; + uint32_t skin_buffer_size = 0; + + // Cache vertex arrays so they can be created + struct Version { + uint32_t input_mask = 0; + GLuint vertex_array; + + Attrib attribs[RS::ARRAY_MAX]; + }; + + SpinLock version_lock; //needed to access versions + Version *versions = nullptr; //allocated on demand + uint32_t version_count = 0; + + GLuint index_buffer = 0; + uint32_t index_count = 0; + uint32_t index_buffer_size = 0; + + struct LOD { + float edge_length = 0.0; + uint32_t index_count = 0; + uint32_t index_buffer_size = 0; + GLuint index_buffer; + }; + + LOD *lods = nullptr; + uint32_t lod_count = 0; + + AABB aabb; + + Vector<AABB> bone_aabbs; + + GLuint blend_shape_buffer = 0; + + RID material; + }; + + uint32_t blend_shape_count = 0; + RS::BlendShapeMode blend_shape_mode = RS::BLEND_SHAPE_MODE_NORMALIZED; + + Surface **surfaces = nullptr; + uint32_t surface_count = 0; + + Vector<AABB> bone_aabbs; + + bool has_bone_weights = false; + + AABB aabb; + AABB custom_aabb; + + Vector<RID> material_cache; + + List<MeshInstance *> instances; + + RID shadow_mesh; + HashSet<Mesh *> shadow_owners; + + Dependency dependency; +}; + +/* Mesh Instance */ + +struct MeshInstance { + Mesh *mesh = nullptr; + RID skeleton; + struct Surface { + GLuint vertex_buffer = 0; + + Mesh::Surface::Version *versions = nullptr; //allocated on demand + uint32_t version_count = 0; + }; + LocalVector<Surface> surfaces; + LocalVector<float> blend_weights; + + GLuint blend_weights_buffer = 0; + List<MeshInstance *>::Element *I = nullptr; //used to erase itself + uint64_t skeleton_version = 0; + bool dirty = false; + bool weights_dirty = false; + SelfList<MeshInstance> weight_update_list; + SelfList<MeshInstance> array_update_list; + MeshInstance() : + weight_update_list(this), array_update_list(this) {} +}; + +/* MultiMesh */ + +struct MultiMesh { + RID mesh; + int instances = 0; + RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D; + bool uses_colors = false; + bool uses_custom_data = false; + int visible_instances = -1; + AABB aabb; + bool aabb_dirty = false; + bool buffer_set = false; + uint32_t stride_cache = 0; + uint32_t color_offset_cache = 0; + uint32_t custom_data_offset_cache = 0; + + Vector<float> data_cache; //used if individual setting is used + bool *data_cache_dirty_regions = nullptr; + uint32_t data_cache_used_dirty_regions = 0; + + GLuint buffer; + + bool dirty = false; + MultiMesh *dirty_list = nullptr; + + Dependency dependency; +}; + +struct Skeleton { + bool use_2d = false; + int size = 0; + Vector<float> data; + GLuint buffer = 0; + + bool dirty = false; + Skeleton *dirty_list = nullptr; + Transform2D base_transform_2d; + + uint64_t version = 1; + + Dependency dependency; +}; + +class MeshStorage : public RendererMeshStorage { +private: + static MeshStorage *singleton; + + /* Mesh */ + + mutable RID_Owner<Mesh, true> mesh_owner; + + void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis = nullptr); + + /* Mesh Instance API */ + + mutable RID_Owner<MeshInstance> mesh_instance_owner; + + void _mesh_instance_clear(MeshInstance *mi); + void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface); + SelfList<MeshInstance>::List dirty_mesh_instance_weights; + SelfList<MeshInstance>::List dirty_mesh_instance_arrays; + + /* MultiMesh */ + + mutable RID_Owner<MultiMesh, true> multimesh_owner; + + MultiMesh *multimesh_dirty_list = nullptr; + + _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; + _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); + _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); + _FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances); + + /* Skeleton */ + + mutable RID_Owner<Skeleton, true> skeleton_owner; + + Skeleton *skeleton_dirty_list = nullptr; + + _FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton); + +public: + static MeshStorage *get_singleton(); + + MeshStorage(); + virtual ~MeshStorage(); + + /* MESH API */ + + Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); }; + bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); }; + + virtual RID mesh_allocate() override; + virtual void mesh_initialize(RID p_rid) override; + virtual void mesh_free(RID p_rid) override; + + virtual void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override; + virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override; + + virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override; + + virtual int mesh_get_blend_shape_count(RID p_mesh) const override; + + virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override; + virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override; + + virtual void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + virtual void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + virtual void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override; + + virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override; + virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override; + + virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override; + virtual int mesh_get_surface_count(RID p_mesh) const override; + + virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override; + virtual AABB mesh_get_custom_aabb(RID p_mesh) const override; + + virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override; + virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override; + virtual void mesh_clear(RID p_mesh) override; + + _FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, nullptr); + r_surface_count = mesh->surface_count; + if (r_surface_count == 0) { + return nullptr; + } + if (mesh->material_cache.is_empty()) { + mesh->material_cache.resize(mesh->surface_count); + for (uint32_t i = 0; i < r_surface_count; i++) { + mesh->material_cache.write[i] = mesh->surfaces[i]->material; + } + } + + return mesh->material_cache.ptr(); + } + + _FORCE_INLINE_ void *mesh_get_surface(RID p_mesh, uint32_t p_surface_index) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, nullptr); + ERR_FAIL_UNSIGNED_INDEX_V(p_surface_index, mesh->surface_count, nullptr); + + return mesh->surfaces[p_surface_index]; + } + + _FORCE_INLINE_ RID mesh_get_shadow_mesh(RID p_mesh) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND_V(!mesh, RID()); + + return mesh->shadow_mesh; + } + + _FORCE_INLINE_ RS::PrimitiveType mesh_surface_get_primitive(void *p_surface) { + Mesh::Surface *surface = reinterpret_cast<Mesh::Surface *>(p_surface); + return surface->primitive; + } + + _FORCE_INLINE_ bool mesh_surface_has_lod(void *p_surface) const { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + return s->lod_count > 0; + } + + _FORCE_INLINE_ uint32_t mesh_surface_get_vertices_drawn_count(void *p_surface) const { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + 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 { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + + int32_t current_lod = -1; + if (r_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) { + break; + } + current_lod = i; + } + if (current_lod == -1) { + return 0; + } else { + if (r_index_count) { + *r_index_count = s->lods[current_lod].index_count; + } + return current_lod + 1; + } + } + + _FORCE_INLINE_ GLuint mesh_surface_get_index_buffer(void *p_surface, uint32_t p_lod) const { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + + if (p_lod == 0) { + return s->index_buffer; + } else { + return s->lods[p_lod - 1].index_buffer; + } + } + + _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; + } + + // Use this to cache Vertex Array Objects so they are only generated once + _FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, GLuint &r_vertex_array_gl) { + Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); + + s->version_lock.lock(); + + //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way + + for (uint32_t i = 0; i < s->version_count; i++) { + if (s->versions[i].input_mask != p_input_mask) { + continue; + } + //we have this version, hooray + r_vertex_array_gl = s->versions[i].vertex_array; + s->version_lock.unlock(); + return; + } + + uint32_t version = s->version_count; + s->version_count++; + s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count); + + _mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask); + + r_vertex_array_gl = s->versions[version].vertex_array; + + s->version_lock.unlock(); + } + + /* MESH INSTANCE API */ + + MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); }; + bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); }; + + virtual RID mesh_instance_create(RID p_base) override; + virtual void mesh_instance_free(RID p_rid) override; + virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override; + virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override; + virtual void mesh_instance_check_for_update(RID p_mesh_instance) override; + virtual void update_mesh_instances() override; + + _FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, GLuint &r_vertex_array_gl) { + MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance); + ERR_FAIL_COND(!mi); + Mesh *mesh = mi->mesh; + ERR_FAIL_UNSIGNED_INDEX(p_surface_index, mesh->surface_count); + + MeshInstance::Surface *mis = &mi->surfaces[p_surface_index]; + Mesh::Surface *s = mesh->surfaces[p_surface_index]; + + s->version_lock.lock(); + + //there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way + + for (uint32_t i = 0; i < mis->version_count; i++) { + if (mis->versions[i].input_mask != p_input_mask) { + continue; + } + //we have this version, hooray + r_vertex_array_gl = mis->versions[i].vertex_array; + s->version_lock.unlock(); + return; + } + + uint32_t version = mis->version_count; + mis->version_count++; + mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count); + + _mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis); + + r_vertex_array_gl = mis->versions[version].vertex_array; + + s->version_lock.unlock(); + } + + /* MULTIMESH API */ + + MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }; + bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }; + + virtual RID multimesh_allocate() override; + virtual void multimesh_initialize(RID p_rid) override; + virtual void multimesh_free(RID p_rid) override; + virtual void multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false) override; + virtual int multimesh_get_instance_count(RID p_multimesh) const override; + + virtual void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override; + virtual void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override; + virtual void multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override; + virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override; + virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override; + + virtual RID multimesh_get_mesh(RID p_multimesh) const override; + virtual AABB multimesh_get_aabb(RID p_multimesh) const override; + + virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override; + virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override; + virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override; + virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override; + virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override; + virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override; + + virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override; + virtual int multimesh_get_visible_instances(RID p_multimesh) const override; + + void _update_dirty_multimeshes(); + + _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->xform_format; + } + + _FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->uses_colors; + } + + _FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->uses_custom_data; + } + + _FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + if (multimesh->visible_instances >= 0) { + return multimesh->visible_instances; + } + return multimesh->instances; + } + + _FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->buffer; + } + + _FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->stride_cache; + } + + _FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->color_offset_cache; + } + + _FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + return multimesh->custom_data_offset_cache; + } + + /* SKELETON API */ + + Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); }; + bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); }; + + virtual RID skeleton_allocate() override; + virtual void skeleton_initialize(RID p_rid) override; + virtual void skeleton_free(RID p_rid) override; + + virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override; + virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override; + virtual int skeleton_get_bone_count(RID p_skeleton) const override; + virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override; + virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override; + 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, 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 diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp new file mode 100644 index 0000000000..9ed9fedd5a --- /dev/null +++ b/drivers/gles3/storage/particles_storage.cpp @@ -0,0 +1,254 @@ +/*************************************************************************/ +/* particles_storage.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 "particles_storage.h" + +using namespace GLES3; + +ParticlesStorage *ParticlesStorage::singleton = nullptr; + +ParticlesStorage *ParticlesStorage::get_singleton() { + return singleton; +} + +ParticlesStorage::ParticlesStorage() { + singleton = this; +} + +ParticlesStorage::~ParticlesStorage() { + singleton = nullptr; +} + +/* PARTICLES */ + +RID ParticlesStorage::particles_allocate() { + return RID(); +} + +void ParticlesStorage::particles_initialize(RID p_rid) { +} + +void ParticlesStorage::particles_free(RID p_rid) { +} + +void ParticlesStorage::particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) { +} + +void ParticlesStorage::particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { +} + +void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting) { +} + +void ParticlesStorage::particles_set_amount(RID p_particles, int p_amount) { +} + +void ParticlesStorage::particles_set_lifetime(RID p_particles, double p_lifetime) { +} + +void ParticlesStorage::particles_set_one_shot(RID p_particles, bool p_one_shot) { +} + +void ParticlesStorage::particles_set_pre_process_time(RID p_particles, double p_time) { +} + +void ParticlesStorage::particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) { +} + +void ParticlesStorage::particles_set_randomness_ratio(RID p_particles, real_t p_ratio) { +} + +void ParticlesStorage::particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) { +} + +void ParticlesStorage::particles_set_speed_scale(RID p_particles, double p_scale) { +} + +void ParticlesStorage::particles_set_use_local_coordinates(RID p_particles, bool p_enable) { +} + +void ParticlesStorage::particles_set_process_material(RID p_particles, RID p_material) { +} + +RID ParticlesStorage::particles_get_process_material(RID p_particles) const { + return RID(); +} + +void ParticlesStorage::particles_set_fixed_fps(RID p_particles, int p_fps) { +} + +void ParticlesStorage::particles_set_interpolate(RID p_particles, bool p_enable) { +} + +void ParticlesStorage::particles_set_fractional_delta(RID p_particles, bool p_enable) { +} + +void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { +} + +void ParticlesStorage::particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) { +} + +void ParticlesStorage::particles_set_collision_base_size(RID p_particles, real_t p_size) { +} + +void ParticlesStorage::particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) { +} + +void ParticlesStorage::particles_set_trails(RID p_particles, bool p_enable, double p_length) { +} + +void ParticlesStorage::particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) { +} + +void ParticlesStorage::particles_restart(RID p_particles) { +} + +void ParticlesStorage::particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) { +} + +void ParticlesStorage::particles_set_draw_passes(RID p_particles, int p_count) { +} + +void ParticlesStorage::particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) { +} + +void ParticlesStorage::particles_request_process(RID p_particles) { +} + +AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) { + return AABB(); +} + +AABB ParticlesStorage::particles_get_aabb(RID p_particles) const { + return AABB(); +} + +void ParticlesStorage::particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) { +} + +bool ParticlesStorage::particles_get_emitting(RID p_particles) { + return false; +} + +int ParticlesStorage::particles_get_draw_passes(RID p_particles) const { + return 0; +} + +RID ParticlesStorage::particles_get_draw_pass_mesh(RID p_particles, int p_pass) const { + return RID(); +} + +void ParticlesStorage::particles_add_collision(RID p_particles, RID p_instance) { +} + +void ParticlesStorage::particles_remove_collision(RID p_particles, RID p_instance) { +} + +void ParticlesStorage::particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) { +} + +void ParticlesStorage::update_particles() { +} + +bool ParticlesStorage::particles_is_inactive(RID p_particles) const { + return false; +} + +/* PARTICLES COLLISION */ + +RID ParticlesStorage::particles_collision_allocate() { + return RID(); +} + +void ParticlesStorage::particles_collision_initialize(RID p_rid) { +} + +void ParticlesStorage::particles_collision_free(RID p_rid) { +} + +void ParticlesStorage::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) { +} + +void ParticlesStorage::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) { +} + +void ParticlesStorage::particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) { +} + +void ParticlesStorage::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) { +} + +void ParticlesStorage::particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) { +} + +void ParticlesStorage::particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) { +} + +void ParticlesStorage::particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) { +} + +void ParticlesStorage::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) { +} + +void ParticlesStorage::particles_collision_height_field_update(RID p_particles_collision) { +} + +void ParticlesStorage::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) { +} + +AABB ParticlesStorage::particles_collision_get_aabb(RID p_particles_collision) const { + return AABB(); +} + +bool ParticlesStorage::particles_collision_is_heightfield(RID p_particles_collision) const { + return false; +} + +RID ParticlesStorage::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const { + return RID(); +} + +RID ParticlesStorage::particles_collision_instance_create(RID p_collision) { + return RID(); +} + +void ParticlesStorage::particles_collision_instance_free(RID p_rid) { +} + +void ParticlesStorage::particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) { +} + +void ParticlesStorage::particles_collision_instance_set_active(RID p_collision_instance, bool p_active) { +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h new file mode 100644 index 0000000000..84d1f94d8c --- /dev/null +++ b/drivers/gles3/storage/particles_storage.h @@ -0,0 +1,140 @@ +/*************************************************************************/ +/* particles_storage.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 PARTICLES_STORAGE_GLES3_H +#define PARTICLES_STORAGE_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/storage/particles_storage.h" + +namespace GLES3 { + +class ParticlesStorage : public RendererParticlesStorage { +private: + static ParticlesStorage *singleton; + +public: + static ParticlesStorage *get_singleton(); + + ParticlesStorage(); + virtual ~ParticlesStorage(); + + /* PARTICLES */ + + virtual RID particles_allocate() override; + virtual void particles_initialize(RID p_rid) override; + virtual void particles_free(RID p_rid) override; + + virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override; + virtual void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override; + virtual void particles_set_emitting(RID p_particles, bool p_emitting) override; + virtual void particles_set_amount(RID p_particles, int p_amount) override; + virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override; + virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override; + virtual void particles_set_pre_process_time(RID p_particles, double p_time) override; + virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override; + virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override; + virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override; + virtual void particles_set_speed_scale(RID p_particles, double p_scale) override; + virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override; + virtual void particles_set_process_material(RID p_particles, RID p_material) override; + virtual RID particles_get_process_material(RID p_particles) const override; + virtual void particles_set_fixed_fps(RID p_particles, int p_fps) override; + virtual void particles_set_interpolate(RID p_particles, bool p_enable) override; + virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override; + virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override; + virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override; + virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override; + + virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override; + + virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override; + virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override; + + virtual void particles_restart(RID p_particles) override; + + virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override; + + virtual void particles_set_draw_passes(RID p_particles, int p_count) override; + virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override; + + virtual void particles_request_process(RID p_particles) override; + virtual AABB particles_get_current_aabb(RID p_particles) override; + virtual AABB particles_get_aabb(RID p_particles) const override; + + virtual void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override; + + virtual bool particles_get_emitting(RID p_particles) override; + virtual int particles_get_draw_passes(RID p_particles) const override; + virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override; + + virtual void particles_add_collision(RID p_particles, RID p_instance) override; + virtual void particles_remove_collision(RID p_particles, RID p_instance) override; + + virtual void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, RID p_texture) override; + + virtual void update_particles() override; + virtual bool particles_is_inactive(RID p_particles) const override; + + /* PARTICLES COLLISION */ + + virtual RID particles_collision_allocate() override; + virtual void particles_collision_initialize(RID p_rid) override; + virtual void particles_collision_free(RID p_rid) override; + + virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override; + virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override; + virtual void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override; + virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override; + virtual void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override; + virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override; + virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override; + virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override; + virtual void particles_collision_height_field_update(RID p_particles_collision) override; + virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override; + virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override; + virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override; + virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override; + + virtual RID particles_collision_instance_create(RID p_collision) override; + virtual void particles_collision_instance_free(RID p_rid) override; + virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override; + virtual void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override; +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // PARTICLES_STORAGE_GLES3_H diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp new file mode 100644 index 0000000000..543638e8ff --- /dev/null +++ b/drivers/gles3/storage/texture_storage.cpp @@ -0,0 +1,1693 @@ +/*************************************************************************/ +/* texture_storage.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 "texture_storage.h" +#include "config.h" +#include "drivers/gles3/effects/copy_effects.h" + +using namespace GLES3; + +TextureStorage *TextureStorage::singleton = nullptr; + +TextureStorage *TextureStorage::get_singleton() { + return singleton; +} + +static const GLenum _cube_side_enum[6] = { + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, +}; + +TextureStorage::TextureStorage() { + singleton = this; + + system_fbo = 0; + + { //create default textures + { // White Textures + + Ref<Image> image; + image.instantiate(); + image->create(4, 4, true, Image::FORMAT_RGBA8); + image->fill(Color(1, 1, 1, 1)); + image->generate_mipmaps(); + + default_gl_textures[DEFAULT_GL_TEXTURE_WHITE] = texture_allocate(); + texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_WHITE], image); + + Vector<Ref<Image>> images; + images.push_back(image); + + default_gl_textures[DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE] = texture_allocate(); + texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE], images, RS::TEXTURE_LAYERED_2D_ARRAY); + + for (int i = 0; i < 3; i++) { + images.push_back(image); + } + + default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE] = texture_allocate(); + texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE], image->get_format(), 4, 4, 4, false, images); + + for (int i = 0; i < 2; i++) { + images.push_back(image); + } + + default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_WHITE] = texture_allocate(); + texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_WHITE], images, RS::TEXTURE_LAYERED_CUBEMAP); + } + + { // black + Ref<Image> image; + image.instantiate(); + image->create(4, 4, true, Image::FORMAT_RGBA8); + image->fill(Color(0, 0, 0, 1)); + image->generate_mipmaps(); + + default_gl_textures[DEFAULT_GL_TEXTURE_BLACK] = texture_allocate(); + texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK], image); + + Vector<Ref<Image>> images; + + for (int i = 0; i < 4; i++) { + images.push_back(image); + } + + default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK] = texture_allocate(); + texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK], image->get_format(), 4, 4, 4, false, images); + + for (int i = 0; i < 2; i++) { + images.push_back(image); + } + default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_BLACK] = texture_allocate(); + texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_BLACK], images, RS::TEXTURE_LAYERED_CUBEMAP); + } + + { // transparent black + Ref<Image> image; + image.instantiate(); + image->create(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); + image->fill(Color(0.5, 0.5, 1, 1)); + image->generate_mipmaps(); + + default_gl_textures[DEFAULT_GL_TEXTURE_NORMAL] = texture_allocate(); + texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_NORMAL], image); + } + + { + Ref<Image> image; + image.instantiate(); + image->create(4, 4, true, Image::FORMAT_RGBA8); + image->fill(Color(1.0, 0.5, 1, 1)); + image->generate_mipmaps(); + + default_gl_textures[DEFAULT_GL_TEXTURE_ANISO] = texture_allocate(); + texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_ANISO], image); + } + + { + unsigned char 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] = 0; + } + + default_gl_textures[DEFAULT_GL_TEXTURE_2D_UINT] = texture_allocate(); + Texture texture; + texture.width = 4; + texture.height = 4; + texture.format = Image::FORMAT_RGBA8; + texture.type = Texture::TYPE_2D; + texture.target = GL_TEXTURE_2D; + texture.active = true; + glGenTextures(1, &texture.tex_id); + texture_owner.initialize_rid(default_gl_textures[DEFAULT_GL_TEXTURE_2D_UINT], texture); + + glBindTexture(GL_TEXTURE_2D, texture.tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 4, 4, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, pixel_data); + texture.gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + } + { + uint16_t pixel_data[4 * 4]; + for (int i = 0; i < 16; i++) { + pixel_data[i] = Math::make_half_float(1.0f); + } + + default_gl_textures[DEFAULT_GL_TEXTURE_DEPTH] = texture_allocate(); + Texture texture; + texture.width = 4; + texture.height = 4; + texture.format = Image::FORMAT_RGBA8; + texture.type = Texture::TYPE_2D; + texture.target = GL_TEXTURE_2D; + texture.active = true; + glGenTextures(1, &texture.tex_id); + texture_owner.initialize_rid(default_gl_textures[DEFAULT_GL_TEXTURE_DEPTH], texture); + + glBindTexture(GL_TEXTURE_2D, texture.tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 4, 4, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, pixel_data); + texture.gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + +#ifdef GLES_OVER_GL + glEnable(GL_PROGRAM_POINT_SIZE); +#endif +} + +TextureStorage::~TextureStorage() { + singleton = nullptr; + for (int i = 0; i < DEFAULT_GL_TEXTURE_MAX; i++) { + texture_free(default_gl_textures[i]); + } +} + +//TODO, move back to storage +bool TextureStorage::can_create_resources_async() const { + return false; +} + +/* Canvas Texture API */ + +RID TextureStorage::canvas_texture_allocate() { + return canvas_texture_owner.allocate_rid(); +} + +void TextureStorage::canvas_texture_initialize(RID p_rid) { + canvas_texture_owner.initialize_rid(p_rid); +} + +void TextureStorage::canvas_texture_free(RID p_rid) { + canvas_texture_owner.free(p_rid); +} + +void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + switch (p_channel) { + case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: { + ct->diffuse = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_NORMAL: { + ct->normal_map = p_texture; + } break; + case RS::CANVAS_TEXTURE_CHANNEL_SPECULAR: { + ct->specular = p_texture; + } break; + } +} + +void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->specular_color.r = p_specular_color.r; + ct->specular_color.g = p_specular_color.g; + ct->specular_color.b = p_specular_color.b; + ct->specular_color.a = p_shininess; +} + +void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_filter = p_filter; +} + +void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) { + CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture); + ct->texture_repeat = p_repeat; +} + +/* CANVAS SHADOW */ + +RID TextureStorage::canvas_light_shadow_buffer_create(int p_width) { + Config *config = Config::get_singleton(); + 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); +} + +/* Texture API */ + +Ref<Image> TextureStorage::_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 { + Config *config = Config::get_singleton(); + r_gl_format = 0; + Ref<Image> image = p_image; + r_compressed = false; + r_real_format = p_format; + + bool need_decompress = false; + + switch (p_format) { + case Image::FORMAT_L8: { +#ifdef GLES_OVER_GL + r_gl_internal_format = GL_R8; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; +#else + r_gl_internal_format = GL_LUMINANCE; + r_gl_format = GL_LUMINANCE; + r_gl_type = GL_UNSIGNED_BYTE; +#endif + } break; + case Image::FORMAT_LA8: { +#ifdef GLES_OVER_GL + r_gl_internal_format = GL_RG8; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; +#else + r_gl_internal_format = GL_LUMINANCE_ALPHA; + r_gl_format = GL_LUMINANCE_ALPHA; + r_gl_type = GL_UNSIGNED_BYTE; +#endif + } break; + case Image::FORMAT_R8: { + r_gl_internal_format = GL_R8; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; + + } break; + case Image::FORMAT_RG8: { + r_gl_internal_format = GL_RG8; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; + + } break; + case Image::FORMAT_RGB8: { + r_gl_internal_format = GL_RGB8; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + + } break; + case Image::FORMAT_RGBA8: { + r_gl_format = GL_RGBA; + r_gl_internal_format = GL_RGBA8; + r_gl_type = GL_UNSIGNED_BYTE; + + } break; + case Image::FORMAT_RGBA4444: { + r_gl_internal_format = GL_RGBA4; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_SHORT_4_4_4_4; + + } break; + case Image::FORMAT_RF: { + r_gl_internal_format = GL_R32F; + r_gl_format = GL_RED; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGF: { + r_gl_internal_format = GL_RG32F; + r_gl_format = GL_RG; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGBF: { + r_gl_internal_format = GL_RGB32F; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RGBAF: { + r_gl_internal_format = GL_RGBA32F; + r_gl_format = GL_RGBA; + r_gl_type = GL_FLOAT; + + } break; + case Image::FORMAT_RH: { + r_gl_internal_format = GL_R16F; + r_gl_format = GL_RED; + r_gl_type = GL_HALF_FLOAT; + } break; + case Image::FORMAT_RGH: { + r_gl_internal_format = GL_RG16F; + r_gl_format = GL_RG; + r_gl_type = GL_HALF_FLOAT; + + } break; + case Image::FORMAT_RGBH: { + r_gl_internal_format = GL_RGB16F; + r_gl_format = GL_RGB; + r_gl_type = GL_HALF_FLOAT; + + } break; + case Image::FORMAT_RGBAH: { + r_gl_internal_format = GL_RGBA16F; + r_gl_format = GL_RGBA; + r_gl_type = GL_HALF_FLOAT; + + } break; + case Image::FORMAT_RGBE9995: { + r_gl_internal_format = GL_RGB9_E5; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_INT_5_9_9_9_REV; + + } break; + case Image::FORMAT_DXT1: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_DXT3: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_DXT5: { + if (config->s3tc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_RGTC_R: { + if (config->rgtc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RED_RGTC1_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_RGTC_RG: { + if (config->rgtc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBA: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA_BPTC_UNORM; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBF: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_BPTC_RGBFU: { + if (config->bptc_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT; + r_gl_format = GL_RGB; + r_gl_type = GL_FLOAT; + r_compressed = true; + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_R11: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_R11_EAC; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_R11S: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_SIGNED_R11_EAC; + r_gl_format = GL_RED; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RG11: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RG11_EAC; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RG11S: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_SIGNED_RG11_EAC; + r_gl_format = GL_RG; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC: + case Image::FORMAT_ETC2_RGB8: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB8_ETC2; + r_gl_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RGBA8: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGBA8_ETC2_EAC; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + case Image::FORMAT_ETC2_RGB8A1: { + if (config->etc2_supported) { + r_gl_internal_format = _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + r_gl_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_compressed = true; + + } else { + need_decompress = true; + } + } break; + default: { + ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer"); + } + } + + if (need_decompress || p_force_decompress) { + if (!image.is_null()) { + image = image->duplicate(); + image->decompress(); + ERR_FAIL_COND_V(image->is_compressed(), image); + switch (image->get_format()) { + case Image::FORMAT_RGB8: { + r_gl_format = GL_RGB; + r_gl_internal_format = GL_RGB; + r_gl_type = GL_UNSIGNED_BYTE; + r_real_format = Image::FORMAT_RGB8; + r_compressed = false; + } break; + case Image::FORMAT_RGBA8: { + r_gl_format = GL_RGBA; + r_gl_internal_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_real_format = Image::FORMAT_RGBA8; + r_compressed = false; + } break; + default: { + image->convert(Image::FORMAT_RGBA8); + r_gl_format = GL_RGBA; + r_gl_internal_format = GL_RGBA; + r_gl_type = GL_UNSIGNED_BYTE; + r_real_format = Image::FORMAT_RGBA8; + r_compressed = false; + + } break; + } + } + + return image; + } + + return p_image; +} + +RID TextureStorage::texture_allocate() { + return texture_owner.allocate_rid(); +} + +void TextureStorage::texture_free(RID p_texture) { + Texture *t = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!t); + ERR_FAIL_COND(t->is_render_target); + + if (t->canvas_texture) { + memdelete(t->canvas_texture); + } + + if (t->tex_id != 0) { + glDeleteTextures(1, &t->tex_id); + t->tex_id = 0; + } + + if (t->is_proxy && t->proxy_to.is_valid()) { + Texture *proxy_to = texture_owner.get_or_null(t->proxy_to); + if (proxy_to) { + proxy_to->proxies.erase(p_texture); + } + } + + //decal_atlas_remove_texture(p_texture); + + for (int i = 0; i < t->proxies.size(); i++) { + Texture *p = texture_owner.get_or_null(t->proxies[i]); + ERR_CONTINUE(!p); + p->proxy_to = RID(); + p->tex_id = 0; + } + + texture_owner.free(p_texture); +} + +void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) { + Texture texture; + texture.width = p_image->get_width(); + texture.height = p_image->get_height(); + texture.alloc_width = texture.width; + texture.alloc_height = texture.height; + texture.mipmaps = p_image->get_mipmap_count(); + texture.format = p_image->get_format(); + texture.type = Texture::TYPE_2D; + texture.target = GL_TEXTURE_2D; + _get_gl_image_and_format(Ref<Image>(), texture.format, texture.real_format, texture.gl_format_cache, texture.gl_internal_format_cache, texture.gl_type_cache, texture.compressed, false); + //texture.total_data_size = p_image->get_image_data_size(); // verify that this returns size in bytes + texture.active = true; + glGenTextures(1, &texture.tex_id); + texture_owner.initialize_rid(p_texture, texture); + texture_set_data(p_texture, p_image); +} + +void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) { + texture_owner.initialize_rid(p_texture, Texture()); +} + +void TextureStorage::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) { + texture_owner.initialize_rid(p_texture, Texture()); +} + +// Called internally when texture_proxy_create(p_base) is called. +// Note: p_base is the root and p_texture is the proxy. +void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { + Texture *texture = texture_owner.get_or_null(p_base); + ERR_FAIL_COND(!texture); + Texture proxy_tex; + proxy_tex.copy_from(*texture); + proxy_tex.proxy_to = p_base; + proxy_tex.is_render_target = false; + proxy_tex.is_proxy = true; + proxy_tex.proxies.clear(); + texture->proxies.push_back(p_texture); + texture_owner.initialize_rid(p_texture, proxy_tex); +} + +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); +#ifdef TOOLS_ENABLED + Texture *tex = texture_owner.get_or_null(p_texture); + + tex->image_cache_2d.unref(); +#endif +} + +void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { +} + +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); + image->fill(Color(1, 0, 1, 1)); + + texture_2d_initialize(p_texture, image); +} + +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); + image->fill(Color(1, 0, 1, 1)); + + Vector<Ref<Image>> images; + if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) { + images.push_back(image); + } else { + //cube + for (int i = 0; i < 6; i++) { + images.push_back(image); + } + } + + texture_2d_layered_initialize(p_texture, images, p_layered_type); +} + +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); + image->fill(Color(1, 0, 1, 1)); + + Vector<Ref<Image>> images; + //cube + for (int i = 0; i < 4; i++) { + images.push_back(image); + } + + texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images); +} + +Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, Ref<Image>()); + +#ifdef TOOLS_ENABLED + if (texture->image_cache_2d.is_valid() && !texture->is_render_target) { + return texture->image_cache_2d; + } +#endif + +#ifdef GLES_OVER_GL + // OpenGL 3.3 supports glGetTexImage which is faster and simpler than glReadPixels. + Vector<uint8_t> data; + + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + uint8_t *w = data.ptrw(); + + glActiveTexture(GL_TEXTURE0); + + glBindTexture(texture->target, texture->tex_id); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + for (int i = 0; i < texture->mipmaps; i++) { + int ofs = Image::get_image_mipmap_offset(texture->alloc_width, texture->alloc_height, texture->real_format, i); + + if (texture->compressed) { + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glGetCompressedTexImage(texture->target, i, &w[ofs]); + + } else { + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + glGetTexImage(texture->target, i, texture->gl_format_cache, texture->gl_type_cache, &w[ofs]); + } + } + + 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); + 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; +#endif + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() && !texture->is_render_target) { + texture->image_cache_2d = image; + } +#endif + + return image; +} + +void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) { + Texture *tex_to = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!tex_to); + ERR_FAIL_COND(tex_to->is_proxy); //can't replace proxy + Texture *tex_from = texture_owner.get_or_null(p_by_texture); + ERR_FAIL_COND(!tex_from); + ERR_FAIL_COND(tex_from->is_proxy); //can't replace proxy + + if (tex_to == tex_from) { + return; + } + + if (tex_to->canvas_texture) { + memdelete(tex_to->canvas_texture); + tex_to->canvas_texture = nullptr; + } + + if (tex_to->tex_id) { + glDeleteTextures(1, &tex_to->tex_id); + tex_to->tex_id = 0; + } + + Vector<RID> proxies_to_update = tex_to->proxies; + Vector<RID> proxies_to_redirect = tex_from->proxies; + + tex_to->copy_from(*tex_from); + + tex_to->proxies = proxies_to_update; //restore proxies, so they can be updated + + if (tex_to->canvas_texture) { + tex_to->canvas_texture->diffuse = p_texture; //update + } + + for (int i = 0; i < proxies_to_update.size(); i++) { + texture_proxy_update(proxies_to_update[i], p_texture); + } + for (int i = 0; i < proxies_to_redirect.size(); i++) { + texture_proxy_update(proxies_to_redirect[i], p_texture); + } + //delete last, so proxies can be updated + texture_owner.free(p_by_texture); + + //decal_atlas_mark_dirty_on_texture(p_texture); +} + +void TextureStorage::texture_set_size_override(RID p_texture, int p_width, int p_height) { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND(!texture); + ERR_FAIL_COND(texture->is_render_target); + + ERR_FAIL_COND(p_width <= 0 || p_width > 16384); + ERR_FAIL_COND(p_height <= 0 || p_height > 16384); + //real texture size is in alloc width and height + texture->width = p_width; + texture->height = p_height; +} + +void TextureStorage::texture_set_path(RID p_texture, const String &p_path) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->path = p_path; +} + +String TextureStorage::texture_get_path(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, ""); + + return texture->path; +} + +void TextureStorage::texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_3d_callback = p_callback; + texture->detect_3d_callback_ud = p_userdata; +} + +void TextureStorage::texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { +} + +void TextureStorage::texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_normal_callback = p_callback; + texture->detect_normal_callback_ud = p_userdata; +} + +void TextureStorage::texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->detect_roughness_callback = p_callback; + texture->detect_roughness_callback_ud = p_userdata; +} + +void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { + List<RID> textures; + texture_owner.get_owned_list(&textures); + + for (List<RID>::Element *E = textures.front(); E; E = E->next()) { + Texture *t = texture_owner.get_or_null(E->get()); + if (!t) { + continue; + } + RS::TextureInfo tinfo; + tinfo.path = t->path; + tinfo.format = t->format; + tinfo.width = t->alloc_width; + tinfo.height = t->alloc_height; + tinfo.depth = 0; + tinfo.bytes = t->total_data_size; + r_info->push_back(tinfo); + } +} + +void TextureStorage::texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND(!texture); + + texture->redraw_if_visible = p_enable; +} + +Size2 TextureStorage::texture_size_with_proxy(RID p_texture) { + const Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_COND_V(!texture, Size2()); + if (texture->is_proxy) { + const Texture *proxy = texture_owner.get_or_null(texture->proxy_to); + return Size2(proxy->width, proxy->height); + } else { + return Size2(texture->width, texture->height); + } +} + +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); + + ERR_FAIL_COND(!texture); + if (texture->target == GL_TEXTURE_3D) { + // Target is set to a 3D texture or array texture, exit early to avoid spamming errors + return; + } + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->is_render_target); + ERR_FAIL_COND(p_image.is_null()); + ERR_FAIL_COND(texture->format != p_image->get_format()); + + ERR_FAIL_COND(!p_image->get_width()); + ERR_FAIL_COND(!p_image->get_height()); + + // ERR_FAIL_COND(texture->type == RS::TEXTURE_TYPE_EXTERNAL); + + GLenum type; + GLenum format; + GLenum internal_format; + bool compressed = false; + + // print_line("texture_set_data width " + itos (p_image->get_width()) + " height " + itos(p_image->get_height())); + + Image::Format real_format; + Ref<Image> img = _get_gl_image_and_format(p_image, p_image->get_format(), real_format, format, internal_format, type, compressed, texture->resize_to_po2); + ERR_FAIL_COND(img.is_null()); + if (texture->resize_to_po2) { + if (p_image->is_compressed()) { + ERR_PRINT("Texture '" + texture->path + "' is required to be a power of 2 because it uses either mipmaps or repeat, so it was decompressed. This will hurt performance and memory usage."); + } + + if (img == p_image) { + img = img->duplicate(); + } + img->resize_to_po2(false); + } + + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; + + Vector<uint8_t> read = img->get_data(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + + // set filtering and repeat state to default + texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + texture->gl_set_repeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + + //set swizle for older format compatibility +#ifdef GLES_OVER_GL + switch (texture->format) { + case Image::FORMAT_L8: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + + } break; + case Image::FORMAT_LA8: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); + } break; + default: { + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + + } break; + } +#endif + + int mipmaps = img->has_mipmaps() ? img->get_mipmap_count() + 1 : 1; + + int w = img->get_width(); + int h = img->get_height(); + + int tsize = 0; + + for (int i = 0; i < mipmaps; i++) { + int size, ofs; + img->get_mipmap_offset_and_size(i, ofs, size); + + if (compressed) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int bw = w; + int bh = h; + + 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]); + } + + tsize += size; + + w = MAX(1, w >> 1); + h = MAX(1, h >> 1); + } + + // info.texture_mem -= texture->total_data_size; // TODO make this work again!! + texture->total_data_size = tsize; + // info.texture_mem += texture->total_data_size; // TODO make this work again!! + + // printf("texture: %i x %i - size: %i - total: %i\n", texture->width, texture->height, tsize, info.texture_mem); + + texture->stored_cube_sides |= (1 << p_layer); + + texture->mipmaps = mipmaps; +} + +void TextureStorage::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) { + ERR_PRINT("Not implemented yet, sorry :("); +} + +Image::Format TextureStorage::texture_get_format(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, Image::FORMAT_L8); + + return texture->format; +} + +uint32_t TextureStorage::texture_get_texid(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->tex_id; +} + +uint32_t TextureStorage::texture_get_width(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->width; +} + +uint32_t TextureStorage::texture_get_height(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->height; +} + +uint32_t TextureStorage::texture_get_depth(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND_V(!texture, 0); + + return texture->depth; +} + +void TextureStorage::texture_bind(RID p_texture, uint32_t p_texture_no) { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_COND(!texture); + + glActiveTexture(GL_TEXTURE0 + p_texture_no); + glBindTexture(texture->target, texture->tex_id); +} + +RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resolution) const { + return RID(); +} + +/* DECAL API */ + +RID TextureStorage::decal_allocate() { + return RID(); +} + +void TextureStorage::decal_initialize(RID p_rid) { +} + +void TextureStorage::decal_set_extents(RID p_decal, const Vector3 &p_extents) { +} + +void TextureStorage::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) { +} + +void TextureStorage::decal_set_emission_energy(RID p_decal, float p_energy) { +} + +void TextureStorage::decal_set_albedo_mix(RID p_decal, float p_mix) { +} + +void TextureStorage::decal_set_modulate(RID p_decal, const Color &p_modulate) { +} + +void TextureStorage::decal_set_cull_mask(RID p_decal, uint32_t p_layers) { +} + +void TextureStorage::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) { +} + +void TextureStorage::decal_set_fade(RID p_decal, float p_above, float p_below) { +} + +void TextureStorage::decal_set_normal_fade(RID p_decal, float p_fade) { +} + +AABB TextureStorage::decal_get_aabb(RID p_decal) const { + return AABB(); +} + +/* RENDER TARGET API */ + +GLuint TextureStorage::system_fbo = 0; + +void TextureStorage::_update_render_target(RenderTarget *rt) { + // do not allocate a render target with no size + if (rt->size.x <= 0 || rt->size.y <= 0) { + return; + } + + // do not allocate a render target that is attached to the screen + if (rt->direct_to_screen) { + rt->fbo = system_fbo; + return; + } + + 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->image_format = Image::FORMAT_RGBA8; + + glDisable(GL_SCISSOR_TEST); + glColorMask(1, 1, 1, 1); + glDepthMask(GL_FALSE); + + { + /* Front FBO */ + + Texture *texture = get_texture(rt->texture); + ERR_FAIL_COND(!texture); + + // framebuffer + glGenFramebuffers(1, &rt->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo); + + // color + glGenTextures(1, &rt->color); + glBindTexture(GL_TEXTURE_2D, rt->color); + + glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, 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, rt->color, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + glDeleteFramebuffers(1, &rt->fbo); + glDeleteTextures(1, &rt->color); + rt->fbo = 0; + rt->size.x = 0; + rt->size.y = 0; + rt->color = 0; + 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; + } + + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); +} + +void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) { + ERR_FAIL_COND_MSG(rt->backbuffer_fbo != 0, "Cannot allocate RenderTarget backbuffer: already initialized."); + ERR_FAIL_COND(rt->direct_to_screen); + // Allocate mipmap chains for full screen blur + // Limit mipmaps so smallest is 32x32 to avoid unnecessary framebuffer switches + int count = MAX(1, Image::get_image_required_mipmaps(rt->size.x, rt->size.y, Image::FORMAT_RGBA8) - 4); + if (rt->size.x > 40 && rt->size.y > 40) { + GLsizei width = rt->size.x; + GLsizei height = rt->size.y; + + rt->mipmap_count = count; + + glGenTextures(1, &rt->backbuffer); + glBindTexture(GL_TEXTURE_2D, rt->backbuffer); + + for (int l = 0; l < count; l++) { + glTexImage2D(GL_TEXTURE_2D, l, rt->color_internal_format, width, height, 0, rt->color_format, rt->color_type, nullptr); + width = MAX(1, (width / 2)); + height = MAX(1, (height / 2)); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1); + + glGenFramebuffers(1, &rt->backbuffer_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT_ONCE("Cannot allocate mipmaps for canvas screen blur. Status: " + get_framebuffer_error(status)); + glBindFramebuffer(GL_FRAMEBUFFER, system_fbo); + return; + } + + // Initialize all levels to opaque Magenta. + for (int j = 0; j < count; j++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, j); + glClearColor(1.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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); + } +} + +void TextureStorage::_clear_render_target(RenderTarget *rt) { + // there is nothing to clear when DIRECT_TO_SCREEN is used + if (rt->direct_to_screen) { + return; + } + + if (rt->fbo) { + glDeleteFramebuffers(1, &rt->fbo); + glDeleteTextures(1, &rt->color); + rt->fbo = 0; + 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); + + rt->external.fbo = 0; + } + */ + + 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->backbuffer_fbo != 0) { + glDeleteFramebuffers(1, &rt->backbuffer_fbo); + glDeleteTextures(1, &rt->backbuffer); + rt->backbuffer = 0; + rt->backbuffer_fbo = 0; + } +} + +RID TextureStorage::render_target_create() { + RenderTarget render_target; + //render_target.was_used = false; + render_target.clear_requested = false; + + Texture t; + t.active = true; + t.render_target = &render_target; + t.is_render_target = true; + + render_target.texture = texture_owner.make_rid(t); + _update_render_target(&render_target); + return render_target_owner.make_rid(render_target); +} + +void TextureStorage::render_target_free(RID p_rid) { + RenderTarget *rt = render_target_owner.get_or_null(p_rid); + _clear_render_target(rt); + + Texture *t = get_texture(rt->texture); + if (t) { + t->is_render_target = false; + texture_free(rt->texture); + //memdelete(t); + } + render_target_owner.free(p_rid); +} + +void TextureStorage::render_target_set_position(RID p_render_target, int p_x, int p_y) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->position = Point2i(p_x, p_y); +} + +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) { + return; + } + + _clear_render_target(rt); + + rt->size = Size2i(p_width, p_height); + + _update_render_target(rt); +} + +// TODO: convert to Size2i internally +Size2i TextureStorage::render_target_get_size(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Size2()); + + return rt->size; +} + +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->external.fbo == 0) { + return rt->texture; + } else { + return rt->external.texture; + } +} + +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_texture_id == 0) { + if (rt->external.fbo != 0) { + // free this + glDeleteFramebuffers(1, &rt->external.fbo); + + // and this + if (rt->external.depth != 0) { + glDeleteRenderbuffers(1, &rt->external.depth); + } + + // 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); + + } else { + // bind our frame buffer + glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); + + // find our texture + t = get_texture(rt->external.texture); + } + + // set our texture + t->tex_id = p_texture_id; + rt->external.color = p_texture_id; + + // 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; + + // 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); + } + + // check status and unbind + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); + + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("framebuffer fail, status: " + get_framebuffer_error(status)); + } + + ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); + } +} + +void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->is_transparent = p_transparent; + + _clear_render_target(rt); + _update_render_target(rt); +} + +void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (p_direct_to_screen == rt->direct_to_screen) { + return; + } + // When setting DIRECT_TO_SCREEN, you need to clear before the value is set, but allocate after as + // 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; + _update_render_target(rt); +} + +bool TextureStorage::render_target_was_used(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + + return rt->used_in_frame; +} + +void TextureStorage::render_target_clear_used(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + rt->used_in_frame = false; +} + +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); + rt->clear_requested = true; + rt->clear_color = p_clear_color; +} + +bool TextureStorage::render_target_is_clear_requested(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, false); + return rt->clear_requested; +} +Color TextureStorage::render_target_get_clear_request_color(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND_V(!rt, Color()); + return rt->clear_color; +} + +void TextureStorage::render_target_disable_clear_request(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + rt->clear_requested = false; +} + +void TextureStorage::render_target_do_clear_request(RID p_render_target) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + if (!rt->clear_requested) { + return; + } + + glClearBufferfv(GL_COLOR, 0, rt->clear_color.components); + rt->clear_requested = false; +} + +void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) { +} + +Rect2i TextureStorage::render_target_get_sdf_rect(RID p_render_target) const { + return Rect2i(); +} + +void TextureStorage::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) { +} + +void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->direct_to_screen); + + if (rt->backbuffer_fbo == 0) { + _create_render_target_backbuffer(rt); + } + + Rect2i region; + if (p_region == Rect2i()) { + region.size = rt->size; + } else { + region = Rect2i(Size2i(), rt->size).intersection(p_region); + if (region.size == Size2i()) { + return; //nothing to do + } + } + + glDisable(GL_BLEND); + //single texture copy for backbuffer + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, rt->color); + GLES3::CopyEffects::get_singleton()->copy_screen(); + + if (p_gen_mipmaps) { + GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region); + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + } + + glEnable(GL_BLEND); // 2D almost always uses blend. +} + +void TextureStorage::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + ERR_FAIL_COND(rt->direct_to_screen); + + if (rt->backbuffer_fbo == 0) { + _create_render_target_backbuffer(rt); + } + + Rect2i region; + if (p_region == Rect2i()) { + // Just do a full screen clear; + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + glClearColor(p_color.r, p_color.g, p_color.b, p_color.a); + glClear(GL_COLOR_BUFFER_BIT); + } else { + region = Rect2i(Size2i(), rt->size).intersection(p_region); + if (region.size == Size2i()) { + return; //nothing to do + } + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); + GLES3::CopyEffects::get_singleton()->set_color(p_color, region); + } +} + +void TextureStorage::render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_COND(!rt); + + if (rt->backbuffer_fbo == 0) { + _create_render_target_backbuffer(rt); + } + + Rect2i region; + if (p_region == Rect2i()) { + region.size = rt->size; + } else { + region = Rect2i(Size2i(), rt->size).intersection(p_region); + if (region.size == Size2i()) { + return; //nothing to do + } + } + + GLES3::CopyEffects::get_singleton()->bilinear_blur(rt->backbuffer, rt->mipmap_count, region); + glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h new file mode 100644 index 0000000000..71f713bc9f --- /dev/null +++ b/drivers/gles3/storage/texture_storage.h @@ -0,0 +1,583 @@ +/*************************************************************************/ +/* texture_storage.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 TEXTURE_STORAGE_GLES3_H +#define TEXTURE_STORAGE_GLES3_H + +#ifdef GLES3_ENABLED + +#include "config.h" +#include "core/os/os.h" +#include "core/templates/rid_owner.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering/storage/texture_storage.h" + +// This must come first to avoid windows.h mess +#include "platform_config.h" +#ifndef OPENGL_INCLUDE_H +#include <GLES3/gl3.h> +#else +#include OPENGL_INCLUDE_H +#endif + +namespace GLES3 { + +#define _GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +#define _EXT_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#define _EXT_ETC1_RGB8_OES 0x8D64 + +#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F + +#define _EXT_COMPRESSED_R11_EAC 0x9270 +#define _EXT_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define _EXT_COMPRESSED_RG11_EAC 0x9272 +#define _EXT_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define _EXT_COMPRESSED_RGB8_ETC2 0x9274 +#define _EXT_COMPRESSED_SRGB8_ETC2 0x9275 +#define _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define _EXT_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 + +#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 + +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, + DEFAULT_GL_TEXTURE_CUBEMAP_BLACK, + //DEFAULT_GL_TEXTURE_CUBEMAP_ARRAY_BLACK, // Cubemap Arrays not supported in GL 3.3 or GL ES 3.0 + DEFAULT_GL_TEXTURE_CUBEMAP_WHITE, + DEFAULT_GL_TEXTURE_3D_WHITE, + DEFAULT_GL_TEXTURE_3D_BLACK, + DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE, + DEFAULT_GL_TEXTURE_2D_UINT, + DEFAULT_GL_TEXTURE_MAX +}; + +struct CanvasTexture { + RID diffuse; + RID normal_map; + RID specular; + Color specular_color = Color(1, 1, 1, 1); + float shininess = 1.0; + + 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; +}; + +/* CANVAS SHADOW */ + +struct CanvasLightShadow { + RID self; + int size; + int height; + GLuint fbo; + GLuint depth; + GLuint distance; //for older devices +}; + +struct RenderTarget; + +struct Texture { + RID self; + + bool is_proxy = false; + bool is_render_target = false; + + RID proxy_to = RID(); + Vector<RID> proxies; + + String path; + int width = 0; + int height = 0; + int depth = 0; + int mipmaps = 1; + int layers = 1; + int alloc_width = 0; + int alloc_height = 0; + Image::Format format = Image::FORMAT_R8; + Image::Format real_format = Image::FORMAT_R8; + + enum Type { + TYPE_2D, + TYPE_LAYERED, + TYPE_3D + }; + + Type type; + RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY; + + GLenum target = GL_TEXTURE_2D; + GLenum gl_format_cache = 0; + GLenum gl_internal_format_cache = 0; + GLenum gl_type_cache = 0; + + int total_data_size = 0; + + bool compressed = false; + + bool resize_to_po2 = false; + + bool active = false; + GLuint tex_id = 0; + + uint16_t stored_cube_sides = 0; + + RenderTarget *render_target = nullptr; + + Ref<Image> image_cache_2d; + + bool redraw_if_visible = false; + + RS::TextureDetectCallback detect_3d_callback = nullptr; + void *detect_3d_callback_ud = nullptr; + + RS::TextureDetectCallback detect_normal_callback = nullptr; + void *detect_normal_callback_ud = nullptr; + + RS::TextureDetectRoughnessCallback detect_roughness_callback = nullptr; + void *detect_roughness_callback_ud = nullptr; + + CanvasTexture *canvas_texture = nullptr; + + void copy_from(const Texture &o) { + proxy_to = o.proxy_to; + is_proxy = o.is_proxy; + width = o.width; + height = o.height; + alloc_width = o.alloc_width; + alloc_height = o.alloc_height; + format = o.format; + type = o.type; + layered_type = o.layered_type; + target = o.target; + total_data_size = o.total_data_size; + compressed = o.compressed; + mipmaps = o.mipmaps; + resize_to_po2 = o.resize_to_po2; + active = o.active; + tex_id = o.tex_id; + stored_cube_sides = o.stored_cube_sides; + render_target = o.render_target; + is_render_target = o.is_render_target; + redraw_if_visible = o.redraw_if_visible; + detect_3d_callback = o.detect_3d_callback; + detect_3d_callback_ud = o.detect_3d_callback_ud; + detect_normal_callback = o.detect_normal_callback; + detect_normal_callback_ud = o.detect_normal_callback_ud; + detect_roughness_callback = o.detect_roughness_callback; + detect_roughness_callback_ud = o.detect_roughness_callback_ud; + } + + // texture state + void gl_set_filter(RS::CanvasItemTextureFilter p_filter) { + if (p_filter == state_filter) { + return; + } + Config *config = Config::get_singleton(); + state_filter = p_filter; + GLenum pmin = GL_NEAREST; // param min + GLenum pmag = GL_NEAREST; // param mag + GLint max_lod = 1000; + bool use_anisotropy = false; + switch (state_filter) { + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: { + pmin = GL_NEAREST; + pmag = GL_NEAREST; + max_lod = 0; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: { + pmin = GL_LINEAR; + pmag = GL_LINEAR; + max_lod = 0; + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { + use_anisotropy = true; + }; + [[fallthrough]]; + case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { + pmag = GL_NEAREST; + if (mipmaps <= 1) { + pmin = GL_NEAREST; + max_lod = 0; + } else if (config->use_nearest_mip_filter) { + pmin = GL_NEAREST_MIPMAP_NEAREST; + } else { + pmin = GL_NEAREST_MIPMAP_LINEAR; + } + } break; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: { + use_anisotropy = true; + }; + [[fallthrough]]; + case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: { + pmag = GL_LINEAR; + if (mipmaps <= 1) { + pmin = GL_LINEAR; + max_lod = 0; + } else if (config->use_nearest_mip_filter) { + pmin = GL_LINEAR_MIPMAP_NEAREST; + } else { + pmin = GL_LINEAR_MIPMAP_LINEAR; + } + } break; + default: { + } break; + } + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, pmin); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, pmag); + glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_lod); + if (config->support_anisotropic_filter && use_anisotropy) { + glTexParameterf(target, _GL_TEXTURE_MAX_ANISOTROPY_EXT, config->anisotropic_level); + } + } + void gl_set_repeat(RS::CanvasItemTextureRepeat p_repeat) { + if (p_repeat == state_repeat) { + return; + } + state_repeat = p_repeat; + GLenum prep = GL_CLAMP_TO_EDGE; // parameter repeat + switch (state_repeat) { + case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: { + prep = GL_REPEAT; + } break; + case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: { + prep = GL_MIRRORED_REPEAT; + } break; + default: { + } break; + } + glTexParameteri(target, GL_TEXTURE_WRAP_T, prep); + glTexParameteri(target, GL_TEXTURE_WRAP_R, prep); + glTexParameteri(target, GL_TEXTURE_WRAP_S, prep); + } + +private: + RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; + RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; +}; + +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); + int mipmap_count = 1; + RID self; + GLuint fbo = 0; + GLuint color = 0; + GLuint backbuffer_fbo = 0; + GLuint backbuffer = 0; + + GLuint color_internal_format = GL_RGBA8; + GLuint color_format = GL_RGBA; + GLuint color_type = GL_UNSIGNED_BYTE; + Image::Format image_format = Image::FORMAT_RGBA8; + + bool is_transparent = false; + bool direct_to_screen = false; + + bool used_in_frame = false; + RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; + + RID texture; + + Color clear_color = Color(1, 1, 1, 1); + bool clear_requested = false; + + RenderTarget() { + } +}; + +class TextureStorage : public RendererTextureStorage { +private: + static TextureStorage *singleton; + + RID default_gl_textures[DEFAULT_GL_TEXTURE_MAX]; + + /* Canvas Texture API */ + + RID_Owner<CanvasTexture, true> canvas_texture_owner; + + /* CANVAS SHADOW */ + + RID_PtrOwner<CanvasLightShadow> canvas_light_shadow_owner; + + /* Texture API */ + + mutable RID_Owner<Texture> texture_owner; + + 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; + + /* Render Target API */ + + mutable RID_Owner<RenderTarget> render_target_owner; + + void _clear_render_target(RenderTarget *rt); + void _update_render_target(RenderTarget *rt); + void _create_render_target_backbuffer(RenderTarget *rt); + +public: + static TextureStorage *get_singleton(); + + TextureStorage(); + virtual ~TextureStorage(); + + _FORCE_INLINE_ RID texture_gl_get_default(DefaultGLTexture p_texture) { + return default_gl_textures[p_texture]; + } + + /* Canvas Texture API */ + + CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); }; + bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); }; + + virtual RID canvas_texture_allocate() override; + virtual void canvas_texture_initialize(RID p_rid) override; + virtual void canvas_texture_free(RID p_rid) override; + + virtual void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override; + virtual void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override; + + virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override; + virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override; + + /* CANVAS SHADOW */ + + RID canvas_light_shadow_buffer_create(int p_width); + + /* Texture API */ + + Texture *get_texture(RID p_rid) { + Texture *texture = texture_owner.get_or_null(p_rid); + if (texture && texture->is_proxy) { + return texture_owner.get_or_null(texture->proxy_to); + } + return texture; + }; + bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }; + + virtual bool can_create_resources_async() const override; + + RID texture_create(); + + virtual RID texture_allocate() override; + virtual void texture_free(RID p_rid) override; + + virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override; + virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override; + 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 + + 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; + + //these two APIs can be used together or in combination with the others. + virtual void texture_2d_placeholder_initialize(RID p_texture) override; + virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override; + virtual void texture_3d_placeholder_initialize(RID p_texture) override; + + virtual Ref<Image> texture_2d_get(RID p_texture) const override; + virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref<Image>(); }; + virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const override { return Vector<Ref<Image>>(); }; + + virtual void texture_replace(RID p_texture, RID p_by_texture) override; + virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override; + + virtual void texture_set_path(RID p_texture, const String &p_path) override; + virtual String texture_get_path(RID p_texture) const override; + + virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; + void texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata); + virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override; + virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override; + + virtual void texture_debug_usage(List<RS::TextureInfo> *r_info) override; + + virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override; + + virtual Size2 texture_size_with_proxy(RID p_proxy) 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; + void texture_set_sampler(RID p_texture, RS::CanvasItemTextureFilter p_filter, RS::CanvasItemTextureRepeat p_repeat); + Image::Format texture_get_format(RID p_texture) const; + uint32_t texture_get_texid(RID p_texture) const; + uint32_t texture_get_width(RID p_texture) const; + uint32_t texture_get_height(RID p_texture) const; + uint32_t texture_get_depth(RID p_texture) const; + void texture_bind(RID p_texture, uint32_t p_texture_no); + RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; + + /* DECAL API */ + + virtual RID decal_allocate() override; + virtual void decal_initialize(RID p_rid) override; + virtual void decal_free(RID p_rid) override{}; + + virtual void decal_set_extents(RID p_decal, const Vector3 &p_extents) override; + virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override; + virtual void decal_set_emission_energy(RID p_decal, float p_energy) override; + virtual void decal_set_albedo_mix(RID p_decal, float p_mix) override; + virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) override; + virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override; + virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override; + virtual void decal_set_fade(RID p_decal, float p_above, float p_below) override; + virtual void decal_set_normal_fade(RID p_decal, float p_fade) override; + + virtual AABB decal_get_aabb(RID p_decal) const override; + + 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 {} + + /* RENDER TARGET API */ + + static GLuint system_fbo; + + RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }; + bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }; + + 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 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 void render_target_set_transparent(RID p_render_target, bool p_is_transparent) 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; + void render_target_clear_used(RID p_render_target); + + // new + void render_target_set_as_unused(RID p_render_target) override { + render_target_clear_used(p_render_target); + } + + void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override; + bool render_target_is_clear_requested(RID p_render_target) override; + Color render_target_get_clear_request_color(RID p_render_target) override; + 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; + + 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 void render_target_set_vrs_texture(RID p_render_target, RID p_texture) 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); +}; + +inline String TextureStorage::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); +} + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#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..654104722b --- /dev/null +++ b/drivers/gles3/storage/utilities.cpp @@ -0,0 +1,353 @@ +/*************************************************************************/ +/* 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" + +using namespace GLES3; + +Utilities *Utilities::singleton = nullptr; + +Utilities::Utilities() { + singleton = this; +} + +Utilities::~Utilities() { + singleton = nullptr; +} + +Vector<uint8_t> Utilities::buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size) { + Vector<uint8_t> 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; + } + 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 { + 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::render_info_begin_capture() { +// info.snap = info.render; +//} + +//void Utilities::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 Utilities::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 Utilities::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 +// } +//} + +/* 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(); +} + +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); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h new file mode 100644 index 0000000000..e054f2f816 --- /dev/null +++ b/drivers/gles3/storage/utilities.h @@ -0,0 +1,159 @@ +/*************************************************************************/ +/* 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 */ + + 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; + + virtual void capture_timestamps_begin() override {} + virtual void capture_timestamp(const String &p_name) override {} + virtual uint32_t get_captured_timestamps_count() const override { + return 0; + } + virtual uint64_t get_captured_timestamps_frame() const override { + return 0; + } + virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override { + return 0; + } + virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override { + return 0; + } + virtual String get_captured_timestamp_name(uint32_t p_index) const override { + return String(); + } + + // 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; + + /* 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; +}; + +} // namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // UTILITIES_GLES3_H diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 46e271f9c9..917bfec574 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -36,18 +36,16 @@ #include <string.h> -Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { +Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) { const uint64_t buffer_size = f->get_length(); Vector<uint8_t> file_buffer; Error err = file_buffer.resize(buffer_size); if (err) { - f->close(); return err; } { uint8_t *writer = file_buffer.ptrw(); f->get_buffer(writer, buffer_size); - f->close(); } const uint8_t *reader = file_buffer.ptr(); return PNGDriverCommon::png_to_image(reader, buffer_size, p_force_linear, p_image); diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index af3bcd5b66..9bcfb720d3 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, FileAccess *f, bool p_force_linear, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, 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.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 77390a5915..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 RES &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."); @@ -46,26 +46,22 @@ Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32 Error err = save_image(p_path, img); return err; -}; +} Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img) { Vector<uint8_t> buffer; Error err = PNGDriverCommon::image_to_png(p_img, buffer); ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG."); - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save PNG at path: '%s'.", p_path)); const uint8_t *reader = buffer.ptr(); file->store_buffer(reader, buffer.size()); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); return ERR_CANT_CREATE; } - file->close(); - memdelete(file); - return OK; } @@ -76,11 +72,11 @@ Vector<uint8_t> ResourceSaverPNG::save_image_to_buffer(const Ref<Image> &p_img) return buffer; } -bool ResourceSaverPNG::recognize(const RES &p_resource) const { +bool ResourceSaverPNG::recognize(const Ref<Resource> &p_resource) const { return (p_resource.is_valid() && p_resource->is_class("ImageTexture")); } -void ResourceSaverPNG::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceSaverPNG::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (Object::cast_to<ImageTexture>(*p_resource)) { p_extensions->push_back("png"); } @@ -89,4 +85,4 @@ void ResourceSaverPNG::get_recognized_extensions(const RES &p_resource, List<Str ResourceSaverPNG::ResourceSaverPNG() { Image::save_png_func = &save_image; Image::save_png_buffer_func = &save_image_to_buffer; -}; +} diff --git a/drivers/png/resource_saver_png.h b/drivers/png/resource_saver_png.h index f39e52c7ec..260a643a1b 100644 --- a/drivers/png/resource_saver_png.h +++ b/drivers/png/resource_saver_png.h @@ -39,9 +39,9 @@ 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 RES &p_resource, uint32_t p_flags = 0); - virtual bool recognize(const RES &p_resource) const; - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + 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; ResourceSaverPNG(); }; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index ef9bbc3483..a6c35b6837 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -41,7 +41,7 @@ #endif void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); switch (pa_context_get_state(c)) { case PA_CONTEXT_TERMINATED: @@ -65,7 +65,7 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { } void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -84,7 +84,7 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l } void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -104,7 +104,7 @@ void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_inf void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) { ERR_FAIL_COND_MSG(!i, "PulseAudio server info is null."); - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); ad->capture_default_device = i->default_source_name; ad->default_device = i->default_sink_name; @@ -379,7 +379,7 @@ float AudioDriverPulseAudio::get_latency() { } void AudioDriverPulseAudio::thread_func(void *p_udata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(p_udata); unsigned int write_ofs = 0; size_t avail_bytes = 0; uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec(); @@ -588,7 +588,7 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const { } void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { @@ -771,7 +771,7 @@ void AudioDriverPulseAudio::capture_set_device(const String &p_name) { } void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) { - AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata; + AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); // If eol is set to a positive number, you're at the end of the list if (eol > 0) { diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 1047e89e85..504ef9843a 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -55,9 +55,7 @@ void unregister_core_driver_types() { } void register_driver_types() { - NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_DRIVER); } void unregister_driver_types() { - NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_DRIVER); } 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 5fdfde4913..b8b72b8d30 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -33,6 +33,7 @@ #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) #include "core/os/memory.h" +#include "core/os/os.h" #include "core/string/print_string.h" #include "core/templates/list.h" @@ -49,10 +50,6 @@ #include <mntent.h> #endif -DirAccess *DirAccessUnix::create_fs() { - return memnew(DirAccessUnix); -} - Error DirAccessUnix::list_dir_begin() { list_dir_end(); //close any previous dir opening! @@ -138,9 +135,9 @@ uint64_t DirAccessUnix::get_modified_time(String p_file) { return flags.st_mtime; } else { ERR_FAIL_V(0); - }; + } return 0; -}; +} String DirAccessUnix::get_next() { if (!dir_stream) { @@ -216,10 +213,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 +237,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 +246,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 +256,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 +266,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(); @@ -320,11 +325,11 @@ Error DirAccessUnix::make_dir(String p_dir) { if (success) { return OK; - }; + } if (err == EEXIST) { return ERR_ALREADY_EXISTS; - }; + } return ERR_CANT_CREATE; } @@ -338,7 +343,7 @@ 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? } @@ -374,7 +379,7 @@ Error DirAccessUnix::change_dir(String p_dir) { return OK; } -String DirAccessUnix::get_current_dir(bool p_include_drive) { +String DirAccessUnix::get_current_dir(bool p_include_drive) const { String base = _get_root_path(); if (!base.is_empty()) { String bd = current_dir.replace_first(base, ""); @@ -474,14 +479,14 @@ uint64_t DirAccessUnix::get_space_left() { 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 { return ""; //TODO this should be implemented @@ -500,7 +505,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 +516,4 @@ DirAccessUnix::~DirAccessUnix() { list_dir_end(); } -#endif //posix_enabled +#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index f90f55605c..f5dca7c282 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -41,56 +41,55 @@ #include <unistd.h> class DirAccessUnix : public DirAccess { - DIR *dir_stream; + DIR *dir_stream = nullptr; - static DirAccess *create_fs(); - - String current_dir; - bool _cisdir; - bool _cishidden; + 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); ///< 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 || LIBC_FILEIO_ENABLED + +#endif // DIR_ACCESS_UNIX_H diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 80ae999ac9..388ad479b9 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -71,10 +71,7 @@ void FileAccessUnix::check_errors() const { } Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { - if (f) { - fclose(f); - } - f = nullptr; + _close(); path_src = p_path; path = fix_path(p_path); @@ -148,7 +145,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { return OK; } -void FileAccessUnix::close() { +void FileAccessUnix::_close() { if (!f) { return; } @@ -246,7 +243,7 @@ uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const { uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessUnix::get_error() const { return last_error; @@ -285,8 +282,9 @@ bool FileAccessUnix::file_exists(const String &p_path) { return false; } #else - if (_access(filename.utf8().get_data(), 4) == -1) + if (_access(filename.utf8().get_data(), 4) == -1) { return false; + } #endif // See if this is a regular file @@ -309,7 +307,7 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { } else { print_verbose("Failed to get modified time for: " + p_file + ""); return 0; - }; + } } uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { @@ -321,7 +319,7 @@ uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { return flags.st_mode & 0x7FF; //only permissions } else { ERR_FAIL_V_MSG(0, "Failed to get unix permissions for: " + p_file + "."); - }; + } } Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { @@ -335,14 +333,10 @@ Error FileAccessUnix::_set_unix_permissions(const String &p_file, uint32_t p_per return FAILED; } -FileAccess *FileAccessUnix::create_libc() { - return memnew(FileAccessUnix); -} - CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr; FileAccessUnix::~FileAccessUnix() { - close(); + _close(); } -#endif +#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 8ebdcd2a2d..297c34e454 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -49,43 +49,43 @@ class FileAccessUnix : public FileAccess { String path; String path_src; - static 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 void close(); ///< close a file - virtual bool is_open() const; ///< true when file is open + virtual Error _open(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 || LIBC_FILEIO_ENABLED + +#endif // FILE_ACCESS_UNIX_H diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index d82dcb8a8d..0dc2efedc1 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -72,10 +72,10 @@ static IPAddress _sockaddr2ip(struct sockaddr *p_addr) { } else if (p_addr->sa_family == AF_INET6) { struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; ip.set_ipv6(addr6->sin6_addr.s6_addr); - }; + } return ip; -}; +} void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const { struct addrinfo hints; @@ -90,22 +90,22 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos } else { hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; - }; + } hints.ai_flags &= ~AI_NUMERICHOST; 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); } return; - }; + } struct addrinfo *next = result; @@ -128,7 +128,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos #if defined(UWP_ENABLED) -void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const { using namespace Windows::Networking; using namespace Windows::Networking::Connectivity; @@ -138,11 +138,12 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con for (int i = 0; i < hostnames->Size; i++) { auto hostname = hostnames->GetAt(i); - if (hostname->Type != HostNameType::Ipv4 && hostname->Type != HostNameType::Ipv6) + if (hostname->Type != HostNameType::Ipv4 && hostname->Type != HostNameType::Ipv6) { continue; + } String name = hostname->RawName->Data(); - Map<String, Interface_Info>::Element *E = r_interfaces->find(name); + HashMap<String, Interface_Info>::Element *E = r_interfaces->find(name); if (!E) { Interface_Info info; info.name = name; @@ -161,7 +162,7 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con #else -void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const { ULONG buf_size = 1024; IP_ADAPTER_ADDRESSES *addrs; @@ -171,14 +172,14 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con nullptr, addrs, &buf_size); if (err == NO_ERROR) { break; - }; + } memfree(addrs); if (err == ERROR_BUFFER_OVERFLOW) { continue; // will go back and alloc the right size - }; + } ERR_FAIL_MSG("Call to GetAdaptersAddresses failed with error " + itos(err) + "."); - }; + } IP_ADAPTER_ADDRESSES *adapter = addrs; @@ -191,25 +192,27 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress; while (address != nullptr) { int family = address->Address.lpSockaddr->sa_family; - if (family != AF_INET && family != AF_INET6) + if (family != AF_INET && family != AF_INET6) { continue; + } info.ip_addresses.push_front(_sockaddr2ip(address->Address.lpSockaddr)); address = address->Next; } adapter = adapter->Next; // Only add interface if it has at least one IP - if (info.ip_addresses.size() > 0) + if (info.ip_addresses.size() > 0) { r_interfaces->insert(info.name, info); - }; + } + } memfree(addrs); -}; +} #endif #else // UNIX -void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const { +void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const { struct ifaddrs *ifAddrStruct = nullptr; struct ifaddrs *ifa = nullptr; int family; @@ -227,7 +230,7 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con continue; } - Map<String, Interface_Info>::Element *E = r_interfaces->find(ifa->ifa_name); + HashMap<String, Interface_Info>::Iterator E = r_interfaces->find(ifa->ifa_name); if (!E) { Interface_Info info; info.name = ifa->ifa_name; @@ -237,7 +240,7 @@ void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) con ERR_CONTINUE(!E); } - Interface_Info &info = E->get(); + Interface_Info &info = E->value; info.ip_addresses.push_front(_sockaddr2ip(ifa->ifa_addr)); } diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index f0ad01d248..798d02095c 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -43,11 +43,12 @@ class IPUnix : public IP { static IP *_create_unix(); public: - virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const override; + virtual void get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const override; static void make_default(); IPUnix(); }; #endif + #endif // IP_UNIX_H diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index bdfd264a0c..f172f31b24 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -147,7 +147,7 @@ void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ if (r_port) { *r_port = ntohs(addr6->sin6_port); } - }; + } } NetSocket *NetSocketPosix::_create_func() { @@ -256,7 +256,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str IPAddress if_ip; uint32_t if_v6id = 0; - Map<String, IP::Interface_Info> if_info; + HashMap<String, IP::Interface_Info> if_info; IP::get_singleton()->get_local_interfaces(&if_info); for (KeyValue<String, IP::Interface_Info> &E : if_info) { IP::Interface_Info &c = E.value; @@ -325,8 +325,9 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { #if defined(__OpenBSD__) // OpenBSD does not support dual stacking, fallback to IPv4 only. - if (ip_type == IP::TYPE_ANY) + if (ip_type == IP::TYPE_ANY) { ip_type = IP::TYPE_IPV4; + } #endif int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; @@ -420,7 +421,7 @@ Error NetSocketPosix::listen(int p_max_pending) { print_verbose("Failed to listen from socket."); close(); return FAILED; - }; + } return OK; } @@ -494,8 +495,9 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { return FAILED; } - if (ret == 0) + if (ret == 0) { return ERR_BUSY; + } if (FD_ISSET(_sock, &ex)) { _get_socket_error(); @@ -503,10 +505,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { return FAILED; } - if (rdp && FD_ISSET(_sock, rdp)) + if (rdp && FD_ISSET(_sock, rdp)) { ready = true; - if (wrp && FD_ISSET(_sock, wrp)) + } + if (wrp && FD_ISSET(_sock, wrp)) { ready = true; + } return ready ? OK : ERR_BUSY; #else 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 00f84eba8c..5bf14056ab 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -65,6 +65,21 @@ #include <time.h> #include <unistd.h> +#if defined(MACOS_ENABLED) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) +// Random location for getentropy. Fitting. +#include <sys/random.h> +#define UNIX_GET_ENTROPY +#elif defined(__FreeBSD__) || defined(__OpenBSD__) || (defined(__GLIBC_MINOR__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26)) +// In <unistd.h>. +// One day... (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) +// https://publications.opengroup.org/standards/unix/c211 +#define UNIX_GET_ENTROPY +#endif + +#if !defined(UNIX_GET_ENTROPY) && !defined(NO_URANDOM) +#include <fcntl.h> +#endif + /// Clock Setup function (used by get_ticks_usec) static uint64_t _clock_start = 0; #if defined(__APPLE__) @@ -91,7 +106,7 @@ static void _setup_clock() { void OS_Unix::debug_break() { assert(false); -}; +} static void handle_interrupt(int sig) { if (!EngineDebugger::is_active()) { @@ -150,6 +165,31 @@ String OS_Unix::get_stdin_string(bool p_block) { return ""; } +Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) { +#if defined(UNIX_GET_ENTROPY) + int left = p_bytes; + int ofs = 0; + do { + int chunk = MIN(left, 256); + ERR_FAIL_COND_V(getentropy(r_buffer + ofs, chunk), FAILED); + left -= chunk; + ofs += chunk; + } while (left > 0); +#elif !defined(NO_URANDOM) + int r = open("/dev/urandom", O_RDONLY); + ERR_FAIL_COND_V(r < 0, FAILED); + int left = p_bytes; + do { + ssize_t ret = read(r, r_buffer, p_bytes); + ERR_FAIL_COND_V(ret <= 0, FAILED); + left -= ret; + } while (left > 0); +#else + return ERR_UNAVAILABLE; +#endif + return OK; +} + String OS_Unix::get_name() const { return "Unix"; } @@ -158,7 +198,7 @@ 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 { time_t t = time(nullptr); @@ -273,7 +313,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(); } @@ -370,7 +415,16 @@ Error OS_Unix::kill(const ProcessID &p_pid) { int OS_Unix::get_process_id() const { return getpid(); -}; +} + +bool OS_Unix::is_process_running(const ProcessID &p_pid) const { + int status = 0; + if (waitpid(p_pid, &status, WNOHANG) != 0) { + return false; + } + + return true; +} bool OS_Unix::has_environment(const String &p_var) const { return getenv(p_var.utf8().get_data()) != nullptr; @@ -389,7 +443,7 @@ String OS_Unix::get_locale() const { return locale; } -Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { +Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { String path = p_path; if (FileAccess::exists(path) && path.is_relative_path()) { @@ -399,17 +453,22 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle } if (!FileAccess::exists(path)) { - //this code exists so gdnative can load .so files from within the executable 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()); } if (!FileAccess::exists(path)) { - //this code exists so gdnative can load .so files from a standard unix location + // 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()); } p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror()); + + if (r_resolved_path != nullptr) { + *r_resolved_path = path; + } + return OK; } @@ -515,8 +574,9 @@ String OS_Unix::get_executable_path() const { char *resolved_path = new char[buff_size + 1]; - if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) + if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) { WARN_PRINT("MAXPATHLEN is too small"); + } String path(resolved_path); delete[] resolved_path; diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 3f0e8a171c..f4609a565b 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -43,7 +43,6 @@ protected: virtual void initialize_core(); virtual int unix_initialize_audio(int p_audio_driver); - //virtual Error initialize(int p_video_driver,int p_audio_driver); virtual void finalize_core() override; @@ -54,17 +53,9 @@ public: virtual String get_stdin_string(bool p_block) override; - //virtual void set_mouse_show(bool p_show); - //virtual void set_mouse_grab(bool p_grab); - //virtual bool is_mouse_grab_enabled() const = 0; - //virtual void get_mouse_position(int &x, int &y) const; - //virtual void set_window_title(const String& p_title); + virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; // Should return cryptographycally-safe random bytes. - //virtual void set_video_mode(const VideoMode& p_video_mode); - //virtual VideoMode get_video_mode() const; - //virtual void get_fullscreen_mode_list(List<VideoMode> *p_list) const; - - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) 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; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override; @@ -85,6 +76,7 @@ public: virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; virtual Error kill(const ProcessID &p_pid) override; virtual int get_process_id() const override; + virtual bool is_process_running(const ProcessID &p_pid) const override; virtual bool has_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override; @@ -106,6 +98,6 @@ public: virtual ~UnixTerminalLogger(); }; -#endif +#endif // UNIX_ENABLED -#endif +#endif // OS_UNIX_H diff --git a/drivers/unix/syslog_logger.cpp b/drivers/unix/syslog_logger.cpp index 0e1a1a89a3..6189d645c6 100644 --- a/drivers/unix/syslog_logger.cpp +++ b/drivers/unix/syslog_logger.cpp @@ -31,7 +31,9 @@ #ifdef UNIX_ENABLED #include "syslog_logger.h" + #include "core/string/print_string.h" + #include <syslog.h> void SyslogLogger::logv(const char *p_format, va_list p_list, bool p_err) { 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.h b/drivers/unix/thread_posix.h index 9cd3ecbe90..672adcba72 100644 --- a/drivers/unix/thread_posix.h +++ b/drivers/unix/thread_posix.h @@ -35,4 +35,4 @@ void init_thread_posix(); #endif -#endif +#endif // THREAD_POSIX_H diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub index 8fe75367a8..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,6 +40,9 @@ 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"] == "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"]) env_thirdparty_vma.add_source_files(thirdparty_obj, thirdparty_sources_vma) diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index ba623eb298..bcb6092d87 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" @@ -43,7 +44,9 @@ //#define FORCE_FULL_BARRIER -// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values) +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). 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)) { @@ -104,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; @@ -122,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; } @@ -130,41 +133,41 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { if (!dependency_map.has(p_depends_on)) { - dependency_map[p_depends_on] = Set<RID>(); + dependency_map[p_depends_on] = HashSet<RID>(); } dependency_map[p_depends_on].insert(p_id); if (!reverse_dependency_map.has(p_id)) { - reverse_dependency_map[p_id] = Set<RID>(); + reverse_dependency_map[p_id] = HashSet<RID>(); } reverse_dependency_map[p_id].insert(p_depends_on); } void RenderingDeviceVulkan::_free_dependencies(RID p_id) { - //direct dependencies must be freed + // Direct dependencies must be freed. - Map<RID, Set<RID>>::Element *E = dependency_map.find(p_id); + HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); if (E) { - while (E->get().size()) { - free(E->get().front()->get()); + while (E->value.size()) { + free(*E->value.begin()); } - dependency_map.erase(E); + dependency_map.remove(E); } - //reverse dependencies must be unreferenced + // Reverse dependencies must be unreferenced. E = reverse_dependency_map.find(p_id); if (E) { - for (Set<RID>::Element *F = E->get().front(); F; F = F->next()) { - Map<RID, Set<RID>>::Element *G = dependency_map.find(F->get()); + for (const RID &F : E->value) { + HashMap<RID, HashSet<RID>>::Iterator G = dependency_map.find(F); ERR_CONTINUE(!G); - ERR_CONTINUE(!G->get().has(p_id)); - G->get().erase(p_id); + ERR_CONTINUE(!G->value.has(p_id)); + G->value.erase(p_id); } - reverse_dependency_map.erase(E); + reverse_dependency_map.remove(E); } } @@ -857,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: @@ -992,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: @@ -1070,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: @@ -1098,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: { } } @@ -1107,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: @@ -1174,31 +1177,31 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form } w = MAX(blockw, w >> 1); h = MAX(blockh, h >> 1); - d = MAX(1, d >> 1); + d = MAX(1u, d >> 1); } return size; } 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) - int w = p_width; - int h = p_height; - int d = p_depth; + // 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; - int mipmaps = 1; + uint32_t mipmaps = 1; while (true) { if (w == 1 && h == 1 && d == 1) { break; } - w = MAX(1, w >> 1); - h = MAX(1, h >> 1); - d = MAX(1, d >> 1); + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); mipmaps++; - }; + } return mipmaps; } @@ -1316,7 +1319,7 @@ const VkImageType RenderingDeviceVulkan::vulkan_image_type[RenderingDevice::TEXT /**** BUFFER MANAGEMENT ****/ /***************************/ -Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping) { +Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags) { VkBufferCreateInfo bufferInfo; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.pNext = nullptr; @@ -1328,13 +1331,18 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size, bufferInfo.pQueueFamilyIndices = nullptr; VmaAllocationCreateInfo allocInfo; - allocInfo.flags = 0; - allocInfo.usage = p_mapping; + allocInfo.flags = p_mem_flags; + allocInfo.usage = p_mem_usage; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; allocInfo.pool = nullptr; allocInfo.pUserData = nullptr; + if (p_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForBufferInfo(allocator, &bufferInfo, &allocInfo, &mem_type_index); + allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); + } VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, nullptr); ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + "."); @@ -1373,8 +1381,8 @@ Error RenderingDeviceVulkan::_insert_staging_block() { bufferInfo.pQueueFamilyIndices = nullptr; VmaAllocationCreateInfo allocInfo; - allocInfo.flags = 0; - allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY; + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; @@ -1393,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; @@ -1417,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 if (staging_buffer_blocks[staging_buffer_current].frame_used > frames_drawn - frame_count) { - //this block may still be in use, let's not touch it unless we have to, so.. can we create a new one? + } else { + // 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; } @@ -1527,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; @@ -1535,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; { @@ -1548,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; @@ -1579,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,8 +1662,8 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T #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; @@ -1728,7 +1736,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.samples = rasterization_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) { @@ -1751,6 +1759,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; } @@ -1788,7 +1800,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; @@ -1822,9 +1834,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()); @@ -1834,16 +1851,24 @@ 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); VmaAllocationCreateInfo allocInfo; - allocInfo.flags = 0; - allocInfo.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY; + allocInfo.flags = (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) ? VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT : 0; + allocInfo.pool = nullptr; + allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; allocInfo.requiredFlags = 0; allocInfo.preferredFlags = 0; allocInfo.memoryTypeBits = 0; - allocInfo.pool = nullptr; allocInfo.pUserData = nullptr; + if (image_size <= SMALL_ALLOCATION_MAX_SIZE) { + uint32_t mem_type_index = 0; + vmaFindMemoryTypeIndexForImageInfo(allocator, &image_create_info, &allocInfo, &mem_type_index); + allocInfo.pool = _find_or_create_small_allocs_pool(mem_type_index); + } Texture texture; @@ -1863,19 +1888,19 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T 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; @@ -1900,7 +1925,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; @@ -1957,7 +1982,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; @@ -1979,6 +2004,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++) { @@ -1994,13 +2022,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; @@ -2061,7 +2089,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; @@ -2108,21 +2136,145 @@ 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; } +RID RenderingDeviceVulkan::texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers) { + _THREAD_SAFE_METHOD_ + // This method creates a texture object using a VkImage created by an extension, module or other external source (OpenXR uses this). + VkImage image = (VkImage)p_image; + + 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. + texture.type = p_type; + texture.format = p_format; + texture.samples = p_samples; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.layers = p_layers; + texture.mipmaps = 0; // Maybe make this settable too? + 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? + + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + texture.read_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_DEPTH_BIT; + + // if (format_has_stencil(p_format.format)) { + // texture.barrier_aspect_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; + // } + } else { + texture.read_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + texture.barrier_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + // Create a view for us to use. + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = nullptr; + image_view_create_info.flags = 0; + image_view_create_info.image = texture.image; + + static const VkImageViewType view_types[TEXTURE_TYPE_MAX] = { + VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_VIEW_TYPE_3D, + VK_IMAGE_VIEW_TYPE_CUBE, + VK_IMAGE_VIEW_TYPE_1D_ARRAY, + VK_IMAGE_VIEW_TYPE_2D_ARRAY, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + }; + + image_view_create_info.viewType = view_types[texture.type]; + image_view_create_info.format = vulkan_formats[texture.format]; + + static const VkComponentSwizzle component_swizzles[TEXTURE_SWIZZLE_MAX] = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_ZERO, + VK_COMPONENT_SWIZZLE_ONE, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + // 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]; + image_view_create_info.components.a = component_swizzles[TEXTURE_SWIZZLE_A]; + + image_view_create_info.subresourceRange.baseMipLevel = 0; + image_view_create_info.subresourceRange.levelCount = texture.mipmaps; + image_view_create_info.subresourceRange.baseArrayLayer = 0; + image_view_create_info.subresourceRange.layerCount = texture.layers; + if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + } else { + image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + VkResult err = vkCreateImageView(device, &image_view_create_info, nullptr, &texture.view); + + if (err) { + // vmaDestroyImage(allocator, texture.image, texture.allocation); + ERR_FAIL_V_MSG(RID(), "vkCreateImageView failed with error " + itos(err) + "."); + } + + // Barrier to set layout. + { + VkImageMemoryBarrier image_memory_barrier; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = nullptr; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_memory_barrier.newLayout = texture.layout; + image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + image_memory_barrier.image = texture.image; + image_memory_barrier.subresourceRange.aspectMask = texture.barrier_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = texture.mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = texture.layers; + + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + } + + RID id = texture_owner.make_rid(texture); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + + return id; +} + RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps, TextureSliceType p_slice_type) { _THREAD_SAFE_METHOD_ 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(), @@ -2134,7 +2286,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()); @@ -2181,6 +2333,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 { @@ -2228,6 +2386,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; @@ -2237,6 +2398,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_ @@ -2249,7 +2426,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, @@ -2271,7 +2448,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; } @@ -2284,7 +2461,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; @@ -2318,12 +2495,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); @@ -2335,12 +2512,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) + "."); @@ -2355,52 +2532,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; @@ -2423,21 +2580,21 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co } mipmap_offset = image_total; - logic_width = MAX(1, logic_width >> 1); - logic_height = MAX(1, logic_height >> 1); + logic_width = MAX(1u, logic_width >> 1); + 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; @@ -2514,7 +2671,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; @@ -2524,7 +2681,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; @@ -2560,19 +2717,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_CPU_ONLY); + _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; @@ -2622,13 +2779,13 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t vkCmdCopyImageToBuffer(command_buffer, tex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer.buffer, 1, &buffer_image_copy); - computed_w = MAX(1, computed_w >> 1); - computed_h = MAX(1, computed_h >> 1); - computed_d = MAX(1, computed_d >> 1); + computed_w = MAX(1u, computed_w >> 1); + computed_h = MAX(1u, computed_h >> 1); + computed_d = MAX(1u, computed_d >> 1); 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; @@ -2743,9 +2900,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; @@ -2765,7 +2922,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; @@ -2786,7 +2943,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; @@ -2813,7 +2970,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; @@ -2834,7 +2991,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; @@ -2851,10 +3008,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; @@ -2877,6 +3034,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; } @@ -2914,9 +3078,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; @@ -2936,7 +3100,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; @@ -2957,7 +3121,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; @@ -2984,7 +3148,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; @@ -3005,7 +3169,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; @@ -3025,7 +3189,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; @@ -3078,13 +3242,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; @@ -3120,7 +3284,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; @@ -3176,7 +3340,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; @@ -3207,6 +3371,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; } @@ -3215,30 +3384,45 @@ 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; + // 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++) { + if (p_attachments[i].usage_flags == AttachmentFormat::UNUSED_ATTACHMENT) { + attachment_remap.push_back(VK_ATTACHMENT_UNUSED); + continue; + } + 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]; @@ -3247,83 +3431,95 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF 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 } } @@ -3333,13 +3529,17 @@ 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; @@ -3378,72 +3578,85 @@ 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. + } } } attachment_last_pass[i] = -1; - + attachment_remap.push_back(attachments.size()); 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()); @@ -3451,20 +3664,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; @@ -3479,18 +3697,21 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } else { ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), if an attachment is marked as multisample, all of them should be multisample and use the same number of samples."); } - reference.attachment = attachment; + reference.attachment = attachment_remap[attachment]; reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; 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; @@ -3498,14 +3719,15 @@ 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) + "), input attachment (" + itos(j) + ")."); ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it isn't marked as an input texture."); ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); - reference.attachment = attachment; + reference.attachment = attachment_remap[attachment]; reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; // TODO: We need to set this here, possibly 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()) + ")."); @@ -3513,7 +3735,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; @@ -3524,22 +3748,26 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF ERR_FAIL_COND_V_MSG(attachment_last_pass[attachment] == i, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it already was used for something else before in this pass."); 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; - reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + reference.attachment = attachment_remap[attachment]; + reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL attachment_last_pass[attachment] = i; } + reference.aspectMask = 0; 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; ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), depth attachment."); ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer depth format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not a depth attachment."); 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; + depth_stencil_reference.attachment = attachment_remap[attachment]; depth_stencil_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depth_stencil_reference.aspectMask = 0; attachment_last_pass[attachment] = i; if (is_multisample_first) { @@ -3554,6 +3782,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 = 0; + + 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++) { @@ -3564,15 +3818,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(); @@ -3609,7 +3871,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; @@ -3619,27 +3883,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(); @@ -3656,13 +3922,21 @@ 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) { + // This may no longer be needed with the new settings already including this. + const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities(); // For now this only works with multiview! @@ -3671,10 +3945,10 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF // 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"); - // 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); - }; + } render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO; render_pass_multiview_create_info.pNext = nullptr; @@ -3689,8 +3963,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; } @@ -3709,7 +3983,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; @@ -3717,16 +3991,16 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c key.passes = p_passes; key.view_count = p_view_count; - const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + 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)); @@ -3745,16 +4019,19 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c FramebufferFormatKey key; key.passes.push_back(FramebufferPass()); - const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); + 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; @@ -3763,8 +4040,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; @@ -3773,13 +4050,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; } @@ -3796,11 +4075,11 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c } RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format, uint32_t p_pass) { - Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format); + HashMap<FramebufferFormatID, FramebufferFormat>::Iterator E = framebuffer_formats.find(p_format); ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1); - ERR_FAIL_COND_V(p_pass >= uint32_t(E->get().pass_samples.size()), TEXTURE_SAMPLES_1); + ERR_FAIL_COND_V(p_pass >= uint32_t(E->value.pass_samples.size()), TEXTURE_SAMPLES_1); - return E->get().pass_samples[p_pass]; + return E->value.pass_samples[p_pass]; } /***********************/ @@ -3815,7 +4094,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) { @@ -3825,14 +4108,15 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac for (int i = 0; i < p_texture_attachments.size(); i++) { Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); - ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); - ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + ERR_FAIL_COND_V_MSG(texture && texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); - if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + 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(i); + pass.color_attachments.push_back(texture ? i : FramebufferPass::ATTACHMENT_UNUSED); } } @@ -3842,33 +4126,43 @@ 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; + attachments.resize(p_texture_attachments.size()); Size2i size; - + bool size_set = false; for (int i = 0; i < p_texture_attachments.size(); i++) { + AttachmentFormat af; Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); - ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); - - ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); - - if (i == 0) { - size.width = texture->width; - size.height = texture->height; + if (!texture) { + af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT; } 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."); - } + ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); + + if (!size_set) { + 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 attachement we assume this is used as the VRS attachment. + // In this case this texture will be 1/16th the size of the color attachement. + // 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."); + } - AttachmentFormat af; - af.format = texture->format; - af.samples = texture->samples; - af.usage_flags = texture->usage_flags; - attachments.push_back(af); + af.format = texture->format; + af.samples = texture->samples; + af.usage_flags = texture->usage_flags; + } + attachments.write[i] = af; } + ERR_FAIL_COND_V_MSG(!size_set, RID(), "All attachments unused."); + FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count); if (format_id == INVALID_ID) { return RID(); @@ -3884,9 +4178,14 @@ 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++) { - _add_dependency(id, p_texture_attachments[i]); + if (p_texture_attachments[i].is_valid()) { + _add_dependency(id, p_texture_attachments[i]); + } } return id; @@ -3901,6 +4200,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 ****/ /*****************/ @@ -3943,7 +4258,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; } /**********************/ @@ -3964,7 +4283,7 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; } Buffer buffer; - _buffer_allocate(&buffer, p_size_bytes, usage, VMA_MEMORY_USAGE_GPU_ONLY); + _buffer_allocate(&buffer, p_size_bytes, usage, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); if (p_data.size()) { uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); @@ -3972,10 +4291,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_ @@ -3987,12 +4310,12 @@ 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()); - Set<int> used_locations; + HashSet<int> used_locations; for (int i = 0; i < p_vertex_formats.size(); i++) { ERR_CONTINUE(p_vertex_formats[i].format >= DATA_FORMAT_MAX); ERR_FAIL_COND_V(used_locations.has(p_vertex_formats[i].location), INVALID_ID); @@ -4043,25 +4366,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."); @@ -4072,7 +4395,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); @@ -4107,7 +4430,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); } @@ -4115,7 +4438,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); } @@ -4126,14 +4449,18 @@ RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBuff #else index_buffer.max_index = 0xFFFFFFFF; #endif - _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + _buffer_allocate(&index_buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); if (p_data.size()) { uint64_t data_size = p_data.size(); const uint8_t *r = p_data.ptr(); _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) { @@ -4196,7 +4523,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; @@ -4210,7 +4537,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; @@ -4310,7 +4637,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; } @@ -4347,10 +4674,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."; @@ -4358,7 +4685,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."; @@ -4366,7 +4693,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; @@ -4375,7 +4702,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; @@ -4394,20 +4721,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 { @@ -4447,7 +4776,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; @@ -4481,7 +4810,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); @@ -4497,6 +4826,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve 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: { @@ -4514,6 +4844,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; @@ -4522,6 +4853,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; @@ -4530,6 +4862,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."); @@ -4568,6 +4901,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; @@ -4578,26 +4917,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. } } @@ -4612,7 +4956,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); @@ -4630,19 +4974,22 @@ 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; - sconst.constant_id = spec_constants[j]->constant_id; - switch (spec_constants[j]->constant_type) { + SpvReflectSpecializationConstant *spc = spec_constants[j]; + + sconst.constant_id = spc->constant_id; + 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; - sconst.bool_value = spec_constants[j]->default_value.int_bool_value != 0; + sconst.bool_value = spc->default_value.int_bool_value != 0; } break; case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: { sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT; - sconst.int_value = spec_constants[j]->default_value.int_bool_value; + sconst.int_value = spc->default_value.int_bool_value; } break; case SPV_REFLECT_SPECIALIZATION_CONSTANT_FLOAT: { sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_FLOAT; - sconst.float_value = spec_constants[j]->default_value.float_value; + sconst.float_value = spc->default_value.float_value; } break; } sconst.stage_flags = 1 << p_spirv[i].shader_stage; @@ -4680,7 +5027,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)); } } @@ -4726,9 +5073,8 @@ 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 push constants."); #if 0 if (pconstants[0] == nullptr) { - FileAccess *f = FileAccess::open("res://popo.spv", FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open("res://popo.spv", FileAccess::WRITE); f->store_buffer((const uint8_t *)&SpirV[0], SpirV.size() * sizeof(uint32_t)); - memdelete(f); } #endif @@ -4750,7 +5096,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; @@ -4762,7 +5108,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); @@ -4775,7 +5121,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); } } @@ -4795,12 +5141,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); } @@ -4811,18 +5157,18 @@ 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; ret.resize(total_size); - uint32_t offset = 0; { + uint32_t offset = 0; uint8_t *binptr = ret.ptrw(); binptr[0] = 'G'; binptr[1] = 'V'; binptr[2] = 'B'; - binptr[3] = 'D'; //godot vulkan binary data + binptr[3] = 'D'; // Godot vulkan binary data. offset += 4; encode_uint32(SHADER_BINARY_VERSION, binptr + offset); offset += sizeof(uint32_t); @@ -4833,7 +5179,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); } @@ -4881,16 +5227,16 @@ 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()); 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); - const RenderingDeviceVulkanShaderBinaryData &binary_data = *(const RenderingDeviceVulkanShaderBinaryData *)(binptr + 12); + const RenderingDeviceVulkanShaderBinaryData &binary_data = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinaryData *>(binptr + 12)); Shader::PushConstant push_constant; push_constant.push_constant_size = binary_data.push_constant_size; @@ -4902,7 +5248,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ bool is_compute = binary_data.is_compute; - uint32_t compute_local_size[3] = { binary_data.compute_local_size[0], binary_data.compute_local_size[1], binary_data.compute_local_size[2] }; + const uint32_t compute_local_size[3] = { binary_data.compute_local_size[0], binary_data.compute_local_size[1], binary_data.compute_local_size[2] }; read_offset += sizeof(uint32_t) * 3 + bin_data_size; @@ -4911,7 +5257,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); } } @@ -4926,13 +5272,14 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) >= binsize, RID()); uint32_t set_count = decode_uint32(binptr + read_offset); read_offset += sizeof(uint32_t); - const RenderingDeviceVulkanShaderBinaryDataBinding *set_ptr = (const RenderingDeviceVulkanShaderBinaryDataBinding *)(binptr + read_offset); + const RenderingDeviceVulkanShaderBinaryDataBinding *set_ptr = reinterpret_cast<const RenderingDeviceVulkanShaderBinaryDataBinding *>(binptr + read_offset); uint32_t set_size = set_count * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding); ERR_FAIL_COND_V(read_offset + set_size >= binsize, RID()); 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; @@ -4998,7 +5345,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ Vector<Shader::SpecializationConstant> specialization_constants; for (uint32_t i = 0; i < binary_data.specialization_constant_count; i++) { - const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(const RenderingDeviceVulkanShaderBinarySpecializationConstant *)(binptr + read_offset); + const RenderingDeviceVulkanShaderBinarySpecializationConstant &src_sc = *(reinterpret_cast<const RenderingDeviceVulkanShaderBinarySpecializationConstant *>(binptr + read_offset)); Shader::SpecializationConstant sc; sc.constant.int_value = src_sc.int_value; sc.constant.type = PipelineSpecializationConstantType(src_sc.type); @@ -5027,7 +5374,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()); @@ -5056,7 +5403,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_ @@ -5112,11 +5459,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; @@ -5135,16 +5482,16 @@ 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; - Map<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); + RBMap<UniformSetFormat, uint32_t>::Element *E = uniform_set_format_cache.find(usformat); if (E) { format = E->get(); } else { @@ -5159,7 +5506,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; @@ -5198,7 +5545,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); } @@ -5210,7 +5557,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) { @@ -5235,7 +5586,7 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve "Creating buffers with data is forbidden during creation of a draw list"); Buffer buffer; - Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { uint64_t data_size = p_data.size(); @@ -5243,7 +5594,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) { @@ -5256,12 +5611,11 @@ 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; } - Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { @@ -5287,7 +5641,7 @@ RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataF ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != size_bytes, RID()); TextureBuffer texture_buffer; - Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); + Error err = _buffer_allocate(&texture_buffer.buffer, size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 0); ERR_FAIL_COND_V(err != OK, RID()); if (p_data.size()) { @@ -5314,36 +5668,40 @@ 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) { if (!descriptor_pools.has(p_key)) { - descriptor_pools[p_key] = Set<DescriptorPool *>(); + descriptor_pools[p_key] = HashSet<DescriptorPool *>(); } DescriptorPool *pool = nullptr; - for (Set<DescriptorPool *>::Element *E = descriptor_pools[p_key].front(); E; E = E->next()) { - if (E->get()->usage < max_descriptors_per_pool) { - pool = E->get(); + for (DescriptorPool *E : descriptor_pools[p_key]) { + if (E->usage < max_descriptors_per_pool) { + pool = E; break; } } 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; @@ -5443,7 +5801,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]; @@ -5456,11 +5814,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; @@ -5481,14 +5839,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; @@ -5496,18 +5854,18 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, switch (uniform.uniform_type) { case UNIFORM_TYPE_SAMPLER: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler elements, so it should be provided equal number of sampler IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Sampler (binding: " + itos(uniform.binding) + ") should provide one ID referencing a sampler (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "Sampler (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid sampler."); VkDescriptorImageInfo img_info; @@ -5519,31 +5877,31 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: { - if (uniform.ids.size() != set_uniform.length * 2) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler&texture elements, so it should provided twice the amount of IDs (sampler,texture pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerTexture (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j += 2) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.ids[j + 0]); + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - Texture *texture = texture_owner.get_or_null(uniform.ids[j + 1]); + Texture *texture = texture_owner.get_or_null(uniform.get_id(j + 1)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -5556,17 +5914,17 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j + 1]; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j + 1); attachable_textures.push_back(attachable_texture); } 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; @@ -5575,28 +5933,28 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size() / 2; + write.descriptorCount = uniform.get_id_count() / 2; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size() / 2; + type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_TEXTURE: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Texture (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -5609,18 +5967,18 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_INPUT_ATTACHMENT_BIT)) { UniformSet::AttachableTexture attachable_texture; attachable_texture.bind = set_uniform.binding; - attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.ids[j]; + attachable_texture.texture = texture->owner.is_valid() ? texture->owner : uniform.get_id(j); attachable_textures.push_back(attachable_texture); } 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; @@ -5629,27 +5987,27 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_IMAGE: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Image (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!texture, RID(), "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -5662,13 +6020,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; @@ -5677,29 +6035,29 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_TEXTURE_BUFFER: { - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") texture buffer elements, so it should be provided equal number of texture buffer IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "Buffer (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorBufferInfo> buffer_info; Vector<VkBufferView> buffer_view; - for (int j = 0; j < uniform.ids.size(); j++) { - TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); buffer_info.push_back(buffer->buffer.buffer_info); @@ -5707,21 +6065,21 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; write.pImageInfo = nullptr; write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE_BUFFER: { - if (uniform.ids.size() != set_uniform.length * 2) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length * 2) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") sampler buffer elements, so it should provided twice the amount of IDs (sampler,buffer pairs) to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ") should provide two IDs referencing a sampler and then a texture buffer (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } @@ -5729,11 +6087,11 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, Vector<VkDescriptorBufferInfo> buffer_info; Vector<VkBufferView> buffer_view; - for (int j = 0; j < uniform.ids.size(); j += 2) { - VkSampler *sampler = sampler_owner.get_or_null(uniform.ids[j + 0]); + for (uint32_t j = 0; j < uniform.get_id_count(); j += 2) { + VkSampler *sampler = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.ids[j + 1]); + TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); VkDescriptorImageInfo img_info; img_info.sampler = *sampler; @@ -5749,23 +6107,23 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size() / 2; + write.descriptorCount = uniform.get_id_count() / 2; write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = buffer_infos.push_back(buffer_info)->get().ptr(); write.pTexelBufferView = buffer_views.push_back(buffer_view)->get().ptr(); - type_size = uniform.ids.size() / 2; + type_size = uniform.get_id_count() / 2; } break; case UNIFORM_TYPE_IMAGE_BUFFER: { - //todo + // Todo. } break; case UNIFORM_TYPE_UNIFORM_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), - "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); - Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.ids[0]); + Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.get_id(0)); ERR_FAIL_COND_V_MSG(!buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); ERR_FAIL_COND_V_MSG(buffer->size != (uint32_t)set_uniform.length, RID(), @@ -5780,21 +6138,21 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } break; case UNIFORM_TYPE_STORAGE_BUFFER: { - ERR_FAIL_COND_V_MSG(uniform.ids.size() != 1, RID(), - "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.ids.size()) + " provided)."); + ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), + "Storage buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); Buffer *buffer = nullptr; - if (storage_buffer_owner.owns(uniform.ids[0])) { - buffer = storage_buffer_owner.get_or_null(uniform.ids[0]); - } else if (vertex_buffer_owner.owns(uniform.ids[0])) { - buffer = vertex_buffer_owner.get_or_null(uniform.ids[0]); + if (storage_buffer_owner.owns(uniform.get_id(0))) { + buffer = storage_buffer_owner.get_or_null(uniform.get_id(0)); + } else if (vertex_buffer_owner.owns(uniform.get_id(0))) { + buffer = vertex_buffer_owner.get_or_null(uniform.get_id(0)); ERR_FAIL_COND_V_MSG(!(buffer->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); } 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) + ")."); @@ -5808,18 +6166,18 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, case UNIFORM_TYPE_INPUT_ATTACHMENT: { ERR_FAIL_COND_V_MSG(shader->is_compute, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") supplied for compute shader (this is not allowed)."); - if (uniform.ids.size() != set_uniform.length) { + if (uniform.get_id_count() != (uint32_t)set_uniform.length) { if (set_uniform.length > 1) { - ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") is an array of (" + itos(set_uniform.length) + ") textures, so it should be provided equal number of texture IDs to satisfy it (IDs provided: " + itos(uniform.get_id_count()) + ")."); } else { - ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.ids.size()) + ")."); + ERR_FAIL_V_MSG(RID(), "InputAttachment (binding: " + itos(uniform.binding) + ") should provide one ID referencing a texture (IDs provided: " + itos(uniform.get_id_count()) + ")."); } } Vector<VkDescriptorImageInfo> image_info; - for (int j = 0; j < uniform.ids.size(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.ids[j]); + for (uint32_t j = 0; j < uniform.get_id_count(); j++) { + Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); ERR_FAIL_COND_V_MSG(!texture, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -5833,7 +6191,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; @@ -5842,13 +6200,13 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms, } write.dstArrayElement = 0; - write.descriptorCount = uniform.ids.size(); + write.descriptorCount = uniform.get_id_count(); write.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; write.pImageInfo = image_infos.push_back(image_info)->get().ptr(); write.pBufferInfo = nullptr; write.pTexelBufferView = nullptr; - type_size = uniform.ids.size(); + type_size = uniform.get_id_count(); } break; default: { } @@ -5861,7 +6219,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()); @@ -5878,7 +6236,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) + "."); } @@ -5894,18 +6252,20 @@ 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]; - int id_count = uniform.ids.size(); - const RID *ids = uniform.ids.ptr(); + int id_count = uniform.get_id_count(); for (int j = 0; j < id_count; j++) { - _add_dependency(id, ids[j]); + _add_dependency(id, uniform.get_id(j)); } } - //write the contents + // Write the contents. if (writes.size()) { for (int i = 0; i < writes.size(); i++) { writes.write[i].dstSet = descriptor_set; @@ -5920,7 +6280,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; @@ -5938,7 +6298,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; } @@ -5950,7 +6310,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); @@ -5986,7 +6346,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; } @@ -5999,7 +6359,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); @@ -6020,10 +6380,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."); @@ -6035,13 +6395,13 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; Buffer tmp_buffer; - _buffer_allocate(&tmp_buffer, buffer->size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + _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); VkBufferCopy region; 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; @@ -6069,7 +6429,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()); @@ -6077,13 +6437,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]; @@ -6096,19 +6456,19 @@ 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 & (1 << i))) { + if (!(shader->vertex_input_mask & (1UL << i))) { continue; } bool found = false; @@ -6123,7 +6483,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; @@ -6135,7 +6495,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()); @@ -6161,7 +6521,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; @@ -6173,12 +6533,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; @@ -6186,7 +6546,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma rasterization_state_create_info.depthClampEnable = p_rasterization_state.enable_depth_clamp; rasterization_state_create_info.rasterizerDiscardEnable = p_rasterization_state.discard_primitives; rasterization_state_create_info.polygonMode = (p_rasterization_state.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL); - static VkCullModeFlags cull_mode[3] = { + static const VkCullModeFlags cull_mode[3] = { VK_CULL_MODE_NONE, VK_CULL_MODE_FRONT_BIT, VK_CULL_MODE_BACK_BIT @@ -6201,7 +6561,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma 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; @@ -6212,8 +6572,8 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma 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 - int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { + // Use sample mask. + const int rasterization_sample_mask_expected_size[TEXTURE_SAMPLES_MAX] = { 1, 2, 4, 8, 16, 32, 64 }; ERR_FAIL_COND_V(rasterization_sample_mask_expected_size[p_multisample_state.sample_count] != p_multisample_state.sample_mask.size(), RID()); @@ -6230,7 +6590,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; @@ -6270,7 +6630,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; @@ -6282,49 +6642,55 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma Vector<VkPipelineColorBlendAttachmentState> attachment_states; { const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass]; - + attachment_states.resize(pass.color_attachments.size()); + ERR_FAIL_COND_V(p_blend_state.attachments.size() < pass.color_attachments.size(), RID()); for (int i = 0; i < pass.color_attachments.size(); i++) { - if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) { - int idx = attachment_states.size(); - - ERR_FAIL_INDEX_V(idx, p_blend_state.attachments.size(), RID()); - VkPipelineColorBlendAttachmentState state; - state.blendEnable = p_blend_state.attachments[idx].enable_blend; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[idx].src_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_color_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].color_blend_op, BLEND_OP_MAX, RID()); - state.colorBlendOp = blend_operations[p_blend_state.attachments[idx].color_blend_op]; - - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].src_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); - state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[idx].dst_alpha_blend_factor]; - ERR_FAIL_INDEX_V(p_blend_state.attachments[idx].alpha_blend_op, BLEND_OP_MAX, RID()); - state.alphaBlendOp = blend_operations[p_blend_state.attachments[idx].alpha_blend_op]; + VkPipelineColorBlendAttachmentState state; + if (pass.color_attachments[i] == FramebufferPass::ATTACHMENT_UNUSED) { + state.blendEnable = false; + + state.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO; + state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + state.colorBlendOp = VK_BLEND_OP_ADD; + + state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + state.alphaBlendOp = VK_BLEND_OP_ADD; + + state.colorWriteMask = 0; + } else { + state.blendEnable = p_blend_state.attachments[i].enable_blend; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcColorBlendFactor = blend_factors[p_blend_state.attachments[i].src_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_color_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstColorBlendFactor = blend_factors[p_blend_state.attachments[i].dst_color_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].color_blend_op, BLEND_OP_MAX, RID()); + state.colorBlendOp = blend_operations[p_blend_state.attachments[i].color_blend_op]; + + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].src_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.srcAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].src_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].dst_alpha_blend_factor, BLEND_FACTOR_MAX, RID()); + state.dstAlphaBlendFactor = blend_factors[p_blend_state.attachments[i].dst_alpha_blend_factor]; + ERR_FAIL_INDEX_V(p_blend_state.attachments[i].alpha_blend_op, BLEND_OP_MAX, RID()); + state.alphaBlendOp = blend_operations[p_blend_state.attachments[i].alpha_blend_op]; state.colorWriteMask = 0; - if (p_blend_state.attachments[idx].write_r) { + if (p_blend_state.attachments[i].write_r) { state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; } - if (p_blend_state.attachments[idx].write_g) { + if (p_blend_state.attachments[i].write_g) { state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; } - if (p_blend_state.attachments[idx].write_b) { + if (p_blend_state.attachments[i].write_b) { state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; } - if (p_blend_state.attachments[idx].write_a) { + if (p_blend_state.attachments[i].write_a) { state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; } - - attachment_states.push_back(state); - idx++; - }; + } + attachment_states.write[i] = state; } - - ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID()); } color_blend_state_create_info.attachmentCount = attachment_states.size(); @@ -6335,15 +6701,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) { @@ -6377,11 +6743,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; @@ -6395,9 +6778,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]; @@ -6492,9 +6875,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; } @@ -6511,14 +6897,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; @@ -6538,15 +6924,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; } } @@ -6581,9 +6967,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; } @@ -6613,7 +7002,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++) { @@ -6647,6 +7036,10 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; + if (!context->window_is_valid_swapchain(p_screen)) { + return INVALID_ID; + } + Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); @@ -6711,7 +7104,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); @@ -6724,10 +7117,13 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu Vector<VkImageView> attachments; for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - ERR_FAIL_COND_V(!texture, ERR_BUG); - 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) { + attachments.push_back(texture->view); + 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(); framebuffer_create_info.pAttachments = attachments.ptr(); @@ -6771,15 +7167,20 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff Vector<VkClearValue> clear_values; clear_values.resize(framebuffer->texture_ids.size()); - + int clear_values_count = 0; { int color_index = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); VkClearValue clear_value; + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + if (!texture) { + color_index++; + continue; + } + 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; @@ -6795,19 +7196,22 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff clear_value.color.float32[2] = 0; clear_value.color.float32[3] = 0; } - clear_values.write[i] = clear_value; + clear_values.write[clear_values_count++] = clear_value; } } - render_pass_begin.clearValueCount = clear_values.size(); + render_pass_begin.clearValueCount = clear_values_count; render_pass_begin.pClearValues = clear_values.ptr(); for (int i = 0; i < p_storage_textures.size(); i++) { Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); + if (!texture) { + continue; + } 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; @@ -6835,13 +7239,16 @@ 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; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + if (!texture) { + continue; + } texture->bound = true; draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); } @@ -6852,13 +7259,18 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff 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) { 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]); - VkClearAttachment clear_at = {}; + if (!texture) { + texture_index++; + continue; + } + + VkClearAttachment clear_at = {}; if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX(color_index, p_clear_colors.size()); //a bug - Color clear_color = p_clear_colors[color_index]; + Color clear_color = p_clear_colors[texture_index++]; clear_at.clearValue.color.float32[0] = clear_color.r; clear_at.clearValue.color.float32[1] = clear_color.g; clear_at.clearValue.color.float32[2] = clear_color.b; @@ -6904,7 +7316,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) && @@ -6933,19 +7345,18 @@ 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->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + // 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))) { 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) + ")."); + 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) + ")."); } VkFramebuffer vkframebuffer; @@ -6998,6 +7409,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); @@ -7009,7 +7423,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) && @@ -7031,13 +7445,13 @@ 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++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { color_count++; } } @@ -7117,7 +7531,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; @@ -7143,7 +7557,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; @@ -7152,17 +7566,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]; @@ -7171,11 +7585,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; @@ -7188,7 +7602,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; @@ -7218,8 +7632,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; @@ -7237,7 +7651,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(); @@ -7263,7 +7677,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; @@ -7287,7 +7701,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; @@ -7339,30 +7753,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) { @@ -7377,7 +7791,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; } @@ -7392,7 +7806,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices "Draw command requested indices, but no index buffer was set."); if (dl->validation.pipeline_vertex_format != INVALID_ID) { - //uses vertices, do some vertex validations + // 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) + ")."); } @@ -7518,7 +7932,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) { @@ -7545,7 +7959,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; @@ -7564,7 +7978,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; @@ -7574,7 +7988,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; @@ -7607,7 +8021,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); @@ -7627,12 +8041,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_ } @@ -7647,7 +8061,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; } @@ -7719,12 +8133,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; @@ -7736,6 +8146,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 } @@ -7747,7 +8159,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); @@ -7767,7 +8179,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; @@ -7776,17 +8188,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]; @@ -7795,11 +8207,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; @@ -7815,7 +8227,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 @@ -7843,8 +8255,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; @@ -7947,7 +8359,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. } } @@ -7960,7 +8372,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(); @@ -8020,18 +8432,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) { @@ -8046,7 +8458,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; } @@ -8072,7 +8484,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."); } @@ -8090,7 +8502,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."); @@ -8103,18 +8515,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) { @@ -8129,7 +8541,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; } @@ -8178,37 +8590,34 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) { uint32_t barrier_idx = 0; - for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) { + for (Texture *E : compute_list->state.textures_to_sampled_layout) { VkImageMemoryBarrier &image_memory_barrier = image_barriers[barrier_idx++]; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; image_memory_barrier.dstAccessMask = access_flags; - image_memory_barrier.oldLayout = E->get()->layout; + image_memory_barrier.oldLayout = E->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - image_memory_barrier.image = E->get()->image; - image_memory_barrier.subresourceRange.aspectMask = E->get()->read_aspect_mask; - image_memory_barrier.subresourceRange.baseMipLevel = E->get()->base_mipmap; - image_memory_barrier.subresourceRange.levelCount = E->get()->mipmaps; - image_memory_barrier.subresourceRange.baseArrayLayer = E->get()->base_layer; - image_memory_barrier.subresourceRange.layerCount = E->get()->layers; + image_memory_barrier.image = E->image; + image_memory_barrier.subresourceRange.aspectMask = E->read_aspect_mask; + image_memory_barrier.subresourceRange.baseMipLevel = E->base_mipmap; + image_memory_barrier.subresourceRange.levelCount = E->mipmaps; + image_memory_barrier.subresourceRange.baseArrayLayer = E->base_layer; + image_memory_barrier.subresourceRange.layerCount = E->layers; - E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + E->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - if (E->get()->used_in_frame != frames_drawn) { - E->get()->used_in_transfer = false; - E->get()->used_in_raster = false; - E->get()->used_in_compute = false; - E->get()->used_in_frame = frames_drawn; + if (E->used_in_frame != frames_drawn) { + E->used_in_transfer = false; + E->used_in_raster = false; + E->used_in_compute = false; + E->used_in_frame = frames_drawn; } } -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else VkMemoryBarrier mem_barrier; mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; mem_barrier.pNext = nullptr; @@ -8219,12 +8628,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_ } @@ -8317,7 +8728,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; } @@ -8334,7 +8745,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); @@ -8342,6 +8761,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); @@ -8398,30 +8822,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); @@ -8460,7 +8888,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) { @@ -8478,6 +8910,7 @@ void RenderingDeviceVulkan::draw_command_end_label() { String RenderingDeviceVulkan::get_device_vendor_name() const { return context->get_device_vendor_name(); } + String RenderingDeviceVulkan::get_device_name() const { return context->get_device_name(); } @@ -8486,6 +8919,10 @@ RenderingDevice::DeviceType RenderingDeviceVulkan::get_device_type() const { return context->get_device_type(); } +String RenderingDeviceVulkan::get_device_api_version() const { + return context->get_device_api_version(); +} + String RenderingDeviceVulkan::get_device_pipeline_cache_uuid() const { return context->get_device_pipeline_cache_uuid(); } @@ -8499,17 +8936,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; @@ -8528,20 +8965,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); @@ -8559,7 +8996,7 @@ void RenderingDeviceVulkan::swap_buffers() { _finalize_command_bufers(); screen_prepared = false; - //swap buffers + // Swap buffers. context->swap_buffers(); frame = (frame + 1) % frame_count; @@ -8587,9 +9024,33 @@ void RenderingDeviceVulkan::sync() { local_device_processing = false; } +VmaPool RenderingDeviceVulkan::_find_or_create_small_allocs_pool(uint32_t p_mem_type_index) { + if (small_allocs_pools.has(p_mem_type_index)) { + return small_allocs_pools[p_mem_type_index]; + } + + print_verbose("Creating VMA small objects pool for memory type index " + itos(p_mem_type_index)); + + VmaPoolCreateInfo pci; + pci.memoryTypeIndex = p_mem_type_index; + pci.flags = 0; + pci.blockSize = 0; + pci.minBlockCount = 0; + pci.maxBlockCount = SIZE_MAX; + pci.priority = 0.5f; + pci.minAllocationAlignment = 0; + 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. + 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(); @@ -8606,7 +9067,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(); @@ -8616,7 +9077,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(); @@ -8625,19 +9086,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); } @@ -8645,7 +9106,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(); @@ -8654,12 +9115,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); } @@ -8667,7 +9128,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(); @@ -8676,14 +9137,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()); @@ -8707,17 +9168,17 @@ uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const { } else if (p_type == MEMORY_TEXTURES) { return image_memory; } else { - VmaStats stats; - vmaCalculateStats(allocator, &stats); - return stats.total.usedBytes; + VmaTotalStatistics stats; + vmaCalculateStatistics(allocator, &stats); + return stats.total.statistics.allocationBytes; } } 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); @@ -8741,7 +9202,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; @@ -8751,7 +9212,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) { @@ -8769,21 +9230,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; @@ -8793,12 +9243,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)); @@ -8808,13 +9258,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; @@ -8825,10 +9275,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; @@ -8843,7 +9293,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; @@ -8854,19 +9304,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; @@ -8879,42 +9329,42 @@ 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 + // 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 = MAX(4, staging_buffer_block_size); - staging_buffer_block_size *= 1024; //kb -> bytes + 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_max_size = MAX(1, staging_buffer_max_size); + 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 = 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); - //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; @@ -8935,6 +9385,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); } } @@ -8944,7 +9399,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; @@ -8996,49 +9451,49 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R switch (p_resource) { case DRIVER_RESOURCE_VULKAN_DEVICE: { return (uint64_t)context->get_device(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: { return (uint64_t)context->get_physical_device(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_INSTANCE: { return (uint64_t)context->get_instance(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_QUEUE: { return (uint64_t)context->get_graphics_queue(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: { return context->get_graphics_queue_family_index(); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_IMAGE: { Texture *tex = texture_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(tex, 0); return (uint64_t)tex->image; - }; break; + } break; case DRIVER_RESOURCE_VULKAN_IMAGE_VIEW: { Texture *tex = texture_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(tex, 0); return (uint64_t)tex->view; - }; break; + } break; case DRIVER_RESOURCE_VULKAN_IMAGE_NATIVE_TEXTURE_FORMAT: { Texture *tex = texture_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(tex, 0); return vulkan_formats[tex->format]; - }; break; + } break; case DRIVER_RESOURCE_VULKAN_SAMPLER: { VkSampler *sampler = sampler_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(sampler, 0); return uint64_t(*sampler); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_DESCRIPTOR_SET: { UniformSet *uniform_set = uniform_set_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(uniform_set, 0); return uint64_t(uniform_set->descriptor_set); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_BUFFER: { Buffer *buffer = nullptr; if (vertex_buffer_owner.owns(p_rid)) { @@ -9056,23 +9511,23 @@ uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, R ERR_FAIL_NULL_V(buffer, 0); return uint64_t(buffer->buffer); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_COMPUTE_PIPELINE: { ComputePipeline *compute_pipeline = compute_pipeline_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(compute_pipeline, 0); return uint64_t(compute_pipeline->pipeline); - }; break; + } break; case DRIVER_RESOURCE_VULKAN_RENDER_PIPELINE: { RenderPipeline *render_pipeline = render_pipeline_owner.get_or_null(p_rid); ERR_FAIL_NULL_V(render_pipeline, 0); return uint64_t(render_pipeline->pipeline); - }; break; + } break; default: { - // not supported for this driver + // Not supported for this driver. return 0; - }; break; + } break; } } @@ -9107,9 +9562,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; @@ -9132,7 +9587,7 @@ String RenderingDeviceVulkan::get_captured_timestamp_name(uint32_t p_index) cons return frames[frame].timestamp_result_names[p_index]; } -int 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; @@ -9202,7 +9657,18 @@ int RenderingDeviceVulkan::limit_get(Limit p_limit) { return limits.maxComputeWorkGroupSize[1]; case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; - + 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); } @@ -9211,7 +9677,7 @@ int RenderingDeviceVulkan::limit_get(Limit p_limit) { } void RenderingDeviceVulkan::finalize() { - //free all resources + // Free all resources. _flush(false); @@ -9229,7 +9695,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()) { @@ -9238,59 +9704,69 @@ 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); } + while (small_allocs_pools.size()) { + HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin(); + vmaDestroyPool(allocator, E->value); + small_allocs_pools.remove(E); + } vmaDestroyAllocator(allocator); while (vertex_formats.size()) { - Map<VertexFormatID, VertexDescriptionCache>::Element *temp = vertex_formats.front(); - memdelete_arr(temp->get().bindings); - memdelete_arr(temp->get().attributes); - vertex_formats.erase(temp); + HashMap<VertexFormatID, VertexDescriptionCache>::Iterator temp = vertex_formats.begin(); + memdelete_arr(temp->value.bindings); + memdelete_arr(temp->value.attributes); + vertex_formats.remove(temp); } - for (int i = 0; i < framebuffer_formats.size(); i++) { - vkDestroyRenderPass(device, framebuffer_formats[i].render_pass, nullptr); + for (KeyValue<FramebufferFormatID, FramebufferFormat> &E : framebuffer_formats) { + vkDestroyRenderPass(device, E.value.render_pass, nullptr); } 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()); @@ -9302,6 +9778,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 f42929ffa4..6572de7c52 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; - Map<RID, Set<RID>> dependency_map; //IDs to IDs that depend on it - Map<RID, Set<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); @@ -152,7 +152,7 @@ class RenderingDeviceVulkan : public RenderingDevice { 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 +206,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,12 +214,12 @@ 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() { } }; - Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping); + Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mem_usage, VmaAllocationCreateFlags p_mem_flags); Error _buffer_free(Buffer *p_buffer); Error _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 = false, uint32_t p_required_align = 32); @@ -241,6 +241,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 +256,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 +271,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 +286,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 +301,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,22 +343,22 @@ class RenderingDeviceVulkan : public RenderingDevice { } } - return false; //equal + return false; // Equal. } }; VkRenderPass _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 = 1, Vector<TextureSamples> *r_samples = nullptr); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. - Map<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; + RBMap<FramebufferFormatKey, FramebufferFormatID> framebuffer_format_cache; struct FramebufferFormat { - const Map<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) + 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). Vector<TextureSamples> pass_samples; - uint32_t view_count = 1; // number of views + uint32_t view_count = 1; // Number of views. }; - Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; + HashMap<FramebufferFormatID, FramebufferFormat> framebuffer_formats; struct Framebuffer { FramebufferFormatID format_id = 0; @@ -391,14 +392,16 @@ 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; }; - Map<VersionKey, Version> framebuffers; + RBMap<VersionKey, Version> framebuffers; Size2 size; uint32_t view_count; }; @@ -451,23 +454,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); } }; @@ -488,7 +491,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkPipelineVertexInputStateCreateInfo create_info; }; - Map<VertexFormatID, VertexDescriptionCache> vertex_formats; + HashMap<VertexFormatID, VertexDescriptionCache> vertex_formats; struct VertexArray { RID buffer; @@ -496,14 +499,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 +515,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 +547,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 +563,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; } @@ -592,7 +599,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // Always grows, never shrinks, ensuring unique IDs, but we assume // the amount of formats will never be a problem, as the amount of shaders // in a game is limited. - Map<UniformSetFormat, uint32_t> uniform_set_format_cache; + RBMap<UniformSetFormat, uint32_t> uniform_set_format_cache; // Shaders in Vulkan are just pretty much // precompiled blocks of SPIR-V bytecode. They @@ -615,7 +622,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 +640,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); @@ -702,7 +708,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t usage; }; - Map<DescriptorPoolKey, Set<DescriptorPool *>> descriptor_pools; + RBMap<DescriptorPoolKey, HashSet<DescriptorPool *>> descriptor_pools; uint32_t max_descriptors_per_pool = 0; DescriptorPool *_descriptor_pool_allocate(const DescriptorPoolKey &p_key); @@ -711,7 +717,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 +740,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 +771,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 +783,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 +797,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 +823,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 +872,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; @@ -886,8 +890,8 @@ class RenderingDeviceVulkan : public RenderingDevice { DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. uint32_t draw_list_subpass_count = 0; uint32_t draw_list_count = 0; - VkRenderPass draw_list_render_pass; - VkFramebuffer draw_list_vkframebuffer; + VkRenderPass draw_list_render_pass = VK_NULL_HANDLE; + VkFramebuffer draw_list_vkframebuffer = VK_NULL_HANDLE; #ifdef DEBUG_ENABLED FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; #endif @@ -923,7 +927,7 @@ class RenderingDeviceVulkan : public RenderingDevice { }; struct State { - Set<Texture *> textures_to_sampled_layout; + HashSet<Texture *> textures_to_sampled_layout; SetState sets[MAX_UNIFORM_SETS]; uint32_t set_count = 0; RID pipeline; @@ -944,7 +948,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 +975,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 +987,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 +997,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; @@ -1016,6 +1019,8 @@ class RenderingDeviceVulkan : public RenderingDevice { void _free_pending_resources(int p_frame); VmaAllocator allocator = nullptr; + HashMap<uint32_t, VmaPool> small_allocs_pools; + VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index); VulkanContext *context = nullptr; @@ -1033,9 +1038,14 @@ class RenderingDeviceVulkan : public RenderingDevice { void _finalize_command_bufers(); void _begin_frame(); +#ifdef DEV_ENABLED + HashMap<RID, String> resource_names; +#endif + 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); + virtual RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, uint64_t p_flags, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D); virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL); @@ -1055,13 +1065,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); @@ -1077,7 +1089,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); @@ -1106,9 +1118,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); @@ -1200,16 +1212,16 @@ public: /**** Limits ****/ /****************/ - virtual int 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; @@ -1226,10 +1238,13 @@ public: virtual String get_device_vendor_name() const; virtual String get_device_name() const; virtual RenderingDevice::DeviceType get_device_type() const; + virtual String get_device_api_version() const; virtual String get_device_pipeline_cache_uuid() const; 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 1aa1bfddc8..afc3e78372 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -46,6 +46,20 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define APP_SHORT_NAME "GodotEngine" +VulkanHooks *VulkanContext::vulkan_hooks = nullptr; + +VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) { + if (fpCreateRenderPass2KHR == nullptr) { + fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr(inst, "vkCreateRenderPass2KHR"); + } + + if (fpCreateRenderPass2KHR == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } else { + return (fpCreateRenderPass2KHR)(device, pCreateInfo, pAllocator, pRenderPass); + } +} + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -70,20 +84,20 @@ 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; } + +#ifdef WINDOWS_ENABLED + // Some software installs Vulkan overlays in Windows registry and never cleans them up on uninstall. + // So we get spammy error level messages from the loader about those - make them verbose instead. + if (strstr(pCallbackData->pMessage, "loader_get_json: Failed to open JSON file") != nullptr) { + messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + } +#endif + if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { return VK_FALSE; } @@ -213,17 +227,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; @@ -267,7 +281,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; @@ -277,15 +291,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; @@ -301,7 +315,7 @@ 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)); @@ -401,7 +415,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"; } @@ -427,7 +441,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 { @@ -492,19 +506,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; @@ -514,82 +531,142 @@ Error VulkanContext::_check_capabilities() { subgroup_capabilities.supportedStages = 0; subgroup_capabilities.supportedOperations = 0; subgroup_capabilities.quadOperationsInAllStages = false; + shader_capabilities.shader_float16_is_supported = false; + shader_capabilities.shader_int8_is_supported = false; + storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = false; + storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = false; + storage_buffer_capabilities.storage_push_constant_16_is_supported = false; + storage_buffer_capabilities.storage_input_output_16 = false; + + // 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. + vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); + } + if (vkGetPhysicalDeviceFeatures2_func != nullptr) { + // 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, + }; - // check for extended features - PFN_vkGetPhysicalDeviceFeatures2 device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2"); - if (device_features_func == nullptr) { - // In Vulkan 1.0 might be accessible under its original extension name - device_features_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR"); - } - if (device_features_func != nullptr) { - // check our extended features - VkPhysicalDeviceMultiviewFeatures multiview_features; - multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - multiview_features.pNext = nullptr; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + /*pNext*/ &vrs_features, + /*shaderFloat16*/ false, + /*shaderInt8*/ false, + }; + + VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, + /*pNext*/ &shader_features, + /*storageBuffer16BitAccess*/ false, + /*uniformAndStorageBuffer16BitAccess*/ false, + /*storagePushConstant16*/ false, + /*storageInputOutput16*/ false, + }; + + VkPhysicalDeviceMultiviewFeatures multiview_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + /*pNext*/ &storage_feature, + /*multiview*/ false, + /*multiviewGeometryShader*/ false, + /*multiviewTessellationShader*/ false, + }; VkPhysicalDeviceFeatures2 device_features; device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; device_features.pNext = &multiview_features; - device_features_func(gpu, &device_features); + vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); + + vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; + vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; + vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; + 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; - VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features; - shader_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; - shader_features.pNext = nullptr; - - device_features.pNext = &shader_features; - - device_features_func(gpu, &device_features); shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; + shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; - VkPhysicalDevice16BitStorageFeaturesKHR storage_feature; - storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; - storage_feature.pNext = nullptr; - - device_features.pNext = &storage_feature; - - device_features_func(gpu, &device_features); 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; } - // 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) { + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties; VkPhysicalDeviceMultiviewProperties multiviewProperties; VkPhysicalDeviceSubgroupProperties subgroupProperties; VkPhysicalDeviceProperties2 physicalDeviceProperties; + void *nextptr = nullptr; subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nullptr; - - physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + subgroupProperties.pNext = nextptr; + 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; @@ -616,10 +693,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) { @@ -654,7 +731,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; @@ -678,19 +755,27 @@ Error VulkanContext::_create_instance() { inst_info.pNext = &dbg_report_callback_create_info; } - VkResult err = vkCreateInstance(&inst_info, nullptr, &inst); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, - "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, - "Cannot find a specified extension library.\n" - "Make sure your layers path is set appropriately.\n" - "vkCreateInstance Failure"); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, - "vkCreateInstance failed.\n\n" - "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" - "Please look at the Getting Started guide for additional information.\n" - "vkCreateInstance Failure"); + VkResult err; + + if (vulkan_hooks) { + if (!vulkan_hooks->create_vulkan_instance(&inst_info, &inst)) { + return ERR_CANT_CREATE; + } + } else { + err = vkCreateInstance(&inst_info, nullptr, &inst); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, + "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, + "Cannot find a specified extension library.\n" + "Make sure your layers path is set appropriately.\n" + "vkCreateInstance Failure"); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, + "vkCreateInstance failed.\n\n" + "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" + "Please look at the Getting Started guide for additional information.\n" + "vkCreateInstance Failure"); + } inst_initialized = true; @@ -699,8 +784,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 = @@ -773,7 +857,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); @@ -803,116 +887,131 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { { 0, nullptr }, }; - // 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. - int32_t device_index = -1; - int type_selected = -1; - print_verbose("Vulkan devices:"); - for (uint32_t i = 0; i < gpu_count; ++i) { - VkPhysicalDeviceProperties props; - vkGetPhysicalDeviceProperties(physical_devices[i], &props); - - bool present_supported = false; - - uint32_t device_queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); - VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); - vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); - for (uint32_t j = 0; j < device_queue_family_count; j++) { - VkBool32 supports; - vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports); - if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) { - present_supported = true; - } else { - continue; - } - } - String name = props.deviceName; - String vendor = "Unknown"; - String dev_type; - switch (props.deviceType) { - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { - dev_type = "Discrete"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { - dev_type = "Integrated"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { - dev_type = "Virtual"; - } break; - case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { - dev_type = "CPU"; - } break; - default: { - dev_type = "Other"; - } break; + if (vulkan_hooks) { + if (!vulkan_hooks->get_physical_device(&gpu)) { + return ERR_CANT_CREATE; } - uint32_t vendor_idx = 0; - while (vendor_names[vendor_idx].name != nullptr) { - if (props.vendorID == vendor_names[vendor_idx].id) { - vendor = vendor_names[vendor_idx].name; + + // 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; break; } - vendor_idx++; } - free(device_queue_props); - print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); - - if (present_supported) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. + } 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. + + int type_selected = -1; + print_verbose("Vulkan devices:"); + for (uint32_t i = 0; i < gpu_count; ++i) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(physical_devices[i], &props); + + bool present_supported = false; + + uint32_t device_queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); + VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); + for (uint32_t j = 0; j < device_queue_family_count; j++) { + VkBool32 supports; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, p_surface, &supports); + if (supports && ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)) { + present_supported = true; + } else { + continue; + } + } + String name = props.deviceName; + String vendor = "Unknown"; + String dev_type; switch (props.deviceType) { case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { - if (type_selected < 4) { - type_selected = 4; - device_index = i; - } + dev_type = "Discrete"; } break; case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { - if (type_selected < 3) { - type_selected = 3; - device_index = i; - } + dev_type = "Integrated"; } break; case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { - if (type_selected < 2) { - type_selected = 2; - device_index = i; - } + dev_type = "Virtual"; } break; case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { - if (type_selected < 1) { - type_selected = 1; - device_index = i; - } + dev_type = "CPU"; } break; default: { - if (type_selected < 0) { - type_selected = 0; - device_index = i; - } + dev_type = "Other"; } break; } + uint32_t vendor_idx = 0; + while (vendor_names[vendor_idx].name != nullptr) { + if (props.vendorID == vendor_names[vendor_idx].id) { + vendor = vendor_names[vendor_idx].name; + break; + } + vendor_idx++; + } + free(device_queue_props); + print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported") + ", " + dev_type); + + if (present_supported) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. + switch (props.deviceType) { + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { + if (type_selected < 4) { + type_selected = 4; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { + if (type_selected < 3) { + type_selected = 3; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { + if (type_selected < 2) { + type_selected = 2; + device_index = i; + } + } break; + case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { + if (type_selected < 1) { + type_selected = 1; + device_index = i; + } + } break; + default: { + if (type_selected < 0) { + type_selected = 0; + device_index = i; + } + } break; + } + } } - } - int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. - if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) { - device_index = user_device_index; - } + int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. + if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) { + device_index = user_device_index; + } - ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues."); + ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues."); + + gpu = physical_devices[device_index]; + } - gpu = physical_devices[device_index]; 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; @@ -954,9 +1053,16 @@ 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)) { + 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"); @@ -1007,19 +1113,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) \ { \ @@ -1034,7 +1139,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) { @@ -1057,9 +1162,73 @@ Error VulkanContext::_create_device() { queues[0].pQueuePriorities = queue_priorities; queues[0].flags = 0; + // Before we retrieved what is supported, here we tell Vulkan we want to enable these features using the same structs. + void *nextptr = nullptr; + + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { + /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + /*pNext*/ nextptr, + /*shaderFloat16*/ shader_capabilities.shader_float16_is_supported, + /*shaderInt8*/ shader_capabilities.shader_int8_is_supported, + }; + 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. + + vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vulkan11features.pNext = nextptr; + vulkan11features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; + vulkan11features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + vulkan11features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + vulkan11features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + vulkan11features.multiview = multiview_capabilities.is_supported; + vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + vulkan11features.variablePointersStorageBuffer = 0; + vulkan11features.variablePointers = 0; + vulkan11features.protectedMemory = 0; + vulkan11features.samplerYcbcrConversion = 0; + vulkan11features.shaderDrawParameters = 0; + nextptr = &vulkan11features; + } else { + // On Vulkan 1.0 and 1.1 we use our older structs to initialise 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; + storage_feature.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; + storage_feature.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; + storage_feature.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; + nextptr = &storage_feature; + + if (vulkan_major == 1 && vulkan_minor == 1) { + multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + multiview_features.pNext = nextptr; + multiview_features.multiview = multiview_capabilities.is_supported; + multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; + multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; + nextptr = &multiview_features; + } + } + VkDeviceCreateInfo sdevice = { /*sType*/ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - /*pNext*/ nullptr, + /*pNext*/ nextptr, /*flags*/ 0, /*queueCreateInfoCount*/ 1, /*pQueueCreateInfos*/ queues, @@ -1067,8 +1236,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; @@ -1080,39 +1248,15 @@ Error VulkanContext::_create_device() { sdevice.queueCreateInfoCount = 2; } - VkPhysicalDeviceVulkan11Features vulkan11features; - VkPhysicalDeviceMultiviewFeatures multiview_features; - if (vulkan_major > 1 || vulkan_minor >= 2) { - vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; - vulkan11features.pNext = nullptr; - // !BAS! Need to figure out which ones of these we want enabled... - vulkan11features.storageBuffer16BitAccess = 0; - vulkan11features.uniformAndStorageBuffer16BitAccess = 0; - vulkan11features.storagePushConstant16 = 0; - vulkan11features.storageInputOutput16 = 0; - vulkan11features.multiview = multiview_capabilities.is_supported; - vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; - vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; - vulkan11features.variablePointersStorageBuffer = 0; - vulkan11features.variablePointers = 0; - vulkan11features.protectedMemory = 0; - vulkan11features.samplerYcbcrConversion = 0; - vulkan11features.shaderDrawParameters = 0; - - sdevice.pNext = &vulkan11features; - } else if (vulkan_major == 1 && vulkan_minor == 1) { - multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - multiview_features.pNext = nullptr; - multiview_features.multiview = multiview_capabilities.is_supported; - multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; - multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; - - sdevice.pNext = &multiview_features; + if (vulkan_hooks) { + if (!vulkan_hooks->create_vulkan_device(&sdevice, &device)) { + return ERR_CANT_CREATE; + } + } else { + err = vkCreateDevice(gpu, &sdevice, nullptr, &device); + ERR_FAIL_COND_V(err, ERR_CANT_CREATE); } - err = vkCreateDevice(gpu, &sdevice, nullptr, &device); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return OK; } @@ -1124,7 +1268,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++) { @@ -1154,7 +1298,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"); @@ -1210,7 +1354,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 normalised buffer or similar. const VkFormat allowed_formats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM @@ -1222,7 +1366,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++) { @@ -1254,7 +1398,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, @@ -1262,7 +1406,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, @@ -1282,7 +1426,7 @@ Error VulkanContext::_create_semaphores() { } frame_index = 0; - // Get Memory information and properties + // Get Memory information and properties. vkGetPhysicalDeviceMemoryProperties(gpu, &memory_properties); return OK; @@ -1348,10 +1492,16 @@ int VulkanContext::window_get_height(DisplayServer::WindowID p_window) { return windows[p_window].height; } +bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) { + ERR_FAIL_COND_V(!windows.has(p_window), false); + Window *w = &windows[p_window]; + return w->swapchain_image_resources != VK_NULL_HANDLE; +} + 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; } @@ -1359,8 +1509,12 @@ 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 - return w->swapchain_image_resources[w->current_buffer].framebuffer; + // Vulkan use of currentbuffer. + if (w->swapchain_image_resources != VK_NULL_HANDLE) { + return w->swapchain_image_resources[w->current_buffer].framebuffer; + } else { + return VK_NULL_HANDLE; + } } void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { @@ -1380,7 +1534,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); @@ -1406,7 +1560,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); @@ -1423,7 +1577,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { } VkExtent2D swapchainExtent; - // width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. + // 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 @@ -1443,7 +1597,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { swapchainExtent.height = surfCapabilities.maxImageExtent.height; } } else { - // If the surface size is defined, the swap chain size must match + // 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; @@ -1451,23 +1605,23 @@ Error VulkanContext::_update_swap_chain(Window *window) { 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 @@ -1512,8 +1666,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))); @@ -1522,15 +1691,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; } @@ -1541,7 +1710,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set + // 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, @@ -1588,7 +1757,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); @@ -1646,7 +1815,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, @@ -1658,14 +1829,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, @@ -1675,8 +1852,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, @@ -1685,9 +1863,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++) { @@ -1760,7 +1940,7 @@ Error VulkanContext::_update_swap_chain(Window *window) { } } - //reset current buffer + // Reset current buffer. window->current_buffer = 0; return OK; @@ -1781,27 +1961,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; @@ -1810,33 +1993,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() { @@ -1846,7 +2029,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]); @@ -1860,19 +2043,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; @@ -1900,7 +2083,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 @@ -1919,7 +2102,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; @@ -1930,24 +2113,25 @@ Error VulkanContext::swap_buffers() { } VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); + VkPipelineStageFlags *pipe_stage_flags = (VkPipelineStageFlags *)alloca(windows.size() * sizeof(VkPipelineStageFlags)); uint32_t semaphores_to_acquire_count = 0; for (KeyValue<int, Window> &E : windows) { Window *w = &E.value; if (w->semaphore_acquired) { - semaphores_to_acquire[semaphores_to_acquire_count++] = w->image_acquired_semaphores[frame_index]; + semaphores_to_acquire[semaphores_to_acquire_count] = w->image_acquired_semaphores[frame_index]; + pipe_stage_flags[semaphores_to_acquire_count] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + semaphores_to_acquire_count++; } } - VkPipelineStageFlags pipe_stage_flags; VkSubmitInfo submit_info; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; - submit_info.pWaitDstStageMask = &pipe_stage_flags; - pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = semaphores_to_acquire_count; submit_info.pWaitSemaphores = semaphores_to_acquire; + submit_info.pWaitDstStageMask = pipe_stage_flags; submit_info.commandBufferCount = commands_to_submit; submit_info.pCommandBuffers = commands_ptr; submit_info.signalSemaphoreCount = 1; @@ -1961,9 +2145,9 @@ 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 = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &draw_complete_semaphores[frame_index]; submit_info.commandBufferCount = 0; @@ -1988,7 +2172,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, @@ -2062,7 +2246,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); @@ -2093,12 +2277,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 { @@ -2143,7 +2327,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]; @@ -2164,13 +2348,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); } @@ -2232,7 +2416,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; @@ -2248,7 +2432,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; @@ -2284,6 +2468,7 @@ void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_objec String VulkanContext::get_device_vendor_name() const { return device_vendor; } + String VulkanContext::get_device_name() const { return device_name; } @@ -2292,17 +2477,21 @@ RenderingDevice::DeviceType VulkanContext::get_device_type() const { return RenderingDevice::DeviceType(device_type); } +String VulkanContext::get_device_api_version() const { + return vformat("%d.%d.%d", vulkan_major, vulkan_minor, vulkan_patch); +} + String VulkanContext::get_device_pipeline_cache_uuid() const { return pipeline_cache_id; } 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 67a675f6c6..5cc3b515d9 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -34,7 +34,7 @@ #include "core/error/error_list.h" #include "core/os/mutex.h" #include "core/string/ustring.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "core/templates/rid_owner.h" #include "servers/display_server.h" #include "servers/rendering/rendering_device.h" @@ -45,6 +45,8 @@ #include <vulkan/vulkan.h> #endif +#include "vulkan_hooks.h" + class VulkanContext { public: struct SubgroupCapabilities { @@ -67,12 +69,25 @@ 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; }; struct StorageBufferCapabilities { bool storage_buffer_16_bit_access_is_supported; + bool uniform_and_storage_buffer_16_bit_access_is_supported; + bool storage_push_constant_16_is_supported; + bool storage_input_output_16; }; private: @@ -82,6 +97,7 @@ private: FRAME_LAG = 2 }; + static VulkanHooks *vulkan_hooks; VkInstance inst = VK_NULL_HANDLE; VkPhysicalDevice gpu = VK_NULL_HANDLE; VkPhysicalDeviceProperties gpu_props; @@ -91,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; @@ -110,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; @@ -154,7 +171,7 @@ private: RID_Owner<LocalDevice, true> local_device_owner; - Map<DisplayServer::WindowID, Window> windows; + HashMap<DisplayServer::WindowID, Window> windows; uint32_t swapchainImageCount = 0; // Commands. @@ -178,27 +195,28 @@ private: */ bool enabled_debug_report = false; - PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT; - PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT; - PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT; - PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT; - PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT; - PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT; - PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT; - PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; - PFN_vkDebugReportMessageEXT DebugReportMessageEXT; - PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; - PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; - PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; - PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; - PFN_vkQueuePresentKHR fpQueuePresentKHR; - PFN_vkGetRefreshCycleDurationGOOGLE fpGetRefreshCycleDurationGOOGLE; - PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE; + PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT = nullptr; + PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT = nullptr; + PFN_vkSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT = nullptr; + PFN_vkCmdBeginDebugUtilsLabelEXT CmdBeginDebugUtilsLabelEXT = nullptr; + PFN_vkCmdEndDebugUtilsLabelEXT CmdEndDebugUtilsLabelEXT = nullptr; + PFN_vkCmdInsertDebugUtilsLabelEXT CmdInsertDebugUtilsLabelEXT = nullptr; + PFN_vkSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT = nullptr; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR = nullptr; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR = nullptr; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR = nullptr; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR = nullptr; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR = nullptr; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR = nullptr; + 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; @@ -249,12 +267,16 @@ protected: Error _get_preferred_validation_layers(uint32_t *count, const char *const **names); public: + // Extension calls. + VkResult vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass); + 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(); @@ -263,9 +285,12 @@ public: VkQueue get_graphics_queue() const; uint32_t get_graphics_queue_family_index() const; + static void set_vulkan_hooks(VulkanHooks *p_vulkan_hooks) { vulkan_hooks = p_vulkan_hooks; }; + void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); int window_get_width(DisplayServer::WindowID p_window = 0); int window_get_height(DisplayServer::WindowID p_window = 0); + bool window_is_valid_swapchain(DisplayServer::WindowID p_window = 0); void window_destroy(DisplayServer::WindowID p_window_id); VkFramebuffer window_get_framebuffer(DisplayServer::WindowID p_window = 0); VkRenderPass window_get_render_pass(DisplayServer::WindowID p_window = 0); @@ -279,8 +304,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(); @@ -295,6 +320,7 @@ public: String get_device_vendor_name() const; String get_device_name() const; RenderingDevice::DeviceType get_device_type() const; + String get_device_api_version() const; String get_device_pipeline_cache_uuid() const; void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode); @@ -304,4 +330,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 new file mode 100644 index 0000000000..dec2042d0d --- /dev/null +++ b/drivers/vulkan/vulkan_hooks.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* vulkan_hooks.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 VULKAN_HOOKS_H +#define VULKAN_HOOKS_H + +#ifdef USE_VOLK +#include <volk.h> +#else +#include <vulkan/vulkan.h> +#endif + +class VulkanHooks { +public: + virtual bool create_vulkan_instance(const VkInstanceCreateInfo *p_vulkan_create_info, VkInstance *r_instance) { return false; }; + virtual bool get_physical_device(VkPhysicalDevice *r_device) { return false; }; + virtual bool create_vulkan_device(const VkDeviceCreateInfo *p_device_create_info, VkDevice *r_device) { return false; }; + virtual ~VulkanHooks(){}; +}; + +#endif // VULKAN_HOOKS_H diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index c7a2d04436..3a62850339 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -454,8 +454,9 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c Error AudioDriverWASAPI::init_render_device(bool reinit) { Error err = audio_device_init(&audio_output, false, reinit); - if (err != OK) + if (err != OK) { return err; + } switch (audio_output.channels) { case 2: // Stereo @@ -485,8 +486,9 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { Error AudioDriverWASAPI::init_capture_device(bool reinit) { Error err = audio_device_init(&audio_input, true, reinit); - if (err != OK) + if (err != OK) { return err; + } // Get the max frames UINT32 max_frames; @@ -678,7 +680,7 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE } void AudioDriverWASAPI::thread_func(void *p_udata) { - AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata; + AudioDriverWASAPI *ad = static_cast<AudioDriverWASAPI *>(p_udata); uint32_t avail_frames = 0; uint32_t write_ofs = 0; diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 89ed90e97b..9058077a1f 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -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 6f3bad12c1..881575d245 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -185,7 +185,7 @@ Error DirAccessWindows::make_dir(String p_dir) { return ERR_CANT_CREATE; } -String DirAccessWindows::get_current_dir(bool p_include_drive) { +String DirAccessWindows::get_current_dir(bool p_include_drive) const { String base = _get_root_path(); if (!base.is_empty()) { String bd = current_dir.replace("\\", "/").replace_first(base, ""); diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h index 78d37074e5..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); ///< 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 cfd5d65f60..1a66d19373 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -59,11 +59,10 @@ void FileAccessWindows::check_errors() const { } Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { + _close(); + path_src = p_path; path = fix_path(p_path); - if (f) { - close(); - } const WCHAR *mode_string; @@ -87,7 +86,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { if (!S_ISREG(st.st_mode)) { return ERR_FILE_CANT_OPEN; } - }; + } #ifdef TOOLS_ENABLED // Windows is case insensitive, but all other platforms are sensitive to it @@ -134,7 +133,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { } } -void FileAccessWindows::close() { +void FileAccessWindows::_close() { if (!f) { return; } @@ -269,7 +268,7 @@ uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); return read; -}; +} Error FileAccessWindows::get_error() const { return last_error; @@ -326,8 +325,9 @@ bool FileAccessWindows::file_exists(const String &p_name) { uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - if (file.ends_with("/") && file != "/") + if (file.ends_with("/") && file != "/") { file = file.substr(0, file.length() - 1); + } struct _stat st; int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); @@ -349,7 +349,7 @@ Error FileAccessWindows::_set_unix_permissions(const String &p_file, uint32_t p_ } FileAccessWindows::~FileAccessWindows() { - close(); + _close(); } #endif // WINDOWS_ENABLED diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 93d37c3b5a..8629bb936b 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -48,35 +48,36 @@ class FileAccessWindows : public FileAccess { String path_src; String save_path; + void _close(); + public: - virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual void close(); ///< close a file - virtual bool is_open() const; ///< true when file is open + virtual Error _open(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 03fdfda23d..c32c7cf1e5 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.cpp +++ b/drivers/xaudio2/audio_driver_xaudio2.cpp @@ -84,7 +84,7 @@ Error AudioDriverXAudio2::init() { } void AudioDriverXAudio2::thread_func(void *p_udata) { - AudioDriverXAudio2 *ad = (AudioDriverXAudio2 *)p_udata; + AudioDriverXAudio2 *ad = static_cast<AudioDriverXAudio2 *>(p_udata); while (!ad->exit_thread) { if (!ad->active) { diff --git a/drivers/xaudio2/audio_driver_xaudio2.h b/drivers/xaudio2/audio_driver_xaudio2.h index 9072269a0e..81432ceb8e 100644 --- a/drivers/xaudio2/audio_driver_xaudio2.h +++ b/drivers/xaudio2/audio_driver_xaudio2.h @@ -106,4 +106,4 @@ public: ~AudioDriverXAudio2() {} }; -#endif +#endif // AUDIO_DRIVER_XAUDIO2_H |