summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/SCsub7
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp8
-rw-r--r--drivers/alsa/audio_driver_alsa.h4
-rw-r--r--drivers/alsamidi/midi_driver_alsamidi.cpp4
-rw-r--r--drivers/alsamidi/midi_driver_alsamidi.h4
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp86
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h4
-rw-r--r--drivers/coremidi/midi_driver_coremidi.cpp4
-rw-r--r--drivers/coremidi/midi_driver_coremidi.h4
-rw-r--r--drivers/dummy/rasterizer_dummy.h774
-rw-r--r--drivers/gl_context/SCsub23
-rw-r--r--drivers/gles3/SCsub8
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp1476
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h285
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp363
-rw-r--r--drivers/gles3/rasterizer_gles3.h102
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp475
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h230
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp3156
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h966
-rw-r--r--drivers/gles3/shader_gles3.cpp703
-rw-r--r--drivers/gles3/shader_gles3.h246
-rw-r--r--drivers/gles3/shaders/SCsub7
-rw-r--r--drivers/gles3/shaders/canvas.glsl753
-rw-r--r--drivers/gles3/shaders/canvas_shadow.glsl60
-rw-r--r--drivers/gles3/shaders/canvas_uniforms_inc.glsl120
-rw-r--r--drivers/gles3/shaders/copy.glsl204
-rw-r--r--drivers/gles3/shaders/cube_to_dp.glsl100
-rw-r--r--drivers/gles3/shaders/cubemap_filter.glsl214
-rw-r--r--drivers/gles3/shaders/effect_blur.glsl291
-rw-r--r--drivers/gles3/shaders/lens_distorted.glsl86
-rw-r--r--drivers/gles3/shaders/scene.glsl2153
-rw-r--r--drivers/gles3/shaders/stdlib_inc.glsl58
-rw-r--r--drivers/gles3/shaders/tonemap.glsl313
-rw-r--r--drivers/gles3/storage/SCsub (renamed from drivers/dummy/SCsub)0
-rw-r--r--drivers/gles3/storage/canvas_texture_storage.cpp96
-rw-r--r--drivers/gles3/storage/canvas_texture_storage.h87
-rw-r--r--drivers/gles3/storage/config.cpp156
-rw-r--r--drivers/gles3/storage/config.h113
-rw-r--r--drivers/gles3/storage/decal_atlas_storage.cpp75
-rw-r--r--drivers/gles3/storage/decal_atlas_storage.h67
-rw-r--r--drivers/gles3/storage/render_target_storage.h132
-rw-r--r--drivers/gles3/storage/texture_storage.cpp1211
-rw-r--r--drivers/gles3/storage/texture_storage.h389
-rw-r--r--drivers/gles3/texture_loader_gles3.cpp (renamed from drivers/dummy/texture_loader_dummy.cpp)27
-rw-r--r--drivers/gles3/texture_loader_gles3.h (renamed from drivers/dummy/texture_loader_dummy.h)20
-rw-r--r--drivers/png/SCsub3
-rw-r--r--drivers/png/image_loader_png.cpp14
-rw-r--r--drivers/png/image_loader_png.h4
-rw-r--r--drivers/png/png_driver_common.cpp8
-rw-r--r--drivers/png/png_driver_common.h4
-rw-r--r--drivers/png/resource_saver_png.cpp12
-rw-r--r--drivers/png/resource_saver_png.h4
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp97
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h6
-rw-r--r--drivers/register_driver_types.cpp9
-rw-r--r--drivers/register_driver_types.h4
-rw-r--r--drivers/unix/dir_access_unix.cpp137
-rw-r--r--drivers/unix/dir_access_unix.h15
-rw-r--r--drivers/unix/file_access_unix.cpp52
-rw-r--r--drivers/unix/file_access_unix.h16
-rw-r--r--drivers/unix/ip_unix.cpp91
-rw-r--r--drivers/unix/ip_unix.h12
-rw-r--r--drivers/unix/net_socket_posix.cpp129
-rw-r--r--drivers/unix/net_socket_posix.h31
-rw-r--r--drivers/unix/os_unix.cpp91
-rw-r--r--drivers/unix/os_unix.h26
-rw-r--r--drivers/unix/syslog_logger.cpp6
-rw-r--r--drivers/unix/syslog_logger.h4
-rw-r--r--drivers/unix/thread_posix.cpp8
-rw-r--r--drivers/unix/thread_posix.h4
-rw-r--r--drivers/vulkan/SCsub146
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp2455
-rw-r--r--drivers/vulkan/rendering_device_vulkan.h179
-rw-r--r--drivers/vulkan/vulkan_context.cpp1122
-rw-r--r--drivers/vulkan/vulkan_context.h120
-rw-r--r--drivers/vulkan/vulkan_hooks.h48
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp187
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h11
-rw-r--r--drivers/windows/dir_access_windows.cpp46
-rw-r--r--drivers/windows/dir_access_windows.h21
-rw-r--r--drivers/windows/file_access_windows.cpp76
-rw-r--r--drivers/windows/file_access_windows.h16
-rw-r--r--drivers/winmidi/midi_driver_winmidi.cpp4
-rw-r--r--drivers/winmidi/midi_driver_winmidi.h5
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.cpp4
-rw-r--r--drivers/xaudio2/audio_driver_xaudio2.h5
87 files changed, 18689 insertions, 2207 deletions
diff --git a/drivers/SCsub b/drivers/SCsub
index e2ac9ee01e..dd81fc645c 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -23,10 +23,11 @@ SConscript("coremidi/SCsub")
SConscript("winmidi/SCsub")
# Graphics drivers
-if env["platform"] != "server" and env["platform"] != "javascript":
+if env["vulkan"]:
SConscript("vulkan/SCsub")
-else:
- SConscript("dummy/SCsub")
+if env["opengl3"]:
+ SConscript("gl_context/SCsub")
+ SConscript("gles3/SCsub")
# Core dependencies
SConscript("png/SCsub")
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 61475c74e7..7884269103 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -283,9 +283,9 @@ Array AudioDriverALSA::get_device_list() {
if (name != nullptr && !strncmp(name, "plughw", 6)) {
if (desc) {
- list.push_back(String(name) + ";" + String(desc));
+ list.push_back(String::utf8(name) + ";" + String::utf8(desc));
} else {
- list.push_back(String(name));
+ list.push_back(String::utf8(name));
}
}
diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h
index ca97e76bc2..dbb40fa088 100644
--- a/drivers/alsa/audio_driver_alsa.h
+++ b/drivers/alsa/audio_driver_alsa.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp
index 245ea07730..0674c90cd6 100644
--- a/drivers/alsamidi/midi_driver_alsamidi.cpp
+++ b/drivers/alsamidi/midi_driver_alsamidi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/alsamidi/midi_driver_alsamidi.h b/drivers/alsamidi/midi_driver_alsamidi.h
index c327712ee7..b0fa8c297a 100644
--- a/drivers/alsamidi/midi_driver_alsamidi.h
+++ b/drivers/alsamidi/midi_driver_alsamidi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index baa60f5526..e2b195350f 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -70,7 +70,7 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID
Error AudioDriverCoreAudio::init() {
AudioComponentDescription desc;
- zeromem(&desc, sizeof(desc));
+ memset(&desc, 0, sizeof(desc));
desc.componentType = kAudioUnitType_Output;
#ifdef OSX_ENABLED
desc.componentSubType = kAudioUnitSubType_HALOutput;
@@ -97,7 +97,7 @@ Error AudioDriverCoreAudio::init() {
AudioStreamBasicDescription strdesc;
- zeromem(&strdesc, sizeof(strdesc));
+ memset(&strdesc, 0, sizeof(strdesc));
UInt32 size = sizeof(strdesc);
result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size);
ERR_FAIL_COND_V(result != noErr, FAILED);
@@ -118,7 +118,7 @@ Error AudioDriverCoreAudio::init() {
mix_rate = GLOBAL_GET("audio/driver/mix_rate");
- zeromem(&strdesc, sizeof(strdesc));
+ memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
strdesc.mChannelsPerFrame = channels;
@@ -148,7 +148,7 @@ Error AudioDriverCoreAudio::init() {
print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
AURenderCallbackStruct callback;
- zeromem(&callback, sizeof(AURenderCallbackStruct));
+ memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = &AudioDriverCoreAudio::output_callback;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
@@ -173,10 +173,10 @@ OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
if (!ad->active || !ad->try_lock()) {
for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
AudioBuffer *abuf = &ioData->mBuffers[i];
- zeromem(abuf->mData, abuf->mDataByteSize);
- };
+ memset(abuf->mData, 0, abuf->mDataByteSize);
+ }
return 0;
- };
+ }
ad->start_counting_ticks();
@@ -195,14 +195,14 @@ 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,
@@ -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;
@@ -293,7 +293,7 @@ void AudioDriverCoreAudio::finish() {
lock();
AURenderCallbackStruct callback;
- zeromem(&callback, sizeof(AURenderCallbackStruct));
+ memset(&callback, 0, sizeof(AURenderCallbackStruct));
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
if (result != noErr) {
ERR_PRINT("AudioUnitSetProperty failed");
@@ -337,7 +337,7 @@ void AudioDriverCoreAudio::finish() {
Error AudioDriverCoreAudio::capture_init() {
AudioComponentDescription desc;
- zeromem(&desc, sizeof(desc));
+ memset(&desc, 0, sizeof(desc));
desc.componentType = kAudioUnitType_Output;
#ifdef OSX_ENABLED
desc.componentSubType = kAudioUnitSubType_HALOutput;
@@ -383,7 +383,7 @@ Error AudioDriverCoreAudio::capture_init() {
#endif
AudioStreamBasicDescription strdesc;
- zeromem(&strdesc, sizeof(strdesc));
+ memset(&strdesc, 0, sizeof(strdesc));
size = sizeof(strdesc);
result = AudioUnitGetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size);
ERR_FAIL_COND_V(result != noErr, FAILED);
@@ -405,7 +405,7 @@ Error AudioDriverCoreAudio::capture_init() {
mix_rate = GLOBAL_GET("audio/driver/mix_rate");
- zeromem(&strdesc, sizeof(strdesc));
+ memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
strdesc.mChannelsPerFrame = capture_channels;
@@ -419,7 +419,7 @@ Error AudioDriverCoreAudio::capture_init() {
ERR_FAIL_COND_V(result != noErr, FAILED);
AURenderCallbackStruct callback;
- zeromem(&callback, sizeof(AURenderCallbackStruct));
+ memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = &AudioDriverCoreAudio::input_callback;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback));
@@ -436,7 +436,7 @@ void AudioDriverCoreAudio::capture_finish() {
lock();
AURenderCallbackStruct callback;
- zeromem(&callback, sizeof(AURenderCallbackStruct));
+ memset(&callback, 0, sizeof(AURenderCallbackStruct));
OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
if (result != noErr) {
ERR_PRINT("AudioUnitSetProperty failed");
@@ -506,7 +506,8 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) {
UInt32 size = 0;
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
- AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size);
+ ERR_FAIL_NULL_V_MSG(audioDevices, list, "Out of memory.");
AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices);
UInt32 deviceCount = size / sizeof(AudioDeviceID);
@@ -515,14 +516,16 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) {
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
- AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioBufferList *bufferList = (AudioBufferList *)memalloc(size);
+ ERR_FAIL_NULL_V_MSG(bufferList, list, "Out of memory.");
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;
+ }
- free(bufferList);
+ memfree(bufferList);
if (channelCount >= 1) {
CFStringRef cfname;
@@ -534,17 +537,18 @@ Array AudioDriverCoreAudio::_get_device_list(bool capture) {
CFIndex length = CFStringGetLength(cfname);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
- char *buffer = (char *)malloc(maxSize);
+ char *buffer = (char *)memalloc(maxSize);
+ ERR_FAIL_NULL_V_MSG(buffer, list, "Out of memory.");
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
// Append the ID to the name in case we have devices with duplicate name
- list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")");
+ list.push_back(String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")");
}
- free(buffer);
+ memfree(buffer);
}
}
- free(audioDevices);
+ memfree(audioDevices);
return list;
}
@@ -561,7 +565,8 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
UInt32 size = 0;
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
- AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size);
+ ERR_FAIL_NULL_MSG(audioDevices, "Out of memory.");
AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices);
UInt32 deviceCount = size / sizeof(AudioDeviceID);
@@ -570,14 +575,16 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
- AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioBufferList *bufferList = (AudioBufferList *)memalloc(size);
+ ERR_FAIL_NULL_MSG(bufferList, "Out of memory.");
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;
+ }
- free(bufferList);
+ memfree(bufferList);
if (channelCount >= 1) {
CFStringRef cfname;
@@ -589,20 +596,21 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
CFIndex length = CFStringGetLength(cfname);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
- char *buffer = (char *)malloc(maxSize);
+ char *buffer = (char *)memalloc(maxSize);
+ ERR_FAIL_NULL_MSG(buffer, "Out of memory.");
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
- String name = String(buffer) + " (" + itos(audioDevices[i]) + ")";
+ String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
if (name == device) {
deviceId = audioDevices[i];
found = true;
}
}
- free(buffer);
+ memfree(buffer);
}
}
- free(audioDevices);
+ memfree(audioDevices);
}
if (!found) {
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index b31835760e..b19f133d89 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp
index 87764d9b10..ecd10f900b 100644
--- a/drivers/coremidi/midi_driver_coremidi.cpp
+++ b/drivers/coremidi/midi_driver_coremidi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/coremidi/midi_driver_coremidi.h b/drivers/coremidi/midi_driver_coremidi.h
index 41a7c760ac..be0a9f610e 100644
--- a/drivers/coremidi/midi_driver_coremidi.h
+++ b/drivers/coremidi/midi_driver_coremidi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h
deleted file mode 100644
index a76520dcdb..0000000000
--- a/drivers/dummy/rasterizer_dummy.h
+++ /dev/null
@@ -1,774 +0,0 @@
-/*************************************************************************/
-/* rasterizer_dummy.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 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_DUMMY_H
-#define RASTERIZER_DUMMY_H
-
-#include "core/math/camera_matrix.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_server.h"
-
-class RasterizerSceneDummy : public RendererSceneRender {
-public:
- GeometryInstance *geometry_instance_create(RID p_base) override { return nullptr; }
- 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_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 Transform &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_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 {}
-
- uint32_t geometry_instance_get_pair_mask() override { return 0; }
- 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_gi_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_gi_probe_instances, uint32_t p_gi_probe_instance_count) override {}
-
- void geometry_instance_free(GeometryInstance *p_geometry_instance) override {}
-
- /* SHADOW ATLAS API */
-
- RID shadow_atlas_create() override { return RID(); }
- void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = false) override {}
- void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override {}
- bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override { return false; }
-
- void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = false) override {}
- int get_directional_light_shadow_size(RID p_light_intance) override { return 0; }
- void set_directional_shadow_count(int p_count) override {}
-
- /* SDFGI UPDATE */
-
- void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
- int sdfgi_get_pending_region_count(RID p_render_buffers) const override { 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 { return RID(); }
- 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_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 { return Ref<Image>(); }
-
- /* ENVIRONMENT API */
-
- RID environment_allocate() override { return RID(); }
- 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, const Color &p_ao_color = Color()) 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) 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_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades 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_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_light, float p_light_energy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount) 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 { return Ref<Image>(); }
-
- bool is_environment(RID p_env) const override { return false; }
- RS::EnvironmentBG environment_get_background(RID p_env) const override { return RS::ENV_BG_KEEP; }
- int environment_get_canvas_max_layer(RID p_env) const override { return 0; }
-
- RID camera_effects_allocate() override { return RID(); }
- void camera_effects_initialize(RID p_rid) override {}
- void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override {}
- void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override {}
-
- void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override {}
- void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override {}
-
- void shadows_quality_set(RS::ShadowQuality p_quality) override {}
- void directional_shadow_quality_set(RS::ShadowQuality p_quality) override {}
-
- RID light_instance_create(RID p_light) override { return RID(); }
- void light_instance_set_transform(RID p_light_instance, const Transform &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 Transform &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 {}
-
- RID reflection_atlas_create() override { return RID(); }
- int reflection_atlas_get_size(RID p_ref_atlas) const override { return 0; }
- void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override {}
-
- RID reflection_probe_instance_create(RID p_probe) override { return RID(); }
- void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) override {}
- void reflection_probe_release_atlas_index(RID p_instance) override {}
- bool reflection_probe_instance_needs_redraw(RID p_instance) override { return false; }
- bool reflection_probe_instance_has_reflection(RID p_instance) override { return false; }
- bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override { return false; }
- bool reflection_probe_instance_postprocess_step(RID p_instance) override { return true; }
-
- RID decal_instance_create(RID p_decal) override { return RID(); }
- void decal_instance_set_transform(RID p_decal, const Transform &p_transform) override {}
-
- RID lightmap_instance_create(RID p_lightmap) override { return RID(); }
- void lightmap_instance_set_transform(RID p_lightmap, const Transform &p_transform) override {}
-
- RID gi_probe_instance_create(RID p_gi_probe) override { return RID(); }
- void gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) override {}
- bool gi_probe_needs_update(RID p_probe) const override { return false; }
- void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override {}
-
- void gi_probe_set_quality(RS::GIProbeQuality) override {}
-
- void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_gi_probes, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_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) override {}
- void render_material(const Transform &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 Transform &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {}
-
- 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 {}
-
- RID render_buffers_create() override { return RID(); }
- void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) 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 {}
- bool screen_space_roughness_limiter_is_active() const override { return false; }
-
- void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override {}
- void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override {}
-
- TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override { return TypedArray<Image>(); }
-
- bool free(RID p_rid) override { return true; }
- void update() override {}
- void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override {}
-
- bool is_low_end() const override { return true; }
-
- RasterizerSceneDummy() {}
- ~RasterizerSceneDummy() {}
-};
-
-class RasterizerStorageDummy : public RendererStorage {
-public:
- bool can_create_resources_async() const override { return false; }
-
- /* TEXTURE API */
- struct DummyTexture {
- Ref<Image> image;
- };
- mutable RID_PtrOwner<DummyTexture> texture_owner;
-
- RID texture_allocate() override {
- DummyTexture *texture = memnew(DummyTexture);
- ERR_FAIL_COND_V(!texture, RID());
- return texture_owner.make_rid(texture);
- }
- void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override {
- DummyTexture *t = texture_owner.getornull(p_texture);
- ERR_FAIL_COND(!t);
- t->image = p_image->duplicate();
- }
-
- void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override {}
- void texture_2d_update_immediate(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override {}
- void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) 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_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override {}
- void texture_proxy_initialize(RID p_texture, RID p_base) 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 {
- DummyTexture *t = texture_owner.getornull(p_texture);
- ERR_FAIL_COND_V(!t, Ref<Image>());
- return t->image;
- }
-
- 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_set_path(RID p_texture, const String &p_path) override {}
- String texture_get_path(RID p_texture) const override { return String(); }
-
- void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override {}
- 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_debug_usage(List<RS::TextureInfo> *r_info) override {}
- void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override {}
- Size2 texture_size_with_proxy(RID p_proxy) override { return Size2(); }
-
- 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 {}
-
- /* CANVAS TEXTURE API */
-
- RID canvas_texture_allocate() override { return RID(); }
- 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_base_color, float p_shininess) override {}
-
- void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override {}
- void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override {}
-
- /* SHADER API */
-
- RID shader_allocate() override { return RID(); }
- void shader_initialize(RID p_rid) override {}
- void shader_set_code(RID p_shader, const String &p_code) override {}
- String shader_get_code(RID p_shader) const override { return ""; }
- 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) override {}
- RID shader_get_default_texture_param(RID p_shader, const StringName &p_name) const override { return RID(); }
- Variant shader_get_param_default(RID p_material, const StringName &p_param) const override { return Variant(); }
-
- RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override { return RS::ShaderNativeSourceCode(); };
-
- /* COMMON MATERIAL API */
-
- RID material_allocate() override { return RID(); }
- void material_initialize(RID p_rid) override {}
- void material_set_render_priority(RID p_material, int priority) override {}
- void material_set_shader(RID p_shader_material, RID p_shader) override {}
-
- 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 { return Variant(); }
-
- void material_set_next_pass(RID p_material, RID p_next_material) override {}
-
- bool material_is_animated(RID p_material) override { return false; }
- bool material_casts_shadows(RID p_material) override { return false; }
- 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 {}
-
- /* MESH API */
-
- RID mesh_allocate() override { return RID(); }
- 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 { return false; }
- RID mesh_instance_create(RID p_base) override { return RID(); }
- 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_lod_threshold(RID p_probe, float p_ratio) override {}
- float reflection_probe_get_lod_threshold(RID p_probe) const override { return 0.0; }
-
- void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override {}
-
- int mesh_get_blend_shape_count(RID p_mesh) const override { return 0; }
-
- 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 { return RS::BLEND_SHAPE_MODE_NORMALIZED; }
-
- void mesh_surface_update_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 { return RID(); }
-
- RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override { return RS::SurfaceData(); }
- int mesh_get_surface_count(RID p_mesh) const override { return 0; }
-
- void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override {}
- AABB mesh_get_custom_aabb(RID p_mesh) const override { return AABB(); }
-
- AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override { return AABB(); }
- void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override {}
- void mesh_clear(RID p_mesh) override {}
-
- /* MULTIMESH API */
-
- RID multimesh_allocate() override { return RID(); }
- 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 { return 0; }
-
- void multimesh_set_mesh(RID p_multimesh, RID p_mesh) override {}
- void multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform &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 { return RID(); }
- AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); }
-
- Transform multimesh_instance_get_transform(RID p_multimesh, int p_index) const override { return Transform(); }
- Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override { return Transform2D(); }
- Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override { return Color(); }
- Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override { return Color(); }
- void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override {}
- Vector<float> multimesh_get_buffer(RID p_multimesh) const override { return Vector<float>(); }
-
- void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override {}
- int multimesh_get_visible_instances(RID p_multimesh) const override { return 0; }
-
- /* IMMEDIATE API */
-
- RID immediate_allocate() override { return RID(); }
- void immediate_initialize(RID p_rid) override {}
- void immediate_begin(RID p_immediate, RS::PrimitiveType p_rimitive, RID p_texture = RID()) override {}
- void immediate_vertex(RID p_immediate, const Vector3 &p_vertex) override {}
- void immediate_normal(RID p_immediate, const Vector3 &p_normal) override {}
- void immediate_tangent(RID p_immediate, const Plane &p_tangent) override {}
- void immediate_color(RID p_immediate, const Color &p_color) override {}
- void immediate_uv(RID p_immediate, const Vector2 &tex_uv) override {}
- void immediate_uv2(RID p_immediate, const Vector2 &tex_uv) override {}
- void immediate_end(RID p_immediate) override {}
- void immediate_clear(RID p_immediate) override {}
- void immediate_set_material(RID p_immediate, RID p_material) override {}
- RID immediate_get_material(RID p_immediate) const override { return RID(); }
- AABB immediate_get_aabb(RID p_immediate) const override { return AABB(); }
-
- /* SKELETON API */
-
- RID skeleton_allocate() override { return RID(); }
- 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 { return 0; }
- void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform &p_transform) override {}
- Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override { return Transform(); }
- 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 { return Transform2D(); }
-
- /* Light API */
-
- RID directional_light_allocate() override { return RID(); }
- void directional_light_initialize(RID p_rid) override {}
- RID omni_light_allocate() override { return RID(); }
- void omni_light_initialize(RID p_rid) override {}
- RID spot_light_allocate() override { return RID(); }
- void spot_light_initialize(RID p_rid) override {}
- RID reflection_probe_allocate() override { return RID(); }
- 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 { return false; }
- void light_directional_set_shadow_depth_range_mode(RID p_light, RS::LightDirectionalShadowDepthRangeMode p_range_mode) 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 { return false; }
- RS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const override { return RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; }
-
- RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override { return RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL; }
- RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override { return RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; }
-
- bool light_has_shadow(RID p_light) const override { return false; }
-
- RS::LightType light_get_type(RID p_light) const override { return RS::LIGHT_OMNI; }
- AABB light_get_aabb(RID p_light) const override { return AABB(); }
- float light_get_param(RID p_light, RS::LightParam p_param) override { return 0.0; }
- Color light_get_color(RID p_light) override { return Color(); }
- RS::LightBakeMode light_get_bake_mode(RID p_light) override { return RS::LIGHT_BAKE_DISABLED; }
- uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
- uint64_t light_get_version(RID p_light) const override { return 0; }
-
- /* 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 { return AABB(); }
- RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override { return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE; }
- uint32_t reflection_probe_get_cull_mask(RID p_probe) const override { return 0; }
- Vector3 reflection_probe_get_extents(RID p_probe) const override { return Vector3(); }
- Vector3 reflection_probe_get_origin_offset(RID p_probe) const override { return Vector3(); }
- float reflection_probe_get_origin_max_distance(RID p_probe) const override { return 0.0; }
- bool reflection_probe_renders_shadows(RID p_probe) const override { return false; }
-
- 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 { return RID(); }
- 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 { return AABB(); }
-
- /* GI PROBE API */
-
- RID gi_probe_allocate() override { return RID(); }
- void gi_probe_initialize(RID p_rid) override {}
- void gi_probe_allocate_data(RID p_gi_probe, const Transform &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 gi_probe_get_bounds(RID p_gi_probe) const override { return AABB(); }
- Vector3i gi_probe_get_octree_size(RID p_gi_probe) const override { return Vector3i(); }
- Vector<uint8_t> gi_probe_get_octree_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); }
- Vector<uint8_t> gi_probe_get_data_cells(RID p_gi_probe) const override { return Vector<uint8_t>(); }
- Vector<uint8_t> gi_probe_get_distance_field(RID p_gi_probe) const override { return Vector<uint8_t>(); }
-
- Vector<int> gi_probe_get_level_counts(RID p_gi_probe) const override { return Vector<int>(); }
- Transform gi_probe_get_to_cell_xform(RID p_gi_probe) const override { return Transform(); }
-
- void gi_probe_set_dynamic_range(RID p_gi_probe, float p_range) override {}
- float gi_probe_get_dynamic_range(RID p_gi_probe) const override { return 0; }
-
- void gi_probe_set_propagation(RID p_gi_probe, float p_range) override {}
- float gi_probe_get_propagation(RID p_gi_probe) const override { return 0; }
-
- void gi_probe_set_energy(RID p_gi_probe, float p_range) override {}
- float gi_probe_get_energy(RID p_gi_probe) const override { return 0.0; }
-
- void gi_probe_set_ao(RID p_gi_probe, float p_ao) override {}
- float gi_probe_get_ao(RID p_gi_probe) const override { return 0; }
-
- void gi_probe_set_ao_size(RID p_gi_probe, float p_strength) override {}
- float gi_probe_get_ao_size(RID p_gi_probe) const override { return 0; }
-
- void gi_probe_set_bias(RID p_gi_probe, float p_range) override {}
- float gi_probe_get_bias(RID p_gi_probe) const override { return 0.0; }
-
- void gi_probe_set_normal_bias(RID p_gi_probe, float p_range) override {}
- float gi_probe_get_normal_bias(RID p_gi_probe) const override { return 0.0; }
-
- void gi_probe_set_interior(RID p_gi_probe, bool p_enable) override {}
- bool gi_probe_is_interior(RID p_gi_probe) const override { return false; }
-
- void gi_probe_set_use_two_bounces(RID p_gi_probe, bool p_enable) override {}
- bool gi_probe_is_using_two_bounces(RID p_gi_probe) const override { return false; }
-
- void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) override {}
- float gi_probe_get_anisotropy_strength(RID p_gi_probe) const override { return 0; }
-
- uint32_t gi_probe_get_version(RID p_gi_probe) override { return 0; }
-
- /* LIGHTMAP CAPTURE */
- RID lightmap_allocate() override { return RID(); }
- 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 { return PackedVector3Array(); }
- PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); }
- PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); }
- PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override { return PackedInt32Array(); }
- AABB lightmap_get_aabb(RID p_lightmap) const override { return AABB(); }
- 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 { return false; }
- void lightmap_set_probe_capture_update_speed(float p_speed) override {}
- float lightmap_get_probe_capture_update_speed() const override { return 0; }
-
- /* PARTICLES */
-
- RID particles_allocate() override { return RID(); }
- void particles_initialize(RID p_rid) override {}
- void particles_emit(RID p_particles, const Transform &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, float 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, float p_time) override {}
- void particles_set_explosiveness_ratio(RID p_particles, float p_ratio) override {}
- void particles_set_randomness_ratio(RID p_particles, float p_ratio) override {}
- void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override {}
- void particles_set_speed_scale(RID p_particles, float 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_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) override {}
- void particles_set_collision_base_size(RID p_particles, float p_size) 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 { return AABB(); }
- AABB particles_get_aabb(RID p_particles) const override { return AABB(); }
-
- void particles_set_emission_transform(RID p_particles, const Transform &p_transform) override {}
-
- bool particles_get_emitting(RID p_particles) override { return false; }
- int particles_get_draw_passes(RID p_particles) const override { return 0; }
- RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override { return RID(); }
-
- void particles_add_collision(RID p_particles, RID p_instance) override {}
- void particles_remove_collision(RID p_particles, RID p_instance) override {}
-
- void update_particles() override {}
-
- /* PARTICLES COLLISION */
-
- RID particles_collision_allocate() override { return RID(); }
- 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, float 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, float p_strength) override {}
- void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) override {}
- void particles_collision_set_attractor_attenuation(RID p_particles_collision, float 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 { return AABB(); }
- bool particles_collision_is_heightfield(RID p_particles_collision) const override { return false; }
- RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const override { return RID(); }
-
- RID particles_collision_instance_create(RID p_collision) override { return RID(); };
- void particles_collision_instance_set_transform(RID p_collision_instance, const Transform &p_transform) override{};
- void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) 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 { return Vector<StringName>(); }
-
- 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 { return Variant(); }
- RS::GlobalVariableType global_variable_get_type(const StringName &p_name) const override { return RS::GLOBAL_VAR_TYPE_MAX; }
-
- 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 { return 0; }
- 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 { return false; }
-
- /* RENDER TARGET */
-
- RID render_target_create() override { return RID(); }
- 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) override {}
- RID render_target_get_texture(RID p_render_target) override { return RID(); }
- 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 { return false; }
- void render_target_set_as_unused(RID p_render_target) override {}
-
- 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 { return false; }
- Color render_target_get_clear_request_color(RID p_render_target) override { return Color(); }
- 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 { return Rect2i(); }
-
- RS::InstanceType get_base_type(RID p_rid) const override { return RS::INSTANCE_NONE; }
- bool free(RID p_rid) override {
- if (texture_owner.owns(p_rid)) {
- // delete the texture
- DummyTexture *texture = texture_owner.getornull(p_rid);
- texture_owner.free(p_rid);
- memdelete(texture);
- }
- return true;
- }
-
- bool has_os_feature(const String &p_feature) const override { return false; }
-
- 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 { return 0; }
-
- int get_render_info(RS::RenderInfo p_info) override { return 0; }
- String get_video_adapter_name() const override { return String(); }
- String get_video_adapter_vendor() const override { return String(); }
-
- static RendererStorage *base_singleton;
-
- 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(); }
-
- RasterizerStorageDummy() {}
- ~RasterizerStorageDummy() {}
-};
-
-class RasterizerCanvasDummy : public RendererCanvasRender {
-public:
- PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override { return 0; }
- void free_polygon(PolygonID p_polygon) override {}
-
- void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override {}
- void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override {}
-
- RID light_create() override { return RID(); }
- void light_set_texture(RID p_rid, RID p_texture) override {}
- void light_set_use_shadow(RID p_rid, bool p_enable) override {}
- void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override {}
- void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override {}
-
- void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override {}
- RID occluder_polygon_create() override { return RID(); }
- void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override {}
- void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override {}
- void set_shadow_texture_size(int p_size) override {}
-
- void draw_window_margins(int *p_margins, RID *p_margin_textures) override {}
-
- bool free(RID p_rid) override { return true; }
- void update() override {}
-
- RasterizerCanvasDummy() {}
- ~RasterizerCanvasDummy() {}
-};
-
-class RasterizerDummy : public RendererCompositor {
-private:
- uint64_t frame = 1;
- float delta = 0;
-
-protected:
- RasterizerCanvasDummy canvas;
- RasterizerStorageDummy storage;
- RasterizerSceneDummy scene;
-
-public:
- RendererStorage *get_storage() override { return &storage; }
- RendererCanvasRender *get_canvas() override { return &canvas; }
- RendererSceneRender *get_scene() override { return &scene; }
-
- void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override {}
-
- void initialize() override {}
- void begin_frame(double frame_step) override {
- frame++;
- delta = frame_step;
- }
-
- void prepare_for_blitting_render_targets() override {}
- void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {}
-
- void end_frame(bool p_swap_buffers) override {
- if (p_swap_buffers) {
- DisplayServer::get_singleton()->swap_buffers();
- }
- }
-
- void finalize() override {}
-
- static RendererCompositor *_create_current() {
- return memnew(RasterizerDummy);
- }
-
- static void make_current() {
- _create_func = _create_current;
- }
-
- bool is_low_end() const override { return true; }
- uint64_t get_frame_number() const override { return frame; }
- float get_frame_delta_time() const override { return delta; }
-
- RasterizerDummy() {}
- ~RasterizerDummy() {}
-};
-
-#endif // RASTERIZER_DUMMY_H
diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub
new file mode 100644
index 0000000000..ddeec6f4c6
--- /dev/null
+++ b/drivers/gl_context/SCsub
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+Import("env")
+
+if env["platform"] in ["haiku", "osx", "windows", "linuxbsd"]:
+ # Thirdparty source files
+ thirdparty_dir = "#thirdparty/glad/"
+ thirdparty_sources = [
+ "glad.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env.Prepend(CPPPATH=[thirdparty_dir])
+
+ env.Append(CPPDEFINES=["GLAD_ENABLED"])
+ env.Append(CPPDEFINES=["GLES_OVER_GL"])
+
+ env_thirdparty = env.Clone()
+ env_thirdparty.disable_warnings()
+ env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
+
+# Godot source files
+env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/gles3/SCsub b/drivers/gles3/SCsub
new file mode 100644
index 0000000000..fcb05a988d
--- /dev/null
+++ b/drivers/gles3/SCsub
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.drivers_sources, "*.cpp")
+
+SConscript("shaders/SCsub")
+SConscript("storage/SCsub")
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
new file mode 100644
index 0000000000..b47d2e08f1
--- /dev/null
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -0,0 +1,1476 @@
+/*************************************************************************/
+/* rasterizer_canvas_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_canvas_gles3.h"
+
+#ifdef GLES3_ENABLED
+
+#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/canvas_texture_storage.h"
+#include "storage/config.h"
+
+#ifndef GLES_OVER_GL
+#define glClearDepth glClearDepthf
+#endif
+
+//static const GLenum gl_primitive[] = {
+// GL_POINTS,
+// GL_LINES,
+// GL_LINE_STRIP,
+// GL_LINE_LOOP,
+// GL_TRIANGLES,
+// GL_TRIANGLE_STRIP,
+// GL_TRIANGLE_FAN
+//};
+
+void RasterizerCanvasGLES3::_update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4) {
+ p_mat4[0] = p_transform.elements[0][0];
+ p_mat4[1] = p_transform.elements[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[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[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[2] = 0;
+ p_mat2x4[3] = p_transform.elements[2][0];
+
+ p_mat2x4[4] = p_transform.elements[0][1];
+ p_mat2x4[5] = p_transform.elements[1][1];
+ p_mat2x4[6] = 0;
+ p_mat2x4[7] = p_transform.elements[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];
+}
+
+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[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[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[11] = 0;
+ p_mat4[12] = p_transform.origin.x;
+ p_mat4[13] = p_transform.origin.y;
+ p_mat4[14] = p_transform.origin.z;
+ p_mat4[15] = 1;
+}
+
+void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) {
+ storage->frame.current_rt = nullptr;
+
+ storage->_set_current_render_target(p_to_render_target);
+
+ Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
+
+ // TODO: Setup Directional Lights
+
+ // TODO: Setup lights
+
+ {
+ //update canvas state uniform buffer
+ StateBuffer state_buffer;
+
+ Size2i ssize = 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.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();
+ _update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
+
+ state_buffer.canvas_modulate[0] = p_modulate.r;
+ state_buffer.canvas_modulate[1] = p_modulate.g;
+ 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);
+ 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;
+ state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
+
+ state_buffer.directional_light_count = 0; //directional_light_count;
+
+ Vector2 canvas_scale = p_canvas_transform.get_scale();
+
+ state_buffer.sdf_to_screen[0] = render_target_size.width / canvas_scale.x;
+ state_buffer.sdf_to_screen[1] = render_target_size.height / canvas_scale.y;
+
+ 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_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;
+ state_buffer.sdf_to_tex[1] = 1.0 / sdf_tex_rect.size.height;
+ 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);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ }
+
+ {
+ state.default_filter = p_default_filter;
+ 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;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+ Item *ci = p_item_list;
+ while (ci) {
+ // just add all items for now
+ items[item_count++] = ci;
+
+ if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
+ //then reset
+ item_count = 0;
+ }
+
+ ci = ci->next;
+ }
+}
+
+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) {
+ Item *current_clip = nullptr;
+
+ Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
+
+ RID framebuffer;
+ Vector<Color> clear_colors;
+
+ canvas_begin();
+
+ RID prev_material;
+ uint32_t index = 0;
+
+ for (int i = 0; i < p_item_count; i++) {
+ Item *ci = items[i];
+
+ 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.
+ }
+ }
+
+ 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;
+ }
+
+ 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);
+
+ GLES3::Texture *t = texture_storage->get_texture(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;
+ }
+
+ continue;
+ }
+
+ //Set texture filter and repeat texture_uniforms[i].filter texture_uniforms[i].repeat
+
+ if (t->redraw_if_visible) {
+ RenderingServerDefault::redraw_request();
+ }
+
+ t = t->get_ptr();
+
+#ifdef TOOLS_ENABLED
+ if (t->detect_normal && texture_uniforms[i].hint == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) {
+ t->detect_normal(t->detect_normal_ud);
+ }
+#endif
+ if (t->render_target) {
+ t->render_target->used_in_frame = true;
+ }
+
+ glBindTexture(t->target, t->tex_id);
+ }
+
+ } else {
+ //state.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD);
+ state.current_shader_version = state.canvas_shader_default_version;
+ }
+ prev_material = material;
+ }
+
+ _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 *&current_clip, Light *p_lights, uint32_t &r_index) {
+ RS::CanvasItemTextureFilter current_filter = state.default_filter;
+ RS::CanvasItemTextureRepeat current_repeat = state.default_repeat;
+
+ if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) {
+ current_filter = p_item->texture_filter;
+ }
+
+ if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) {
+ current_repeat = p_item->texture_repeat;
+ }
+
+ Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
+ Transform2D draw_transform; // Used by transform command
+
+ Color base_color = p_item->final_modulate;
+
+ uint32_t base_flags = 0;
+
+ RID last_texture;
+ Size2 texpixel_size;
+
+ bool skipping = false;
+
+ const Item::Command *c = p_item->commands;
+ while (c) {
+ if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) {
+ c = c->next;
+ continue;
+ }
+
+ _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;
+ 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;
+ 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;
+
+ 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].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
+
+ switch (c->type) {
+ case Item::Command::TYPE_RECT: {
+ const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
+
+ if (rect->flags & CANVAS_RECT_TILE) {
+ current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
+ }
+
+ if (rect->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) {
+ state.end_batch = true;
+ _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);
+
+ 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);
+ dst_rect = Rect2(rect->rect.position, rect->rect.size);
+
+ if (dst_rect.size.width < 0) {
+ dst_rect.position.x += dst_rect.size.width;
+ dst_rect.size.width *= -1;
+ }
+ if (dst_rect.size.height < 0) {
+ dst_rect.position.y += dst_rect.size.height;
+ dst_rect.size.height *= -1;
+ }
+
+ if (rect->flags & CANVAS_RECT_FLIP_H) {
+ src_rect.size.x *= -1;
+ }
+
+ if (rect->flags & CANVAS_RECT_FLIP_V) {
+ src_rect.size.y *= -1;
+ }
+
+ if (rect->flags & CANVAS_RECT_TRANSPOSE) {
+ dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform
+ }
+
+ if (rect->flags & CANVAS_RECT_CLIP_UV) {
+ state.instance_data_array[r_index].flags |= FLAGS_CLIP_RECT_UV;
+ }
+
+ } else {
+ dst_rect = Rect2(rect->rect.position, rect->rect.size);
+
+ if (dst_rect.size.width < 0) {
+ dst_rect.position.x += dst_rect.size.width;
+ dst_rect.size.width *= -1;
+ }
+ if (dst_rect.size.height < 0) {
+ dst_rect.position.y += dst_rect.size.height;
+ dst_rect.size.height *= -1;
+ }
+
+ src_rect = Rect2(0, 0, 1, 1);
+ }
+
+ if (rect->flags & CANVAS_RECT_MSDF) {
+ state.instance_data_array[r_index].flags |= FLAGS_USE_MSDF;
+ state.instance_data_array[r_index].msdf[0] = rect->px_range; // Pixel range.
+ state.instance_data_array[r_index].msdf[1] = rect->outline; // Outline size.
+ state.instance_data_array[r_index].msdf[2] = 0.f; // Reserved.
+ state.instance_data_array[r_index].msdf[3] = 0.f; // Reserved.
+ }
+
+ state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r;
+ state.instance_data_array[r_index].modulation[1] = rect->modulate.g * base_color.g;
+ state.instance_data_array[r_index].modulation[2] = rect->modulate.b * base_color.b;
+ state.instance_data_array[r_index].modulation[3] = rect->modulate.a * base_color.a;
+
+ state.instance_data_array[r_index].src_rect[0] = src_rect.position.x;
+ state.instance_data_array[r_index].src_rect[1] = src_rect.position.y;
+ state.instance_data_array[r_index].src_rect[2] = src_rect.size.width;
+ state.instance_data_array[r_index].src_rect[3] = src_rect.size.height;
+
+ state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x;
+ 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);
+ }
+
+ //bind textures
+
+ _bind_canvas_texture(p_draw_list, np->texture, current_filter, current_repeat, index, last_texture, texpixel_size);
+
+ Rect2 src_rect;
+ Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
+
+ if (np->texture == RID()) {
+ texpixel_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);
+ 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;
+
+ } else {
+ src_rect = Rect2(0, 0, 1, 1);
+ }
+ }
+
+ state.instance_data_array[r_index].modulation[0] = np->color.r * base_color.r;
+ state.instance_data_array[r_index].modulation[1] = np->color.g * base_color.g;
+ state.instance_data_array[r_index].modulation[2] = np->color.b * base_color.b;
+ state.instance_data_array[r_index].modulation[3] = np->color.a * base_color.a;
+
+ state.instance_data_array[r_index].src_rect[0] = src_rect.position.x;
+ state.instance_data_array[r_index].src_rect[1] = src_rect.position.y;
+ state.instance_data_array[r_index].src_rect[2] = src_rect.size.width;
+ state.instance_data_array[r_index].src_rect[3] = src_rect.size.height;
+
+ state.instance_data_array[r_index].dst_rect[0] = dst_rect.position.x;
+ 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;
+
+ state.instance_data_array[r_index].flags |= int(np->axis_x) << FLAGS_NINEPATCH_H_MODE_SHIFT;
+ state.instance_data_array[r_index].flags |= int(np->axis_y) << FLAGS_NINEPATCH_V_MODE_SHIFT;
+
+ if (np->draw_center) {
+ state.instance_data_array[r_index].flags |= FLAGS_NINEPACH_DRAW_CENTER;
+ }
+
+ state.instance_data_array[r_index].ninepatch_margins[0] = np->margin[SIDE_LEFT];
+ state.instance_data_array[r_index].ninepatch_margins[1] = np->margin[SIDE_TOP];
+ 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);
+
+ // 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;
+*/
+ } break;
+
+ case Item::Command::TYPE_POLYGON: {
+ const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
+
+ PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id);
+ ERR_CONTINUE(!pb);
+
+ if (polygon->texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) {
+ state.end_batch = true;
+ _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);
+
+ state.current_primitive = polygon->primitive;
+ state.instance_data_array[r_index].modulation[0] = base_color.r;
+ state.instance_data_array[r_index].modulation[1] = base_color.g;
+ state.instance_data_array[r_index].modulation[2] = base_color.b;
+ state.instance_data_array[r_index].modulation[3] = base_color.a;
+
+ for (int j = 0; j < 4; j++) {
+ state.instance_data_array[r_index].src_rect[j] = 0;
+ state.instance_data_array[r_index].dst_rect[j] = 0;
+ 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
+ glBindVertexArray(pb->vertex_array);
+
+ static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
+
+ if (pb->index_buffer != 0) {
+ glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
+ } else {
+ glDrawArrays(prim[polygon->primitive], 0, pb->count);
+ }
+ glBindVertexArray(0);
+ state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+
+ state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
+ } 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;
+ _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);
+
+ 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;
+ state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j].y;
+ Color col = primitive->colors[j] * base_color;
+ state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
+ state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
+ }
+ r_index++;
+ if (primitive->point_count == 4) {
+ // Reset base data
+ _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
+ state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
+ state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
+
+ state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
+
+ for (uint32_t j = 0; j < 3; j++) {
+ //second half of triangle
+ state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x;
+ state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y;
+ state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x;
+ state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y;
+ Color col = primitive->colors[j + 1] * base_color;
+ state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
+ state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
+ }
+ r_index++;
+ }
+ if (r_index >= state.max_instances_per_batch - 1) {
+ //r_index--;
+ state.end_batch = true;
+ _render_batch(r_index);
+ }
+ } break;
+
+ case Item::Command::TYPE_MESH:
+ case Item::Command::TYPE_MULTIMESH:
+ case Item::Command::TYPE_PARTICLES: {
+ /*
+ RID mesh;
+ RID mesh_instance;
+ RID texture;
+ Color modulate(1, 1, 1, 1);
+ int instance_count = 1;
+
+ if (c->type == Item::Command::TYPE_MESH) {
+ const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
+ mesh = m->mesh;
+ mesh_instance = m->mesh_instance;
+ texture = m->texture;
+ modulate = m->modulate;
+ _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world);
+ } 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);
+ texture = mm->texture;
+
+ if (storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
+ break;
+ }
+
+ instance_count = 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;
+ }
+ }
+
+ // TODO: implement particles here
+
+ if (mesh.is_null()) {
+ break;
+ }
+
+ if (texture != last_texture || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) {
+ state.end_batch = true;
+ _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);
+
+ uint32_t surf_count = 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;
+ state.instance_data_array[r_index].modulation[2] = base_color.b * modulate.b;
+ state.instance_data_array[r_index].modulation[3] = base_color.a * modulate.a;
+
+ for (int j = 0; j < 4; j++) {
+ state.instance_data_array[r_index].src_rect[j] = 0;
+ state.instance_data_array[r_index].dst_rect[j] = 0;
+ state.instance_data_array[r_index].ninepatch_margins[j] = 0;
+ }
+
+ for (uint32_t j = 0; j < surf_count; j++) {
+ RS::SurfaceData *surface = storage->mesh_get_surface(mesh, j);
+
+ RS::PrimitiveType primitive = 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 };
+
+ // Draw directly, no need to batch
+ }
+ */
+ } break;
+ case Item::Command::TYPE_TRANSFORM: {
+ const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
+ draw_transform = transform->xform;
+ } 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);
+ reclip = true;
+ } else {
+ RD::get_singleton()->draw_list_enable_scissor(p_draw_list, current_clip->final_clip_rect);
+ reclip = false;
+ }
+ }
+ }
+ */
+ } break;
+ case Item::Command::TYPE_ANIMATION_SLICE: {
+ /*
+ const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c);
+ double current_time = RendererCompositorRD::singleton->get_total_time();
+ double local_time = Math::fposmod(current_time - as->offset, as->animation_length);
+ skipping = !(local_time >= as->slice_begin && local_time < as->slice_end);
+
+ RenderingServerDefault::redraw_request(); // animation visible means redraw request
+ */
+ } break;
+ }
+
+ c = c->next;
+ }
+}
+
+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
+ glBindVertexArray(data.canvas_quad_array);
+ if (state.current_primitive_points == 0) {
+ glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index);
+ } else {
+ static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES };
+ glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index);
+ }
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ 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];
+ state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i];
+ state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i];
+ state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i];
+ state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i];
+ }
+ state.instance_data_array[0].flags = state.instance_data_array[r_index].flags;
+ state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0];
+ state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1];
+
+ state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0];
+ state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1];
+ for (int i = 0; i < 6; i++) {
+ state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i];
+ }
+
+ r_index = 0;
+ }
+}
+
+// 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;
+ }
+ 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);
+}
+
+RID RasterizerCanvasGLES3::light_create() {
+ return RID();
+}
+
+void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) {
+}
+
+void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) {
+}
+
+void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
+}
+
+void RasterizerCanvasGLES3::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) {
+}
+
+void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) {
+}
+
+RID RasterizerCanvasGLES3::occluder_polygon_create() {
+ return RID();
+}
+
+void RasterizerCanvasGLES3::occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) {
+}
+
+void RasterizerCanvasGLES3::occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) {
+}
+
+void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) {
+}
+
+bool RasterizerCanvasGLES3::free(RID p_rid) {
+ return true;
+}
+
+void RasterizerCanvasGLES3::update() {
+}
+
+void RasterizerCanvasGLES3::canvas_begin() {
+ state.using_transparent_rt = false;
+
+ 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 (storage->frame.current_rt && storage->frame.current_rt->clear_requested) {
+ const Color &col = storage->frame.current_rt->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;
+ }
+
+ reset_canvas();
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+}
+
+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, RID &r_last_texture, Size2 &r_texpixel_size) {
+ if (p_texture == RID()) {
+ p_texture = default_canvas_texture;
+ }
+
+ if (r_last_texture == p_texture) {
+ return; //nothing to do, its the same
+ }
+
+ state.end_batch = true;
+ _render_batch(r_index);
+
+ GLES3::CanvasTexture *ct = nullptr;
+
+ GLES3::Texture *t = texture_storage->get_texture(p_texture);
+
+ if (t) {
+ //regular texture
+ if (!t->canvas_texture) {
+ t->canvas_texture = memnew(GLES3::CanvasTexture);
+ t->canvas_texture->diffuse = p_texture;
+ }
+
+ ct = t->canvas_texture;
+ } else {
+ ct = GLES3::CanvasTextureStorage::get_singleton()->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);
+ return;
+ }
+
+ RS::CanvasItemTextureFilter filter = ct->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? ct->texture_filter : p_base_filter;
+ ERR_FAIL_COND(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT);
+
+ 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);
+
+ 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);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+
+ } else {
+ texture = texture->get_ptr();
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture->tex_id);
+
+ state.current_tex = ct->diffuse;
+ 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);
+ }
+
+ GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
+
+ if (!normal_map) {
+ state.current_normal = RID();
+ ct->use_normal_cache = false;
+ glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6);
+ glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex);
+
+ } else {
+ normal_map = normal_map->get_ptr();
+
+ glActiveTexture(GL_TEXTURE0 + storage->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);
+ }
+
+ 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);
+
+ } else {
+ specular_map = specular_map->get_ptr();
+
+ glActiveTexture(GL_TEXTURE0 + storage->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);
+ }
+
+ if (ct->use_specular_cache) {
+ state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ } else {
+ state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ }
+
+ if (ct->use_normal_cache) {
+ state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
+ } else {
+ state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED;
+ }
+
+ state.instance_data_array[r_index].specular_shininess = uint32_t(CLAMP(ct->specular_color.a * 255.0, 0, 255)) << 24;
+ state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.b * 255.0, 0, 255)) << 16;
+ 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.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() {
+}
+
+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);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void RasterizerCanvasGLES3::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {
+}
+
+void RasterizerCanvasGLES3::canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) {
+}
+
+void RasterizerCanvasGLES3::draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample) {
+}
+
+RendererCanvasRender::PolygonID RasterizerCanvasGLES3::request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights) {
+ // We interleave the vertex data into one big VBO to improve cache coherence
+ uint32_t vertex_count = p_points.size();
+ uint32_t stride = 2;
+ if ((uint32_t)p_colors.size() == vertex_count) {
+ stride += 4;
+ }
+ if ((uint32_t)p_uvs.size() == vertex_count) {
+ stride += 2;
+ }
+ if ((uint32_t)p_bones.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) {
+ stride += 4;
+ }
+
+ PolygonBuffers pb;
+ glGenBuffers(1, &pb.vertex_buffer);
+ glGenVertexArrays(1, &pb.vertex_array);
+ glBindVertexArray(pb.vertex_array);
+ pb.count = vertex_count;
+ pb.index_buffer = 0;
+
+ uint32_t buffer_size = stride * p_points.size();
+
+ Vector<uint8_t> polygon_buffer;
+ polygon_buffer.resize(buffer_size * sizeof(float));
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, pb.vertex_buffer);
+ glBufferData(GL_ARRAY_BUFFER, stride * vertex_count * sizeof(float), nullptr, GL_STATIC_DRAW); // TODO may not be necessary
+ const uint8_t *r = polygon_buffer.ptr();
+ float *fptr = (float *)r;
+ uint32_t *uptr = (uint32_t *)r;
+ uint32_t base_offset = 0;
+ {
+ // Always uses vertex positions
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), nullptr);
+ const Vector2 *points_ptr = p_points.ptr();
+
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ fptr[base_offset + i * stride + 0] = points_ptr[i].x;
+ fptr[base_offset + i * stride + 1] = points_ptr[i].y;
+ }
+
+ base_offset += 2;
+ }
+
+ // 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) {
+ 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)));
+
+ const Color *color_ptr = p_colors.ptr();
+
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ fptr[base_offset + i * stride + 0] = color_ptr[i].r;
+ fptr[base_offset + i * stride + 1] = color_ptr[i].g;
+ fptr[base_offset + i * stride + 2] = color_ptr[i].b;
+ fptr[base_offset + i * stride + 3] = color_ptr[i].a;
+ }
+ base_offset += 4;
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_COLOR);
+ glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
+ }
+
+ if ((uint32_t)p_uvs.size() == vertex_count) {
+ glEnableVertexAttribArray(RS::ARRAY_TEX_UV);
+ glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float)));
+
+ const Vector2 *uv_ptr = p_uvs.ptr();
+
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ fptr[base_offset + i * stride + 0] = uv_ptr[i].x;
+ fptr[base_offset + i * stride + 1] = uv_ptr[i].y;
+ }
+
+ base_offset += 2;
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_TEX_UV);
+ }
+
+ if ((uint32_t)p_indices.size() == vertex_count * 4 && (uint32_t)p_weights.size() == vertex_count * 4) {
+ glEnableVertexAttribArray(RS::ARRAY_BONES);
+ glVertexAttribPointer(RS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float)));
+
+ const int *bone_ptr = p_bones.ptr();
+
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ uint16_t *bone16w = (uint16_t *)&uptr[base_offset + i * stride];
+
+ bone16w[0] = bone_ptr[i * 4 + 0];
+ bone16w[1] = bone_ptr[i * 4 + 1];
+ bone16w[2] = bone_ptr[i * 4 + 2];
+ bone16w[3] = bone_ptr[i * 4 + 3];
+ }
+
+ base_offset += 2;
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_BONES);
+ }
+
+ if ((uint32_t)p_weights.size() == vertex_count * 4) {
+ glEnableVertexAttribArray(RS::ARRAY_WEIGHTS);
+ glVertexAttribPointer(RS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(base_offset * sizeof(float)));
+
+ const float *weight_ptr = p_weights.ptr();
+
+ for (uint32_t i = 0; i < vertex_count; i++) {
+ uint16_t *weight16w = (uint16_t *)&uptr[base_offset + i * stride];
+
+ weight16w[0] = CLAMP(weight_ptr[i * 4 + 0] * 65535, 0, 65535);
+ weight16w[1] = CLAMP(weight_ptr[i * 4 + 1] * 65535, 0, 65535);
+ weight16w[2] = CLAMP(weight_ptr[i * 4 + 2] * 65535, 0, 65535);
+ weight16w[3] = CLAMP(weight_ptr[i * 4 + 3] * 65535, 0, 65535);
+ }
+
+ base_offset += 2;
+ } else {
+ glDisableVertexAttribArray(RS::ARRAY_WEIGHTS);
+ }
+
+ ERR_FAIL_COND_V(base_offset != stride, 0);
+ glBufferData(GL_ARRAY_BUFFER, vertex_count * stride * sizeof(float), polygon_buffer.ptr(), GL_STATIC_DRAW);
+ }
+
+ if (p_indices.size()) {
+ //create indices, as indices were requested
+ Vector<uint8_t> index_buffer;
+ index_buffer.resize(p_indices.size() * sizeof(int32_t));
+ {
+ uint8_t *w = index_buffer.ptrw();
+ memcpy(w, p_indices.ptr(), sizeof(int32_t) * p_indices.size());
+ }
+ glGenBuffers(1, &pb.index_buffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pb.index_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, nullptr, GL_STATIC_DRAW); // TODO may not be necessary
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_indices.size() * 4, index_buffer.ptr(), GL_STATIC_DRAW);
+ pb.count = p_indices.size();
+ }
+
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ PolygonID id = polygon_buffers.last_id++;
+
+ polygon_buffers.polygons[id] = pb;
+
+ return id;
+}
+void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) {
+ PolygonBuffers *pb_ptr = polygon_buffers.polygons.getptr(p_polygon);
+ ERR_FAIL_COND(!pb_ptr);
+
+ PolygonBuffers &pb = *pb_ptr;
+
+ if (pb.index_buffer != 0) {
+ glDeleteBuffers(1, &pb.index_buffer);
+ }
+
+ glDeleteVertexArrays(1, &pb.vertex_array);
+ glDeleteBuffers(1, &pb.vertex_buffer);
+
+ polygon_buffers.polygons.erase(p_polygon);
+}
+
+// Creates a new uniform buffer and uses it right away
+// This expands the instance buffer continually
+// In theory allocations can reach as high as number_of_draw_calls * 3 frames
+// because OpenGL can start rendering subsequent frames before finishing the current one
+void RasterizerCanvasGLES3::_allocate_instance_data_buffer() {
+ GLuint new_buffer;
+ glGenBuffers(1, &new_buffer);
+ glBindBuffer(GL_UNIFORM_BUFFER, new_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW);
+ state.current_buffer = (state.current_buffer + 1);
+ state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer);
+ state.fences.insert(state.current_buffer, GLsync());
+ state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size();
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+}
+
+void RasterizerCanvasGLES3::initialize() {
+ // !BAS! shouldn't we be obtaining storage here as well?
+ canvas_texture_storage = GLES3::CanvasTextureStorage::get_singleton();
+ texture_storage = GLES3::TextureStorage::get_singleton();
+
+ // quad buffer
+ {
+ glGenBuffers(1, &data.canvas_quad_vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices);
+
+ const float qv[8] = {
+ 0, 0,
+ 0, 1,
+ 1, 1,
+ 1, 0
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glGenVertexArrays(1, &data.canvas_quad_array);
+ glBindVertexArray(data.canvas_quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
+ glEnableVertexAttribArray(0);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+
+ {
+ //particle quad buffers
+
+ glGenBuffers(1, &data.particle_quad_vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
+ {
+ //quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle
+ const float qv[16] = {
+ -0.5, -0.5,
+ 0.0, 0.0,
+ -0.5, 0.5,
+ 0.0, 1.0,
+ 0.5, 0.5,
+ 1.0, 1.0,
+ 0.5, -0.5,
+ 1.0, 0.0
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &data.particle_quad_array);
+ glBindVertexArray(data.particle_quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_TEX_UV);
+ glVertexAttribPointer(RS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(8));
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+
+ // ninepatch buffers
+ {
+ // array buffer
+ glGenBuffers(1, &data.ninepatch_vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices);
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, nullptr, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // element buffer
+ glGenBuffers(1, &data.ninepatch_elements);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements);
+
+#define _EIDX(y, x) (y * 4 + x)
+ uint8_t elems[3 * 2 * 9] = {
+ // first row
+
+ _EIDX(0, 0), _EIDX(0, 1), _EIDX(1, 1),
+ _EIDX(1, 1), _EIDX(1, 0), _EIDX(0, 0),
+
+ _EIDX(0, 1), _EIDX(0, 2), _EIDX(1, 2),
+ _EIDX(1, 2), _EIDX(1, 1), _EIDX(0, 1),
+
+ _EIDX(0, 2), _EIDX(0, 3), _EIDX(1, 3),
+ _EIDX(1, 3), _EIDX(1, 2), _EIDX(0, 2),
+
+ // second row
+
+ _EIDX(1, 0), _EIDX(1, 1), _EIDX(2, 1),
+ _EIDX(2, 1), _EIDX(2, 0), _EIDX(1, 0),
+
+ // the center one would be here, but we'll put it at the end
+ // so it's easier to disable the center and be able to use
+ // one draw call for both
+
+ _EIDX(1, 2), _EIDX(1, 3), _EIDX(2, 3),
+ _EIDX(2, 3), _EIDX(2, 2), _EIDX(1, 2),
+
+ // third row
+
+ _EIDX(2, 0), _EIDX(2, 1), _EIDX(3, 1),
+ _EIDX(3, 1), _EIDX(3, 0), _EIDX(2, 0),
+
+ _EIDX(2, 1), _EIDX(2, 2), _EIDX(3, 2),
+ _EIDX(3, 2), _EIDX(3, 1), _EIDX(2, 1),
+
+ _EIDX(2, 2), _EIDX(2, 3), _EIDX(3, 3),
+ _EIDX(3, 3), _EIDX(3, 2), _EIDX(2, 2),
+
+ // center field
+
+ _EIDX(1, 1), _EIDX(1, 2), _EIDX(2, 2),
+ _EIDX(2, 2), _EIDX(2, 1), _EIDX(1, 1)
+ };
+#undef _EIDX
+
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ //state.canvas_shadow_shader.init();
+
+ int uniform_max_size;
+ glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &uniform_max_size);
+ if (uniform_max_size < 65536) {
+ state.max_lights_per_render = 64;
+ state.max_instances_per_batch = 128;
+ } else {
+ state.max_lights_per_render = 256;
+ state.max_instances_per_batch = 512;
+ }
+
+ // Reserve 64 Uniform Buffers for instance data
+ state.canvas_instance_data_buffers.resize(64);
+ state.fences.resize(64);
+ glGenBuffers(64, state.canvas_instance_data_buffers.ptr());
+ for (int i = 0; i < 64; i++) {
+ 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);
+ }
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch);
+
+ glGenBuffers(1, &state.canvas_state_buffer);
+ glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ String global_defines;
+ global_defines += "#define MAX_GLOBAL_VARIABLES 256\n"; // TODO: this is arbitrary for now
+ global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n";
+ global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n";
+
+ 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));
+
+ {
+ default_canvas_group_shader = storage->shader_allocate();
+ storage->shader_initialize(default_canvas_group_shader);
+
+ storage->shader_set_code(default_canvas_group_shader, R"(
+// Default CanvasGroup shader.
+
+shader_type canvas_item;
+
+void fragment() {
+ vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
+
+ if (c.a > 0.0001) {
+ c.rgb /= c.a;
+ }
+
+ COLOR *= c;
+}
+)");
+ default_canvas_group_material = storage->material_allocate();
+ storage->material_initialize(default_canvas_group_material);
+
+ storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
+ }
+
+ default_canvas_texture = canvas_texture_storage->canvas_texture_allocate();
+ canvas_texture_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;
+}
+
+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);
+ canvas_texture_storage->canvas_texture_free(default_canvas_texture);
+}
+
+void RasterizerCanvasGLES3::finalize() {
+ glDeleteBuffers(1, &data.canvas_quad_vertices);
+ glDeleteVertexArrays(1, &data.canvas_quad_array);
+
+ glDeleteBuffers(1, &data.canvas_quad_vertices);
+ glDeleteVertexArrays(1, &data.canvas_quad_array);
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
new file mode 100644
index 0000000000..1f57c2b5ea
--- /dev/null
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -0,0 +1,285 @@
+/*************************************************************************/
+/* rasterizer_canvas_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_CANVAS_OPENGL_H
+#define RASTERIZER_CANVAS_OPENGL_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/canvas_texture_storage.h"
+#include "storage/texture_storage.h"
+
+#include "shaders/canvas.glsl.gen.h"
+
+class RasterizerSceneGLES3;
+
+class RasterizerCanvasGLES3 : public RendererCanvasRender {
+ _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);
+
+ _FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
+ _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),
+ FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
+
+ FLAGS_CLIP_RECT_UV = (1 << 9),
+ FLAGS_TRANSPOSE_RECT = (1 << 10),
+
+ FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
+ FLAGS_USING_PARTICLES = (1 << 13),
+
+ FLAGS_USE_SKELETON = (1 << 15),
+ FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
+ FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
+ FLAGS_LIGHT_COUNT_SHIFT = 20,
+
+ FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
+ FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
+
+ FLAGS_USE_MSDF = (1 << 28),
+ };
+
+ enum {
+ LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
+ LIGHT_FLAGS_BLEND_SHIFT = 16,
+ LIGHT_FLAGS_BLEND_MASK = (3 << 16),
+ LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
+ LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
+ LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
+ LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16),
+ LIGHT_FLAGS_HAS_SHADOW = (1 << 20),
+ LIGHT_FLAGS_FILTER_SHIFT = 22
+
+ };
+
+ enum {
+ MAX_RENDER_ITEMS = 256 * 1024,
+ MAX_LIGHT_TEXTURES = 1024,
+ MAX_LIGHTS_PER_ITEM = 16,
+ DEFAULT_MAX_LIGHTS_PER_RENDER = 256,
+ };
+
+public:
+ struct StateBuffer {
+ float canvas_transform[16];
+ float screen_transform[16];
+ float canvas_normal_transform[16];
+ float canvas_modulate[4];
+
+ float screen_pixel_size[2];
+ float time;
+ uint32_t use_pixel_snap;
+
+ float sdf_to_tex[4];
+ float sdf_to_screen[2];
+ float screen_to_sdf[2];
+
+ uint32_t directional_light_count;
+ float tex_to_sdf;
+ uint32_t pad1;
+ uint32_t pad2;
+ };
+
+ struct InstanceData {
+ float world[6];
+ float color_texture_pixel_size[2];
+ union {
+ //rect
+ struct {
+ float modulation[4];
+ union {
+ float msdf[4];
+ float ninepatch_margins[4];
+ };
+ float dst_rect[4];
+ float src_rect[4];
+ float pad[2];
+ };
+ //primitive
+ struct {
+ float points[6]; // vec2 points[3]
+ float uvs[6]; // vec2 points[3]
+ uint32_t colors[6]; // colors encoded as half
+ };
+ };
+ uint32_t flags;
+ uint32_t specular_shininess;
+ uint32_t lights[4];
+ };
+
+ struct Data {
+ GLuint canvas_quad_vertices;
+ GLuint canvas_quad_array;
+
+ GLuint particle_quad_vertices;
+ GLuint particle_quad_array;
+
+ GLuint ninepatch_vertices;
+ GLuint ninepatch_elements;
+ } data;
+
+ struct State {
+ GLuint canvas_state_buffer;
+ LocalVector<GLuint> canvas_instance_data_buffers;
+ LocalVector<GLsync> fences;
+ uint32_t current_buffer = 0;
+
+ InstanceData *instance_data_array;
+ 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();
+ RID current_normal = RID();
+ RID current_specular = RID();
+ GLES3::Texture *current_tex_ptr;
+ RID current_shader_version = RID();
+ RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX;
+ uint32_t current_primitive_points = 0;
+ Item::Command::Type current_command = Item::Command::TYPE_RECT;
+
+ bool end_batch = false;
+
+ Transform3D vp;
+ Light *using_light;
+ bool using_shadow;
+ bool using_transparent_rt;
+
+ // FROM RD Renderer
+
+ uint32_t max_lights_per_render;
+ uint32_t max_lights_per_item;
+ uint32_t max_instances_per_batch;
+
+ RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
+ RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
+ } state;
+
+ Item *items[MAX_RENDER_ITEMS];
+
+ RID default_canvas_texture;
+ RID default_canvas_group_material;
+ RID default_canvas_group_shader;
+
+ typedef void Texture;
+
+ RasterizerSceneGLES3 *scene_render;
+
+ GLES3::CanvasTextureStorage *canvas_texture_storage;
+ GLES3::TextureStorage *texture_storage;
+ RasterizerStorageGLES3 *storage;
+
+ void _set_uniforms();
+
+ void canvas_begin();
+ void canvas_end();
+
+ //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);
+
+ virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) override;
+
+ RID light_create() override;
+ void light_set_texture(RID p_rid, RID p_texture) override;
+ void light_set_use_shadow(RID p_rid, bool p_enable) override;
+ void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) override;
+ void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override;
+
+ void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override;
+ RID occluder_polygon_create() override;
+ void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override;
+ void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode) override;
+ void set_shadow_texture_size(int p_size) override;
+
+ 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);
+
+ struct PolygonBuffers {
+ GLuint vertex_buffer;
+ GLuint vertex_array;
+ GLuint index_buffer;
+ int count;
+ };
+
+ struct {
+ HashMap<PolygonID, PolygonBuffers> polygons;
+ PolygonID last_id;
+ } polygon_buffers;
+
+ RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override;
+ void free_polygon(PolygonID p_polygon) override;
+
+ void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override;
+ void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false);
+ void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_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 _allocate_instance_data_buffer();
+
+ void initialize();
+ void finalize();
+ RasterizerCanvasGLES3();
+ ~RasterizerCanvasGLES3();
+};
+
+#endif // GLES3_ENABLED
+#endif // RASTERIZER_CANVAS_OPENGL_H
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
new file mode 100644
index 0000000000..faadb2a4ed
--- /dev/null
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -0,0 +1,363 @@
+/*************************************************************************/
+/* rasterizer_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_gles3.h"
+
+#ifdef GLES3_ENABLED
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+
+#define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
+#define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
+#define _EXT_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
+#define _EXT_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
+#define _EXT_DEBUG_SOURCE_API_ARB 0x8246
+#define _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
+#define _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
+#define _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
+#define _EXT_DEBUG_SOURCE_APPLICATION_ARB 0x824A
+#define _EXT_DEBUG_SOURCE_OTHER_ARB 0x824B
+#define _EXT_DEBUG_TYPE_ERROR_ARB 0x824C
+#define _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
+#define _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
+#define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F
+#define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
+#define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251
+#define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
+#define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
+#define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145
+#define _EXT_DEBUG_SEVERITY_HIGH_ARB 0x9146
+#define _EXT_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
+#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
+#define _EXT_DEBUG_OUTPUT 0x92E0
+
+#ifndef GLAPIENTRY
+#if defined(WINDOWS_ENABLED) && !defined(UWP_ENABLED)
+#define GLAPIENTRY APIENTRY
+#else
+#define GLAPIENTRY
+#endif
+#endif
+
+#if !defined(IPHONE_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
+#endif
+
+#if !defined(GLES_OVER_GL) && defined(CAN_DEBUG)
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <GLES3/gl3platform.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
+
+#if defined(MINGW_ENABLED) || defined(_MSC_VER)
+#define strcpy strcpy_s
+#endif
+
+void RasterizerGLES3::begin_frame(double frame_step) {
+ frame++;
+ delta = frame_step;
+
+ time_total += 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;
+
+ storage.update_dirty_resources();
+
+ storage.info.render_final = storage.info.render;
+ storage.info.render.reset();
+
+ //scene->iteration();
+}
+
+void RasterizerGLES3::end_frame(bool p_swap_buffers) {
+ // if (OS::get_singleton()->is_layered_allowed()) {
+ // if (!OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+ //clear alpha
+ // glColorMask(false, false, false, true);
+ // glClearColor(0.5, 0, 0, 1);
+ // glClear(GL_COLOR_BUFFER_BIT);
+ // glColorMask(true, true, true, true);
+ // }
+ // }
+
+ // glClearColor(1, 0, 0, 1);
+ // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ if (p_swap_buffers) {
+ DisplayServer::get_singleton()->swap_buffers();
+ } else {
+ glFinish();
+ }
+}
+
+#ifdef CAN_DEBUG
+static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
+ if (type == _EXT_DEBUG_TYPE_OTHER_ARB) {
+ return;
+ }
+
+ if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) {
+ return; //these are ultimately annoying, so removing for now
+ }
+
+ char debSource[256], debType[256], debSev[256];
+
+ if (source == _EXT_DEBUG_SOURCE_API_ARB) {
+ strcpy(debSource, "OpenGL");
+ } else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) {
+ strcpy(debSource, "Windows");
+ } else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB) {
+ strcpy(debSource, "Shader Compiler");
+ } else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB) {
+ strcpy(debSource, "Third Party");
+ } else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB) {
+ strcpy(debSource, "Application");
+ } else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB) {
+ strcpy(debSource, "Other");
+ }
+
+ if (type == _EXT_DEBUG_TYPE_ERROR_ARB) {
+ strcpy(debType, "Error");
+ } else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) {
+ strcpy(debType, "Deprecated behavior");
+ } else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) {
+ strcpy(debType, "Undefined behavior");
+ } else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB) {
+ strcpy(debType, "Portability");
+ } else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) {
+ strcpy(debType, "Performance");
+ } else if (type == _EXT_DEBUG_TYPE_OTHER_ARB) {
+ strcpy(debType, "Other");
+ }
+
+ if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) {
+ strcpy(debSev, "High");
+ } else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB) {
+ strcpy(debSev, "Medium");
+ } else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB) {
+ strcpy(debSev, "Low");
+ }
+
+ String output = String() + "GL ERROR: Source: " + debSource + "\tType: " + debType + "\tID: " + itos(id) + "\tSeverity: " + debSev + "\tMessage: " + message;
+
+ ERR_PRINT(output);
+}
+#endif
+
+typedef void (*DEBUGPROCARB)(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const char *message,
+ const void *userParam);
+
+typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam);
+
+void RasterizerGLES3::initialize() {
+ print_verbose("Using OpenGL video driver");
+
+ texture_storage.set_main_thread_id(Thread::get_caller_id());
+
+#ifdef GLAD_ENABLED
+ if (!gladLoadGL()) {
+ ERR_PRINT("Error initializing GLAD");
+ return;
+ }
+#endif
+
+#ifdef GLAD_ENABLED
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ if (GLAD_GL_ARB_debug_output) {
+ glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
+ glDebugMessageCallbackARB(_gl_debug_print, nullptr);
+ glEnable(_EXT_DEBUG_OUTPUT);
+ } else {
+ print_line("OpenGL debugging not supported!");
+ }
+ }
+#endif // GLAD_ENABLED
+
+ // For debugging
+#ifdef CAN_DEBUG
+#ifdef GLES_OVER_GL
+ if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) {
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
+ // glDebugMessageInsertARB(
+ // GL_DEBUG_SOURCE_API_ARB,
+ // GL_DEBUG_TYPE_OTHER_ARB, 1,
+ // GL_DEBUG_SEVERITY_HIGH_ARB, 5, "hello");
+ }
+#else
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback");
+ if (!callback) {
+ callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR");
+ }
+
+ if (callback) {
+ print_line("godot: ENABLING GL DEBUG");
+ glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
+ callback(_gl_debug_print, NULL);
+ glEnable(_EXT_DEBUG_OUTPUT);
+ }
+ }
+#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);
+}
+
+RasterizerGLES3::RasterizerGLES3() {
+ canvas.storage = &storage;
+ canvas.scene_render = &scene;
+ storage.canvas = &canvas;
+ //scene.storage = &storage;
+ storage.scene = &scene;
+}
+
+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::RenderTarget *rt = storage.render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+
+ // TODO: do we need a keep 3d linear option?
+
+ 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);
+}
+
+// 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;
+
+ for (int i = 0; i < p_amount; i++) {
+ const BlitToScreen &blit = p_render_targets[i];
+
+ RID rid_rt = blit.render_target;
+
+ Rect2 dst_rect = blit.dst_rect;
+ _blit_render_target_to_screen(rid_rt, p_screen, dst_rect);
+ }
+}
+
+void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
+ if (p_image.is_null() || p_image->is_empty()) {
+ return;
+ }
+
+ Size2i win_size = DisplayServer::get_singleton()->screen_get_size();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, win_size.width, win_size.height);
+ glDisable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ if (false) {
+ // if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ } else {
+ glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
+ }
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ canvas.canvas_begin();
+
+ RID texture = texture_storage.texture_create();
+ //texture_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);
+ texture_storage._texture_allocate_internal(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D);
+ texture_storage.texture_set_data(texture, p_image);
+
+ Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height());
+ Rect2 screenrect;
+ if (p_scale) {
+ if (win_size.width > win_size.height) {
+ //scale horizontally
+ screenrect.size.y = win_size.height;
+ screenrect.size.x = imgrect.size.x * win_size.height / imgrect.size.y;
+ screenrect.position.x = (win_size.width - screenrect.size.x) / 2;
+
+ } else {
+ //scale vertically
+ screenrect.size.x = win_size.width;
+ screenrect.size.y = imgrect.size.y * win_size.width / imgrect.size.x;
+ screenrect.position.y = (win_size.height - screenrect.size.y) / 2;
+ }
+ } else {
+ screenrect = imgrect;
+ screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor();
+ }
+
+ 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();
+
+ texture_storage.texture_free(texture);
+
+ end_frame(true);
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
new file mode 100644
index 0000000000..4aa94e5293
--- /dev/null
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* rasterizer_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_OPENGL_H
+#define RASTERIZER_OPENGL_H
+
+#ifdef GLES3_ENABLED
+
+#include "rasterizer_canvas_gles3.h"
+#include "rasterizer_scene_gles3.h"
+#include "rasterizer_storage_gles3.h"
+#include "servers/rendering/renderer_compositor.h"
+#include "storage/canvas_texture_storage.h"
+#include "storage/config.h"
+#include "storage/decal_atlas_storage.h"
+#include "storage/render_target_storage.h"
+#include "storage/texture_storage.h"
+
+class RasterizerGLES3 : public RendererCompositor {
+private:
+ uint64_t frame = 1;
+ float delta = 0;
+
+ double time_total = 0.0;
+
+protected:
+ GLES3::Config config;
+ GLES3::CanvasTextureStorage canvas_texture_storage;
+ GLES3::TextureStorage texture_storage;
+ GLES3::DecalAtlasStorage decal_atlas_storage;
+ RasterizerStorageGLES3 storage;
+ RasterizerCanvasGLES3 canvas;
+ RasterizerSceneGLES3 scene;
+
+ void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect);
+
+public:
+ RendererCanvasTextureStorage *get_canvas_texture_storage() { return &canvas_texture_storage; }
+ RendererTextureStorage *get_texture_storage() { return &texture_storage; }
+ RendererDecalAtlasStorage *get_decal_atlas_storage() { return &decal_atlas_storage; }
+ RendererStorage *get_storage() { return &storage; }
+ 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);
+
+ void initialize();
+ void begin_frame(double frame_step);
+
+ void prepare_for_blitting_render_targets();
+ void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount);
+
+ void end_frame(bool p_swap_buffers);
+
+ void finalize() {}
+
+ static RendererCompositor *_create_current() {
+ return memnew(RasterizerGLES3);
+ }
+
+ static void make_current() {
+ _create_func = _create_current;
+ }
+
+ 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() {}
+};
+
+#endif // GLES3_ENABLED
+
+#endif
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
new file mode 100644
index 0000000000..1382573461
--- /dev/null
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -0,0 +1,475 @@
+/*************************************************************************/
+/* rasterizer_scene_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_scene_gles3.h"
+
+#ifdef GLES3_ENABLED
+
+// TODO: 3D support not implemented yet.
+
+RasterizerSceneGLES3::GeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_base) {
+ return nullptr;
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_skeleton(GeometryInstance *p_geometry_instance, RID p_skeleton) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_material_override(GeometryInstance *p_geometry_instance, RID p_override) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_material_overlay(GeometryInstance *p_geometry_instance, RID p_overlay) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_surface_materials(GeometryInstance *p_geometry_instance, const Vector<RID> &p_material) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_mesh_instance(GeometryInstance *p_geometry_instance, RID p_mesh_instance) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_transform(GeometryInstance *p_geometry_instance, const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_layer_mask(GeometryInstance *p_geometry_instance, uint32_t p_layer_mask) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_lod_bias(GeometryInstance *p_geometry_instance, float p_lod_bias) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_transparency(GeometryInstance *p_geometry_instance, float p_transparency) {
+}
+
+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) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_parent_fade_alpha(GeometryInstance *p_geometry_instance, float p_alpha) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_use_baked_light(GeometryInstance *p_geometry_instance, bool p_enable) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_use_dynamic_gi(GeometryInstance *p_geometry_instance, bool p_enable) {
+}
+
+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) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_lightmap_capture(GeometryInstance *p_geometry_instance, const Color *p_sh9) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_instance_shader_parameters_offset(GeometryInstance *p_geometry_instance, int32_t p_offset) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_cast_double_sided_shadows(GeometryInstance *p_geometry_instance, bool p_enable) {
+}
+
+uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
+ return 0;
+}
+
+void RasterizerSceneGLES3::geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) {
+}
+
+void RasterizerSceneGLES3::geometry_instance_free(GeometryInstance *p_geometry_instance) {
+}
+
+/* SHADOW ATLAS API */
+
+RID RasterizerSceneGLES3::shadow_atlas_create() {
+ return RID();
+}
+
+void RasterizerSceneGLES3::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) {
+}
+
+void RasterizerSceneGLES3::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) {
+}
+
+bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
+ return false;
+}
+
+void RasterizerSceneGLES3::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) {
+}
+
+int RasterizerSceneGLES3::get_directional_light_shadow_size(RID p_light_intance) {
+ return 0;
+}
+
+void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) {
+}
+
+/* SDFGI UPDATE */
+
+void RasterizerSceneGLES3::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) {
+}
+
+int RasterizerSceneGLES3::sdfgi_get_pending_region_count(RID p_render_buffers) const {
+ return 0;
+}
+
+AABB RasterizerSceneGLES3::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const {
+ return AABB();
+}
+
+uint32_t RasterizerSceneGLES3::sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const {
+ return 0;
+}
+
+/* SKY API */
+
+RID RasterizerSceneGLES3::sky_allocate() {
+ return RID();
+}
+
+void RasterizerSceneGLES3::sky_initialize(RID p_rid) {
+}
+
+void RasterizerSceneGLES3::sky_set_radiance_size(RID p_sky, int p_radiance_size) {
+}
+
+void RasterizerSceneGLES3::sky_set_mode(RID p_sky, RS::SkyMode p_samples) {
+}
+
+void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) {
+}
+
+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 */
+
+RID RasterizerSceneGLES3::environment_allocate() {
+ return RID();
+}
+
+void RasterizerSceneGLES3::environment_initialize(RID p_rid) {
+}
+
+void RasterizerSceneGLES3::environment_set_background(RID p_env, RS::EnvironmentBG p_bg) {
+}
+
+void RasterizerSceneGLES3::environment_set_sky(RID p_env, RID p_sky) {
+}
+
+void RasterizerSceneGLES3::environment_set_sky_custom_fov(RID p_env, float p_scale) {
+}
+
+void RasterizerSceneGLES3::environment_set_sky_orientation(RID p_env, const Basis &p_orientation) {
+}
+
+void RasterizerSceneGLES3::environment_set_bg_color(RID p_env, const Color &p_color) {
+}
+
+void RasterizerSceneGLES3::environment_set_bg_energy(RID p_env, float p_energy) {
+}
+
+void RasterizerSceneGLES3::environment_set_canvas_max_layer(RID p_env, int p_max_layer) {
+}
+
+void RasterizerSceneGLES3::environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source) {
+}
+
+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) {
+}
+
+void RasterizerSceneGLES3::environment_glow_set_use_bicubic_upscale(bool 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) {
+}
+
+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) {
+}
+
+void RasterizerSceneGLES3::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) {
+}
+
+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) {
+}
+
+void RasterizerSceneGLES3::environment_set_volumetric_fog_filter_active(bool p_enable) {
+}
+
+Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) {
+ 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();
+}
+
+void RasterizerSceneGLES3::camera_effects_initialize(RID p_rid) {
+}
+
+void RasterizerSceneGLES3::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) {
+}
+
+void RasterizerSceneGLES3::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) {
+}
+
+void RasterizerSceneGLES3::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) {
+}
+
+void RasterizerSceneGLES3::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) {
+}
+
+void RasterizerSceneGLES3::shadows_quality_set(RS::ShadowQuality p_quality) {
+}
+
+void RasterizerSceneGLES3::directional_shadow_quality_set(RS::ShadowQuality p_quality) {
+}
+
+RID RasterizerSceneGLES3::light_instance_create(RID p_light) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) {
+}
+
+void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const 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_mark_visible(RID p_light_instance) {
+}
+
+RID RasterizerSceneGLES3::fog_volume_instance_create(RID p_fog_volume) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) {
+}
+
+void RasterizerSceneGLES3::fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) {
+}
+
+RID RasterizerSceneGLES3::fog_volume_instance_get_volume(RID p_fog_volume_instance) const {
+ return RID();
+}
+
+Vector3 RasterizerSceneGLES3::fog_volume_instance_get_position(RID p_fog_volume_instance) const {
+ return Vector3();
+}
+
+RID RasterizerSceneGLES3::reflection_atlas_create() {
+ return RID();
+}
+
+int RasterizerSceneGLES3::reflection_atlas_get_size(RID p_ref_atlas) const {
+ return 0;
+}
+
+void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) {
+}
+
+RID RasterizerSceneGLES3::reflection_probe_instance_create(RID p_probe) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) {
+}
+
+void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) {
+}
+
+bool RasterizerSceneGLES3::reflection_probe_instance_needs_redraw(RID p_instance) {
+ return false;
+}
+
+bool RasterizerSceneGLES3::reflection_probe_instance_has_reflection(RID p_instance) {
+ return false;
+}
+
+bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
+ return false;
+}
+
+bool RasterizerSceneGLES3::reflection_probe_instance_postprocess_step(RID p_instance) {
+ return true;
+}
+
+RID RasterizerSceneGLES3::decal_instance_create(RID p_decal) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) {
+}
+
+RID RasterizerSceneGLES3::lightmap_instance_create(RID p_lightmap) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) {
+}
+
+RID RasterizerSceneGLES3::voxel_gi_instance_create(RID p_voxel_gi) {
+ return RID();
+}
+
+void RasterizerSceneGLES3::voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) {
+}
+
+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_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::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) {
+}
+
+void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) {
+}
+
+void RasterizerSceneGLES3::set_scene_pass(uint64_t p_pass) {
+}
+
+void RasterizerSceneGLES3::set_time(double p_time, double p_step) {
+}
+
+void RasterizerSceneGLES3::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) {
+}
+
+RID RasterizerSceneGLES3::render_buffers_create() {
+ return RID();
+}
+
+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::gi_set_use_half_resolution(bool p_enable) {
+}
+
+void RasterizerSceneGLES3::screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) {
+}
+
+bool RasterizerSceneGLES3::screen_space_roughness_limiter_is_active() const {
+ return false;
+}
+
+void RasterizerSceneGLES3::sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) {
+}
+
+void RasterizerSceneGLES3::sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) {
+}
+
+TypedArray<Image> RasterizerSceneGLES3::bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) {
+ return TypedArray<Image>();
+}
+
+bool RasterizerSceneGLES3::free(RID p_rid) {
+ return false;
+}
+
+void RasterizerSceneGLES3::update() {
+}
+
+void RasterizerSceneGLES3::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) {
+}
+
+void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) {
+}
+
+void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) {
+}
+
+RasterizerSceneGLES3::RasterizerSceneGLES3() {
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
new file mode 100644
index 0000000000..12bb21a5a0
--- /dev/null
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -0,0 +1,230 @@
+/*************************************************************************/
+/* rasterizer_scene_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_SCENE_OPENGL_H
+#define RASTERIZER_SCENE_OPENGL_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/math/camera_matrix.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"
+
+class RasterizerSceneGLES3 : public RendererSceneRender {
+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;
+
+ uint32_t geometry_instance_get_pair_mask() override;
+ void geometry_instance_pair_light_instances(GeometryInstance *p_geometry_instance, const RID *p_light_instances, uint32_t p_light_instance_count) override;
+ void geometry_instance_pair_reflection_probe_instances(GeometryInstance *p_geometry_instance, const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
+ void geometry_instance_pair_decal_instances(GeometryInstance *p_geometry_instance, const RID *p_decal_instances, uint32_t p_decal_instance_count) override;
+ void geometry_instance_pair_voxel_gi_instances(GeometryInstance *p_geometry_instance, const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override;
+ void geometry_instance_set_softshadow_projector_pairing(GeometryInstance *p_geometry_instance, bool p_softshadow, bool p_projector) override;
+
+ void geometry_instance_free(GeometryInstance *p_geometry_instance) override;
+
+ /* SHADOW ATLAS API */
+
+ RID shadow_atlas_create() override;
+ void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override;
+ void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
+ bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override;
+
+ void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override;
+ int get_directional_light_shadow_size(RID p_light_intance) override;
+ void set_directional_shadow_count(int p_count) override;
+
+ /* 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;
+
+ /* 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_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_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
+ void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
+ void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override;
+
+ void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) override;
+
+ void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) override;
+
+ void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) override;
+ void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject) override;
+ void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override;
+ void environment_set_volumetric_fog_filter_active(bool p_enable) override;
+
+ Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override;
+
+ bool is_environment(RID p_env) const override;
+ RS::EnvironmentBG environment_get_background(RID p_env) const override;
+ int environment_get_canvas_max_layer(RID p_env) const override;
+
+ RID camera_effects_allocate() override;
+ void camera_effects_initialize(RID p_rid) override;
+ void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override;
+ void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override;
+
+ void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override;
+ void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override;
+
+ void shadows_quality_set(RS::ShadowQuality p_quality) override;
+ void directional_shadow_quality_set(RS::ShadowQuality p_quality) override;
+
+ RID light_instance_create(RID p_light) override;
+ void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override;
+ void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override;
+ void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
+ void light_instance_mark_visible(RID p_light_instance) override;
+
+ RID fog_volume_instance_create(RID p_fog_volume) override;
+ void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override;
+ void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override;
+ RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override;
+ Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override;
+
+ RID reflection_atlas_create() override;
+ int reflection_atlas_get_size(RID p_ref_atlas) const override;
+ void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override;
+
+ RID reflection_probe_instance_create(RID p_probe) override;
+ void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
+ void reflection_probe_release_atlas_index(RID p_instance) override;
+ bool reflection_probe_instance_needs_redraw(RID p_instance) override;
+ bool reflection_probe_instance_has_reflection(RID p_instance) override;
+ bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
+ bool reflection_probe_instance_postprocess_step(RID p_instance) override;
+
+ RID decal_instance_create(RID p_decal) override;
+ void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override;
+
+ RID lightmap_instance_create(RID p_lightmap) override;
+ void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
+
+ RID voxel_gi_instance_create(RID p_voxel_gi) override;
+ void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override;
+ bool voxel_gi_needs_update(RID p_probe) const override;
+ void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects) override;
+
+ void voxel_gi_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 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;
+
+ 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 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;
+ bool screen_space_roughness_limiter_is_active() const override;
+
+ void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) override;
+ void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override;
+
+ TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) override;
+
+ bool free(RID p_rid) override;
+ void update() override;
+ void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override;
+
+ void decals_set_filter(RS::DecalFilter p_filter) override;
+ void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
+
+ RasterizerSceneGLES3();
+};
+
+#endif // GLES3_ENABLED
+
+#endif // RASTERIZER_SCENE_OPENGL_H
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
new file mode 100644
index 0000000000..5efbc715ab
--- /dev/null
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -0,0 +1,3156 @@
+/*************************************************************************/
+/* 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;
+
+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);
+}
+
+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_model_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["MODEL_MATRIX"] = &p_shader->canvas_item.uses_model_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() && !GLES3::TextureStorage::get_singleton()->owns_texture(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_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_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
+}
+
+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_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) {
+}
+
+RS::LightDirectionalSkyMode RasterizerStorageGLES3::light_directional_get_sky_mode(RID p_light) const {
+ return RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
+}
+
+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) {
+}
+
+/* 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) {
+}
+RID RasterizerStorageGLES3::particles_get_process_material(RID p_particles) const {
+ return RID();
+}
+
+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) {
+ GLES3::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(GLES3::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 */
+
+ GLES3::Texture *texture = GLES3::TextureStorage::get_singleton()->get_texture(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 & GLES3::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;
+
+ GLES3::TextureStorage::get_singleton()->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) {
+ GLES3::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++) {
+ GLES3::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(GLES3::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
+ GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->external.texture);
+ t->alloc_height = 0;
+ t->alloc_width = 0;
+ t->width = 0;
+ t->height = 0;
+ t->active = false;
+ GLES3::TextureStorage::get_singleton()->texture_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;
+ }
+
+ GLES3::Texture *tex = GLES3::TextureStorage::get_singleton()->get_texture(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() {
+ GLES3::RenderTarget *rt = memnew(GLES3::RenderTarget);
+ GLES3::Texture *t = memnew(GLES3::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 = GLES3::TextureStorage::get_singleton()->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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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
+ GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->external.texture);
+ t->alloc_height = 0;
+ t->alloc_width = 0;
+ t->width = 0;
+ t->height = 0;
+ t->active = false;
+ GLES3::TextureStorage::get_singleton()->texture_free(rt->external.texture);
+ memdelete(t);
+
+ rt->external.fbo = 0;
+ rt->external.color = 0;
+ rt->external.depth = 0;
+ }
+ } else {
+ GLES3::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(GLES3::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 = GLES3::TextureStorage::get_singleton()->make_rid(t);
+
+ } else {
+ // bind our frame buffer
+ bind_framebuffer(rt->external.fbo);
+
+ // find our texture
+ t = GLES3::TextureStorage::get_singleton()->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->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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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::GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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) {
+ GLES3::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)) {
+ GLES3::RenderTarget *rt = render_target_owner.get_or_null(p_rid);
+ _render_target_clear(rt);
+
+ GLES3::Texture *t = GLES3::TextureStorage::get_singleton()->get_texture(rt->texture);
+ if (t) {
+ GLES3::TextureStorage::get_singleton()->texture_free(rt->texture);
+ memdelete(t);
+ }
+ render_target_owner.free(p_rid);
+ memdelete(rt);
+
+ 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::CanvasTextureStorage::get_singleton()->owns_canvas_texture(p_rid)) {
+ GLES3::CanvasTextureStorage::get_singleton()->canvas_texture_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;
+ config = GLES3::Config::get_singleton();
+ config->initialize();
+
+ //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;
+
+ // 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
+}
+
+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;
+}
+
+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
new file mode 100644
index 0000000000..4e04b918c2
--- /dev/null
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -0,0 +1,966 @@
+/*************************************************************************/
+/* rasterizer_storage_gles3.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RASTERIZER_STORAGE_OPENGL_H
+#define RASTERIZER_STORAGE_OPENGL_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/local_vector.h"
+#include "core/templates/rid_owner.h"
+#include "core/templates/self_list.h"
+#include "servers/rendering/renderer_compositor.h"
+#include "servers/rendering/renderer_storage.h"
+#include "servers/rendering/shader_compiler.h"
+#include "servers/rendering/shader_language.h"
+#include "storage/canvas_texture_storage.h"
+#include "storage/config.h"
+#include "storage/render_target_storage.h"
+#include "storage/texture_storage.h"
+
+#include "shaders/copy.glsl.gen.h"
+
+class RasterizerCanvasGLES3;
+class RasterizerSceneGLES3;
+
+class RasterizerStorageGLES3 : public RendererStorage {
+public:
+ RasterizerCanvasGLES3 *canvas;
+ RasterizerSceneGLES3 *scene;
+
+ static GLuint system_fbo;
+
+ GLES3::Config *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////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+public:
+ /* 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_model_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_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_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) 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_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) override;
+ RS::LightDirectionalSkyMode light_directional_get_sky_mode(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;
+
+ /* 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;
+ RID particles_get_process_material(RID p_particles) const 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
+
+ mutable RID_PtrOwner<GLES3::RenderTarget> render_target_owner;
+
+ void _render_target_clear(GLES3::RenderTarget *rt);
+ void _render_target_allocate(GLES3::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
+ // GLES3::RenderTarget * render_target_get(RID p_render_target);
+
+ /* CANVAS SHADOW */
+
+ struct CanvasLightShadow {
+ RID self;
+ int size;
+ int height;
+ GLuint fbo;
+ GLuint depth;
+ GLuint distance; //for older devices
+ };
+
+ RID_PtrOwner<CanvasLightShadow> canvas_light_shadow_owner;
+
+ RID canvas_light_shadow_buffer_create(int p_width);
+
+ /* LIGHT SHADOW MAPPING */
+ /*
+ struct CanvasOccluder {
+ RID self;
+
+ GLuint vertex_id; // 0 means, unconfigured
+ GLuint index_id; // 0 means, unconfigured
+ LocalVector<Vector2> lines;
+ int len;
+ };
+
+ RID_Owner<CanvasOccluder> canvas_occluder_owner;
+
+ RID canvas_light_occluder_create();
+ void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines);
+*/
+
+ RS::InstanceType get_base_type(RID p_rid) const override;
+
+ bool free(RID p_rid) override;
+
+ struct Frame {
+ GLES3::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
new file mode 100644
index 0000000000..9349722625
--- /dev/null
+++ b/drivers/gles3/shader_gles3.cpp
@@ -0,0 +1,703 @@
+/*************************************************************************/
+/* shader_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 "shader_gles3.h"
+
+#ifdef GLES3_ENABLED
+
+#include "core/io/compression.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+
+void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) {
+ Vector<String> lines = String(p_code).split("\n");
+
+ String text;
+
+ for (int i = 0; i < lines.size(); i++) {
+ String l = lines[i];
+ bool push_chunk = false;
+
+ StageTemplate::Chunk chunk;
+
+ if (l.begins_with("#GLOBALS")) {
+ switch (p_stage_type) {
+ case STAGE_TYPE_VERTEX:
+ chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS;
+ break;
+ case STAGE_TYPE_FRAGMENT:
+ chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS;
+ break;
+ default: {
+ }
+ }
+
+ push_chunk = true;
+ } else if (l.begins_with("#MATERIAL_UNIFORMS")) {
+ chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS;
+ push_chunk = true;
+ } else if (l.begins_with("#CODE")) {
+ chunk.type = StageTemplate::Chunk::TYPE_CODE;
+ push_chunk = true;
+ chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper();
+ } else {
+ text += l + "\n";
+ }
+
+ if (push_chunk) {
+ if (text != String()) {
+ StageTemplate::Chunk text_chunk;
+ text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
+ text_chunk.text = text.utf8();
+ stage_templates[p_stage_type].chunks.push_back(text_chunk);
+ text = String();
+ }
+ stage_templates[p_stage_type].chunks.push_back(chunk);
+ }
+
+ if (text != String()) {
+ StageTemplate::Chunk text_chunk;
+ text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
+ text_chunk.text = text.utf8();
+ stage_templates[p_stage_type].chunks.push_back(text_chunk);
+ text = String();
+ }
+ }
+}
+
+void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) {
+ name = p_name;
+
+ if (p_vertex_code) {
+ _add_stage(p_vertex_code, STAGE_TYPE_VERTEX);
+ }
+ if (p_fragment_code) {
+ _add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
+ }
+
+ uniform_names = p_uniform_names;
+ uniform_count = p_uniform_count;
+ ubo_pairs = p_ubos;
+ ubo_count = p_ubo_count;
+ texunit_pairs = p_tex_units;
+ texunit_pair_count = p_texture_count;
+ specializations = p_specializations;
+ specialization_count = p_specialization_count;
+ specialization_default_mask = 0;
+ for (int i = 0; i < specialization_count; i++) {
+ if (specializations[i].default_value) {
+ specialization_default_mask |= (uint64_t(1) << uint64_t(i));
+ }
+ }
+ variant_defines = p_variants;
+ variant_count = p_variant_count;
+
+ StringBuilder tohash;
+ /*
+ tohash.append("[SpirvCacheKey]");
+ tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key());
+ tohash.append("[BinaryCacheKey]");
+ tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key());
+ */
+ tohash.append("[Vertex]");
+ tohash.append(p_vertex_code ? p_vertex_code : "");
+ tohash.append("[Fragment]");
+ tohash.append(p_fragment_code ? p_fragment_code : "");
+
+ base_sha256 = tohash.as_string().sha256_text();
+}
+
+RID ShaderGLES3::version_create() {
+ //initialize() was never called
+ ERR_FAIL_COND_V(variant_count == 0, RID());
+
+ Version version;
+ return version_owner.make_rid(version);
+}
+
+void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) {
+#ifdef GLES_OVER_GL
+ builder.append("#version 330\n");
+ builder.append("#define USE_GLES_OVER_GL\n");
+#else
+ builder.append("#version 300 es\n");
+#endif
+
+ for (int i = 0; i < specialization_count; i++) {
+ if (p_specialization & (uint64_t(1) << uint64_t(i))) {
+ builder.append("#define " + String(specializations[i].name) + "\n");
+ }
+ }
+ if (p_version->uniforms.size()) {
+ builder.append("#define MATERIAL_UNIFORMS_USED\n");
+ }
+ for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
+ builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
+ }
+
+ builder.append("\n"); //make sure defines begin at newline
+ builder.append(general_defines.get_data());
+ builder.append(variant_defines[p_variant]);
+ 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
+
+ for (uint32_t i = 0; i < p_template.chunks.size(); i++) {
+ const StageTemplate::Chunk &chunk = p_template.chunks[i];
+ switch (chunk.type) {
+ case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
+ builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
+ } break;
+ case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
+ builder.append(p_version->vertex_globals.get_data()); // vertex globals
+ } break;
+ case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
+ builder.append(p_version->fragment_globals.get_data()); // fragment globals
+ } break;
+ case StageTemplate::Chunk::TYPE_CODE: {
+ if (p_version->code_sections.has(chunk.code)) {
+ builder.append(p_version->code_sections[chunk.code].get_data());
+ }
+ } break;
+ case StageTemplate::Chunk::TYPE_TEXT: {
+ builder.append(chunk.text.get_data());
+ } break;
+ }
+ }
+}
+
+static void _display_error_with_code(const String &p_error, const String &p_code) {
+ int line = 1;
+ Vector<String> lines = p_code.split("\n");
+
+ for (int j = 0; j < lines.size(); j++) {
+ print_line(itos(line) + ": " + lines[j]);
+ line++;
+ }
+
+ ERR_PRINT(p_error);
+}
+
+void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) {
+ spec.id = glCreateProgram();
+ spec.ok = false;
+ GLint status;
+
+ //vertex stage
+ {
+ StringBuilder builder;
+ _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization);
+
+ spec.vert_id = glCreateShader(GL_VERTEX_SHADER);
+ String builder_string = builder.as_string();
+ CharString cs = builder_string.utf8();
+ const char *cstr = cs.ptr();
+ glShaderSource(spec.vert_id, 1, &cstr, nullptr);
+ glCompileShader(spec.vert_id);
+
+ glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ GLsizei iloglen;
+ glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen);
+
+ if (iloglen < 0) {
+ glDeleteShader(spec.vert_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+
+ ERR_PRINT("No OpenGL vertex shader compiler log.");
+ } else {
+ if (iloglen == 0) {
+ iloglen = 4096; // buggy driver (Adreno 220+)
+ }
+
+ char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
+ ilogmem[iloglen] = '\0';
+ glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem);
+
+ String err_string = name + ": Vertex shader compilation failed:\n";
+
+ err_string += ilogmem;
+
+ _display_error_with_code(err_string, builder_string);
+
+ Memory::free_static(ilogmem);
+ glDeleteShader(spec.vert_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+ }
+
+ ERR_FAIL();
+ }
+ }
+
+ //fragment stage
+ {
+ StringBuilder builder;
+ _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization);
+
+ spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER);
+ String builder_string = builder.as_string();
+ CharString cs = builder_string.utf8();
+ const char *cstr = cs.ptr();
+ glShaderSource(spec.frag_id, 1, &cstr, nullptr);
+ glCompileShader(spec.frag_id);
+
+ glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ GLsizei iloglen;
+ glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen);
+
+ if (iloglen < 0) {
+ glDeleteShader(spec.frag_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+
+ ERR_PRINT("No OpenGL fragment shader compiler log.");
+ } else {
+ if (iloglen == 0) {
+ iloglen = 4096; // buggy driver (Adreno 220+)
+ }
+
+ char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
+ ilogmem[iloglen] = '\0';
+ glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem);
+
+ String err_string = name + ": Fragment shader compilation failed:\n";
+
+ err_string += ilogmem;
+
+ _display_error_with_code(err_string, builder_string);
+
+ Memory::free_static(ilogmem);
+ glDeleteShader(spec.frag_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+ }
+
+ ERR_FAIL();
+ }
+ }
+
+ glAttachShader(spec.id, spec.frag_id);
+ glAttachShader(spec.id, spec.vert_id);
+
+ //for (int i = 0; i < attribute_pair_count; i++) {
+ // glBindAttribLocation(v.id, attribute_pairs[i].index, attribute_pairs[i].name);
+ //}
+
+ glLinkProgram(spec.id);
+
+ glGetProgramiv(spec.id, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE) {
+ GLsizei iloglen;
+ glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen);
+
+ if (iloglen < 0) {
+ glDeleteShader(spec.frag_id);
+ glDeleteShader(spec.vert_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+
+ ERR_PRINT("No OpenGL program link log. What the frick?");
+ ERR_FAIL();
+ }
+
+ if (iloglen == 0) {
+ iloglen = 4096; // buggy driver (Adreno 220+)
+ }
+
+ char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
+ ilogmem[iloglen] = '\0';
+ glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem);
+
+ String err_string = name + ": Program linking failed:\n";
+
+ err_string += ilogmem;
+
+ _display_error_with_code(err_string, String());
+
+ Memory::free_static(ilogmem);
+ glDeleteShader(spec.frag_id);
+ glDeleteShader(spec.vert_id);
+ glDeleteProgram(spec.id);
+ spec.id = 0;
+
+ ERR_FAIL();
+ }
+
+ // get uniform locations
+
+ glUseProgram(spec.id);
+
+ spec.uniform_location.resize(uniform_count);
+ for (int i = 0; i < uniform_count; i++) {
+ spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]);
+ }
+
+ for (int i = 0; i < texunit_pair_count; i++) {
+ GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name);
+ if (loc >= 0) {
+ if (texunit_pairs[i].index < 0) {
+ glUniform1i(loc, max_image_units + texunit_pairs[i].index);
+ } else {
+ glUniform1i(loc, texunit_pairs[i].index);
+ }
+ }
+ }
+
+ for (int i = 0; i < ubo_count; i++) {
+ GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name);
+ if (loc >= 0) {
+ glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index);
+ }
+ }
+ // textures
+ for (int i = 0; i < p_version->texture_uniforms.size(); i++) {
+ String native_uniform_name = p_version->texture_uniforms[i];
+ GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data());
+ glUniform1i(location, i + base_texture_index);
+ }
+
+ glUseProgram(0);
+ spec.ok = true;
+}
+
+RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) {
+ Version *version = version_owner.get_or_null(p_version);
+ RS::ShaderNativeSourceCode source_code;
+ ERR_FAIL_COND_V(!version, source_code);
+
+ source_code.versions.resize(variant_count);
+
+ for (int i = 0; i < source_code.versions.size(); i++) {
+ //vertex stage
+
+ {
+ StringBuilder builder;
+ _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask);
+
+ RS::ShaderNativeSourceCode::Version::Stage stage;
+ stage.name = "vertex";
+ stage.code = builder.as_string();
+
+ source_code.versions.write[i].stages.push_back(stage);
+ }
+
+ //fragment stage
+ {
+ StringBuilder builder;
+ _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask);
+
+ RS::ShaderNativeSourceCode::Version::Stage stage;
+ stage.name = "fragment";
+ stage.code = builder.as_string();
+
+ source_code.versions.write[i].stages.push_back(stage);
+ }
+ }
+
+ return source_code;
+}
+
+String ShaderGLES3::_version_get_sha1(Version *p_version) const {
+ StringBuilder hash_build;
+
+ hash_build.append("[uniforms]");
+ hash_build.append(p_version->uniforms.get_data());
+ hash_build.append("[vertex_globals]");
+ hash_build.append(p_version->vertex_globals.get_data());
+ hash_build.append("[fragment_globals]");
+ hash_build.append(p_version->fragment_globals.get_data());
+
+ Vector<StringName> code_sections;
+ for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
+ code_sections.push_back(E.key);
+ }
+ code_sections.sort_custom<StringName::AlphCompare>();
+
+ for (int i = 0; i < code_sections.size(); i++) {
+ hash_build.append(String("[code:") + String(code_sections[i]) + "]");
+ hash_build.append(p_version->code_sections[code_sections[i]].get_data());
+ }
+ for (int i = 0; i < p_version->custom_defines.size(); i++) {
+ hash_build.append("[custom_defines:" + itos(i) + "]");
+ hash_build.append(p_version->custom_defines[i].get_data());
+ }
+
+ return hash_build.as_string().sha1_text();
+}
+
+//static const char *shader_file_header = "GLSC";
+//static const uint32_t cache_file_version = 2;
+
+bool ShaderGLES3::_load_from_cache(Version *p_version) {
+#if 0
+ String sha1 = _version_get_sha1(p_version);
+ String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache";
+
+ FileAccessRef f = FileAccess::open(path, FileAccess::READ);
+ if (!f) {
+ return false;
+ }
+
+ char header[5] = { 0, 0, 0, 0, 0 };
+ f->get_buffer((uint8_t *)header, 4);
+ ERR_FAIL_COND_V(header != String(shader_file_header), false);
+
+ uint32_t file_version = f->get_32();
+ if (file_version != cache_file_version) {
+ return false; // wrong version
+ }
+
+ uint32_t variant_count = f->get_32();
+
+ ERR_FAIL_COND_V(variant_count != (uint32_t)variant_count, false); //should not happen but check
+
+ for (uint32_t i = 0; i < variant_count; i++) {
+ uint32_t variant_size = f->get_32();
+ ERR_FAIL_COND_V(variant_size == 0 && variants_enabled[i], false);
+ if (!variants_enabled[i]) {
+ continue;
+ }
+ Vector<uint8_t> variant_bytes;
+ variant_bytes.resize(variant_size);
+
+ uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
+
+ ERR_FAIL_COND_V(br != variant_size, false);
+
+ p_version->variant_data[i] = variant_bytes;
+ }
+
+ for (uint32_t i = 0; i < variant_count; i++) {
+ if (!variants_enabled[i]) {
+ MutexLock lock(variant_set_mutex);
+ p_version->variants[i] = RID();
+ continue;
+ }
+ RID shader = GLES3::get_singleton()->shader_create_from_bytecode(p_version->variant_data[i]);
+ if (shader.is_null()) {
+ for (uint32_t j = 0; j < i; j++) {
+ GLES3::get_singleton()->free(p_version->variants[i]);
+ }
+ ERR_FAIL_COND_V(shader.is_null(), false);
+ }
+ {
+ MutexLock lock(variant_set_mutex);
+ p_version->variants[i] = shader;
+ }
+ }
+
+ memdelete_arr(p_version->variant_data); //clear stages
+ p_version->variant_data = nullptr;
+ p_version->valid = true;
+ return true;
+#endif
+ return false;
+}
+
+void ShaderGLES3::_save_to_cache(Version *p_version) {
+#if 0
+ String sha1 = _version_get_sha1(p_version);
+ String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache";
+
+ FileAccessRef f = FileAccess::open(path, FileAccess::WRITE);
+ ERR_FAIL_COND(!f);
+ f->store_buffer((const uint8_t *)shader_file_header, 4);
+ f->store_32(cache_file_version); //file version
+ uint32_t variant_count = variant_count;
+ f->store_32(variant_count); //variant count
+
+ for (uint32_t i = 0; i < variant_count; i++) {
+ 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
+}
+
+void ShaderGLES3::_clear_version(Version *p_version) {
+ // Variants not compiled yet, just return
+ if (p_version->variants.size() == 0) {
+ return;
+ }
+
+ 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) {
+ glDeleteShader(it.value->vert_id);
+ glDeleteShader(it.value->frag_id);
+ glDeleteProgram(it.value->id);
+ }
+ }
+ }
+
+ p_version->variants.clear();
+}
+
+void ShaderGLES3::_initialize_version(Version *p_version) {
+ ERR_FAIL_COND(p_version->variants.size() > 0);
+ p_version->variants.reserve(variant_count);
+ for (int i = 0; i < variant_count; i++) {
+ OAHashMap<uint64_t, Version::Specialization> variant;
+ p_version->variants.push_back(variant);
+ Version::Specialization spec;
+ _compile_specialization(spec, i, p_version, specialization_default_mask);
+ p_version->variants[i].insert(specialization_default_mask, spec);
+ }
+}
+
+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) {
+ Version *version = version_owner.get_or_null(p_version);
+ ERR_FAIL_COND(!version);
+
+ _clear_version(version); //clear if existing
+
+ version->vertex_globals = p_vertex_globals.utf8();
+ version->fragment_globals = p_fragment_globals.utf8();
+ version->uniforms = p_uniforms.utf8();
+ version->code_sections.clear();
+ version->texture_uniforms = p_texture_uniforms;
+ for (const KeyValue<String, String> &E : p_code) {
+ version->code_sections[StringName(E.key.to_upper())] = E.value.utf8();
+ }
+
+ version->custom_defines.clear();
+ for (int i = 0; i < p_custom_defines.size(); i++) {
+ version->custom_defines.push_back(p_custom_defines[i].utf8());
+ }
+
+ if (p_initialize) {
+ _initialize_version(version);
+ }
+}
+
+bool ShaderGLES3::version_is_valid(RID p_version) {
+ Version *version = version_owner.get_or_null(p_version);
+ return version != nullptr;
+}
+
+bool ShaderGLES3::version_free(RID p_version) {
+ if (version_owner.owns(p_version)) {
+ Version *version = version_owner.get_or_null(p_version);
+ _clear_version(version);
+ version_owner.free(p_version);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool ShaderGLES3::shader_cache_cleanup_on_start = false;
+
+ShaderGLES3::ShaderGLES3() {
+}
+
+void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture_index) {
+ general_defines = p_general_defines.utf8();
+ base_texture_index = p_base_texture_index;
+
+ _init();
+
+ if (shader_cache_dir != String()) {
+ StringBuilder hash_build;
+
+ hash_build.append("[base_hash]");
+ hash_build.append(base_sha256);
+ hash_build.append("[general_defines]");
+ hash_build.append(general_defines.get_data());
+ for (int i = 0; i < variant_count; i++) {
+ hash_build.append("[variant_defines:" + itos(i) + "]");
+ hash_build.append(variant_defines[i]);
+ }
+
+ base_sha256 = hash_build.as_string().sha256_text();
+
+ DirAccessRef d = DirAccess::open(shader_cache_dir);
+ ERR_FAIL_COND(!d);
+ if (d->change_dir(name) != OK) {
+ Error err = d->make_dir(name);
+ ERR_FAIL_COND(err != OK);
+ d->change_dir(name);
+ }
+
+ //erase other versions?
+ if (shader_cache_cleanup_on_start) {
+ }
+ //
+ if (d->change_dir(base_sha256) != OK) {
+ Error err = d->make_dir(base_sha256);
+ ERR_FAIL_COND(err != OK);
+ }
+ shader_cache_dir_valid = true;
+
+ print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
+ }
+
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units);
+}
+
+void ShaderGLES3::set_shader_cache_dir(const String &p_dir) {
+ shader_cache_dir = p_dir;
+}
+
+void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) {
+ shader_cache_save_compressed = p_enable;
+}
+
+void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) {
+ shader_cache_save_compressed_zstd = p_enable;
+}
+
+void ShaderGLES3::set_shader_cache_save_debug(bool p_enable) {
+ shader_cache_save_debug = p_enable;
+}
+
+String ShaderGLES3::shader_cache_dir;
+bool ShaderGLES3::shader_cache_save_compressed = true;
+bool ShaderGLES3::shader_cache_save_compressed_zstd = true;
+bool ShaderGLES3::shader_cache_save_debug = true;
+
+ShaderGLES3::~ShaderGLES3() {
+ List<RID> remaining;
+ version_owner.get_owned_list(&remaining);
+ if (remaining.size()) {
+ ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
+ while (remaining.size()) {
+ version_free(remaining.front()->get());
+ remaining.pop_front();
+ }
+ }
+}
+#endif
diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h
new file mode 100644
index 0000000000..f344ea047f
--- /dev/null
+++ b/drivers/gles3/shader_gles3.h
@@ -0,0 +1,246 @@
+/*************************************************************************/
+/* shader_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 SHADER_OPENGL_H
+#define SHADER_OPENGL_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/rid_owner.h"
+#include "core/variant/variant.h"
+#include "servers/rendering_server.h"
+
+#ifdef GLES3_ENABLED
+
+// 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
+
+#include <stdio.h>
+
+class ShaderGLES3 {
+protected:
+ struct TexUnitPair {
+ const char *name;
+ int index;
+ };
+
+ struct UBOPair {
+ const char *name;
+ int index;
+ };
+
+ struct Specialization {
+ const char *name;
+ bool default_value = false;
+ };
+
+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
+ // 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)
+ struct Version {
+ Vector<StringName> texture_uniforms;
+ CharString uniforms;
+ CharString vertex_globals;
+ CharString fragment_globals;
+ Map<StringName, CharString> code_sections;
+ Vector<CharString> custom_defines;
+
+ struct Specialization {
+ GLuint id;
+ GLuint vert_id;
+ GLuint frag_id;
+ LocalVector<GLint> uniform_location;
+ LocalVector<GLint> texture_uniform_locations;
+ Map<StringName, GLint> custom_uniform_locations;
+ bool build_queued = false;
+ bool ok = false;
+ Specialization() {
+ id = 0;
+ vert_id = 0;
+ frag_id = 0;
+ }
+ };
+
+ LocalVector<OAHashMap<uint64_t, Specialization>> variants;
+ };
+
+ Mutex variant_set_mutex;
+
+ void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization);
+
+ void _clear_version(Version *p_version);
+ void _initialize_version(Version *p_version);
+
+ RID_Owner<Version> version_owner;
+
+ struct StageTemplate {
+ struct Chunk {
+ enum Type {
+ TYPE_MATERIAL_UNIFORMS,
+ TYPE_VERTEX_GLOBALS,
+ TYPE_FRAGMENT_GLOBALS,
+ TYPE_CODE,
+ TYPE_TEXT
+ };
+
+ Type type;
+ StringName code;
+ CharString text;
+ };
+ LocalVector<Chunk> chunks;
+ };
+
+ String name;
+
+ String base_sha256;
+
+ static String shader_cache_dir;
+ static bool shader_cache_cleanup_on_start;
+ static bool shader_cache_save_compressed;
+ static bool shader_cache_save_compressed_zstd;
+ static bool shader_cache_save_debug;
+ bool shader_cache_dir_valid = false;
+
+ GLint max_image_units;
+
+ enum StageType {
+ STAGE_TYPE_VERTEX,
+ STAGE_TYPE_FRAGMENT,
+ STAGE_TYPE_MAX,
+ };
+
+ StageTemplate stage_templates[STAGE_TYPE_MAX];
+
+ void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization);
+
+ void _add_stage(const char *p_code, StageType p_stage_type);
+
+ String _version_get_sha1(Version *p_version) const;
+ bool _load_from_cache(Version *p_version);
+ void _save_to_cache(Version *p_version);
+
+ const char **uniform_names = nullptr;
+ int uniform_count = 0;
+ const UBOPair *ubo_pairs = nullptr;
+ int ubo_count = 0;
+ const TexUnitPair *texunit_pairs = nullptr;
+ int texunit_pair_count = 0;
+ int specialization_count = 0;
+ const Specialization *specializations = nullptr;
+ uint64_t specialization_default_mask = 0;
+ const char **variant_defines = nullptr;
+ int variant_count = 0;
+
+ int base_texture_index = 0;
+ Version::Specialization *current_shader = nullptr;
+
+protected:
+ ShaderGLES3();
+ void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants);
+
+ _FORCE_INLINE_ void _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) {
+ ERR_FAIL_INDEX(p_variant, variant_count);
+
+ Version *version = version_owner.get_or_null(p_version);
+ ERR_FAIL_COND(!version);
+
+ if (version->variants.size() == 0) {
+ _initialize_version(version); //may lack initialization
+ }
+
+ Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization);
+ if (!spec) {
+ if (false) {
+ // Queue load this specialization and use defaults in the meantime (TODO)
+
+ spec = version->variants[p_variant].lookup_ptr(specialization_default_mask);
+ } else {
+ // Compile on the spot
+ Version::Specialization s;
+ _compile_specialization(s, p_variant, version, p_specialization);
+ version->variants[p_variant].insert(p_specialization, s);
+ spec = version->variants[p_variant].lookup_ptr(p_specialization);
+ }
+ } else if (spec->build_queued) {
+ // Still queued, wait
+ spec = version->variants[p_variant].lookup_ptr(specialization_default_mask);
+ }
+
+ ERR_FAIL_COND(!spec); // Should never happen
+ ERR_FAIL_COND(!spec->ok); // Should never happen
+
+ glUseProgram(spec->id);
+ current_shader = spec;
+ }
+
+ _FORCE_INLINE_ int _version_get_uniform(int p_which, RID p_version, int p_variant, uint64_t p_specialization) {
+ 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];
+ }
+
+ virtual void _init() = 0;
+
+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);
+
+ bool version_is_valid(RID p_version);
+
+ bool version_free(RID p_version);
+
+ static void set_shader_cache_dir(const String &p_dir);
+ static void set_shader_cache_save_compressed(bool p_enable);
+ static void set_shader_cache_save_compressed_zstd(bool p_enable);
+ static void set_shader_cache_save_debug(bool p_enable);
+
+ RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
+
+ void initialize(const String &p_general_defines = "", int p_base_texture_index = 0);
+ virtual ~ShaderGLES3();
+};
+
+#endif // SHADER_OPENGL_H
+#endif
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
new file mode 100644
index 0000000000..2f56b77bdc
--- /dev/null
+++ b/drivers/gles3/shaders/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import("env")
+
+if "GLES3_GLSL" in env["BUILDERS"]:
+ env.GLES3_GLSL("canvas.glsl")
+ env.GLES3_GLSL("copy.glsl")
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
new file mode 100644
index 0000000000..8812447f6e
--- /dev/null
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -0,0 +1,753 @@
+/* clang-format off */
+#[modes]
+
+mode_quad =
+mode_ninepatch = #define USE_NINEPATCH
+mode_primitive = #define USE_PRIMITIVE
+mode_attributes = #define USE_ATTRIBUTES
+
+#[specializations]
+
+DISABLE_LIGHTING = false
+
+#[vertex]
+
+#ifdef USE_ATTRIBUTES
+layout(location = 0) in vec2 vertex_attrib;
+layout(location = 3) in vec4 color_attrib;
+layout(location = 4) in vec2 uv_attrib;
+
+layout(location = 10) in uvec4 bone_attrib;
+layout(location = 11) in vec4 weight_attrib;
+
+#endif
+/* clang-format on */
+#include "canvas_uniforms_inc.glsl"
+#include "stdlib_inc.glsl"
+
+uniform sampler2D transforms_texture; //texunit:-1
+
+out vec2 uv_interp;
+out vec4 color_interp;
+out vec2 vertex_interp;
+flat out int draw_data_instance;
+
+#ifdef USE_NINEPATCH
+
+out vec2 pixel_size_interp;
+
+#endif
+
+#ifdef MATERIAL_UNIFORMS_USED
+layout(std140) uniform MaterialUniforms{
+//ubo:4
+
+#MATERIAL_UNIFORMS
+
+};
+#endif
+
+#GLOBALS
+
+void main() {
+ vec4 instance_custom = vec4(0.0);
+ draw_data_instance = gl_InstanceID;
+#ifdef USE_PRIMITIVE
+
+ //weird bug,
+ //this works
+ vec2 vertex;
+ vec2 uv;
+ vec4 color;
+
+ if (gl_VertexID == 0) {
+ vertex = draw_data[draw_data_instance].point_a;
+ uv = draw_data[draw_data_instance].uv_a;
+ color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba));
+ } else if (gl_VertexID == 1) {
+ vertex = draw_data[draw_data_instance].point_b;
+ uv = draw_data[draw_data_instance].uv_b;
+ color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba));
+ } else {
+ vertex = draw_data[draw_data_instance].point_c;
+ uv = draw_data[draw_data_instance].uv_c;
+ color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba));
+ }
+ uvec4 bones = uvec4(0, 0, 0, 0);
+ vec4 bone_weights = vec4(0.0);
+
+#elif defined(USE_ATTRIBUTES)
+
+ 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;
+#else
+
+ vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
+ vec2 vertex_base = vertex_base_arr[gl_VertexID];
+
+ vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
+ vec4 color = draw_data[draw_data_instance].modulation;
+ vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0)));
+ uvec4 bones = uvec4(0, 0, 0, 0);
+
+#endif
+
+ 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));
+
+ // MultiMeshes don't batch, so always read from draw_data[0]
+ uint instancing = draw_data[0].flags & FLAGS_INSTANCING_MASK;
+
+#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;
+
+ 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);
+ model_matrix = model_matrix * matrix;
+ }
+ }
+*/
+#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
+ if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_PARTICLES)) {
+ //scale by texture size
+ vertex /= draw_data[draw_data_instance].color_texture_pixel_size;
+ }
+#endif
+
+#ifdef USE_POINT_SIZE
+ float point_size = 1.0;
+#endif
+ {
+#CODE : VERTEX
+ }
+
+#ifdef USE_NINEPATCH
+ pixel_size_interp = abs(draw_data[draw_data_instance].dst_rect.zw) * vertex_base;
+#endif
+
+#if !defined(SKIP_TRANSFORM_USED)
+ vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
+#endif
+
+ color_interp = color;
+
+ if (use_pixel_snap) {
+ vertex = floor(vertex + 0.5);
+ // precision issue on some hardware creates artifacts within texture
+ // offset uv by a small amount to avoid
+ uv += 1e-5;
+ }
+
+#ifdef USE_ATTRIBUTES
+#if 0
+ if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_SKELETON) && bone_weights != vec4(0.0)) { //must be a valid bone
+ //skeleton transform
+ ivec4 bone_indicesi = ivec4(bone_indices);
+
+ uvec2 tex_ofs = bone_indicesi.x * 2;
+
+ mat2x4 m;
+ m = mat2x4(
+ texelFetch(skeleton_buffer, tex_ofs + 0),
+ texelFetch(skeleton_buffer, tex_ofs + 1)) *
+ bone_weights.x;
+
+ tex_ofs = bone_indicesi.y * 2;
+
+ m += mat2x4(
+ texelFetch(skeleton_buffer, tex_ofs + 0),
+ texelFetch(skeleton_buffer, tex_ofs + 1)) *
+ bone_weights.y;
+
+ tex_ofs = bone_indicesi.z * 2;
+
+ m += mat2x4(
+ texelFetch(skeleton_buffer, tex_ofs + 0),
+ texelFetch(skeleton_buffer, tex_ofs + 1)) *
+ bone_weights.z;
+
+ tex_ofs = bone_indicesi.w * 2;
+
+ m += mat2x4(
+ texelFetch(skeleton_buffer, tex_ofs + 0),
+ texelFetch(skeleton_buffer, tex_ofs + 1)) *
+ bone_weights.w;
+
+ mat4 bone_matrix = skeleton_data.skeleton_transform * transpose(mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))) * skeleton_data.skeleton_transform_inverse;
+
+ //outvec = bone_matrix * outvec;
+ }
+#endif
+#endif
+
+ vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
+
+ vertex_interp = vertex;
+ uv_interp = uv;
+
+ gl_Position = screen_transform * vec4(vertex, 0.0, 1.0);
+
+#ifdef USE_POINT_SIZE
+ gl_PointSize = point_size;
+#endif
+}
+
+#[fragment]
+
+#include "canvas_uniforms_inc.glsl"
+#include "stdlib_inc.glsl"
+
+uniform sampler2D atlas_texture; //texunit:-2
+uniform sampler2D shadow_atlas_texture; //texunit:-3
+uniform sampler2D screen_texture; //texunit:-4
+uniform sampler2D sdf_texture; //texunit:-5
+uniform sampler2D normal_texture; //texunit:-6
+uniform sampler2D specular_texture; //texunit:-7
+
+uniform sampler2D color_texture; //texunit:0
+
+in vec2 uv_interp;
+in vec4 color_interp;
+in vec2 vertex_interp;
+flat in int draw_data_instance;
+
+#ifdef USE_NINEPATCH
+
+in vec2 pixel_size_interp;
+
+#endif
+
+layout(location = 0) out vec4 frag_color;
+
+#ifdef MATERIAL_UNIFORMS_USED
+uniform MaterialUniforms{
+//ubo:4
+
+#MATERIAL_UNIFORMS
+
+};
+#endif
+
+vec2 screen_uv_to_sdf(vec2 p_uv) {
+ return screen_to_sdf * p_uv;
+}
+
+float texture_sdf(vec2 p_sdf) {
+ vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
+ float d = texture(sdf_texture, uv).r;
+ d *= SDF_MAX_LENGTH;
+ return d * tex_to_sdf;
+}
+
+vec2 texture_sdf_normal(vec2 p_sdf) {
+ vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
+
+ const float EPSILON = 0.001;
+ return normalize(vec2(
+ texture(sdf_texture, uv + vec2(EPSILON, 0.0)).r - texture(sdf_texture, uv - vec2(EPSILON, 0.0)).r,
+ texture(sdf_texture, uv + vec2(0.0, EPSILON)).r - texture(sdf_texture, uv - vec2(0.0, EPSILON)).r));
+}
+
+vec2 sdf_to_screen_uv(vec2 p_sdf) {
+ return p_sdf * sdf_to_screen;
+}
+
+#GLOBALS
+
+#ifdef LIGHT_CODE_USED
+
+vec4 light_compute(
+ vec3 light_vertex,
+ vec3 light_position,
+ vec3 normal,
+ vec4 light_color,
+ float light_energy,
+ vec4 specular_shininess,
+ inout vec4 shadow_modulate,
+ vec2 screen_uv,
+ vec2 uv,
+ vec4 color, bool is_directional) {
+ vec4 light = vec4(0.0);
+
+#CODE : LIGHT
+
+ return light;
+}
+
+#endif
+
+#ifdef USE_NINEPATCH
+
+float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
+ float tex_size = 1.0 / tex_pixel_size;
+
+ if (pixel < margin_begin) {
+ return pixel * tex_pixel_size;
+ } else if (pixel >= draw_size - margin_end) {
+ return (tex_size - (draw_size - pixel)) * tex_pixel_size;
+ } else {
+ if (!bool(draw_data[draw_data_instance].flags & FLAGS_NINEPACH_DRAW_CENTER)) {
+ draw_center--;
+ }
+
+ // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
+ if (np_repeat == 0) { // Stretch.
+ // Convert to ratio.
+ float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
+ // Scale to source texture.
+ return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
+ } else if (np_repeat == 1) { // Tile.
+ // Convert to offset.
+ float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
+ // Scale to source texture.
+ return (margin_begin + ofs) * tex_pixel_size;
+ } else if (np_repeat == 2) { // Tile Fit.
+ // Calculate scale.
+ float src_area = draw_size - margin_begin - margin_end;
+ float dst_area = tex_size - margin_begin - margin_end;
+ float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
+ // Convert to ratio.
+ float ratio = (pixel - margin_begin) / src_area;
+ ratio = mod(ratio * scale, 1.0);
+ // Scale to source texture.
+ return (margin_begin + ratio * dst_area) * tex_pixel_size;
+ } else { // Shouldn't happen, but silences compiler warning.
+ return 0.0;
+ }
+ }
+}
+
+#endif
+
+vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
+ float cNdotL = max(0.0, dot(normal, light_vec));
+
+ if (specular_shininess_used) {
+ //blinn
+ vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
+ vec3 half_vec = normalize(view + light_vec);
+
+ float cNdotV = max(dot(normal, view), 0.0);
+ float cNdotH = max(dot(normal, half_vec), 0.0);
+ float cVdotH = max(dot(view, half_vec), 0.0);
+ float cLdotH = max(dot(light_vec, half_vec), 0.0);
+ float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
+ float blinn = pow(cNdotH, shininess);
+ blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
+ float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
+
+ return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
+ } else {
+ return light_color * base_color * cNdotL;
+ }
+}
+
+//float distance = length(shadow_pos);
+vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
+#ifdef LIGHT_CODE_USED
+ ,
+ vec3 shadow_modulate
+#endif
+) {
+ float shadow;
+ uint shadow_mode = light_data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
+
+ if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
+ shadow = textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x;
+ } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
+ vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+ shadow = 0.0;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+ shadow /= 5.0;
+ } else { //PCF13
+ vec4 shadow_pixel_size = vec4(light_data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+ shadow = 0.0;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv - shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
+ shadow += textureProjLod(shadow_atlas_texture, shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
+ shadow /= 13.0;
+ }
+
+ vec4 shadow_color = unpackUnorm4x8(light_data[light_base].shadow_color);
+#ifdef LIGHT_CODE_USED
+ shadow_color.rgb *= shadow_modulate;
+#endif
+
+ shadow_color.a *= light_color.a; //respect light alpha
+
+ return mix(light_color, shadow_color, shadow);
+}
+
+void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
+ uint blend_mode = light_data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
+
+ switch (blend_mode) {
+ case LIGHT_FLAGS_BLEND_MODE_ADD: {
+ color.rgb += light_color.rgb * light_color.a;
+ } break;
+ case LIGHT_FLAGS_BLEND_MODE_SUB: {
+ color.rgb -= light_color.rgb * light_color.a;
+ } break;
+ case LIGHT_FLAGS_BLEND_MODE_MIX: {
+ color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
+ } break;
+ }
+}
+
+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;
+ vec2 vertex = vertex_interp;
+
+#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
+
+#ifdef USE_NINEPATCH
+
+ int draw_center = 2;
+ uv = vec2(
+ map_ninepatch_axis(pixel_size_interp.x, abs(draw_data[draw_data_instance].dst_rect.z), draw_data[draw_data_instance].color_texture_pixel_size.x, draw_data[draw_data_instance].ninepatch_margins.x, draw_data[draw_data_instance].ninepatch_margins.z, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
+ map_ninepatch_axis(pixel_size_interp.y, abs(draw_data[draw_data_instance].dst_rect.w), draw_data[draw_data_instance].color_texture_pixel_size.y, draw_data[draw_data_instance].ninepatch_margins.y, draw_data[draw_data_instance].ninepatch_margins.w, int(draw_data[draw_data_instance].flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
+
+ if (draw_center == 0) {
+ color.a = 0.0;
+ }
+
+ uv = uv * draw_data[draw_data_instance].src_rect.zw + draw_data[draw_data_instance].src_rect.xy; //apply region if needed
+
+#endif
+ if (bool(draw_data[draw_data_instance].flags & FLAGS_CLIP_RECT_UV)) {
+ uv = clamp(uv, draw_data[draw_data_instance].src_rect.xy, draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw));
+ }
+
+#endif
+
+#ifndef USE_PRIMITIVE
+ if (bool(draw_data[draw_data_instance].flags & FLAGS_USE_MSDF)) {
+ float px_range = draw_data[draw_data_instance].ninepatch_margins.x;
+ float outline_thickness = draw_data[draw_data_instance].ninepatch_margins.y;
+ //float reserved1 = draw_data[draw_data_instance].ninepatch_margins.z;
+ //float reserved2 = draw_data[draw_data_instance].ninepatch_margins.w;
+
+ vec4 msdf_sample = texture(color_texture, uv);
+ vec2 msdf_size = vec2(textureSize(color_texture, 0));
+ vec2 dest_size = vec2(1.0) / fwidth(uv);
+ 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;
+ float a = clamp((d + cr) * px_size, 0.0, 1.0);
+ color.a = a * color.a;
+ } else {
+ float a = clamp(d * px_size + 0.5, 0.0, 1.0);
+ color.a = a * color.a;
+ }
+
+ } else {
+#else
+ {
+#endif
+ color *= texture(color_texture, uv);
+ }
+
+ uint light_count = (draw_data[draw_data_instance].flags >> FLAGS_LIGHT_COUNT_SHIFT) & uint(0xF); //max 16 lights
+ bool using_light = light_count > uint(0) || directional_light_count > uint(0);
+
+ vec3 normal;
+
+#if defined(NORMAL_USED)
+ bool normal_used = true;
+#else
+ bool normal_used = false;
+#endif
+
+ if (normal_used || (using_light && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
+ normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
+ normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
+ normal_used = true;
+ } else {
+ normal = vec3(0.0, 0.0, 1.0);
+ }
+
+ vec4 specular_shininess;
+
+#if defined(SPECULAR_SHININESS_USED)
+
+ bool specular_shininess_used = true;
+#else
+ bool specular_shininess_used = false;
+#endif
+
+ if (specular_shininess_used || (using_light && normal_used && bool(draw_data[draw_data_instance].flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
+ specular_shininess = texture(specular_texture, uv);
+ specular_shininess *= unpackUnorm4x8(draw_data[draw_data_instance].specular_shininess);
+ specular_shininess_used = true;
+ } else {
+ specular_shininess = vec4(1.0);
+ }
+
+#if defined(SCREEN_UV_USED)
+ vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
+#else
+ vec2 screen_uv = vec2(0.0);
+#endif
+
+ vec3 light_vertex = vec3(vertex, 0.0);
+ vec2 shadow_vertex = vertex;
+
+ {
+ float normal_map_depth = 1.0;
+
+#if defined(NORMAL_MAP_USED)
+ vec3 normal_map = vec3(0.0, 0.0, 1.0);
+ normal_used = true;
+#endif
+
+#CODE : FRAGMENT
+
+#if defined(NORMAL_MAP_USED)
+ normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth);
+#endif
+ }
+
+ if (normal_used) {
+ //convert by item transform
+ normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy;
+ //convert by canvas transform
+ normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz);
+ }
+
+ vec3 base_color = color.rgb;
+ if (bool(draw_data[draw_data_instance].flags & FLAGS_USING_LIGHT_MASK)) {
+ color = vec4(0.0); //invisible by default due to using light mask
+ }
+
+#ifdef MODE_LIGHT_ONLY
+ color = vec4(0.0);
+#else
+ color *= canvas_modulation;
+#endif
+
+#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED)
+
+ for (uint i = uint(0); i < directional_light_count; i++) {
+ uint light_base = i;
+
+ vec2 direction = light_data[light_base].position;
+ vec4 light_color = light_data[light_base].color;
+
+#ifdef LIGHT_CODE_USED
+
+ vec4 shadow_modulate = vec4(1.0);
+ light_color = light_compute(light_vertex, vec3(direction, light_data[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, true);
+#else
+
+ if (normal_used) {
+ vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_data[light_base].height));
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
+ }
+#endif
+
+ if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+
+ vec4 shadow_uv = vec4(shadow_pos.x, light_data[light_base].shadow_y_ofs, shadow_pos.y * light_data[light_base].shadow_zfar_inv, 1.0);
+
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_CODE_USED
+ ,
+ shadow_modulate.rgb
+#endif
+ );
+ }
+
+ light_blend_compute(light_base, light_color, color.rgb);
+ }
+
+ // Positional Lights
+
+ for (uint i = uint(0); i < MAX_LIGHTS_PER_ITEM; i++) {
+ if (i >= light_count) {
+ break;
+ }
+ uint light_base;
+ if (i < uint(8)) {
+ if (i < uint(4)) {
+ light_base = draw_data[draw_data_instance].lights.x;
+ } else {
+ light_base = draw_data[draw_data_instance].lights.y;
+ }
+ } else {
+ if (i < uint(12)) {
+ light_base = draw_data[draw_data_instance].lights.z;
+ } else {
+ light_base = draw_data[draw_data_instance].lights.w;
+ }
+ }
+ light_base >>= (i & uint(3)) * uint(8);
+ light_base &= uint(0xFF);
+
+ vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_data[light_base].texture_matrix[0], light_data[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+ vec2 tex_uv_atlas = tex_uv * light_data[light_base].atlas_rect.zw + light_data[light_base].atlas_rect.xy;
+ vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0);
+ vec4 light_base_color = light_data[light_base].color;
+
+#ifdef LIGHT_CODE_USED
+
+ vec4 shadow_modulate = vec4(1.0);
+ vec3 light_position = vec3(light_data[light_base].position, light_data[light_base].height);
+
+ light_color.rgb *= light_base_color.rgb;
+ light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, color, false);
+#else
+
+ light_color.rgb *= light_base_color.rgb * light_base_color.a;
+
+ if (normal_used) {
+ vec3 light_pos = vec3(light_data[light_base].position, light_data[light_base].height);
+ vec3 pos = light_vertex;
+ vec3 light_vec = normalize(light_pos - pos);
+ float cNdotL = max(0.0, dot(normal, light_vec));
+
+ light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
+ }
+#endif
+ if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
+ //if outside the light texture, light color is zero
+ light_color.a = 0.0;
+ }
+
+ if (bool(light_data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_data[light_base].shadow_matrix[0], light_data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+
+ vec2 pos_norm = normalize(shadow_pos);
+ vec2 pos_abs = abs(pos_norm);
+ vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
+ vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
+ float tex_ofs;
+ float distance;
+ if (pos_rot.y > 0) {
+ if (pos_rot.x > 0) {
+ tex_ofs = pos_box.y * 0.125 + 0.125;
+ distance = shadow_pos.x;
+ } else {
+ tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
+ distance = shadow_pos.y;
+ }
+ } else {
+ if (pos_rot.x < 0) {
+ tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
+ distance = -shadow_pos.x;
+ } else {
+ tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
+ distance = -shadow_pos.y;
+ }
+ }
+
+ distance *= light_data[light_base].shadow_zfar_inv;
+
+ //float distance = length(shadow_pos);
+ vec4 shadow_uv = vec4(tex_ofs, light_data[light_base].shadow_y_ofs, distance, 1.0);
+
+ light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_CODE_USED
+ ,
+ shadow_modulate.rgb
+#endif
+ );
+ }
+
+ light_blend_compute(light_base, light_color, color.rgb);
+ }
+#endif // UNSHADED
+
+ frag_color = color;
+}
diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl
new file mode 100644
index 0000000000..94485abd11
--- /dev/null
+++ b/drivers/gles3/shaders/canvas_shadow.glsl
@@ -0,0 +1,60 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+layout(location = 0) in highp vec3 vertex;
+
+uniform highp mat4 projection_matrix;
+/* clang-format on */
+uniform highp mat4 light_matrix;
+uniform highp mat4 model_matrix;
+uniform highp float distance_norm;
+
+out highp vec4 position_interp;
+
+void main() {
+ gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0)));
+ position_interp = gl_Position;
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+#endif
+
+in highp vec4 position_interp;
+/* clang-format on */
+
+void main() {
+ highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias
+
+#ifdef USE_RGBA_SHADOWS
+
+ highp vec4 comp = fract(depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
+ comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
+ frag_color = comp;
+#else
+
+ frag_color = vec4(depth);
+#endif
+}
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
new file mode 100644
index 0000000000..e08a15e59d
--- /dev/null
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -0,0 +1,120 @@
+
+#define MAX_LIGHTS_PER_ITEM uint(16)
+
+#define M_PI 3.14159265359
+
+#define SDF_MAX_LENGTH 16384.0
+
+//1 means enabled, 2+ means trails in use
+#define FLAGS_INSTANCING_MASK uint(0x7F)
+#define FLAGS_INSTANCING_HAS_COLORS uint(1 << 7)
+#define FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << 8)
+
+#define FLAGS_CLIP_RECT_UV uint(1 << 9)
+#define FLAGS_TRANSPOSE_RECT uint(1 << 10)
+#define FLAGS_USING_LIGHT_MASK uint(1 << 11)
+#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
+#define FLAGS_USING_PARTICLES uint(1 << 13)
+
+#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
+#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
+
+#define FLAGS_LIGHT_COUNT_SHIFT 20
+
+#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26)
+#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27)
+
+#define FLAGS_USE_MSDF uint(1 << 28)
+
+// must be always 128 bytes long
+struct DrawData {
+ vec2 world_x;
+ vec2 world_y;
+ vec2 world_ofs;
+ vec2 color_texture_pixel_size;
+#ifdef USE_PRIMITIVE
+ vec2 point_a;
+ vec2 point_b;
+ vec2 point_c;
+ vec2 uv_a;
+ vec2 uv_b;
+ vec2 uv_c;
+ uint color_a_rg;
+ uint color_a_ba;
+ uint color_b_rg;
+ uint color_b_ba;
+ uint color_c_rg;
+ uint color_c_ba;
+#else
+ vec4 modulation;
+ vec4 ninepatch_margins;
+ vec4 dst_rect; //for built-in rect and UV
+ vec4 src_rect;
+ uint pad;
+ uint pad2;
+#endif
+ uint flags;
+ uint specular_shininess;
+ uvec4 lights;
+};
+
+layout(std140) uniform GlobalVariableData { //ubo:1
+ vec4 global_variables[MAX_GLOBAL_VARIABLES];
+};
+
+layout(std140) uniform CanvasData { //ubo:0
+ mat4 canvas_transform;
+ mat4 screen_transform;
+ mat4 canvas_normal_transform;
+ vec4 canvas_modulation;
+ vec2 screen_pixel_size;
+ float time;
+ bool use_pixel_snap;
+
+ vec4 sdf_to_tex;
+ vec2 screen_to_sdf;
+ vec2 sdf_to_screen;
+
+ uint directional_light_count;
+ float tex_to_sdf;
+ uint pad1;
+ uint pad2;
+};
+
+#define LIGHT_FLAGS_BLEND_MASK uint(3 << 16)
+#define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16)
+#define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16)
+#define LIGHT_FLAGS_BLEND_MODE_MIX uint(2 << 16)
+#define LIGHT_FLAGS_BLEND_MODE_MASK uint(3 << 16)
+#define LIGHT_FLAGS_HAS_SHADOW uint(1 << 20)
+#define LIGHT_FLAGS_FILTER_SHIFT 22
+#define LIGHT_FLAGS_FILTER_MASK uint(3 << 22)
+#define LIGHT_FLAGS_SHADOW_NEAREST uint(0 << 22)
+#define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22)
+#define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22)
+
+struct Light {
+ mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
+ mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
+ vec4 color;
+
+ uint shadow_color; // packed
+ uint flags; //index to light texture
+ float shadow_pixel_size;
+ float height;
+
+ vec2 position;
+ float shadow_zfar_inv;
+ float shadow_y_ofs;
+
+ vec4 atlas_rect;
+};
+
+layout(std140) uniform LightData { //ubo:2
+ Light light_data[MAX_LIGHTS];
+};
+
+layout(std140) uniform DrawDataInstances { //ubo:3
+
+ DrawData draw_data[MAX_DRAW_DATA_INSTANCES];
+};
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
new file mode 100644
index 0000000000..62332a15a7
--- /dev/null
+++ b/drivers/gles3/shaders/copy.glsl
@@ -0,0 +1,204 @@
+/* clang-format off */
+#[modes]
+
+mode_default =
+mode_cubemap = #define USE_CUBEMAP
+mode_panorama = #define USE_PANORAMA
+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
+
+#[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;
+
+#if defined(USE_CUBEMAP) || defined(USE_PANORAMA)
+out vec3 cube_interp;
+#else
+out vec2 uv_interp;
+#endif
+out vec2 uv2_interp;
+
+#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;
+
+#ifdef USE_COPY_SECTION
+ 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;
+#endif
+
+#ifdef USE_CUSTOM_ALPHA
+uniform float custom_alpha;
+#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
+
+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
+ vec4 color = texture(source, uv_interp);
+#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;
+#endif
+
+ frag_color = color;
+}
diff --git a/drivers/gles3/shaders/cube_to_dp.glsl b/drivers/gles3/shaders/cube_to_dp.glsl
new file mode 100644
index 0000000000..2384529a89
--- /dev/null
+++ b/drivers/gles3/shaders/cube_to_dp.glsl
@@ -0,0 +1,100 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision mediump float;
+precision mediump int;
+#endif
+
+layout(location = 0) in highp vec4 vertex_attrib;
+/* clang-format on */
+layout(location = 4) in vec2 uv_in;
+
+out vec2 uv_interp;
+
+void main() {
+ uv_interp = uv_in;
+ gl_Position = vertex_attrib;
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+#endif
+
+uniform highp samplerCube source_cube; //texunit:0
+/* clang-format on */
+in vec2 uv_interp;
+
+uniform bool z_flip;
+uniform highp float z_far;
+uniform highp float z_near;
+uniform highp float bias;
+
+void main() {
+ highp vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0);
+ /*
+ if (z_flip) {
+ normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+ } else {
+ normal.z = -0.5 + 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+ }
+ */
+
+ //normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
+ //normal.xy *= 1.0 + normal.z;
+
+ normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+ normal = normalize(normal);
+ /*
+ normal.z = 0.5;
+ normal = normalize(normal);
+ */
+
+ if (!z_flip) {
+ normal.z = -normal.z;
+ }
+
+ //normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 ));
+ float depth = textureCube(source_cube, normal).r;
+
+ // absolute values for direction cosines, bigger value equals closer to basis axis
+ vec3 unorm = abs(normal);
+
+ if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
+ // x code
+ unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
+ } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
+ // y code
+ unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
+ } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
+ // z code
+ unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
+ } else {
+ // oh-no we messed up code
+ // has to be
+ unorm = vec3(1.0, 0.0, 0.0);
+ }
+
+ float depth_fix = 1.0 / dot(normal, unorm);
+
+ depth = 2.0 * depth - 1.0;
+ float linear_depth = 2.0 * z_near * z_far / (z_far + z_near - depth * (z_far - z_near));
+ gl_FragDepth = (linear_depth * depth_fix + bias) / z_far;
+}
diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/cubemap_filter.glsl
new file mode 100644
index 0000000000..2081abfef6
--- /dev/null
+++ b/drivers/gles3/shaders/cubemap_filter.glsl
@@ -0,0 +1,214 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+layout(location = 0) in highp vec2 vertex;
+/* 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);
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+
+#endif
+
+#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
+
+#endif
+
+#ifdef USE_SOURCE_PANORAMA
+
+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 textureLod(pano, st, 0.0);
+}
+
+#endif
+
+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
+
+ // +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
+
+ // -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
+
+ // +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
+
+ // -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
+
+ // +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
+
+ // 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;
+ }
+ }
+ 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;
+ 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
+
+#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;
+ }
+ }
+
+ 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))));
+
+ frag_color = vec4(sum.rgb, 1.0);
+#endif
+}
diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl
new file mode 100644
index 0000000000..c9184cca77
--- /dev/null
+++ b/drivers/gles3/shaders/effect_blur.glsl
@@ -0,0 +1,291 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+layout(location = 0) in vec2 vertex_attrib;
+/* clang-format on */
+layout(location = 4) in vec2 uv_in;
+
+out vec2 uv_interp;
+
+#ifdef USE_BLUR_SECTION
+
+uniform vec4 blur_section;
+
+#endif
+
+void main() {
+ uv_interp = uv_in;
+ gl_Position = vec4(vertex_attrib, 0.0, 1.0);
+#ifdef USE_BLUR_SECTION
+
+ uv_interp = blur_section.xy + uv_interp * blur_section.zw;
+ gl_Position.xy = (blur_section.xy + (gl_Position.xy * 0.5 + 0.5) * blur_section.zw) * 2.0 - 1.0;
+#endif
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+#endif
+
+in vec2 uv_interp;
+/* clang-format on */
+uniform sampler2D source_color; //texunit:0
+
+uniform float lod;
+uniform vec2 pixel_size;
+
+#if defined(GLOW_GAUSSIAN_HORIZONTAL) || defined(GLOW_GAUSSIAN_VERTICAL)
+
+uniform float glow_strength;
+
+#endif
+
+#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
+
+#ifdef USE_GLES_OVER_GL
+#ifdef DOF_QUALITY_LOW
+const int dof_kernel_size = 5;
+const int dof_kernel_from = 2;
+const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
+#endif
+
+#ifdef DOF_QUALITY_MEDIUM
+const int dof_kernel_size = 11;
+const int dof_kernel_from = 5;
+const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
+
+#endif
+
+#ifdef DOF_QUALITY_HIGH
+const int dof_kernel_size = 21;
+const int dof_kernel_from = 10;
+const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
+#endif
+#endif
+
+uniform sampler2D dof_source_depth; //texunit:1
+uniform float dof_begin;
+uniform float dof_end;
+uniform vec2 dof_dir;
+uniform float dof_radius;
+
+#endif
+
+#ifdef GLOW_FIRST_PASS
+
+uniform highp float luminance_cap;
+
+uniform float glow_bloom;
+uniform float glow_hdr_threshold;
+uniform float glow_hdr_scale;
+
+#endif
+
+uniform float camera_z_far;
+uniform float camera_z_near;
+
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+#ifdef GLOW_GAUSSIAN_HORIZONTAL
+ vec2 pix_size = pixel_size;
+ pix_size *= 0.5; //reading from larger buffer, so use more samples
+ vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.174938;
+ color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.165569;
+ color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.140367;
+ color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.106595;
+ color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.165569;
+ color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.140367;
+ color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.106595;
+ color *= glow_strength;
+ frag_color = color;
+#endif
+
+#ifdef GLOW_GAUSSIAN_VERTICAL
+ vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pixel_size, lod) * 0.288713;
+ color += textureLod(source_color, uv_interp + vec2(0.0, 1.0) * pixel_size, lod) * 0.233062;
+ color += textureLod(source_color, uv_interp + vec2(0.0, 2.0) * pixel_size, lod) * 0.122581;
+ color += textureLod(source_color, uv_interp + vec2(0.0, -1.0) * pixel_size, lod) * 0.233062;
+ color += textureLod(source_color, uv_interp + vec2(0.0, -2.0) * pixel_size, lod) * 0.122581;
+ color *= glow_strength;
+ frag_color = color;
+#endif
+
+#ifndef USE_GLES_OVER_GL
+#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
+
+#ifdef DOF_QUALITY_LOW
+ const int dof_kernel_size = 5;
+ const int dof_kernel_from = 2;
+ float dof_kernel[5];
+ dof_kernel[0] = 0.153388;
+ dof_kernel[1] = 0.221461;
+ dof_kernel[2] = 0.250301;
+ dof_kernel[3] = 0.221461;
+ dof_kernel[4] = 0.153388;
+#endif
+
+#ifdef DOF_QUALITY_MEDIUM
+ const int dof_kernel_size = 11;
+ const int dof_kernel_from = 5;
+ float dof_kernel[11];
+ dof_kernel[0] = 0.055037;
+ dof_kernel[1] = 0.072806;
+ dof_kernel[2] = 0.090506;
+ dof_kernel[3] = 0.105726;
+ dof_kernel[4] = 0.116061;
+ dof_kernel[5] = 0.119726;
+ dof_kernel[6] = 0.116061;
+ dof_kernel[7] = 0.105726;
+ dof_kernel[8] = 0.090506;
+ dof_kernel[9] = 0.072806;
+ dof_kernel[10] = 0.055037;
+#endif
+
+#ifdef DOF_QUALITY_HIGH
+ const int dof_kernel_size = 21;
+ const int dof_kernel_from = 10;
+ float dof_kernel[21];
+ dof_kernel[0] = 0.028174;
+ dof_kernel[1] = 0.032676;
+ dof_kernel[2] = 0.037311;
+ dof_kernel[3] = 0.041944;
+ dof_kernel[4] = 0.046421;
+ dof_kernel[5] = 0.050582;
+ dof_kernel[6] = 0.054261;
+ dof_kernel[7] = 0.057307;
+ dof_kernel[8] = 0.059587;
+ dof_kernel[9] = 0.060998;
+ dof_kernel[10] = 0.061476;
+ dof_kernel[11] = 0.060998;
+ dof_kernel[12] = 0.059587;
+ dof_kernel[13] = 0.057307;
+ dof_kernel[14] = 0.054261;
+ dof_kernel[15] = 0.050582;
+ dof_kernel[16] = 0.046421;
+ dof_kernel[17] = 0.041944;
+ dof_kernel[18] = 0.037311;
+ dof_kernel[19] = 0.032676;
+ dof_kernel[20] = 0.028174;
+#endif
+#endif
+#endif //!USE_GLES_OVER_GL
+
+#ifdef DOF_FAR_BLUR
+
+ vec4 color_accum = vec4(0.0);
+
+ float depth = textureLod(dof_source_depth, uv_interp, 0.0).r;
+ depth = depth * 2.0 - 1.0;
+#ifdef USE_ORTHOGONAL_PROJECTION
+ depth = ((depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+#else
+ depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
+#endif
+
+ float amount = smoothstep(dof_begin, dof_end, depth);
+ float k_accum = 0.0;
+
+ for (int i = 0; i < dof_kernel_size; i++) {
+ int int_ofs = i - dof_kernel_from;
+ vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
+
+ float tap_k = dof_kernel[i];
+
+ float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
+ tap_depth = tap_depth * 2.0 - 1.0;
+#ifdef USE_ORTHOGONAL_PROJECTION
+ tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+#else
+ tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
+#endif
+ float tap_amount = int_ofs == 0 ? 1.0 : smoothstep(dof_begin, dof_end, tap_depth);
+ tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+
+ vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k;
+
+ k_accum += tap_k * tap_amount;
+ color_accum += tap_color * tap_amount;
+ }
+
+ if (k_accum > 0.0) {
+ color_accum /= k_accum;
+ }
+
+ frag_color = color_accum; ///k_accum;
+
+#endif
+
+#ifdef DOF_NEAR_BLUR
+
+ vec4 color_accum = vec4(0.0);
+
+ float max_accum = 0.0;
+
+ for (int i = 0; i < dof_kernel_size; i++) {
+ int int_ofs = i - dof_kernel_from;
+ vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius;
+ float ofs_influence = max(0.0, 1.0 - abs(float(int_ofs)) / float(dof_kernel_from));
+
+ float tap_k = dof_kernel[i];
+
+ vec4 tap_color = textureLod(source_color, tap_uv, 0.0);
+
+ float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
+ tap_depth = tap_depth * 2.0 - 1.0;
+#ifdef USE_ORTHOGONAL_PROJECTION
+ tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
+#else
+ tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
+#endif
+ float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
+ tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
+
+#ifdef DOF_NEAR_FIRST_TAP
+
+ tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
+
+#endif
+
+ max_accum = max(max_accum, tap_amount * ofs_influence);
+
+ color_accum += tap_color * tap_k;
+ }
+
+ color_accum.a = max(color_accum.a, sqrt(max_accum));
+
+ frag_color = color_accum;
+
+#endif
+
+#ifdef GLOW_FIRST_PASS
+
+ float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
+ float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, luminance), glow_bloom);
+
+ frag_color = min(frag_color * feedback, vec4(luminance_cap));
+
+#endif
+}
diff --git a/drivers/gles3/shaders/lens_distorted.glsl b/drivers/gles3/shaders/lens_distorted.glsl
new file mode 100644
index 0000000000..3aaf1050e5
--- /dev/null
+++ b/drivers/gles3/shaders/lens_distorted.glsl
@@ -0,0 +1,86 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+layout(location = 0) in highp vec2 vertex;
+/* clang-format on */
+
+uniform vec2 offset;
+uniform vec2 scale;
+
+out vec2 uv_interp;
+
+void main() {
+ uv_interp = vertex.xy * 2.0 - 1.0;
+
+ vec2 v = vertex.xy * scale + offset;
+ gl_Position = vec4(v, 0.0, 1.0);
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+#endif
+
+uniform sampler2D source; //texunit:0
+/* clang-format on */
+
+uniform vec2 eye_center;
+uniform float k1;
+uniform float k2;
+uniform float upscale;
+uniform float aspect_ratio;
+
+in vec2 uv_interp;
+
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ vec2 coords = uv_interp;
+ vec2 offset = coords - eye_center;
+
+ // take aspect ratio into account
+ offset.y /= aspect_ratio;
+
+ // distort
+ vec2 offset_sq = offset * offset;
+ float radius_sq = offset_sq.x + offset_sq.y;
+ float radius_s4 = radius_sq * radius_sq;
+ float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
+ offset *= distortion_scale;
+
+ // reapply aspect ratio
+ offset.y *= aspect_ratio;
+
+ // add our eye center back in
+ coords = offset + eye_center;
+ coords /= upscale;
+
+ // and check our color
+ if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
+ frag_color = vec4(0.0, 0.0, 0.0, 1.0);
+ } else {
+ coords = (coords + vec2(1.0)) / vec2(2.0);
+ frag_color = texture(source, coords);
+ }
+}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
new file mode 100644
index 0000000000..ebb00e81d0
--- /dev/null
+++ b/drivers/gles3/shaders/scene.glsl
@@ -0,0 +1,2153 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+#define SHADER_IS_SRGB true //TODO remove
+
+#define M_PI 3.14159265359
+
+//
+// attributes
+//
+
+layout(location = 0) in highp vec4 vertex_attrib;
+/* clang-format on */
+layout(location = 1) in vec3 normal_attrib;
+
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+layout(location = 2) in vec4 tangent_attrib;
+#endif
+
+#if defined(ENABLE_COLOR_INTERP)
+layout(location = 3) in vec4 color_attrib;
+#endif
+
+#if defined(ENABLE_UV_INTERP)
+layout(location = 4) in vec2 uv_attrib;
+#endif
+
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
+layout(location = 5) in vec2 uv2_attrib;
+#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
+
+layout(location = 6) in vec4 bone_ids;
+layout(location = 7) in highp vec4 bone_weights;
+
+uniform highp sampler2D bone_transforms; // texunit:-1
+uniform ivec2 skeleton_texture_size;
+
+#endif
+
+#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;
+
+#endif
+
+//
+// uniforms
+//
+
+uniform highp mat4 inv_view_matrix;
+uniform highp mat4 view_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;
+#endif
+
+//
+// varyings
+//
+
+#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
+out highp vec4 position_interp;
+#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;
+#endif
+
+#if defined(ENABLE_COLOR_INTERP)
+out vec4 color_interp;
+#endif
+
+#if defined(ENABLE_UV_INTERP)
+out vec2 uv_interp;
+#endif
+
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
+out vec2 uv2_interp;
+#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;
+
+#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;
+#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;
+
+#endif
+
+#endif
+
+#if defined(USE_VERTEX_LIGHTING) && defined(USE_LIGHTING)
+
+out highp vec3 diffuse_interp;
+out highp vec3 specular_interp;
+
+// general for all lights
+uniform highp vec4 light_color;
+uniform highp vec4 shadow_color;
+uniform highp float light_specular;
+
+// directional
+uniform highp vec3 light_direction;
+
+// omni
+uniform highp vec3 light_position;
+
+uniform highp float light_range;
+uniform highp float light_attenuation;
+
+// spot
+uniform highp float light_spot_attenuation;
+uniform highp float light_spot_range;
+uniform highp float light_spot_angle;
+
+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
+
+//#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)
+
+ float NdotL = dot(N, L);
+ float cNdotL = max(NdotL, 0.0); // clamped NdotL
+ float NdotV = dot(N, V);
+ float cNdotV = max(NdotV, 0.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);
+ }
+#else
+ // lambert by default for everything else
+ diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#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;
+#endif
+
+ SRGB_APPROX(specular_brdf_NL)
+ specular_interp += specular_brdf_NL * light_color * attenuation * (1.0 / M_PI);
+ }
+}
+
+#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;
+#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;
+#endif
+
+#endif //reflection probe2
+
+#endif //vertex lighting for refprobes
+
+#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
+
+out vec4 fog_interp;
+
+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
+
+#ifdef FOG_HEIGHT_ENABLED
+uniform highp float fog_height_min;
+uniform highp float fog_height_max;
+uniform mediump float fog_height_curve;
+#endif
+
+#endif //fog
+
+void main() {
+ highp vec4 vertex = vertex_attrib;
+
+ 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));
+ model_matrix = model_matrix * transpose(m);
+ }
+
+#endif
+
+ vec3 normal = normal_attrib;
+
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+ vec3 tangent = tangent_attrib.xyz;
+ float binormalf = tangent_attrib.a;
+ vec3 binormal = normalize(cross(normal, tangent) * binormalf);
+#endif
+
+#if defined(ENABLE_COLOR_INTERP)
+ color_interp = color_attrib;
+#ifdef USE_INSTANCING
+ color_interp *= instance_color;
+#endif
+#endif
+
+#if defined(ENABLE_UV_INTERP)
+ uv_interp = uv_attrib;
+#endif
+
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
+ uv2_interp = uv2_attrib;
+#endif
+
+#if defined(OVERRIDE_POSITION)
+ highp vec4 position;
+#endif
+
+#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
+ vertex = model_matrix * vertex;
+ normal = normalize((model_matrix * vec4(normal, 0.0)).xyz);
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+
+ tangent = normalize((model_matrix * vec4(tangent, 0.0)).xyz);
+ binormal = normalize((model_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);
+
+#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);
+
+ 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));
+
+ bone_transform += transpose(b) * bone_weights[i];
+ }
+ }
+
+#endif
+
+ model_matrix = model_matrix * bone_transform;
+
+#endif
+
+#ifdef USE_INSTANCING
+ vec4 instance_custom = instance_custom_data;
+#else
+ vec4 instance_custom = vec4(0.0);
+
+#endif
+
+ mat4 local_projection_matrix = projection_matrix;
+
+ mat4 modelview = view_matrix * model_matrix;
+ float roughness = 1.0;
+
+#define projection_matrix local_projection_matrix
+#define world_transform model_matrix
+
+ float point_size = 1.0;
+
+ {
+ /* clang-format off */
+
+VERTEX_SHADER_CODE
+
+ /* clang-format on */
+ }
+
+ gl_PointSize = point_size;
+ vec4 outvec = vertex;
+
+ // use local coordinates
+#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 = view_matrix * vertex;
+ normal = normalize((view_matrix * vec4(normal, 0.0)).xyz);
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+ tangent = normalize((view_matrix * vec4(tangent, 0.0)).xyz);
+ binormal = normalize((view_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);
+
+#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);
+
+#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
+
+ refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 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;
+#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;
+ }
+#endif
+
+#ifdef FOG_HEIGHT_ENABLED
+ {
+ float y = (inv_view_matrix * vec4(vertex_interp, 1.0)).y;
+ fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve));
+ }
+#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]
+
+#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 inv_view_matrix;
+/* clang-format on */
+uniform highp mat4 view_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;
+#endif
+
+#if defined(SCREEN_TEXTURE_USED)
+uniform highp sampler2D screen_texture; //texunit:-4
+#endif
+#if defined(DEPTH_TEXTURE_USED)
+uniform highp sampler2D depth_texture; //texunit:-4
+#endif
+
+#ifdef USE_REFLECTION_PROBE1
+
+#ifdef USE_VERTEX_LIGHTING
+
+in mediump vec4 refprobe1_reflection_normal_blend;
+#ifndef USE_LIGHTMAP
+in mediump vec3 refprobe1_ambient_normal;
+#endif
+
+#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;
+
+#endif //USE_REFLECTION_PROBE1
+
+#ifdef USE_REFLECTION_PROBE2
+
+#ifdef USE_VERTEX_LIGHTING
+
+in mediump vec4 refprobe2_reflection_normal_blend;
+#ifndef USE_LIGHTMAP
+in mediump vec3 refprobe2_ambient_normal;
+#endif
+
+#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,
+#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
+
+ vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz;
+#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);
+ }
+
+ ambient_out.a = blend;
+ ambient_out.rgb *= blend;
+ ambient_accum += ambient_out;
+
+#endif
+}
+
+#endif //use refprobe 1 or 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;
+
+#endif
+
+#ifdef USE_RADIANCE_MAP
+
+uniform samplerCube radiance_map; // texunit:-2
+
+uniform mat4 radiance_inverse_xform;
+
+#endif
+
+uniform vec4 bg_color;
+uniform float bg_energy;
+
+uniform float ambient_sky_contribution;
+uniform vec4 ambient_color;
+uniform float ambient_energy;
+
+#ifdef USE_LIGHTING
+
+uniform highp vec4 shadow_color;
+
+#ifdef USE_VERTEX_LIGHTING
+
+//get from vertex
+in highp vec3 diffuse_interp;
+in highp vec3 specular_interp;
+
+uniform highp vec3 light_direction; //may be used by fog, so leave here
+
+#else
+//done in fragment
+// general for all lights
+uniform highp vec4 light_color;
+
+uniform highp float light_specular;
+
+// directional
+uniform highp vec3 light_direction;
+// omni
+uniform highp vec3 light_position;
+
+uniform highp float light_attenuation;
+
+// spot
+uniform highp float light_spot_attenuation;
+uniform highp float light_spot_range;
+uniform highp float light_spot_angle;
+#endif
+
+//this is needed outside above if because dual paraboloid wants it
+uniform highp float light_range;
+
+#ifdef USE_SHADOW
+
+uniform highp vec2 shadow_pixel_size;
+
+#if defined(LIGHT_MODE_OMNI) || defined(LIGHT_MODE_SPOT)
+uniform highp sampler2D light_shadow_atlas; //texunit:-3
+#endif
+
+#ifdef LIGHT_MODE_DIRECTIONAL
+uniform highp sampler2D light_directional_shadow; // texunit:-3
+uniform highp vec4 light_split_offsets;
+#endif
+
+in highp vec4 shadow_coord;
+
+#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
+
+uniform vec4 light_clamp;
+
+#endif // light shadow
+
+// directional shadow
+
+#endif
+
+//
+// varyings
+//
+
+#if defined(RENDER_DEPTH) && defined(USE_RGBA_SHADOWS)
+in highp vec4 position_interp;
+#endif
+
+in highp vec3 vertex_interp;
+in vec3 normal_interp;
+
+#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+in vec3 tangent_interp;
+in vec3 binormal_interp;
+#endif
+
+#if defined(ENABLE_COLOR_INTERP)
+in vec4 color_interp;
+#endif
+
+#if defined(ENABLE_UV_INTERP)
+in vec2 uv_interp;
+#endif
+
+#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP)
+in vec2 uv2_interp;
+#endif
+
+in vec3 view_interp;
+
+layout(location = 0) out vec4 frag_color;
+
+vec3 F0(float metallic, float specular, vec3 albedo) {
+ float dielectric = 0.16 * specular * specular;
+ // use albedo * metallic as colored specular reflectance at 0 angle for metallic materials;
+ // see https://google.github.io/filament/Filament.md.html
+ 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);
+}
+
+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);
+}
+*/
+
+// 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);
+}
+
+float D_GGX_anisotropic(float cos_theta_m, float alpha_x, float alpha_y, float cos_phi, float sin_phi, float NdotH) {
+ float alpha2 = alpha_x * alpha_y;
+ highp vec3 v = vec3(alpha_y * cos_phi, alpha_x * sin_phi, alpha2 * NdotH);
+ 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 SchlickFresnel(float u) {
+ float m = 1.0 - u;
+ float m2 = m * m;
+ 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_roughness,
+ 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)
+
+#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
+
+ /* clang-format on */
+
+#else
+ float NdotL = dot(N, L);
+ float cNdotL = max(NdotL, 0.0); // clamped NdotL
+ float NdotV = dot(N, V);
+ float cNdotV = max(abs(NdotV), 1e-6);
+
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_BLINN) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ 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);
+#endif
+
+#if defined(DIFFUSE_BURLEY) || defined(SPECULAR_SCHLICK_GGX) || defined(LIGHT_USE_CLEARCOAT)
+ float cLdotH = max(dot(L, H), 0.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);
+
+#elif defined(DIFFUSE_BURLEY)
+
+ {
+ float FD90_minus_1 = 2.0 * cLdotH * cLdotH * roughness - 0.5;
+ 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;
+
+#if defined(TRANSMISSION_USED)
+ diffuse_light += light_color * diffuse_color * (vec3(1.0 / M_PI) - diffuse_brdf_NL) * transmission * attenuation;
+#endif
+
+#if defined(LIGHT_USE_RIM)
+ 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;
+#endif
+ }
+
+ if (roughness > 0.0) {
+
+#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);
+
+#elif 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;
+
+#elif defined(SPECULAR_DISABLED)
+ // none..
+#elif defined(SPECULAR_SCHLICK_GGX)
+ // shlick+ggx as default
+
+#if defined(LIGHT_USE_ANISOTROPY)
+ float alpha_ggx = roughness * roughness;
+ 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 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;
+ 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);
+ float cLdotH5 = SchlickFresnel(cLdotH);
+ vec3 F = mix(vec3(cLdotH5), vec3(1.0), f0);
+
+ specular_brdf_NL = cNdotL * D * F * G;
+
+#endif
+
+ SRGB_APPROX(specular_brdf_NL)
+ specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
+
+#if defined(LIGHT_USE_CLEARCOAT)
+
+#if !defined(SPECULAR_SCHLICK_GGX)
+ float cLdotH5 = SchlickFresnel(cLdotH);
+#endif
+ float Dr = GTR1(cNdotH, mix(.1, .001, clearcoat_roughness));
+ 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;
+
+ specular_light += clearcoat_specular_brdf_NL * light_color * specular_blob_intensity * attenuation;
+#endif
+ }
+
+#ifdef USE_SHADOW_TO_OPACITY
+ alpha = min(alpha, clamp(1.0 - length(attenuation), 0.0, 1.0));
+#endif
+
+#endif //defined(USE_LIGHT_SHADER_CODE)
+}
+
+#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
+
+#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);
+#endif
+
+#ifdef SHADOW_MODE_PCF_5
+
+ 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);
+ return avg * (1.0 / 5.0);
+
+#endif
+
+#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13)
+
+ return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos);
+#endif
+}
+
+#endif
+
+#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
+
+#if defined(USE_VERTEX_LIGHTING)
+
+in vec4 fog_interp;
+
+#else
+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
+
+#ifdef FOG_HEIGHT_ENABLED
+uniform highp float fog_height_min;
+uniform highp float fog_height_max;
+uniform mediump float fog_height_curve;
+#endif
+
+#endif //vertex lit
+#endif //fog
+
+void main() {
+#ifdef RENDER_DEPTH_DUAL_PARABOLOID
+
+ if (dp_clip > 0.0)
+ discard;
+#endif
+ highp vec3 vertex = vertex_interp;
+ vec3 view = -normalize(vertex_interp);
+ vec3 albedo = vec3(1.0);
+ vec3 transmission = vec3(0.0);
+ float metallic = 0.0;
+ float specular = 0.5;
+ vec3 emission = vec3(0.0);
+ float roughness = 1.0;
+ float rim = 0.0;
+ float rim_tint = 0.0;
+ float clearcoat = 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;
+#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;
+#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);
+#endif
+ float normaldepth = 1.0;
+
+#if defined(ALPHA_SCISSOR_USED)
+ float alpha_scissor = 0.5;
+#endif
+
+#if defined(SCREEN_UV_USED)
+ vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
+#endif
+
+ {
+ /* clang-format off */
+
+FRAGMENT_SHADER_CODE
+
+ /* clang-format on */
+ }
+
+#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)));
+
+ normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side;
+ //normal = normalmap;
+#endif
+
+ normal = normalize(normal);
+
+ vec3 N = normal;
+
+ 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);
+
+ vec3 eye_position = view;
+
+#if !defined(USE_SHADOW_TO_OPACITY)
+
+#if defined(ALPHA_SCISSOR_USED)
+ if (alpha < alpha_scissor) {
+ discard;
+ }
+#endif // ALPHA_SCISSOR_USED
+
+#ifdef USE_DEPTH_PREPASS
+ if (alpha < 0.1) {
+ discard;
+ }
+#endif // USE_DEPTH_PREPASS
+
+#endif // !USE_SHADOW_TO_OPACITY
+
+#ifdef BASE_PASS
+
+ // 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);
+
+#ifdef AMBIENT_LIGHT_DISABLED
+ ambient_light = vec3(0.0, 0.0, 0.0);
+#else
+
+#ifdef USE_RADIANCE_MAP
+
+ vec3 ref_vec = reflect(-eye_position, N);
+ ref_vec = normalize((radiance_inverse_xform * vec4(ref_vec, 0.0)).xyz);
+
+ ref_vec.z *= -1.0;
+
+ 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;
+
+ ambient_light = mix(ambient_color.rgb, env_ambient, ambient_sky_contribution);
+ }
+#endif
+
+#else
+
+ ambient_light = ambient_color.rgb;
+ specular_light = bg_color.rgb * bg_energy;
+
+#endif
+#endif // AMBIENT_LIGHT_DISABLED
+ ambient_light *= ambient_energy;
+
+#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
+
+ vec4 ambient_accum = vec4(0.0);
+ vec4 reflection_accum = vec4(0.0);
+
+#ifdef USE_REFLECTION_PROBE1
+
+ 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);
+
+#endif // USE_REFLECTION_PROBE1
+
+#ifdef USE_REFLECTION_PROBE2
+
+ 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,
+#else
+ normal_interp, vertex_interp, refprobe2_local_matrix,
+ refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset,
+#endif
+ refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness,
+ ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#endif // USE_REFLECTION_PROBE2
+
+ if (reflection_accum.a > 0.0) {
+ specular_light = reflection_accum.rgb / reflection_accum.a;
+ }
+
+#ifndef USE_LIGHTMAP
+ if (ambient_accum.a > 0.0) {
+ ambient_light = ambient_accum.rgb / ambient_accum.a;
+ }
+#endif
+
+#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2)
+
+ // 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
+ 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 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(inv_view_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;
+ }
+ }
+#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);
+
+ 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;
+#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);
+#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);
+#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);
+#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);
+ }
+
+#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);
+ }
+#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;
+#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);
+#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);
+#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;
+
+#ifdef LIGHT_USE_PSSM_BLEND
+ pssm_coord2 = shadow_coord2;
+ pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
+#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;
+#endif
+ }
+
+#endif // LIGHT_USE_PSSM2
+
+#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2)
+ {
+ pssm_coord = shadow_coord;
+ }
+#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);
+ }
+#endif
+
+ light_att *= mix(shadow_color.rgb, vec3(1.0), shadow);
+ }
+ }
+#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_roughness,
+ anisotropy,
+ diffuse_light,
+ specular_light,
+ alpha);
+
+#endif //vertex lighting
+
+#endif //USE_LIGHTING
+ //compute and merge
+
+#ifdef USE_SHADOW_TO_OPACITY
+
+ alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
+
+#if defined(ALPHA_SCISSOR_USED)
+ if (alpha < alpha_scissor) {
+ discard;
+ }
+#endif // ALPHA_SCISSOR_USED
+
+#ifdef USE_DEPTH_PREPASS
+ if (alpha < 0.1) {
+ discard;
+ }
+#endif // USE_DEPTH_PREPASS
+
+#endif // !USE_SHADOW_TO_OPACITY
+
+#ifndef RENDER_DEPTH
+
+#ifdef SHADELESS
+
+ 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 *= 1.0 - metallic;
+ ambient_light *= 1.0 - metallic;
+
+ frag_color = vec4(ambient_light + diffuse_light + specular_light, alpha);
+
+ //add emission if in base pass
+#ifdef BASE_PASS
+ frag_color.rgb += emission;
+#endif
+ // frag_color = vec4(normal, 1.0);
+
+//apply fog
+#if defined(FOG_DEPTH_ENABLED) || defined(FOG_HEIGHT_ENABLED)
+
+#if defined(USE_VERTEX_LIGHTING)
+
+#if defined(BASE_PASS)
+ frag_color.rgb = mix(frag_color.rgb, fog_interp.rgb, fog_interp.a);
+#else
+ frag_color.rgb *= (1.0 - fog_interp.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));
+
+ 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);
+ }
+ }
+#endif
+
+#ifdef FOG_HEIGHT_ENABLED
+ {
+ float y = (inv_view_matrix * vec4(vertex, 1.0)).y;
+ fog_amount = max(fog_amount, pow(smoothstep(fog_height_min, fog_height_max, y), fog_height_curve));
+ }
+#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
+}
diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl
new file mode 100644
index 0000000000..2eddf9d479
--- /dev/null
+++ b/drivers/gles3/shaders/stdlib_inc.glsl
@@ -0,0 +1,58 @@
+//TODO: only needed by GLES_OVER_GL
+
+uint float2half(uint f) {
+ return ((f >> uint(16)) & uint(0x8000)) |
+ ((((f & uint(0x7f800000)) - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) |
+ ((f >> uint(13)) & uint(0x03ff));
+}
+
+uint half2float(uint h) {
+ return ((h & uint(0x8000)) << uint(16)) | (((h & uint(0x7c00)) + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13));
+}
+
+uint packHalf2x16(vec2 v) {
+ return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16);
+}
+
+vec2 unpackHalf2x16(uint v) {
+ return vec2(uintBitsToFloat(half2float(v & uint(0xffff))),
+ uintBitsToFloat(half2float(v >> uint(16))));
+}
+
+uint packUnorm2x16(vec2 v) {
+ uvec2 uv = uvec2(round(clamp(v, vec2(0.0), vec2(1.0)) * 65535.0));
+ return uv.x | uv.y << uint(16);
+}
+
+vec2 unpackUnorm2x16(uint p) {
+ return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization
+}
+
+uint packSnorm2x16(vec2 v) {
+ uvec2 uv = uvec2(round(clamp(v, vec2(-1.0), vec2(1.0)) * 32767.0) + 32767.0);
+ return uv.x | uv.y << uint(16);
+}
+
+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));
+}
+
+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);
+}
+
+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
+}
+
+uint packSnorm4x8(vec4 v) {
+ uvec4 uv = uvec4(round(clamp(v, vec4(-1.0), vec4(1.0)) * 127.0) + 127.0);
+ return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24);
+}
+
+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)));
+ 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
new file mode 100644
index 0000000000..4f962626a3
--- /dev/null
+++ b/drivers/gles3/shaders/tonemap.glsl
@@ -0,0 +1,313 @@
+/* clang-format off */
+[vertex]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+precision highp float;
+precision highp int;
+#endif
+
+layout(location = 0) in vec2 vertex_attrib;
+/* clang-format on */
+layout(location = 4) in vec2 uv_in;
+
+out vec2 uv_interp;
+
+void main() {
+ gl_Position = vec4(vertex_attrib, 0.0, 1.0);
+
+ uv_interp = uv_in;
+}
+
+/* clang-format off */
+[fragment]
+
+#ifdef USE_GLES_OVER_GL
+#define lowp
+#define mediump
+#define highp
+#else
+#if defined(USE_HIGHP_PRECISION)
+precision highp float;
+precision highp int;
+#else
+precision mediump float;
+precision mediump int;
+#endif
+#endif
+
+in vec2 uv_interp;
+/* clang-format on */
+
+layout(location = 0) out vec4 frag_color;
+
+uniform highp sampler2D source; //texunit:0
+
+#if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7)
+#define USING_GLOW // only use glow when at least one glow level is selected
+
+#ifdef USE_MULTI_TEXTURE_GLOW
+uniform highp sampler2D source_glow1; //texunit:2
+uniform highp sampler2D source_glow2; //texunit:3
+uniform highp sampler2D source_glow3; //texunit:4
+uniform highp sampler2D source_glow4; //texunit:5
+uniform highp sampler2D source_glow5; //texunit:6
+uniform highp sampler2D source_glow6; //texunit:7
+#ifdef USE_GLOW_LEVEL7
+uniform highp sampler2D source_glow7; //texunit:8
+#endif
+#else
+uniform highp sampler2D source_glow; //texunit:2
+#endif
+uniform highp float glow_intensity;
+#endif
+
+#ifdef USE_BCS
+uniform vec3 bcs;
+#endif
+
+#ifdef USE_FXAA
+uniform vec2 pixel_size;
+#endif
+
+#ifdef USE_COLOR_CORRECTION
+uniform sampler2D color_correction; //texunit:1
+#endif
+
+#ifdef USE_GLOW_FILTER_BICUBIC
+// w0, w1, w2, and w3 are the four cubic B-spline basis functions
+float w0(float a) {
+ return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
+}
+
+float w1(float a) {
+ return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
+}
+
+float w2(float a) {
+ return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
+}
+
+float w3(float a) {
+ return (1.0 / 6.0) * (a * a * a);
+}
+
+// g0 and g1 are the two amplitude functions
+float g0(float a) {
+ return w0(a) + w1(a);
+}
+
+float g1(float a) {
+ return w2(a) + w3(a);
+}
+
+// h0 and h1 are the two offset functions
+float h0(float a) {
+ return -1.0 + w1(a) / (w0(a) + w1(a));
+}
+
+float h1(float a) {
+ return 1.0 + w3(a) / (w2(a) + w3(a));
+}
+
+uniform ivec2 glow_texture_size;
+
+vec4 texture_bicubic(sampler2D tex, vec2 uv, int p_lod) {
+ float lod = float(p_lod);
+ vec2 tex_size = vec2(glow_texture_size >> p_lod);
+ vec2 texel_size = vec2(1.0) / tex_size;
+
+ uv = uv * tex_size + vec2(0.5);
+
+ vec2 iuv = floor(uv);
+ vec2 fuv = fract(uv);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+ vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+
+ return (g0(fuv.y) * (g0x * textureLod(tex, p0, lod) + g1x * textureLod(tex, p1, lod))) +
+ (g1(fuv.y) * (g0x * textureLod(tex, p2, lod) + g1x * textureLod(tex, p3, lod)));
+}
+
+#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) texture_bicubic(m_tex, m_uv, m_lod)
+#else //!USE_GLOW_FILTER_BICUBIC
+#define GLOW_TEXTURE_SAMPLE(m_tex, m_uv, m_lod) textureLod(m_tex, m_uv, float(m_lod))
+#endif //USE_GLOW_FILTER_BICUBIC
+
+vec3 apply_glow(vec3 color, vec3 glow) { // apply glow using the selected blending mode
+#ifdef USE_GLOW_REPLACE
+ color = glow;
+#endif
+
+#ifdef USE_GLOW_SCREEN
+ color = max((color + glow) - (color * glow), vec3(0.0));
+#endif
+
+#ifdef USE_GLOW_SOFTLIGHT
+ glow = glow * vec3(0.5) + vec3(0.5);
+
+ color.r = (glow.r <= 0.5) ? (color.r - (1.0 - 2.0 * glow.r) * color.r * (1.0 - color.r)) : (((glow.r > 0.5) && (color.r <= 0.25)) ? (color.r + (2.0 * glow.r - 1.0) * (4.0 * color.r * (4.0 * color.r + 1.0) * (color.r - 1.0) + 7.0 * color.r)) : (color.r + (2.0 * glow.r - 1.0) * (sqrt(color.r) - color.r)));
+ color.g = (glow.g <= 0.5) ? (color.g - (1.0 - 2.0 * glow.g) * color.g * (1.0 - color.g)) : (((glow.g > 0.5) && (color.g <= 0.25)) ? (color.g + (2.0 * glow.g - 1.0) * (4.0 * color.g * (4.0 * color.g + 1.0) * (color.g - 1.0) + 7.0 * color.g)) : (color.g + (2.0 * glow.g - 1.0) * (sqrt(color.g) - color.g)));
+ color.b = (glow.b <= 0.5) ? (color.b - (1.0 - 2.0 * glow.b) * color.b * (1.0 - color.b)) : (((glow.b > 0.5) && (color.b <= 0.25)) ? (color.b + (2.0 * glow.b - 1.0) * (4.0 * color.b * (4.0 * color.b + 1.0) * (color.b - 1.0) + 7.0 * color.b)) : (color.b + (2.0 * glow.b - 1.0) * (sqrt(color.b) - color.b)));
+#endif
+
+#if !defined(USE_GLOW_SCREEN) && !defined(USE_GLOW_SOFTLIGHT) && !defined(USE_GLOW_REPLACE) // no other selected -> additive
+ color += glow;
+#endif
+
+ return color;
+}
+
+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;
+}
+
+vec3 apply_color_correction(vec3 color, sampler2D correction_tex) {
+ color.r = texture(correction_tex, vec2(color.r, 0.0)).r;
+ color.g = texture(correction_tex, vec2(color.g, 0.0)).g;
+ color.b = texture(correction_tex, vec2(color.b, 0.0)).b;
+
+ return color;
+}
+
+vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) {
+ const float FXAA_REDUCE_MIN = (1.0 / 128.0);
+ const float FXAA_REDUCE_MUL = (1.0 / 8.0);
+ const float FXAA_SPAN_MAX = 8.0;
+
+ vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz;
+ vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz;
+ vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz;
+ vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz;
+ vec3 rgbM = color;
+ vec3 luma = vec3(0.299, 0.587, 0.114);
+ float lumaNW = dot(rgbNW, luma);
+ float lumaNE = dot(rgbNE, luma);
+ float lumaSW = dot(rgbSW, luma);
+ float lumaSE = dot(rgbSE, luma);
+ float lumaM = dot(rgbM, luma);
+ float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
+ float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
+
+ vec2 dir;
+ dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
+ dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
+
+ float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) *
+ (0.25 * FXAA_REDUCE_MUL),
+ FXAA_REDUCE_MIN);
+
+ float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
+ dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
+ max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
+ dir * rcpDirMin)) *
+ pixel_size;
+
+ vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);
+ vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz);
+
+ float lumaB = dot(rgbB, luma);
+ if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
+ return rgbA;
+ } else {
+ return rgbB;
+ }
+}
+
+void main() {
+ vec3 color = textureLod(source, uv_interp, 0.0).rgb;
+
+#ifdef USE_FXAA
+ color = apply_fxaa(color, uv_interp, pixel_size);
+#endif
+
+ // Glow
+
+#ifdef USING_GLOW
+ vec3 glow = vec3(0.0);
+#ifdef USE_MULTI_TEXTURE_GLOW
+#ifdef USE_GLOW_LEVEL1
+ glow += GLOW_TEXTURE_SAMPLE(source_glow1, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL2
+ glow += GLOW_TEXTURE_SAMPLE(source_glow2, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL3
+ glow += GLOW_TEXTURE_SAMPLE(source_glow3, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL4
+ glow += GLOW_TEXTURE_SAMPLE(source_glow4, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL5
+ glow += GLOW_TEXTURE_SAMPLE(source_glow5, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL6
+ glow += GLOW_TEXTURE_SAMPLE(source_glow6, uv_interp, 0).rgb;
+#ifdef USE_GLOW_LEVEL7
+ glow += GLOW_TEXTURE_SAMPLE(source_glow7, uv_interp, 0).rgb;
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#else
+
+#ifdef USE_GLOW_LEVEL1
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 1).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL2
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 2).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL3
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 3).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL4
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 4).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL5
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 5).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL6
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 6).rgb;
+#endif
+
+#ifdef USE_GLOW_LEVEL7
+ glow += GLOW_TEXTURE_SAMPLE(source_glow, uv_interp, 7).rgb;
+#endif
+#endif //USE_MULTI_TEXTURE_GLOW
+
+ glow *= glow_intensity;
+ color = apply_glow(color, glow);
+#endif
+
+ // Additional effects
+
+#ifdef USE_BCS
+ color = apply_bcs(color, bcs);
+#endif
+
+#ifdef USE_COLOR_CORRECTION
+ color = apply_color_correction(color, color_correction);
+#endif
+
+ frag_color = vec4(color, 1.0);
+}
diff --git a/drivers/dummy/SCsub b/drivers/gles3/storage/SCsub
index 91e1140b75..91e1140b75 100644
--- a/drivers/dummy/SCsub
+++ b/drivers/gles3/storage/SCsub
diff --git a/drivers/gles3/storage/canvas_texture_storage.cpp b/drivers/gles3/storage/canvas_texture_storage.cpp
new file mode 100644
index 0000000000..fe12700c21
--- /dev/null
+++ b/drivers/gles3/storage/canvas_texture_storage.cpp
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* canvas_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 "canvas_texture_storage.h"
+
+using namespace GLES3;
+
+CanvasTextureStorage *CanvasTextureStorage::singleton = nullptr;
+
+CanvasTextureStorage *CanvasTextureStorage::get_singleton() {
+ return singleton;
+}
+
+CanvasTextureStorage::CanvasTextureStorage() {
+ singleton = this;
+}
+
+CanvasTextureStorage::~CanvasTextureStorage() {
+ singleton = nullptr;
+}
+
+RID CanvasTextureStorage::canvas_texture_allocate() {
+ return canvas_texture_owner.allocate_rid();
+}
+
+void CanvasTextureStorage::canvas_texture_initialize(RID p_rid) {
+ canvas_texture_owner.initialize_rid(p_rid);
+}
+
+void CanvasTextureStorage::canvas_texture_free(RID p_rid) {
+ canvas_texture_owner.free(p_rid);
+}
+
+void CanvasTextureStorage::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 CanvasTextureStorage::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 CanvasTextureStorage::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 CanvasTextureStorage::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;
+}
+
+#endif // !GLES3_ENABLED
diff --git a/drivers/gles3/storage/canvas_texture_storage.h b/drivers/gles3/storage/canvas_texture_storage.h
new file mode 100644
index 0000000000..5930e927fe
--- /dev/null
+++ b/drivers/gles3/storage/canvas_texture_storage.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* canvas_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 CANVAS_TEXTURE_STORAGE_GLES3_H
+#define CANVAS_TEXTURE_STORAGE_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/rid_owner.h"
+#include "servers/rendering/storage/canvas_texture_storage.h"
+
+namespace GLES3 {
+
+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;
+};
+
+class CanvasTextureStorage : public RendererCanvasTextureStorage {
+private:
+ static CanvasTextureStorage *singleton;
+
+ RID_Owner<CanvasTexture, true> canvas_texture_owner;
+
+public:
+ static CanvasTextureStorage *get_singleton();
+
+ CanvasTextureStorage();
+ virtual ~CanvasTextureStorage();
+
+ 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;
+};
+
+} // namespace GLES3
+
+#endif // !GLES3_ENABLED
+
+#endif // !CANVAS_TEXTURE_STORAGE_GLES3_H
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
new file mode 100644
index 0000000000..1f66401427
--- /dev/null
+++ b/drivers/gles3/storage/config.cpp
@@ -0,0 +1,156 @@
+/*************************************************************************/
+/* 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/templates/vector.h"
+
+using namespace GLES3;
+
+Config *Config::singleton = nullptr;
+
+Config::Config() {
+ singleton = this;
+ should_orphan = true;
+}
+
+Config::~Config() {
+ singleton = nullptr;
+}
+
+void Config::initialize() {
+ {
+ const GLubyte *extension_string = glGetString(GL_EXTENSIONS);
+
+ Vector<String> exts = String((const char *)extension_string).split(" ");
+
+ for (int i = 0; i < exts.size(); i++) {
+ extensions.insert(exts[i]);
+ }
+ }
+
+ keep_original_textures = true; // false
+ shrink_textures_x2 = false;
+ depth_internalformat = GL_DEPTH_COMPONENT;
+ depth_type = GL_UNSIGNED_INT;
+
+#ifdef GLES_OVER_GL
+ float_texture_supported = true;
+ s3tc_supported = true;
+ etc_supported = false;
+ support_npot_repeat_mipmap = true;
+ depth_buffer_internalformat = GL_DEPTH_COMPONENT24;
+#else
+ float_texture_supported = extensions.has("GL_ARB_texture_float") || extensions.has("GL_OES_texture_float");
+ s3tc_supported = extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc");
+ etc_supported = extensions.has("GL_OES_compressed_ETC1_RGB8_texture") || extensions.has("WEBGL_compressed_texture_etc1");
+ support_npot_repeat_mipmap = 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/
+ depth_buffer_internalformat = GL_DEPTH_COMPONENT16;
+ depth_type = GL_UNSIGNED_INT;
+#else
+ // on mobile check for 24 bit depth support for RenderBufferStorage
+ if (extensions.has("GL_OES_depth24")) {
+ depth_buffer_internalformat = _DEPTH_COMPONENT24_OES;
+ depth_type = GL_UNSIGNED_INT;
+ } else {
+ depth_buffer_internalformat = GL_DEPTH_COMPONENT16;
+ 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
+ render_to_mipmap_supported = false;
+#else
+ //check if mipmaps can be used for SCREEN_TEXTURE and Glow on Mobile and web platforms
+ render_to_mipmap_supported = extensions.has("GL_OES_fbo_render_mipmap") && extensions.has("GL_EXT_texture_lod");
+#endif
+
+#ifdef GLES_OVER_GL
+ use_rgba_2d_shadows = false;
+ support_depth_texture = true;
+ use_rgba_3d_shadows = false;
+ support_depth_cubemaps = true;
+#else
+ use_rgba_2d_shadows = !(float_texture_supported && extensions.has("GL_EXT_texture_rg"));
+ support_depth_texture = extensions.has("GL_OES_depth_texture") || extensions.has("WEBGL_depth_texture");
+ use_rgba_3d_shadows = !support_depth_texture;
+ support_depth_cubemaps = extensions.has("GL_OES_depth_texture_cube_map");
+#endif
+
+#ifdef GLES_OVER_GL
+ support_32_bits_indices = true;
+#else
+ support_32_bits_indices = extensions.has("GL_OES_element_index_uint");
+#endif
+
+#ifdef GLES_OVER_GL
+ support_write_depth = true;
+#elif defined(JAVASCRIPT_ENABLED)
+ support_write_depth = false;
+#else
+ support_write_depth = extensions.has("GL_EXT_frag_depth");
+#endif
+
+ 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
+ support_half_float_vertices = false;
+#endif
+ bool disable_half_float = false; //GLOBAL_GET("rendering/opengl/compatibility/disable_half_float");
+ if (disable_half_float) {
+ support_half_float_vertices = false;
+ }
+
+ etc_supported = extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
+ latc_supported = extensions.has("GL_EXT_texture_compression_latc");
+ bptc_supported = extensions.has("GL_ARB_texture_compression_bptc");
+ rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
+ bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc");
+ srgb_decode_supported = extensions.has("GL_EXT_texture_sRGB_decode");
+
+ glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+
+ force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
+ use_fast_texture_filter = false; //GLOBAL_GET("rendering/quality/filters/use_nearest_mipmap_filter");
+ // should_orphan = GLOBAL_GET("rendering/options/api_usage_legacy/orphan_buffers");
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
new file mode 100644
index 0000000000..25bd3fd9a1
--- /dev/null
+++ b/drivers/gles3/storage/config.h
@@ -0,0 +1,113 @@
+/*************************************************************************/
+/* config.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 CONFIG_GLES3_H
+#define CONFIG_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/string/ustring.h"
+#include "core/templates/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 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 = true;
+
+ static Config *get_singleton() { return singleton; };
+
+ Config();
+ ~Config();
+ void initialize();
+};
+
+} // namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // !CONFIG_GLES3_H
diff --git a/drivers/gles3/storage/decal_atlas_storage.cpp b/drivers/gles3/storage/decal_atlas_storage.cpp
new file mode 100644
index 0000000000..7bac34ea19
--- /dev/null
+++ b/drivers/gles3/storage/decal_atlas_storage.cpp
@@ -0,0 +1,75 @@
+/*************************************************************************/
+/* decal_atlas_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 "decal_atlas_storage.h"
+
+using namespace GLES3;
+
+RID DecalAtlasStorage::decal_allocate() {
+ return RID();
+}
+
+void DecalAtlasStorage::decal_initialize(RID p_rid) {
+}
+
+void DecalAtlasStorage::decal_set_extents(RID p_decal, const Vector3 &p_extents) {
+}
+
+void DecalAtlasStorage::decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) {
+}
+
+void DecalAtlasStorage::decal_set_emission_energy(RID p_decal, float p_energy) {
+}
+
+void DecalAtlasStorage::decal_set_albedo_mix(RID p_decal, float p_mix) {
+}
+
+void DecalAtlasStorage::decal_set_modulate(RID p_decal, const Color &p_modulate) {
+}
+
+void DecalAtlasStorage::decal_set_cull_mask(RID p_decal, uint32_t p_layers) {
+}
+
+void DecalAtlasStorage::decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) {
+}
+
+void DecalAtlasStorage::decal_set_fade(RID p_decal, float p_above, float p_below) {
+}
+
+void DecalAtlasStorage::decal_set_normal_fade(RID p_decal, float p_fade) {
+}
+
+AABB DecalAtlasStorage::decal_get_aabb(RID p_decal) const {
+ return AABB();
+}
+
+#endif // !GLES3_ENABLED
diff --git a/drivers/gles3/storage/decal_atlas_storage.h b/drivers/gles3/storage/decal_atlas_storage.h
new file mode 100644
index 0000000000..f5dc36b1fb
--- /dev/null
+++ b/drivers/gles3/storage/decal_atlas_storage.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* decal_atlas_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 DECAL_ATLAS_STORAGE_GLES3_H
+#define DECAL_ATLAS_STORAGE_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/rid_owner.h"
+#include "servers/rendering/storage/decal_atlas_storage.h"
+
+namespace GLES3 {
+
+class DecalAtlasStorage : public RendererDecalAtlasStorage {
+public:
+ 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 {}
+};
+
+} // namespace GLES3
+
+#endif // !GLES3_ENABLED
+
+#endif // !DECAL_ATLAS_STORAGE_GLES3_H
diff --git a/drivers/gles3/storage/render_target_storage.h b/drivers/gles3/storage/render_target_storage.h
new file mode 100644
index 0000000000..816cc76e40
--- /dev/null
+++ b/drivers/gles3/storage/render_target_storage.h
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* render_target_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 RENDER_TARGET_STORAGE_GLES3_H
+#define RENDER_TARGET_STORAGE_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/rid_owner.h"
+#include "servers/rendering/renderer_compositor.h"
+#include "servers/rendering/renderer_storage.h" // included until we move stuff into storage/render_target_storage.h
+// #include "servers/rendering/storage/render_target_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 {
+
+// NOTE, this class currently is just a container for the the RenderTarget struct and is not yet implemented further, we'll do that next after we finish with TextureStorage
+
+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 copy_screen_effect;
+
+ struct MipMaps {
+ struct Size {
+ GLuint fbo = 0;
+ GLuint color = 0;
+ int width = 0;
+ int height = 0;
+ };
+
+ Vector<Size> sizes;
+ GLuint color = 0;
+ int levels = 0;
+ };
+
+ MipMaps mip_maps[2];
+
+ struct External {
+ GLuint fbo = 0;
+ GLuint color = 0;
+ GLuint depth = 0;
+ RID texture;
+ } external;
+
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+
+ bool flags[RendererStorage::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 < RendererStorage::RENDER_TARGET_FLAG_MAX; ++i) {
+ flags[i] = false;
+ }
+ external.fbo = 0;
+ }
+};
+
+} // namespace GLES3
+
+#endif // !GLES3_ENABLED
+
+#endif // !RENDER_TARGET_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..d199b1032e
--- /dev/null
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -0,0 +1,1211 @@
+/*************************************************************************/
+/* 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"
+
+using namespace GLES3;
+
+TextureStorage *TextureStorage::singleton = nullptr;
+
+TextureStorage *TextureStorage::get_singleton() {
+ return singleton;
+}
+
+TextureStorage::TextureStorage() {
+ singleton = this;
+}
+
+TextureStorage::~TextureStorage() {
+ singleton = nullptr;
+}
+
+void TextureStorage::set_main_thread_id(Thread::ID p_id) {
+ _main_thread_id = p_id;
+}
+
+bool TextureStorage::_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;
+}
+
+bool TextureStorage::can_create_resources_async() const {
+ return false;
+}
+
+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,
+};
+
+Ref<Image> TextureStorage::_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 {
+ 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;
+ //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;
+}
+
+void TextureStorage::_texture_set_state_from_flags(Texture *p_tex) {
+ // Config *config = Config::get_singleton();
+
+ 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 TextureStorage::_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;
+
+ // Config *config = Config::get_singleton();
+
+ 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;
+}
+
+RID TextureStorage::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);
+}
+
+RID TextureStorage::texture_allocate() {
+ RID id = texture_create();
+ ERR_FAIL_COND_V(id == RID(), id);
+ return id;
+}
+
+void TextureStorage::texture_free(RID p_rid) {
+ Texture *t = texture_owner.get_or_null(p_rid);
+
+ // can't free a render target texture
+ ERR_FAIL_COND(t->render_target);
+ if (t->canvas_texture) {
+ memdelete(t->canvas_texture);
+ }
+
+ // info.texture_mem -= t->total_data_size; // TODO make this work again!!
+ texture_owner.free(p_rid);
+ memdelete(t);
+}
+
+void TextureStorage::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 TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
+}
+
+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) {
+}
+
+void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
+ texture_set_proxy(p_texture, p_base);
+}
+
+//RID TextureStorage::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 TextureStorage::texture_2d_layered_create(const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
+// return RID();
+//}
+
+//void TextureStorage::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 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);
+}
+
+void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
+}
+
+void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) {
+}
+
+void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
+}
+
+Ref<Image> TextureStorage::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 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);
+ 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);
+ }
+
+ texture_free(p_by_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->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 = p_callback;
+ texture->detect_3d_ud = p_userdata;
+}
+
+void TextureStorage::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 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 = p_callback;
+ texture->detect_normal_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->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 TextureStorage::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 TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) {
+ Config *config = Config::get_singleton();
+ 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; // 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);
+
+ 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 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) {
+ // TODO
+ ERR_PRINT("Not implemented (ask Karroffel to do it :p)");
+}
+
+/*
+Ref<Image> TextureStorage::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 TextureStorage::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 TextureStorage::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 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;
+}
+
+RenderingDevice::TextureType TextureStorage::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 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);
+}
+
+void TextureStorage::texture_set_shrink_all_x2_on_set_data(bool p_enable) {
+ Config::get_singleton()->shrink_textures_x2 = p_enable;
+}
+
+RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resolution) const {
+ return RID();
+}
+
+void TextureStorage::textures_keep_original(bool p_enable) {
+ Config::get_singleton()->keep_original_textures = p_enable;
+}
+
+#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..8a921fbe10
--- /dev/null
+++ b/drivers/gles3/storage/texture_storage.h
@@ -0,0 +1,389 @@
+/*************************************************************************/
+/* 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 "canvas_texture_storage.h"
+#include "config.h"
+#include "core/os/os.h"
+#include "core/templates/rid_owner.h"
+#include "render_target_storage.h"
+#include "servers/rendering/storage/texture_storage.h"
+
+namespace GLES3 {
+
+#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
+
+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
+};
+
+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;
+};
+
+class TextureStorage : public RendererTextureStorage {
+private:
+ static TextureStorage *singleton;
+
+ Thread::ID _main_thread_id = 0;
+ bool _is_main_thread();
+
+ 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);
+
+ void texture_set_proxy(RID p_texture, RID p_proxy);
+
+public:
+ static TextureStorage *get_singleton();
+
+ TextureStorage();
+ virtual ~TextureStorage();
+
+ Texture *get_texture(RID p_rid) { return texture_owner.get_or_null(p_rid); };
+ bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); };
+ RID make_rid(Texture *p_texture) { return texture_owner.make_rid(p_texture); };
+
+ void set_main_thread_id(Thread::ID p_id);
+
+ virtual bool can_create_resources_async() const override;
+
+ RID texture_create();
+ 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);
+
+ 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_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_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);
+ void texture_set_shrink_all_x2_on_set_data(bool p_enable);
+ RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const;
+ void textures_keep_original(bool p_enable);
+};
+
+} // namespace GLES3
+
+#endif // !GLES3_ENABLED
+
+#endif // !TEXTURE_STORAGE_GLES3_H
diff --git a/drivers/dummy/texture_loader_dummy.cpp b/drivers/gles3/texture_loader_gles3.cpp
index f148e42845..f8d4cfdc61 100644
--- a/drivers/dummy/texture_loader_dummy.cpp
+++ b/drivers/gles3/texture_loader_gles3.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* texture_loader_dummy.cpp */
+/* texture_loader_gles3.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,14 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "texture_loader_dummy.h"
+#include "texture_loader_gles3.h"
-#include "core/os/file_access.h"
+#ifdef GLES3_ENABLED
+
+#include "core/io/file_access.h"
#include "core/string/print_string.h"
#include <string.h>
-RES ResourceFormatDummyTexture::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) {
+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;
@@ -60,13 +62,14 @@ RES ResourceFormatDummyTexture::load(const String &p_path, const String &p_origi
Ref<ImageTexture> texture = memnew(ImageTexture);
texture->create_from_image(img);
- if (r_error)
+ if (r_error) {
*r_error = OK;
+ }
return texture;
}
-void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_extensions) const {
+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");
@@ -77,16 +80,15 @@ void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_exten
p_extensions->push_back("png");
p_extensions->push_back("pvr");
p_extensions->push_back("svg");
- p_extensions->push_back("svgz");
p_extensions->push_back("tga");
p_extensions->push_back("webp");
}
-bool ResourceFormatDummyTexture::handles_type(const String &p_type) const {
+bool ResourceFormatGLES2Texture::handles_type(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Texture2D");
}
-String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const {
+String ResourceFormatGLES2Texture::get_resource_type(const String &p_path) const {
String extension = p_path.get_extension().to_lower();
if (
extension == "bmp" ||
@@ -99,7 +101,6 @@ String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const
extension == "png" ||
extension == "pvr" ||
extension == "svg" ||
- extension == "svgz" ||
extension == "tga" ||
extension == "webp") {
return "ImageTexture";
@@ -107,3 +108,5 @@ String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const
return "";
}
+
+#endif
diff --git a/drivers/dummy/texture_loader_dummy.h b/drivers/gles3/texture_loader_gles3.h
index 00e6b9cc53..54ddf80a96 100644
--- a/drivers/dummy/texture_loader_dummy.h
+++ b/drivers/gles3/texture_loader_gles3.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* texture_loader_dummy.h */
+/* texture_loader_gles3.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,20 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTURE_LOADER_DUMMY_H
-#define TEXTURE_LOADER_DUMMY_H
+#ifndef TEXTURE_LOADER_OPENGL_H
+#define TEXTURE_LOADER_OPENGL_H
+
+#ifdef GLES3_ENABLED
#include "core/io/resource_loader.h"
#include "scene/resources/texture.h"
-class ResourceFormatDummyTexture : public ResourceFormatLoader {
+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;
- virtual ~ResourceFormatDummyTexture() {}
+ virtual ~ResourceFormatGLES2Texture() {}
};
-#endif // TEXTURE_LOADER_DUMMY_H
+#endif // GLES3_ENABLED
+
+#endif // TEXTURE_LOADER_OPENGL_H
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index 26508dc612..39d296e7cf 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -36,7 +36,8 @@ if env["builtin_libpng"]:
# Currently .ASM filter_neon.S does not compile on NT.
import os
- use_neon = "neon_enabled" in env and env["neon_enabled"] and os.name != "nt"
+ # Enable ARM NEON instructions on 32-bit Android to compile more optimized code.
+ use_neon = "android_arch" in env and env["android_arch"] == "armv7" and os.name != "nt"
if use_neon:
env_png.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 2)])
else:
diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp
index 854c6706e6..46e271f9c9 100644
--- a/drivers/png/image_loader_png.cpp
+++ b/drivers/png/image_loader_png.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,7 +37,7 @@
#include <string.h>
Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
- const size_t buffer_size = f->get_len();
+ const uint64_t buffer_size = f->get_length();
Vector<uint8_t> file_buffer;
Error err = file_buffer.resize(buffer_size);
if (err) {
@@ -59,7 +59,7 @@ void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const
Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) {
Ref<Image> img;
- img.instance();
+ img.instantiate();
// the value of p_force_linear does not matter since it only applies to 16 bit
Error err = PNGDriverCommon::png_to_image(p_png, p_size, false, img);
@@ -88,7 +88,7 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) {
{
// must be closed before call to image_to_png
uint8_t *writer = out_buffer.ptrw();
- copymem(writer, "PNG ", 4);
+ memcpy(writer, "PNG ", 4);
}
Error err = PNGDriverCommon::image_to_png(p_image, out_buffer);
@@ -101,6 +101,6 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) {
ImageLoaderPNG::ImageLoaderPNG() {
Image::_png_mem_loader_func = load_mem_png;
- Image::lossless_unpacker = lossless_unpack_png;
- Image::lossless_packer = lossless_pack_png;
+ Image::png_unpacker = lossless_unpack_png;
+ Image::png_packer = lossless_pack_png;
}
diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h
index b4a58616f6..af3bcd5b66 100644
--- a/drivers/png/image_loader_png.h
+++ b/drivers/png/image_loader_png.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp
index 9e848a2253..bc4bb3782b 100644
--- a/drivers/png/png_driver_common.cpp
+++ b/drivers/png/png_driver_common.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -60,7 +60,7 @@ static bool check_error(const png_image &image) {
Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image) {
png_image png_img;
- zeromem(&png_img, sizeof(png_img));
+ memset(&png_img, 0, sizeof(png_img));
png_img.version = PNG_IMAGE_VERSION;
// fetch image properties
@@ -134,7 +134,7 @@ Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V(source_image->is_compressed(), FAILED);
png_image png_img;
- zeromem(&png_img, sizeof(png_img));
+ memset(&png_img, 0, sizeof(png_img));
png_img.version = PNG_IMAGE_VERSION;
png_img.width = source_image->get_width();
png_img.height = source_image->get_height();
diff --git a/drivers/png/png_driver_common.h b/drivers/png/png_driver_common.h
index 003b587913..62302bbdbb 100644
--- a/drivers/png/png_driver_common.h
+++ b/drivers/png/png_driver_common.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/png/resource_saver_png.cpp b/drivers/png/resource_saver_png.cpp
index f47fc403cc..ca84fb6be9 100644
--- a/drivers/png/resource_saver_png.cpp
+++ b/drivers/png/resource_saver_png.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "resource_saver_png.h"
+#include "core/io/file_access.h"
#include "core/io/image.h"
-#include "core/os/file_access.h"
#include "drivers/png/png_driver_common.h"
#include "scene/resources/texture.h"
@@ -41,12 +41,12 @@ Error ResourceSaverPNG::save(const String &p_path, const RES &p_resource, uint32
ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as PNG.");
ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as PNG.");
- Ref<Image> img = texture->get_data();
+ Ref<Image> img = texture->get_image();
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;
@@ -89,4 +89,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 c924438224..f39e52c7ec 100644
--- a/drivers/png/resource_saver_png.h
+++ b/drivers/png/resource_saver_png.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 5e87bc019b..ef9bbc3483 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,19 +34,30 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+#include "core/version.h"
+
+#ifdef ALSAMIDI_ENABLED
+#include "drivers/alsa/asound-so_wrap.h"
+#endif
void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED:
+ print_verbose("PulseAudio: context terminated");
+ ad->pa_ready = -1;
+ break;
case PA_CONTEXT_FAILED:
+ print_verbose("PulseAudio: context failed");
ad->pa_ready = -1;
break;
case PA_CONTEXT_READY:
+ print_verbose("PulseAudio: context ready");
ad->pa_ready = 1;
break;
default:
+ print_verbose("PulseAudio: context other");
// TODO: Check if we want to handle some of the other
// PA context states like PA_CONTEXT_UNCONNECTED.
break;
@@ -61,6 +72,13 @@ void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l
return;
}
+ // If eol is set to a negative number there's an error.
+ if (eol < 0) {
+ ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));
+ ad->pa_status--;
+ return;
+ }
+
ad->pa_map = l->channel_map;
ad->pa_status++;
}
@@ -73,6 +91,13 @@ void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_inf
return;
}
+ // If eol is set to a negative number there's an error.
+ if (eol < 0) {
+ ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));
+ ad->pa_status--;
+ return;
+ }
+
ad->pa_rec_map = l->channel_map;
ad->pa_status++;
}
@@ -86,7 +111,7 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf
ad->pa_status++;
}
-void AudioDriverPulseAudio::detect_channels(bool capture) {
+Error AudioDriverPulseAudio::detect_channels(bool capture) {
pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map);
String device = capture ? capture_device_name : device_name;
@@ -104,7 +129,8 @@ void AudioDriverPulseAudio::detect_channels(bool capture) {
pa_operation_unref(pa_op);
} else {
- ERR_PRINT("pa_context_get_server_info error");
+ ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
+ return FAILED;
}
}
@@ -114,6 +140,7 @@ void AudioDriverPulseAudio::detect_channels(bool capture) {
} else {
strcpy(dev, device.utf8().get_data());
}
+ print_verbose("PulseAudio: Detecting channels for device: " + String(dev));
// Now using the device name get the amount of channels
pa_status = 0;
@@ -133,6 +160,10 @@ void AudioDriverPulseAudio::detect_channels(bool capture) {
}
pa_operation_unref(pa_op);
+
+ if (pa_status == -1) {
+ return FAILED;
+ }
} else {
if (capture) {
ERR_PRINT("pa_context_get_source_info_by_name error");
@@ -140,6 +171,8 @@ void AudioDriverPulseAudio::detect_channels(bool capture) {
ERR_PRINT("pa_context_get_sink_info_by_name error");
}
}
+
+ return OK;
}
Error AudioDriverPulseAudio::init_device() {
@@ -156,7 +189,13 @@ Error AudioDriverPulseAudio::init_device() {
// Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,
// if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels
// will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)
- detect_channels();
+ Error err = detect_channels();
+ if (err != OK) {
+ // This most likely means there are no sinks.
+ ERR_PRINT("PulseAudio: init device failed to detect number of output channels");
+ return err;
+ }
+
switch (pa_map.channels) {
case 1: // Mono
case 3: // Surround 2.1
@@ -173,7 +212,7 @@ Error AudioDriverPulseAudio::init_device() {
break;
default:
- WARN_PRINT("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
+ WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));
pa_channel_map_init_stereo(&pa_map);
channels = 2;
break;
@@ -183,8 +222,8 @@ Error AudioDriverPulseAudio::init_device() {
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
- print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " channels");
- print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
+ print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
+ print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
@@ -238,6 +277,10 @@ Error AudioDriverPulseAudio::init() {
#else
int dylibloader_verbose = 0;
#endif
+#ifdef ALSAMIDI_ENABLED
+ // If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well
+ initialize_asound(dylibloader_verbose);
+#endif
if (initialize_pulse(dylibloader_verbose)) {
return ERR_CANT_OPEN;
}
@@ -251,7 +294,17 @@ Error AudioDriverPulseAudio::init() {
pa_ml = pa_mainloop_new();
ERR_FAIL_COND_V(pa_ml == nullptr, ERR_CANT_OPEN);
- pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot");
+ String context_name;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ context_name = VERSION_NAME " Editor";
+ } else {
+ context_name = GLOBAL_GET("application/config/name");
+ if (context_name.is_empty()) {
+ context_name = VERSION_NAME " Project";
+ }
+ }
+
+ pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr());
ERR_FAIL_COND_V(pa_ctx == nullptr, ERR_CANT_OPEN);
pa_ready = 0;
@@ -294,10 +347,8 @@ Error AudioDriverPulseAudio::init() {
return ERR_CANT_OPEN;
}
- Error err = init_device();
- if (err == OK) {
- thread.start(AudioDriverPulseAudio::thread_func, this);
- }
+ init_device();
+ thread.start(AudioDriverPulseAudio::thread_func, this);
return OK;
}
@@ -331,7 +382,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
unsigned int write_ofs = 0;
size_t avail_bytes = 0;
- uint32_t default_device_msec = OS::get_singleton()->get_ticks_msec();
+ uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();
while (!ad->exit_thread) {
size_t read_bytes = 0;
@@ -342,15 +393,15 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
ad->start_counting_ticks();
if (!ad->active) {
- for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
- ad->samples_out.write[i] = 0;
- }
+ ad->samples_out.fill(0);
} else {
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
+ int16_t *out_ptr = ad->samples_out.ptrw();
+
if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
- ad->samples_out.write[i] = ad->samples_in[i] >> 16;
+ out_ptr[i] = ad->samples_in[i] >> 16;
}
} else {
// Uneven amount of channels
@@ -359,11 +410,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
for (int j = 0; j < ad->pa_map.channels - 1; j++) {
- ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16;
+ out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
uint32_t l = ad->samples_in[in_idx++] >> 16;
uint32_t r = ad->samples_in[in_idx++] >> 16;
- ad->samples_out.write[out_idx++] = (l + r) / 2;
+ out_ptr[out_idx++] = (l + r) / 2;
}
}
}
@@ -423,7 +474,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
// If we're using the default device check that the current device is still the default
if (ad->device_name == "Default") {
- uint32_t msec = OS::get_singleton()->get_ticks_msec();
+ uint64_t msec = OS::get_singleton()->get_ticks_msec();
if (msec > (default_device_msec + 1000)) {
String old_default_device = ad->default_device;
@@ -441,7 +492,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
pa_operation_unref(pa_op);
} else {
- ERR_PRINT("pa_context_get_server_info error");
+ ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx))));
}
if (old_default_device != ad->default_device) {
@@ -649,6 +700,8 @@ Error AudioDriverPulseAudio::capture_init_device() {
break;
}
+ print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
+
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index fa9b573d94..af96489972 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -89,7 +89,7 @@ class AudioDriverPulseAudio : public AudioDriver {
Error capture_init_device();
void capture_finish_device();
- void detect_channels(bool capture = false);
+ Error detect_channels(bool capture = false);
static void thread_func(void *p_udata);
diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp
index 18262c74c4..1047e89e85 100644
--- a/drivers/register_driver_types.cpp
+++ b/drivers/register_driver_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,7 @@
#include "register_driver_types.h"
+#include "core/extension/native_extension_manager.h"
#include "drivers/png/image_loader_png.h"
#include "drivers/png/resource_saver_png.h"
@@ -40,7 +41,7 @@ void register_core_driver_types() {
image_loader_png = memnew(ImageLoaderPNG);
ImageLoader::add_image_format_loader(image_loader_png);
- resource_saver_png.instance();
+ resource_saver_png.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_png);
}
@@ -54,7 +55,9 @@ 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 607aa91cb8..c008d93185 100644
--- a/drivers/register_driver_types.h
+++ b/drivers/register_driver_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index eda929850c..af47173b41 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -71,7 +71,7 @@ Error DirAccessUnix::list_dir_begin() {
bool DirAccessUnix::file_exists(String p_file) {
GLOBAL_LOCK_FUNCTION
- if (p_file.is_rel_path()) {
+ if (p_file.is_relative_path()) {
p_file = current_dir.plus_file(p_file);
}
@@ -90,7 +90,7 @@ bool DirAccessUnix::file_exists(String p_file) {
bool DirAccessUnix::dir_exists(String p_dir) {
GLOBAL_LOCK_FUNCTION
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
p_dir = get_current_dir().plus_file(p_dir);
}
@@ -102,8 +102,30 @@ bool DirAccessUnix::dir_exists(String p_dir) {
return (success && S_ISDIR(flags.st_mode));
}
+bool DirAccessUnix::is_readable(String p_dir) {
+ GLOBAL_LOCK_FUNCTION
+
+ if (p_dir.is_relative_path()) {
+ p_dir = get_current_dir().plus_file(p_dir);
+ }
+
+ p_dir = fix_path(p_dir);
+ return (access(p_dir.utf8().get_data(), R_OK) == 0);
+}
+
+bool DirAccessUnix::is_writable(String p_dir) {
+ GLOBAL_LOCK_FUNCTION
+
+ if (p_dir.is_relative_path()) {
+ p_dir = get_current_dir().plus_file(p_dir);
+ }
+
+ p_dir = fix_path(p_dir);
+ return (access(p_dir.utf8().get_data(), W_OK) == 0);
+}
+
uint64_t DirAccessUnix::get_modified_time(String p_file) {
- if (p_file.is_rel_path()) {
+ if (p_file.is_relative_path()) {
p_file = current_dir.plus_file(p_file);
}
@@ -116,9 +138,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) {
@@ -194,6 +216,8 @@ static bool _filter_drive(struct mntent *mnt) {
#endif
static void _get_drives(List<String> *list) {
+ list->push_back("/");
+
#if defined(HAVE_MNTENT) && defined(X11_ENABLED)
// Check /etc/mtab for the list of mounted partitions
FILE *mtab = setmntent("/etc/mtab", "r");
@@ -204,8 +228,9 @@ static void _get_drives(List<String> *list) {
while (getmntent_r(mtab, &mnt, strings, sizeof(strings))) {
if (mnt.mnt_dir != nullptr && _filter_drive(&mnt)) {
// Avoid duplicates
- if (!list->find(mnt.mnt_dir)) {
- list->push_back(mnt.mnt_dir);
+ String name = String::utf8(mnt.mnt_dir);
+ if (!list->find(name)) {
+ list->push_back(name);
}
}
}
@@ -218,8 +243,9 @@ static void _get_drives(List<String> *list) {
const char *home = getenv("HOME");
if (home) {
// Only add if it's not a duplicate
- if (!list->find(home)) {
- list->push_back(home);
+ String home_name = String::utf8(home);
+ if (!list->find(home_name)) {
+ list->push_back(home_name);
}
// Check $HOME/.config/gtk-3.0/bookmarks
@@ -232,7 +258,7 @@ static void _get_drives(List<String> *list) {
// 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
- String fpath = String(string + 7).strip_edges().split_spaces()[0].uri_decode();
+ String fpath = String::utf8(string + 7).strip_edges().split_spaces()[0].uri_decode();
if (!list->find(fpath)) {
list->push_back(fpath);
}
@@ -262,6 +288,20 @@ String DirAccessUnix::get_drive(int p_drive) {
return list[p_drive];
}
+int DirAccessUnix::get_current_drive() {
+ int drive = 0;
+ int max_length = -1;
+ const String path = get_current_dir().to_lower();
+ for (int i = 0; i < get_drive_count(); i++) {
+ const String d = get_drive(i).to_lower();
+ if (max_length < d.length() && path.begins_with(d)) {
+ max_length = d.length();
+ drive = i;
+ }
+ }
+ return drive;
+}
+
bool DirAccessUnix::drives_are_shortcuts() {
return true;
}
@@ -269,7 +309,7 @@ bool DirAccessUnix::drives_are_shortcuts() {
Error DirAccessUnix::make_dir(String p_dir) {
GLOBAL_LOCK_FUNCTION
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
p_dir = get_current_dir().plus_file(p_dir);
}
@@ -280,11 +320,11 @@ Error DirAccessUnix::make_dir(String p_dir) {
if (success) {
return OK;
- };
+ }
if (err == EEXIST) {
return ERR_ALREADY_EXISTS;
- };
+ }
return ERR_CANT_CREATE;
}
@@ -304,7 +344,7 @@ Error DirAccessUnix::change_dir(String p_dir) {
// try_dir is the directory we are trying to change into
String try_dir = "";
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
String next_dir = current_dir.plus_file(p_dir);
next_dir = next_dir.simplify_path();
try_dir = next_dir;
@@ -318,7 +358,7 @@ Error DirAccessUnix::change_dir(String p_dir) {
}
String base = _get_root_path();
- if (base != String() && !try_dir.begins_with(base)) {
+ if (!base.is_empty() && !try_dir.begins_with(base)) {
ERR_FAIL_COND_V(getcwd(real_current_dir_name, 2048) == nullptr, ERR_BUG);
String new_dir;
new_dir.parse_utf8(real_current_dir_name);
@@ -336,7 +376,7 @@ Error DirAccessUnix::change_dir(String p_dir) {
String DirAccessUnix::get_current_dir(bool p_include_drive) {
String base = _get_root_path();
- if (base != "") {
+ if (!base.is_empty()) {
String bd = current_dir.replace_first(base, "");
if (bd.begins_with("/")) {
return _get_root_string() + bd.substr(1, bd.length());
@@ -348,13 +388,13 @@ String DirAccessUnix::get_current_dir(bool p_include_drive) {
}
Error DirAccessUnix::rename(String p_path, String p_new_path) {
- if (p_path.is_rel_path()) {
+ if (p_path.is_relative_path()) {
p_path = get_current_dir().plus_file(p_path);
}
p_path = fix_path(p_path);
- if (p_new_path.is_rel_path()) {
+ if (p_new_path.is_relative_path()) {
p_new_path = get_current_dir().plus_file(p_new_path);
}
@@ -364,7 +404,7 @@ Error DirAccessUnix::rename(String p_path, String p_new_path) {
}
Error DirAccessUnix::remove(String p_path) {
- if (p_path.is_rel_path()) {
+ if (p_path.is_relative_path()) {
p_path = get_current_dir().plus_file(p_path);
}
@@ -382,19 +422,66 @@ Error DirAccessUnix::remove(String p_path) {
}
}
-size_t DirAccessUnix::get_space_left() {
+bool DirAccessUnix::is_link(String p_file) {
+ if (p_file.is_relative_path()) {
+ p_file = get_current_dir().plus_file(p_file);
+ }
+
+ p_file = fix_path(p_file);
+
+ struct stat flags;
+ if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
+ return FAILED;
+ }
+
+ return S_ISLNK(flags.st_mode);
+}
+
+String DirAccessUnix::read_link(String p_file) {
+ if (p_file.is_relative_path()) {
+ p_file = get_current_dir().plus_file(p_file);
+ }
+
+ p_file = fix_path(p_file);
+
+ char buf[256];
+ memset(buf, 0, 256);
+ ssize_t len = readlink(p_file.utf8().get_data(), buf, sizeof(buf));
+ String link;
+ if (len > 0) {
+ link.parse_utf8(buf, len);
+ }
+ return link;
+}
+
+Error DirAccessUnix::create_link(String p_source, String p_target) {
+ if (p_target.is_relative_path()) {
+ p_target = get_current_dir().plus_file(p_target);
+ }
+
+ p_source = fix_path(p_source);
+ p_target = fix_path(p_target);
+
+ if (symlink(p_source.utf8().get_data(), p_target.utf8().get_data()) == 0) {
+ return OK;
+ } else {
+ return FAILED;
+ }
+}
+
+uint64_t DirAccessUnix::get_space_left() {
#ifndef NO_STATVFS
struct statvfs vfs;
if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) {
return 0;
- };
+ }
- return vfs.f_bfree * vfs.f_bsize;
+ 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
diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h
index b70df1ca02..f90f55605c 100644
--- a/drivers/unix/dir_access_unix.h
+++ b/drivers/unix/dir_access_unix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED)
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include <dirent.h>
#include <sys/stat.h>
@@ -63,6 +63,7 @@ public:
virtual int get_drive_count();
virtual String get_drive(int p_drive);
+ virtual int get_current_drive();
virtual bool drives_are_shortcuts();
virtual Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
@@ -71,13 +72,19 @@ public:
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 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 size_t get_space_left();
+ 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 uint64_t get_space_left();
virtual String get_filesystem_type() const;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 6b24a85ff6..ea442ad8bf 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -111,7 +111,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) {
}
}
- if (is_backup_save_enabled() && (p_mode_flags & WRITE) && !(p_mode_flags & READ)) {
+ if (is_backup_save_enabled() && (p_mode_flags == WRITE)) {
save_path = path;
path = path + ".tmp";
}
@@ -160,7 +160,7 @@ void FileAccessUnix::close() {
close_notification_func(path, flags);
}
- if (save_path != "") {
+ if (!save_path.is_empty()) {
int rename_error = rename((save_path + ".tmp").utf8().get_data(), save_path.utf8().get_data());
if (rename_error && close_fail_notify) {
@@ -184,11 +184,11 @@ String FileAccessUnix::get_path_absolute() const {
return path;
}
-void FileAccessUnix::seek(size_t p_position) {
+void FileAccessUnix::seek(uint64_t p_position) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
last_error = OK;
- if (fseek(f, p_position, SEEK_SET)) {
+ if (fseeko(f, p_position, SEEK_SET)) {
check_errors();
}
}
@@ -196,15 +196,15 @@ void FileAccessUnix::seek(size_t p_position) {
void FileAccessUnix::seek_end(int64_t p_position) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
- if (fseek(f, p_position, SEEK_END)) {
+ if (fseeko(f, p_position, SEEK_END)) {
check_errors();
}
}
-size_t FileAccessUnix::get_position() const {
+uint64_t FileAccessUnix::get_position() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
- long pos = ftell(f);
+ int64_t pos = ftello(f);
if (pos < 0) {
check_errors();
ERR_FAIL_V(0);
@@ -212,15 +212,15 @@ size_t FileAccessUnix::get_position() const {
return pos;
}
-size_t FileAccessUnix::get_len() const {
+uint64_t FileAccessUnix::get_length() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
- long pos = ftell(f);
+ int64_t pos = ftello(f);
ERR_FAIL_COND_V(pos < 0, 0);
- ERR_FAIL_COND_V(fseek(f, 0, SEEK_END), 0);
- long size = ftell(f);
+ ERR_FAIL_COND_V(fseeko(f, 0, SEEK_END), 0);
+ int64_t size = ftello(f);
ERR_FAIL_COND_V(size < 0, 0);
- ERR_FAIL_COND_V(fseek(f, pos, SEEK_SET), 0);
+ ERR_FAIL_COND_V(fseeko(f, pos, SEEK_SET), 0);
return size;
}
@@ -239,12 +239,14 @@ uint8_t FileAccessUnix::get_8() const {
return b;
}
-int FileAccessUnix::get_buffer(uint8_t *p_dst, int p_length) const {
+uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(!f, -1, "File must be opened before use.");
- int read = fread(p_dst, 1, p_length, f);
+
+ uint64_t read = fread(p_dst, 1, p_length, f);
check_errors();
return read;
-};
+}
Error FileAccessUnix::get_error() const {
return last_error;
@@ -260,10 +262,10 @@ void FileAccessUnix::store_8(uint8_t p_dest) {
ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1);
}
-void FileAccessUnix::store_buffer(const uint8_t *p_src, int p_length) {
+void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(!f, "File must be opened before use.");
- ERR_FAIL_COND(!p_src);
- ERR_FAIL_COND((int)fwrite(p_src, 1, p_length, f) != p_length);
+ ERR_FAIL_COND(!p_src && p_length > 0);
+ ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != p_length);
}
bool FileAccessUnix::file_exists(const String &p_path) {
@@ -283,8 +285,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
@@ -305,8 +308,9 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) {
if (!err) {
return flags.st_mtime;
} else {
- ERR_FAIL_V_MSG(0, "Failed to get modified time for: " + p_file + ".");
- };
+ print_verbose("Failed to get modified time for: " + p_file + "");
+ return 0;
+ }
}
uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) {
@@ -318,7 +322,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) {
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 998fad7909..8ebdcd2a2d 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef FILE_ACCESS_UNIX_H
#define FILE_ACCESS_UNIX_H
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/memory.h"
#include <stdio.h>
@@ -61,21 +61,21 @@ public:
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 void seek(size_t p_position); ///< seek to a given position
+ 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 size_t get_position() const; ///< get position in the file
- virtual size_t get_len() const; ///< get size of the file
+ virtual uint64_t get_position() const; ///< get position in the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
- virtual int get_buffer(uint8_t *p_dst, int p_length) const;
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
virtual Error get_error() const; ///< 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, int p_length); ///< store an array of bytes
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
virtual bool file_exists(const String &p_path); ///< return true if a file exists
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 8ec1de4386..d442e521bf 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,24 +36,12 @@
#ifdef WINDOWS_ENABLED
#include <stdio.h>
-#include <winsock2.h>
-// Needs to be included after winsocks2.h
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+#include <winsock2.h>
#include <ws2tcpip.h>
#ifndef UWP_ENABLED
-#if defined(__MINGW32__) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4)
-// MinGW-w64 on Ubuntu 12.04 (our Travis build env) has bugs in this code where
-// some includes are missing in dependencies of iphlpapi.h for WINVER >= 0x0600 (Vista).
-// We don't use this Vista code for now, so working it around by disabling it.
-// MinGW-w64 >= 4.0 seems to be better judging by its headers.
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0501 // Windows XP, disable Vista API
-#include <iphlpapi.h>
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0600 // Re-enable Vista API
-#else
#include <iphlpapi.h>
-#endif // MINGW hack
#endif
#else // UNIX
#include <netdb.h>
@@ -75,8 +63,8 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last
#endif
-static IP_Address _sockaddr2ip(struct sockaddr *p_addr) {
- IP_Address ip;
+static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
+ IPAddress ip;
if (p_addr->sa_family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *)p_addr;
@@ -84,12 +72,12 @@ static IP_Address _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;
-};
+}
-IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
+void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
struct addrinfo hints;
struct addrinfo *result = nullptr;
@@ -102,35 +90,45 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
} 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.");
- return IP_Address();
- };
+ return;
+ }
if (result == nullptr || result->ai_addr == nullptr) {
ERR_PRINT("Invalid response from getaddrinfo");
if (result) {
freeaddrinfo(result);
}
- return IP_Address();
- };
+ return;
+ }
- IP_Address ip = _sockaddr2ip(result->ai_addr);
+ struct addrinfo *next = result;
- freeaddrinfo(result);
+ do {
+ if (next->ai_addr == nullptr) {
+ next = next->ai_next;
+ continue;
+ }
+ IPAddress ip = _sockaddr2ip(next->ai_addr);
+ if (ip.is_valid() && !r_addresses.find(ip)) {
+ r_addresses.push_back(ip);
+ }
+ next = next->ai_next;
+ } while (next);
- return ip;
+ freeaddrinfo(result);
}
#if defined(WINDOWS_ENABLED)
#if defined(UWP_ENABLED)
-void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
using namespace Windows::Networking;
using namespace Windows::Networking::Connectivity;
@@ -140,8 +138,9 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
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);
@@ -156,14 +155,14 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
Interface_Info &info = E->get();
- IP_Address ip = IP_Address(hostname->CanonicalName->Data());
+ IPAddress ip = IPAddress(hostname->CanonicalName->Data());
info.ip_addresses.push_front(ip);
}
}
#else
-void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
ULONG buf_size = 1024;
IP_ADAPTER_ADDRESSES *addrs;
@@ -173,14 +172,14 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
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;
@@ -193,25 +192,27 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
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 IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
+void IPUnix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
int family;
@@ -249,15 +250,15 @@ void IP_Unix::get_local_interfaces(Map<String, Interface_Info> *r_interfaces) co
}
#endif
-void IP_Unix::make_default() {
+void IPUnix::make_default() {
_create = _create_unix;
}
-IP *IP_Unix::_create_unix() {
- return memnew(IP_Unix);
+IP *IPUnix::_create_unix() {
+ return memnew(IPUnix);
}
-IP_Unix::IP_Unix() {
+IPUnix::IPUnix() {
}
#endif
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index ca2ee17f4e..f0ad01d248 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,10 +35,10 @@
#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
-class IP_Unix : public IP {
- GDCLASS(IP_Unix, IP);
+class IPUnix : public IP {
+ GDCLASS(IPUnix, IP);
- virtual IP_Address _resolve_hostname(const String &p_hostname, IP::Type p_type) override;
+ virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;
static IP *_create_unix();
@@ -46,7 +46,7 @@ public:
virtual void get_local_interfaces(Map<String, Interface_Info> *r_interfaces) const override;
static void make_default();
- IP_Unix();
+ IPUnix();
};
#endif
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index 19753943c8..3130d5cae2 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -95,7 +95,7 @@
#endif
-size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) {
+size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
memset(p_addr, 0, sizeof(struct sockaddr_storage));
if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket
@@ -106,7 +106,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(p_port);
if (p_ip.is_valid()) {
- copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
+ memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
} else {
addr6->sin6_addr = in6addr_any;
}
@@ -121,7 +121,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
addr4->sin_port = htons(p_port); // short, network byte order
if (p_ip.is_valid()) {
- copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
+ memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
} else {
addr4->sin_addr.s_addr = INADDR_ANY;
}
@@ -130,19 +130,24 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
}
}
-void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) {
+void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
if (p_addr->ss_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
- r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
-
- r_port = ntohs(addr4->sin_port);
-
+ if (r_ip) {
+ r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
+ }
+ if (r_port) {
+ *r_port = ntohs(addr4->sin_port);
+ }
} else if (p_addr->ss_family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
- r_ip.set_ipv6(addr6->sin6_addr.s6_addr);
-
- r_port = ntohs(addr6->sin6_port);
- };
+ if (r_ip) {
+ r_ip->set_ipv6(addr6->sin6_addr.s6_addr);
+ }
+ if (r_port) {
+ *r_port = ntohs(addr6->sin6_port);
+ }
+ }
}
NetSocket *NetSocketPosix::_create_func() {
@@ -186,13 +191,21 @@ NetSocketPosix::~NetSocketPosix() {
NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
#if defined(WINDOWS_ENABLED)
int err = WSAGetLastError();
-
- if (err == WSAEISCONN)
+ if (err == WSAEISCONN) {
return ERR_NET_IS_CONNECTED;
- if (err == WSAEINPROGRESS || err == WSAEALREADY)
+ }
+ if (err == WSAEINPROGRESS || err == WSAEALREADY) {
return ERR_NET_IN_PROGRESS;
- if (err == WSAEWOULDBLOCK)
+ }
+ if (err == WSAEWOULDBLOCK) {
return ERR_NET_WOULD_BLOCK;
+ }
+ if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) {
+ return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
+ }
+ if (err == WSAEACCES) {
+ return ERR_NET_UNAUTHORIZED;
+ }
print_verbose("Socket error: " + itos(err));
return ERR_NET_OTHER;
#else
@@ -205,6 +218,12 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return ERR_NET_WOULD_BLOCK;
}
+ if (errno == EADDRINUSE || errno == EINVAL || errno == EADDRNOTAVAIL) {
+ return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
+ }
+ if (errno == EACCES) {
+ return ERR_NET_UNAUTHORIZED;
+ }
print_verbose("Socket error: " + itos(errno));
return ERR_NET_OTHER;
#endif
@@ -214,7 +233,7 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
#pragma GCC diagnostic pop
#endif
-bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const {
+bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {
return false;
} else if (!p_for_bind && !p_ip.is_valid()) {
@@ -225,7 +244,7 @@ bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind)
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
}
-_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add) {
+_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER);
@@ -235,12 +254,12 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St
int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
int ret = -1;
- IP_Address if_ip;
+ IPAddress if_ip;
uint32_t if_v6id = 0;
Map<String, IP::Interface_Info> if_info;
IP::get_singleton()->get_local_interfaces(&if_info);
- for (Map<String, IP::Interface_Info>::Element *E = if_info.front(); E; E = E->next()) {
- IP::Interface_Info &c = E->get();
+ for (KeyValue<String, IP::Interface_Info> &E : if_info) {
+ IP::Interface_Info &c = E.value;
if (c.name != p_if_name) {
continue;
}
@@ -250,11 +269,11 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St
break; // IPv6 uses index.
}
- for (List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
- if (!F->get().is_ipv4()) {
+ for (const IPAddress &F : c.ip_addresses) {
+ if (!F.is_ipv4()) {
continue; // Wrong IP type
}
- if_ip = F->get();
+ if_ip = F;
break;
}
break;
@@ -264,13 +283,13 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St
ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER);
struct ip_mreq greq;
int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
- copymem(&greq.imr_multiaddr, p_ip.get_ipv4(), 4);
- copymem(&greq.imr_interface, if_ip.get_ipv4(), 4);
+ memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4);
+ memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4);
ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));
} else {
struct ipv6_mreq greq;
int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP;
- copymem(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16);
+ memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16);
greq.ipv6mr_interface = if_v6id;
ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));
}
@@ -306,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;
@@ -376,7 +396,7 @@ void NetSocketPosix::close() {
_is_stream = false;
}
-Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
+Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER);
@@ -384,8 +404,8 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
- _get_socket_error();
- print_verbose("Failed to bind socket.");
+ NetError err = _get_socket_error();
+ print_verbose("Failed to bind socket. Error: " + itos(err));
close();
return ERR_UNAVAILABLE;
}
@@ -401,12 +421,12 @@ Error NetSocketPosix::listen(int p_max_pending) {
print_verbose("Failed to listen from socket.");
close();
return FAILED;
- };
+ }
return OK;
}
-Error NetSocketPosix::connect_to_host(IP_Address p_host, uint16_t p_port) {
+Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER);
@@ -446,8 +466,8 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
FD_ZERO(&wr);
FD_ZERO(&ex);
FD_SET(_sock, &ex);
- struct timeval timeout = { p_timeout, 0 };
- // For blocking operation, pass nullptr timeout pointer to select.
+ struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 };
+ // For blocking operation, pass nullptr timeout pointer to select.
struct timeval *tp = nullptr;
if (p_timeout >= 0) {
// If timeout is non-negative, we want to specify the timeout instead.
@@ -475,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();
@@ -484,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
@@ -540,7 +563,7 @@ Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
return OK;
}
-Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek) {
+Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
struct sockaddr_storage from;
@@ -597,7 +620,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
return OK;
}
-Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) {
+Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
struct sockaddr_storage addr;
@@ -716,7 +739,21 @@ int NetSocketPosix::get_available_bytes() const {
return len;
}
-Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
+Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
+ ERR_FAIL_COND_V(!is_open(), FAILED);
+
+ struct sockaddr_storage saddr;
+ socklen_t len = sizeof(saddr);
+ if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {
+ _get_socket_error();
+ print_verbose("Error when reading local socket address.");
+ return FAILED;
+ }
+ _set_ip_port(&saddr, r_ip, r_port);
+ return OK;
+}
+
+Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
Ref<NetSocket> out;
ERR_FAIL_COND_V(!is_open(), out);
@@ -729,7 +766,7 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
return out;
}
- _set_ip_port(&their_addr, r_ip, r_port);
+ _set_ip_port(&their_addr, &r_ip, &r_port);
NetSocketPosix *ns = memnew(NetSocketPosix);
ns->_set_socket(fd, _ip_type, _is_stream);
@@ -737,11 +774,11 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
return Ref<NetSocket>(ns);
}
-Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, true);
}
-Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, false);
}
#endif
diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h
index cc6af661c8..867513099a 100644
--- a/drivers/unix/net_socket_posix.h
+++ b/drivers/unix/net_socket_posix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -54,39 +54,42 @@ private:
ERR_NET_WOULD_BLOCK,
ERR_NET_IS_CONNECTED,
ERR_NET_IN_PROGRESS,
- ERR_NET_OTHER
+ ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
+ ERR_NET_UNAUTHORIZED,
+ ERR_NET_OTHER,
};
NetError _get_socket_error() const;
void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream);
- _FORCE_INLINE_ Error _change_multicast_group(IP_Address p_ip, String p_if_name, bool p_add);
+ _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add);
_FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled);
protected:
static NetSocket *_create_func();
- bool _can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const;
+ bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const;
public:
static void make_default();
static void cleanup();
- static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port);
- static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type);
+ static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port);
+ static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type);
virtual Error open(Type p_sock_type, IP::Type &ip_type);
virtual void close();
- virtual Error bind(IP_Address p_addr, uint16_t p_port);
+ virtual Error bind(IPAddress p_addr, uint16_t p_port);
virtual Error listen(int p_max_pending);
- virtual Error connect_to_host(IP_Address p_host, uint16_t p_port);
+ virtual Error connect_to_host(IPAddress p_host, uint16_t p_port);
virtual Error poll(PollType p_type, int timeout) const;
virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read);
- virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek = false);
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false);
virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent);
- virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port);
- virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port);
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port);
+ virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port);
virtual bool is_open() const;
virtual int get_available_bytes() const;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const;
virtual Error set_broadcasting_enabled(bool p_enabled);
virtual void set_blocking_enabled(bool p_enabled);
@@ -94,8 +97,8 @@ public:
virtual void set_tcp_no_delay_enabled(bool p_enabled);
virtual void set_reuse_address_enabled(bool p_enabled);
virtual void set_reuse_port_enabled(bool p_enabled);
- virtual Error join_multicast_group(const IP_Address &p_multi_address, String p_if_name);
- virtual Error leave_multicast_group(const IP_Address &p_multi_address, String p_if_name);
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name);
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name);
NetSocketPosix();
~NetSocketPosix();
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index b9bd773c2e..3b5e1bf91d 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -65,6 +65,21 @@
#include <time.h>
#include <unistd.h>
+#if defined(OSX_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()) {
@@ -129,7 +144,7 @@ void OS_Unix::initialize_core() {
#ifndef NO_NETWORK
NetSocketPosix::make_default();
- IP_Unix::make_default();
+ IPUnix::make_default();
#endif
_setup_clock();
@@ -139,10 +154,6 @@ void OS_Unix::finalize_core() {
NetSocketPosix::cleanup();
}
-void OS_Unix::alert(const String &p_alert, const String &p_title) {
- fprintf(stderr, "ERROR: %s\n", p_alert.utf8().get_data());
-}
-
String OS_Unix::get_stdin_string(bool p_block) {
if (p_block) {
char buff[1024];
@@ -154,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";
}
@@ -162,12 +198,12 @@ 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 utc) const {
+OS::Date OS_Unix::get_date(bool p_utc) const {
time_t t = time(nullptr);
struct tm lt;
- if (utc) {
+ if (p_utc) {
gmtime_r(&t, &lt);
} else {
localtime_r(&t, &lt);
@@ -185,18 +221,18 @@ OS::Date OS_Unix::get_date(bool utc) const {
return ret;
}
-OS::Time OS_Unix::get_time(bool utc) const {
+OS::Time OS_Unix::get_time(bool p_utc) const {
time_t t = time(nullptr);
struct tm lt;
- if (utc) {
+ if (p_utc) {
gmtime_r(&t, &lt);
} else {
localtime_r(&t, &lt);
}
Time ret;
ret.hour = lt.tm_hour;
- ret.min = lt.tm_min;
- ret.sec = lt.tm_sec;
+ ret.minute = lt.tm_min;
+ ret.second = lt.tm_sec;
get_time_zone_info();
return ret;
}
@@ -253,7 +289,7 @@ uint64_t OS_Unix::get_ticks_usec() const {
return longtime;
}
-Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
@@ -322,7 +358,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, St
#endif
}
-Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
@@ -374,7 +410,7 @@ Error OS_Unix::kill(const ProcessID &p_pid) {
int OS_Unix::get_process_id() const {
return getpid();
-};
+}
bool OS_Unix::has_environment(const String &p_var) const {
return getenv(p_var.utf8().get_data()) != nullptr;
@@ -396,19 +432,19 @@ String OS_Unix::get_locale() const {
Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
String path = p_path;
- if (FileAccess::exists(path) && path.is_rel_path()) {
+ if (FileAccess::exists(path) && path.is_relative_path()) {
// dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path,
// otherwise it will end up searching various system directories for the lib instead and finally failing.
path = "./" + path;
}
if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .so files from within the executable path
+ // 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());
}
@@ -464,11 +500,11 @@ int OS_Unix::get_processor_count() const {
String OS_Unix::get_user_data_dir() const {
String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
- if (appname != "") {
+ if (!appname.is_empty()) {
bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir");
if (use_custom_dir) {
String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true);
- if (custom_dir == "") {
+ if (custom_dir.is_empty()) {
custom_dir = appname;
}
return get_data_path().plus_file(custom_dir);
@@ -477,7 +513,7 @@ String OS_Unix::get_user_data_dir() const {
}
}
- return ProjectSettings::get_singleton()->get_resource_path();
+ return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("[unnamed project]");
}
String OS_Unix::get_executable_path() const {
@@ -490,7 +526,7 @@ String OS_Unix::get_executable_path() const {
if (len > 0) {
b.parse_utf8(buf, len);
}
- if (b == "") {
+ if (b.is_empty()) {
WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]");
return OS::get_executable_path();
}
@@ -519,8 +555,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;
@@ -532,7 +569,7 @@ String OS_Unix::get_executable_path() const {
#endif
}
-void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
if (!should_log(true)) {
return;
}
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 6c79d984e9..460ba4b9e1 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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;
@@ -52,18 +51,9 @@ protected:
public:
OS_Unix();
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
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 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 get_entropy(uint8_t *r_buffer, int p_bytes) override; // Should return cryptographycally-safe random bytes.
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override;
virtual Error close_dynamic_library(void *p_library_handle) override;
@@ -73,8 +63,8 @@ public:
virtual String get_name() const override;
- virtual Date get_date(bool utc) const override;
- virtual Time get_time(bool utc) const override;
+ virtual Date get_date(bool p_utc) const override;
+ virtual Time get_time(bool p_utc) const override;
virtual TimeZoneInfo get_time_zone_info() const override;
virtual double get_unix_time() const override;
@@ -82,8 +72,8 @@ public:
virtual void delay_usec(uint32_t p_usec) const override;
virtual uint64_t get_ticks_usec() const override;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ 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;
@@ -103,7 +93,7 @@ public:
class UnixTerminalLogger : public StdLogger {
public:
- virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) override;
virtual ~UnixTerminalLogger();
};
diff --git a/drivers/unix/syslog_logger.cpp b/drivers/unix/syslog_logger.cpp
index 423ddac793..6189d645c6 100644
--- a/drivers/unix/syslog_logger.cpp
+++ b/drivers/unix/syslog_logger.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 d9f7f2ff99..697a96a6f9 100644
--- a/drivers/unix/syslog_logger.h
+++ b/drivers/unix/syslog_logger.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/unix/thread_posix.cpp b/drivers/unix/thread_posix.cpp
index 19fab1d475..cb5f261e6e 100644
--- a/drivers/unix/thread_posix.cpp
+++ b/drivers/unix/thread_posix.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,6 +35,10 @@
#include "core/os/thread.h"
#include "core/string/ustring.h"
+#ifdef PTHREAD_BSD_SET_NAME
+#include <pthread_np.h>
+#endif
+
static Error set_name(const String &p_name) {
#ifdef PTHREAD_NO_RENAME
return ERR_UNAVAILABLE;
diff --git a/drivers/unix/thread_posix.h b/drivers/unix/thread_posix.h
index 8b8a736bf0..9cd3ecbe90 100644
--- a/drivers/unix/thread_posix.h
+++ b/drivers/unix/thread_posix.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/vulkan/SCsub b/drivers/vulkan/SCsub
index 14b9d63204..b6ceb1cdea 100644
--- a/drivers/vulkan/SCsub
+++ b/drivers/vulkan/SCsub
@@ -3,116 +3,48 @@
Import("env")
thirdparty_obj = []
+thirdparty_dir = "#thirdparty/vulkan"
+thirdparty_volk_dir = "#thirdparty/volk"
-# FIXME: Refactor all this to reduce code duplication.
-if env["platform"] == "android":
- # Use NDK Vulkan headers
- thirdparty_dir = env["ANDROID_NDK_ROOT"] + "/sources/third_party/vulkan/src"
- thirdparty_includes = [
- thirdparty_dir,
- thirdparty_dir + "/include",
- thirdparty_dir + "/layers",
- thirdparty_dir + "/layers/generated",
- ]
- env.Prepend(CPPPATH=thirdparty_includes)
-
- # Build Vulkan memory allocator
- env_thirdparty = env.Clone()
- env_thirdparty.disable_warnings()
-
- thirdparty_dir = "#thirdparty/vulkan"
- vma_sources = [thirdparty_dir + "/android/vk_mem_alloc.cpp"]
- env_thirdparty.add_source_files(thirdparty_obj, vma_sources)
+# Use bundled Vulkan headers
+env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
+
+if env["use_volk"]:
+ env.AppendUnique(CPPDEFINES=["USE_VOLK"])
+ env.Prepend(CPPPATH=[thirdparty_volk_dir])
+if env["platform"] == "android":
+ env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"])
elif env["platform"] == "iphone":
- # Use bundled Vulkan headers
- thirdparty_dir = "#thirdparty/vulkan"
- env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include", thirdparty_dir + "/loader"])
-
- # Build Vulkan memory allocator
- env_thirdparty = env.Clone()
- env_thirdparty.disable_warnings()
-
- vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"]
- env_thirdparty.add_source_files(thirdparty_obj, vma_sources)
-
-elif env["builtin_vulkan"]:
- # Use bundled Vulkan headers
- thirdparty_dir = "#thirdparty/vulkan"
- env.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include", thirdparty_dir + "/loader"])
-
- # Build Vulkan loader library
- env_thirdparty = env.Clone()
- env_thirdparty.disable_warnings()
-
- loader_sources = [
- "cJSON.c",
- "debug_utils.c",
- "dev_ext_trampoline.c",
- "loader.c",
- "murmurhash.c",
- "phys_dev_ext.c",
- "trampoline.c",
- "unknown_ext_chain.c",
- "wsi.c",
- "extension_manual.c",
- ]
- vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"]
-
- if env["platform"] == "windows":
- loader_sources.append("dirent_on_windows.c")
- env_thirdparty.AppendUnique(
- CPPDEFINES=[
- "VK_USE_PLATFORM_WIN32_KHR",
- "VULKAN_NON_CMAKE_BUILD",
- "WIN32_LEAN_AND_MEAN",
- 'API_NAME=\\"%s\\"' % "Vulkan",
- ]
- )
- if not env.msvc: # Windows 7+, missing in mingw headers
- env_thirdparty.AppendUnique(
- CPPDEFINES=["CM_GETIDLIST_FILTER_CLASS=0x00000200", "CM_GETIDLIST_FILTER_PRESENT=0x00000100"]
- )
- elif env["platform"] == "osx":
- env_thirdparty.AppendUnique(
- CPPDEFINES=[
- "VK_USE_PLATFORM_MACOS_MVK",
- "VULKAN_NON_CMAKE_BUILD",
- 'SYSCONFDIR=\\"%s\\"' % "/etc",
- 'FALLBACK_DATA_DIRS=\\"%s\\"' % "/usr/local/share:/usr/share",
- 'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg",
- ]
- )
- elif env["platform"] == "linuxbsd":
- env_thirdparty.AppendUnique(
- CPPDEFINES=[
- "VK_USE_PLATFORM_XLIB_KHR",
- "VULKAN_NON_CMAKE_BUILD",
- 'SYSCONFDIR=\\"%s\\"' % "/etc",
- 'FALLBACK_DATA_DIRS=\\"%s\\"' % "/usr/local/share:/usr/share",
- 'FALLBACK_CONFIG_DIRS=\\"%s\\"' % "/etc/xdg",
- ]
- )
- import platform
-
- if platform.system() == "Linux":
- # In glibc since 2.17 and musl libc since 1.1.24. Used by loader.c.
- env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
-
- loader_sources = [thirdparty_dir + "/loader/" + file for file in loader_sources]
- env_thirdparty.add_source_files(thirdparty_obj, loader_sources)
- env_thirdparty.add_source_files(thirdparty_obj, vma_sources)
-
-else: # Always build VMA.
- thirdparty_dir = "#thirdparty/vulkan"
- env.Prepend(CPPPATH=[thirdparty_dir])
-
- # Build Vulkan loader library
- env_thirdparty = env.Clone()
- env_thirdparty.disable_warnings()
- vma_sources = [thirdparty_dir + "/vk_mem_alloc.cpp"]
-
- env_thirdparty.add_source_files(thirdparty_obj, vma_sources)
+ env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"])
+elif env["platform"] == "linuxbsd":
+ env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"])
+elif env["platform"] == "osx":
+ env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"])
+elif env["platform"] == "windows":
+ env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_WIN32_KHR"])
+
+# Build Vulkan memory allocator and volk
+env_thirdparty_vma = env.Clone()
+env_thirdparty_vma.disable_warnings()
+thirdparty_sources_vma = [thirdparty_dir + "/vk_mem_alloc.cpp"]
+
+if env["use_volk"]:
+ env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_STATIC_VULKAN_FUNCTIONS=1"])
+ env_thirdparty_volk = env.Clone()
+ env_thirdparty_volk.disable_warnings()
+
+ thirdparty_sources_volk = [thirdparty_volk_dir + "/volk.c"]
+ env_thirdparty_volk.add_source_files(thirdparty_obj, thirdparty_sources_volk)
+elif env["platform"] == "android":
+ # Our current NDK version only provides old Vulkan headers,
+ # so we have to limit VMA.
+ env_thirdparty_vma.AppendUnique(CPPDEFINES=["VMA_VULKAN_VERSION=1000000"])
+elif env["platform"] == "osx" or env["platform"] == "iphone":
+ # 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)
env.drivers_sources += thirdparty_obj
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 9584dd3f67..62de01e8bb 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,20 +31,25 @@
#include "rendering_device_vulkan.h"
#include "core/config/project_settings.h"
-#include "core/os/file_access.h"
+#include "core/io/compression.h"
+#include "core/io/file_access.h"
+#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/templates/hashfuncs.h"
#include "drivers/vulkan/vulkan_context.h"
+#include "thirdparty/misc/smolv.h"
#include "thirdparty/spirv-reflect/spirv_reflect.h"
//#define FORCE_FULL_BARRIER
+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)) {
- buffer = vertex_buffer_owner.getornull(p_buffer);
+ buffer = vertex_buffer_owner.get_or_null(p_buffer);
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
r_access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
@@ -61,7 +66,7 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
} else if (index_buffer_owner.owns(p_buffer)) {
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
r_access_mask |= VK_ACCESS_INDEX_READ_BIT;
- buffer = index_buffer_owner.getornull(p_buffer);
+ buffer = index_buffer_owner.get_or_null(p_buffer);
} else if (uniform_buffer_owner.owns(p_buffer)) {
if (p_post_barrier & BARRIER_MASK_RASTER) {
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
@@ -70,7 +75,7 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
r_stage_mask |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
r_access_mask |= VK_ACCESS_UNIFORM_READ_BIT;
- buffer = uniform_buffer_owner.getornull(p_buffer);
+ buffer = uniform_buffer_owner.get_or_null(p_buffer);
} else if (texture_buffer_owner.owns(p_buffer)) {
if (p_post_barrier & BARRIER_MASK_RASTER) {
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
@@ -81,9 +86,9 @@ RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID
r_access_mask |= VK_ACCESS_SHADER_READ_BIT;
}
- buffer = &texture_buffer_owner.getornull(p_buffer)->buffer;
+ buffer = &texture_buffer_owner.get_or_null(p_buffer)->buffer;
} else if (storage_buffer_owner.owns(p_buffer)) {
- buffer = storage_buffer_owner.getornull(p_buffer);
+ buffer = storage_buffer_owner.get_or_null(p_buffer);
if (p_post_barrier & BARRIER_MASK_RASTER) {
r_stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
r_access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
@@ -119,7 +124,7 @@ static void update_external_dependency_for_store(VkSubpassDependency &dependency
}
if (is_depth) {
- // Depth resources have addtional 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;
}
@@ -384,14 +389,6 @@ const VkFormat RenderingDeviceVulkan::vulkan_formats[RenderingDevice::DATA_FORMA
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
- VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG,
- VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG,
- VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG,
};
const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MAX] = {
@@ -613,14 +610,6 @@ const char *RenderingDeviceVulkan::named_formats[RenderingDevice::DATA_FORMAT_MA
"G16_B16_R16_3Plane_422_Unorm",
"G16_B16R16_2Plane_422_Unorm",
"G16_B16_R16_3Plane_444_Unorm",
- "Pvrtc1_2Bpp_Unorm_Block_Img",
- "Pvrtc1_4Bpp_Unorm_Block_Img",
- "Pvrtc2_2Bpp_Unorm_Block_Img",
- "Pvrtc2_4Bpp_Unorm_Block_Img",
- "Pvrtc1_2Bpp_Srgb_Block_Img",
- "Pvrtc1_4Bpp_Srgb_Block_Img",
- "Pvrtc2_2Bpp_Srgb_Block_Img",
- "Pvrtc2_4Bpp_Srgb_Block_Img"
};
int RenderingDeviceVulkan::get_format_vertex_size(DataFormat p_format) {
@@ -967,15 +956,6 @@ uint32_t RenderingDeviceVulkan::get_image_format_pixel_size(DataFormat p_format)
case DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
return 8;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- return 1;
default: {
ERR_PRINT("Format not handled, bug");
}
@@ -1045,20 +1025,6 @@ void RenderingDeviceVulkan::get_compressed_image_format_block_dimensions(DataFor
r_w = 4;
r_h = 4;
return;
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- r_w = 4;
- r_h = 4;
- return;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- r_w = 8;
- r_h = 4;
- return;
default: {
r_w = 1;
r_h = 1;
@@ -1135,15 +1101,6 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_block_byte_size(Data
case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK:
case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK:
return 8; //wrong
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- return 8; //what varies is resolution
default: {
}
}
@@ -1164,16 +1121,7 @@ uint32_t RenderingDeviceVulkan::get_compressed_image_format_pixel_rshift(DataFor
case DATA_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case DATA_FORMAT_EAC_R11_UNORM_BLOCK:
case DATA_FORMAT_EAC_R11_SNORM_BLOCK:
- case DATA_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
return 1;
- case DATA_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: //these formats are quarter byte size, so rshift is 1
- case DATA_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
- case DATA_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
- case DATA_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
- return 2;
default: {
}
}
@@ -1228,7 +1176,7 @@ 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;
@@ -1236,23 +1184,23 @@ uint32_t RenderingDeviceVulkan::get_image_format_required_size(DataFormat p_form
uint32_t RenderingDeviceVulkan::get_image_required_mipmaps(uint32_t p_width, uint32_t p_height, uint32_t p_depth) {
//formats and block size don't really matter here since they can all go down to 1px (even if block is larger)
- int w = p_width;
- int h = p_height;
- int d = p_depth;
+ 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;
}
@@ -1389,6 +1337,11 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size,
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) + ".");
@@ -1398,12 +1351,15 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size,
p_buffer->buffer_info.range = p_size;
p_buffer->usage = p_usage;
+ buffer_memory += p_size;
+
return OK;
}
Error RenderingDeviceVulkan::_buffer_free(Buffer *p_buffer) {
ERR_FAIL_COND_V(p_buffer->size == 0, ERR_INVALID_PARAMETER);
+ buffer_memory -= p_buffer->size;
vmaDestroyBuffer(allocator, p_buffer->buffer, p_buffer->allocation);
p_buffer->buffer = VK_NULL_HANDLE;
p_buffer->allocation = nullptr;
@@ -1486,7 +1442,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_
// 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..
- if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) {
+ if ((uint64_t)staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) {
//we can, so we are safe
Error err = _insert_staging_block();
if (err) {
@@ -1530,7 +1486,7 @@ Error RenderingDeviceVulkan::_staging_buffer_allocate(uint32_t p_amount, uint32_
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?
- if (staging_buffer_blocks.size() * staging_buffer_block_size < staging_buffer_max_size) {
+ 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
Error err = _insert_staging_block();
if (err) {
@@ -1600,7 +1556,7 @@ Error RenderingDeviceVulkan::_buffer_update(Buffer *p_buffer, size_t p_offset, c
}
//copy to staging buffer
- copymem(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount);
+ memcpy(((uint8_t *)data_ptr) + block_write_offset, p_data + submit_from, block_write_amount);
//unmap
vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation);
@@ -1798,6 +1754,10 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
image_create_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
+ if (p_format.usage_bits & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) {
+ image_create_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+ }
+
if (p_format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT) {
image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
@@ -1883,20 +1843,28 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
//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.pool = nullptr;
allocInfo.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY;
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;
VkResult err = vmaCreateImage(allocator, &image_create_info, &allocInfo, &texture.image, &texture.allocation, &texture.allocation_info);
ERR_FAIL_COND_V_MSG(err, RID(), "vmaCreateImage failed with error " + itos(err) + ".");
-
+ image_memory += texture.allocation_info.size;
texture.type = p_format.texture_type;
texture.format = p_format.format;
texture.width = image_create_info.extent.width;
@@ -2029,7 +1997,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
if (p_data.size()) {
for (uint32_t i = 0; i < image_create_info.arrayLayers; i++) {
- texture_update(id, i, p_data[i]);
+ _texture_update(id, i, p_data[i], RD::BARRIER_MASK_ALL, true);
}
}
return id;
@@ -2038,12 +2006,12 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID p_with_texture) {
_THREAD_SAFE_METHOD_
- Texture *src_texture = texture_owner.getornull(p_with_texture);
+ 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
p_with_texture = src_texture->owner;
- src_texture = texture_owner.getornull(src_texture->owner);
+ src_texture = texture_owner.get_or_null(src_texture->owner);
ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug
}
@@ -2128,6 +2096,10 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID
}
}
+ if (texture.usage_flags & TEXTURE_USAGE_INPUT_ATTACHMENT_BIT) {
+ usage_info.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
+ }
+
if (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
usage_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
}
@@ -2156,15 +2128,133 @@ RID RenderingDeviceVulkan::texture_create_shared(const TextureView &p_view, RID
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, TextureSliceType p_slice_type) {
+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);
+
+ 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.getornull(p_with_texture);
+ 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
p_with_texture = src_texture->owner;
- src_texture = texture_owner.getornull(src_texture->owner);
+ src_texture = texture_owner.get_or_null(src_texture->owner);
ERR_FAIL_COND_V(!src_texture, RID()); //this is a bug
}
@@ -2180,6 +2270,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
//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());
ERR_FAIL_UNSIGNED_INDEX_V(p_layer, src_texture->layers, RID());
int slice_layers = 1;
@@ -2192,7 +2283,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
Texture texture = *src_texture;
get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height);
- texture.mipmaps = 1;
+ texture.mipmaps = p_mipmaps;
texture.layers = slice_layers;
texture.base_mipmap = p_mipmap;
texture.base_layer = p_layer;
@@ -2255,7 +2346,7 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
"Specified layer must be a multiple of 6.");
}
image_view_create_info.subresourceRange.baseMipLevel = p_mipmap;
- image_view_create_info.subresourceRange.levelCount = 1;
+ image_view_create_info.subresourceRange.levelCount = p_mipmaps;
image_view_create_info.subresourceRange.layerCount = slice_layers;
image_view_create_info.subresourceRange.baseArrayLayer = p_layer;
@@ -2276,17 +2367,21 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
}
Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, uint32_t p_post_barrier) {
+ return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false);
+}
+
+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_
- ERR_FAIL_COND_V_MSG(draw_list || compute_list, ERR_INVALID_PARAMETER,
- "Updating textures in is forbidden during creation of a draw or compute list");
+ ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER,
+ "Updating textures is forbidden during creation of a draw or compute list");
- Texture *texture = texture_owner.getornull(p_texture);
+ Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER);
if (texture->owner != RID()) {
p_texture = texture->owner;
- texture = texture_owner.getornull(texture->owner);
+ texture = texture_owner.get_or_null(texture->owner);
ERR_FAIL_COND_V(!texture, ERR_BUG); //this is a bug
}
@@ -2320,7 +2415,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con
const uint8_t *r = p_data.ptr();
- VkCommandBuffer command_buffer = p_post_barrier ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer;
+ VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer;
//barrier to transfer
{
@@ -2373,7 +2468,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con
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_post_barrier);
+ Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false, !p_use_setup_queue);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
uint8_t *write_ptr;
@@ -2461,8 +2556,8 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con
}
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
@@ -2502,7 +2597,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con
image_memory_barrier.subresourceRange.baseArrayLayer = p_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);
}
if (texture->used_in_frame != frames_drawn) {
@@ -2558,7 +2653,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex
const uint8_t *rptr = slice_read_ptr + y * layout.rowPitch;
uint8_t *wptr = write_ptr + y * line_width;
- copymem(wptr, rptr, line_width);
+ memcpy(wptr, rptr, line_width);
}
} else {
@@ -2566,7 +2661,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex
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;
- copymem(wptr, rptr, pixel_size * width);
+ memcpy(wptr, rptr, (uint64_t)pixel_size * width);
}
}
}
@@ -2583,7 +2678,7 @@ Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex
Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t p_layer) {
_THREAD_SAFE_METHOD_
- Texture *tex = texture_owner.getornull(p_texture);
+ Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_V(!tex, Vector<uint8_t>());
ERR_FAIL_COND_V_MSG(tex->bound, Vector<uint8_t>(),
@@ -2660,9 +2755,9 @@ 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;
}
@@ -2699,7 +2794,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t
{
buffer_data.resize(buffer_size);
uint8_t *w = buffer_data.ptrw();
- copymem(w, buffer_mem, buffer_size);
+ memcpy(w, buffer_mem, buffer_size);
}
vmaUnmapMemory(allocator, tmp_buffer.allocation);
@@ -2713,7 +2808,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t
bool RenderingDeviceVulkan::texture_is_shared(RID p_texture) {
_THREAD_SAFE_METHOD_
- Texture *tex = texture_owner.getornull(p_texture);
+ Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_V(!tex, false);
return tex->owner.is_valid();
}
@@ -2722,10 +2817,18 @@ bool RenderingDeviceVulkan::texture_is_valid(RID p_texture) {
return texture_owner.owns(p_texture);
}
+Size2i RenderingDeviceVulkan::texture_size(RID p_texture) {
+ _THREAD_SAFE_METHOD_
+
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND_V(!tex, Size2i());
+ return Size2i(tex->width, tex->height);
+}
+
Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, uint32_t p_post_barrier) {
_THREAD_SAFE_METHOD_
- Texture *src_tex = texture_owner.getornull(p_from_texture);
+ Texture *src_tex = texture_owner.get_or_null(p_from_texture);
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
@@ -2746,7 +2849,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
ERR_FAIL_COND_V(p_src_mipmap >= src_tex->mipmaps, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_src_layer >= src_layer_count, ERR_INVALID_PARAMETER);
- Texture *dst_tex = texture_owner.getornull(p_to_texture);
+ Texture *dst_tex = texture_owner.get_or_null(p_to_texture);
ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER,
@@ -2913,7 +3016,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture,
Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID p_to_texture, uint32_t p_post_barrier) {
_THREAD_SAFE_METHOD_
- Texture *src_tex = texture_owner.getornull(p_from_texture);
+ Texture *src_tex = texture_owner.get_or_null(p_from_texture);
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
@@ -2924,7 +3027,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID
ERR_FAIL_COND_V_MSG(src_tex->type != TEXTURE_TYPE_2D, ERR_INVALID_PARAMETER, "Source texture must be 2D (or a slice of a 3D/Cube texture)");
ERR_FAIL_COND_V_MSG(src_tex->samples == TEXTURE_SAMPLES_1, ERR_INVALID_PARAMETER, "Source texture must be multisampled.");
- Texture *dst_tex = texture_owner.getornull(p_to_texture);
+ Texture *dst_tex = texture_owner.get_or_null(p_to_texture);
ERR_FAIL_COND_V(!dst_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(dst_tex->bound, ERR_INVALID_PARAMETER,
@@ -3084,7 +3187,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID
Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, uint32_t p_post_barrier) {
_THREAD_SAFE_METHOD_
- Texture *src_tex = texture_owner.getornull(p_texture);
+ Texture *src_tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_V(!src_tex, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(src_tex->bound, ERR_INVALID_PARAMETER,
@@ -3108,7 +3211,7 @@ 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 onwner 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;
@@ -3244,58 +3347,69 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f
/**** ATTACHMENT ****/
/********************/
-VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) {
- Vector<VkAttachmentDescription> attachments;
- Vector<VkAttachmentReference> color_references;
- Vector<VkAttachmentReference> depth_stencil_references;
- Vector<VkAttachmentReference> resolve_references;
-
- // Set up a dependencies from/to external equivalent to the default (implicit) one, and then amend them
+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
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_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
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];
+ LocalVector<int32_t> attachment_last_pass;
+ attachment_last_pass.resize(p_attachments.size());
- for (int i = 0; i < p_format.size(); i++) {
- ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE);
- ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE);
- ERR_FAIL_COND_V_MSG(!(p_format[i].usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)),
- VK_NULL_HANDLE, "Texture format for index (" + itos(i) + ") requires an attachment (depth, stencil or resolve) bit set.");
+ Vector<VkAttachmentDescription> attachments;
+
+ for (int i = 0; i < p_attachments.size(); i++) {
+ 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.");
VkAttachmentDescription description = {};
description.flags = 0;
- description.format = vulkan_formats[p_format[i].format];
- description.samples = rasterization_sample_count[p_format[i].samples];
+ description.format = vulkan_formats[p_attachments[i].format];
+ description.samples = rasterization_sample_count[p_attachments[i].samples];
- bool is_depth_stencil = p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
- bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT;
- bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT;
+ bool is_sampled = p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT;
+ bool is_storage = p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT;
+ bool is_depth = p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
// For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write
- // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution syncronization vs.
- // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that
+ // 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_stencil ? p_initial_depth_action : p_initial_color_action) {
+
+ switch (is_depth ? p_initial_depth_action : p_initial_action) {
case INITIAL_ACTION_CLEAR_REGION:
case INITIAL_ACTION_CLEAR: {
- description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
- description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there
- dependency_from_external.srcStageMask |= reading_stages;
+ 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_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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;
@@ -3308,11 +3422,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
}
} break;
case INITIAL_ACTION_DROP: {
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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;
@@ -3326,11 +3440,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
} break;
case INITIAL_ACTION_CLEAR_REGION_CONTINUE:
case INITIAL_ACTION_CONTINUE: {
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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;
@@ -3346,14 +3460,65 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
}
}
- switch (is_depth_stencil ? p_final_depth_action : p_final_color_action) {
+ bool used_last = false;
+
+ {
+ int last_pass = p_passes.size() - 1;
+
+ if (is_depth) {
+ //likely missing depth resolve?
+ if (p_passes[last_pass].depth_attachment == i) {
+ used_last = true;
+ }
+ } else {
+ if (p_passes[last_pass].resolve_attachments.size()) {
+ //if using resolve attachments, check resolve attachments
+ for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) {
+ if (p_passes[last_pass].resolve_attachments[j] == i) {
+ used_last = true;
+ break;
+ }
+ }
+ } else {
+ for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) {
+ if (p_passes[last_pass].color_attachments[j] == i) {
+ used_last = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!used_last) {
+ for (int j = 0; j < p_passes[last_pass].preserve_attachments.size(); j++) {
+ if (p_passes[last_pass].preserve_attachments[j] == i) {
+ used_last = true;
+ break;
+ }
+ }
+ }
+ }
+
+ FinalAction final_action = p_final_action;
+ FinalAction final_depth_action = p_final_depth_action;
+
+ if (!used_last) {
+ if (is_depth) {
+ final_depth_action = FINAL_ACTION_DISCARD;
+
+ } else {
+ final_action = FINAL_ACTION_DISCARD;
+ }
+ }
+
+ switch (is_depth ? final_depth_action : final_action) {
case FINAL_ACTION_READ: {
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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);
@@ -3366,11 +3531,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
}
} break;
case FINAL_ACTION_DISCARD: {
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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);
@@ -3381,11 +3546,11 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
}
} break;
case FINAL_ACTION_CONTINUE: {
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ 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_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ } 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;
@@ -3401,89 +3566,289 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
}
}
+ attachment_last_pass[i] = -1;
+
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<LocalVector<uint32_t>> preserve_reference_array;
+ LocalVector<VkAttachmentReference> depth_reference_array;
+
+ subpasses.resize(p_passes.size());
+ color_reference_array.resize(p_passes.size());
+ input_reference_array.resize(p_passes.size());
+ resolve_reference_array.resize(p_passes.size());
+ preserve_reference_array.resize(p_passes.size());
+ depth_reference_array.resize(p_passes.size());
+
+ LocalVector<VkSubpassDependency> 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];
+
+ TextureSamples texture_samples = TEXTURE_SAMPLES_1;
+ bool is_multisample_first = true;
- VkAttachmentReference reference;
- reference.attachment = i;
+ for (int j = 0; j < pass->color_attachments.size(); j++) {
+ int32_t attachment = pass->color_attachments[j];
+ VkAttachmentReference reference;
+ if (attachment == FramebufferPass::ATTACHMENT_UNUSED) {
+ reference.attachment = VK_ATTACHMENT_UNUSED;
+ reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
+ } else {
+ ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), color attachment (" + itos(j) + ").");
+ ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), it's marked as depth, but it's not usable as color attachment.");
+ 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.");
- if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
- reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ if (is_multisample_first) {
+ texture_samples = p_attachments[attachment].samples;
+ is_multisample_first = false;
+ } 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.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attachment_last_pass[attachment] = i;
+ }
color_references.push_back(reference);
- } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
- reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
- depth_stencil_references.push_back(reference);
- } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) {
- reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ }
+
+ LocalVector<VkAttachmentReference> &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;
+ if (attachment == FramebufferPass::ATTACHMENT_UNUSED) {
+ reference.attachment = VK_ATTACHMENT_UNUSED;
+ reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
+ } else {
+ 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.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ attachment_last_pass[attachment] = i;
+ }
+ input_references.push_back(reference);
+ }
+
+ LocalVector<VkAttachmentReference> &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()) + ").");
+ ERR_FAIL_COND_V_MSG(texture_samples == TEXTURE_SAMPLES_1, VK_NULL_HANDLE, "Resolve attachments specified, but color attachments are not multisample.");
+ }
+ for (int j = 0; j < pass->resolve_attachments.size(); j++) {
+ int32_t attachment = pass->resolve_attachments[j];
+ VkAttachmentReference reference;
+ if (attachment == FramebufferPass::ATTACHMENT_UNUSED) {
+ reference.attachment = VK_ATTACHMENT_UNUSED;
+ reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
+ } else {
+ ERR_FAIL_INDEX_V_MSG(attachment, p_attachments.size(), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + ").");
+ ERR_FAIL_COND_V_MSG(pass->color_attachments[j] == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment (" + itos(j) + "), the respective color attachment is marked as unused.");
+ ERR_FAIL_COND_V_MSG(!(p_attachments[attachment].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT), VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), resolve attachment, it isn't marked as a color 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.");
+ 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;
+ attachment_last_pass[attachment] = i;
+ }
resolve_references.push_back(reference);
- // if resolves are done, we need to ensure the copy is safe
- dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
- dependency_to_external.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT;
+ }
+
+ VkAttachmentReference &depth_stencil_reference = depth_reference_array[i];
+
+ 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.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ attachment_last_pass[attachment] = i;
+
+ if (is_multisample_first) {
+ texture_samples = p_attachments[attachment].samples;
+ is_multisample_first = false;
+ } else {
+ ERR_FAIL_COND_V_MSG(texture_samples != p_attachments[attachment].samples, VK_NULL_HANDLE, "Invalid framebuffer depth 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 including the depth.");
+ }
+
+ } else {
+ depth_stencil_reference.attachment = VK_ATTACHMENT_UNUSED;
+ depth_stencil_reference.layout = VK_IMAGE_LAYOUT_UNDEFINED;
+ }
+
+ LocalVector<uint32_t> &preserve_references = preserve_reference_array[i];
+
+ for (int j = 0; j < pass->preserve_attachments.size(); j++) {
+ int32_t attachment = pass->preserve_attachments[j];
+
+ ERR_FAIL_COND_V_MSG(attachment == FramebufferPass::ATTACHMENT_UNUSED, VK_NULL_HANDLE, "Invalid framebuffer format attachment(" + itos(attachment) + "), in pass (" + itos(i) + "), preserve attachment (" + itos(j) + "). Preserve attachments can't be unused.");
+
+ 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
+ attachment_last_pass[attachment] = i;
+ preserve_references.push_back(attachment);
+ }
+ }
+
+ VkSubpassDescription &subpass = subpasses[i];
+ subpass.flags = 0;
+ subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpass.inputAttachmentCount = input_references.size();
+ if (input_references.size()) {
+ subpass.pInputAttachments = input_references.ptr();
+ } else {
+ subpass.pInputAttachments = nullptr;
+ }
+ subpass.colorAttachmentCount = color_references.size();
+ if (color_references.size()) {
+ subpass.pColorAttachments = color_references.ptr();
+ } else {
+ subpass.pColorAttachments = nullptr;
+ }
+ if (depth_stencil_reference.attachment != VK_ATTACHMENT_UNUSED) {
+ subpass.pDepthStencilAttachment = &depth_stencil_reference;
+ } else {
+ subpass.pDepthStencilAttachment = nullptr;
+ }
+
+ if (resolve_references.size()) {
+ subpass.pResolveAttachments = resolve_references.ptr();
} else {
- ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment.");
+ subpass.pResolveAttachments = nullptr;
}
+ subpass.preserveAttachmentCount = preserve_references.size();
+ if (preserve_references.size()) {
+ subpass.pPreserveAttachments = preserve_references.ptr();
+ } else {
+ subpass.pPreserveAttachments = nullptr;
+ }
+
+ if (r_samples) {
+ r_samples->push_back(texture_samples);
+ }
+
+ if (i > 0) {
+ VkSubpassDependency dependency;
+ dependency.srcSubpass = i - 1;
+ dependency.dstSubpass = i;
+ dependency.srcStageMask = 0;
+ dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+
+ 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;
+ subpass_dependencies.push_back(dependency);
+ }
+ /*
// NOTE: Big Mallet Approach -- any layout transition causes a full barrier
if (reference.layout != description.initialLayout) {
- // NOTE: this should be smarter based on the textures knowledge of it's 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 textures knowledge of it's 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;
}
+ */
}
- ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE,
- "Formats can only have one depth/stencil attachment, supplied (" + itos(depth_stencil_references.size()) + ").");
-
- ERR_FAIL_COND_V_MSG(resolve_references.size() > 1, VK_NULL_HANDLE,
- "Formats can only have one resolve attachment, supplied (" + itos(resolve_references.size()) + ").");
-
- VkSubpassDescription subpass;
- subpass.flags = 0;
- subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.inputAttachmentCount = 0; //unsupported for now
- subpass.pInputAttachments = nullptr;
- subpass.colorAttachmentCount = color_references.size();
- subpass.pColorAttachments = color_references.ptr();
- subpass.pDepthStencilAttachment = depth_stencil_references.ptr();
- subpass.pResolveAttachments = resolve_references.ptr();
- subpass.preserveAttachmentCount = 0;
- subpass.pPreserveAttachments = nullptr;
-
VkRenderPassCreateInfo render_pass_create_info;
render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
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 = 1;
- render_pass_create_info.pSubpasses = &subpass;
+ render_pass_create_info.subpassCount = subpasses.size();
+ render_pass_create_info.pSubpasses = subpasses.ptr();
// Commenting this because it seems it just avoids raster and compute to work at the same time.
// Other barriers seem to be protecting the render pass fine.
// render_pass_create_info.dependencyCount = 2;
// render_pass_create_info.pDependencies = dependencies;
- render_pass_create_info.dependencyCount = 0;
- render_pass_create_info.pDependencies = nullptr;
+ render_pass_create_info.dependencyCount = subpass_dependencies.size();
+ if (subpass_dependencies.size()) {
+ render_pass_create_info.pDependencies = subpass_dependencies.ptr();
+ } else {
+ 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;
+ Vector<uint32_t> view_masks;
+ VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info;
+
+ if (p_view_count > 1) {
+ const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities();
+
+ // For now this only works with multiview!
+ ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported");
+
+ // Make sure we limit this to the number of views we support.
+ ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass");
+
+ // 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;
+ render_pass_multiview_create_info.subpassCount = subpasses.size();
+ render_pass_multiview_create_info.pViewMasks = view_masks.ptr();
+ render_pass_multiview_create_info.dependencyCount = 0;
+ render_pass_multiview_create_info.pViewOffsets = nullptr;
+ render_pass_multiview_create_info.correlationMaskCount = 1;
+ render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask;
+
+ render_pass_create_info.pNext = &render_pass_multiview_create_info;
+ }
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) + ".");
- if (r_color_attachment_count) {
- *r_color_attachment_count = color_references.size();
- }
return render_pass;
}
-RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) {
+RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) {
+ FramebufferPass pass;
+ for (int i = 0; i < p_format.size(); i++) {
+ if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ pass.depth_attachment = i;
+ } else {
+ pass.color_attachments.push_back(i);
+ }
+ }
+
+ Vector<FramebufferPass> passes;
+ 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) {
_THREAD_SAFE_METHOD_
FramebufferFormatKey key;
- key.attachments = p_format;
+ key.attachments = p_attachments;
+ key.passes = p_passes;
+ key.view_count = p_view_count;
const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key);
if (E) {
@@ -3491,8 +3856,8 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
return E->get();
}
- int color_references;
- VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case
+ 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
if (render_pass == VK_NULL_HANDLE) { //was likely invalid
return INVALID_ID;
@@ -3502,15 +3867,16 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
E = framebuffer_format_cache.insert(key, id);
FramebufferFormat fb_format;
fb_format.E = E;
- fb_format.color_attachments = color_references;
fb_format.render_pass = render_pass;
- fb_format.samples = p_format[0].samples;
+ fb_format.pass_samples = samples;
+ fb_format.view_count = p_view_count;
framebuffer_formats[id] = fb_format;
return id;
}
RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(TextureSamples p_samples) {
FramebufferFormatKey key;
+ key.passes.push_back(FramebufferPass());
const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key);
if (E) {
@@ -3544,7 +3910,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
VkRenderPass render_pass;
VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass);
- ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass for empty fb failed with error " + itos(res) + ".");
+ ERR_FAIL_COND_V_MSG(res, 0, "vkCreateRenderPass for empty fb failed with error " + itos(res) + ".");
if (render_pass == VK_NULL_HANDLE) { //was likely invalid
return INVALID_ID;
@@ -3556,18 +3922,18 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
FramebufferFormat fb_format;
fb_format.E = E;
- fb_format.color_attachments = 0;
fb_format.render_pass = render_pass;
- fb_format.samples = p_samples;
+ fb_format.pass_samples.push_back(p_samples);
framebuffer_formats[id] = fb_format;
return id;
}
-RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) {
+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);
ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1);
+ ERR_FAIL_COND_V(p_pass >= uint32_t(E->get().pass_samples.size()), TEXTURE_SAMPLES_1);
- return E->get().samples;
+ return E->get().pass_samples[p_pass];
}
/***********************/
@@ -3580,20 +3946,47 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur
framebuffer.format_id = framebuffer_format_create_empty(p_samples);
ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID());
framebuffer.size = p_size;
+ framebuffer.view_count = 1;
return framebuffer_owner.make_rid(framebuffer);
}
-RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) {
+RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) {
+ _THREAD_SAFE_METHOD_
+
+ FramebufferPass pass;
+
+ 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");
+
+ if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
+ pass.depth_attachment = i;
+ } else {
+ pass.color_attachments.push_back(i);
+ }
+ }
+
+ Vector<FramebufferPass> passes;
+ passes.push_back(pass);
+
+ 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) {
_THREAD_SAFE_METHOD_
Vector<AttachmentFormat> attachments;
Size2i size;
for (int i = 0; i < p_texture_attachments.size(); i++) {
- Texture *texture = texture_owner.getornull(p_texture_attachments[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");
+
if (i == 0) {
size.width = texture->width;
size.height = texture->height;
@@ -3609,7 +4002,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
attachments.push_back(af);
}
- FramebufferFormatID format_id = framebuffer_format_create(attachments);
+ FramebufferFormatID format_id = framebuffer_format_create_multipass(attachments, p_passes, p_view_count);
if (format_id == INVALID_ID) {
return RID();
}
@@ -3621,6 +4014,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
framebuffer.format_id = format_id;
framebuffer.texture_ids = p_texture_attachments;
framebuffer.size = size;
+ framebuffer.view_count = p_view_count;
RID id = framebuffer_owner.make_rid(framebuffer);
@@ -3634,7 +4028,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_get_format(RID p_framebuffer) {
_THREAD_SAFE_METHOD_
- Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer);
+ Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer);
ERR_FAIL_COND_V(!framebuffer, INVALID_ID);
return framebuffer->format_id;
@@ -3784,7 +4178,7 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo
vertex_array.description = p_vertex_format;
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.getornull(p_src_buffers[i]);
+ Buffer *buffer = vertex_buffer_owner.get_or_null(p_src_buffers[i]);
//validate with buffer
{
@@ -3880,7 +4274,7 @@ RID RenderingDeviceVulkan::index_array_create(RID p_index_buffer, uint32_t p_ind
ERR_FAIL_COND_V(!index_buffer_owner.owns(p_index_buffer), RID());
- IndexBuffer *index_buffer = index_buffer_owner.getornull(p_index_buffer);
+ IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_index_buffer);
ERR_FAIL_COND_V(p_index_count == 0, RID());
ERR_FAIL_COND_V(p_index_offset + p_index_count > index_buffer->index_count, RID());
@@ -3924,7 +4318,7 @@ static VkShaderStageFlagBits shader_stage_masks[RenderingDevice::SHADER_STAGE_MA
String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) {
String ret;
- const Shader *shader = shader_owner.getornull(p_shader);
+ const Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, String());
for (int i = 0; i < shader->sets.size(); i++) {
if (p_set >= 0 && i != p_set) {
@@ -3932,7 +4326,7 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) {
}
for (int j = 0; j < shader->sets[i].uniform_info.size(); j++) {
const UniformInfo &ui = shader->sets[i].uniform_info[j];
- if (ret != String()) {
+ if (!ret.is_empty()) {
ret += "\n";
}
ret += "Set: " + itos(i) + " Binding: " + itos(ui.binding) + " Type: " + shader_uniform_names[ui.type] + " Length: " + itos(ui.length);
@@ -4133,51 +4527,91 @@ bool RenderingDeviceVulkan::_uniform_add_binding(Vector<Vector<VkDescriptorSetLa
}
#endif
-RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages) {
- //descriptor layouts
- Vector<Vector<VkDescriptorSetLayoutBinding>> set_bindings;
- Vector<Vector<UniformInfo>> uniform_info;
- Shader::PushConstant push_constant;
- push_constant.push_constant_size = 0;
- push_constant.push_constants_vk_stage = 0;
+//version 1: initial
+//version 2: Added shader name
- uint32_t vertex_input_mask = 0;
+#define SHADER_BINARY_VERSION 2
- uint32_t fragment_outputs = 0;
+String RenderingDeviceVulkan::shader_get_binary_cache_key() const {
+ return "Vulkan-SV" + itos(SHADER_BINARY_VERSION);
+}
- uint32_t stages_processed = 0;
+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)
+};
- bool is_compute = false;
+struct RenderingDeviceVulkanShaderBinarySpecializationConstant {
+ uint32_t type;
+ uint32_t constant_id;
+ union {
+ uint32_t int_value;
+ float float_value;
+ bool bool_value;
+ };
+ uint32_t stage_flags;
+};
+
+struct RenderingDeviceVulkanShaderBinaryData {
+ uint32_t vertex_input_mask;
+ uint32_t fragment_outputs;
+ uint32_t specialization_constant_count;
+ uint32_t is_compute;
+ uint32_t compute_local_size[3];
+ uint32_t set_count;
+ uint32_t push_constant_size;
+ uint32_t push_constants_vk_stage;
+ uint32_t stage_count;
+ uint32_t shader_name_len;
+};
- uint32_t compute_local_size[3] = { 0, 0, 0 };
+Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name) {
+ RenderingDeviceVulkanShaderBinaryData binary_data;
+ binary_data.vertex_input_mask = 0;
+ binary_data.fragment_outputs = 0;
+ binary_data.specialization_constant_count = 0;
+ binary_data.is_compute = 0;
+ binary_data.compute_local_size[0] = 0;
+ binary_data.compute_local_size[1] = 0;
+ binary_data.compute_local_size[2] = 0;
+ binary_data.set_count = 0;
+ binary_data.push_constant_size = 0;
+ binary_data.push_constants_vk_stage = 0;
+
+ Vector<Vector<RenderingDeviceVulkanShaderBinaryDataBinding>> uniform_info; //set bindings
+ Vector<RenderingDeviceVulkanShaderBinarySpecializationConstant> specialization_constants;
- for (int i = 0; i < p_stages.size(); i++) {
- if (p_stages[i].shader_stage == SHADER_STAGE_COMPUTE) {
- is_compute = true;
- ERR_FAIL_COND_V_MSG(p_stages.size() != 1, RID(),
+ uint32_t stages_processed = 0;
+
+ for (int i = 0; i < p_spirv.size(); i++) {
+ if (p_spirv[i].shader_stage == SHADER_STAGE_COMPUTE) {
+ binary_data.is_compute = true;
+ ERR_FAIL_COND_V_MSG(p_spirv.size() != 1, Vector<uint8_t>(),
"Compute shaders can only receive one stage, dedicated to compute.");
}
- ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_stages[i].shader_stage), RID(),
- "Stage " + String(shader_stage_names[p_stages[i].shader_stage]) + " submitted more than once.");
+ ERR_FAIL_COND_V_MSG(stages_processed & (1 << p_spirv[i].shader_stage), Vector<uint8_t>(),
+ "Stage " + String(shader_stage_names[p_spirv[i].shader_stage]) + " submitted more than once.");
{
SpvReflectShaderModule module;
- const uint8_t *spirv = p_stages[i].spir_v.ptr();
- SpvReflectResult result = spvReflectCreateShaderModule(p_stages[i].spir_v.size(), spirv, &module);
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed parsing shader.");
-
- if (is_compute) {
- compute_local_size[0] = module.entry_points->local_size.x;
- compute_local_size[1] = module.entry_points->local_size.y;
- compute_local_size[2] = module.entry_points->local_size.z;
+ const uint8_t *spirv = p_spirv[i].spir_v.ptr();
+ SpvReflectResult result = spvReflectCreateShaderModule(p_spirv[i].spir_v.size(), spirv, &module);
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed parsing shader.");
+
+ if (binary_data.is_compute) {
+ binary_data.compute_local_size[0] = module.entry_points->local_size.x;
+ binary_data.compute_local_size[1] = module.entry_points->local_size.y;
+ binary_data.compute_local_size[2] = module.entry_points->local_size.z;
}
uint32_t binding_count = 0;
result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, nullptr);
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating descriptor bindings.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating descriptor bindings.");
- uint32_t stage = p_stages[i].shader_stage;
+ uint32_t stage = p_spirv[i].shader_stage;
if (binding_count > 0) {
//Parse bindings
@@ -4186,56 +4620,47 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
bindings.resize(binding_count);
result = spvReflectEnumerateDescriptorBindings(&module, &binding_count, bindings.ptrw());
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed getting descriptor bindings.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed getting descriptor bindings.");
for (uint32_t j = 0; j < binding_count; j++) {
const SpvReflectDescriptorBinding &binding = *bindings[j];
- VkDescriptorSetLayoutBinding layout_binding;
- UniformInfo info;
+ RenderingDeviceVulkanShaderBinaryDataBinding info;
bool need_array_dimensions = false;
bool need_block_size = false;
switch (binding.descriptor_type) {
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
info.type = UNIFORM_TYPE_SAMPLER;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
info.type = UNIFORM_TYPE_SAMPLER_WITH_TEXTURE;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
info.type = UNIFORM_TYPE_TEXTURE;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
info.type = UNIFORM_TYPE_IMAGE;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
info.type = UNIFORM_TYPE_TEXTURE_BUFFER;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
info.type = UNIFORM_TYPE_IMAGE_BUFFER;
need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
info.type = UNIFORM_TYPE_UNIFORM_BUFFER;
need_block_size = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
info.type = UNIFORM_TYPE_STORAGE_BUFFER;
need_block_size = true;
} break;
@@ -4248,8 +4673,8 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
continue;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: {
- layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
info.type = UNIFORM_TYPE_INPUT_ATTACHMENT;
+ need_array_dimensions = true;
} break;
case SPV_REFLECT_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: {
ERR_PRINT("Acceleration structure not supported.");
@@ -4270,42 +4695,35 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
}
}
- layout_binding.descriptorCount = info.length;
-
} else if (need_block_size) {
info.length = binding.block.size;
- layout_binding.descriptorCount = 1;
} else {
info.length = 0;
- layout_binding.descriptorCount = 1;
}
info.binding = binding.binding;
uint32_t set = binding.set;
- //print_line("Stage: " + String(shader_stage_names[stage]) + " set=" + itos(set) + " binding=" + itos(info.binding) + " type=" + shader_uniform_names[info.type] + " length=" + itos(info.length));
-
- ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, RID(),
+ ERR_FAIL_COND_V_MSG(set >= MAX_UNIFORM_SETS, Vector<uint8_t>(),
"On shader stage '" + String(shader_stage_names[stage]) + "', uniform '" + binding.name + "' uses a set (" + itos(set) + ") index larger than what is supported (" + itos(MAX_UNIFORM_SETS) + ").");
- ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, RID(),
+ ERR_FAIL_COND_V_MSG(set >= limits.maxBoundDescriptorSets, Vector<uint8_t>(),
"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)set_bindings.size()) {
+ if (set < (uint32_t)uniform_info.size()) {
//check if this already exists
bool exists = false;
- for (int k = 0; k < set_bindings[set].size(); k++) {
- if (set_bindings[set][k].binding == (uint32_t)info.binding) {
+ 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
- ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorType != layout_binding.descriptorType, RID(),
+ 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
- ERR_FAIL_COND_V_MSG(set_bindings[set][k].descriptorCount != layout_binding.descriptorCount || uniform_info[set][k].length != info.length, RID(),
+ 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
- set_bindings.write[set].write[k].stageFlags |= shader_stage_masks[stage];
uniform_info.write[set].write[k].stages |= 1 << stage;
exists = true;
}
@@ -4316,40 +4734,90 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
}
}
- layout_binding.binding = info.binding;
- layout_binding.stageFlags = shader_stage_masks[stage];
- layout_binding.pImmutableSamplers = nullptr; //no support for this yet
-
info.stages = 1 << stage;
- info.binding = info.binding;
- if (set >= (uint32_t)set_bindings.size()) {
- set_bindings.resize(set + 1);
+ if (set >= (uint32_t)uniform_info.size()) {
uniform_info.resize(set + 1);
}
- set_bindings.write[set].push_back(layout_binding);
uniform_info.write[set].push_back(info);
}
}
+ {
+ //specialization constants
+
+ uint32_t sc_count = 0;
+ result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, nullptr);
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating specialization constants.");
+
+ if (sc_count) {
+ Vector<SpvReflectSpecializationConstant *> spec_constants;
+ spec_constants.resize(sc_count);
+
+ result = spvReflectEnumerateSpecializationConstants(&module, &sc_count, spec_constants.ptrw());
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining specialization constants.");
+
+ for (uint32_t j = 0; j < sc_count; j++) {
+ int32_t existing = -1;
+ RenderingDeviceVulkanShaderBinarySpecializationConstant sconst;
+ 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 = spc->default_value.int_bool_value != 0;
+ } break;
+ case SPV_REFLECT_SPECIALIZATION_CONSTANT_INT: {
+ sconst.type = PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
+ 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 = spc->default_value.float_value;
+ } break;
+ }
+ sconst.stage_flags = 1 << p_spirv[i].shader_stage;
+
+ for (int k = 0; k < specialization_constants.size(); k++) {
+ if (specialization_constants[k].constant_id == sconst.constant_id) {
+ ERR_FAIL_COND_V_MSG(specialization_constants[k].type != sconst.type, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their types differ.");
+ ERR_FAIL_COND_V_MSG(specialization_constants[k].int_value != sconst.int_value, Vector<uint8_t>(), "More than one specialization constant used for id (" + itos(sconst.constant_id) + "), but their default values differ.");
+ existing = k;
+ break;
+ }
+ }
+
+ if (existing > 0) {
+ specialization_constants.write[existing].stage_flags |= sconst.stage_flags;
+ } else {
+ specialization_constants.push_back(sconst);
+ }
+ }
+ }
+ }
+
if (stage == SHADER_STAGE_VERTEX) {
uint32_t iv_count = 0;
result = spvReflectEnumerateInputVariables(&module, &iv_count, nullptr);
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating input variables.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating input variables.");
if (iv_count) {
Vector<SpvReflectInterfaceVariable *> input_vars;
input_vars.resize(iv_count);
result = spvReflectEnumerateInputVariables(&module, &iv_count, input_vars.ptrw());
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining input variables.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "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
- vertex_input_mask |= (1 << uint32_t(input_vars[j]->location));
+ binary_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location));
}
}
}
@@ -4358,20 +4826,21 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
if (stage == SHADER_STAGE_FRAGMENT) {
uint32_t ov_count = 0;
result = spvReflectEnumerateOutputVariables(&module, &ov_count, nullptr);
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating output variables.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating output variables.");
if (ov_count) {
Vector<SpvReflectInterfaceVariable *> output_vars;
output_vars.resize(ov_count);
result = spvReflectEnumerateOutputVariables(&module, &ov_count, output_vars.ptrw());
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining output variables.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed obtaining output variables.");
for (uint32_t j = 0; j < ov_count; j++) {
- if (output_vars[j]) {
- fragment_outputs = MAX(fragment_outputs, output_vars[j]->location + 1);
+ const SpvReflectInterfaceVariable *refvar = output_vars[j];
+ if (refvar != nullptr && refvar->built_in != SpvBuiltInFragDepth) {
+ binary_data.fragment_outputs |= 1 << refvar->location;
}
}
}
@@ -4379,18 +4848,18 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
uint32_t pc_count = 0;
result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, nullptr);
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed enumerating push constants.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "' failed enumerating push constants.");
if (pc_count) {
- ERR_FAIL_COND_V_MSG(pc_count > 1, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages.");
+ ERR_FAIL_COND_V_MSG(pc_count > 1, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Only one push constant is supported, which should be the same across shader stages.");
Vector<SpvReflectBlockVariable *> pconstants;
pconstants.resize(pc_count);
result = spvReflectEnumeratePushConstantBlocks(&module, &pc_count, pconstants.ptrw());
- ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "' failed obtaining push constants.");
+ ERR_FAIL_COND_V_MSG(result != SPV_REFLECT_RESULT_SUCCESS, Vector<uint8_t>(),
+ "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);
@@ -4399,11 +4868,11 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
}
#endif
- ERR_FAIL_COND_V_MSG(push_constant.push_constant_size && push_constant.push_constant_size != pconstants[0]->size, RID(),
- "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_stages[i].shader_stage]) + "': Push constant block must be the same across shader stages.");
+ ERR_FAIL_COND_V_MSG(binary_data.push_constant_size && binary_data.push_constant_size != pconstants[0]->size, Vector<uint8_t>(),
+ "Reflection of SPIR-V shader stage '" + String(shader_stage_names[p_spirv[i].shader_stage]) + "': Push constant block must be the same across shader stages.");
- push_constant.push_constant_size = pconstants[0]->size;
- push_constant.push_constants_vk_stage |= shader_stage_masks[stage];
+ binary_data.push_constant_size = pconstants[0]->size;
+ binary_data.push_constants_vk_stage |= shader_stage_masks[stage];
//print_line("Stage: " + String(shader_stage_names[stage]) + " push constant of size=" + itos(push_constant.push_constant_size));
}
@@ -4412,9 +4881,317 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
spvReflectDestroyShaderModule(&module);
}
- stages_processed |= (1 << p_stages[i].shader_stage);
+ stages_processed |= (1 << p_spirv[i].shader_stage);
+ }
+
+ Vector<Vector<uint8_t>> compressed_stages;
+ Vector<uint32_t> smolv_size;
+ Vector<uint32_t> zstd_size; //if 0, stdno t used
+
+ uint32_t stages_binary_size = 0;
+
+ bool strip_debug = false;
+
+ for (int i = 0; i < p_spirv.size(); i++) {
+ smolv::ByteArray smolv;
+ if (!smolv::Encode(p_spirv[i].spir_v.ptr(), p_spirv[i].spir_v.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) {
+ 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
+ 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);
+
+ if (dst_size > 0 && (uint32_t)dst_size < smolv.size()) {
+ zstd_size.push_back(dst_size);
+ zstd.resize(dst_size);
+ compressed_stages.push_back(zstd);
+ } else {
+ Vector<uint8_t> smv;
+ smv.resize(smolv.size());
+ memcpy(smv.ptrw(), &smolv[0], smolv.size());
+ zstd_size.push_back(0); //not using zstd
+ compressed_stages.push_back(smv);
+ }
+ }
+ }
+ uint32_t s = compressed_stages[i].size();
+ if (s % 4 != 0) {
+ s += 4 - (s % 4);
+ }
+ stages_binary_size += s;
+ }
+
+ binary_data.specialization_constant_count = specialization_constants.size();
+ binary_data.set_count = uniform_info.size();
+ binary_data.stage_count = p_spirv.size();
+
+ CharString shader_name_utf = p_shader_name.utf8();
+
+ binary_data.shader_name_len = shader_name_utf.length();
+
+ 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
+ total_size += 4 - (binary_data.shader_name_len % 4);
}
+ for (int i = 0; i < uniform_info.size(); i++) {
+ total_size += sizeof(uint32_t);
+ total_size += uniform_info[i].size() * sizeof(RenderingDeviceVulkanShaderBinaryDataBinding);
+ }
+
+ total_size += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size();
+
+ 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;
+ {
+ uint8_t *binptr = ret.ptrw();
+ binptr[0] = 'G';
+ binptr[1] = 'V';
+ binptr[2] = 'B';
+ binptr[3] = 'D'; //godot vulkan binary data
+ offset += 4;
+ encode_uint32(SHADER_BINARY_VERSION, binptr + offset);
+ offset += sizeof(uint32_t);
+ encode_uint32(sizeof(RenderingDeviceVulkanShaderBinaryData), binptr + offset);
+ offset += sizeof(uint32_t);
+ memcpy(binptr + offset, &binary_data, sizeof(RenderingDeviceVulkanShaderBinaryData));
+ offset += sizeof(RenderingDeviceVulkanShaderBinaryData);
+ 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
+ offset += 4 - (binary_data.shader_name_len % 4);
+ }
+
+ for (int i = 0; i < uniform_info.size(); i++) {
+ int count = uniform_info[i].size();
+ encode_uint32(count, binptr + offset);
+ offset += sizeof(uint32_t);
+ if (count > 0) {
+ memcpy(binptr + offset, uniform_info[i].ptr(), sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count);
+ offset += sizeof(RenderingDeviceVulkanShaderBinaryDataBinding) * count;
+ }
+ }
+
+ if (specialization_constants.size()) {
+ memcpy(binptr + offset, specialization_constants.ptr(), sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size());
+ offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) * specialization_constants.size();
+ }
+
+ for (int i = 0; i < compressed_stages.size(); i++) {
+ encode_uint32(p_spirv[i].shader_stage, binptr + offset);
+ offset += sizeof(uint32_t);
+ encode_uint32(smolv_size[i], binptr + offset);
+ offset += sizeof(uint32_t);
+ encode_uint32(zstd_size[i], binptr + offset);
+ offset += sizeof(uint32_t);
+ memcpy(binptr + offset, compressed_stages[i].ptr(), compressed_stages[i].size());
+
+ uint32_t s = compressed_stages[i].size();
+
+ if (s % 4 != 0) {
+ s += 4 - (s % 4);
+ }
+
+ offset += s;
+ }
+
+ ERR_FAIL_COND_V(offset != (uint32_t)ret.size(), Vector<uint8_t>());
+ }
+
+ return ret;
+}
+
+RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary) {
+ const uint8_t *binptr = p_shader_binary.ptr();
+ uint32_t binsize = p_shader_binary.size();
+
+ uint32_t read_offset = 0;
+ //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());
+
+ uint32_t bin_data_size = decode_uint32(binptr + 8);
+
+ const RenderingDeviceVulkanShaderBinaryData &binary_data = *(const RenderingDeviceVulkanShaderBinaryData *)(binptr + 12);
+
+ Shader::PushConstant push_constant;
+ push_constant.push_constant_size = binary_data.push_constant_size;
+ push_constant.push_constants_vk_stage = binary_data.push_constants_vk_stage;
+
+ uint32_t vertex_input_mask = binary_data.vertex_input_mask;
+
+ uint32_t fragment_outputs = binary_data.fragment_outputs;
+
+ 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] };
+
+ read_offset += sizeof(uint32_t) * 3 + bin_data_size;
+
+ String name;
+
+ 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
+ read_offset += 4 - (binary_data.shader_name_len % 4);
+ }
+ }
+
+ Vector<Vector<VkDescriptorSetLayoutBinding>> set_bindings;
+ Vector<Vector<UniformInfo>> uniform_info;
+
+ set_bindings.resize(binary_data.set_count);
+ uniform_info.resize(binary_data.set_count);
+
+ for (uint32_t i = 0; i < binary_data.set_count; i++) {
+ 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);
+ 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.length = set_ptr[j].length;
+ info.binding = set_ptr[j].binding;
+ info.stages = set_ptr[j].stages;
+
+ VkDescriptorSetLayoutBinding layout_binding;
+ layout_binding.pImmutableSamplers = nullptr;
+ layout_binding.binding = set_ptr[j].binding;
+ layout_binding.descriptorCount = 1;
+ layout_binding.stageFlags = 0;
+ for (uint32_t k = 0; k < SHADER_STAGE_MAX; k++) {
+ if (set_ptr[j].stages & (1 << k)) {
+ layout_binding.stageFlags |= shader_stage_masks[k];
+ }
+ }
+
+ switch (info.type) {
+ case UNIFORM_TYPE_SAMPLER: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
+ layout_binding.descriptorCount = set_ptr[j].length;
+ } break;
+ case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ layout_binding.descriptorCount = set_ptr[j].length;
+ } break;
+ case UNIFORM_TYPE_TEXTURE: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ layout_binding.descriptorCount = set_ptr[j].length;
+ } break;
+ case UNIFORM_TYPE_IMAGE: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+ layout_binding.descriptorCount = set_ptr[j].length;
+ } break;
+ case UNIFORM_TYPE_TEXTURE_BUFFER: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+ layout_binding.descriptorCount = set_ptr[j].length;
+ } break;
+ case UNIFORM_TYPE_IMAGE_BUFFER: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+ } break;
+ case UNIFORM_TYPE_UNIFORM_BUFFER: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ } break;
+ case UNIFORM_TYPE_STORAGE_BUFFER: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ } break;
+ case UNIFORM_TYPE_INPUT_ATTACHMENT: {
+ layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
+ } break;
+ default: {
+ ERR_FAIL_V(RID());
+ }
+ }
+
+ set_bindings.write[i].push_back(layout_binding);
+ uniform_info.write[i].push_back(info);
+ }
+
+ read_offset += set_size;
+ }
+
+ ERR_FAIL_COND_V(read_offset + binary_data.specialization_constant_count * sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant) >= binsize, RID());
+
+ 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);
+ Shader::SpecializationConstant sc;
+ sc.constant.int_value = src_sc.int_value;
+ sc.constant.type = PipelineSpecializationConstantType(src_sc.type);
+ sc.constant.constant_id = src_sc.constant_id;
+ sc.stage_flags = src_sc.stage_flags;
+ specialization_constants.push_back(sc);
+
+ read_offset += sizeof(RenderingDeviceVulkanShaderBinarySpecializationConstant);
+ }
+
+ Vector<Vector<uint8_t>> stage_spirv_data;
+ Vector<ShaderStage> stage_type;
+
+ for (uint32_t i = 0; i < binary_data.stage_count; i++) {
+ ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, RID());
+ uint32_t stage = decode_uint32(binptr + read_offset);
+ read_offset += sizeof(uint32_t);
+ uint32_t smolv_size = decode_uint32(binptr + read_offset);
+ read_offset += sizeof(uint32_t);
+ uint32_t zstd_size = decode_uint32(binptr + read_offset);
+ read_offset += sizeof(uint32_t);
+
+ uint32_t buf_size = (zstd_size > 0) ? zstd_size : smolv_size;
+
+ Vector<uint8_t> smolv;
+ const uint8_t *src_smolv = nullptr;
+
+ if (zstd_size > 0) {
+ //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());
+ src_smolv = smolv.ptr();
+ } else {
+ src_smolv = binptr + read_offset;
+ }
+
+ Vector<uint8_t> spirv;
+ uint32_t spirv_size = smolv::GetDecodedBufferSize(src_smolv, smolv_size);
+ spirv.resize(spirv_size);
+ if (!smolv::Decode(src_smolv, smolv_size, spirv.ptrw(), spirv_size)) {
+ ERR_FAIL_V_MSG(RID(), "Malformed smolv input uncompressing shader stage:" + String(shader_stage_names[stage]));
+ }
+ stage_spirv_data.push_back(spirv);
+ stage_type.push_back(ShaderStage(stage));
+
+ if (buf_size % 4 != 0) {
+ buf_size += 4 - (buf_size % 4);
+ }
+
+ ERR_FAIL_COND_V(read_offset + buf_size > binsize, RID());
+
+ read_offset += buf_size;
+ }
+
+ ERR_FAIL_COND_V(read_offset != binsize, RID());
+
//all good, let's create modules
_THREAD_SAFE_METHOD_
@@ -4422,23 +5199,25 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
Shader shader;
shader.vertex_input_mask = vertex_input_mask;
- shader.fragment_outputs = fragment_outputs;
+ shader.fragment_output_mask = fragment_outputs;
shader.push_constant = push_constant;
shader.is_compute = is_compute;
shader.compute_local_size[0] = compute_local_size[0];
shader.compute_local_size[1] = compute_local_size[1];
shader.compute_local_size[2] = compute_local_size[2];
+ shader.specialization_constants = specialization_constants;
+ shader.name = name;
String error_text;
bool success = true;
- for (int i = 0; i < p_stages.size(); i++) {
+ for (int i = 0; i < stage_spirv_data.size(); i++) {
VkShaderModuleCreateInfo shader_module_create_info;
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_module_create_info.pNext = nullptr;
shader_module_create_info.flags = 0;
- shader_module_create_info.codeSize = p_stages[i].spir_v.size();
- const uint8_t *r = p_stages[i].spir_v.ptr();
+ shader_module_create_info.codeSize = stage_spirv_data[i].size();
+ const uint8_t *r = stage_spirv_data[i].ptr();
shader_module_create_info.pCode = (const uint32_t *)r;
@@ -4446,7 +5225,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
VkResult res = vkCreateShaderModule(device, &shader_module_create_info, nullptr, &module);
if (res) {
success = false;
- error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(shader_stage_names[p_stages[i].shader_stage]);
+ error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(shader_stage_names[stage_type[i]]);
break;
}
@@ -4462,7 +5241,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stage.pNext = nullptr;
shader_stage.flags = 0;
- shader_stage.stage = shader_stage_bits[p_stages[i].shader_stage];
+ shader_stage.stage = shader_stage_bits[stage_type[i]];
shader_stage.module = module;
shader_stage.pName = "main";
shader_stage.pSpecializationInfo = nullptr;
@@ -4573,7 +5352,7 @@ RID RenderingDeviceVulkan::shader_create(const Vector<ShaderStageData> &p_stages
uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
_THREAD_SAFE_METHOD_
- const Shader *shader = shader_owner.getornull(p_shader);
+ const Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, 0);
return shader->vertex_input_mask;
}
@@ -4795,7 +5574,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
ERR_FAIL_COND_V(p_uniforms.size() == 0, RID());
- Shader *shader = shader_owner.getornull(p_shader);
+ Shader *shader = shader_owner.get_or_null(p_shader);
ERR_FAIL_COND_V(!shader, RID());
ERR_FAIL_COND_V_MSG(p_shader_set >= (uint32_t)shader->sets.size() || shader->sets[p_shader_set].uniform_info.size() == 0, RID(),
@@ -4853,18 +5632,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.getornull(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;
@@ -4876,31 +5655,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.getornull(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.getornull(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(),
@@ -4910,10 +5689,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
img_info.sampler = *sampler;
img_info.imageView = texture->view;
- if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) {
+ 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);
}
@@ -4922,7 +5701,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
mutable_sampled_textures.push_back(texture);
}
if (texture->owner.is_valid()) {
- texture = texture_owner.getornull(texture->owner);
+ texture = texture_owner.get_or_null(texture->owner);
ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen
}
@@ -4932,28 +5711,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.getornull(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(),
@@ -4963,10 +5742,10 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
img_info.sampler = VK_NULL_HANDLE;
img_info.imageView = texture->view;
- if (texture->usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT)) {
+ 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);
}
@@ -4976,7 +5755,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
}
if (texture->owner.is_valid()) {
- texture = texture_owner.getornull(texture->owner);
+ texture = texture_owner.get_or_null(texture->owner);
ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen
}
@@ -4986,27 +5765,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.getornull(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.");
@@ -5024,7 +5803,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
}
if (texture->owner.is_valid()) {
- texture = texture_owner.getornull(texture->owner);
+ texture = texture_owner.get_or_null(texture->owner);
ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen
}
@@ -5034,29 +5813,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.getornull(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);
@@ -5064,21 +5843,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()) + ").");
}
}
@@ -5086,11 +5865,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.getornull(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.getornull(uniform.ids[j + 1]);
+ TextureBuffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1));
VkDescriptorImageInfo img_info;
img_info.sampler = *sampler;
@@ -5106,23 +5885,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
} 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.getornull(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(),
@@ -5137,21 +5916,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.getornull(uniform.ids[0]);
- } else if (vertex_buffer_owner.owns(uniform.ids[0])) {
- buffer = vertex_buffer_owner.getornull(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 its 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) + ").");
@@ -5163,6 +5942,49 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
write.pTexelBufferView = nullptr;
} break;
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.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.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.get_id_count()) + ").");
+ }
+ }
+
+ Vector<VkDescriptorImageInfo> image_info;
+
+ 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.");
+
+ ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(),
+ "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_SAMPLING_BIT usage flag set in order to be used as uniform.");
+
+ VkDescriptorImageInfo img_info;
+ img_info.sampler = VK_NULL_HANDLE;
+ img_info.imageView = texture->view;
+
+ if (texture->owner.is_valid()) {
+ texture = texture_owner.get_or_null(texture->owner);
+ ERR_FAIL_COND_V(!texture, RID()); //bug, should never happen
+ }
+
+ img_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+ image_info.push_back(img_info);
+ }
+
+ write.dstArrayElement = 0;
+ 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.get_id_count();
} break;
default: {
}
@@ -5212,10 +6034,9 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
_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));
}
}
@@ -5234,6 +6055,13 @@ 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) {
+ UniformSet *us = uniform_set_owner.get_or_null(p_uniform_set);
+ ERR_FAIL_COND(!us);
+ us->invalidated_callback = p_callback;
+ us->invalidated_callback_userdata = p_userdata;
+}
+
Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier) {
_THREAD_SAFE_METHOD_
@@ -5336,7 +6164,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_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.");
}
- // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below.
+ // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below.
_buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, src_access_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, false);
VkCommandBuffer command_buffer = frames[frame].setup_command_buffer;
@@ -5359,7 +6187,7 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) {
{
buffer_data.resize(buffer->size);
uint8_t *w = buffer_data.ptrw();
- copymem(w, buffer_mem, buffer->size);
+ memcpy(w, buffer_mem, buffer->size);
}
vmaUnmapMemory(allocator, tmp_buffer.allocation);
@@ -5373,11 +6201,11 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) {
/**** RENDER PIPELINE ****/
/*************************/
-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) {
+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
- Shader *shader = shader_owner.getornull(p_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(),
@@ -5392,8 +6220,16 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
{ //validate shader vs framebuffer
- ERR_FAIL_COND_V_MSG(shader->fragment_outputs != fb_format.color_attachments, RID(),
- "Mismatch fragment output bindings (" + itos(shader->fragment_outputs) + ") and framebuffer color buffers (" + itos(fb_format.color_attachments) + ") when binding both in render pipeline.");
+ 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];
+ uint32_t output_mask = 0;
+ for (int i = 0; i < pass.color_attachments.size(); i++) {
+ if (pass.color_attachments[i] != FramebufferPass::ATTACHMENT_UNUSED) {
+ output_mask |= 1 << i;
+ }
+ }
+ 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
VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info;
@@ -5465,7 +6301,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
tessellation_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessellation_create_info.pNext = nullptr;
tessellation_create_info.flags = 0;
- ERR_FAIL_COND_V(p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize, RID());
+ ERR_FAIL_COND_V(limits.maxTessellationPatchSize > 0 && (p_rasterization_state.patch_control_points < 1 || p_rasterization_state.patch_control_points > limits.maxTessellationPatchSize), RID());
tessellation_create_info.patchControlPoints = p_rasterization_state.patch_control_points;
VkPipelineViewportStateCreateInfo viewport_state_create_info;
@@ -5578,44 +6414,53 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
ERR_FAIL_INDEX_V(p_blend_state.logic_op, LOGIC_OP_MAX, RID());
color_blend_state_create_info.logicOp = logic_operations[p_blend_state.logic_op];
- ERR_FAIL_COND_V(fb_format.color_attachments != p_blend_state.attachments.size(), RID());
-
Vector<VkPipelineColorBlendAttachmentState> attachment_states;
+ {
+ const FramebufferPass &pass = fb_format.E->key().passes[p_for_render_pass];
+
+ 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];
+
+ state.colorWriteMask = 0;
+ if (p_blend_state.attachments[idx].write_r) {
+ state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
+ }
+ if (p_blend_state.attachments[idx].write_g) {
+ state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
+ }
+ if (p_blend_state.attachments[idx].write_b) {
+ state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
+ }
+ if (p_blend_state.attachments[idx].write_a) {
+ state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
+ }
- for (int i = 0; i < p_blend_state.attachments.size(); i++) {
- VkPipelineColorBlendAttachmentState state;
- 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[i].write_r) {
- state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
- }
- if (p_blend_state.attachments[i].write_g) {
- state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
- }
- if (p_blend_state.attachments[i].write_b) {
- state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
- }
- if (p_blend_state.attachments[i].write_a) {
- state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
+ attachment_states.push_back(state);
+ idx++;
+ }
}
- attachment_states.push_back(state);
- };
+ ERR_FAIL_COND_V(attachment_states.size() != p_blend_state.attachments.size(), RID());
+ }
color_blend_state_create_info.attachmentCount = attachment_states.size();
color_blend_state_create_info.pAttachments = attachment_states.ptr();
@@ -5674,8 +6519,62 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
graphics_pipeline_create_info.pNext = nullptr;
graphics_pipeline_create_info.flags = 0;
- graphics_pipeline_create_info.stageCount = shader->pipeline_stages.size();
- graphics_pipeline_create_info.pStages = shader->pipeline_stages.ptr();
+ Vector<VkPipelineShaderStageCreateInfo> pipeline_stages = shader->pipeline_stages;
+ Vector<VkSpecializationInfo> specialization_info;
+ Vector<Vector<VkSpecializationMapEntry>> specialization_map_entries;
+ Vector<uint32_t> specialization_constant_data;
+
+ if (shader->specialization_constants.size()) {
+ specialization_constant_data.resize(shader->specialization_constants.size());
+ uint32_t *data_ptr = specialization_constant_data.ptrw();
+ 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
+ const Shader::SpecializationConstant &sc = shader->specialization_constants[i];
+ 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] = psc.int_value;
+ break;
+ }
+ }
+
+ VkSpecializationMapEntry entry;
+
+ entry.constantID = sc.constant.constant_id;
+ entry.offset = i * sizeof(uint32_t);
+ entry.size = sizeof(uint32_t);
+
+ for (int j = 0; j < SHADER_STAGE_MAX; j++) {
+ if (sc.stage_flags & (1 << j)) {
+ VkShaderStageFlagBits stage = shader_stage_masks[j];
+ for (int k = 0; k < pipeline_stages.size(); k++) {
+ if (pipeline_stages[k].stage == stage) {
+ specialization_map_entries.write[k].push_back(entry);
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < pipeline_stages.size(); i++) {
+ if (specialization_map_entries[i].size()) {
+ specialization_info.write[i].dataSize = specialization_constant_data.size() * sizeof(uint32_t);
+ specialization_info.write[i].pData = data_ptr;
+ specialization_info.write[i].mapEntryCount = specialization_map_entries[i].size();
+ specialization_info.write[i].pMapEntries = specialization_map_entries[i].ptr();
+ pipeline_stages.write[i].pSpecializationInfo = specialization_info.ptr() + i;
+ }
+ }
+ }
+
+ graphics_pipeline_create_info.stageCount = pipeline_stages.size();
+ graphics_pipeline_create_info.pStages = pipeline_stages.ptr();
+
graphics_pipeline_create_info.pVertexInputState = &pipeline_vertex_input_state_create_info;
graphics_pipeline_create_info.pInputAssemblyState = &input_assembly_create_info;
graphics_pipeline_create_info.pTessellationState = &tessellation_create_info;
@@ -5688,13 +6587,13 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
graphics_pipeline_create_info.layout = shader->pipeline_layout;
graphics_pipeline_create_info.renderPass = fb_format.render_pass;
- graphics_pipeline_create_info.subpass = 0;
+ graphics_pipeline_create_info.subpass = p_for_render_pass;
graphics_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
graphics_pipeline_create_info.basePipelineIndex = 0;
RenderPipeline pipeline;
VkResult err = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline.pipeline);
- ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + ".");
+ ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateGraphicsPipelines failed with error " + itos(err) + " for shader '" + shader->name + "'.");
pipeline.set_formats = shader->set_formats;
pipeline.push_constant_stages = shader->push_constant.push_constants_vk_stage;
@@ -5705,6 +6604,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
#ifdef DEBUG_ENABLED
pipeline.validation.dynamic_state = p_dynamic_state_flags;
pipeline.validation.framebuffer_format = p_framebuffer_format;
+ pipeline.validation.render_pass = p_for_render_pass;
pipeline.validation.vertex_format = p_vertex_format;
pipeline.validation.uses_restart_indices = input_assembly_create_info.primitiveRestartEnable;
@@ -5729,7 +6629,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
#endif
//create ID to associate with this pipeline
RID id = render_pipeline_owner.make_rid(pipeline);
- //now add aall the dependencies
+ //now add all the dependencies
_add_dependency(id, p_shader);
return id;
}
@@ -5743,11 +6643,11 @@ bool RenderingDeviceVulkan::render_pipeline_is_valid(RID p_pipeline) {
/**** COMPUTE PIPELINE ****/
/**************************/
-RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) {
+RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants) {
_THREAD_SAFE_METHOD_
//needs a shader
- Shader *shader = shader_owner.getornull(p_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(),
@@ -5765,6 +6665,44 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) {
compute_pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
compute_pipeline_create_info.basePipelineIndex = 0;
+ VkSpecializationInfo specialization_info;
+ Vector<VkSpecializationMapEntry> specialization_map_entries;
+ Vector<uint32_t> specialization_constant_data;
+
+ if (shader->specialization_constants.size()) {
+ 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
+ const Shader::SpecializationConstant &sc = shader->specialization_constants[i];
+ 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;
+ break;
+ }
+ }
+
+ VkSpecializationMapEntry entry;
+
+ entry.constantID = sc.constant.constant_id;
+ entry.offset = i * sizeof(uint32_t);
+ entry.size = sizeof(uint32_t);
+
+ specialization_map_entries.push_back(entry);
+ }
+
+ specialization_info.dataSize = specialization_constant_data.size() * sizeof(uint32_t);
+ specialization_info.pData = data_ptr;
+ specialization_info.mapEntryCount = specialization_map_entries.size();
+ specialization_info.pMapEntries = specialization_map_entries.ptr();
+
+ compute_pipeline_create_info.stage.pSpecializationInfo = &specialization_info;
+ }
+
ComputePipeline pipeline;
VkResult err = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &compute_pipeline_create_info, nullptr, &pipeline.pipeline);
ERR_FAIL_COND_V_MSG(err, RID(), "vkCreateComputePipelines failed with error " + itos(err) + ".");
@@ -5780,7 +6718,7 @@ RID RenderingDeviceVulkan::compute_pipeline_create(RID p_shader) {
//create ID to associate with this pipeline
RID id = compute_pipeline_owner.make_rid(pipeline);
- //now add aall the dependencies
+ //now add all the dependencies
_add_dependency(id, p_shader);
return id;
}
@@ -5843,13 +6781,18 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di
ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time.");
VkCommandBuffer command_buffer = frames[frame].draw_command_buffer;
- draw_list = memnew(DrawList);
- draw_list->command_buffer = 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);
#ifdef DEBUG_ENABLED
- draw_list->validation.framebuffer_format = screen_get_framebuffer_format();
+ draw_list_framebuffer_format = screen_get_framebuffer_format();
#endif
- draw_list_count = 0;
- draw_list_split = false;
+ draw_list_subpass_count = 1;
VkRenderPassBeginInfo render_pass_begin;
render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
@@ -5857,8 +6800,8 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di
render_pass_begin.renderPass = context->window_get_render_pass(p_screen);
render_pass_begin.framebuffer = context->window_get_framebuffer(p_screen);
- render_pass_begin.renderArea.extent.width = context->window_get_width(p_screen);
- render_pass_begin.renderArea.extent.height = context->window_get_height(p_screen);
+ render_pass_begin.renderArea.extent.width = size.width;
+ render_pass_begin.renderArea.extent.height = size.height;
render_pass_begin.renderArea.offset.x = 0;
render_pass_begin.renderArea.offset.y = 0;
@@ -5898,18 +6841,19 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin_for_screen(Di
return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
}
-Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass) {
+Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count) {
Framebuffer::VersionKey vk;
vk.initial_color_action = p_initial_color_action;
vk.final_color_action = p_final_color_action;
vk.initial_depth_action = p_initial_depth_action;
vk.final_depth_action = p_final_depth_action;
+ vk.view_count = p_framebuffer->view_count;
if (!p_framebuffer->framebuffers.has(vk)) {
//need to create this version
Framebuffer::Version version;
- version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action);
+ 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);
VkFramebufferCreateInfo framebuffer_create_info;
framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
@@ -5918,7 +6862,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu
framebuffer_create_info.renderPass = version.render_pass;
Vector<VkImageView> attachments;
for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) {
- Texture *texture = texture_owner.getornull(p_framebuffer->texture_ids[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);
@@ -5933,11 +6877,14 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu
VkResult err = vkCreateFramebuffer(device, &framebuffer_create_info, nullptr, &version.framebuffer);
ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkCreateFramebuffer failed with error " + itos(err) + ".");
+ version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size();
+
p_framebuffer->framebuffers.insert(vk, version);
}
const Framebuffer::Version &version = p_framebuffer->framebuffers[vk];
*r_framebuffer = version.framebuffer;
*r_render_pass = version.render_pass;
+ *r_subpass_count = version.subpass_count;
return OK;
}
@@ -5967,7 +6914,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff
{
int color_index = 0;
for (int i = 0; i < framebuffer->texture_ids.size(); i++) {
- Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]);
+ Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]);
VkClearValue clear_value;
if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
@@ -5995,7 +6942,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff
render_pass_begin.pClearValues = clear_values.ptr();
for (int i = 0; i < p_storage_textures.size(); i++) {
- Texture *texture = texture_owner.getornull(p_storage_textures[i]);
+ Texture *texture = texture_owner.get_or_null(p_storage_textures[i]);
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) {
@@ -6033,7 +6980,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff
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.getornull(framebuffer->texture_ids[i]);
+ Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]);
texture->bound = true;
draw_list_bound_textures.push_back(framebuffer->texture_ids[i]);
}
@@ -6045,7 +6992,7 @@ void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list,
Vector<VkClearAttachment> clear_attachments;
int color_index = 0;
for (int i = 0; i < framebuffer->texture_ids.size(); i++) {
- Texture *texture = texture_owner.getornull(framebuffer->texture_ids[i]);
+ Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]);
VkClearAttachment clear_at = {};
if (p_clear_color && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
@@ -6088,7 +7035,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "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, INVALID_ID, "Only one draw/compute list can be active at the same time.");
- Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer);
+ Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer);
ERR_FAIL_COND_V(!framebuffer, INVALID_ID);
Point2i viewport_offset;
@@ -6127,15 +7074,23 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values
- int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments;
- ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, INVALID_ID,
- "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ").");
+ 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) {
+ 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;
VkRenderPass render_pass;
- Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass);
+ Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count);
ERR_FAIL_COND_V(err != OK, INVALID_ID);
VkCommandBuffer command_buffer = frames[frame].draw_command_buffer;
@@ -6145,13 +7100,14 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
return INVALID_ID;
}
- draw_list = memnew(DrawList);
- draw_list->command_buffer = command_buffer;
+ draw_list_render_pass = render_pass;
+ draw_list_vkframebuffer = vkframebuffer;
+
+ _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0);
#ifdef DEBUG_ENABLED
- draw_list->validation.framebuffer_format = framebuffer->format_id;
+ draw_list_framebuffer_format = framebuffer->format_id;
#endif
- draw_list_count = 0;
- draw_list_split = false;
+ draw_list_current_subpass = 0;
if (needs_clear_color || needs_clear_depth) {
_draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil);
@@ -6175,7 +7131,6 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
- draw_list->viewport = Rect2i(viewport_offset, viewport_size);
return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
}
@@ -6184,7 +7139,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION);
- Framebuffer *framebuffer = framebuffer_owner.getornull(p_framebuffer);
+ Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer);
ERR_FAIL_COND_V(!framebuffer, ERR_INVALID_DECLARATION);
Point2i viewport_offset;
@@ -6217,47 +7172,23 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
if (p_initial_color_action == INITIAL_ACTION_CLEAR) { //check clear values
- int color_attachments = framebuffer_formats[framebuffer->format_id].color_attachments;
- ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_attachments, ERR_INVALID_PARAMETER,
- "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_attachments) + ").");
- }
-
- if (p_splits > (uint32_t)split_draw_list_allocators.size()) {
- uint32_t from = split_draw_list_allocators.size();
- split_draw_list_allocators.resize(p_splits);
- for (uint32_t i = from; i < p_splits; i++) {
- VkCommandPoolCreateInfo cmd_pool_info;
- cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
- cmd_pool_info.pNext = nullptr;
- cmd_pool_info.queueFamilyIndex = context->get_graphics_queue();
- cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
-
- VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool);
- ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + ".");
-
- for (int j = 0; j < frame_count; j++) {
- VkCommandBuffer command_buffer;
-
- VkCommandBufferAllocateInfo cmdbuf;
- //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;
- cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
- cmdbuf.commandBufferCount = 1;
-
- VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer);
- ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + ".");
+ 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]);
- split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer);
+ if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) {
+ color_count++;
}
}
+
+ ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER,
+ "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ").");
}
VkFramebuffer vkframebuffer;
VkRenderPass render_pass;
- Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass);
+ Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &vkframebuffer, &render_pass, &draw_list_subpass_count);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer;
@@ -6267,53 +7198,24 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
return ERR_CANT_CREATE;
}
- draw_list = memnew_arr(DrawList, p_splits);
- draw_list_count = p_splits;
- draw_list_split = true;
-
- for (uint32_t i = 0; i < p_splits; i++) {
- //take a command buffer and initialize it
- VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame];
-
- VkCommandBufferInheritanceInfo inheritance_info;
- inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
- inheritance_info.pNext = nullptr;
- inheritance_info.renderPass = render_pass;
- inheritance_info.subpass = 0;
- inheritance_info.framebuffer = vkframebuffer;
- inheritance_info.occlusionQueryEnable = false;
- inheritance_info.queryFlags = 0; //?
- inheritance_info.pipelineStatistics = 0;
-
- VkCommandBufferBeginInfo cmdbuf_begin;
- cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
- cmdbuf_begin.pNext = nullptr;
- cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
- cmdbuf_begin.pInheritanceInfo = &inheritance_info;
+ draw_list_current_subpass = 0;
- VkResult res = vkResetCommandBuffer(command_buffer, 0);
- if (res) {
- memdelete_arr(draw_list);
- draw_list = nullptr;
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + ".");
- }
-
- res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin);
- if (res) {
- memdelete_arr(draw_list);
- draw_list = nullptr;
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + ".");
- }
-
- draw_list[i].command_buffer = command_buffer;
#ifdef DEBUG_ENABLED
- draw_list[i].validation.framebuffer_format = framebuffer->format_id;
+ draw_list_framebuffer_format = framebuffer->format_id;
#endif
+ draw_list_render_pass = render_pass;
+ draw_list_vkframebuffer = vkframebuffer;
- if (i == 0 && (needs_clear_color || needs_clear_depth)) {
- _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil);
- }
+ err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0);
+ if (err != OK) {
+ return err;
+ }
+ if (needs_clear_color || needs_clear_depth) {
+ _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil);
+ }
+
+ for (uint32_t i = 0; i < p_splits; i++) {
VkViewport viewport;
viewport.x = viewport_offset.x;
viewport.y = viewport_offset.y;
@@ -6322,7 +7224,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
viewport.minDepth = 0;
viewport.maxDepth = 1.0;
- vkCmdSetViewport(command_buffer, 0, 1, &viewport);
+ vkCmdSetViewport(draw_list[i].command_buffer, 0, 1, &viewport);
VkRect2D scissor;
scissor.offset.x = viewport_offset.x;
@@ -6330,10 +7232,8 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
scissor.extent.width = viewport_size.width;
scissor.extent.height = viewport_size.height;
- vkCmdSetScissor(command_buffer, 0, 1, &scissor);
+ vkCmdSetScissor(draw_list[i].command_buffer, 0, 1, &scissor);
r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i;
-
- draw_list[i].viewport = Rect2i(viewport_offset, viewport_size);
}
return OK;
@@ -6375,10 +7275,10 @@ void RenderingDeviceVulkan::draw_list_bind_render_pipeline(DrawListID p_list, RI
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const RenderPipeline *pipeline = render_pipeline_owner.getornull(p_render_pipeline);
+ const RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_render_pipeline);
ERR_FAIL_COND(!pipeline);
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND(pipeline->validation.framebuffer_format != dl->validation.framebuffer_format);
+ ERR_FAIL_COND(pipeline->validation.framebuffer_format != draw_list_framebuffer_format && pipeline->validation.render_pass != draw_list_current_subpass);
#endif
if (p_render_pipeline == dl->state.pipeline) {
@@ -6450,7 +7350,7 @@ void RenderingDeviceVulkan::draw_list_bind_uniform_set(DrawListID p_list, RID p_
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set);
+ const UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set);
ERR_FAIL_COND(!uniform_set);
if (p_index > dl->state.set_count) {
@@ -6498,7 +7398,7 @@ void RenderingDeviceVulkan::draw_list_bind_vertex_array(DrawListID p_list, RID p
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const VertexArray *vertex_array = vertex_array_owner.getornull(p_vertex_array);
+ const VertexArray *vertex_array = vertex_array_owner.get_or_null(p_vertex_array);
ERR_FAIL_COND(!vertex_array);
if (dl->state.vertex_array == p_vertex_array) {
@@ -6522,7 +7422,7 @@ void RenderingDeviceVulkan::draw_list_bind_index_array(DrawListID p_list, RID p_
ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified.");
#endif
- const IndexArray *index_array = index_array_owner.getornull(p_index_array);
+ const IndexArray *index_array = index_array_owner.get_or_null(p_index_array);
ERR_FAIL_COND(!index_array);
if (dl->state.index_array == p_index_array) {
@@ -6608,7 +7508,7 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices
if (dl->state.sets[i].uniform_set_format == 0) {
ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline");
} else if (uniform_set_owner.owns(dl->state.sets[i].uniform_set)) {
- UniformSet *us = uniform_set_owner.getornull(dl->state.sets[i].uniform_set);
+ UniformSet *us = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set);
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader));
} else {
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(dl->state.pipeline_shader));
@@ -6717,33 +7617,175 @@ void RenderingDeviceVulkan::draw_list_disable_scissor(DrawListID p_list) {
vkCmdSetScissor(dl->command_buffer, 0, 1, &scissor);
}
-void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) {
- _THREAD_SAFE_METHOD_
+uint32_t RenderingDeviceVulkan::draw_list_get_current_pass() {
+ return draw_list_current_subpass;
+}
- ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive.");
+RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_switch_to_next_pass() {
+ ERR_FAIL_COND_V(draw_list == nullptr, INVALID_ID);
+ ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, INVALID_FORMAT_ID);
+
+ draw_list_current_subpass++;
+
+ Rect2i viewport;
+ _draw_list_free(&viewport);
+
+ vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE);
+
+ _draw_list_allocate(viewport, 0, draw_list_current_subpass);
+
+ return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT;
+}
+Error RenderingDeviceVulkan::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) {
+ ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER);
+
+ draw_list_current_subpass++;
+
+ Rect2i viewport;
+ _draw_list_free(&viewport);
+
+ vkCmdNextSubpass(frames[frame].draw_command_buffer, VK_SUBPASS_CONTENTS_INLINE);
+
+ _draw_list_allocate(viewport, p_splits, draw_list_current_subpass);
+
+ for (uint32_t i = 0; i < p_splits; i++) {
+ r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i;
+ }
+
+ return OK;
+}
+
+Error RenderingDeviceVulkan::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) {
+ // Lock while draw_list is active
+ _THREAD_SAFE_LOCK_
+
+ if (p_splits == 0) {
+ draw_list = memnew(DrawList);
+ draw_list->command_buffer = frames[frame].draw_command_buffer;
+ draw_list->viewport = p_viewport;
+ draw_list_count = 0;
+ draw_list_split = false;
+ } else {
+ if (p_splits > (uint32_t)split_draw_list_allocators.size()) {
+ uint32_t from = split_draw_list_allocators.size();
+ split_draw_list_allocators.resize(p_splits);
+ for (uint32_t i = from; i < p_splits; i++) {
+ VkCommandPoolCreateInfo cmd_pool_info;
+ cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ cmd_pool_info.pNext = nullptr;
+ cmd_pool_info.queueFamilyIndex = context->get_graphics_queue_family_index();
+ cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+
+ VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &split_draw_list_allocators.write[i].command_pool);
+ ERR_FAIL_COND_V_MSG(res, ERR_CANT_CREATE, "vkCreateCommandPool failed with error " + itos(res) + ".");
+
+ for (int j = 0; j < frame_count; j++) {
+ VkCommandBuffer command_buffer;
+
+ VkCommandBufferAllocateInfo cmdbuf;
+ //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;
+ cmdbuf.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
+ cmdbuf.commandBufferCount = 1;
+
+ VkResult err = vkAllocateCommandBuffers(device, &cmdbuf, &command_buffer);
+ ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "vkAllocateCommandBuffers failed with error " + itos(err) + ".");
+
+ split_draw_list_allocators.write[i].command_buffers.push_back(command_buffer);
+ }
+ }
+ }
+ draw_list = memnew_arr(DrawList, p_splits);
+ draw_list_count = p_splits;
+ draw_list_split = true;
+
+ for (uint32_t i = 0; i < p_splits; i++) {
+ //take a command buffer and initialize it
+ VkCommandBuffer command_buffer = split_draw_list_allocators[i].command_buffers[frame];
+
+ VkCommandBufferInheritanceInfo inheritance_info;
+ inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
+ inheritance_info.pNext = nullptr;
+ inheritance_info.renderPass = draw_list_render_pass;
+ inheritance_info.subpass = p_subpass;
+ inheritance_info.framebuffer = draw_list_vkframebuffer;
+ inheritance_info.occlusionQueryEnable = false;
+ inheritance_info.queryFlags = 0; //?
+ inheritance_info.pipelineStatistics = 0;
+
+ VkCommandBufferBeginInfo cmdbuf_begin;
+ cmdbuf_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ cmdbuf_begin.pNext = nullptr;
+ cmdbuf_begin.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
+ cmdbuf_begin.pInheritanceInfo = &inheritance_info;
+
+ VkResult res = vkResetCommandBuffer(command_buffer, 0);
+ if (res) {
+ memdelete_arr(draw_list);
+ draw_list = nullptr;
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkResetCommandBuffer failed with error " + itos(res) + ".");
+ }
+
+ res = vkBeginCommandBuffer(command_buffer, &cmdbuf_begin);
+ if (res) {
+ memdelete_arr(draw_list);
+ draw_list = nullptr;
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "vkBeginCommandBuffer failed with error " + itos(res) + ".");
+ }
+
+ draw_list[i].command_buffer = command_buffer;
+ draw_list[i].viewport = p_viewport;
+ }
+ }
+ return OK;
+}
+
+void RenderingDeviceVulkan::_draw_list_free(Rect2i *r_last_viewport) {
if (draw_list_split) {
//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);
command_buffers[i] = draw_list[i].command_buffer;
+ if (r_last_viewport) {
+ if (i == 0 || draw_list[i].viewport_set) {
+ *r_last_viewport = draw_list[i].viewport;
+ }
+ }
}
vkCmdExecuteCommands(frames[frame].draw_command_buffer, draw_list_count, command_buffers);
- vkCmdEndRenderPass(frames[frame].draw_command_buffer);
memdelete_arr(draw_list);
draw_list = nullptr;
} else {
+ if (r_last_viewport) {
+ *r_last_viewport = draw_list->viewport;
+ }
//just end the list
- vkCmdEndRenderPass(draw_list->command_buffer);
memdelete(draw_list);
draw_list = nullptr;
}
+ // draw_list is no longer active
+ _THREAD_SAFE_UNLOCK_
+}
+
+void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive.");
+
+ _draw_list_free();
+
+ vkCmdEndRenderPass(frames[frame].draw_command_buffer);
+
for (int i = 0; i < draw_list_bound_textures.size(); i++) {
- Texture *texture = texture_owner.getornull(draw_list_bound_textures[i]);
+ Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]);
ERR_CONTINUE(!texture); //wtf
if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) {
texture->bound = false;
@@ -6791,7 +7833,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) {
}
for (uint32_t i = 0; i < image_barrier_count; i++) {
- Texture *texture = texture_owner.getornull(draw_list_storage_textures[i]);
+ Texture *texture = texture_owner.get_or_null(draw_list_storage_textures[i]);
VkImageMemoryBarrier &image_memory_barrier = image_barriers[i];
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -6817,7 +7859,7 @@ void RenderingDeviceVulkan::draw_list_end(uint32_t p_post_barrier) {
// 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
+ // * Another render pass happens (since we may be done)
#ifdef FORCE_FULL_BARRIER
_full_barrier(true);
@@ -6844,6 +7886,9 @@ 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
+ _THREAD_SAFE_LOCK_
+
compute_list = memnew(ComputeList);
compute_list->command_buffer = frames[frame].draw_command_buffer;
compute_list->state.allow_draw_overlap = p_allow_draw_overlap;
@@ -6857,7 +7902,7 @@ void RenderingDeviceVulkan::compute_list_bind_compute_pipeline(ComputeListID p_l
ComputeList *cl = compute_list;
- const ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_compute_pipeline);
+ const ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_compute_pipeline);
ERR_FAIL_COND(!pipeline);
if (p_compute_pipeline == cl->state.pipeline) {
@@ -6930,7 +7975,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list,
ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified.");
#endif
- UniformSet *uniform_set = uniform_set_owner.getornull(p_uniform_set);
+ UniformSet *uniform_set = uniform_set_owner.get_or_null(p_uniform_set);
ERR_FAIL_COND(!uniform_set);
if (p_index > cl->state.set_count) {
@@ -7012,13 +8057,13 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list,
textures_to_storage[i]->used_in_compute = false;
textures_to_storage[i]->used_in_raster = false;
- textures_to_storage[i]->used_in_compute = false;
+ textures_to_storage[i]->used_in_transfer = false;
} else {
src_access_flags = 0;
textures_to_storage[i]->used_in_compute = false;
textures_to_storage[i]->used_in_raster = false;
- textures_to_storage[i]->used_in_compute = false;
+ textures_to_storage[i]->used_in_transfer = false;
textures_to_storage[i]->used_in_frame = frames_drawn;
}
@@ -7096,12 +8141,15 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t
ComputeList *cl = compute_list;
#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(p_x_groups == 0, "Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is zero.");
+ ERR_FAIL_COND_MSG(p_z_groups == 0, "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is zero.");
+ ERR_FAIL_COND_MSG(p_y_groups == 0, "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is zero.");
ERR_FAIL_COND_MSG(p_x_groups > limits.maxComputeWorkGroupCount[0],
"Dispatch amount of X compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
ERR_FAIL_COND_MSG(p_y_groups > limits.maxComputeWorkGroupCount[1],
- "Dispatch amount of Y compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
+ "Dispatch amount of Y compute groups (" + itos(p_y_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[1]) + ")");
ERR_FAIL_COND_MSG(p_z_groups > limits.maxComputeWorkGroupCount[2],
- "Dispatch amount of Z compute groups (" + itos(p_x_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[0]) + ")");
+ "Dispatch amount of Z compute groups (" + itos(p_z_groups) + ") is larger than device limit (" + itos(limits.maxComputeWorkGroupCount[2]) + ")");
ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified.");
#endif
@@ -7129,7 +8177,7 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t
if (cl->state.sets[i].uniform_set_format == 0) {
ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline");
} else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) {
- UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set);
+ UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set);
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
} else {
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
@@ -7150,6 +8198,12 @@ void RenderingDeviceVulkan::compute_list_dispatch_threads(ComputeListID p_list,
ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
ERR_FAIL_COND(!compute_list);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(p_x_threads == 0, "Dispatch amount of X compute threads (" + itos(p_x_threads) + ") is zero.");
+ ERR_FAIL_COND_MSG(p_y_threads == 0, "Dispatch amount of Y compute threads (" + itos(p_y_threads) + ") is zero.");
+ ERR_FAIL_COND_MSG(p_z_threads == 0, "Dispatch amount of Z compute threads (" + itos(p_z_threads) + ") is zero.");
+#endif
+
ComputeList *cl = compute_list;
#ifdef DEBUG_ENABLED
@@ -7172,7 +8226,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list,
ERR_FAIL_COND(!compute_list);
ComputeList *cl = compute_list;
- Buffer *buffer = storage_buffer_owner.getornull(p_buffer);
+ 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.");
@@ -7206,7 +8260,7 @@ void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list,
if (cl->state.sets[i].uniform_set_format == 0) {
ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline");
} else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) {
- UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set);
+ UniformSet *us = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set);
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
} else {
ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
@@ -7308,6 +8362,9 @@ void RenderingDeviceVulkan::compute_list_end(uint32_t p_post_barrier) {
memdelete(compute_list);
compute_list = nullptr;
+
+ // compute_list is no longer active
+ _THREAD_SAFE_UNLOCK_
}
void RenderingDeviceVulkan::barrier(uint32_t p_from, uint32_t p_to) {
@@ -7418,60 +8475,65 @@ void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_frame
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)
if (texture_owner.owns(p_id)) {
- Texture *texture = texture_owner.getornull(p_id);
+ Texture *texture = texture_owner.get_or_null(p_id);
frames[frame].textures_to_dispose_of.push_back(*texture);
texture_owner.free(p_id);
} else if (framebuffer_owner.owns(p_id)) {
- Framebuffer *framebuffer = framebuffer_owner.getornull(p_id);
+ Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_id);
frames[frame].framebuffers_to_dispose_of.push_back(*framebuffer);
framebuffer_owner.free(p_id);
} else if (sampler_owner.owns(p_id)) {
- VkSampler *sampler = sampler_owner.getornull(p_id);
+ VkSampler *sampler = sampler_owner.get_or_null(p_id);
frames[frame].samplers_to_dispose_of.push_back(*sampler);
sampler_owner.free(p_id);
} else if (vertex_buffer_owner.owns(p_id)) {
- Buffer *vertex_buffer = vertex_buffer_owner.getornull(p_id);
+ Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id);
frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer);
vertex_buffer_owner.free(p_id);
} else if (vertex_array_owner.owns(p_id)) {
vertex_array_owner.free(p_id);
} else if (index_buffer_owner.owns(p_id)) {
- IndexBuffer *index_buffer = index_buffer_owner.getornull(p_id);
+ IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id);
Buffer b;
b.allocation = index_buffer->allocation;
b.buffer = index_buffer->buffer;
b.size = index_buffer->size;
+ b.buffer_info = {};
frames[frame].buffers_to_dispose_of.push_back(b);
index_buffer_owner.free(p_id);
} else if (index_array_owner.owns(p_id)) {
index_array_owner.free(p_id);
} else if (shader_owner.owns(p_id)) {
- Shader *shader = shader_owner.getornull(p_id);
+ Shader *shader = shader_owner.get_or_null(p_id);
frames[frame].shaders_to_dispose_of.push_back(*shader);
shader_owner.free(p_id);
} else if (uniform_buffer_owner.owns(p_id)) {
- Buffer *uniform_buffer = uniform_buffer_owner.getornull(p_id);
+ Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id);
frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer);
uniform_buffer_owner.free(p_id);
} else if (texture_buffer_owner.owns(p_id)) {
- TextureBuffer *texture_buffer = texture_buffer_owner.getornull(p_id);
+ TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id);
frames[frame].buffers_to_dispose_of.push_back(texture_buffer->buffer);
frames[frame].buffer_views_to_dispose_of.push_back(texture_buffer->view);
texture_buffer_owner.free(p_id);
} else if (storage_buffer_owner.owns(p_id)) {
- Buffer *storage_buffer = storage_buffer_owner.getornull(p_id);
+ Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id);
frames[frame].buffers_to_dispose_of.push_back(*storage_buffer);
storage_buffer_owner.free(p_id);
} else if (uniform_set_owner.owns(p_id)) {
- UniformSet *uniform_set = uniform_set_owner.getornull(p_id);
+ UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id);
frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set);
uniform_set_owner.free(p_id);
+
+ if (uniform_set->invalidated_callback != nullptr) {
+ uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata);
+ }
} else if (render_pipeline_owner.owns(p_id)) {
- RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id);
+ RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id);
frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline);
render_pipeline_owner.free(p_id);
} else if (compute_pipeline_owner.owns(p_id)) {
- ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id);
+ ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id);
frames[frame].compute_pipelines_to_dispose_of.push_back(*pipeline);
compute_pipeline_owner.free(p_id);
} else {
@@ -7490,49 +8552,49 @@ void RenderingDeviceVulkan::free(RID p_id) {
// 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.getornull(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
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.getornull(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
} else if (sampler_owner.owns(p_id)) {
- VkSampler *sampler = sampler_owner.getornull(p_id);
+ VkSampler *sampler = sampler_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_SAMPLER, uint64_t(*sampler), p_name);
} else if (vertex_buffer_owner.owns(p_id)) {
- Buffer *vertex_buffer = vertex_buffer_owner.getornull(p_id);
+ Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(vertex_buffer->buffer), p_name);
} else if (index_buffer_owner.owns(p_id)) {
- IndexBuffer *index_buffer = index_buffer_owner.getornull(p_id);
+ IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(index_buffer->buffer), p_name);
} else if (shader_owner.owns(p_id)) {
- Shader *shader = shader_owner.getornull(p_id);
+ Shader *shader = shader_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(shader->pipeline_layout), p_name + " Pipeline Layout");
for (int i = 0; i < shader->sets.size(); i++) {
context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, uint64_t(shader->sets[i].descriptor_set_layout), p_name);
}
} else if (uniform_buffer_owner.owns(p_id)) {
- Buffer *uniform_buffer = uniform_buffer_owner.getornull(p_id);
+ Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(uniform_buffer->buffer), p_name);
} else if (texture_buffer_owner.owns(p_id)) {
- TextureBuffer *texture_buffer = texture_buffer_owner.getornull(p_id);
+ TextureBuffer *texture_buffer = texture_buffer_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(texture_buffer->buffer.buffer), p_name);
context->set_object_name(VK_OBJECT_TYPE_BUFFER_VIEW, uint64_t(texture_buffer->view), p_name + " View");
} else if (storage_buffer_owner.owns(p_id)) {
- Buffer *storage_buffer = storage_buffer_owner.getornull(p_id);
+ Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_BUFFER, uint64_t(storage_buffer->buffer), p_name);
} else if (uniform_set_owner.owns(p_id)) {
- UniformSet *uniform_set = uniform_set_owner.getornull(p_id);
+ UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_DESCRIPTOR_SET, uint64_t(uniform_set->descriptor_set), p_name);
} else if (render_pipeline_owner.owns(p_id)) {
- RenderPipeline *pipeline = render_pipeline_owner.getornull(p_id);
+ RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name);
context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout");
} else if (compute_pipeline_owner.owns(p_id)) {
- ComputePipeline *pipeline = compute_pipeline_owner.getornull(p_id);
+ ComputePipeline *pipeline = compute_pipeline_owner.get_or_null(p_id);
context->set_object_name(VK_OBJECT_TYPE_PIPELINE, uint64_t(pipeline->pipeline), p_name);
context->set_object_name(VK_OBJECT_TYPE_PIPELINE_LAYOUT, uint64_t(pipeline->pipeline_layout), p_name + " Layout");
} else {
@@ -7558,6 +8620,11 @@ String RenderingDeviceVulkan::get_device_vendor_name() const {
String RenderingDeviceVulkan::get_device_name() const {
return context->get_device_name();
}
+
+RenderingDevice::DeviceType RenderingDeviceVulkan::get_device_type() const {
+ return context->get_device_type();
+}
+
String RenderingDeviceVulkan::get_device_pipeline_cache_uuid() const {
return context->get_device_pipeline_cache_uuid();
}
@@ -7659,6 +8726,30 @@ 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
@@ -7730,10 +8821,10 @@ void RenderingDeviceVulkan::_free_pending_resources(int p_frame) {
while (frames[p_frame].framebuffers_to_dispose_of.front()) {
Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get();
- for (Map<Framebuffer::VersionKey, Framebuffer::Version>::Element *E = framebuffer->framebuffers.front(); E; E = E->next()) {
+ for (const KeyValue<Framebuffer::VersionKey, Framebuffer::Version> &E : framebuffer->framebuffers) {
//first framebuffer, then render pass because it depends on it
- vkDestroyFramebuffer(device, E->get().framebuffer, nullptr);
- vkDestroyRenderPass(device, E->get().render_pass, nullptr);
+ vkDestroyFramebuffer(device, E.value.framebuffer, nullptr);
+ vkDestroyRenderPass(device, E.value.render_pass, nullptr);
}
frames[p_frame].framebuffers_to_dispose_of.pop_front();
@@ -7749,6 +8840,7 @@ 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
+ image_memory -= texture->allocation_info.size;
vmaDestroyImage(allocator, texture->image, texture->allocation);
}
frames[p_frame].textures_to_dispose_of.pop_front();
@@ -7772,15 +8864,21 @@ uint32_t RenderingDeviceVulkan::get_frame_delay() const {
return frame_count;
}
-uint64_t RenderingDeviceVulkan::get_memory_usage() const {
- VmaStats stats;
- vmaCalculateStats(allocator, &stats);
- return stats.total.usedBytes;
+uint64_t RenderingDeviceVulkan::get_memory_usage(MemoryType p_type) const {
+ if (p_type == MEMORY_BUFFERS) {
+ return buffer_memory;
+ } else if (p_type == MEMORY_TEXTURES) {
+ return image_memory;
+ } else {
+ 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; //flushign 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)
if (p_current_frame) {
@@ -7834,6 +8932,23 @@ void RenderingDeviceVulkan::_flush(bool p_current_frame) {
}
void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_device) {
+ // 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;
device = p_context->get_device();
if (p_local_device) {
@@ -7852,6 +8967,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
memset(&allocatorInfo, 0, sizeof(VmaAllocatorCreateInfo));
allocatorInfo.physicalDevice = p_context->get_physical_device();
allocatorInfo.device = device;
+ allocatorInfo.instance = p_context->get_instance();
vmaCreateAllocator(&allocatorInfo, &allocator);
}
@@ -7865,7 +8981,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
VkCommandPoolCreateInfo cmd_pool_info;
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmd_pool_info.pNext = nullptr;
- cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue();
+ cmd_pool_info.queueFamilyIndex = p_context->get_graphics_queue_family_index();
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VkResult res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &frames[i].command_pool);
@@ -7931,11 +9047,13 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
}
}
+ // 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 = 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) {
@@ -7974,14 +9092,19 @@ void RenderingDeviceVulkan::_free_rids(T &p_owner, const char *p_type) {
List<RID> owned;
p_owner.get_owned_list(&owned);
if (owned.size()) {
- WARN_PRINT(itos(owned.size()) + " RIDs of type '" + p_type + "' were leaked.");
- for (List<RID>::Element *E = owned.front(); E; E = E->next()) {
- free(E->get());
+ if (owned.size() == 1) {
+ WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type));
+ } else {
+ WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type));
+ }
+ for (const RID &E : owned) {
+ free(E);
}
}
}
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
@@ -7991,35 +9114,35 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) {
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.pNext = nullptr;
memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
- VK_ACCESS_INDEX_READ_BIT |
- VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
- VK_ACCESS_UNIFORM_READ_BIT |
- VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
- VK_ACCESS_SHADER_READ_BIT |
- VK_ACCESS_SHADER_WRITE_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 |
- VK_ACCESS_TRANSFER_READ_BIT |
- VK_ACCESS_TRANSFER_WRITE_BIT |
- VK_ACCESS_HOST_READ_BIT |
- VK_ACCESS_HOST_WRITE_BIT;
+ VK_ACCESS_INDEX_READ_BIT |
+ VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+ VK_ACCESS_UNIFORM_READ_BIT |
+ VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+ VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_SHADER_WRITE_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 |
+ VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT |
+ VK_ACCESS_HOST_READ_BIT |
+ VK_ACCESS_HOST_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT |
- VK_ACCESS_INDEX_READ_BIT |
- VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
- VK_ACCESS_UNIFORM_READ_BIT |
- VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
- VK_ACCESS_SHADER_READ_BIT |
- VK_ACCESS_SHADER_WRITE_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 |
- VK_ACCESS_TRANSFER_READ_BIT |
- VK_ACCESS_TRANSFER_WRITE_BIT |
- VK_ACCESS_HOST_READ_BIT |
- VK_ACCESS_HOST_WRITE_BIT;
+ VK_ACCESS_INDEX_READ_BIT |
+ VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT |
+ VK_ACCESS_UNIFORM_READ_BIT |
+ VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
+ VK_ACCESS_SHADER_READ_BIT |
+ VK_ACCESS_SHADER_WRITE_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 |
+ VK_ACCESS_TRANSFER_READ_BIT |
+ VK_ACCESS_TRANSFER_WRITE_BIT |
+ VK_ACCESS_HOST_READ_BIT |
+ VK_ACCESS_HOST_WRITE_BIT;
vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr);
}
@@ -8030,6 +9153,92 @@ void RenderingDeviceVulkan::capture_timestamp(const String &p_name) {
frames[frame].timestamp_count++;
}
+uint64_t RenderingDeviceVulkan::get_driver_resource(DriverResource p_resource, RID p_rid, uint64_t p_index) {
+ _THREAD_SAFE_METHOD_
+
+ switch (p_resource) {
+ case DRIVER_RESOURCE_VULKAN_DEVICE: {
+ return (uint64_t)context->get_device();
+ } break;
+ case DRIVER_RESOURCE_VULKAN_PHYSICAL_DEVICE: {
+ return (uint64_t)context->get_physical_device();
+ } break;
+ case DRIVER_RESOURCE_VULKAN_INSTANCE: {
+ return (uint64_t)context->get_instance();
+ } break;
+ case DRIVER_RESOURCE_VULKAN_QUEUE: {
+ return (uint64_t)context->get_graphics_queue();
+ } break;
+ case DRIVER_RESOURCE_VULKAN_QUEUE_FAMILY_INDEX: {
+ return context->get_graphics_queue_family_index();
+ } 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ case DRIVER_RESOURCE_VULKAN_BUFFER: {
+ Buffer *buffer = nullptr;
+ if (vertex_buffer_owner.owns(p_rid)) {
+ buffer = vertex_buffer_owner.get_or_null(p_rid);
+ } else if (index_buffer_owner.owns(p_rid)) {
+ buffer = index_buffer_owner.get_or_null(p_rid);
+ } else if (uniform_buffer_owner.owns(p_rid)) {
+ buffer = uniform_buffer_owner.get_or_null(p_rid);
+ } else if (texture_buffer_owner.owns(p_rid)) {
+ buffer = &texture_buffer_owner.get_or_null(p_rid)->buffer;
+ } else if (storage_buffer_owner.owns(p_rid)) {
+ buffer = storage_buffer_owner.get_or_null(p_rid);
+ }
+
+ ERR_FAIL_NULL_V(buffer, 0);
+
+ return uint64_t(buffer->buffer);
+ } 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;
+ 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;
+ default: {
+ // not supported for this driver
+ return 0;
+ } break;
+ }
+}
+
uint32_t RenderingDeviceVulkan::get_captured_timestamps_count() const {
return frames[frame].timestamp_result_count;
}
@@ -8086,7 +9295,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) {
switch (p_limit) {
case LIMIT_MAX_BOUND_UNIFORM_SETS:
return limits.maxBoundDescriptorSets;
@@ -8187,19 +9396,23 @@ void RenderingDeviceVulkan::finalize() {
List<RID> owned;
texture_owner.get_owned_list(&owned);
if (owned.size()) {
- WARN_PRINT(itos(owned.size()) + " RIDs of type 'Texture' were leaked.");
+ if (owned.size() == 1) {
+ WARN_PRINT("1 RID of type \"Texture\" was leaked.");
+ } else {
+ WARN_PRINT(vformat("%d RIDs of type \"Texture\" were leaked.", owned.size()));
+ }
//free shared first
for (List<RID>::Element *E = owned.front(); E;) {
List<RID>::Element *N = E->next();
if (texture_is_shared(E->get())) {
free(E->get());
- owned.erase(E->get());
+ owned.erase(E);
}
E = N;
}
//free non shared second, this will avoid an error trying to free unexisting textures due to dependencies.
- for (List<RID>::Element *E = owned.front(); E; E = E->next()) {
- free(E->get());
+ for (const RID &E : owned) {
+ free(E);
}
}
}
@@ -8226,6 +9439,11 @@ void RenderingDeviceVulkan::finalize() {
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()) {
+ Map<uint32_t, VmaPool>::Element *E = small_allocs_pools.front();
+ vmaDestroyPool(allocator, E->get());
+ small_allocs_pools.erase(E);
+ }
vmaDestroyAllocator(allocator);
while (vertex_formats.size()) {
@@ -8253,6 +9471,7 @@ RenderingDevice *RenderingDeviceVulkan::create_local_device() {
}
RenderingDeviceVulkan::RenderingDeviceVulkan() {
+ device_capabilities.device_family = DEVICE_VULKAN;
}
RenderingDeviceVulkan::~RenderingDeviceVulkan() {
diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h
index a2527d5c33..a4d5af91a4 100644
--- a/drivers/vulkan/rendering_device_vulkan.h
+++ b/drivers/vulkan/rendering_device_vulkan.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,11 @@
#endif
#include "vk_mem_alloc.h"
+#ifdef USE_VOLK
+#include <volk.h>
+#else
#include <vulkan/vulkan.h>
+#endif
class VulkanContext;
@@ -156,6 +160,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
uint32_t texture_upload_region_size_px = 0;
Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false);
+ Error _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);
/*****************/
/**** SAMPLER ****/
@@ -170,7 +175,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
// These are temporary buffers on CPU memory that hold
// the information until the CPU fetches it and places it
// either on GPU buffers, or images (textures). It ensures
- // updates are properly synchronized with whathever the
+ // updates are properly synchronized with whatever the
// GPU is doing.
//
// The logic here is as follows, only 3 of these
@@ -234,7 +239,87 @@ class RenderingDeviceVulkan : public RenderingDevice {
struct FramebufferFormatKey {
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;
+ }
+
+ uint32_t pass_size = passes.size();
+ uint32_t key_pass_size = p_key.passes.size();
+ if (pass_size != key_pass_size) {
+ return pass_size < key_pass_size;
+ }
+ const FramebufferPass *pass_ptr = passes.ptr();
+ const FramebufferPass *key_pass_ptr = p_key.passes.ptr();
+
+ for (uint32_t i = 0; i < pass_size; i++) {
+ { //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) {
+ return attachment_size < key_attachment_size;
+ }
+ const int32_t *pass_attachment_ptr = pass_ptr[i].color_attachments.ptr();
+ const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].color_attachments.ptr();
+
+ for (uint32_t j = 0; j < attachment_size; j++) {
+ if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+ return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+ }
+ }
+ }
+ { //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) {
+ return attachment_size < key_attachment_size;
+ }
+ const int32_t *pass_attachment_ptr = pass_ptr[i].input_attachments.ptr();
+ const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].input_attachments.ptr();
+
+ for (uint32_t j = 0; j < attachment_size; j++) {
+ if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+ return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+ }
+ }
+ }
+ { //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) {
+ return attachment_size < key_attachment_size;
+ }
+ const int32_t *pass_attachment_ptr = pass_ptr[i].resolve_attachments.ptr();
+ const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].resolve_attachments.ptr();
+
+ for (uint32_t j = 0; j < attachment_size; j++) {
+ if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+ return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+ }
+ }
+ }
+ { //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) {
+ return attachment_size < key_attachment_size;
+ }
+ const int32_t *pass_attachment_ptr = pass_ptr[i].preserve_attachments.ptr();
+ const int32_t *key_pass_attachment_ptr = key_pass_ptr[i].preserve_attachments.ptr();
+
+ for (uint32_t j = 0; j < attachment_size; j++) {
+ if (pass_attachment_ptr[j] != key_pass_attachment_ptr[j]) {
+ return pass_attachment_ptr[j] < key_pass_attachment_ptr[j];
+ }
+ }
+ }
+ if (pass_ptr[i].depth_attachment != key_pass_ptr[i].depth_attachment) {
+ return pass_ptr[i].depth_attachment < key_pass_ptr[i].depth_attachment;
+ }
+ }
+
int as = attachments.size();
int bs = p_key.attachments.size();
if (as != bs) {
@@ -261,16 +346,15 @@ class RenderingDeviceVulkan : public RenderingDevice {
}
};
- VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr);
-
+ 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;
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)
- int color_attachments = 0; //used for pipeline validation
- TextureSamples samples;
+ Vector<TextureSamples> pass_samples;
+ uint32_t view_count = 1; // number of views
};
Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats;
@@ -282,11 +366,17 @@ class RenderingDeviceVulkan : public RenderingDevice {
FinalAction final_color_action;
InitialAction initial_depth_action;
FinalAction final_depth_action;
+ uint32_t view_count;
+
bool operator<(const VersionKey &p_key) const {
if (initial_color_action == p_key.initial_color_action) {
if (final_color_action == p_key.final_color_action) {
if (initial_depth_action == p_key.initial_depth_action) {
- return final_depth_action < p_key.final_depth_action;
+ if (final_depth_action == p_key.final_depth_action) {
+ return view_count < p_key.view_count;
+ } else {
+ return final_depth_action < p_key.final_depth_action;
+ }
} else {
return initial_depth_action < p_key.initial_depth_action;
}
@@ -305,10 +395,12 @@ class RenderingDeviceVulkan : public RenderingDevice {
struct Version {
VkFramebuffer framebuffer = VK_NULL_HANDLE;
VkRenderPass render_pass = VK_NULL_HANDLE; //this one is owned
+ uint32_t subpass_count = 1;
};
Map<VersionKey, Version> framebuffers;
Size2 size;
+ uint32_t view_count;
};
RID_Owner<Framebuffer, true> framebuffer_owner;
@@ -524,7 +616,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
};
uint32_t vertex_input_mask = 0; //inputs used, this is mostly for validation
- int fragment_outputs = 0;
+ uint32_t fragment_output_mask = 0;
struct PushConstant {
uint32_t push_constant_size = 0;
@@ -535,12 +627,19 @@ class RenderingDeviceVulkan : public RenderingDevice {
uint32_t compute_local_size[3] = { 0, 0, 0 };
+ struct SpecializationConstant {
+ PipelineSpecializationConstant constant;
+ uint32_t stage_flags = 0;
+ };
+
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 _shader_uniform_debug(RID p_shader, int p_set = -1);
@@ -558,8 +657,8 @@ class RenderingDeviceVulkan : public RenderingDevice {
// Basically, you can mix and match pools as you
// like, but you'll run into fragmentation issues.
// Because of this, the recommended approach is to
- // create a a pool for every descriptor set type,
- // as this prevents fragmentation.
+ // create a pool for every descriptor set type, as
+ // this prevents fragmentation.
//
// This is implemented here as a having a list of
// pools (each can contain up to 64 sets) for each
@@ -644,6 +743,8 @@ class RenderingDeviceVulkan : public RenderingDevice {
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;
+ void *invalidated_callback_userdata = nullptr;
};
RID_Owner<UniformSet, true> uniform_set_owner;
@@ -668,6 +769,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
#ifdef DEBUG_ENABLED
struct Validation {
FramebufferFormatID framebuffer_format = 0;
+ uint32_t render_pass = 0;
uint32_t dynamic_state = 0;
VertexFormatID vertex_format = 0;
bool uses_restart_indices = false;
@@ -711,7 +813,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
// When using split command lists, this is
// implemented internally using secondary command
// buffers. As they can be created in threads,
- // each needs it's own command pool.
+ // each needs its own command pool.
struct SplitDrawListAllocator {
VkCommandPool command_pool = VK_NULL_HANDLE;
@@ -723,6 +825,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
struct DrawList {
VkCommandBuffer command_buffer = VK_NULL_HANDLE; // If persistent, this is owned, otherwise it's shared with the ringbuffer.
Rect2i viewport;
+ bool viewport_set = false;
struct SetState {
uint32_t pipeline_expected_format = 0;
@@ -746,7 +849,6 @@ class RenderingDeviceVulkan : public RenderingDevice {
#ifdef DEBUG_ENABLED
struct Validation {
bool active = true; // Means command buffer was not closed, so you can keep adding things.
- FramebufferFormatID framebuffer_format = INVALID_ID;
// Actual render pass values.
uint32_t dynamic_state = 0;
VertexFormatID vertex_format = INVALID_ID;
@@ -782,7 +884,15 @@ 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;
+#ifdef DEBUG_ENABLED
+ FramebufferFormatID draw_list_framebuffer_format = INVALID_ID;
+#endif
+ uint32_t draw_list_current_subpass = 0;
+
bool draw_list_split = false;
Vector<RID> draw_list_bound_textures;
Vector<RID> draw_list_storage_textures;
@@ -790,10 +900,12 @@ class RenderingDeviceVulkan : public RenderingDevice {
bool draw_list_unbind_depth_textures = false;
void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil);
- Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass);
+ Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count);
Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures);
_FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id);
Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, uint32_t p_post_barrier);
+ Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass);
+ void _draw_list_free(Rect2i *r_last_viewport = nullptr);
/**********************/
/**** COMPUTE LIST ****/
@@ -904,9 +1016,14 @@ class RenderingDeviceVulkan : public RenderingDevice {
void _free_pending_resources(int p_frame);
VmaAllocator allocator = nullptr;
+ Map<uint32_t, VmaPool> small_allocs_pools;
+ VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index);
VulkanContext *context = nullptr;
+ uint64_t image_memory = 0;
+ uint64_t buffer_memory = 0;
+
void _free_internal(RID p_id);
void _flush(bool p_current_frame);
@@ -921,14 +1038,16 @@ class RenderingDeviceVulkan : public RenderingDevice {
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, TextureSliceType p_slice_type = TEXTURE_SLICE_2D);
+ 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);
virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer);
virtual bool texture_is_format_supported_for_usage(DataFormat p_format, uint32_t p_usage) const;
virtual bool texture_is_shared(RID p_texture);
virtual bool texture_is_valid(RID p_texture);
+ virtual Size2i texture_size(RID p_texture);
virtual Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, uint32_t p_post_barrier = BARRIER_MASK_ALL);
virtual Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, uint32_t p_post_barrier = BARRIER_MASK_ALL);
@@ -938,11 +1057,13 @@ public:
/**** FRAMEBUFFER ****/
/*********************/
- virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format);
+ 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_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1);
- virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format);
+ 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);
+ 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_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID);
virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer);
@@ -971,7 +1092,11 @@ public:
/**** SHADER ****/
/****************/
- virtual RID shader_create(const Vector<ShaderStageData> &p_stages);
+ virtual String shader_get_binary_cache_key() const;
+ virtual Vector<uint8_t> shader_compile_binary_from_spirv(const Vector<ShaderStageSPIRVData> &p_spirv, const String &p_shader_name = "");
+
+ virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary);
+
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader);
/*****************/
@@ -984,6 +1109,7 @@ 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 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);
@@ -993,14 +1119,14 @@ public:
/**** RENDER PIPELINE ****/
/*************************/
- virtual RID 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 = 0);
+ virtual RID 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 = 0, uint32_t p_for_render_pass = 0, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>());
virtual bool render_pipeline_is_valid(RID p_pipeline);
/**************************/
/**** COMPUTE PIPELINE ****/
/**************************/
- virtual RID compute_pipeline_create(RID p_shader);
+ virtual RID compute_pipeline_create(RID p_shader, const Vector<PipelineSpecializationConstant> &p_specialization_constants = Vector<PipelineSpecializationConstant>());
virtual bool compute_pipeline_is_valid(RID p_pipeline);
/****************/
@@ -1032,6 +1158,10 @@ public:
virtual void draw_list_enable_scissor(DrawListID p_list, const Rect2 &p_rect);
virtual void draw_list_disable_scissor(DrawListID p_list);
+ virtual uint32_t draw_list_get_current_pass();
+ virtual DrawListID draw_list_switch_to_next_pass();
+ virtual Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids);
+
virtual void draw_list_end(uint32_t p_post_barrier = BARRIER_MASK_ALL);
/***********************/
@@ -1073,7 +1203,7 @@ public:
/**** Limits ****/
/****************/
- virtual int limit_get(Limit p_limit);
+ virtual uint64_t limit_get(Limit p_limit);
virtual void prepare_screen_for_drawing();
void initialize(VulkanContext *p_context, bool p_local_device = false);
@@ -1088,7 +1218,7 @@ public:
virtual RenderingDevice *create_local_device();
- virtual uint64_t get_memory_usage() const;
+ virtual uint64_t get_memory_usage(MemoryType p_type) const;
virtual void set_resource_name(RID p_id, const String p_name);
@@ -1098,8 +1228,11 @@ 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_pipeline_cache_uuid() const;
+ virtual uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0);
+
RenderingDeviceVulkan();
~RenderingDeviceVulkan();
};
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index c564cee757..3551b5d6c4 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,9 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/string/ustring.h"
+#include "core/templates/local_vector.h"
#include "core/version.h"
+#include "servers/rendering/rendering_device.h"
#include "vk_enum_string_helper.h"
@@ -44,6 +46,8 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define APP_SHORT_NAME "GodotEngine"
+VulkanHooks *VulkanContext::vulkan_hooks = nullptr;
+
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
@@ -135,10 +139,10 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(
}
String error_message(type_string +
- " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) +
- " | Message Id Name: " + pCallbackData->pMessageIdName +
- "\n\t" + pCallbackData->pMessage +
- objects_string + labels_string);
+ " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) +
+ " | Message Id Name: " + pCallbackData->pMessageIdName +
+ "\n\t" + pCallbackData->pMessage +
+ objects_string + labels_string);
// Convert VK severity to our own log macros.
switch (messageSeverity) {
@@ -163,7 +167,36 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback(
return VK_FALSE;
}
-VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers) {
+VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objectType,
+ uint64_t object,
+ size_t location,
+ int32_t messageCode,
+ const char *pLayerPrefix,
+ const char *pMessage,
+ void *pUserData) {
+ String debugMessage = String("Vulkan Debug Report: object - ") +
+ String::num_int64(object) + "\n" + pMessage;
+
+ switch (flags) {
+ case VK_DEBUG_REPORT_DEBUG_BIT_EXT:
+ case VK_DEBUG_REPORT_INFORMATION_BIT_EXT:
+ print_line(debugMessage);
+ break;
+ case VK_DEBUG_REPORT_WARNING_BIT_EXT:
+ case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT:
+ WARN_PRINT(debugMessage);
+ break;
+ case VK_DEBUG_REPORT_ERROR_BIT_EXT:
+ ERR_PRINT(debugMessage);
+ break;
+ }
+
+ return VK_FALSE;
+}
+
+VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers) {
for (uint32_t i = 0; i < check_count; i++) {
VkBool32 found = 0;
for (uint32_t j = 0; j < layer_count; j++) {
@@ -180,55 +213,85 @@ VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char **check_n
return 1;
}
-Error VulkanContext::_create_validation_layers() {
+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
+ { "VK_LAYER_KHRONOS_validation" },
+
+ // 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
+ { "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
+ *count = 0;
+ if (names != nullptr) {
+ *names = nullptr;
+ }
+
VkResult err;
- const char *instance_validation_layers_alt1[] = { "VK_LAYER_KHRONOS_validation" };
- const char *instance_validation_layers_alt2[] = { "VK_LAYER_LUNARG_standard_validation" };
- const char *instance_validation_layers_alt3[] = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" };
+ uint32_t instance_layer_count;
- uint32_t instance_layer_count = 0;
err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr);
- ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ if (err) {
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
- VkBool32 validation_found = 0;
- uint32_t validation_layer_count = 0;
- const char **instance_validation_layers = nullptr;
- if (instance_layer_count > 0) {
- VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count);
- err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers);
- if (err) {
- free(instance_layers);
- ERR_FAIL_V(ERR_CANT_CREATE);
- }
+ if (instance_layer_count < 1) {
+ return OK;
+ }
- validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt1);
- instance_validation_layers = instance_validation_layers_alt1;
- validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers);
+ VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count);
+ err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers);
+ if (err) {
+ free(instance_layers);
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ }
- // use alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers
- if (!validation_found) {
- validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt2);
- instance_validation_layers = instance_validation_layers_alt2;
- validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers);
+ for (uint32_t i = 0; i < instance_validation_layers_alt.size(); i++) {
+ if (_check_layers(instance_validation_layers_alt[i].size(), instance_validation_layers_alt[i].ptr(), instance_layer_count, instance_layers)) {
+ *count = instance_validation_layers_alt[i].size();
+ if (names != nullptr) {
+ *names = instance_validation_layers_alt[i].ptr();
+ }
+ break;
}
+ }
- // use alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers
- if (!validation_found) {
- validation_layer_count = ARRAY_SIZE(instance_validation_layers_alt3);
- instance_validation_layers = instance_validation_layers_alt3;
- validation_found = _check_layers(validation_layer_count, instance_validation_layers, instance_layer_count, instance_layers);
- }
+ free(instance_layers);
- free(instance_layers);
- }
+ return OK;
+}
- if (validation_found) {
- enabled_layer_count = validation_layer_count;
- for (uint32_t i = 0; i < validation_layer_count; i++) {
- enabled_layers[i] = instance_validation_layers[i];
+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.
+ _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion");
+ if (func != nullptr) {
+ uint32_t api_version;
+ VkResult res = func(&api_version);
+ if (res == VK_SUCCESS) {
+ vulkan_major = VK_API_VERSION_MAJOR(api_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
+ ERR_FAIL_V(ERR_CANT_CREATE);
}
} else {
- return ERR_CANT_CREATE;
+ print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0.");
+ }
+
+ // we don't go above 1.2
+ if ((vulkan_major > 1) || (vulkan_major == 1 && vulkan_minor > 2)) {
+ vulkan_major = 1;
+ vulkan_minor = 2;
+ vulkan_patch = 0;
}
return OK;
@@ -238,8 +301,8 @@ Error VulkanContext::_initialize_extensions() {
uint32_t instance_extension_count = 0;
enabled_extension_count = 0;
- enabled_layer_count = 0;
enabled_debug_utils = false;
+ enabled_debug_report = false;
/* Look for instance extensions */
VkBool32 surfaceExtFound = 0;
VkBool32 platformSurfaceExtFound = 0;
@@ -266,14 +329,18 @@ Error VulkanContext::_initialize_extensions() {
extension_names[enabled_extension_count++] = _get_platform_surface_extension();
}
if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, instance_extensions[i].extensionName)) {
- if (use_validation_layers) {
+ if (_use_validation_layers()) {
extension_names[enabled_extension_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
+ enabled_debug_report = true;
}
}
if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) {
extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
enabled_debug_utils = true;
}
+ if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) {
+ extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
+ }
if (enabled_extension_count >= MAX_EXTENSIONS) {
free(instance_extensions);
ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG");
@@ -289,12 +356,289 @@ Error VulkanContext::_initialize_extensions() {
return OK;
}
-Error VulkanContext::_create_physical_device() {
- /* Look for validation layers */
- if (use_validation_layers) {
- _create_validation_layers();
+uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const {
+ uint32_t flags = 0;
+
+ if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) {
+ flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT;
+ }
+ if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
+ flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT;
+ }
+ if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
+ flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT;
+ }
+ // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) {
+ // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT;
+ // }
+ if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) {
+ flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT;
+ }
+ if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) {
+ flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT;
+ }
+
+ return flags;
+}
+
+String VulkanContext::SubgroupCapabilities::supported_stages_desc() const {
+ String res;
+
+ if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) {
+ res += ", STAGE_VERTEX";
+ }
+ if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) {
+ res += ", STAGE_TESSELLATION_CONTROL";
+ }
+ if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
+ res += ", STAGE_TESSELLATION_EVALUATION";
+ }
+ if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) {
+ res += ", STAGE_GEOMETRY";
+ }
+ if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) {
+ res += ", STAGE_FRAGMENT";
+ }
+ if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) {
+ res += ", STAGE_COMPUTE";
+ }
+
+ /* these are not defined on Android GRMBL */
+ if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) {
+ res += ", STAGE_RAYGEN_KHR";
+ }
+ if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) {
+ res += ", STAGE_ANY_HIT_KHR";
+ }
+ if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) {
+ res += ", STAGE_CLOSEST_HIT_KHR";
+ }
+ if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) {
+ res += ", STAGE_MISS_KHR";
+ }
+ if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) {
+ res += ", STAGE_INTERSECTION_KHR";
+ }
+ if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) {
+ res += ", STAGE_CALLABLE_KHR";
+ }
+ if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) {
+ res += ", STAGE_TASK_NV";
+ }
+ if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) {
+ res += ", STAGE_MESH_NV";
+ }
+
+ return res.substr(2); // remove first ", "
+}
+
+uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const {
+ uint32_t flags = 0;
+
+ if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT;
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) {
+ flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT;
+ }
+
+ return flags;
+}
+
+String VulkanContext::SubgroupCapabilities::supported_operations_desc() const {
+ String res;
+
+ if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) {
+ res += ", FEATURE_BASIC";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) {
+ res += ", FEATURE_VOTE";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) {
+ res += ", FEATURE_ARITHMETIC";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) {
+ res += ", FEATURE_BALLOT";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) {
+ res += ", FEATURE_SHUFFLE";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) {
+ res += ", FEATURE_SHUFFLE_RELATIVE";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) {
+ res += ", FEATURE_CLUSTERED";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) {
+ res += ", FEATURE_QUAD";
+ }
+ if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) {
+ res += ", FEATURE_PARTITIONED_NV";
+ }
+
+ 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.
+
+ // 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
+ multiview_capabilities.is_supported = false;
+ multiview_capabilities.geometry_shader_is_supported = false;
+ multiview_capabilities.tessellation_shader_is_supported = false;
+ multiview_capabilities.max_view_count = 0;
+ multiview_capabilities.max_instance_count = 0;
+ subgroup_capabilities.size = 0;
+ 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
+ VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {
+ /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR,
+ /*pNext*/ nullptr,
+ /*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;
+
+ vkGetPhysicalDeviceFeatures2_func(gpu, &device_features);
+
+ multiview_capabilities.is_supported = multiview_features.multiview;
+ multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader;
+ multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader;
+
+ shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16;
+ shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8;
+
+ 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
+ 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
+ device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR");
+ }
+ if (device_properties_func != nullptr) {
+ VkPhysicalDeviceMultiviewProperties multiviewProperties;
+ VkPhysicalDeviceSubgroupProperties subgroupProperties;
+ VkPhysicalDeviceProperties2 physicalDeviceProperties;
+
+ subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
+ subgroupProperties.pNext = nullptr;
+
+ physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+
+ if (multiview_capabilities.is_supported) {
+ multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES;
+ multiviewProperties.pNext = &subgroupProperties;
+
+ physicalDeviceProperties.pNext = &multiviewProperties;
+ } else {
+ physicalDeviceProperties.pNext = &subgroupProperties;
+ }
+
+ 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
+ subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages;
+
+ if (multiview_capabilities.is_supported) {
+ multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount;
+ multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex;
+
+ print_verbose("- Vulkan multiview supported:");
+ print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count));
+ print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count));
+ } else {
+ print_verbose("- Vulkan multiview not supported");
+ }
+
+ print_verbose("- Vulkan subgroup:");
+ print_verbose(" size: " + itos(subgroup_capabilities.size));
+ print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc());
+ print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc());
+ if (subgroup_capabilities.quadOperationsInAllStages) {
+ print_verbose(" quad operations in all stages");
+ }
+ } else {
+ print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2");
+ }
+
+ return OK;
+}
+
+Error VulkanContext::_create_instance() {
+ /* obtain version */
+ _obtain_vulkan_version();
+
+ /* initialise extensions */
{
Error err = _initialize_extensions();
if (err != OK) {
@@ -303,34 +647,31 @@ Error VulkanContext::_create_physical_device() {
}
CharString cs = ProjectSettings::get_singleton()->get("application/config/name").operator String().utf8();
- String name = "GodotEngine " + String(VERSION_FULL_NAME);
- CharString namecs = name.utf8();
const VkApplicationInfo app = {
/*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO,
/*pNext*/ nullptr,
/*pApplicationName*/ cs.get_data(),
/*applicationVersion*/ 0,
- /*pEngineName*/ namecs.get_data(),
- /*engineVersion*/ 0,
- /*apiVersion*/ VK_API_VERSION_1_0,
- };
- VkInstanceCreateInfo inst_info = {
- /*sType*/ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- /*pNext*/ nullptr,
- /*flags*/ 0,
- /*pApplicationInfo*/ &app,
- /*enabledLayerCount*/ enabled_layer_count,
- /*ppEnabledLayerNames*/ (const char *const *)enabled_layers,
- /*enabledExtensionCount*/ enabled_extension_count,
- /*ppEnabledExtensionNames*/ (const char *const *)extension_names,
+ /*pEngineName*/ VERSION_NAME,
+ /*engineVersion*/ VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH),
+ /*apiVersion*/ VK_MAKE_VERSION(vulkan_major, vulkan_minor, 0)
};
+ VkInstanceCreateInfo inst_info{};
+ inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ inst_info.pApplicationInfo = &app;
+ inst_info.enabledExtensionCount = enabled_extension_count;
+ inst_info.ppEnabledExtensionNames = (const char *const *)extension_names;
+ if (_use_validation_layers()) {
+ _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames);
+ }
/*
- * This is info for a temp callback to use during CreateInstance.
- * After the instance is created, we use the instance-based
- * function to register the final callback.
- */
+ * This is info for a temp callback to use during CreateInstance.
+ * After the instance is created, we use the instance-based
+ * function to register the final callback.
+ */
VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info;
+ VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info{};
if (enabled_debug_utils) {
// VK_EXT_debug_utils style
dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
@@ -339,35 +680,130 @@ Error VulkanContext::_create_physical_device() {
dbg_messenger_create_info.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
- VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
- VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
+ VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
+ VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback;
dbg_messenger_create_info.pUserData = this;
inst_info.pNext = &dbg_messenger_create_info;
+ } else if (enabled_debug_report) {
+ dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
+ dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
+ VK_DEBUG_REPORT_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_ERROR_BIT_EXT |
+ VK_DEBUG_REPORT_DEBUG_BIT_EXT;
+ dbg_report_callback_create_info.pfnCallback = _debug_report_callback;
+ dbg_report_callback_create_info.pUserData = this;
+ inst_info.pNext = &dbg_report_callback_create_info;
}
- uint32_t gpu_count;
+ VkResult err;
- 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");
+ 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;
+#ifdef USE_VOLK
+ volkLoadInstance(inst);
+#endif
+
+ if (enabled_debug_utils) {
+ // Setup VK_EXT_debug_utils function pointers always (we use them for
+ // debug labels and names).
+ CreateDebugUtilsMessengerEXT =
+ (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
+ DestroyDebugUtilsMessengerEXT =
+ (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
+ SubmitDebugUtilsMessageEXT =
+ (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
+ CmdBeginDebugUtilsLabelEXT =
+ (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
+ CmdEndDebugUtilsLabelEXT =
+ (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
+ CmdInsertDebugUtilsLabelEXT =
+ (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
+ SetDebugUtilsObjectNameEXT =
+ (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
+ if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
+ nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
+ nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
+ nullptr == SetDebugUtilsObjectNameEXT) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "GetProcAddr: Failed to init VK_EXT_debug_utils\n"
+ "GetProcAddr: Failure");
+ }
+
+ err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
+ switch (err) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugUtilsMessengerEXT: out of host memory\n"
+ "CreateDebugUtilsMessengerEXT Failure");
+ break;
+ default:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugUtilsMessengerEXT: unknown failure\n"
+ "CreateDebugUtilsMessengerEXT Failure");
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ break;
+ }
+ } else if (enabled_debug_report) {
+ CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT");
+ DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT");
+ DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT");
+
+ if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "GetProcAddr: Failed to init VK_EXT_debug_report\n"
+ "GetProcAddr: Failure");
+ }
+
+ err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report);
+ switch (err) {
+ case VK_SUCCESS:
+ break;
+ case VK_ERROR_OUT_OF_HOST_MEMORY:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugReportCallbackEXT: out of host memory\n"
+ "CreateDebugReportCallbackEXT Failure");
+ break;
+ default:
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE,
+ "CreateDebugReportCallbackEXT: unknown failure\n"
+ "CreateDebugReportCallbackEXT Failure");
+ ERR_FAIL_V(ERR_CANT_CREATE);
+ break;
+ }
+ }
+
+ return OK;
+}
+
+Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) {
/* Make initial call to query gpu_count, then second call for gpu info*/
- err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
+ uint32_t gpu_count = 0;
+ VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
-
ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE,
"vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
"Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
@@ -379,19 +815,6 @@ Error VulkanContext::_create_physical_device() {
free(physical_devices);
ERR_FAIL_V(ERR_CANT_CREATE);
}
- /* for now, just grab the first physical device */
- uint32_t device_index = 0;
- gpu = physical_devices[device_index];
- free(physical_devices);
-
- /* 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 */
- vkGetPhysicalDeviceProperties(gpu, &gpu_props);
static const struct {
uint32_t id;
@@ -399,13 +822,143 @@ Error VulkanContext::_create_physical_device() {
} vendor_names[] = {
{ 0x1002, "AMD" },
{ 0x1010, "ImgTec" },
+ { 0x106B, "Apple" },
{ 0x10DE, "NVIDIA" },
{ 0x13B5, "ARM" },
{ 0x5143, "Qualcomm" },
- { 0x8086, "INTEL" },
+ { 0x8086, "Intel" },
{ 0, nullptr },
};
+
+ int32_t device_index = -1;
+ if (vulkan_hooks) {
+ if (!vulkan_hooks->get_physical_device(&gpu)) {
+ return ERR_CANT_CREATE;
+ }
+
+ // 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;
+ }
+ }
+ } 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: {
+ 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;
+ }
+ 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 preffered 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;
+ }
+
+ 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];
+ }
+
+ free(physical_devices);
+
+ /* 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 */
+ vkGetPhysicalDeviceProperties(gpu, &gpu_props);
+
device_name = gpu_props.deviceName;
+ device_type = gpu_props.deviceType;
pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE);
pipeline_cache_id += "-driver-" + itos(gpu_props.driverVersion);
{
@@ -419,9 +972,11 @@ Error VulkanContext::_create_physical_device() {
vendor_idx++;
}
}
-#ifdef DEBUG_ENABLED
- print_line("Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name);
-#endif
+
+ print_line(
+ "Vulkan API " + itos(vulkan_major) + "." + itos(vulkan_minor) + "." + itos(vulkan_patch) +
+ " - " + "Using Vulkan Device #" + itos(device_index) + ": " + device_vendor + " - " + device_name);
+
device_api_version = gpu_props.apiVersion;
err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr);
@@ -440,6 +995,10 @@ Error VulkanContext::_create_physical_device() {
swapchainExtFound = 1;
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
+ extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME;
+ }
if (enabled_extension_count >= MAX_EXTENSIONS) {
free(device_extensions);
ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG");
@@ -490,51 +1049,7 @@ Error VulkanContext::_create_physical_device() {
" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n"
"vkCreateInstance Failure");
- if (enabled_debug_utils) {
- // Setup VK_EXT_debug_utils function pointers always (we use them for
- // debug labels and names).
- CreateDebugUtilsMessengerEXT =
- (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT");
- DestroyDebugUtilsMessengerEXT =
- (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT");
- SubmitDebugUtilsMessageEXT =
- (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT");
- CmdBeginDebugUtilsLabelEXT =
- (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT");
- CmdEndDebugUtilsLabelEXT =
- (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT");
- CmdInsertDebugUtilsLabelEXT =
- (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT");
- SetDebugUtilsObjectNameEXT =
- (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT");
- if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT ||
- nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT ||
- nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT ||
- nullptr == SetDebugUtilsObjectNameEXT) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "GetProcAddr: Failed to init VK_EXT_debug_utils\n"
- "GetProcAddr: Failure");
- }
-
- err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger);
- switch (err) {
- case VK_SUCCESS:
- break;
- case VK_ERROR_OUT_OF_HOST_MEMORY:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugUtilsMessengerEXT: out of host memory\n"
- "CreateDebugUtilsMessengerEXT Failure");
- break;
- default:
- ERR_FAIL_V_MSG(ERR_CANT_CREATE,
- "CreateDebugUtilsMessengerEXT: unknown failure\n"
- "CreateDebugUtilsMessengerEXT Failure");
- ERR_FAIL_V(ERR_CANT_CREATE);
- break;
- }
- }
-
- /* Call with NULL 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);
@@ -561,6 +1076,15 @@ Error VulkanContext::_create_physical_device() {
GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR);
GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR);
+ // get info about what our vulkan driver is capable off
+ {
+ Error res = _check_capabilities();
+ if (res != OK) {
+ return res;
+ }
+ }
+
+ device_initialized = true;
return OK;
}
@@ -575,9 +1099,61 @@ 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;
+
+ 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,
@@ -586,7 +1162,6 @@ Error VulkanContext::_create_device() {
/*enabledExtensionCount*/ enabled_extension_count,
/*ppEnabledExtensionNames*/ (const char *const *)extension_names,
/*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;
@@ -597,17 +1172,24 @@ Error VulkanContext::_create_device() {
queues[1].flags = 0;
sdevice.queueCreateInfoCount = 2;
}
- err = vkCreateDevice(gpu, &sdevice, nullptr, &device);
- ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+
+ 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);
+ }
return OK;
}
-Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) {
+Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) {
// Iterate over each queue to learn whether it supports presenting:
VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32));
for (uint32_t i = 0; i < queue_family_count; i++) {
- fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &supportsPresent[i]);
+ fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]);
}
// Search for a graphics and a present queue in the array of queue
@@ -681,10 +1263,10 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) {
// Get the list of VkFormat's that are supported:
uint32_t formatCount;
- VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, nullptr);
+ VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
- err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, surfFormats);
+ err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats);
if (err) {
free(surfFormats);
ERR_FAIL_V(ERR_CANT_CREATE);
@@ -692,16 +1274,39 @@ Error VulkanContext::_initialize_queues(VkSurfaceKHR surface) {
// If the format list includes just one entry of VK_FORMAT_UNDEFINED,
// the surface has no preferred format. Otherwise, at least one
// supported format will be returned.
- if (true || (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED)) {
+ if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
format = VK_FORMAT_B8G8R8A8_UNORM;
+ 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
+ const VkFormat allowed_formats[] = {
+ VK_FORMAT_B8G8R8A8_UNORM,
+ VK_FORMAT_R8G8B8A8_UNORM
+ };
+ uint32_t allowed_formats_count = sizeof(allowed_formats) / sizeof(VkFormat);
+
if (formatCount < 1) {
free(surfFormats);
ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1");
}
- format = surfFormats[0].format;
+
+ // 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++) {
+ if (surfFormats[sf].format == allowed_formats[af]) {
+ format = surfFormats[sf].format;
+ color_space = surfFormats[sf].colorSpace;
+ }
+ }
+ }
+
+ if (format == VK_FORMAT_UNDEFINED) {
+ free(surfFormats);
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found.");
+ }
}
- color_space = surfFormats[0].colorSpace;
free(surfFormats);
@@ -736,9 +1341,6 @@ Error VulkanContext::_create_semaphores() {
err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
- err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_acquired_semaphores[i]);
- ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
-
err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
@@ -755,9 +1357,18 @@ Error VulkanContext::_create_semaphores() {
return OK;
}
-Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height) {
+bool VulkanContext::_use_validation_layers() {
+ return Engine::get_singleton()->is_validation_layers_enabled();
+}
+
+Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) {
ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER);
+ if (!device_initialized) {
+ Error err = _create_physical_device(p_surface);
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ }
+
if (!queues_initialized) {
// We use a single GPU, but we need a surface to initialize the
// queues, so this process must be deferred until a surface
@@ -770,9 +1381,21 @@ Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, VkSurfa
window.surface = p_surface;
window.width = p_width;
window.height = p_height;
+ window.vsync_mode = p_vsync_mode;
Error err = _update_swap_chain(&window);
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
+ VkSemaphoreCreateInfo semaphoreCreateInfo = {
+ /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ /*pNext*/ nullptr,
+ /*flags*/ 0,
+ };
+
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window.image_acquired_semaphores[i]);
+ ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE);
+ }
+
windows[p_window_id] = window;
return OK;
}
@@ -794,6 +1417,12 @@ 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];
@@ -806,12 +1435,20 @@ VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_wi
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;
+ 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) {
ERR_FAIL_COND(!windows.has(p_window_id));
_clean_up_swap_chain(&windows[p_window_id]);
+ for (uint32_t i = 0; i < FRAME_LAG; i++) {
+ vkDestroySemaphore(device, windows[p_window_id].image_acquired_semaphores[i], nullptr);
+ }
+
vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr);
windows.erase(p_window_id);
}
@@ -898,7 +1535,6 @@ Error VulkanContext::_update_swap_chain(Window *window) {
}
// 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.
- VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
// 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
@@ -927,16 +1563,41 @@ Error VulkanContext::_update_swap_chain(Window *window) {
// the application wants the late image to be immediately displayed, even
// though that may mean some tearing.
- if (window->presentMode != swapchainPresentMode) {
- for (size_t i = 0; i < presentModeCount; ++i) {
- if (presentModes[i] == window->presentMode) {
- swapchainPresentMode = window->presentMode;
- break;
- }
+ VkPresentModeKHR requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR;
+ switch (window->vsync_mode) {
+ case DisplayServer::VSYNC_MAILBOX:
+ requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR;
+ break;
+ case DisplayServer::VSYNC_ADAPTIVE:
+ requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+ break;
+ case DisplayServer::VSYNC_ENABLED:
+ requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR;
+ break;
+ case DisplayServer::VSYNC_DISABLED:
+ requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR;
+ break;
+ }
+
+ // Check if the requested mode is available.
+ bool present_mode_available = false;
+ for (uint32_t i = 0; i < presentModeCount; i++) {
+ if (presentModes[i] == requested_present_mode) {
+ present_mode_available = true;
}
}
+
+ // Set the windows present mode if it is available, otherwise FIFO is used (guaranteed supported).
+ 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
+ }
+
+ print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode)));
+
free(presentModes);
- ERR_FAIL_COND_V_MSG(swapchainPresentMode != window->presentMode, ERR_CANT_CREATE, "Present mode specified is not supported\n");
// Determine the number of VkImages to use in the swap chain.
// Application desires to acquire 3 images at a time for triple
@@ -993,7 +1654,7 @@ Error VulkanContext::_update_swap_chain(Window *window) {
/*pQueueFamilyIndices*/ nullptr,
/*preTransform*/ (VkSurfaceTransformFlagBitsKHR)preTransform,
/*compositeAlpha*/ compositeAlpha,
- /*presentMode*/ swapchainPresentMode,
+ /*presentMode*/ window->presentMode,
/*clipped*/ true,
/*oldSwapchain*/ VK_NULL_HANDLE,
};
@@ -1185,12 +1846,17 @@ Error VulkanContext::_update_swap_chain(Window *window) {
}
Error VulkanContext::initialize() {
- Error err = _create_physical_device();
- if (err) {
+#ifdef USE_VOLK
+ if (volkInitialize() != VK_SUCCESS) {
+ return FAILED;
+ }
+#endif
+
+ Error err = _create_instance();
+ if (err != OK) {
return err;
}
- device_initialized = true;
return OK;
}
@@ -1263,8 +1929,10 @@ Error VulkanContext::prepare_buffers() {
vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX);
vkResetFences(device, 1, &fences[frame_index]);
- for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) {
- Window *w = &E->get();
+ for (KeyValue<int, Window> &E : windows) {
+ Window *w = &E.value;
+
+ w->semaphore_acquired = false;
if (w->swapchain == VK_NULL_HANDLE) {
continue;
@@ -1274,21 +1942,23 @@ Error VulkanContext::prepare_buffers() {
// Get the index of the next available swapchain image:
err =
fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX,
- image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer);
+ 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:
- print_line("early out of data");
+ print_verbose("Vulkan: Early out of date swapchain, recreating.");
//resize_notify();
_update_swap_chain(w);
} else if (err == VK_SUBOPTIMAL_KHR) {
- print_line("early suboptimal");
// 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;
+ } else if (err != VK_SUCCESS) {
+ ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully.");
} else {
- ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ w->semaphore_acquired = true;
}
} while (err != VK_SUCCESS);
}
@@ -1313,13 +1983,13 @@ Error VulkanContext::swap_buffers() {
DemoUpdateTargetIPD(demo);
// Note: a real application would position its geometry to that it's in
- // the correct locatoin for when the next image is presented. It might
+ // the correct location for when the next image is presented. It might
// also wait, so that there's less latency between any input and when
// the next image is rendered/presented. This demo program is so
// simple that it doesn't do either of those.
}
#endif
- // Wait for the image acquired semaphore to be signaled to ensure
+ // Wait for the image acquired semaphore to be signalled to ensure
// that the image won't be rendered to until the presentation
// engine has fully released ownership to the application, and it is
// okay to render to the image.
@@ -1338,14 +2008,26 @@ Error VulkanContext::swap_buffers() {
commands_to_submit = command_buffer_count;
}
- VkPipelineStageFlags pipe_stage_flags;
+ 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];
+ pipe_stage_flags[semaphores_to_acquire_count] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ semaphores_to_acquire_count++;
+ }
+ }
+
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 = 1;
- submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index];
+ 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;
@@ -1361,7 +2043,7 @@ Error VulkanContext::swap_buffers() {
// present queue before presenting, waiting for the draw complete
// 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;
@@ -1369,8 +2051,8 @@ Error VulkanContext::swap_buffers() {
VkCommandBuffer *cmdbufptr = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer *) * windows.size());
submit_info.pCommandBuffers = cmdbufptr;
- for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) {
- Window *w = &E->get();
+ for (KeyValue<int, Window> &E : windows) {
+ Window *w = &E.value;
if (w->swapchain == VK_NULL_HANDLE) {
continue;
@@ -1385,7 +2067,7 @@ Error VulkanContext::swap_buffers() {
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
}
- // If we are using separate queues we have to wait for image ownership,
+ // If we are using separate queues, we have to wait for image ownership,
// otherwise wait for draw complete
VkPresentInfoKHR present = {
/*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@@ -1404,8 +2086,8 @@ Error VulkanContext::swap_buffers() {
present.pSwapchains = pSwapchains;
present.pImageIndices = pImageIndices;
- for (Map<int, Window>::Element *E = windows.front(); E; E = E->next()) {
- Window *w = &E->get();
+ for (KeyValue<int, Window> &E : windows) {
+ Window *w = &E.value;
if (w->swapchain == VK_NULL_HANDLE) {
continue;
@@ -1493,12 +2175,12 @@ Error VulkanContext::swap_buffers() {
if (err == VK_ERROR_OUT_OF_DATE_KHR) {
// swapchain is out of date (e.g. the window was resized) and
// must be recreated:
- print_line("out of date");
+ 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
// presentation engine will still present the image correctly.
- print_line("suboptimal");
+ print_verbose("Vulkan: Swapchain is suboptimal.");
} else {
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
}
@@ -1522,7 +2204,11 @@ int VulkanContext::get_swapchain_image_count() const {
return swapchainImageCount;
}
-uint32_t VulkanContext::get_graphics_queue() const {
+VkQueue VulkanContext::get_graphics_queue() const {
+ return graphics_queue;
+}
+
+uint32_t VulkanContext::get_graphics_queue_family_index() const {
return graphics_queue_family_index;
}
@@ -1573,12 +2259,12 @@ RID VulkanContext::local_device_create() {
}
VkDevice VulkanContext::local_device_get_vk_device(RID p_local_device) {
- LocalDevice *ld = local_device_owner.getornull(p_local_device);
+ LocalDevice *ld = local_device_owner.get_or_null(p_local_device);
return ld->device;
}
void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count) {
- LocalDevice *ld = local_device_owner.getornull(p_local_device);
+ LocalDevice *ld = local_device_owner.get_or_null(p_local_device);
ERR_FAIL_COND(ld->waiting);
VkSubmitInfo submit_info;
@@ -1594,13 +2280,13 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const
VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE);
if (err == VK_ERROR_OUT_OF_HOST_MEMORY) {
- print_line("out of host memory");
+ print_line("Vulkan: Out of host memory!");
}
if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
- print_line("out of device memory");
+ print_line("Vulkan: Out of device memory!");
}
if (err == VK_ERROR_DEVICE_LOST) {
- print_line("device lost");
+ print_line("Vulkan: Device lost!");
}
ERR_FAIL_COND(err);
@@ -1608,7 +2294,7 @@ void VulkanContext::local_device_push_command_buffers(RID p_local_device, const
}
void VulkanContext::local_device_sync(RID p_local_device) {
- LocalDevice *ld = local_device_owner.getornull(p_local_device);
+ LocalDevice *ld = local_device_owner.get_or_null(p_local_device);
ERR_FAIL_COND(!ld->waiting);
vkDeviceWaitIdle(ld->device);
@@ -1616,7 +2302,7 @@ void VulkanContext::local_device_sync(RID p_local_device) {
}
void VulkanContext::local_device_free(RID p_local_device) {
- LocalDevice *ld = local_device_owner.getornull(p_local_device);
+ LocalDevice *ld = local_device_owner.get_or_null(p_local_device);
vkDestroyDevice(ld->device, nullptr);
local_device_owner.free(p_local_device);
}
@@ -1681,13 +2367,27 @@ String VulkanContext::get_device_vendor_name() const {
String VulkanContext::get_device_name() const {
return device_name;
}
+
+RenderingDevice::DeviceType VulkanContext::get_device_type() const {
+ return RenderingDevice::DeviceType(device_type);
+}
+
String VulkanContext::get_device_pipeline_cache_uuid() const {
return pipeline_cache_id;
}
-VulkanContext::VulkanContext() {
- use_validation_layers = Engine::get_singleton()->is_validation_layers_enabled();
+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.");
+ 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.");
+ windows[p_window].vsync_mode = p_mode;
+ _update_swap_chain(&windows[p_window]);
+}
+VulkanContext::VulkanContext() {
command_buffer_queue.resize(1); // First one is always the setup command.
command_buffer_queue.write[0] = nullptr;
}
@@ -1699,15 +2399,17 @@ VulkanContext::~VulkanContext() {
if (device_initialized) {
for (uint32_t i = 0; i < FRAME_LAG; i++) {
vkDestroyFence(device, fences[i], nullptr);
- vkDestroySemaphore(device, image_acquired_semaphores[i], nullptr);
vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr);
if (separate_present_queue) {
vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr);
}
}
- if (inst_initialized && use_validation_layers) {
+ if (inst_initialized && enabled_debug_utils) {
DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr);
}
+ if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) {
+ DestroyDebugReportCallbackEXT(inst, dbg_debug_report, nullptr);
+ }
vkDestroyDevice(device, nullptr);
}
if (inst_initialized) {
diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h
index dc6b0410bc..8c0111714c 100644
--- a/drivers/vulkan/vulkan_context.h
+++ b/drivers/vulkan/vulkan_context.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,18 +37,59 @@
#include "core/templates/map.h"
#include "core/templates/rid_owner.h"
#include "servers/display_server.h"
+#include "servers/rendering/rendering_device.h"
+#ifdef USE_VOLK
+#include <volk.h>
+#else
#include <vulkan/vulkan.h>
+#endif
+
+#include "vulkan_hooks.h"
class VulkanContext {
+public:
+ struct SubgroupCapabilities {
+ uint32_t size;
+ VkShaderStageFlags supportedStages;
+ VkSubgroupFeatureFlags supportedOperations;
+ VkBool32 quadOperationsInAllStages;
+
+ uint32_t supported_stages_flags_rd() const;
+ String supported_stages_desc() const;
+ uint32_t supported_operations_flags_rd() const;
+ String supported_operations_desc() const;
+ };
+
+ struct MultiviewCapabilities {
+ bool is_supported;
+ bool geometry_shader_is_supported;
+ bool tessellation_shader_is_supported;
+ uint32_t max_view_count;
+ uint32_t max_instance_count;
+ };
+
+ 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:
enum {
MAX_EXTENSIONS = 128,
MAX_LAYERS = 64,
FRAME_LAG = 2
};
+ static VulkanHooks *vulkan_hooks;
VkInstance inst = VK_NULL_HANDLE;
- VkSurfaceKHR surface = VK_NULL_HANDLE;
VkPhysicalDevice gpu = VK_NULL_HANDLE;
VkPhysicalDeviceProperties gpu_props;
uint32_t queue_family_count = 0;
@@ -57,8 +98,18 @@ class VulkanContext {
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
+ uint32_t vulkan_major = 1;
+ uint32_t vulkan_minor = 0;
+ uint32_t vulkan_patch = 0;
+ SubgroupCapabilities subgroup_capabilities;
+ MultiviewCapabilities multiview_capabilities;
+ ShaderCapabilities shader_capabilities;
+ StorageBufferCapabilities storage_buffer_capabilities;
+
String device_vendor;
String device_name;
+ VkPhysicalDeviceType device_type;
String pipeline_cache_id;
uint32_t device_api_version = 0;
@@ -73,7 +124,6 @@ class VulkanContext {
VkQueue present_queue = VK_NULL_HANDLE;
VkColorSpaceKHR color_space;
VkFormat format;
- VkSemaphore image_acquired_semaphores[FRAME_LAG];
VkSemaphore draw_complete_semaphores[FRAME_LAG];
VkSemaphore image_ownership_semaphores[FRAME_LAG];
int frame_index = 0;
@@ -93,9 +143,12 @@ class VulkanContext {
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
SwapchainImageResources *swapchain_image_resources = VK_NULL_HANDLE;
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ VkSemaphore image_acquired_semaphores[FRAME_LAG];
+ bool semaphore_acquired = false;
uint32_t current_buffer = 0;
int width = 0;
int height = 0;
+ DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
VkCommandPool present_cmd_pool = VK_NULL_HANDLE; // For separate present queue.
VkRenderPass render_pass = VK_NULL_HANDLE;
};
@@ -126,8 +179,11 @@ class VulkanContext {
const char *extension_names[MAX_EXTENSIONS];
bool enabled_debug_utils = false;
- uint32_t enabled_layer_count = 0;
- const char *enabled_layers[MAX_LAYERS];
+ /**
+ * True if VK_EXT_debug_report extension is used. VK_EXT_debug_report is deprecated but it is
+ * still used if VK_EXT_debug_utils is not available.
+ */
+ bool enabled_debug_report = false;
PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT;
PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT;
@@ -136,6 +192,9 @@ class VulkanContext {
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;
@@ -149,20 +208,34 @@ class VulkanContext {
PFN_vkGetPastPresentationTimingGOOGLE fpGetPastPresentationTimingGOOGLE;
VkDebugUtilsMessengerEXT dbg_messenger = VK_NULL_HANDLE;
+ VkDebugReportCallbackEXT dbg_debug_report = VK_NULL_HANDLE;
- Error _create_validation_layers();
+ Error _obtain_vulkan_version();
Error _initialize_extensions();
+ Error _check_capabilities();
- VkBool32 _check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers);
+ VkBool32 _check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers);
static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData);
- Error _create_physical_device();
+ static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback(
+ VkDebugReportFlagsEXT flags,
+ VkDebugReportObjectTypeEXT objectType,
+ uint64_t object,
+ size_t location,
+ int32_t messageCode,
+ const char *pLayerPrefix,
+ const char *pMessage,
+ void *pUserData);
+
+ Error _create_instance();
- Error _initialize_queues(VkSurfaceKHR surface);
+ Error _create_physical_device(VkSurfaceKHR p_surface);
+
+ Error _initialize_queues(VkSurfaceKHR p_surface);
Error _create_device();
@@ -176,24 +249,33 @@ class VulkanContext {
protected:
virtual const char *_get_platform_surface_extension() const = 0;
- // Enabled via command line argument.
- bool use_validation_layers = false;
+ virtual Error _window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height);
- virtual Error _window_create(DisplayServer::WindowID p_window_id, VkSurfaceKHR p_surface, int p_width, int p_height);
+ virtual bool _use_validation_layers();
- VkInstance _get_instance() {
- return inst;
- }
+ Error _get_preferred_validation_layers(uint32_t *count, const char *const **names);
public:
+ 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; };
+
VkDevice get_device();
VkPhysicalDevice get_physical_device();
+ VkInstance get_instance() { return inst; }
int get_swapchain_image_count() const;
- uint32_t get_graphics_queue() const;
+ 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);
@@ -222,8 +304,12 @@ public:
String get_device_vendor_name() const;
String get_device_name() const;
+ RenderingDevice::DeviceType get_device_type() const;
String get_device_pipeline_cache_uuid() const;
+ void set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode);
+ DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const;
+
VulkanContext();
virtual ~VulkanContext();
};
diff --git a/drivers/vulkan/vulkan_hooks.h b/drivers/vulkan/vulkan_hooks.h
new file mode 100644
index 0000000000..3f244b4d45
--- /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
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 43c8722b06..c9609b469a 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,8 +35,60 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+#include <stdint.h> // INT32_MAX
+
#include <functiondiscoverykeys.h>
+// Define IAudioClient3 if not already defined by MinGW headers
+#if defined __MINGW32__ || defined __MINGW64__
+
+#ifndef __IAudioClient3_FWD_DEFINED__
+#define __IAudioClient3_FWD_DEFINED__
+
+typedef interface IAudioClient3 IAudioClient3;
+
+#endif // __IAudioClient3_FWD_DEFINED__
+
+#ifndef __IAudioClient3_INTERFACE_DEFINED__
+#define __IAudioClient3_INTERFACE_DEFINED__
+
+MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42")
+IAudioClient3 : public IAudioClient2 {
+public:
+ virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(
+ /* [annotation][in] */
+ _In_ const WAVEFORMATEX *pFormat,
+ /* [annotation][out] */
+ _Out_ UINT32 *pDefaultPeriodInFrames,
+ /* [annotation][out] */
+ _Out_ UINT32 *pFundamentalPeriodInFrames,
+ /* [annotation][out] */
+ _Out_ UINT32 *pMinPeriodInFrames,
+ /* [annotation][out] */
+ _Out_ UINT32 *pMaxPeriodInFrames) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(
+ /* [unique][annotation][out] */
+ _Out_ WAVEFORMATEX * *ppFormat,
+ /* [annotation][out] */
+ _Out_ UINT32 * pCurrentPeriodInFrames) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(
+ /* [annotation][in] */
+ _In_ DWORD StreamFlags,
+ /* [annotation][in] */
+ _In_ UINT32 PeriodInFrames,
+ /* [annotation][in] */
+ _In_ const WAVEFORMATEX *pFormat,
+ /* [annotation][in] */
+ _In_opt_ LPCGUID AudioSessionGuid) = 0;
+};
+__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42)
+
+#endif // __IAudioClient3_INTERFACE_DEFINED__
+
+#endif // __MINGW32__ || __MINGW64__
+
#ifndef PKEY_Device_FriendlyName
#undef DEFINE_PROPERTYKEY
@@ -51,6 +103,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
+const IID IID_IAudioClient3 = __uuidof(IAudioClient3);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
@@ -68,6 +121,12 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
static bool default_render_device_changed = false;
static bool default_capture_device_changed = false;
+// Silence warning due to a COM API weirdness (GH-35194).
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
class CMMNotificationClient : public IMMNotificationClient {
LONG _cRef = 1;
IMMDeviceEnumerator *_pEnumerator = nullptr;
@@ -109,7 +168,7 @@ public:
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
return S_OK;
- };
+ }
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
return S_OK;
@@ -136,6 +195,10 @@ public:
}
};
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
static CMMNotificationClient notif_client;
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) {
@@ -221,7 +284,22 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error");
}
- hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
+ using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input)
+ if (using_audio_client_3) {
+ hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
+ if (hr != S_OK) {
+ // IID_IAudioClient3 will never activate on OS versions before Windows 10.
+ // Older Windows versions should fall back gracefully.
+ using_audio_client_3 = false;
+ print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface");
+ } else {
+ print_verbose("WASAPI: Activated device using IAudioClient3 interface");
+ }
+ }
+ if (!using_audio_client_3) {
+ hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client);
+ }
+
SAFE_RELEASE(device)
if (reinit) {
@@ -232,6 +310,16 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
}
+ if (using_audio_client_3) {
+ AudioClientProperties audioProps{};
+ audioProps.cbSize = sizeof(AudioClientProperties);
+ audioProps.bIsOffload = FALSE;
+ audioProps.eCategory = AudioCategory_GameEffects;
+
+ hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps);
+ ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + ".");
+ }
+
hr = p_device->audio_client->GetMixFormat(&pwfex);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
@@ -285,15 +373,70 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
}
}
- DWORD streamflags = 0;
- if ((DWORD)mix_rate != pwfex->nSamplesPerSec) {
- streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
- pwfex->nSamplesPerSec = mix_rate;
- pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8);
- }
+ if (!using_audio_client_3) {
+ DWORD streamflags = 0;
+ if ((DWORD)mix_rate != pwfex->nSamplesPerSec) {
+ streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
+ pwfex->nSamplesPerSec = mix_rate;
+ pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8);
+ }
+ hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
+ ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + ".");
+ UINT32 max_frames;
+ hr = p_device->audio_client->GetBufferSize(&max_frames);
+ ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
+
+ // Due to WASAPI Shared Mode we have no control of the buffer size
+ if (!p_capture) {
+ buffer_frames = max_frames;
- hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
- ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + ".");
+ int64_t latency = 0;
+ audio_output.audio_client->GetStreamLatency(&latency);
+ // WASAPI REFERENCE_TIME units are 100 nanoseconds per unit
+ // https://docs.microsoft.com/en-us/windows/win32/directshow/reference-time
+ // Convert REFTIME to seconds as godot uses for latency
+ real_latency = (float)latency / (float)REFTIMES_PER_SEC;
+ }
+
+ } else {
+ IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client;
+
+ // AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use
+ // the closest supported mix rate supported by the audio driver.
+ mix_rate = pwfex->nSamplesPerSec;
+ print_verbose("WASAPI: mix_rate = " + itos(mix_rate));
+
+ UINT32 default_period_frames, fundamental_period_frames, min_period_frames, max_period_frames;
+ hr = device_audio_client_3->GetSharedModeEnginePeriod(
+ pwfex,
+ &default_period_frames,
+ &fundamental_period_frames,
+ &min_period_frames,
+ &max_period_frames);
+ ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ".");
+
+ // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail,
+ // so we need to select the closest multiple to the user-specified latency.
+ UINT32 desired_period_frames = target_latency_ms * mix_rate / 1000;
+ UINT32 period_frames = (desired_period_frames / fundamental_period_frames) * fundamental_period_frames;
+ if (ABS((int64_t)period_frames - (int64_t)desired_period_frames) > ABS((int64_t)(period_frames + fundamental_period_frames) - (int64_t)desired_period_frames)) {
+ period_frames = period_frames + fundamental_period_frames;
+ }
+ period_frames = CLAMP(period_frames, min_period_frames, max_period_frames);
+ print_verbose("WASAPI: fundamental_period_frames = " + itos(fundamental_period_frames));
+ print_verbose("WASAPI: min_period_frames = " + itos(min_period_frames));
+ print_verbose("WASAPI: max_period_frames = " + itos(max_period_frames));
+ print_verbose("WASAPI: selected a period frame size of " + itos(period_frames));
+ buffer_frames = period_frames;
+
+ hr = device_audio_client_3->InitializeSharedAudioStream(0, period_frames, pwfex, nullptr);
+ ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ".");
+ uint32_t output_latency_in_frames;
+ WAVEFORMATEX *current_pwfex;
+ device_audio_client_3->GetCurrentSharedModeEnginePeriod(&current_pwfex, &output_latency_in_frames);
+ real_latency = (float)output_latency_in_frames / (float)current_pwfex->nSamplesPerSec;
+ CoTaskMemFree(current_pwfex);
+ }
if (p_capture) {
hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client);
@@ -311,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
@@ -328,13 +472,6 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) {
break;
}
- UINT32 max_frames;
- HRESULT hr = audio_output.audio_client->GetBufferSize(&max_frames);
- ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
-
- // Due to WASAPI Shared Mode we have no control of the buffer size
- buffer_frames = max_frames;
-
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
samples_in.resize(buffer_frames * channels);
@@ -349,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;
@@ -367,7 +505,6 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) {
if (p_device->audio_client) {
p_device->audio_client->Stop();
}
-
p_device->active = false;
}
@@ -389,6 +526,8 @@ Error AudioDriverWASAPI::finish_capture_device() {
Error AudioDriverWASAPI::init() {
mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ target_latency_ms = GLOBAL_GET("audio/driver/output_latency");
+
Error err = init_render_device();
if (err != OK) {
ERR_PRINT("WASAPI: init_render_device error");
@@ -406,6 +545,10 @@ int AudioDriverWASAPI::get_mix_rate() const {
return mix_rate;
}
+float AudioDriverWASAPI::get_latency() {
+ return real_latency;
+}
+
AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index b9b325f0fb..89ed90e97b 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,6 +39,7 @@
#include <audioclient.h>
#include <mmdeviceapi.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class AudioDriverWASAPI : public AudioDriver {
@@ -71,6 +72,9 @@ class AudioDriverWASAPI : public AudioDriver {
unsigned int channels = 0;
int mix_rate = 0;
int buffer_frames = 0;
+ int target_latency_ms = 0;
+ float real_latency = 0.0;
+ bool using_audio_client_3 = false;
bool thread_exited = false;
mutable bool exit_thread = false;
@@ -97,6 +101,7 @@ public:
virtual Error init();
virtual void start();
virtual int get_mix_rate() const;
+ virtual float get_latency();
virtual SpeakerMode get_speaker_mode() const;
virtual Array get_device_list();
virtual String get_device();
@@ -114,5 +119,5 @@ public:
AudioDriverWASAPI();
};
+#endif // WASAPI_ENABLED
#endif // AUDIO_DRIVER_WASAPI_H
-#endif
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 2c9f28717d..6f3bad12c1 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,7 @@
#include <stdio.h>
#include <wchar.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/*
@@ -132,7 +133,7 @@ Error DirAccessWindows::change_dir(String p_dir) {
bool worked = (SetCurrentDirectoryW((LPCWSTR)(p_dir.utf16().get_data())) != 0);
String base = _get_root_path();
- if (base != "") {
+ if (!base.is_empty()) {
GetCurrentDirectoryW(2048, real_current_dir_name);
String new_dir = String::utf16((const char16_t *)real_current_dir_name).replace("\\", "/");
if (!new_dir.begins_with(base)) {
@@ -155,7 +156,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
GLOBAL_LOCK_FUNCTION
p_dir = fix_path(p_dir);
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
p_dir = current_dir.plus_file(p_dir);
}
@@ -164,8 +165,11 @@ Error DirAccessWindows::make_dir(String p_dir) {
bool success;
int err;
- p_dir = "\\\\?\\" + p_dir; //done according to
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
+ if (!p_dir.is_network_share_path()) {
+ p_dir = "\\\\?\\" + p_dir;
+ // Add "\\?\" to the path to extend max. path length past 248, if it's not a network share UNC path.
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa363855(v=vs.85).aspx
+ }
success = CreateDirectoryW((LPCWSTR)(p_dir.utf16().get_data()), nullptr);
err = GetLastError();
@@ -183,7 +187,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
String DirAccessWindows::get_current_dir(bool p_include_drive) {
String base = _get_root_path();
- if (base != "") {
+ if (!base.is_empty()) {
String bd = current_dir.replace("\\", "/").replace_first(base, "");
if (bd.begins_with("/")) {
return _get_root_string() + bd.substr(1, bd.length());
@@ -195,10 +199,10 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) {
if (p_include_drive) {
return current_dir;
} else {
- if (_get_root_string() == "") {
+ if (_get_root_string().is_empty()) {
int p = current_dir.find(":");
if (p != -1) {
- return current_dir.right(p + 1);
+ return current_dir.substr(p + 1);
}
}
return current_dir;
@@ -208,7 +212,7 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) {
bool DirAccessWindows::file_exists(String p_file) {
GLOBAL_LOCK_FUNCTION
- if (!p_file.is_abs_path()) {
+ if (!p_file.is_absolute_path()) {
p_file = get_current_dir().plus_file(p_file);
}
@@ -227,7 +231,7 @@ bool DirAccessWindows::file_exists(String p_file) {
bool DirAccessWindows::dir_exists(String p_dir) {
GLOBAL_LOCK_FUNCTION
- if (p_dir.is_rel_path()) {
+ if (p_dir.is_relative_path()) {
p_dir = get_current_dir().plus_file(p_dir);
}
@@ -242,13 +246,13 @@ bool DirAccessWindows::dir_exists(String p_dir) {
}
Error DirAccessWindows::rename(String p_path, String p_new_path) {
- if (p_path.is_rel_path()) {
+ if (p_path.is_relative_path()) {
p_path = get_current_dir().plus_file(p_path);
}
p_path = fix_path(p_path);
- if (p_new_path.is_rel_path()) {
+ if (p_new_path.is_relative_path()) {
p_new_path = get_current_dir().plus_file(p_new_path);
}
@@ -256,6 +260,11 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) {
// If we're only changing file name case we need to do a little juggling
if (p_path.to_lower() == p_new_path.to_lower()) {
+ if (dir_exists(p_path)) {
+ // The path is a dir; just rename
+ return ::_wrename((LPCWSTR)(p_path.utf16().get_data()), (LPCWSTR)(p_new_path.utf16().get_data())) == 0 ? OK : FAILED;
+ }
+ // The path is a file; juggle
WCHAR tmpfile[MAX_PATH];
if (!GetTempFileNameW((LPCWSTR)(fix_path(get_current_dir()).utf16().get_data()), nullptr, 0, tmpfile)) {
@@ -281,7 +290,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) {
}
Error DirAccessWindows::remove(String p_path) {
- if (p_path.is_rel_path()) {
+ if (p_path.is_relative_path()) {
p_path = get_current_dir().plus_file(p_path);
}
@@ -325,14 +334,15 @@ FileType DirAccessWindows::get_file_type(const String& p_file) const {
}
*/
-size_t DirAccessWindows::get_space_left() {
+
+uint64_t DirAccessWindows::get_space_left() {
uint64_t bytes = 0;
if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) {
return 0;
}
//this is either 0 or a value in bytes.
- return (size_t)bytes;
+ return bytes;
}
String DirAccessWindows::get_filesystem_type() const {
@@ -342,6 +352,10 @@ String DirAccessWindows::get_filesystem_type() const {
ERR_FAIL_COND_V(unit_end == -1, String());
String unit = path.substr(0, unit_end + 1) + "\\";
+ if (path.is_network_share_path()) {
+ return "Network Share";
+ }
+
WCHAR szVolumeName[100];
WCHAR szFileSystemName[10];
DWORD dwSerialNumber = 0;
diff --git a/drivers/windows/dir_access_windows.h b/drivers/windows/dir_access_windows.h
index 7f10023470..78d37074e5 100644
--- a/drivers/windows/dir_access_windows.h
+++ b/drivers/windows/dir_access_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,11 +33,7 @@
#ifdef WINDOWS_ENABLED
-#include "core/os/dir_access.h"
-
-/**
- @author Juan Linietsky <reduz@gmail.com>
-*/
+#include "core/io/dir_access.h"
struct DirAccessWindowsPrivate;
@@ -78,8 +74,11 @@ public:
virtual Error rename(String p_path, String p_new_path);
virtual Error remove(String p_path);
- //virtual FileType get_file_type() const;
- size_t get_space_left();
+ 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; };
+
+ uint64_t get_space_left();
virtual String get_filesystem_type() const;
@@ -87,6 +86,6 @@ public:
~DirAccessWindows();
};
-#endif //WINDOWS_ENABLED
+#endif // WINDOWS_ENABLED
-#endif
+#endif // DIR_ACCESS_WINDOWS_H
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 35f61c0623..59dc1d8e77 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,9 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include <share.h> // _SH_DENYNO
#include <shlwapi.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <errno.h>
@@ -77,15 +79,15 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
return ERR_INVALID_PARAMETER;
}
- /* pretty much every implementation that uses fopen as primary
- backend supports utf8 encoding */
+ /* Pretty much every implementation that uses fopen as primary
+ backend supports utf8 encoding. */
struct _stat st;
if (_wstat((LPCWSTR)(path.utf16().get_data()), &st) == 0) {
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
@@ -97,7 +99,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d);
if (f != INVALID_HANDLE_VALUE) {
String fname = String::utf16((const char16_t *)(d.cFileName));
- if (fname != String()) {
+ if (!fname.is_empty()) {
String base_file = path.get_file();
if (base_file != fname && base_file.findn(fname) == 0) {
WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
@@ -108,15 +110,15 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
}
#endif
- if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) {
+ if (is_backup_save_enabled() && p_mode_flags == WRITE) {
save_path = path;
path = path + ".tmp";
}
- errno_t errcode = _wfopen_s(&f, (LPCWSTR)(path.utf16().get_data()), mode_string);
+ f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, _SH_DENYNO);
if (f == nullptr) {
- switch (errcode) {
+ switch (errno) {
case ENOENT: {
last_error = ERR_FILE_NOT_FOUND;
} break;
@@ -140,7 +142,7 @@ void FileAccessWindows::close() {
fclose(f);
f = nullptr;
- if (save_path != "") {
+ if (!save_path.is_empty()) {
bool rename_error = true;
int attempts = 4;
while (rename_error && attempts) {
@@ -157,10 +159,10 @@ void FileAccessWindows::close() {
#else
if (!PathFileExistsW((LPCWSTR)(save_path.utf16().get_data()))) {
#endif
- //creating new file
+ // Creating new file
rename_error = _wrename((LPCWSTR)((save_path + ".tmp").utf16().get_data()), (LPCWSTR)(save_path.utf16().get_data())) != 0;
} else {
- //atomic replace for existing file
+ // Atomic replace for existing file
rename_error = !ReplaceFileW((LPCWSTR)(save_path.utf16().get_data()), (LPCWSTR)((save_path + ".tmp").utf16().get_data()), nullptr, 2 | 4, nullptr, nullptr);
}
if (rename_error) {
@@ -193,10 +195,11 @@ bool FileAccessWindows::is_open() const {
return (f != nullptr);
}
-void FileAccessWindows::seek(size_t p_position) {
+void FileAccessWindows::seek(uint64_t p_position) {
ERR_FAIL_COND(!f);
+
last_error = OK;
- if (fseek(f, p_position, SEEK_SET)) {
+ if (_fseeki64(f, p_position, SEEK_SET)) {
check_errors();
}
prev_op = 0;
@@ -204,28 +207,28 @@ void FileAccessWindows::seek(size_t p_position) {
void FileAccessWindows::seek_end(int64_t p_position) {
ERR_FAIL_COND(!f);
- if (fseek(f, p_position, SEEK_END)) {
+
+ if (_fseeki64(f, p_position, SEEK_END)) {
check_errors();
}
prev_op = 0;
}
-size_t FileAccessWindows::get_position() const {
- size_t aux_position = 0;
- aux_position = ftell(f);
- if (!aux_position) {
+uint64_t FileAccessWindows::get_position() const {
+ int64_t aux_position = _ftelli64(f);
+ if (aux_position < 0) {
check_errors();
}
return aux_position;
}
-size_t FileAccessWindows::get_len() const {
+uint64_t FileAccessWindows::get_length() const {
ERR_FAIL_COND_V(!f, 0);
- size_t pos = get_position();
- fseek(f, 0, SEEK_END);
- int size = get_position();
- fseek(f, pos, SEEK_SET);
+ uint64_t pos = get_position();
+ _fseeki64(f, 0, SEEK_END);
+ uint64_t size = get_position();
+ _fseeki64(f, pos, SEEK_SET);
return size;
}
@@ -237,6 +240,7 @@ bool FileAccessWindows::eof_reached() const {
uint8_t FileAccessWindows::get_8() const {
ERR_FAIL_COND_V(!f, 0);
+
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == WRITE) {
fflush(f);
@@ -252,18 +256,20 @@ uint8_t FileAccessWindows::get_8() const {
return b;
}
-int FileAccessWindows::get_buffer(uint8_t *p_dst, int p_length) const {
+uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+ ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V(!f, -1);
+
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == WRITE) {
fflush(f);
}
prev_op = READ;
}
- int read = fread(p_dst, 1, p_length, f);
+ uint64_t read = fread(p_dst, 1, p_length, f);
check_errors();
return read;
-};
+}
Error FileAccessWindows::get_error() const {
return last_error;
@@ -271,6 +277,7 @@ Error FileAccessWindows::get_error() const {
void FileAccessWindows::flush() {
ERR_FAIL_COND(!f);
+
fflush(f);
if (prev_op == WRITE) {
prev_op = 0;
@@ -279,6 +286,7 @@ void FileAccessWindows::flush() {
void FileAccessWindows::store_8(uint8_t p_dest) {
ERR_FAIL_COND(!f);
+
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == READ) {
if (last_error != ERR_FILE_EOF) {
@@ -290,8 +298,10 @@ void FileAccessWindows::store_8(uint8_t p_dest) {
fwrite(&p_dest, 1, 1, f);
}
-void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) {
+void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND(!f);
+ ERR_FAIL_COND(!p_src && p_length > 0);
+
if (flags == READ_WRITE || flags == WRITE_READ) {
if (prev_op == READ) {
if (last_error != ERR_FILE_EOF) {
@@ -304,10 +314,8 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, int p_length) {
}
bool FileAccessWindows::file_exists(const String &p_name) {
- FILE *g;
- //printf("opening file %s\n", p_fname.utf8().get_data());
String filename = fix_path(p_name);
- _wfopen_s(&g, (LPCWSTR)(filename.utf16().get_data()), L"rb");
+ FILE *g = _wfsopen((LPCWSTR)(filename.utf16().get_data()), L"rb", _SH_DENYNO);
if (g == nullptr) {
return false;
} else {
@@ -318,8 +326,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);
@@ -327,7 +336,8 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
if (rv == 0) {
return st.st_mtime;
} else {
- ERR_FAIL_V_MSG(0, "Failed to get modified time for: " + file + ".");
+ print_verbose("Failed to get modified time for: " + p_file + "");
+ return 0;
}
}
diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h
index 507e0b2c20..93d37c3b5a 100644
--- a/drivers/windows/file_access_windows.h
+++ b/drivers/windows/file_access_windows.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifdef WINDOWS_ENABLED
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/memory.h"
#include <stdio.h>
@@ -56,21 +56,21 @@ public:
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 void seek(size_t p_position); ///< seek to a given position
+ 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 size_t get_position() const; ///< get position in the file
- virtual size_t get_len() const; ///< get size of the file
+ virtual uint64_t get_position() const; ///< get position in the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
virtual uint8_t get_8() const; ///< get a byte
- virtual int get_buffer(uint8_t *p_dst, int p_length) const;
+ virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;
virtual Error get_error() const; ///< 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, int p_length); ///< store an array of bytes
+ virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
virtual bool file_exists(const String &p_name); ///< return true if a file exists
diff --git a/drivers/winmidi/midi_driver_winmidi.cpp b/drivers/winmidi/midi_driver_winmidi.cpp
index 730d608bbf..64912dc3af 100644
--- a/drivers/winmidi/midi_driver_winmidi.cpp
+++ b/drivers/winmidi/midi_driver_winmidi.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/winmidi/midi_driver_winmidi.h b/drivers/winmidi/midi_driver_winmidi.h
index bb9a87d610..6572ba0c16 100644
--- a/drivers/winmidi/midi_driver_winmidi.h
+++ b/drivers/winmidi/midi_driver_winmidi.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,7 @@
#include "core/templates/vector.h"
#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
diff --git a/drivers/xaudio2/audio_driver_xaudio2.cpp b/drivers/xaudio2/audio_driver_xaudio2.cpp
index 1c7bf5d6c6..03fdfda23d 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.cpp
+++ b/drivers/xaudio2/audio_driver_xaudio2.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/drivers/xaudio2/audio_driver_xaudio2.h b/drivers/xaudio2/audio_driver_xaudio2.h
index d3938a19d0..9072269a0e 100644
--- a/drivers/xaudio2/audio_driver_xaudio2.h
+++ b/drivers/xaudio2/audio_driver_xaudio2.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,6 +36,7 @@
#include "servers/audio_server.h"
#include <mmsystem.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wrl/client.h>
#include <xaudio2.h>