summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/SCsub3
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp2
-rw-r--r--drivers/coreaudio/SCsub2
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h7
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.mm (renamed from drivers/coreaudio/audio_driver_coreaudio.cpp)71
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp8
-rw-r--r--drivers/egl/egl_manager.cpp24
-rw-r--r--drivers/egl/egl_manager.h2
-rw-r--r--drivers/gles3/effects/feed_effects.cpp128
-rw-r--r--drivers/gles3/effects/feed_effects.h68
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp131
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h38
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp3
-rw-r--r--drivers/gles3/rasterizer_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp43
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h1
-rw-r--r--drivers/gles3/shader_gles3.cpp4
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/canvas.glsl57
-rw-r--r--drivers/gles3/shaders/canvas_uniforms_inc.glsl37
-rw-r--r--drivers/gles3/shaders/feed.glsl39
-rw-r--r--drivers/gles3/shaders/scene.glsl22
-rw-r--r--drivers/gles3/shaders/sky.glsl6
-rw-r--r--drivers/gles3/shaders/stdlib_inc.glsl40
-rw-r--r--drivers/gles3/storage/config.cpp7
-rw-r--r--drivers/gles3/storage/config.h11
-rw-r--r--drivers/gles3/storage/light_storage.cpp17
-rw-r--r--drivers/gles3/storage/light_storage.h25
-rw-r--r--drivers/gles3/storage/material_storage.cpp4
-rw-r--r--drivers/gles3/storage/material_storage.h8
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp4
-rw-r--r--drivers/gles3/storage/mesh_storage.h17
-rw-r--r--drivers/gles3/storage/particles_storage.h2
-rw-r--r--drivers/gles3/storage/texture_storage.cpp65
-rw-r--r--drivers/gles3/storage/texture_storage.h18
-rw-r--r--drivers/gles3/storage/utilities.h4
-rw-r--r--drivers/metal/metal_objects.h312
-rw-r--r--drivers/metal/metal_objects.mm284
-rw-r--r--drivers/metal/rendering_context_driver_metal.h2
-rw-r--r--drivers/metal/rendering_context_driver_metal.mm8
-rw-r--r--drivers/metal/rendering_device_driver_metal.h11
-rw-r--r--drivers/metal/rendering_device_driver_metal.mm103
-rw-r--r--drivers/png/SCsub16
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h2
-rw-r--r--drivers/unix/file_access_unix.cpp43
-rw-r--r--drivers/unix/file_access_unix.h4
-rw-r--r--drivers/unix/file_access_unix_pipe.cpp2
-rw-r--r--drivers/unix/ip_unix.cpp75
-rw-r--r--drivers/unix/ip_unix.h6
-rw-r--r--drivers/unix/net_socket_unix.cpp (renamed from drivers/unix/net_socket_posix.cpp)328
-rw-r--r--drivers/unix/net_socket_unix.h (renamed from drivers/unix/net_socket_posix.h)80
-rw-r--r--drivers/unix/os_unix.cpp97
-rw-r--r--drivers/unix/os_unix.h6
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp492
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h28
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp2
-rw-r--r--drivers/windows/dir_access_windows.cpp6
-rw-r--r--drivers/windows/file_access_windows.cpp6
-rw-r--r--drivers/windows/file_access_windows_pipe.cpp20
-rw-r--r--drivers/windows/file_access_windows_pipe.h2
-rw-r--r--drivers/windows/ip_windows.cpp164
-rw-r--r--drivers/windows/ip_windows.h54
-rw-r--r--drivers/windows/net_socket_winsock.cpp613
-rw-r--r--drivers/windows/net_socket_winsock.h102
64 files changed, 2837 insertions, 952 deletions
diff --git a/drivers/SCsub b/drivers/SCsub
index e0bfa138f5..03ad70649b 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -14,7 +14,8 @@ SConscript("windows/SCsub")
# Sounds drivers
SConscript("alsa/SCsub")
-SConscript("coreaudio/SCsub")
+if env["platform"] == "ios" or env["platform"] == "macos":
+ SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 0c9635339b..6ee22b6eb5 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -80,7 +80,7 @@ Error AudioDriverALSA::init_output_device() {
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
} else {
String device = output_device_name;
- int pos = device.find(";");
+ int pos = device.find_char(';');
if (pos != -1) {
device = device.substr(0, pos);
}
diff --git a/drivers/coreaudio/SCsub b/drivers/coreaudio/SCsub
index 69d667c57b..83ac27f4b6 100644
--- a/drivers/coreaudio/SCsub
+++ b/drivers/coreaudio/SCsub
@@ -4,4 +4,4 @@ from misc.utility.scons_hints import *
Import("env")
# Driver source files
-env.add_source_files(env.drivers_sources, "*.cpp")
+env.add_source_files(env.drivers_sources, "*.mm")
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index 67ff3f3efc..54e02514ec 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -38,6 +38,8 @@
#import <AudioUnit/AudioUnit.h>
#ifdef MACOS_ENABLED
#import <CoreAudio/AudioHardware.h>
+#else
+#import <AVFoundation/AVFoundation.h>
#endif
class AudioDriverCoreAudio : public AudioDriver {
@@ -51,9 +53,11 @@ class AudioDriverCoreAudio : public AudioDriver {
String input_device_name = "Default";
int mix_rate = 0;
+ int capture_mix_rate = 0;
unsigned int channels = 2;
unsigned int capture_channels = 2;
unsigned int buffer_frames = 0;
+ unsigned int capture_buffer_frames = 0;
Vector<int32_t> samples_in;
Vector<int16_t> input_buf;
@@ -89,11 +93,12 @@ class AudioDriverCoreAudio : public AudioDriver {
public:
virtual const char *get_name() const override {
return "CoreAudio";
- };
+ }
virtual Error init() override;
virtual void start() override;
virtual int get_mix_rate() const override;
+ virtual int get_input_mix_rate() const override;
virtual SpeakerMode get_speaker_mode() const override;
virtual void lock() override;
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.mm
index fd0adb1fd1..4e6ff2edb3 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.mm
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* audio_driver_coreaudio.cpp */
+/* audio_driver_coreaudio.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -121,7 +121,24 @@ Error AudioDriverCoreAudio::init() {
break;
}
- mix_rate = _get_configured_mix_rate();
+#ifdef MACOS_ENABLED
+ AudioDeviceID device_id;
+ UInt32 dev_id_size = sizeof(AudioDeviceID);
+
+ AudioObjectPropertyAddress property_dev_id = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_dev_id, 0, nullptr, &dev_id_size, &device_id);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+
+ double hw_mix_rate;
+ UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
+
+ AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
+ result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+#else
+ double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
+#endif
+ mix_rate = hw_mix_rate;
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
@@ -147,10 +164,10 @@ Error AudioDriverCoreAudio::init() {
unsigned int buffer_size = buffer_frames * channels;
samples_in.resize(buffer_size);
- input_buf.resize(buffer_size);
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
- print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
+ print_verbose("CoreAudio: output sampling rate: " + itos(mix_rate) + " Hz");
+ print_verbose("CoreAudio: output audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
AURenderCallbackStruct callback;
memset(&callback, 0, sizeof(AURenderCallbackStruct));
@@ -250,7 +267,7 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
}
void AudioDriverCoreAudio::start() {
- if (!active) {
+ if (!active && audio_unit != nullptr) {
OSStatus result = AudioOutputUnitStart(audio_unit);
if (result != noErr) {
ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result));
@@ -275,6 +292,10 @@ int AudioDriverCoreAudio::get_mix_rate() const {
return mix_rate;
}
+int AudioDriverCoreAudio::get_input_mix_rate() const {
+ return capture_mix_rate;
+}
+
AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
@@ -378,14 +399,14 @@ Error AudioDriverCoreAudio::init_input_device() {
UInt32 size;
#ifdef MACOS_ENABLED
- AudioDeviceID deviceId;
+ AudioDeviceID device_id;
size = sizeof(AudioDeviceID);
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
- result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
+ result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
ERR_FAIL_COND_V(result != noErr, FAILED);
- result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+ result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
ERR_FAIL_COND_V(result != noErr, FAILED);
#endif
@@ -410,13 +431,23 @@ Error AudioDriverCoreAudio::init_input_device() {
break;
}
- mix_rate = _get_configured_mix_rate();
+#ifdef MACOS_ENABLED
+ double hw_mix_rate;
+ UInt32 hw_mix_rate_size = sizeof(hw_mix_rate);
+
+ AudioObjectPropertyAddress property_sr = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
+ result = AudioObjectGetPropertyData(device_id, &property_sr, 0, nullptr, &hw_mix_rate_size, &hw_mix_rate);
+ ERR_FAIL_COND_V(result != noErr, FAILED);
+#else
+ double hw_mix_rate = [AVAudioSession sharedInstance].sampleRate;
+#endif
+ capture_mix_rate = hw_mix_rate;
memset(&strdesc, 0, sizeof(strdesc));
strdesc.mFormatID = kAudioFormatLinearPCM;
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
strdesc.mChannelsPerFrame = capture_channels;
- strdesc.mSampleRate = mix_rate;
+ strdesc.mSampleRate = capture_mix_rate;
strdesc.mFramesPerPacket = 1;
strdesc.mBitsPerChannel = 16;
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
@@ -425,6 +456,13 @@ Error AudioDriverCoreAudio::init_input_device() {
result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
ERR_FAIL_COND_V(result != noErr, FAILED);
+ int latency = Engine::get_singleton()->get_audio_output_latency();
+ // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
+ capture_buffer_frames = closest_power_of_2(latency * capture_mix_rate / 1000);
+
+ unsigned int buffer_size = capture_buffer_frames * capture_channels;
+ input_buf.resize(buffer_size);
+
AURenderCallbackStruct callback;
memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = &AudioDriverCoreAudio::input_callback;
@@ -435,6 +473,9 @@ Error AudioDriverCoreAudio::init_input_device() {
result = AudioUnitInitialize(input_unit);
ERR_FAIL_COND_V(result != noErr, FAILED);
+ print_verbose("CoreAudio: input sampling rate: " + itos(capture_mix_rate) + " Hz");
+ print_verbose("CoreAudio: input audio buffer frames: " + itos(capture_buffer_frames) + " calculated latency: " + itos(capture_buffer_frames * 1000 / capture_mix_rate) + "ms");
+
return OK;
}
@@ -477,7 +518,7 @@ void AudioDriverCoreAudio::finish_input_device() {
}
Error AudioDriverCoreAudio::input_start() {
- input_buffer_init(buffer_frames);
+ input_buffer_init(capture_buffer_frames);
OSStatus result = AudioOutputUnitStart(input_unit);
if (result != noErr) {
@@ -561,7 +602,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
}
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
- AudioDeviceID deviceId;
+ AudioDeviceID device_id;
bool found = false;
if (output_device != "Default") {
AudioObjectPropertyAddress prop;
@@ -608,7 +649,7 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
if (name == output_device) {
- deviceId = audioDevices[i];
+ device_id = audioDevices[i];
found = true;
}
}
@@ -626,14 +667,14 @@ void AudioDriverCoreAudio::_set_device(const String &output_device, bool input)
UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
- OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &device_id);
ERR_FAIL_COND(result != noErr);
found = true;
}
if (found) {
- OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+ OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &device_id, sizeof(AudioDeviceID));
ERR_FAIL_COND(result != noErr);
if (input) {
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 479afbba93..0b1b0651c8 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -2160,6 +2160,7 @@ RDD::FenceID RenderingDeviceDriverD3D12::fence_create() {
Error RenderingDeviceDriverD3D12::fence_wait(FenceID p_fence) {
FenceInfo *fence = (FenceInfo *)(p_fence.id);
+ fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle);
DWORD res = WaitForSingleObjectEx(fence->event_handle, INFINITE, FALSE);
#ifdef PIX_ENABLED
PIXNotifyWakeFromFenceSignal(fence->event_handle);
@@ -2254,7 +2255,6 @@ Error RenderingDeviceDriverD3D12::command_queue_execute_and_present(CommandQueue
FenceInfo *fence = (FenceInfo *)(p_cmd_fence.id);
fence->fence_value++;
command_queue->d3d_queue->Signal(fence->d3d_fence.Get(), fence->fence_value);
- fence->d3d_fence->SetEventOnCompletion(fence->fence_value, fence->event_handle);
}
}
@@ -2461,8 +2461,6 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
- print_verbose("Using swap chain flags: " + itos(creation_flags) + ", sync interval: " + itos(sync_interval) + ", present flags: " + itos(present_flags));
-
if (swap_chain->d3d_swap_chain != nullptr && creation_flags != swap_chain->creation_flags) {
// The swap chain must be recreated if the creation flags are different.
_swap_chain_release(swap_chain);
@@ -6179,6 +6177,8 @@ uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) {
return false;
case API_TRAIT_USE_GENERAL_IN_COPY_QUEUES:
return true;
+ case API_TRAIT_BUFFERS_REQUIRE_TRANSITIONS:
+ return !barrier_capabilities.enhanced_barriers_supported;
default:
return RenderingDeviceDriver::api_trait_get(p_trait);
}
@@ -6585,7 +6585,7 @@ static Error create_command_signature(ID3D12Device *device, D3D12_INDIRECT_ARGUM
HRESULT res = device->CreateCommandSignature(&cs_desc, nullptr, IID_PPV_ARGS(r_cmd_sig->GetAddressOf()));
ERR_FAIL_COND_V_MSG(!SUCCEEDED(res), ERR_CANT_CREATE, "CreateCommandSignature failed with error " + vformat("0x%08ux", (uint64_t)res) + ".");
return OK;
-};
+}
Error RenderingDeviceDriverD3D12::_initialize_frames(uint32_t p_frame_count) {
Error err;
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 603dfadd4b..8ca0aa5943 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -414,6 +414,30 @@ EGLContext EGLManager::get_context(DisplayServer::WindowID p_window_id) {
return display.egl_context;
}
+EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) {
+ GLWindow &glwindow = windows[p_window_id];
+
+ if (!glwindow.initialized) {
+ return EGL_NO_CONTEXT;
+ }
+
+ GLDisplay &display = displays[glwindow.gldisplay_id];
+
+ return display.egl_display;
+}
+
+EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) {
+ GLWindow &glwindow = windows[p_window_id];
+
+ if (!glwindow.initialized) {
+ return nullptr;
+ }
+
+ GLDisplay &display = displays[glwindow.gldisplay_id];
+
+ return display.egl_config;
+}
+
Error EGLManager::initialize(void *p_native_display) {
#if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
// Loading EGL with a new display gets us just the bare minimum API. We'll then
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index f1b3dc99b7..2e1ae6ec53 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -113,6 +113,8 @@ public:
bool is_using_vsync() const;
EGLContext get_context(DisplayServer::WindowID p_window_id);
+ EGLDisplay get_display(DisplayServer::WindowID p_window_id);
+ EGLConfig get_config(DisplayServer::WindowID p_window_id);
Error initialize(void *p_native_display = nullptr);
diff --git a/drivers/gles3/effects/feed_effects.cpp b/drivers/gles3/effects/feed_effects.cpp
new file mode 100644
index 0000000000..8ca88da662
--- /dev/null
+++ b/drivers/gles3/effects/feed_effects.cpp
@@ -0,0 +1,128 @@
+/**************************************************************************/
+/* feed_effects.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "feed_effects.h"
+
+#ifdef ANDROID_ENABLED
+#include <GLES3/gl3ext.h>
+#endif
+
+#define GL_PROGRAM_POINT_SIZE 0x8642
+
+using namespace GLES3;
+
+FeedEffects *FeedEffects::singleton = nullptr;
+
+FeedEffects *FeedEffects::get_singleton() {
+ return singleton;
+}
+
+FeedEffects::FeedEffects() {
+ singleton = this;
+
+ feed.shader.initialize();
+ feed.shader_version = feed.shader.version_create();
+ feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT);
+
+ { // Screen Triangle.
+ glGenBuffers(1, &screen_triangle);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+
+ const float qv[6] = {
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &screen_triangle_array);
+ glBindVertexArray(screen_triangle_array);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+}
+
+FeedEffects::~FeedEffects() {
+ singleton = nullptr;
+ glDeleteBuffers(1, &screen_triangle);
+ glDeleteVertexArrays(1, &screen_triangle_array);
+ feed.shader.version_free(feed.shader_version);
+}
+
+Transform3D transform3D_from_mat4(const float *p_mat4) {
+ Transform3D res;
+
+ res.basis.rows[0][0] = p_mat4[0];
+ res.basis.rows[1][0] = p_mat4[1];
+ res.basis.rows[2][0] = p_mat4[2];
+ // p_mat4[3] = 0;
+ res.basis.rows[0][1] = p_mat4[4];
+ res.basis.rows[1][1] = p_mat4[5];
+ res.basis.rows[2][1] = p_mat4[6];
+ // p_mat4[7] = 0;
+ res.basis.rows[0][2] = p_mat4[8];
+ res.basis.rows[1][2] = p_mat4[9];
+ res.basis.rows[2][2] = p_mat4[10];
+ // p_mat4[11] = 0;
+ res.origin.x = p_mat4[12];
+ res.origin.y = p_mat4[13];
+ res.origin.z = p_mat4[14];
+ // p_mat4[15] = 1;
+
+ return res;
+}
+
+void FeedEffects::draw() {
+ bool success = feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT, FeedShaderGLES3::USE_EXTERNAL_SAMPLER);
+ if (!success) {
+ OS::get_singleton()->print("Godot : FeedShaderGLES3 Could not bind version_bind_shader USE_EXTERNAL_SAMPLER");
+ return;
+ }
+
+ draw_screen_triangle();
+}
+
+void FeedEffects::draw_screen_triangle() {
+ glBindVertexArray(screen_triangle_array);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ glBindVertexArray(0);
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/effects/feed_effects.h b/drivers/gles3/effects/feed_effects.h
new file mode 100644
index 0000000000..5856a3e04b
--- /dev/null
+++ b/drivers/gles3/effects/feed_effects.h
@@ -0,0 +1,68 @@
+/**************************************************************************/
+/* feed_effects.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 FEED_EFFECTS_GLES3_H
+#define FEED_EFFECTS_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "drivers/gles3/shaders/feed.glsl.gen.h"
+
+namespace GLES3 {
+
+class FeedEffects {
+private:
+ struct Feed {
+ FeedShaderGLES3 shader;
+ RID shader_version;
+ } feed;
+
+ static FeedEffects *singleton;
+
+ GLuint screen_triangle = 0;
+ GLuint screen_triangle_array = 0;
+
+public:
+ static FeedEffects *get_singleton();
+
+ FeedEffects();
+ ~FeedEffects();
+
+ void draw();
+
+private:
+ void draw_screen_triangle();
+};
+
+} // namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // FEED_EFFECTS_GLES3_H
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 3c959f0143..5fd90744a4 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -687,6 +687,8 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.current_tex = RID();
+ const uint64_t base_specialization = GLES3::Config::get_singleton()->float_texture_supported ? 0 : CanvasShaderGLES3::USE_RGBA_SHADOWS;
+
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
// Skipping when there is no instances.
if (state.canvas_instance_batches[i].instance_count == 0) {
@@ -705,10 +707,9 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data;
- CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
- uint64_t specialization = 0;
- specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled);
- specialization |= uint64_t(!GLES3::Config::get_singleton()->float_texture_supported) << 1;
+ CanvasShaderGLES3::ShaderVariant variant = CanvasShaderGLES3::MODE_DEFAULT;
+ uint64_t specialization = state.canvas_instance_batches[i].specialization;
+ specialization |= base_specialization;
RID shader_version = data.canvas_shader_default_version;
if (material_data) {
@@ -719,11 +720,14 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
}
}
- bool success = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(shader_version, variant, specialization);
+ bool success = material_storage->shaders.canvas_shader.version_bind_shader(shader_version, variant, specialization);
if (!success) {
continue;
}
+ // Bind per-batch uniforms.
+ material_storage->shaders.canvas_shader.version_set_uniform(CanvasShaderGLES3::BATCH_FLAGS, state.canvas_instance_batches[i].flags, shader_version, variant, specialization);
+
GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
Color blend_color = state.canvas_instance_batches[i].blend_color;
@@ -810,6 +814,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
+ const uint64_t specialization_command_mask = ~(CanvasShaderGLES3::USE_NINEPATCH | CanvasShaderGLES3::USE_PRIMITIVE | CanvasShaderGLES3::USE_ATTRIBUTES | CanvasShaderGLES3::USE_INSTANCING);
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
_new_batch(r_batch_broken);
@@ -845,6 +850,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
uint32_t lights[4] = { 0, 0, 0, 0 };
uint16_t light_count = 0;
+ uint16_t shadow_mask = 0;
{
Light *light = p_lights;
@@ -854,6 +860,10 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
uint32_t light_index = light->render_index_cache;
lights[light_count >> 2] |= light_index << ((light_count & 3) * 8);
+ if (p_item->light_mask & light->item_shadow_mask) {
+ shadow_mask |= 1 << light_count;
+ }
+
light_count++;
if (light_count == data.max_lights_per_item - 1) {
@@ -863,14 +873,15 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
light = light->next_ptr;
}
- base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
+ base_flags |= light_count << INSTANCE_FLAGS_LIGHT_COUNT_SHIFT;
+ base_flags |= shadow_mask << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT;
}
bool lights_disabled = light_count == 0 && !state.using_directional_lights;
- if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) {
+ if (lights_disabled != bool(state.canvas_instance_batches[state.current_batch_index].specialization & CanvasShaderGLES3::DISABLE_LIGHTING)) {
_new_batch(r_batch_broken);
- state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled;
+ state.canvas_instance_batches[state.current_batch_index].specialization ^= CanvasShaderGLES3::DISABLE_LIGHTING;
}
const Item::Command *c = p_item->commands;
@@ -904,7 +915,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.instance_data_array[r_index].lights[2] = lights[2];
state.instance_data_array[r_index].lights[3] = lights[3];
- state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); // Reset on each command for safety, keep canvastexture binding config.
+ state.instance_data_array[r_index].flags = base_flags;
Color blend_color = base_color;
GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode;
@@ -936,7 +947,8 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = rect->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -959,20 +971,18 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
if (rect->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
- state.instance_data_array[r_index].flags |= FLAGS_FLIP_H;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
- state.instance_data_array[r_index].flags |= FLAGS_FLIP_V;
}
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
- state.instance_data_array[r_index].flags |= FLAGS_TRANSPOSE_RECT;
+ state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_TRANSPOSE_RECT;
}
if (rect->flags & CANVAS_RECT_CLIP_UV) {
- state.instance_data_array[r_index].flags |= FLAGS_CLIP_RECT_UV;
+ state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_CLIP_RECT_UV;
}
} else {
@@ -991,13 +1001,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
}
if (rect->flags & CANVAS_RECT_MSDF) {
- state.instance_data_array[r_index].flags |= FLAGS_USE_MSDF;
+ state.instance_data_array[r_index].flags |= INSTANCE_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.
} else if (rect->flags & CANVAS_RECT_LCD) {
- state.instance_data_array[r_index].flags |= FLAGS_USE_LCD;
+ state.instance_data_array[r_index].flags |= INSTANCE_FLAGS_USE_LCD;
}
state.instance_data_array[r_index].modulation[0] = rect->modulate.r * base_color.r;
@@ -1026,7 +1036,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = np->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_NINEPATCH;
+ state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1064,11 +1076,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
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;
+ state.instance_data_array[r_index].flags |= int(np->axis_x) << INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT;
+ state.instance_data_array[r_index].flags |= int(np->axis_y) << INSTANCE_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].flags |= INSTANCE_FLAGS_NINEPACH_DRAW_CENTER;
}
state.instance_data_array[r_index].ninepatch_margins[0] = np->margin[SIDE_LEFT];
@@ -1092,7 +1104,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].flags = 0;
_prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1119,7 +1133,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count;
state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE;
state.canvas_instance_batches[state.current_batch_index].command = c;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_PRIMITIVE;
+ state.canvas_instance_batches[state.current_batch_index].flags = 0;
}
_prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
@@ -1160,11 +1176,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
case Item::Command::TYPE_MESH:
case Item::Command::TYPE_MULTIMESH:
case Item::Command::TYPE_PARTICLES: {
- // Mesh's can't be batched, so always create a new batch
+ // Meshes can't be batched, so always create a new batch.
_new_batch(r_batch_broken);
Color modulate(1, 1, 1, 1);
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].specialization &= specialization_command_mask;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_ATTRIBUTES;
+ state.canvas_instance_batches[state.current_batch_index].flags = 0;
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = m->texture;
@@ -1174,13 +1192,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
state.canvas_instance_batches[state.current_batch_index].tex = mm->texture;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_colors(mm->multimesh)) {
- state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_COLORS;
}
if (GLES3::MeshStorage::get_singleton()->multimesh_uses_custom_data(mm->multimesh)) {
- state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA;
}
} else if (c->type == Item::Command::TYPE_PARTICLES) {
GLES3::ParticlesStorage *particles_storage = GLES3::ParticlesStorage::get_singleton();
@@ -1189,9 +1207,9 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
RID particles = pt->particles;
state.canvas_instance_batches[state.current_batch_index].tex = pt->texture;
- state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
- state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_COLORS;
- state.instance_data_array[r_index].flags |= FLAGS_INSTANCING_HAS_CUSTOM_DATA;
+ state.canvas_instance_batches[state.current_batch_index].specialization |= CanvasShaderGLES3::USE_INSTANCING;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_COLORS;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA;
if (particles_storage->particles_has_collision(particles) && texture_storage->render_target_is_sdf_enabled(p_render_target)) {
// Pass collision information.
@@ -1654,28 +1672,39 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c
return;
}
- for (int i = 0; i < 4; i++) {
- glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
+ Projection projection;
+ {
+ real_t fov = 90;
+ real_t nearp = p_near;
+ real_t farp = p_far;
+ real_t aspect = 1.0;
- Projection projection;
- {
- real_t fov = 90;
- real_t nearp = p_near;
- real_t farp = p_far;
- real_t aspect = 1.0;
+ real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
+ real_t ymin = -ymax;
+ real_t xmin = ymin * aspect;
+ real_t xmax = ymax * aspect;
- real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5));
- real_t ymin = -ymax;
- real_t xmin = ymin * aspect;
- real_t xmax = ymax * aspect;
+ projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
+ }
- projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp);
- }
+ // Precomputed:
+ // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse());
+ const Projection projections[4] = {
+ projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
+
+ projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
+
+ projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)),
- Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0));
+ projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1))
+
+ };
+
+ for (int i = 0; i < 4; i++) {
+ glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2);
- projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse());
- shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant);
+ shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projections[i], shadow_render.shader_version, variant);
static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) };
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant);
@@ -2347,15 +2376,15 @@ void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasIte
GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
if (ct->specular_color.a < 0.999) {
- state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED;
} else {
- state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED;
+ state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
if (normal_map) {
- state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
+ state.canvas_instance_batches[state.current_batch_index].flags |= BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED;
} else {
- state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED;
+ state.canvas_instance_batches[state.current_batch_index].flags &= ~BATCH_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;
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index a82e2713e0..e099fd0cc0 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -54,29 +54,27 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4);
enum {
+ INSTANCE_FLAGS_LIGHT_COUNT_SHIFT = 0, // 4 bits for light count.
- FLAGS_INSTANCING_MASK = 0x7F,
- FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
- FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
+ INSTANCE_FLAGS_CLIP_RECT_UV = (1 << 4),
+ INSTANCE_FLAGS_TRANSPOSE_RECT = (1 << 5),
+ INSTANCE_FLAGS_USE_MSDF = (1 << 6),
+ INSTANCE_FLAGS_USE_LCD = (1 << 7),
- FLAGS_CLIP_RECT_UV = (1 << 9),
- FLAGS_TRANSPOSE_RECT = (1 << 10),
+ INSTANCE_FLAGS_NINEPACH_DRAW_CENTER = (1 << 8),
+ INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT = 9,
+ INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT = 11,
- FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
-
- 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),
+ INSTANCE_FLAGS_SHADOW_MASKED_SHIFT = 13, // 16 bits.
+ };
- FLAGS_USE_MSDF = (1 << 28),
- FLAGS_USE_LCD = (1 << 29),
+ enum {
+ BATCH_FLAGS_INSTANCING_MASK = 0x7F,
+ BATCH_FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
+ BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
- FLAGS_FLIP_H = (1 << 30),
- FLAGS_FLIP_V = (1 << 31),
+ BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 9),
+ BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 10),
};
enum {
@@ -273,14 +271,14 @@ public:
RID material;
GLES3::CanvasMaterialData *material_data = nullptr;
- CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
+ uint64_t specialization = 0;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
uint32_t primitive_points = 0;
- bool lights_disabled = false;
+ uint32_t flags = 0;
};
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 6e508c6ebf..0fda42979f 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -35,6 +35,7 @@
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
@@ -217,6 +218,7 @@ void RasterizerGLES3::finalize() {
memdelete(glow);
memdelete(cubemap_filter);
memdelete(copy_effects);
+ memdelete(feed_effects);
memdelete(light_storage);
memdelete(particles_storage);
memdelete(mesh_storage);
@@ -365,6 +367,7 @@ RasterizerGLES3::RasterizerGLES3() {
cubemap_filter = memnew(GLES3::CubemapFilter);
glow = memnew(GLES3::Glow);
post_effects = memnew(GLES3::PostEffects);
+ feed_effects = memnew(GLES3::FeedEffects);
gi = memnew(GLES3::GI);
fog = memnew(GLES3::Fog);
canvas = memnew(RasterizerCanvasGLES3());
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 6765d8b4d5..abda2a5e06 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -35,6 +35,7 @@
#include "effects/copy_effects.h"
#include "effects/cubemap_filter.h"
+#include "effects/feed_effects.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
@@ -78,6 +79,7 @@ protected:
GLES3::CubemapFilter *cubemap_filter = nullptr;
GLES3::Glow *glow = nullptr;
GLES3::PostEffects *post_effects = nullptr;
+ GLES3::FeedEffects *feed_effects = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
RasterizerSceneGLES3 *scene = nullptr;
static RasterizerGLES3 *singleton;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index a73f14c796..dfc7d02ac0 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -31,6 +31,7 @@
#include "rasterizer_scene_gles3.h"
#include "drivers/gles3/effects/copy_effects.h"
+#include "drivers/gles3/effects/feed_effects.h"
#include "rasterizer_gles3.h"
#include "storage/config.h"
#include "storage/mesh_storage.h"
@@ -39,6 +40,8 @@
#include "core/config/project_settings.h"
#include "core/templates/sort_array.h"
+#include "servers/camera/camera_feed.h"
+#include "servers/camera_server.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/rendering/rendering_server_globals.h"
@@ -235,7 +238,7 @@ void RasterizerSceneGLES3::_geometry_instance_add_surface_with_material(Geometry
GLES3::SceneMaterialData *material_shadow = nullptr;
void *surface_shadow = nullptr;
- if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates) {
+ if (!p_material->shader_data->uses_particle_trails && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_prepass_alpha && !p_material->shader_data->uses_alpha_clip && !p_material->shader_data->uses_world_coordinates && !p_material->shader_data->wireframe) {
flags |= GeometryInstanceSurface::FLAG_USES_SHARED_SHADOW_MATERIAL;
material_shadow = static_cast<GLES3::SceneMaterialData *>(GLES3::MaterialStorage::get_singleton()->material_get_data(scene_globals.default_material, RS::SHADER_SPATIAL));
@@ -2259,6 +2262,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
render_data.cam_projection = p_camera_data->main_projection;
render_data.cam_orthogonal = p_camera_data->is_orthogonal;
+ render_data.cam_frustum = p_camera_data->is_frustum;
render_data.camera_visible_layers = p_camera_data->visible_layers;
render_data.main_cam_transform = p_camera_data->main_transform;
@@ -2382,7 +2386,9 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
bool draw_sky = false;
bool draw_sky_fog_only = false;
bool keep_color = false;
+ bool draw_feed = false;
float sky_energy_multiplier = 1.0;
+ int camera_feed_id = -1;
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW)) {
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
@@ -2427,6 +2433,8 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
keep_color = true;
} break;
case RS::ENV_BG_CAMERA_FEED: {
+ camera_feed_id = environment_get_camera_feed_id(render_data.environment);
+ draw_feed = true;
} break;
default: {
}
@@ -2538,7 +2546,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glClear(GL_DEPTH_BUFFER_BIT);
}
- if (!keep_color) {
+ if (!keep_color && !draw_feed) {
clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
} else if (fbo != rt->fbo) {
@@ -2578,6 +2586,29 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING;
}
}
+
+ if (draw_feed && camera_feed_id > -1) {
+ RENDER_TIMESTAMP("Render Camera feed");
+
+ scene_state.enable_gl_depth_draw(false);
+ scene_state.enable_gl_depth_test(false);
+ scene_state.enable_gl_blend(false);
+ scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+
+ if (feed.is_valid()) {
+ RID camera_YCBCR = feed->get_texture(CameraServer::FEED_YCBCR_IMAGE);
+ GLES3::TextureStorage::get_singleton()->texture_bind(camera_YCBCR, 0);
+
+ GLES3::FeedEffects *feed_effects = GLES3::FeedEffects::get_singleton();
+ feed_effects->draw();
+ }
+ scene_state.enable_gl_depth_draw(true);
+ scene_state.enable_gl_depth_test(true);
+ scene_state.enable_gl_blend(true);
+ }
+
// Render Opaque Objects.
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe);
@@ -2592,14 +2623,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+ Transform3D transform = render_data.cam_transform;
Projection projection = render_data.cam_projection;
if (is_reflection_probe) {
Projection correction;
correction.columns[1][1] = -1.0;
projection = correction * render_data.cam_projection;
+ } else if (render_data.cam_frustum) {
+ // Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip.
+ projection[2].y = -projection[2].y;
}
- _draw_sky(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
+ _draw_sky(render_data.environment, projection, transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
}
if (rt && (scene_state.used_screen_texture || scene_state.used_depth_texture)) {
@@ -3122,7 +3157,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
bool use_wireframe = false;
- if (p_params->force_wireframe) {
+ if (p_params->force_wireframe || shader->wireframe) {
GLuint wireframe_index_array_gl = mesh_storage->mesh_surface_get_index_buffer_wireframe(mesh_surface);
if (wireframe_index_array_gl) {
index_array_gl = wireframe_index_array_gl;
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 06371b2b7f..4f088a0e7d 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -103,6 +103,7 @@ struct RenderDataGLES3 {
Transform3D inv_cam_transform;
Projection cam_projection;
bool cam_orthogonal = false;
+ bool cam_frustum = false;
uint32_t camera_visible_layers = 0xFFFFFFFF;
// For billboards to cast correct shadows.
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index b73debf04a..83f74865f6 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -168,6 +168,10 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
builder.append("#version 300 es\n");
}
+ if (GLES3::Config::get_singleton()->polyfill_half2float) {
+ builder.append("#define USE_HALF2FLOAT\n");
+ }
+
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");
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index df2c515035..0207ba12b7 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -17,6 +17,7 @@ if "GLES3_GLSL" in env["BUILDERS"]:
# as we have a few, not yet, converted files we name the ones we want to include:
env.GLES3_GLSL("canvas.glsl")
+ env.GLES3_GLSL("feed.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 5e7fb3b338..3857aa8841 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -1,17 +1,16 @@
/* clang-format off */
#[modes]
-mode_quad =
-mode_ninepatch = #define USE_NINEPATCH
-mode_primitive = #define USE_PRIMITIVE
-mode_attributes = #define USE_ATTRIBUTES
-mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
+mode_default =
#[specializations]
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
-SINGLE_INSTANCE = false
+USE_NINEPATCH = false
+USE_PRIMITIVE = false
+USE_ATTRIBUTES = false
+USE_INSTANCING = false
#[vertex]
@@ -111,6 +110,9 @@ layout(std140) uniform MaterialUniforms{ //ubo:4
};
#endif
+
+uniform mediump uint batch_flags;
+
/* clang-format on */
#include "canvas_uniforms_inc.glsl"
@@ -180,13 +182,13 @@ void main() {
vec2 uv = uv_attrib;
#ifdef USE_INSTANCING
- if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_COLORS)) {
+ if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_COLORS)) {
vec4 instance_color;
instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
color *= instance_color;
}
- if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
+ if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
}
@@ -219,7 +221,7 @@ void main() {
else if (vertex_id == 5)
vertex_base = vec2(1.0, 1.0);
- vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
+ vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = read_draw_data_modulation;
vec2 vertex = read_draw_data_dst_rect.xy + abs(read_draw_data_dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(read_draw_data_src_rect.zw, vec2(0.0, 0.0)));
@@ -337,6 +339,8 @@ uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0
+uniform mediump uint batch_flags;
+
layout(location = 0) out vec4 frag_color;
/* clang-format off */
@@ -520,7 +524,7 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
- if (!bool(read_draw_data_flags & FLAGS_NINEPACH_DRAW_CENTER)) {
+ if (!bool(read_draw_data_flags & INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER)) {
draw_center--;
}
@@ -568,8 +572,8 @@ void main() {
int draw_center = 2;
uv = vec2(
- map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
- map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
+ map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
+ map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
@@ -578,7 +582,7 @@ void main() {
uv = uv * read_draw_data_src_rect.zw + read_draw_data_src_rect.xy; //apply region if needed
#endif
- if (bool(read_draw_data_flags & FLAGS_CLIP_RECT_UV)) {
+ if (bool(read_draw_data_flags & INSTANCE_FLAGS_CLIP_RECT_UV)) {
vec2 half_texpixel = read_draw_data_color_texture_pixel_size * 0.5;
uv = clamp(uv, read_draw_data_src_rect.xy + half_texpixel, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) - half_texpixel);
}
@@ -586,7 +590,7 @@ void main() {
#endif
#ifndef USE_PRIMITIVE
- if (bool(read_draw_data_flags & FLAGS_USE_MSDF)) {
+ if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_MSDF)) {
float px_range = read_draw_data_ninepatch_margins.x;
float outline_thickness = read_draw_data_ninepatch_margins.y;
@@ -604,7 +608,7 @@ void main() {
float a = clamp(d * px_size + 0.5, 0.0, 1.0);
color.a = a * color.a;
}
- } else if (bool(read_draw_data_flags & FLAGS_USE_LCD)) {
+ } else if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_LCD)) {
vec4 lcd_sample = texture(color_texture, uv);
if (lcd_sample.a == 1.0) {
color.rgb = lcd_sample.rgb * color.a;
@@ -618,7 +622,7 @@ void main() {
color *= texture(color_texture, uv);
}
- uint light_count = (read_draw_data_flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights
+ uint light_count = read_draw_data_flags & uint(0xF); // Max 16 lights.
bool using_light = light_count > 0u || directional_light_count > 0u;
vec3 normal;
@@ -629,17 +633,16 @@ void main() {
bool normal_used = false;
#endif
- if (normal_used || (using_light && bool(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
+ if (normal_used || (using_light && bool(batch_flags & BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
- if (bool(read_draw_data_flags & FLAGS_TRANSPOSE_RECT)) {
+
+#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
+ if (bool(read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT)) {
normal.xy = normal.yx;
}
- if (bool(read_draw_data_flags & FLAGS_FLIP_H)) {
- normal.x = -normal.x;
- }
- if (bool(read_draw_data_flags & FLAGS_FLIP_V)) {
- normal.y = -normal.y;
- }
+ normal.xy *= sign(read_draw_data_src_rect.zw);
+#endif
+
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
normal_used = true;
} else {
@@ -655,7 +658,7 @@ void main() {
bool specular_shininess_used = false;
#endif
- if (specular_shininess_used || (using_light && normal_used && bool(read_draw_data_flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
+ if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess);
specular_shininess_used = true;
@@ -728,7 +731,7 @@ void main() {
}
#endif
- if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW) && bool(read_draw_data_flags & uint(INSTANCE_FLAGS_SHADOW_MASKED << i))) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0);
@@ -803,7 +806,7 @@ void main() {
}
#endif
- if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+ if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW) && bool(read_draw_data_flags & uint(INSTANCE_FLAGS_SHADOW_MASKED << i))) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);
diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
index f6ad2b730a..94cbbdd8a7 100644
--- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl
+++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl
@@ -1,33 +1,32 @@
-
#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)
-// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here.
-#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
+#define INSTANCE_FLAGS_LIGHT_COUNT_SHIFT 0 // 4 bits.
-#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
-#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
+#define INSTANCE_FLAGS_CLIP_RECT_UV uint(1 << 4)
+#define INSTANCE_FLAGS_TRANSPOSE_RECT uint(1 << 5)
+#define INSTANCE_FLAGS_USE_MSDF uint(1 << 6)
+#define INSTANCE_FLAGS_USE_LCD uint(1 << 7)
-#define FLAGS_LIGHT_COUNT_SHIFT 20
+#define INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER uint(1 << 8)
+#define INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT 9
+#define INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT 11
-#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26)
-#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27)
+#define INSTANCE_FLAGS_SHADOW_MASKED_SHIFT 13u // 16 bits.
+#define INSTANCE_FLAGS_SHADOW_MASKED uint(1 << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT)
-#define FLAGS_USE_MSDF uint(1 << 28)
-#define FLAGS_USE_LCD uint(1 << 29)
+// 1 means enabled, 2+ means trails in use
+#define BATCH_FLAGS_INSTANCING_MASK uint(0x7F)
+#define BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT 7
+#define BATCH_FLAGS_INSTANCING_HAS_COLORS uint(1 << BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT)
+#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8
+#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT)
-#define FLAGS_FLIP_H uint(1 << 30)
-#define FLAGS_FLIP_V uint(1 << 31)
+#define BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 9)
+#define BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 10)
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
diff --git a/drivers/gles3/shaders/feed.glsl b/drivers/gles3/shaders/feed.glsl
new file mode 100644
index 0000000000..9d89fc699d
--- /dev/null
+++ b/drivers/gles3/shaders/feed.glsl
@@ -0,0 +1,39 @@
+/* clang-format off */
+#[modes]
+
+mode_default =
+
+#[specializations]
+
+USE_EXTERNAL_SAMPLER = false
+
+#[vertex]
+
+layout(location = 0) in vec2 vertex_attrib;
+
+out vec2 uv_interp;
+
+
+void main() {
+ uv_interp = vertex_attrib * 0.5 + 0.5;
+ gl_Position = vec4(vertex_attrib, 1.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+layout(location = 0) out vec4 frag_color;
+in vec2 uv_interp;
+
+/* clang-format on */
+#ifdef USE_EXTERNAL_SAMPLER
+uniform samplerExternalOES sourceFeed; // texunit:0
+#else
+uniform sampler2D sourceFeed; // texunit:0
+#endif
+
+void main() {
+ vec4 color = texture(sourceFeed, uv_interp);
+
+ frag_color = color;
+}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index fcfbeddb9e..2022c8ee43 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1259,7 +1259,7 @@ float SchlickFresnel(float u) {
return m2 * m2 * m; // pow(m,5)
}
-void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha,
+void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_directional, float attenuation, vec3 f0, float roughness, float metallic, float specular_amount, vec3 albedo, inout float alpha, vec2 screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -1423,7 +1423,7 @@ float get_omni_spot_attenuation(float distance, float inv_range, float decay) {
}
#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
-void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha,
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, vec2 screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -1450,7 +1450,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
omni_attenuation *= shadow;
- light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha,
+ light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, omni_attenuation, f0, roughness, metallic, omni_lights[idx].specular_amount, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -1469,7 +1469,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
#endif // !DISABLE_LIGHT_OMNI
#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
-void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha,
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f0, float roughness, float metallic, float shadow, vec3 albedo, inout float alpha, vec2 screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
vec3 backlight,
#endif
@@ -1506,7 +1506,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
spot_attenuation *= shadow;
- light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha,
+ light_compute(normal, normalize(light_rel_vec), eye_vec, size_A, color, false, spot_attenuation, f0, roughness, metallic, spot_lights[idx].specular_amount, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2114,7 +2114,7 @@ void main() {
continue;
}
#endif
- light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha,
+ light_compute(normal, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].size, directional_lights[i].color * directional_lights[i].energy, true, 1.0, f0, roughness, metallic, 1.0, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2143,7 +2143,7 @@ void main() {
continue;
}
#endif
- light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
+ light_process_omni(omni_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2171,7 +2171,7 @@ void main() {
continue;
}
#endif
- light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha,
+ light_process_spot(spot_light_indices[i], vertex, view, normal, f0, roughness, metallic, 1.0, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2389,7 +2389,7 @@ void main() {
#endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING
- light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha,
+ light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2421,7 +2421,7 @@ void main() {
#endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING
- light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
+ light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
@@ -2451,7 +2451,7 @@ void main() {
#endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING
- light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha,
+ light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha, screen_uv,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
#endif
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index 186b630bc8..043023aee0 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -2,17 +2,15 @@
#[modes]
mode_background =
-mode_half_res = #define USE_HALF_RES_PASS
-mode_quarter_res = #define USE_QUARTER_RES_PASS
mode_cubemap = #define USE_CUBEMAP_PASS
-mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS
-mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS
#[specializations]
USE_MULTIVIEW = false
USE_INVERTED_Y = true
APPLY_TONEMAPPING = true
+USE_QUARTER_RES_PASS = false
+USE_HALF_RES_PASS = false
#[vertex]
diff --git a/drivers/gles3/shaders/stdlib_inc.glsl b/drivers/gles3/shaders/stdlib_inc.glsl
index f88c218506..004db9e5d0 100644
--- a/drivers/gles3/shaders/stdlib_inc.glsl
+++ b/drivers/gles3/shaders/stdlib_inc.glsl
@@ -1,25 +1,28 @@
-
// Compatibility renames. These are exposed with the "godot_" prefix
// to work around two distinct Adreno bugs:
// 1. Some Adreno devices expose ES310 functions in ES300 shaders.
// Internally, we must use the "godot_" prefix, but user shaders
// will be mapped automatically.
// 2. Adreno 3XX devices have poor implementations of the other packing
-// functions, so we just use our own everywhere to keep it simple.
+// functions, so we just use our own there to keep it simple.
+#ifdef USE_HALF2FLOAT
// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
+// It appears to be safe to expose these on mobile, but when running through ANGLE this appears to break.
uint float2half(uint f) {
- uint b = f + uint(0x00001000);
- uint e = (b & uint(0x7F800000)) >> 23;
- uint m = b & uint(0x007FFFFF);
- return (b & uint(0x80000000)) >> uint(16) | uint(e > uint(112)) * ((((e - uint(112)) << uint(10)) & uint(0x7C00)) | m >> uint(13)) | (uint(e < uint(113)) & uint(e > uint(101))) * ((((uint(0x007FF000) + m) >> (uint(125) - e)) + uint(1)) >> uint(1)) | uint(e > uint(143)) * uint(0x7FFF);
+ uint e = f & uint(0x7f800000);
+ if (e <= uint(0x38000000)) {
+ return uint(0);
+ } else {
+ return ((f >> uint(16)) & uint(0x8000)) |
+ (((e - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) |
+ ((f >> uint(13)) & uint(0x03ff));
+ }
}
uint half2float(uint h) {
- uint e = (h & uint(0x7C00)) >> uint(10);
- uint m = (h & uint(0x03FF)) << uint(13);
- uint v = m >> uint(23);
- return (h & uint(0x8000)) << uint(16) | uint(e != uint(0)) * ((e + uint(112)) << uint(23) | m) | (uint(e == uint(0)) & uint(m != uint(0))) * ((v - uint(37)) << uint(23) | ((m << (uint(150) - v)) & uint(0x007FE000)));
+ uint h_e = h & uint(0x7c00);
+ return ((h & uint(0x8000)) << uint(16)) | uint((h_e >> uint(10)) != uint(0)) * (((h_e + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)));
}
uint godot_packHalf2x16(vec2 v) {
@@ -50,6 +53,17 @@ vec2 godot_unpackSnorm2x16(uint p) {
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
+#define packHalf2x16 godot_packHalf2x16
+#define unpackHalf2x16 godot_unpackHalf2x16
+#define packUnorm2x16 godot_packUnorm2x16
+#define unpackUnorm2x16 godot_unpackUnorm2x16
+#define packSnorm2x16 godot_packSnorm2x16
+#define unpackSnorm2x16 godot_unpackSnorm2x16
+
+#endif // USE_HALF2FLOAT
+
+// Always expose these as they are ES310 functions and not available in ES300 or GLSL 330.
+
uint godot_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));
@@ -73,9 +87,3 @@ vec4 godot_unpackSnorm4x8(uint p) {
#define unpackUnorm4x8 godot_unpackUnorm4x8
#define packSnorm4x8 godot_packSnorm4x8
#define unpackSnorm4x8 godot_unpackSnorm4x8
-#define packHalf2x16 godot_packHalf2x16
-#define unpackHalf2x16 godot_unpackHalf2x16
-#define packUnorm2x16 godot_packUnorm2x16
-#define unpackUnorm2x16 godot_unpackUnorm2x16
-#define packSnorm2x16 godot_packSnorm2x16
-#define unpackSnorm2x16 godot_unpackSnorm2x16
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 209b7dd7d2..07dd5f58c1 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -231,6 +231,13 @@ Config::Config() {
} else if (rendering_device_name == "PowerVR Rogue GE8320") {
disable_transform_feedback_shader_cache = true;
}
+
+ if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3_angle") {
+ polyfill_half2float = false;
+ }
+#ifdef WEB_ENABLED
+ polyfill_half2float = false;
+#endif
}
Config::~Config() {
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
index d60f295d66..51f6714320 100644
--- a/drivers/gles3/storage/config.h
+++ b/drivers/gles3/storage/config.h
@@ -95,13 +95,16 @@ public:
bool multiview_supported = false;
bool external_texture_supported = false;
- // Adreno 3XX compatibility
- bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
+ // Adreno 3XX compatibility.
+ bool disable_particles_workaround = false; // Set to 'true' to disable 'GPUParticles'.
bool flip_xy_workaround = false;
- // PowerVR GE 8320 workaround
+ // PowerVR GE 8320 workaround.
bool disable_transform_feedback_shader_cache = false;
+ // ANGLE shader workaround.
+ bool polyfill_half2float = true;
+
#ifdef ANDROID_ENABLED
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
@@ -110,7 +113,7 @@ public:
PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr;
#endif
- static Config *get_singleton() { return singleton; };
+ static Config *get_singleton() { return singleton; }
Config();
~Config();
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index 9b976c2206..9b81430d45 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -213,6 +213,23 @@ void LightStorage::light_set_cull_mask(RID p_light, uint32_t p_mask) {
light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
}
+void LightStorage::light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL(light);
+
+ light->shadow_caster_mask = p_caster_mask;
+
+ light->version++;
+ light->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_LIGHT);
+}
+
+uint32_t LightStorage::light_get_shadow_caster_mask(RID p_light) const {
+ Light *light = light_owner.get_or_null(p_light);
+ ERR_FAIL_NULL_V(light, 0);
+
+ return light->shadow_caster_mask;
+}
+
void LightStorage::light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) {
Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL(light);
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index ed00dd235f..0695102640 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -59,6 +59,7 @@ struct Light {
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
+ uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
@@ -305,8 +306,8 @@ public:
/* Light API */
- Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); };
- bool owns_light(RID p_rid) { return light_owner.owns(p_rid); };
+ Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); }
+ bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }
void _light_initialize(RID p_rid, RS::LightType p_type);
@@ -327,6 +328,8 @@ public:
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
+ virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
+ virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
@@ -431,8 +434,8 @@ public:
/* LIGHT INSTANCE API */
- LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); };
- bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); };
+ LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }
+ bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }
virtual RID light_instance_create(RID p_light) override;
virtual void light_instance_free(RID p_light_instance) override;
@@ -630,8 +633,8 @@ public:
/* PROBE API */
- ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); };
- bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); };
+ ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); }
+ bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); }
virtual RID reflection_probe_allocate() override;
virtual void reflection_probe_initialize(RID p_rid) override;
@@ -712,8 +715,8 @@ public:
/* LIGHTMAP CAPTURE */
- Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); };
- bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); };
+ Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }
+ bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }
virtual RID lightmap_allocate() override;
virtual void lightmap_initialize(RID p_rid) override;
@@ -736,15 +739,15 @@ public:
/* LIGHTMAP INSTANCE */
- LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); };
- bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); };
+ LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }
+ bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }
virtual RID lightmap_instance_create(RID p_lightmap) override;
virtual void lightmap_instance_free(RID p_lightmap) override;
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
/* SHADOW ATLAS API */
- bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); };
+ bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); }
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 684f179492..04cbf7f2cd 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1237,6 +1237,8 @@ MaterialStorage::MaterialStorage() {
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
+ actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
+ actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
@@ -1276,8 +1278,6 @@ MaterialStorage::MaterialStorage() {
actions.renames["CUSTOM1"] = "custom1_attrib";
actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib";
- actions.renames["OUTPUT_IS_SRGB"] = "SHADER_IS_SRGB";
- actions.renames["CLIP_SPACE_FAR"] = "SHADER_SPACE_FAR";
actions.renames["LIGHT_VERTEX"] = "light_vertex";
actions.renames["NODE_POSITION_WORLD"] = "model_matrix[3].xyz";
diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h
index 392ebcc570..6c21abc276 100644
--- a/drivers/gles3/storage/material_storage.h
+++ b/drivers/gles3/storage/material_storage.h
@@ -576,8 +576,8 @@ public:
/* SHADER API */
- Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); };
- bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); };
+ Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }
+ bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }
void _shader_make_dirty(Shader *p_shader);
@@ -598,8 +598,8 @@ public:
/* MATERIAL API */
- Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); };
- bool owns_material(RID p_rid) { return material_owner.owns(p_rid); };
+ Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }
+ bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }
void _material_queue_update(Material *material, bool p_uniform, bool p_texture);
void _update_queued_materials();
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 73d95d75ba..630bfdf1b7 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -1974,6 +1974,10 @@ void MeshStorage::_multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_
}
}
+RID MeshStorage::_multimesh_get_buffer_rd_rid(RID p_multimesh) const {
+ ERR_FAIL_V_MSG(RID(), "GLES3 does not contain a Rid for the multimesh buffer.");
+}
+
Vector<float> MeshStorage::_multimesh_get_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, Vector<float>());
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index 8615b89a30..5e3a56366e 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -280,8 +280,8 @@ public:
/* MESH API */
- Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); };
- bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); };
+ Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); }
+ bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); }
virtual RID mesh_allocate() override;
virtual void mesh_initialize(RID p_rid) override;
@@ -443,8 +443,8 @@ public:
/* MESH INSTANCE API */
- MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); };
- bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); };
+ MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); }
+ bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); }
virtual RID mesh_instance_create(RID p_base) override;
virtual void mesh_instance_free(RID p_rid) override;
@@ -492,8 +492,8 @@ public:
/* MULTIMESH API */
- MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); };
- bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); };
+ MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }
+ bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
@@ -517,6 +517,7 @@ public:
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
+ virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
@@ -571,8 +572,8 @@ public:
/* SKELETON API */
- Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };
- bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); };
+ Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); }
+ bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); }
virtual RID skeleton_allocate() override;
virtual void skeleton_initialize(RID p_rid) override;
diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h
index 086f5f7936..0552a5324f 100644
--- a/drivers/gles3/storage/particles_storage.h
+++ b/drivers/gles3/storage/particles_storage.h
@@ -396,7 +396,7 @@ public:
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
- ERR_FAIL_NULL_V(particles, 0);
+ ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index d7b4d6911d..14bbe635a4 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -230,6 +230,32 @@ TextureStorage::TextureStorage() {
sdf_shader.shader_version = sdf_shader.shader.version_create();
}
+ // Initialize texture placeholder data for the `texture_*_placeholder_initialize()` methods.
+
+ constexpr int placeholder_size = 4;
+ texture_2d_placeholder = Image::create_empty(placeholder_size, placeholder_size, false, Image::FORMAT_RGBA8);
+ // Draw a magenta/black checkerboard pattern.
+ for (int i = 0; i < placeholder_size * placeholder_size; i++) {
+ const int x = i % placeholder_size;
+ const int y = i / placeholder_size;
+ texture_2d_placeholder->set_pixel(x, y, (x + y) % 2 == 0 ? Color(1, 0, 1) : Color(0, 0, 0));
+ }
+
+ texture_2d_array_placeholder.push_back(texture_2d_placeholder);
+
+ for (int i = 0; i < 6; i++) {
+ cubemap_placeholder.push_back(texture_2d_placeholder);
+ }
+
+ Ref<Image> texture_2d_placeholder_rotated;
+ texture_2d_placeholder_rotated.instantiate();
+ texture_2d_placeholder_rotated->copy_from(texture_2d_placeholder);
+ texture_2d_placeholder_rotated->rotate_90(CLOCKWISE);
+ for (int i = 0; i < 4; i++) {
+ // Alternate checkerboard pattern on odd layers (by using a copy that is rotated 90 degrees).
+ texture_3d_placeholder.push_back(i % 2 == 0 ? texture_2d_placeholder : texture_2d_placeholder_rotated);
+ }
+
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
glEnable(GL_PROGRAM_POINT_SIZE);
@@ -1014,46 +1040,19 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
}
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
- //this could be better optimized to reuse an existing image , done this way
- //for now to get it working
- Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
- image->fill(Color(1, 0, 1, 1));
-
- texture_2d_initialize(p_texture, image);
+ texture_2d_initialize(p_texture, texture_2d_placeholder);
}
-void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) {
- //this could be better optimized to reuse an existing image , done this way
- //for now to get it working
- Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
- image->fill(Color(1, 0, 1, 1));
-
- Vector<Ref<Image>> images;
+void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) {
if (p_layered_type == RS::TEXTURE_LAYERED_2D_ARRAY) {
- images.push_back(image);
+ texture_2d_layered_initialize(p_texture, texture_2d_array_placeholder, p_layered_type);
} else {
- //cube
- for (int i = 0; i < 6; i++) {
- images.push_back(image);
- }
+ texture_2d_layered_initialize(p_texture, cubemap_placeholder, p_layered_type);
}
-
- texture_2d_layered_initialize(p_texture, images, p_layered_type);
}
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
- //this could be better optimized to reuse an existing image , done this way
- //for now to get it working
- Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
- image->fill(Color(1, 0, 1, 1));
-
- Vector<Ref<Image>> images;
- //cube
- for (int i = 0; i < 4; i++) {
- images.push_back(image);
- }
-
- texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, images);
+ texture_3d_initialize(p_texture, Image::FORMAT_RGBA8, 4, 4, 4, false, texture_3d_placeholder);
}
Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
@@ -2453,7 +2452,7 @@ Point2i TextureStorage::render_target_get_position(RID p_render_target) const {
ERR_FAIL_NULL_V(rt, Point2i());
return rt->position;
-};
+}
void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 3786c8c690..916662dcd6 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -36,6 +36,7 @@
#include "platform_gl.h"
#include "config.h"
+#include "core/io/image.h"
#include "core/os/os.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
@@ -477,8 +478,8 @@ public:
/* Canvas Texture API */
- CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); };
- bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); };
+ 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;
@@ -498,8 +499,8 @@ public:
return texture_owner.get_or_null(texture->proxy_to);
}
return texture;
- };
- bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); };
+ }
+ bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }
void texture_2d_initialize_from_texture(RID p_texture, Texture &p_tex) {
texture_owner.initialize_rid(p_texture, p_tex);
@@ -521,6 +522,11 @@ public:
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
+ Ref<Image> texture_2d_placeholder;
+ Vector<Ref<Image>> texture_2d_array_placeholder;
+ Vector<Ref<Image>> cubemap_placeholder;
+ Vector<Ref<Image>> texture_3d_placeholder;
+
//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;
@@ -612,8 +618,8 @@ public:
static GLuint system_fbo;
- RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); };
- bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); };
+ RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }
+ bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }
void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture);
diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h
index 7c3b08717e..88ac802018 100644
--- a/drivers/gles3/storage/utilities.h
+++ b/drivers/gles3/storage/utilities.h
@@ -165,8 +165,8 @@ public:
/* VISIBILITY NOTIFIER */
- VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); };
- bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); };
+ VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); }
+ bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); }
virtual RID visibility_notifier_allocate() override;
virtual void visibility_notifier_initialize(RID p_notifier) override;
diff --git a/drivers/metal/metal_objects.h b/drivers/metal/metal_objects.h
index 030b353ee8..5eda826d2a 100644
--- a/drivers/metal/metal_objects.h
+++ b/drivers/metal/metal_objects.h
@@ -82,6 +82,9 @@ MTL_CLASS(Texture)
} //namespace MTL
+/// Metal buffer index for the view mask when rendering multi-view.
+const uint32_t VIEW_MASK_BUFFER_INDEX = 24;
+
enum ShaderStageUsage : uint32_t {
None = 0,
Vertex = RDD::SHADER_STAGE_VERTEX_BIT,
@@ -96,6 +99,22 @@ _FORCE_INLINE_ ShaderStageUsage &operator|=(ShaderStageUsage &p_a, int p_b) {
return p_a;
}
+enum StageResourceUsage : uint32_t {
+ VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
+ VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
+ FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
+ FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
+ TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
+ TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
+ ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
+ ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
+};
+
+typedef LocalVector<__unsafe_unretained id<MTLResource>> ResourceVector;
+typedef HashMap<StageResourceUsage, ResourceVector> ResourceUsageMap;
+
enum class MDCommandBufferStateType {
None,
Render,
@@ -126,6 +145,12 @@ struct ClearAttKey {
const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1;
const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1;
+ enum Flags : uint16_t {
+ CLEAR_FLAGS_NONE = 0,
+ CLEAR_FLAGS_LAYERED = 1 << 0,
+ };
+
+ Flags flags = CLEAR_FLAGS_NONE;
uint16_t sample_count = 0;
uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 };
@@ -134,19 +159,22 @@ struct ClearAttKey {
_FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; }
_FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; }
_FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; }
+ _FORCE_INLINE_ void enable_layered_rendering() { flags::set(flags, CLEAR_FLAGS_LAYERED); }
_FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; }
_FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; }
_FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; }
+ _FORCE_INLINE_ bool is_layered_rendering_enabled() const { return flags::any(flags, CLEAR_FLAGS_LAYERED); }
_FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const {
return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0;
}
uint32_t hash() const {
- uint32_t h = hash_murmur3_one_32(sample_count);
+ uint32_t h = hash_murmur3_one_32(flags);
+ h = hash_murmur3_one_32(sample_count, h);
h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h);
- return h;
+ return hash_fmix32(h);
}
};
@@ -190,6 +218,97 @@ public:
~MDResourceCache() = default;
};
+enum class MDAttachmentType : uint8_t {
+ None = 0,
+ Color = 1 << 0,
+ Depth = 1 << 1,
+ Stencil = 1 << 2,
+};
+
+_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
+ flags::set(p_a, p_b);
+ return p_a;
+}
+
+_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
+ return uint8_t(p_a) & uint8_t(p_b);
+}
+
+struct MDSubpass {
+ uint32_t subpass_index = 0;
+ uint32_t view_count = 0;
+ LocalVector<RDD::AttachmentReference> input_references;
+ LocalVector<RDD::AttachmentReference> color_references;
+ RDD::AttachmentReference depth_stencil_reference;
+ LocalVector<RDD::AttachmentReference> resolve_references;
+
+ MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
+};
+
+struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
+private:
+ uint32_t index = 0;
+ uint32_t firstUseSubpassIndex = 0;
+ uint32_t lastUseSubpassIndex = 0;
+
+public:
+ MTLPixelFormat format = MTLPixelFormatInvalid;
+ MDAttachmentType type = MDAttachmentType::None;
+ MTLLoadAction loadAction = MTLLoadActionDontCare;
+ MTLStoreAction storeAction = MTLStoreActionDontCare;
+ MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
+ MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
+ uint32_t samples = 1;
+
+ /*!
+ * @brief Returns true if this attachment is first used in the given subpass.
+ * @param p_subpass
+ * @return
+ */
+ _FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
+ return p_subpass.subpass_index == firstUseSubpassIndex;
+ }
+
+ /*!
+ * @brief Returns true if this attachment is last used in the given subpass.
+ * @param p_subpass
+ * @return
+ */
+ _FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
+ return p_subpass.subpass_index == lastUseSubpassIndex;
+ }
+
+ void linkToSubpass(MDRenderPass const &p_pass);
+
+ MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
+ bool p_is_rendering_entire_area,
+ bool p_has_resolve,
+ bool p_can_resolve,
+ bool p_is_stencil) const;
+ bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
+ PixelFormats &p_pf,
+ MDSubpass const &p_subpass,
+ id<MTLTexture> p_attachment,
+ bool p_is_rendering_entire_area,
+ bool p_has_resolve,
+ bool p_can_resolve,
+ bool p_is_stencil) const;
+ /** Returns whether this attachment should be cleared in the subpass. */
+ bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
+};
+
+class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
+public:
+ Vector<MDAttachment> attachments;
+ Vector<MDSubpass> subpasses;
+
+ uint32_t get_sample_count() const {
+ return attachments.is_empty() ? 1 : attachments[0].samples;
+ }
+
+ MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
+};
+
class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer {
private:
RenderingDeviceDriverMetal *device_driver = nullptr;
@@ -204,8 +323,8 @@ private:
void _render_set_dirty_state();
void _render_bind_uniform_sets();
- static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
- static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
+ void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects);
+ uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size);
void _end_render_pass();
void _render_clear_render_area();
@@ -230,6 +349,7 @@ public:
uint32_t index_offset = 0;
LocalVector<id<MTLBuffer> __unsafe_unretained> vertex_buffers;
LocalVector<NSUInteger> vertex_offsets;
+ ResourceUsageMap resource_usage;
// clang-format off
enum DirtyFlag: uint8_t {
DIRTY_NONE = 0b0000'0000,
@@ -251,26 +371,12 @@ public:
// Bit mask of the uniform sets that are dirty, to prevent redundant binding.
uint64_t uniform_set_mask = 0;
- _FORCE_INLINE_ void reset() {
- pass = nil;
- frameBuffer = nil;
- pipeline = nil;
- current_subpass = UINT32_MAX;
- render_area = {};
- is_rendering_entire_area = false;
- desc = nil;
- encoder = nil;
- index_buffer = nil;
- index_type = MTLIndexTypeUInt16;
- dirty = DIRTY_NONE;
- uniform_sets.clear();
- uniform_set_mask = 0;
- clear_values.clear();
- viewports.clear();
- scissors.clear();
- blend_constants.reset();
- vertex_buffers.clear();
- vertex_offsets.clear();
+ _FORCE_INLINE_ void reset();
+ void end_encoding();
+
+ _ALWAYS_INLINE_ const MDSubpass &get_subpass() const {
+ DEV_ASSERT(pass != nullptr);
+ return pass->subpasses[current_subpass];
}
_FORCE_INLINE_ void mark_viewport_dirty() {
@@ -356,13 +462,20 @@ public:
} render;
// State specific for a compute pass.
- struct {
+ struct ComputeState {
MDComputePipeline *pipeline = nullptr;
id<MTLComputeCommandEncoder> encoder = nil;
+ ResourceUsageMap resource_usage;
_FORCE_INLINE_ void reset() {
pipeline = nil;
encoder = nil;
+ // Keep the keys, as they are likely to be used again.
+ for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
+ kv.value.clear();
+ }
}
+
+ void end_encoding();
} compute;
// State specific to a blit pass.
@@ -619,6 +732,7 @@ public:
uint32_t size = 0;
} frag;
} push_constants;
+ bool needs_view_mask_buffer = false;
MDLibrary *vert;
MDLibrary *frag;
@@ -629,20 +743,10 @@ public:
void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final;
- MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *p_vert, MDLibrary *p_frag);
-};
-
-enum StageResourceUsage : uint32_t {
- VertexRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_VERTEX * 2),
- VertexWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_VERTEX * 2),
- FragmentRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_FRAGMENT * 2),
- FragmentWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_FRAGMENT * 2),
- TesselationControlRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
- TesselationControlWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_CONTROL * 2),
- TesselationEvaluationRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
- TesselationEvaluationWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_TESSELATION_EVALUATION * 2),
- ComputeRead = (MTLResourceUsageRead << RDD::SHADER_STAGE_COMPUTE * 2),
- ComputeWrite = (MTLResourceUsageWrite << RDD::SHADER_STAGE_COMPUTE * 2),
+ MDRenderShader(CharString p_name,
+ bool p_needs_view_mask_buffer,
+ Vector<UniformSet> p_sets,
+ MDLibrary *p_vert, MDLibrary *p_frag);
};
_FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) {
@@ -667,7 +771,13 @@ struct HashMapComparatorDefault<RDD::ShaderID> {
struct BoundUniformSet {
id<MTLBuffer> buffer;
- HashMap<id<MTLResource>, StageResourceUsage> bound_resources;
+ ResourceUsageMap usage_to_resources;
+
+ /// Perform a 2-way merge each key of `ResourceVector` resources from this set into the
+ /// destination set.
+ ///
+ /// Assumes the vectors of resources are sorted.
+ void merge_into(ResourceUsageMap &p_dst) const;
};
class API_AVAILABLE(macos(11.0), ios(14.0)) MDUniformSet {
@@ -679,96 +789,6 @@ public:
BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device);
};
-enum class MDAttachmentType : uint8_t {
- None = 0,
- Color = 1 << 0,
- Depth = 1 << 1,
- Stencil = 1 << 2,
-};
-
-_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) {
- flags::set(p_a, p_b);
- return p_a;
-}
-
-_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) {
- return uint8_t(p_a) & uint8_t(p_b);
-}
-
-struct MDSubpass {
- uint32_t subpass_index = 0;
- LocalVector<RDD::AttachmentReference> input_references;
- LocalVector<RDD::AttachmentReference> color_references;
- RDD::AttachmentReference depth_stencil_reference;
- LocalVector<RDD::AttachmentReference> resolve_references;
-
- MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const;
-};
-
-struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment {
-private:
- uint32_t index = 0;
- uint32_t firstUseSubpassIndex = 0;
- uint32_t lastUseSubpassIndex = 0;
-
-public:
- MTLPixelFormat format = MTLPixelFormatInvalid;
- MDAttachmentType type = MDAttachmentType::None;
- MTLLoadAction loadAction = MTLLoadActionDontCare;
- MTLStoreAction storeAction = MTLStoreActionDontCare;
- MTLLoadAction stencilLoadAction = MTLLoadActionDontCare;
- MTLStoreAction stencilStoreAction = MTLStoreActionDontCare;
- uint32_t samples = 1;
-
- /*!
- * @brief Returns true if this attachment is first used in the given subpass.
- * @param p_subpass
- * @return
- */
- _FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const {
- return p_subpass.subpass_index == firstUseSubpassIndex;
- }
-
- /*!
- * @brief Returns true if this attachment is last used in the given subpass.
- * @param p_subpass
- * @return
- */
- _FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const {
- return p_subpass.subpass_index == lastUseSubpassIndex;
- }
-
- void linkToSubpass(MDRenderPass const &p_pass);
-
- MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass,
- bool p_is_rendering_entire_area,
- bool p_has_resolve,
- bool p_can_resolve,
- bool p_is_stencil) const;
- bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc,
- PixelFormats &p_pf,
- MDSubpass const &p_subpass,
- id<MTLTexture> p_attachment,
- bool p_is_rendering_entire_area,
- bool p_has_resolve,
- bool p_can_resolve,
- bool p_is_stencil) const;
- /** Returns whether this attachment should be cleared in the subpass. */
- bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const;
-};
-
-class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass {
-public:
- Vector<MDAttachment> attachments;
- Vector<MDSubpass> subpasses;
-
- uint32_t get_sample_count() const {
- return attachments.is_empty() ? 1 : attachments[0].samples;
- }
-
- MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses);
-};
-
class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline {
public:
MDPipelineType type;
@@ -818,7 +838,7 @@ public:
if (!enabled)
return;
[p_enc setStencilFrontReferenceValue:front_reference backReferenceValue:back_reference];
- };
+ }
} stencil;
struct {
@@ -832,7 +852,7 @@ public:
//if (!enabled)
// return;
[p_enc setBlendColorRed:r green:g blue:b alpha:a];
- };
+ }
} blend;
_FORCE_INLINE_ void apply(id<MTLRenderCommandEncoder> __unsafe_unretained p_enc) const {
@@ -869,13 +889,39 @@ public:
};
class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer {
-public:
Vector<MTL::Texture> textures;
+
+public:
Size2i size;
MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) :
textures(p_textures), size(p_size) {}
MDFrameBuffer() {}
+ /// Returns the texture at the given index.
+ _ALWAYS_INLINE_ MTL::Texture get_texture(uint32_t p_idx) const {
+ return textures[p_idx];
+ }
+
+ /// Returns true if the texture at the given index is not nil.
+ _ALWAYS_INLINE_ bool has_texture(uint32_t p_idx) const {
+ return textures[p_idx] != nil;
+ }
+
+ /// Set the texture at the given index.
+ _ALWAYS_INLINE_ void set_texture(uint32_t p_idx, MTL::Texture p_texture) {
+ textures.write[p_idx] = p_texture;
+ }
+
+ /// Unset or nil the texture at the given index.
+ _ALWAYS_INLINE_ void unset_texture(uint32_t p_idx) {
+ textures.write[p_idx] = nil;
+ }
+
+ /// Resizes buffers to the specified size.
+ _ALWAYS_INLINE_ void set_texture_count(uint32_t p_size) {
+ textures.resize(p_size);
+ }
+
virtual ~MDFrameBuffer() = default;
};
diff --git a/drivers/metal/metal_objects.mm b/drivers/metal/metal_objects.mm
index 596728212a..11ab209d60 100644
--- a/drivers/metal/metal_objects.mm
+++ b/drivers/metal/metal_objects.mm
@@ -58,7 +58,7 @@
void MDCommandBuffer::begin() {
DEV_ASSERT(commandBuffer == nil);
- commandBuffer = queue.commandBuffer;
+ commandBuffer = queue.commandBufferWithUnretainedReferences;
}
void MDCommandBuffer::end() {
@@ -96,6 +96,9 @@ void MDCommandBuffer::bind_pipeline(RDD::PipelineID p_pipeline) {
MDRenderPipeline *rp = (MDRenderPipeline *)p;
if (render.encoder == nil) {
+ // This error would happen if the render pass failed.
+ ERR_FAIL_NULL_MSG(render.desc, "Render pass descriptor is null.");
+
// This condition occurs when there are no attachments when calling render_next_subpass()
// and is due to the SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS flag.
render.desc.defaultRasterSampleCount = static_cast<NSUInteger>(rp->sample_count);
@@ -223,8 +226,9 @@ void MDCommandBuffer::render_bind_uniform_set(RDD::UniformSetID p_uniform_set, R
void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
- uint32_t vertex_count = p_rects.size() * 6;
+ const MDSubpass &subpass = render.get_subpass();
+ uint32_t vertex_count = p_rects.size() * 6 * subpass.view_count;
simd::float4 vertices[vertex_count];
simd::float4 clear_colors[ClearAttKey::ATTACHMENT_COUNT];
@@ -235,6 +239,9 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
ClearAttKey key;
key.sample_count = render.pass->get_sample_count();
+ if (subpass.view_count > 1) {
+ key.enable_layered_rendering();
+ }
float depth_value = 0;
uint32_t stencil_value = 0;
@@ -245,7 +252,7 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
if (attClear.aspect.has_flag(RDD::TEXTURE_ASPECT_COLOR_BIT)) {
attachment_index = attClear.color_attachment;
} else {
- attachment_index = render.pass->subpasses[render.current_subpass].depth_stencil_reference.attachment;
+ attachment_index = subpass.depth_stencil_reference.attachment;
}
MDAttachment const &mda = render.pass->attachments[attachment_index];
@@ -310,6 +317,13 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear>
void MDCommandBuffer::_render_set_dirty_state() {
_render_bind_uniform_sets();
+ MDSubpass const &subpass = render.get_subpass();
+ if (subpass.view_count > 1) {
+ uint32_t view_range[2] = { 0, subpass.view_count };
+ [render.encoder setVertexBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX];
+ [render.encoder setFragmentBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX];
+ }
+
if (render.dirty.has_flag(RenderState::DIRTY_PIPELINE)) {
[render.encoder setRenderPipelineState:render.pipeline->state];
}
@@ -390,6 +404,38 @@ void MDCommandBuffer::render_set_blend_constants(const Color &p_constants) {
}
}
+void BoundUniformSet::merge_into(ResourceUsageMap &p_dst) const {
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : usage_to_resources) {
+ ResourceVector *resources = p_dst.getptr(keyval.key);
+ if (resources == nullptr) {
+ resources = &p_dst.insert(keyval.key, ResourceVector())->value;
+ }
+ // Reserve space for the new resources, assuming they are all added.
+ resources->reserve(resources->size() + keyval.value.size());
+
+ uint32_t i = 0, j = 0;
+ __unsafe_unretained id<MTLResource> *resources_ptr = resources->ptr();
+ const __unsafe_unretained id<MTLResource> *keyval_ptr = keyval.value.ptr();
+ // 2-way merge.
+ while (i < resources->size() && j < keyval.value.size()) {
+ if (resources_ptr[i] < keyval_ptr[j]) {
+ i++;
+ } else if (resources_ptr[i] > keyval_ptr[j]) {
+ resources->insert(i, keyval_ptr[j]);
+ i++;
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ // Append the remaining resources.
+ for (; j < keyval.value.size(); j++) {
+ resources->push_back(keyval_ptr[j]);
+ }
+ }
+}
+
void MDCommandBuffer::_render_bind_uniform_sets() {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
if (!render.dirty.has_flag(RenderState::DIRTY_UNIFORMS)) {
@@ -408,7 +454,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
// Find the index of the next set bit.
int index = __builtin_ctzll(set_uniforms);
// Clear the set bit.
- set_uniforms &= ~(1ULL << index);
+ set_uniforms &= (set_uniforms - 1);
MDUniformSet *set = render.uniform_sets[index];
if (set == nullptr || set->index >= (uint32_t)shader->sets.size()) {
continue;
@@ -416,17 +462,7 @@ void MDCommandBuffer::_render_bind_uniform_sets() {
UniformSet const &set_info = shader->sets[set->index];
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
-
- for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bus.bound_resources) {
- MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_VERTEX);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage stages:MTLRenderStageVertex];
- }
- usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage stages:MTLRenderStageFragment];
- }
- }
+ bus.merge_into(render.resource_usage);
// Set the buffer for the vertex stage.
{
@@ -470,36 +506,40 @@ uint32_t MDCommandBuffer::_populate_vertices(simd::float4 *p_vertices, uint32_t
simd::float4 vtx;
uint32_t idx = p_index;
- vtx.z = 0.0;
- vtx.w = (float)1;
+ uint32_t endLayer = render.get_subpass().view_count;
- // Top left vertex - First triangle.
- vtx.y = topPos;
- vtx.x = leftPos;
- p_vertices[idx++] = vtx;
+ for (uint32_t layer = 0; layer < endLayer; layer++) {
+ vtx.z = 0.0;
+ vtx.w = (float)layer;
- // Bottom left vertex.
- vtx.y = bottomPos;
- vtx.x = leftPos;
- p_vertices[idx++] = vtx;
+ // Top left vertex - First triangle.
+ vtx.y = topPos;
+ vtx.x = leftPos;
+ p_vertices[idx++] = vtx;
- // Bottom right vertex.
- vtx.y = bottomPos;
- vtx.x = rightPos;
- p_vertices[idx++] = vtx;
+ // Bottom left vertex.
+ vtx.y = bottomPos;
+ vtx.x = leftPos;
+ p_vertices[idx++] = vtx;
- // Bottom right vertex - Second triangle.
- p_vertices[idx++] = vtx;
+ // Bottom right vertex.
+ vtx.y = bottomPos;
+ vtx.x = rightPos;
+ p_vertices[idx++] = vtx;
- // Top right vertex.
- vtx.y = topPos;
- vtx.x = rightPos;
- p_vertices[idx++] = vtx;
+ // Bottom right vertex - Second triangle.
+ p_vertices[idx++] = vtx;
- // Top left vertex.
- vtx.y = topPos;
- vtx.x = leftPos;
- p_vertices[idx++] = vtx;
+ // Top right vertex.
+ vtx.y = topPos;
+ vtx.x = rightPos;
+ p_vertices[idx++] = vtx;
+
+ // Top left vertex.
+ vtx.y = topPos;
+ vtx.x = leftPos;
+ p_vertices[idx++] = vtx;
+ }
return idx;
}
@@ -526,8 +566,7 @@ void MDCommandBuffer::render_begin_pass(RDD::RenderPassID p_render_pass, RDD::Fr
void MDCommandBuffer::_end_render_pass() {
MDFrameBuffer const &fb_info = *render.frameBuffer;
- MDRenderPass const &pass_info = *render.pass;
- MDSubpass const &subpass = pass_info.subpasses[render.current_subpass];
+ MDSubpass const &subpass = render.get_subpass();
PixelFormats &pf = device_driver->get_pixel_formats();
@@ -535,23 +574,22 @@ void MDCommandBuffer::_end_render_pass() {
uint32_t color_index = subpass.color_references[i].attachment;
uint32_t resolve_index = subpass.resolve_references[i].attachment;
DEV_ASSERT((color_index == RDD::AttachmentReference::UNUSED) == (resolve_index == RDD::AttachmentReference::UNUSED));
- if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.textures[color_index]) {
+ if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.has_texture(color_index)) {
continue;
}
- id<MTLTexture> resolve_tex = fb_info.textures[resolve_index];
+ id<MTLTexture> resolve_tex = fb_info.get_texture(resolve_index);
CRASH_COND_MSG(!flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve), "not implemented: unresolvable texture types");
// see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407
}
- [render.encoder endEncoding];
- render.encoder = nil;
+ render.end_encoding();
}
void MDCommandBuffer::_render_clear_render_area() {
MDRenderPass const &pass = *render.pass;
- MDSubpass const &subpass = pass.subpasses[render.current_subpass];
+ MDSubpass const &subpass = render.get_subpass();
// First determine attachments that should be cleared.
LocalVector<RDD::AttachmentClear> clears;
@@ -598,9 +636,14 @@ void MDCommandBuffer::render_next_subpass() {
MDFrameBuffer const &fb = *render.frameBuffer;
MDRenderPass const &pass = *render.pass;
- MDSubpass const &subpass = pass.subpasses[render.current_subpass];
+ MDSubpass const &subpass = render.get_subpass();
MTLRenderPassDescriptor *desc = MTLRenderPassDescriptor.renderPassDescriptor;
+
+ if (subpass.view_count > 1) {
+ desc.renderTargetArrayLength = subpass.view_count;
+ }
+
PixelFormats &pf = device_driver->get_pixel_formats();
uint32_t attachmentCount = 0;
@@ -617,7 +660,7 @@ void MDCommandBuffer::render_next_subpass() {
bool has_resolve = resolveIdx != RDD::AttachmentReference::UNUSED;
bool can_resolve = true;
if (resolveIdx != RDD::AttachmentReference::UNUSED) {
- id<MTLTexture> resolve_tex = fb.textures[resolveIdx];
+ id<MTLTexture> resolve_tex = fb.get_texture(resolveIdx);
can_resolve = flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve);
if (can_resolve) {
ca.resolveTexture = resolve_tex;
@@ -628,7 +671,9 @@ void MDCommandBuffer::render_next_subpass() {
MDAttachment const &attachment = pass.attachments[idx];
- id<MTLTexture> tex = fb.textures[idx];
+ id<MTLTexture> tex = fb.get_texture(idx);
+ ERR_FAIL_NULL_MSG(tex, "Frame buffer color texture is null.");
+
if ((attachment.type & MDAttachmentType::Color)) {
if (attachment.configureDescriptor(ca, pf, subpass, tex, render.is_rendering_entire_area, has_resolve, can_resolve, false)) {
Color clearColor = render.clear_values[idx].color;
@@ -641,7 +686,8 @@ void MDCommandBuffer::render_next_subpass() {
attachmentCount += 1;
uint32_t idx = subpass.depth_stencil_reference.attachment;
MDAttachment const &attachment = pass.attachments[idx];
- id<MTLTexture> tex = fb.textures[idx];
+ id<MTLTexture> tex = fb.get_texture(idx);
+ ERR_FAIL_NULL_MSG(tex, "Frame buffer depth / stencil texture is null.");
if (attachment.type & MDAttachmentType::Depth) {
MTLRenderPassDepthAttachmentDescriptor *da = desc.depthAttachment;
if (attachment.configureDescriptor(da, pf, subpass, tex, render.is_rendering_entire_area, false, false, false)) {
@@ -681,8 +727,15 @@ void MDCommandBuffer::render_draw(uint32_t p_vertex_count,
uint32_t p_base_vertex,
uint32_t p_first_instance) {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
+
_render_set_dirty_state();
+ MDSubpass const &subpass = render.get_subpass();
+ if (subpass.view_count > 1) {
+ p_instance_count *= subpass.view_count;
+ }
+
DEV_ASSERT(render.dirty == 0);
id<MTLRenderCommandEncoder> enc = render.encoder;
@@ -730,8 +783,15 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count,
int32_t p_vertex_offset,
uint32_t p_first_instance) {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
+
_render_set_dirty_state();
+ MDSubpass const &subpass = render.get_subpass();
+ if (subpass.view_count > 1) {
+ p_instance_count *= subpass.view_count;
+ }
+
id<MTLRenderCommandEncoder> enc = render.encoder;
uint32_t index_offset = render.index_offset;
@@ -749,6 +809,8 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count,
void MDCommandBuffer::render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
+
_render_set_dirty_state();
id<MTLRenderCommandEncoder> enc = render.encoder;
@@ -773,6 +835,8 @@ void MDCommandBuffer::render_draw_indexed_indirect_count(RDD::BufferID p_indirec
void MDCommandBuffer::render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
+ ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer.");
+
_render_set_dirty_state();
id<MTLRenderCommandEncoder> enc = render.encoder;
@@ -795,11 +859,90 @@ void MDCommandBuffer::render_draw_indirect_count(RDD::BufferID p_indirect_buffer
void MDCommandBuffer::render_end_pass() {
DEV_ASSERT(type == MDCommandBufferStateType::Render);
- [render.encoder endEncoding];
+ render.end_encoding();
render.reset();
type = MDCommandBufferStateType::None;
}
+#pragma mark - RenderState
+
+void MDCommandBuffer::RenderState::reset() {
+ pass = nil;
+ frameBuffer = nil;
+ pipeline = nil;
+ current_subpass = UINT32_MAX;
+ render_area = {};
+ is_rendering_entire_area = false;
+ desc = nil;
+ encoder = nil;
+ index_buffer = nil;
+ index_type = MTLIndexTypeUInt16;
+ dirty = DIRTY_NONE;
+ uniform_sets.clear();
+ uniform_set_mask = 0;
+ clear_values.clear();
+ viewports.clear();
+ scissors.clear();
+ blend_constants.reset();
+ vertex_buffers.clear();
+ vertex_offsets.clear();
+ // Keep the keys, as they are likely to be used again.
+ for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) {
+ kv.value.clear();
+ }
+}
+
+void MDCommandBuffer::RenderState::end_encoding() {
+ if (encoder == nil) {
+ return;
+ }
+
+ // Bind all resources.
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
+ if (keyval.value.is_empty()) {
+ continue;
+ }
+
+ MTLResourceUsage vert_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_VERTEX);
+ MTLResourceUsage frag_usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_FRAGMENT);
+ if (vert_usage == frag_usage) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex | MTLRenderStageFragment];
+ } else {
+ if (vert_usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:vert_usage stages:MTLRenderStageVertex];
+ }
+ if (frag_usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:frag_usage stages:MTLRenderStageFragment];
+ }
+ }
+ }
+
+ [encoder endEncoding];
+ encoder = nil;
+}
+
+#pragma mark - ComputeState
+
+void MDCommandBuffer::ComputeState::end_encoding() {
+ if (encoder == nil) {
+ return;
+ }
+
+ // Bind all resources.
+ for (KeyValue<StageResourceUsage, ResourceVector> const &keyval : resource_usage) {
+ if (keyval.value.is_empty()) {
+ continue;
+ }
+ MTLResourceUsage usage = resource_usage_for_stage(keyval.key, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
+ if (usage != 0) {
+ [encoder useResources:keyval.value.ptr() count:keyval.value.size() usage:usage];
+ }
+ }
+
+ [encoder endEncoding];
+ encoder = nil;
+}
+
#pragma mark - Compute
void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index) {
@@ -813,13 +956,7 @@ void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set,
MDUniformSet *set = (MDUniformSet *)(p_uniform_set.id);
BoundUniformSet &bus = set->boundUniformSetForShader(shader, device);
-
- for (KeyValue<id<MTLResource>, StageResourceUsage> &keyval : bus.bound_resources) {
- MTLResourceUsage usage = resource_usage_for_stage(keyval.value, RDD::ShaderStage::SHADER_STAGE_COMPUTE);
- if (usage != 0) {
- [enc useResource:keyval.key usage:usage];
- }
- }
+ bus.merge_into(compute.resource_usage);
uint32_t const *offset = set_info.offsets.getptr(RDD::SHADER_STAGE_COMPUTE);
if (offset) {
@@ -848,7 +985,7 @@ void MDCommandBuffer::compute_dispatch_indirect(RDD::BufferID p_indirect_buffer,
void MDCommandBuffer::_end_compute_dispatch() {
DEV_ASSERT(type == MDCommandBufferStateType::Compute);
- [compute.encoder endEncoding];
+ compute.end_encoding();
compute.reset();
type = MDCommandBufferStateType::None;
}
@@ -879,8 +1016,11 @@ void MDComputeShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDC
[enc setBytes:ptr length:length atIndex:push_constants.binding];
}
-MDRenderShader::MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) :
- MDShader(p_name, p_sets), vert(p_vert), frag(p_frag) {
+MDRenderShader::MDRenderShader(CharString p_name,
+ bool p_needs_view_mask_buffer,
+ Vector<UniformSet> p_sets,
+ MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) :
+ MDShader(p_name, p_sets), needs_view_mask_buffer(p_needs_view_mask_buffer), vert(p_vert), frag(p_frag) {
}
void MDRenderShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) {
@@ -1052,7 +1192,20 @@ BoundUniformSet &MDUniformSet::boundUniformSetForShader(MDShader *p_shader, id<M
}
}
- BoundUniformSet bs = { .buffer = enc_buffer, .bound_resources = bound_resources };
+ SearchArray<__unsafe_unretained id<MTLResource>> search;
+ ResourceUsageMap usage_to_resources;
+ for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
+ ResourceVector *resources = usage_to_resources.getptr(keyval.value);
+ if (resources == nullptr) {
+ resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
+ }
+ int64_t pos = search.bisect(resources->ptr(), resources->size(), keyval.key, true);
+ if (pos == resources->size() || (*resources)[pos] != keyval.key) {
+ resources->insert(pos, keyval.key);
+ }
+ }
+
+ BoundUniformSet bs = { .buffer = enc_buffer, .usage_to_resources = usage_to_resources };
bound_uniforms.insert(p_shader, bs);
return bound_uniforms.get(p_shader);
}
@@ -1202,7 +1355,7 @@ typedef struct {
typedef struct {
float4 v_position [[position]];
- uint layer;
+ uint layer%s;
} VaryingsPos;
vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant ClearColorsIn& ccIn [[buffer(0)]]) {
@@ -1211,8 +1364,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
varyings.layer = uint(attributes.a_position.w);
return varyings;
}
-)",
- ClearAttKey::DEPTH_INDEX];
+)", p_key.is_layered_rendering_enabled() ? " [[render_target_array_index]]" : "", ClearAttKey::DEPTH_INDEX];
return new_func(msl, @"vertClear", nil);
}
@@ -1502,7 +1654,7 @@ void ShaderCacheEntry::notify_free() const {
self->_library = library;
self->_error = error;
if (error) {
- ERR_PRINT(String(U"Error compiling shader %s: %s").format(entry->name.get_data(), error.localizedDescription.UTF8String));
+ ERR_PRINT(vformat(U"Error compiling shader %s: %s", entry->name.get_data(), error.localizedDescription.UTF8String));
}
{
diff --git a/drivers/metal/rendering_context_driver_metal.h b/drivers/metal/rendering_context_driver_metal.h
index 7e0b09186d..1fdc255f93 100644
--- a/drivers/metal/rendering_context_driver_metal.h
+++ b/drivers/metal/rendering_context_driver_metal.h
@@ -107,6 +107,7 @@ public:
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
+ double present_minimum_duration = 0.0;
Surface(
#ifdef __OBJC__
@@ -123,6 +124,7 @@ public:
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
virtual void present(MDCommandBuffer *p_cmd_buffer) = 0;
+ void set_max_fps(int p_max_fps) { present_minimum_duration = p_max_fps ? 1.0 / p_max_fps : 0.0; }
};
#ifdef __OBJC__
diff --git a/drivers/metal/rendering_context_driver_metal.mm b/drivers/metal/rendering_context_driver_metal.mm
index b97b586352..199ec25d79 100644
--- a/drivers/metal/rendering_context_driver_metal.mm
+++ b/drivers/metal/rendering_context_driver_metal.mm
@@ -134,7 +134,7 @@ public:
frame_buffers.resize(p_desired_framebuffer_count);
for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) {
// Reserve space for the drawable texture.
- frame_buffers[i].textures.resize(1);
+ frame_buffers[i].set_texture_count(1);
}
return OK;
@@ -154,7 +154,7 @@ public:
id<CAMetalDrawable> drawable = layer.nextDrawable;
ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available");
drawables[rear] = drawable;
- frame_buffer.textures.write[0] = drawable.texture;
+ frame_buffer.set_texture(0, drawable.texture);
return RDD::FramebufferID(&frame_buffer);
}
@@ -165,14 +165,14 @@ public:
}
// Release texture and drawable.
- frame_buffers[front].textures.write[0] = nil;
+ frame_buffers[front].unset_texture(0);
id<MTLDrawable> drawable = drawables[front];
drawables[front] = nil;
count--;
front = (front + 1) % frame_buffers.size();
- [p_cmd_buffer->get_command_buffer() presentDrawable:drawable];
+ [p_cmd_buffer->get_command_buffer() presentDrawable:drawable afterMinimumDuration:present_minimum_duration];
}
};
diff --git a/drivers/metal/rendering_device_driver_metal.h b/drivers/metal/rendering_device_driver_metal.h
index 7c23624e43..09ab7601e9 100644
--- a/drivers/metal/rendering_device_driver_metal.h
+++ b/drivers/metal/rendering_device_driver_metal.h
@@ -220,6 +220,7 @@ public:
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
+ virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
#pragma mark - Frame Buffer
@@ -239,7 +240,13 @@ private:
friend struct PushConstantData;
private:
- Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection);
+ /// Contains additional metadata about the shader.
+ struct ShaderMeta {
+ /// Indicates whether the shader uses multiview.
+ bool has_multiview = false;
+ };
+
+ Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta);
public:
virtual String shader_get_binary_cache_key() override final;
@@ -410,7 +417,7 @@ public:
virtual uint64_t api_trait_get(ApiTrait p_trait) override final;
virtual bool has_feature(Features p_feature) override final;
virtual const MultiviewCapabilities &get_multiview_capabilities() override final;
- virtual String get_api_name() const override final { return "Metal"; };
+ virtual String get_api_name() const override final { return "Metal"; }
virtual String get_api_version() const override final;
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index a4a408356a..784c9d5ae8 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -982,6 +982,12 @@ RDD::DataFormat RenderingDeviceDriverMetal::swap_chain_get_format(SwapChainID p_
return swap_chain->data_format;
}
+void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
+ RenderingContextDriverMetal::Surface *metal_surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface);
+ metal_surface->set_max_fps(p_max_fps);
+}
+
void RenderingDeviceDriverMetal::swap_chain_free(SwapChainID p_swap_chain) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
_swap_chain_release(swap_chain);
@@ -1026,7 +1032,7 @@ void RenderingDeviceDriverMetal::framebuffer_free(FramebufferID p_framebuffer) {
#pragma mark - Shader
-const uint32_t SHADER_BINARY_VERSION = 1;
+const uint32_t SHADER_BINARY_VERSION = 3;
// region Serialization
@@ -1330,23 +1336,32 @@ struct ComputeSize {
struct ShaderStageData {
RD::ShaderStage stage = RD::ShaderStage::SHADER_STAGE_MAX;
+ uint32_t is_position_invariant = UINT32_MAX;
+ uint32_t supports_fast_math = UINT32_MAX;
CharString entry_point_name;
CharString source;
size_t serialize_size() const {
int comp_size = Compression::get_max_compressed_buffer_size(source.length(), Compression::MODE_ZSTD);
return sizeof(uint32_t) // Stage.
- + sizeof(uint32_t) /* entry_point_name.utf8().length */ + entry_point_name.length() + sizeof(uint32_t) /* uncompressed size */ + sizeof(uint32_t) /* compressed size */ + comp_size;
+ + sizeof(uint32_t) // is_position_invariant
+ + sizeof(uint32_t) // supports_fast_math
+ + sizeof(uint32_t) /* entry_point_name.utf8().length */
+ + entry_point_name.length() + sizeof(uint32_t) /* uncompressed size */ + sizeof(uint32_t) /* compressed size */ + comp_size;
}
void serialize(BufWriter &p_writer) const {
p_writer.write((uint32_t)stage);
+ p_writer.write(is_position_invariant);
+ p_writer.write(supports_fast_math);
p_writer.write(entry_point_name);
p_writer.write_compressed(source);
}
void deserialize(BufReader &p_reader) {
p_reader.read((uint32_t &)stage);
+ p_reader.read(is_position_invariant);
+ p_reader.read(supports_fast_math);
p_reader.read(entry_point_name);
p_reader.read_compressed(source);
}
@@ -1503,6 +1518,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData {
uint32_t fragment_output_mask = UINT32_MAX;
uint32_t spirv_specialization_constants_ids_mask = UINT32_MAX;
uint32_t is_compute = UINT32_MAX;
+ uint32_t needs_view_mask_buffer = UINT32_MAX;
ComputeSize compute_local_size;
PushConstantData push_constant;
LocalVector<ShaderStageData> stages;
@@ -1523,6 +1539,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData {
size += sizeof(uint32_t); // fragment_output_mask
size += sizeof(uint32_t); // spirv_specialization_constants_ids_mask
size += sizeof(uint32_t); // is_compute
+ size += sizeof(uint32_t); // needs_view_mask_buffer
size += compute_local_size.serialize_size(); // compute_local_size
size += push_constant.serialize_size(); // push_constant
size += sizeof(uint32_t); // stages.size()
@@ -1547,6 +1564,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData {
p_writer.write(fragment_output_mask);
p_writer.write(spirv_specialization_constants_ids_mask);
p_writer.write(is_compute);
+ p_writer.write(needs_view_mask_buffer);
p_writer.write(compute_local_size);
p_writer.write(push_constant);
p_writer.write(VectorView(stages));
@@ -1561,6 +1579,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData {
p_reader.read(fragment_output_mask);
p_reader.read(spirv_specialization_constants_ids_mask);
p_reader.read(is_compute);
+ p_reader.read(needs_view_mask_buffer);
p_reader.read(compute_local_size);
p_reader.read(push_constant);
p_reader.read(stages);
@@ -1572,14 +1591,16 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData {
// endregion
String RenderingDeviceDriverMetal::shader_get_binary_cache_key() {
- return "Metal-SV" + uitos(SHADER_BINARY_VERSION);
+ static const String cache_key = "Metal-SV" + uitos(SHADER_BINARY_VERSION);
+ return cache_key;
}
-Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection) {
+Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta) {
using namespace spirv_cross;
using spirv_cross::Resource;
r_reflection = {};
+ r_shader_meta = {};
for (uint32_t i = 0; i < p_spirv.size(); i++) {
ShaderStageSPIRVData const &v = p_spirv[i];
@@ -1811,6 +1832,20 @@ Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVDa
}
}
+ for (const BuiltInResource &res : resources.builtin_inputs) {
+ if (res.builtin == spv::BuiltInViewIndex || res.builtin == spv::BuiltInViewportIndex) {
+ r_shader_meta.has_multiview = true;
+ }
+ }
+
+ if (!r_shader_meta.has_multiview) {
+ for (const BuiltInResource &res : resources.builtin_outputs) {
+ if (res.builtin == spv::BuiltInViewIndex || res.builtin == spv::BuiltInViewportIndex) {
+ r_shader_meta.has_multiview = true;
+ }
+ }
+ }
+
// Specialization constants.
for (SpecializationConstant const &constant : compiler.get_specialization_constants()) {
int32_t existing = -1;
@@ -1874,7 +1909,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
using spirv_cross::Resource;
ShaderReflection spirv_data;
- ERR_FAIL_COND_V(_reflect_spirv16(p_spirv, spirv_data), Result());
+ ShaderMeta shader_meta;
+ ERR_FAIL_COND_V(_reflect_spirv16(p_spirv, spirv_data, shader_meta), Result());
ShaderBinaryData bin_data{};
if (!p_shader_name.is_empty()) {
@@ -1893,6 +1929,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
bin_data.is_compute = spirv_data.is_compute;
bin_data.push_constant.size = spirv_data.push_constant_size;
bin_data.push_constant.stages = (ShaderStageUsage)(uint8_t)spirv_data.push_constant_stages;
+ bin_data.needs_view_mask_buffer = shader_meta.has_multiview ? 1 : 0;
for (uint32_t i = 0; i < spirv_data.uniform_sets.size(); i++) {
const ::Vector<ShaderUniform> &spirv_set = spirv_data.uniform_sets[i];
@@ -1947,6 +1984,11 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
msl_options.pad_fragment_output_components = true;
msl_options.r32ui_alignment_constant_id = R32UI_ALIGNMENT_CONSTANT_ID;
msl_options.agx_manual_cube_grad_fixup = true;
+ if (shader_meta.has_multiview) {
+ msl_options.multiview = true;
+ msl_options.multiview_layered_rendering = true;
+ msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX;
+ }
CompilerGLSL::Options options{};
options.vertex.flip_vert_y = true;
@@ -1978,7 +2020,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
ERR_FAIL_COND_V_MSG(compiler.get_entry_points_and_stages().size() != 1, Result(), "Expected a single entry point and stage.");
- EntryPoint &entry_point_stage = compiler.get_entry_points_and_stages().front();
+ SmallVector<EntryPoint> entry_pts_stages = compiler.get_entry_points_and_stages();
+ EntryPoint &entry_point_stage = entry_pts_stages.front();
SPIREntryPoint &entry_point = compiler.get_entry_point(entry_point_stage.name, entry_point_stage.execution_model);
// Process specialization constants.
@@ -2060,6 +2103,10 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
case BT::Sampler: {
primary.dataType = MTLDataTypeSampler;
+ primary.arrayLength = 1;
+ for (uint32_t const &a : a_type.array) {
+ primary.arrayLength *= a;
+ }
} break;
default: {
@@ -2067,7 +2114,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
} break;
}
- // Find array length.
+ // Find array length of image.
if (basetype == BT::Image || basetype == BT::SampledImage) {
primary.arrayLength = 1;
for (uint32_t const &a : a_type.array) {
@@ -2256,6 +2303,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
ShaderStageData stage_data;
stage_data.stage = v.shader_stage;
+ stage_data.is_position_invariant = compiler.is_position_invariant();
+ stage_data.supports_fast_math = !entry_point.flags.get(spv::ExecutionModeSignedZeroInfNanPreserve);
stage_data.entry_point_name = entry_point.name.c_str();
stage_data.source = source.c_str();
bin_data.stages.push_back(stage_data);
@@ -2328,7 +2377,8 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_bytecode(const Vect
ShaderCacheEntry *cd = memnew(ShaderCacheEntry(*this, key));
cd->name = binary_data.shader_name;
cd->stage = shader_data.stage;
-
+ options.preserveInvariance = shader_data.is_position_invariant;
+ options.fastMathEnabled = YES;
MDLibrary *library = [MDLibrary newLibraryWithCacheEntry:cd
device:device
source:source
@@ -2444,7 +2494,7 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_bytecode(const Vect
#endif
shader = cs;
} else {
- MDRenderShader *rs = new MDRenderShader(binary_data.shader_name, uniform_sets, libraries[ShaderStage::SHADER_STAGE_VERTEX], libraries[ShaderStage::SHADER_STAGE_FRAGMENT]);
+ MDRenderShader *rs = new MDRenderShader(binary_data.shader_name, (bool)binary_data.needs_view_mask_buffer, uniform_sets, libraries[ShaderStage::SHADER_STAGE_VERTEX], libraries[ShaderStage::SHADER_STAGE_FRAGMENT]);
uint32_t *vert_binding = binary_data.push_constant.msl_binding.getptr(SHADER_STAGE_VERTEX);
if (vert_binding) {
@@ -2952,6 +3002,7 @@ RDD::RenderPassID RenderingDeviceDriverMetal::render_pass_create(VectorView<Atta
for (uint32_t i = 0; i < subpass_count; i++) {
MDSubpass &subpass = subpasses.write[i];
subpass.subpass_index = i;
+ subpass.view_count = p_view_count;
subpass.input_references = p_subpasses[i].input_references;
subpass.color_references = p_subpasses[i].color_references;
subpass.depth_stencil_reference = p_subpasses[i].depth_stencil_reference;
@@ -3671,8 +3722,7 @@ void RenderingDeviceDriverMetal::set_object_name(ObjectType p_type, ID p_driver_
uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p_type, ID p_driver_id) {
switch (p_type) {
case DRIVER_RESOURCE_LOGICAL_DEVICE: {
- uintptr_t devicePtr = (uintptr_t)(__bridge void *)device;
- return (uint64_t)devicePtr;
+ return (uint64_t)(uintptr_t)(__bridge void *)device;
}
case DRIVER_RESOURCE_PHYSICAL_DEVICE: {
return 0;
@@ -3681,7 +3731,7 @@ uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p
return 0;
}
case DRIVER_RESOURCE_COMMAND_QUEUE: {
- return 0;
+ return (uint64_t)(uintptr_t)(__bridge void *)device_queue;
}
case DRIVER_RESOURCE_QUEUE_FAMILY: {
return 0;
@@ -3698,15 +3748,20 @@ uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p
case DRIVER_RESOURCE_SAMPLER: {
return p_driver_id.id;
}
- case DRIVER_RESOURCE_UNIFORM_SET:
+ case DRIVER_RESOURCE_UNIFORM_SET: {
return 0;
+ }
case DRIVER_RESOURCE_BUFFER: {
return p_driver_id.id;
}
- case DRIVER_RESOURCE_COMPUTE_PIPELINE:
- return 0;
- case DRIVER_RESOURCE_RENDER_PIPELINE:
- return 0;
+ case DRIVER_RESOURCE_COMPUTE_PIPELINE: {
+ MDComputePipeline *pipeline = (MDComputePipeline *)(p_driver_id.id);
+ return (uint64_t)(uintptr_t)(__bridge void *)pipeline->state;
+ }
+ case DRIVER_RESOURCE_RENDER_PIPELINE: {
+ MDRenderPipeline *pipeline = (MDRenderPipeline *)(p_driver_id.id);
+ return (uint64_t)(uintptr_t)(__bridge void *)pipeline->state;
+ }
default: {
return 0;
}
@@ -3838,7 +3893,7 @@ uint64_t RenderingDeviceDriverMetal::api_trait_get(ApiTrait p_trait) {
bool RenderingDeviceDriverMetal::has_feature(Features p_feature) {
switch (p_feature) {
case SUPPORTS_MULTIVIEW:
- return false;
+ return multiview_capabilities.is_supported;
case SUPPORTS_FSR_HALF_FLOAT:
return true;
case SUPPORTS_ATTACHMENT_VRS:
@@ -3947,6 +4002,18 @@ Error RenderingDeviceDriverMetal::initialize(uint32_t p_device_index, uint32_t p
metal_device_properties = memnew(MetalDeviceProperties(device));
pixel_formats = memnew(PixelFormats(device));
+ if (metal_device_properties->features.layeredRendering) {
+ multiview_capabilities.is_supported = true;
+ multiview_capabilities.max_view_count = metal_device_properties->limits.maxViewports;
+ // NOTE: I'm not sure what the limit is as I don't see it referenced anywhere
+ multiview_capabilities.max_instance_count = UINT32_MAX;
+
+ print_verbose("- Metal 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("- Metal multiview not supported");
+ }
// Check required features and abort if any of them is missing.
if (!metal_device_properties->features.imageCubeArray) {
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index fce37257b1..4268a66475 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -46,18 +46,18 @@ if env["builtin_libpng"]:
if "S_compiler" in env:
env_neon["CC"] = env["S_compiler"]
neon_sources = []
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/arm_init.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/filter_neon_intrinsics.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/filter_neon.S"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/palette_neon_intrinsics.c"))
thirdparty_obj += neon_sources
elif env["arch"].startswith("x86"):
env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"])
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c")
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/intel_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/filter_sse2_intrinsics.c")
elif env["arch"] == "ppc64":
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c")
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/powerpc_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/filter_vsx_intrinsics.c")
env.drivers_sources += thirdparty_obj
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index 585e882059..6456467cb9 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -100,7 +100,7 @@ class AudioDriverPulseAudio : public AudioDriver {
public:
virtual const char *get_name() const override {
return "PulseAudio";
- };
+ }
virtual Error init() override;
virtual void start() override;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 3d584341ed..4b92e5f8a1 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -41,6 +41,11 @@
#include <sys/types.h>
#include <unistd.h>
+#if defined(TOOLS_ENABLED)
+#include <limits.h>
+#include <stdlib.h>
+#endif
+
void FileAccessUnix::check_errors() const {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
@@ -87,6 +92,22 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
}
}
+#if defined(TOOLS_ENABLED)
+ if (p_mode_flags & READ) {
+ String real_path = get_real_path();
+ if (real_path != path) {
+ // Don't warn on symlinks, since they can be used to simply share addons on multiple projects.
+ if (real_path.to_lower() == path.to_lower()) {
+ // The File system is case insensitive, but other platforms can be sensitive to it
+ // To ease cross-platform development, we issue a warning if users try to access
+ // a file using the wrong case (which *works* on Windows and macOS, but won't on other
+ // platforms).
+ WARN_PRINT(vformat("Case mismatch opening requested file '%s', stored as '%s' in the filesystem. This file will not open when exported to other case-sensitive platforms.", path, real_path));
+ }
+ }
+ }
+#endif
+
if (is_backup_save_enabled() && (p_mode_flags == WRITE)) {
save_path = path;
// Create a temporary file in the same directory as the target file.
@@ -97,7 +118,7 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
- fchmod(fd, 0666);
+ fchmod(fd, 0644);
path = String::utf8(cs.ptr());
f = fdopen(fd, mode_string);
@@ -173,6 +194,26 @@ String FileAccessUnix::get_path_absolute() const {
return path;
}
+#if defined(TOOLS_ENABLED)
+String FileAccessUnix::get_real_path() const {
+ char *resolved_path = ::realpath(path.utf8().get_data(), nullptr);
+
+ if (!resolved_path) {
+ return path;
+ }
+
+ String result;
+ Error parse_ok = result.parse_utf8(resolved_path);
+ ::free(resolved_path);
+
+ if (parse_ok != OK) {
+ return path;
+ }
+
+ return result.simplify_path();
+}
+#endif
+
void FileAccessUnix::seek(uint64_t p_position) {
ERR_FAIL_NULL_MSG(f, "File must be opened before use.");
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 76f629f7c2..7caf8a14d7 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -51,6 +51,10 @@ class FileAccessUnix : public FileAccess {
void _close();
+#if defined(TOOLS_ENABLED)
+ String get_real_path() const; // Returns the resolved real path for the current open file.
+#endif
+
public:
static CloseNotificationFunc close_notification_func;
diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp
index 0a78429dec..fd60bec9d0 100644
--- a/drivers/unix/file_access_unix_pipe.cpp
+++ b/drivers/unix/file_access_unix_pipe.cpp
@@ -70,7 +70,7 @@ Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags)
struct stat st = {};
int err = stat(path.utf8().get_data(), &st);
if (err) {
- if (mkfifo(path.utf8().get_data(), 0666) != 0) {
+ if (mkfifo(path.utf8().get_data(), 0600) != 0) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 5987c1675d..5a1a26e874 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -28,22 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "ip_unix.h"
-
-#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
-
-#ifdef WINDOWS_ENABLED
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#include <iphlpapi.h>
-
-#include <stdio.h>
+#if defined(UNIX_ENABLED)
-#else // UNIX
+#include "ip_unix.h"
#include <netdb.h>
@@ -67,8 +54,6 @@
#include <net/if.h> // Order is important on OpenBSD, leave as last.
-#endif // UNIX
-
#include <string.h>
static IPAddress _sockaddr2ip(struct sockaddr *p_addr) {
@@ -108,7 +93,7 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
}
if (result == nullptr || result->ai_addr == nullptr) {
- print_verbose("Invalid response from getaddrinfo");
+ print_verbose("Invalid response from getaddrinfo.");
if (result) {
freeaddrinfo(result);
}
@@ -132,56 +117,6 @@ void IPUnix::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hos
freeaddrinfo(result);
}
-#if defined(WINDOWS_ENABLED)
-
-void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
- ULONG buf_size = 1024;
- IP_ADAPTER_ADDRESSES *addrs;
-
- while (true) {
- addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
- int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
- 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;
-
- while (adapter != nullptr) {
- Interface_Info info;
- info.name = adapter->AdapterName;
- info.name_friendly = adapter->FriendlyName;
- info.index = String::num_uint64(adapter->IfIndex);
-
- IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
- while (address != nullptr) {
- int family = address->Address.lpSockaddr->sa_family;
- 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) {
- r_interfaces->insert(info.name, info);
- }
- }
-
- memfree(addrs);
-}
-
-#else // UNIX
-
void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
struct ifaddrs *ifAddrStruct = nullptr;
struct ifaddrs *ifa = nullptr;
@@ -219,8 +154,6 @@ void IPUnix::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces)
}
}
-#endif // UNIX
-
void IPUnix::make_default() {
_create = _create_unix;
}
@@ -232,4 +165,4 @@ IP *IPUnix::_create_unix() {
IPUnix::IPUnix() {
}
-#endif // UNIX_ENABLED || WINDOWS_ENABLED
+#endif // UNIX_ENABLED
diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h
index 274b7c561e..7e496629ef 100644
--- a/drivers/unix/ip_unix.h
+++ b/drivers/unix/ip_unix.h
@@ -31,9 +31,9 @@
#ifndef IP_UNIX_H
#define IP_UNIX_H
-#include "core/io/ip.h"
+#if defined(UNIX_ENABLED)
-#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED)
+#include "core/io/ip.h"
class IPUnix : public IP {
GDCLASS(IPUnix, IP);
@@ -49,6 +49,6 @@ public:
IPUnix();
};
-#endif
+#endif // UNIX_ENABLED
#endif // IP_UNIX_H
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_unix.cpp
index 5caa33100e..3eaf1b2885 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_unix.cpp
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* net_socket_posix.cpp */
+/* net_socket_unix.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,13 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "net_socket_posix.h"
-
// Some proprietary Unix-derived platforms don't expose Unix sockets
// so this allows skipping this file to reimplement this API differently.
-#ifndef UNIX_SOCKET_UNAVAILABLE
+#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
-#if defined(UNIX_ENABLED)
+#include "net_socket_unix.h"
#include <errno.h>
#include <fcntl.h>
@@ -62,44 +60,11 @@
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
-// Some custom defines to minimize ifdefs
-#define SOCK_EMPTY -1
-#define SOCK_BUF(x) x
-#define SOCK_CBUF(x) x
-#define SOCK_IOCTL ioctl
-#define SOCK_FIONREAD_LEN_TYPE int
-#define SOCK_CLOSE ::close
-#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::connect(p_sock, p_addr, p_addr_len)
-
-/* Windows */
-#elif defined(WINDOWS_ENABLED)
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#include <mswsock.h>
-// Some custom defines to minimize ifdefs
-#define SOCK_EMPTY INVALID_SOCKET
-#define SOCK_BUF(x) (char *)(x)
-#define SOCK_CBUF(x) (const char *)(x)
-#define SOCK_IOCTL ioctlsocket
-#define SOCK_FIONREAD_LEN_TYPE unsigned long
-#define SOCK_CLOSE closesocket
-// connect is broken on windows under certain conditions, reasons unknown:
-// See https://github.com/godotengine/webrtc-native/issues/6
-#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::WSAConnect(p_sock, p_addr, p_addr_len, nullptr, nullptr, nullptr, nullptr)
-
-// Workaround missing flag in MinGW
-#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
-#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
-#endif
-
-#endif // UNIX_ENABLED
-
-size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) {
+size_t NetSocketUnix::_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
+ if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket.
- // IPv6 only socket with IPv4 address
+ // IPv6 only socket with IPv4 address.
ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
@@ -111,14 +76,14 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
addr6->sin6_addr = in6addr_any;
}
return sizeof(sockaddr_in6);
- } else { // IPv4 socket
+ } else { // IPv4 socket.
- // IPv4 socket with IPv6 address
+ // IPv4 socket with IPv6 address.
ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
addr4->sin_family = AF_INET;
- addr4->sin_port = htons(p_port); // short, network byte order
+ addr4->sin_port = htons(p_port); // Short, network byte order.
if (p_ip.is_valid()) {
memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
@@ -130,7 +95,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
}
}
-void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) {
+void NetSocketUnix::_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;
if (r_ip) {
@@ -150,34 +115,21 @@ void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_
}
}
-NetSocket *NetSocketPosix::_create_func() {
- return memnew(NetSocketPosix);
+NetSocket *NetSocketUnix::_create_func() {
+ return memnew(NetSocketUnix);
}
-void NetSocketPosix::make_default() {
-#if defined(WINDOWS_ENABLED)
- if (_create == nullptr) {
- WSADATA data;
- WSAStartup(MAKEWORD(2, 2), &data);
- }
-#endif
+void NetSocketUnix::make_default() {
_create = _create_func;
}
-void NetSocketPosix::cleanup() {
-#if defined(WINDOWS_ENABLED)
- if (_create != nullptr) {
- WSACleanup();
- }
- _create = nullptr;
-#endif
+void NetSocketUnix::cleanup() {
}
-NetSocketPosix::NetSocketPosix() :
- _sock(SOCK_EMPTY) {
+NetSocketUnix::NetSocketUnix() {
}
-NetSocketPosix::~NetSocketPosix() {
+NetSocketUnix::~NetSocketUnix() {
close();
}
@@ -188,30 +140,7 @@ NetSocketPosix::~NetSocketPosix() {
#pragma GCC diagnostic ignored "-Wlogical-op"
#endif
-NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
-#if defined(WINDOWS_ENABLED)
- int err = WSAGetLastError();
- if (err == WSAEISCONN) {
- return ERR_NET_IS_CONNECTED;
- }
- if (err == WSAEINPROGRESS || err == WSAEALREADY) {
- return ERR_NET_IN_PROGRESS;
- }
- 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;
- }
- if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
- return ERR_NET_BUFFER_TOO_SMALL;
- }
- print_verbose("Socket error: " + itos(err));
- return ERR_NET_OTHER;
-#else
+NetSocketUnix::NetError NetSocketUnix::_get_socket_error() const {
if (errno == EISCONN) {
return ERR_NET_IS_CONNECTED;
}
@@ -230,16 +159,15 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == ENOBUFS) {
return ERR_NET_BUFFER_TOO_SMALL;
}
- print_verbose("Socket error: " + itos(errno));
+ print_verbose("Socket error: " + itos(errno) + ".");
return ERR_NET_OTHER;
-#endif
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
-bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const {
+bool NetSocketUnix::_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()) {
@@ -250,11 +178,11 @@ bool NetSocketPosix::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) c
return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
}
-_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) {
+_FORCE_INLINE_ Error NetSocketUnix::_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);
- // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4
+ // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type;
// This needs to be the proper level for the multicast group, no matter if the socket is dual stacking.
int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
@@ -277,7 +205,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str
for (const IPAddress &F : c.ip_addresses) {
if (!F.is_ipv4()) {
- continue; // Wrong IP type
+ continue; // Wrong IP type.
}
if_ip = F;
break;
@@ -304,7 +232,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str
return OK;
}
-void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) {
+void NetSocketUnix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) {
_sock = p_sock;
_ip_type = p_ip_type;
_is_stream = p_is_stream;
@@ -312,15 +240,13 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_
_set_close_exec_enabled(true);
}
-void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) {
-#ifndef WINDOWS_ENABLED
+void NetSocketUnix::_set_close_exec_enabled(bool p_enabled) {
// Enable close on exec to avoid sharing with subprocesses. Off by default on Windows.
int opts = fcntl(_sock, F_GETFD);
fcntl(_sock, F_SETFD, opts | FD_CLOEXEC);
-#endif
}
-Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
+Error NetSocketUnix::open(Type p_sock_type, IP::Type &ip_type) {
ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
@@ -336,7 +262,7 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
_sock = socket(family, type, protocol);
- if (_sock == SOCK_EMPTY && ip_type == IP::TYPE_ANY) {
+ if (_sock == -1 && ip_type == IP::TYPE_ANY) {
// Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
// in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
ip_type = IP::TYPE_IPV4;
@@ -344,11 +270,11 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
_sock = socket(family, type, protocol);
}
- ERR_FAIL_COND_V(_sock == SOCK_EMPTY, FAILED);
+ ERR_FAIL_COND_V(_sock == -1, FAILED);
_ip_type = ip_type;
if (family == AF_INET6) {
- // Select IPv4 over IPv6 mapping
+ // Select IPv4 over IPv6 mapping.
set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
}
@@ -363,41 +289,27 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
// Disable descriptor sharing with subprocesses.
_set_close_exec_enabled(true);
-#if defined(WINDOWS_ENABLED)
- if (!_is_stream) {
- // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when
- // recv/recvfrom and an ICMP reply was received from a previous send/sendto.
- unsigned long disable = 0;
- if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) {
- print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows");
- }
- if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) {
- // This feature seems not to be supported on wine.
- print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows");
- }
- }
-#endif
#if defined(SO_NOSIGPIPE)
- // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS)
+ // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS).
int par = 1;
- if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, SOCK_CBUF(&par), sizeof(int)) != 0) {
- print_verbose("Unable to turn off SIGPIPE on socket");
+ if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) {
+ print_verbose("Unable to turn off SIGPIPE on socket.");
}
#endif
return OK;
}
-void NetSocketPosix::close() {
- if (_sock != SOCK_EMPTY) {
- SOCK_CLOSE(_sock);
+void NetSocketUnix::close() {
+ if (_sock != -1) {
+ ::close(_sock);
}
- _sock = SOCK_EMPTY;
+ _sock = -1;
_ip_type = IP::TYPE_NONE;
_is_stream = false;
}
-Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
+Error NetSocketUnix::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);
@@ -406,7 +318,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
- print_verbose("Failed to bind socket. Error: " + itos(err));
+ print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
close();
return ERR_UNAVAILABLE;
}
@@ -414,7 +326,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) {
return OK;
}
-Error NetSocketPosix::listen(int p_max_pending) {
+Error NetSocketUnix::listen(int p_max_pending) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
if (::listen(_sock, p_max_pending) != 0) {
@@ -427,26 +339,26 @@ Error NetSocketPosix::listen(int p_max_pending) {
return OK;
}
-Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
+Error NetSocketUnix::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);
struct sockaddr_storage addr;
size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
- if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
+ if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
switch (err) {
- // We are already connected
+ // We are already connected.
case ERR_NET_IS_CONNECTED:
return OK;
- // Still waiting to connect, try again in a while
+ // Still waiting to connect, try again in a while.
case ERR_NET_WOULD_BLOCK:
case ERR_NET_IN_PROGRESS:
return ERR_BUSY;
default:
- print_verbose("Connection to remote host failed!");
+ print_verbose("Connection to remote host failed.");
close();
return FAILED;
}
@@ -455,66 +367,9 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) {
return OK;
}
-Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
+Error NetSocketUnix::poll(PollType p_type, int p_timeout) const {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
-#if defined(WINDOWS_ENABLED)
- bool ready = false;
- fd_set rd, wr, ex;
- fd_set *rdp = nullptr;
- fd_set *wrp = nullptr;
- FD_ZERO(&rd);
- FD_ZERO(&wr);
- FD_ZERO(&ex);
- FD_SET(_sock, &ex);
- 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.
- tp = &timeout;
- }
-
- switch (p_type) {
- case POLL_TYPE_IN:
- FD_SET(_sock, &rd);
- rdp = &rd;
- break;
- case POLL_TYPE_OUT:
- FD_SET(_sock, &wr);
- wrp = &wr;
- break;
- case POLL_TYPE_IN_OUT:
- FD_SET(_sock, &rd);
- FD_SET(_sock, &wr);
- rdp = &rd;
- wrp = &wr;
- }
- int ret = select(1, rdp, wrp, &ex, tp);
-
- if (ret == SOCKET_ERROR) {
- return FAILED;
- }
-
- if (ret == 0) {
- return ERR_BUSY;
- }
-
- if (FD_ISSET(_sock, &ex)) {
- _get_socket_error();
- print_verbose("Exception when polling socket.");
- return FAILED;
- }
-
- if (rdp && FD_ISSET(_sock, rdp)) {
- ready = true;
- }
- if (wrp && FD_ISSET(_sock, wrp)) {
- ready = true;
- }
-
- return ready ? OK : ERR_BUSY;
-#else
struct pollfd pfd;
pfd.fd = _sock;
pfd.events = POLLIN;
@@ -544,13 +399,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
}
return OK;
-#endif
}
-Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
+Error NetSocketUnix::recv(uint8_t *p_buffer, int p_len, int &r_read) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
- r_read = ::recv(_sock, SOCK_BUF(p_buffer), p_len, 0);
+ r_read = ::recv(_sock, p_buffer, p_len, 0);
if (r_read < 0) {
NetError err = _get_socket_error();
@@ -568,14 +422,14 @@ 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, IPAddress &r_ip, uint16_t &r_port, bool p_peek) {
+Error NetSocketUnix::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;
socklen_t len = sizeof(struct sockaddr_storage);
memset(&from, 0, len);
- r_read = ::recvfrom(_sock, SOCK_BUF(p_buffer), p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
+ r_read = ::recvfrom(_sock, p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
if (r_read < 0) {
NetError err = _get_socket_error();
@@ -606,7 +460,7 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr
return OK;
}
-Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
+Error NetSocketUnix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
int flags = 0;
@@ -615,7 +469,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
flags = MSG_NOSIGNAL;
}
#endif
- r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags);
+ r_sent = ::send(_sock, p_buffer, p_len, flags);
if (r_sent < 0) {
NetError err = _get_socket_error();
@@ -632,12 +486,12 @@ 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, IPAddress p_ip, uint16_t p_port) {
+Error NetSocketUnix::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;
size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
- r_sent = ::sendto(_sock, SOCK_CBUF(p_buffer), p_len, 0, (struct sockaddr *)&addr, addr_size);
+ r_sent = ::sendto(_sock, p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size);
if (r_sent < 0) {
NetError err = _get_socket_error();
@@ -654,7 +508,7 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP
return OK;
}
-Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) {
+Error NetSocketUnix::set_broadcasting_enabled(bool p_enabled) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
// IPv6 has no broadcast support.
if (_ip_type == IP::TYPE_IPV6) {
@@ -662,90 +516,68 @@ Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) {
}
int par = p_enabled ? 1 : 0;
- if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, SOCK_CBUF(&par), sizeof(int)) != 0) {
- WARN_PRINT("Unable to change broadcast setting");
+ if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, &par, sizeof(int)) != 0) {
+ WARN_PRINT("Unable to change broadcast setting.");
return FAILED;
}
return OK;
}
-void NetSocketPosix::set_blocking_enabled(bool p_enabled) {
+void NetSocketUnix::set_blocking_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
int ret = 0;
-#if defined(WINDOWS_ENABLED)
- unsigned long par = p_enabled ? 0 : 1;
- ret = SOCK_IOCTL(_sock, FIONBIO, &par);
-#else
int opts = fcntl(_sock, F_GETFL);
if (p_enabled) {
ret = fcntl(_sock, F_SETFL, opts & ~O_NONBLOCK);
} else {
ret = fcntl(_sock, F_SETFL, opts | O_NONBLOCK);
}
-#endif
if (ret != 0) {
- WARN_PRINT("Unable to change non-block mode");
+ WARN_PRINT("Unable to change non-block mode.");
}
}
-void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) {
+void NetSocketUnix::set_ipv6_only_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
// This option is only available in IPv6 sockets.
ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4);
int par = p_enabled ? 1 : 0;
- if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, SOCK_CBUF(&par), sizeof(int)) != 0) {
- WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option");
+ if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, &par, sizeof(int)) != 0) {
+ WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option.");
}
}
-void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) {
+void NetSocketUnix::set_tcp_no_delay_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
- ERR_FAIL_COND(!_is_stream); // Not TCP
+ ERR_FAIL_COND(!_is_stream); // Not TCP.
int par = p_enabled ? 1 : 0;
- if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) {
- ERR_PRINT("Unable to set TCP no delay option");
+ if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &par, sizeof(int)) < 0) {
+ WARN_PRINT("Unable to set TCP no delay option.");
}
}
-void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) {
+void NetSocketUnix::set_reuse_address_enabled(bool p_enabled) {
ERR_FAIL_COND(!is_open());
-// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying...
-// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT
-#ifndef WINDOWS_ENABLED
- int par = p_enabled ? 1 : 0;
- if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, SOCK_CBUF(&par), sizeof(int)) < 0) {
- WARN_PRINT("Unable to set socket REUSEADDR option!");
- }
-#endif
-}
-
-void NetSocketPosix::set_reuse_port_enabled(bool p_enabled) {
- ERR_FAIL_COND(!is_open());
-
-// See comment above...
-#ifdef WINDOWS_ENABLED
-#define SO_REUSEPORT SO_REUSEADDR
-#endif
int par = p_enabled ? 1 : 0;
- if (setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, SOCK_CBUF(&par), sizeof(int)) < 0) {
- WARN_PRINT("Unable to set socket REUSEPORT option!");
+ if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &par, sizeof(int)) < 0) {
+ WARN_PRINT("Unable to set socket REUSEADDR option.");
}
}
-bool NetSocketPosix::is_open() const {
- return _sock != SOCK_EMPTY;
+bool NetSocketUnix::is_open() const {
+ return _sock != -1;
}
-int NetSocketPosix::get_available_bytes() const {
+int NetSocketUnix::get_available_bytes() const {
ERR_FAIL_COND_V(!is_open(), -1);
- SOCK_FIONREAD_LEN_TYPE len;
- int ret = SOCK_IOCTL(_sock, FIONREAD, &len);
+ int len;
+ int ret = ioctl(_sock, FIONREAD, &len);
if (ret == -1) {
_get_socket_error();
print_verbose("Error when checking available bytes on socket.");
@@ -754,7 +586,7 @@ int NetSocketPosix::get_available_bytes() const {
return len;
}
-Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
+Error NetSocketUnix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const {
ERR_FAIL_COND_V(!is_open(), FAILED);
struct sockaddr_storage saddr;
@@ -768,14 +600,14 @@ Error NetSocketPosix::get_socket_address(IPAddress *r_ip, uint16_t *r_port) cons
return OK;
}
-Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
+Ref<NetSocket> NetSocketUnix::accept(IPAddress &r_ip, uint16_t &r_port) {
Ref<NetSocket> out;
ERR_FAIL_COND_V(!is_open(), out);
struct sockaddr_storage their_addr;
socklen_t size = sizeof(their_addr);
- SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
- if (fd == SOCK_EMPTY) {
+ int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
+ if (fd == -1) {
_get_socket_error();
print_verbose("Error when accepting socket connection.");
return out;
@@ -783,18 +615,18 @@ Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) {
_set_ip_port(&their_addr, &r_ip, &r_port);
- NetSocketPosix *ns = memnew(NetSocketPosix);
+ NetSocketUnix *ns = memnew(NetSocketUnix);
ns->_set_socket(fd, _ip_type, _is_stream);
ns->set_blocking_enabled(false);
return Ref<NetSocket>(ns);
}
-Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
+Error NetSocketUnix::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, true);
}
-Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
+Error NetSocketUnix::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
return _change_multicast_group(p_multi_address, p_if_name, false);
}
-#endif // UNIX_SOCKET_UNAVAILABLE
+#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE
diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_unix.h
index aa59ff36ee..08c45f2ac3 100644
--- a/drivers/unix/net_socket_posix.h
+++ b/drivers/unix/net_socket_unix.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* net_socket_posix.h */
+/* net_socket_unix.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,25 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef NET_SOCKET_POSIX_H
-#define NET_SOCKET_POSIX_H
+#ifndef NET_SOCKET_UNIX_H
+#define NET_SOCKET_UNIX_H
-#include "core/io/net_socket.h"
+#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE)
-#if defined(WINDOWS_ENABLED)
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#define SOCKET_TYPE SOCKET
+#include "core/io/net_socket.h"
-#else
#include <sys/socket.h>
-#define SOCKET_TYPE int
-#endif
-
-class NetSocketPosix : public NetSocket {
+class NetSocketUnix : public NetSocket {
private:
- SOCKET_TYPE _sock; // NOLINT - the default value is defined in the .cpp
+ int _sock = -1;
IP::Type _ip_type = IP::TYPE_NONE;
bool _is_stream = false;
@@ -61,7 +54,7 @@ private:
};
NetError _get_socket_error() const;
- void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream);
+ void _set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream);
_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);
@@ -76,33 +69,34 @@ public:
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(IPAddress p_addr, uint16_t p_port);
- virtual Error listen(int p_max_pending);
- 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, 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, 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);
- virtual void set_ipv6_only_enabled(bool p_enabled);
- 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 IPAddress &p_multi_address, const String &p_if_name);
- virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name);
-
- NetSocketPosix();
- ~NetSocketPosix();
+ virtual Error open(Type p_sock_type, IP::Type &ip_type) override;
+ virtual void close() override;
+ virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
+ virtual Error listen(int p_max_pending) override;
+ virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
+ virtual Error poll(PollType p_type, int timeout) const override;
+ virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
+ virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
+ virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
+
+ virtual bool is_open() const override;
+ virtual int get_available_bytes() const override;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
+
+ virtual Error set_broadcasting_enabled(bool p_enabled) override;
+ virtual void set_blocking_enabled(bool p_enabled) override;
+ virtual void set_ipv6_only_enabled(bool p_enabled) override;
+ virtual void set_tcp_no_delay_enabled(bool p_enabled) override;
+ virtual void set_reuse_address_enabled(bool p_enabled) override;
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
+
+ NetSocketUnix();
+ ~NetSocketUnix() override;
};
-#endif // NET_SOCKET_POSIX_H
+#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE
+
+#endif // NET_SOCKET_UNIX_H
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 8a9b130068..ffc270cd36 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -38,7 +38,7 @@
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "drivers/unix/file_access_unix_pipe.h"
-#include "drivers/unix/net_socket_posix.h"
+#include "drivers/unix/net_socket_unix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@@ -77,6 +77,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
@@ -166,7 +167,9 @@ void OS_Unix::initialize_core() {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
- NetSocketPosix::make_default();
+#ifndef UNIX_SOCKET_UNAVAILABLE
+ NetSocketUnix::make_default();
+#endif
IPUnix::make_default();
process_map = memnew((HashMap<ProcessID, ProcessInfo>));
@@ -175,16 +178,96 @@ void OS_Unix::initialize_core() {
void OS_Unix::finalize_core() {
memdelete(process_map);
- NetSocketPosix::cleanup();
+#ifndef UNIX_SOCKET_UNAVAILABLE
+ NetSocketUnix::cleanup();
+#endif
}
Vector<String> OS_Unix::get_video_adapter_driver_info() const {
return Vector<String>();
}
-String OS_Unix::get_stdin_string() {
- char buff[1024];
- return String::utf8(fgets(buff, 1024, stdin));
+String OS_Unix::get_stdin_string(int64_t p_buffer_size) {
+ Vector<uint8_t> data;
+ data.resize(p_buffer_size);
+ if (fgets((char *)data.ptrw(), data.size(), stdin)) {
+ return String::utf8((char *)data.ptr());
+ }
+ return String();
+}
+
+PackedByteArray OS_Unix::get_stdin_buffer(int64_t p_buffer_size) {
+ Vector<uint8_t> data;
+ data.resize(p_buffer_size);
+ size_t sz = fread((void *)data.ptrw(), 1, data.size(), stdin);
+ if (sz > 0) {
+ data.resize(sz);
+ return data;
+ }
+ return PackedByteArray();
+}
+
+OS_Unix::StdHandleType OS_Unix::get_stdin_type() const {
+ int h = fileno(stdin);
+ if (h == -1) {
+ return STD_HANDLE_INVALID;
+ }
+
+ if (isatty(h)) {
+ return STD_HANDLE_CONSOLE;
+ }
+ struct stat statbuf;
+ if (fstat(h, &statbuf) < 0) {
+ return STD_HANDLE_UNKNOWN;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ return STD_HANDLE_PIPE;
+ } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ return STD_HANDLE_FILE;
+ }
+ return STD_HANDLE_UNKNOWN;
+}
+
+OS_Unix::StdHandleType OS_Unix::get_stdout_type() const {
+ int h = fileno(stdout);
+ if (h == -1) {
+ return STD_HANDLE_INVALID;
+ }
+
+ if (isatty(h)) {
+ return STD_HANDLE_CONSOLE;
+ }
+ struct stat statbuf;
+ if (fstat(h, &statbuf) < 0) {
+ return STD_HANDLE_UNKNOWN;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ return STD_HANDLE_PIPE;
+ } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ return STD_HANDLE_FILE;
+ }
+ return STD_HANDLE_UNKNOWN;
+}
+
+OS_Unix::StdHandleType OS_Unix::get_stderr_type() const {
+ int h = fileno(stderr);
+ if (h == -1) {
+ return STD_HANDLE_INVALID;
+ }
+
+ if (isatty(h)) {
+ return STD_HANDLE_CONSOLE;
+ }
+ struct stat statbuf;
+ if (fstat(h, &statbuf) < 0) {
+ return STD_HANDLE_UNKNOWN;
+ }
+ if (S_ISFIFO(statbuf.st_mode)) {
+ return STD_HANDLE_PIPE;
+ } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
+ return STD_HANDLE_FILE;
+ }
+ return STD_HANDLE_UNKNOWN;
}
Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) {
@@ -777,7 +860,7 @@ String OS_Unix::get_locale() const {
}
String locale = get_environment("LANG");
- int tp = locale.find(".");
+ int tp = locale.find_char('.');
if (tp != -1) {
locale = locale.substr(0, tp);
}
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 3add5df055..2c7920c142 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -58,7 +58,11 @@ public:
virtual Vector<String> get_video_adapter_driver_info() const override;
- virtual String get_stdin_string() override;
+ virtual String get_stdin_string(int64_t p_buffer_size = 1024) override;
+ virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) override;
+ virtual StdHandleType get_stdin_type() const override;
+ virtual StdHandleType get_stdout_type() const override;
+ virtual StdHandleType get_stderr_type() const override;
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 154095552b..b6e5ed0287 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -35,6 +35,16 @@
#include "thirdparty/misc/smolv.h"
#include "vulkan_hooks.h"
+#if defined(ANDROID_ENABLED)
+#include "platform/android/java_godot_wrapper.h"
+#include "platform/android/os_android.h"
+#include "platform/android/thread_jandroid.h"
+#endif
+
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+#include "thirdparty/swappy-frame-pacing/swappyVk.h"
+#endif
+
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define PRINT_NATIVE_COMMANDS 0
@@ -43,6 +53,10 @@
/**** GENERIC ****/
/*****************/
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+static const uint32_t BREADCRUMB_BUFFER_ENTRIES = 512u;
+#endif
+
static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
VK_FORMAT_R4G4_UNORM_PACK8,
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
@@ -534,6 +548,37 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
err = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, device_extensions.ptr());
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ if (swappy_frame_pacer_enable) {
+ char **swappy_required_extensions;
+ uint32_t swappy_required_extensions_count = 0;
+ // Determine number of extensions required by Swappy frame pacer.
+ SwappyVk_determineDeviceExtensions(physical_device, device_extension_count, device_extensions.ptr(), &swappy_required_extensions_count, nullptr);
+
+ if (swappy_required_extensions_count < device_extension_count) {
+ // Determine the actual extensions.
+ swappy_required_extensions = (char **)malloc(swappy_required_extensions_count * sizeof(char *));
+ char *pRequiredExtensionsData = (char *)malloc(swappy_required_extensions_count * (VK_MAX_EXTENSION_NAME_SIZE + 1));
+ for (uint32_t i = 0; i < swappy_required_extensions_count; i++) {
+ swappy_required_extensions[i] = &pRequiredExtensionsData[i * (VK_MAX_EXTENSION_NAME_SIZE + 1)];
+ }
+ SwappyVk_determineDeviceExtensions(physical_device, device_extension_count,
+ device_extensions.ptr(), &swappy_required_extensions_count, swappy_required_extensions);
+
+ // Enable extensions requested by Swappy.
+ for (uint32_t i = 0; i < swappy_required_extensions_count; i++) {
+ CharString extension_name(swappy_required_extensions[i]);
+ if (requested_device_extensions.has(extension_name)) {
+ enabled_device_extension_names.insert(extension_name);
+ }
+ }
+
+ free(pRequiredExtensionsData);
+ free(swappy_required_extensions);
+ }
+ }
+#endif
+
#ifdef DEV_ENABLED
for (uint32_t i = 0; i < device_extension_count; i++) {
print_verbose(String("VULKAN: Found device extension ") + String::utf8(device_extensions[i].extensionName));
@@ -1370,7 +1415,22 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
ERR_FAIL_COND_V(err != OK, err);
max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool");
- breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
+
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ breadcrumb_buffer = buffer_create(2u * sizeof(uint32_t) * BREADCRUMB_BUFFER_ENTRIES, BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
+#endif
+
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ swappy_frame_pacer_enable = GLOBAL_GET("display/window/frame_pacing/android/enable_frame_pacing");
+ swappy_mode = GLOBAL_GET("display/window/frame_pacing/android/swappy_mode");
+
+ if (VulkanHooks::get_singleton() != nullptr) {
+ // Hooks control device creation & possibly presentation
+ // (e.g. OpenXR) thus it's too risky to use Swappy.
+ swappy_frame_pacer_enable = false;
+ OS::get_singleton()->print("VulkanHooks detected (e.g. OpenXR): Force-disabling Swappy Frame Pacing.\n");
+ }
+#endif
return OK;
}
@@ -1459,7 +1519,7 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
err = vmaAllocateMemoryForBuffer(allocator, vk_buffer, &alloc_create_info, &allocation, &alloc_info);
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't allocate memory for buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
- err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, NULL);
+ err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, nullptr);
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't bind memory to buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
// Bookkeep.
@@ -1685,7 +1745,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImage failed with error " + itos(err) + ".");
err = vmaAllocateMemoryForImage(allocator, vk_image, &alloc_create_info, &allocation, &alloc_info);
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't allocate memory for image, error: " + itos(err) + ".");
- err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, NULL);
+ err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, nullptr);
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't bind memory to image, error: " + itos(err) + ".");
// Create view.
@@ -2332,7 +2392,9 @@ RDD::CommandQueueFamilyID RenderingDeviceDriverVulkan::command_queue_family_get(
}
}
- ERR_FAIL_COND_V_MSG(picked_family_index >= queue_family_properties.size(), CommandQueueFamilyID(), "A queue family with the requested bits could not be found.");
+ if (picked_family_index >= queue_family_properties.size()) {
+ return CommandQueueFamilyID();
+ }
// Since 0 is a valid index and we use 0 as the error case, we make the index start from 1 instead.
return CommandQueueFamilyID(picked_family_index + 1);
@@ -2357,6 +2419,14 @@ RDD::CommandQueueID RenderingDeviceDriverVulkan::command_queue_create(CommandQue
ERR_FAIL_COND_V_MSG(picked_queue_index >= queue_family.size(), CommandQueueID(), "A queue in the picked family could not be found.");
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ if (swappy_frame_pacer_enable) {
+ VkQueue selected_queue;
+ vkGetDeviceQueue(vk_device, family_index, picked_queue_index, &selected_queue);
+ SwappyVk_setQueueFamilyIndex(vk_device, selected_queue, family_index);
+ }
+#endif
+
// Create the virtual queue.
CommandQueue *command_queue = memnew(CommandQueue);
command_queue->queue_family = family_index;
@@ -2502,7 +2572,16 @@ Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueu
present_info.pResults = results.ptr();
device_queue.submit_mutex.lock();
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ if (swappy_frame_pacer_enable) {
+ err = SwappyVk_queuePresent(device_queue.queue, &present_info);
+ } else {
+ err = device_functions.QueuePresentKHR(device_queue.queue, &present_info);
+ }
+#else
err = device_functions.QueuePresentKHR(device_queue.queue, &present_info);
+#endif
+
device_queue.submit_mutex.unlock();
// Set the index to an invalid value. If any of the swap chains returned out of date, indicate it should be resized the next time it's acquired.
@@ -2534,7 +2613,10 @@ Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueu
// it'll lead to very low performance in Android by entering an endless loop where it'll always resize the swap chain
// every frame.
- ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, FAILED);
+ ERR_FAIL_COND_V_MSG(
+ err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR,
+ FAILED,
+ "QueuePresentKHR failed with error: " + get_vulkan_result(err));
}
return OK;
@@ -2637,11 +2719,13 @@ bool RenderingDeviceDriverVulkan::command_buffer_begin(CommandBufferID p_cmd_buf
bool RenderingDeviceDriverVulkan::command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) {
// Reset is implicit (VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT).
+ Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
+
VkCommandBufferInheritanceInfo inheritance_info = {};
inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
inheritance_info.renderPass = (VkRenderPass)p_render_pass.id;
inheritance_info.subpass = p_subpass;
- inheritance_info.framebuffer = (VkFramebuffer)p_framebuffer.id;
+ inheritance_info.framebuffer = framebuffer->vk_framebuffer;
VkCommandBufferBeginInfo cmd_buf_begin_info = {};
cmd_buf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
@@ -2682,6 +2766,14 @@ void RenderingDeviceDriverVulkan::_swap_chain_release(SwapChain *swap_chain) {
swap_chain->framebuffers.clear();
if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ if (swappy_frame_pacer_enable) {
+ // Swappy has a bug where the ANativeWindow will be leaked if we call
+ // SwappyVk_destroySwapchain, so we must release it by hand.
+ SwappyVk_setWindow(vk_device, swap_chain->vk_swapchain, nullptr);
+ SwappyVk_destroySwapchain(vk_device, swap_chain->vk_swapchain);
+ }
+#endif
device_functions.DestroySwapchainKHR(vk_device, swap_chain->vk_swapchain, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_SWAPCHAIN_KHR));
swap_chain->vk_swapchain = VK_NULL_HANDLE;
}
@@ -2798,9 +2890,30 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
VkResult err = functions.GetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface->vk_surface, &surface_capabilities);
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
+ // No swapchain yet, this is the first time we're creating it.
+ if (!swap_chain->vk_swapchain) {
+ if (surface_capabilities.currentExtent.width == 0xFFFFFFFF) {
+ // The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
+ // We make sure to overwrite surface_capabilities.currentExtent.width so that the same check further below
+ // does not set extent.width = CLAMP( surface->width, ... ) on the first run of this function, because
+ // that'd be potentially unswapped.
+ surface_capabilities.currentExtent.width = CLAMP(surface->width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
+ surface_capabilities.currentExtent.height = CLAMP(surface->height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
+ }
+
+ // We must SWAP() only once otherwise we'll keep ping-ponging between
+ // the right and wrong resolutions after multiple calls to swap_chain_resize().
+ if (surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
+ surface_capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
+ // Swap to get identity width and height.
+ SWAP(surface_capabilities.currentExtent.width, surface_capabilities.currentExtent.height);
+ }
+ }
+
VkExtent2D extent;
if (surface_capabilities.currentExtent.width == 0xFFFFFFFF) {
// The current extent is currently undefined, so the current surface width and height will be clamped to the surface's capabilities.
+ // We can only be here on the second call to swap_chain_resize(), by which time surface->width & surface->height should already be swapped if needed.
extent.width = CLAMP(surface->width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
extent.height = CLAMP(surface->height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
} else {
@@ -2848,9 +2961,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
}
bool present_mode_available = present_modes.has(present_mode);
- if (present_mode_available) {
- print_verbose("Using present mode: " + present_mode_name);
- } else {
+ if (!present_mode_available) {
// Present mode is not available, fall back to FIFO which is guaranteed to be supported.
WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled.", present_mode_name));
surface->vsync_mode = DisplayServer::VSYNC_ENABLED;
@@ -2864,15 +2975,8 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
desired_swapchain_images = MIN(desired_swapchain_images, surface_capabilities.maxImageCount);
}
- // Prefer identity transform if it's supported, use the current transform otherwise.
- // This behavior is intended as Godot does not supported native rotation in platforms that use these bits.
// Refer to the comment in command_queue_present() for more details.
- VkSurfaceTransformFlagBitsKHR surface_transform_bits;
- if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
- surface_transform_bits = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
- } else {
- surface_transform_bits = surface_capabilities.currentTransform;
- }
+ VkSurfaceTransformFlagBitsKHR surface_transform_bits = surface_capabilities.currentTransform;
VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
if (OS::get_singleton()->is_layered_allowed() || !(surface_capabilities.supportedCompositeAlpha & composite_alpha)) {
@@ -2904,12 +3008,60 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swap_create_info.preTransform = surface_transform_bits;
+ switch (swap_create_info.preTransform) {
+ case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 0;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 90;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 180;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 270;
+ break;
+ default:
+ WARN_PRINT("Unexpected swap_create_info.preTransform = " + itos(swap_create_info.preTransform) + ".");
+ swap_chain->pre_transform_rotation_degrees = 0;
+ break;
+ }
swap_create_info.compositeAlpha = composite_alpha;
swap_create_info.presentMode = present_mode;
swap_create_info.clipped = true;
err = device_functions.CreateSwapchainKHR(vk_device, &swap_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_SWAPCHAIN_KHR), &swap_chain->vk_swapchain);
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ if (swappy_frame_pacer_enable) {
+ SwappyVk_initAndGetRefreshCycleDuration(get_jni_env(), static_cast<OS_Android *>(OS::get_singleton())->get_godot_java()->get_activity(), physical_device,
+ vk_device, swap_chain->vk_swapchain, &swap_chain->refresh_duration);
+ SwappyVk_setWindow(vk_device, swap_chain->vk_swapchain, static_cast<OS_Android *>(OS::get_singleton())->get_native_window());
+ SwappyVk_setSwapIntervalNS(vk_device, swap_chain->vk_swapchain, swap_chain->refresh_duration);
+
+ enum SwappyModes {
+ PIPELINE_FORCED_ON,
+ AUTO_FPS_PIPELINE_FORCED_ON,
+ AUTO_FPS_AUTO_PIPELINE,
+ };
+
+ switch (swappy_mode) {
+ case PIPELINE_FORCED_ON:
+ SwappyVk_setAutoSwapInterval(true);
+ SwappyVk_setAutoPipelineMode(true);
+ break;
+ case AUTO_FPS_PIPELINE_FORCED_ON:
+ SwappyVk_setAutoSwapInterval(true);
+ SwappyVk_setAutoPipelineMode(false);
+ break;
+ case AUTO_FPS_AUTO_PIPELINE:
+ SwappyVk_setAutoSwapInterval(false);
+ SwappyVk_setAutoPipelineMode(false);
+ break;
+ }
+ }
+#endif
+
uint32_t image_count = 0;
err = device_functions.GetSwapchainImagesKHR(vk_device, swap_chain->vk_swapchain, &image_count, nullptr);
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
@@ -2951,12 +3103,16 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
fb_create_info.height = surface->height;
fb_create_info.layers = 1;
- VkFramebuffer framebuffer;
+ VkFramebuffer vk_framebuffer;
for (uint32_t i = 0; i < image_count; i++) {
fb_create_info.pAttachments = &swap_chain->image_views[i];
- err = vkCreateFramebuffer(vk_device, &fb_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER), &framebuffer);
+ err = vkCreateFramebuffer(vk_device, &fb_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER), &vk_framebuffer);
ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
+ Framebuffer *framebuffer = memnew(Framebuffer);
+ framebuffer->vk_framebuffer = vk_framebuffer;
+ framebuffer->swap_chain_image = swap_chain->images[i];
+ framebuffer->swap_chain_image_subresource_range = view_create_info.subresourceRange;
swap_chain->framebuffers.push_back(RDD::FramebufferID(framebuffer));
}
@@ -3025,7 +3181,10 @@ RDD::FramebufferID RenderingDeviceDriverVulkan::swap_chain_acquire_framebuffer(C
command_queue->pending_semaphores_for_fence.push_back(semaphore_index);
// Return the corresponding framebuffer to the new current image.
- return swap_chain->framebuffers[swap_chain->image_index];
+ FramebufferID framebuffer_id = swap_chain->framebuffers[swap_chain->image_index];
+ Framebuffer *framebuffer = (Framebuffer *)(framebuffer_id.id);
+ framebuffer->swap_chain_acquired = true;
+ return framebuffer_id;
}
RDD::RenderPassID RenderingDeviceDriverVulkan::swap_chain_get_render_pass(SwapChainID p_swap_chain) {
@@ -3035,6 +3194,13 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::swap_chain_get_render_pass(SwapCh
return swap_chain->render_pass;
}
+int RenderingDeviceDriverVulkan::swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) {
+ DEV_ASSERT(p_swap_chain.id != 0);
+
+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
+ return swap_chain->pre_transform_rotation_degrees;
+}
+
RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
@@ -3050,6 +3216,22 @@ RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p
}
}
+void RenderingDeviceDriverVulkan::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
+ DEV_ASSERT(p_swap_chain.id != 0);
+
+#ifdef SWAPPY_FRAME_PACING_ENABLED
+ if (!swappy_frame_pacer_enable) {
+ return;
+ }
+
+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
+ if (swap_chain->vk_swapchain != VK_NULL_HANDLE) {
+ const uint64_t max_time = p_max_fps > 0 ? uint64_t((1000.0 * 1000.0 * 1000.0) / p_max_fps) : 0;
+ SwappyVk_setSwapIntervalNS(vk_device, swap_chain->vk_swapchain, MAX(swap_chain->refresh_duration, max_time));
+ }
+#endif
+}
+
void RenderingDeviceDriverVulkan::swap_chain_free(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
@@ -3094,11 +3276,15 @@ RDD::FramebufferID RenderingDeviceDriverVulkan::framebuffer_create(RenderPassID
}
#endif
- return FramebufferID(vk_framebuffer);
+ Framebuffer *framebuffer = memnew(Framebuffer);
+ framebuffer->vk_framebuffer = vk_framebuffer;
+ return FramebufferID(framebuffer);
}
void RenderingDeviceDriverVulkan::framebuffer_free(FramebufferID p_framebuffer) {
- vkDestroyFramebuffer(vk_device, (VkFramebuffer)p_framebuffer.id, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER));
+ Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
+ vkDestroyFramebuffer(vk_device, framebuffer->vk_framebuffer, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_FRAMEBUFFER));
+ memdelete(framebuffer);
}
/****************/
@@ -3865,7 +4051,7 @@ RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<Bou
}
// Need a descriptor pool.
- DescriptorSetPools::Iterator pool_sets_it = {};
+ DescriptorSetPools::Iterator pool_sets_it;
VkDescriptorPool vk_pool = _descriptor_set_pool_find_or_create(pool_key, &pool_sets_it);
DEV_ASSERT(vk_pool);
pool_sets_it->value[vk_pool]++;
@@ -4316,10 +4502,25 @@ void RenderingDeviceDriverVulkan::render_pass_free(RenderPassID p_render_pass) {
static_assert(ARRAYS_COMPATIBLE_FIELDWISE(RDD::RenderPassClearValue, VkClearValue));
void RenderingDeviceDriverVulkan::command_begin_render_pass(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, FramebufferID p_framebuffer, CommandBufferType p_cmd_buffer_type, const Rect2i &p_rect, VectorView<RenderPassClearValue> p_clear_values) {
+ Framebuffer *framebuffer = (Framebuffer *)(p_framebuffer.id);
+ if (framebuffer->swap_chain_acquired) {
+ // Insert a barrier to wait for the acquisition of the framebuffer before the render pass begins.
+ VkImageMemoryBarrier image_barrier = {};
+ image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ image_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ image_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ image_barrier.image = framebuffer->swap_chain_image;
+ image_barrier.subresourceRange = framebuffer->swap_chain_image_subresource_range;
+ vkCmdPipelineBarrier((VkCommandBuffer)p_cmd_buffer.id, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_barrier);
+ framebuffer->swap_chain_acquired = false;
+ }
+
VkRenderPassBeginInfo render_pass_begin = {};
render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin.renderPass = (VkRenderPass)p_render_pass.id;
- render_pass_begin.framebuffer = (VkFramebuffer)p_framebuffer.id;
+ render_pass_begin.framebuffer = framebuffer->vk_framebuffer;
render_pass_begin.renderArea.offset.x = p_rect.position.x;
render_pass_begin.renderArea.offset.y = p_rect.position.y;
@@ -4976,10 +5177,65 @@ void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer
/**** DEBUG *****/
/****************/
void RenderingDeviceDriverVulkan::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
if (p_data == BreadcrumbMarker::NONE) {
return;
}
- vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, 0, sizeof(uint32_t), p_data);
+
+ if (Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
+ // Force a full barrier so commands are not executed in parallel.
+ // This will mean that the last breadcrumb to see was actually the
+ // last (group of) command to be executed (hence, the one causing the crash).
+ VkMemoryBarrier memoryBarrier;
+ 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;
+ 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;
+
+ vkCmdPipelineBarrier(
+ (VkCommandBuffer)p_cmd_buffer.id,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0, 1u, &memoryBarrier, 0u, nullptr, 0u, nullptr);
+ }
+
+ // We write to a circular buffer. If you're getting barrier sync errors here,
+ // increase the value of BREADCRUMB_BUFFER_ENTRIES.
+ vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset, sizeof(uint32_t), breadcrumb_id++);
+ vkCmdFillBuffer((VkCommandBuffer)p_cmd_buffer.id, ((BufferInfo *)breadcrumb_buffer.id)->vk_buffer, breadcrumb_offset + sizeof(uint32_t), sizeof(uint32_t), p_data);
+ breadcrumb_offset += sizeof(uint32_t) * 2u;
+ if (breadcrumb_offset >= BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u) {
+ breadcrumb_offset = 0u;
+ }
+#endif
}
void RenderingDeviceDriverVulkan::on_device_lost() const {
@@ -5061,68 +5317,142 @@ void RenderingDeviceDriverVulkan::on_device_lost() const {
void RenderingDeviceDriverVulkan::print_lost_device_info() {
#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
- void *breadcrumb_ptr;
- vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
- vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, sizeof(uint32_t));
-
- vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &breadcrumb_ptr);
- uint32_t last_breadcrumb = *(uint32_t *)breadcrumb_ptr;
- vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
- uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
- uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
- String error_msg = "Last known breadcrumb: ";
-
- switch (phase) {
- case BreadcrumbMarker::ALPHA_PASS:
- error_msg += "ALPHA_PASS";
- break;
- case BreadcrumbMarker::BLIT_PASS:
- error_msg += "BLIT_PASS";
- break;
- case BreadcrumbMarker::DEBUG_PASS:
- error_msg += "DEBUG_PASS";
- break;
- case BreadcrumbMarker::LIGHTMAPPER_PASS:
- error_msg += "LIGHTMAPPER_PASS";
- break;
- case BreadcrumbMarker::OPAQUE_PASS:
- error_msg += "OPAQUE_PASS";
- break;
- case BreadcrumbMarker::POST_PROCESSING_PASS:
- error_msg += "POST_PROCESSING_PASS";
- break;
- case BreadcrumbMarker::REFLECTION_PROBES:
- error_msg += "REFLECTION_PROBES";
- break;
- case BreadcrumbMarker::SHADOW_PASS_CUBE:
- error_msg += "SHADOW_PASS_CUBE";
- break;
- case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
- error_msg += "SHADOW_PASS_DIRECTIONAL";
- break;
- case BreadcrumbMarker::SKY_PASS:
- error_msg += "SKY_PASS";
- break;
- case BreadcrumbMarker::TRANSPARENT_PASS:
- error_msg += "TRANSPARENT_PASS";
- break;
- case BreadcrumbMarker::UI_PASS:
- error_msg += "UI_PASS";
- break;
- default:
- error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
- break;
+ {
+ String error_msg = "Printing last known breadcrumbs in reverse order (last executed first).";
+ if (!Engine::get_singleton()->is_accurate_breadcrumbs_enabled()) {
+ error_msg += "\nSome of them might be inaccurate. Try running with --accurate-breadcrumbs for precise information.";
+ }
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
}
- if (user_data != 0) {
- error_msg += " | User data: " + itos(user_data);
- }
+ uint8_t *breadcrumb_ptr = nullptr;
+ VkResult map_result = VK_SUCCESS;
+
+ vmaFlushAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
+ vmaInvalidateAllocation(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, 0, BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u);
+ {
+ void *ptr = nullptr;
+ map_result = vmaMapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle, &ptr);
+ breadcrumb_ptr = reinterpret_cast<uint8_t *>(ptr);
+ }
+
+ if (breadcrumb_ptr && map_result == VK_SUCCESS) {
+ uint32_t last_breadcrumb_offset = 0;
+ {
+ _err_print_error_asap("Searching last breadcrumb. We've sent up to ID: " + itos(breadcrumb_id - 1u));
+
+ // Scan the whole buffer to find the offset with the highest ID.
+ // That means that was the last one to be written.
+ //
+ // We use "breadcrumb_id - id" to account for wraparound.
+ // e.g. breadcrumb_id = 2 and id = 4294967294; then 2 - 4294967294 = 4.
+ // The one with the smallest difference is the closest to breadcrumb_id, which means it's
+ // the last written command.
+ uint32_t biggest_id = 0u;
+ uint32_t smallest_id_diff = std::numeric_limits<uint32_t>::max();
+ const uint32_t *breadcrumb_ptr32 = reinterpret_cast<const uint32_t *>(breadcrumb_ptr);
+ for (size_t i = 0u; i < BREADCRUMB_BUFFER_ENTRIES; ++i) {
+ const uint32_t id = breadcrumb_ptr32[i * 2u];
+ const uint32_t id_diff = breadcrumb_id - id;
+ if (id_diff < smallest_id_diff) {
+ biggest_id = i;
+ smallest_id_diff = id_diff;
+ }
+ }
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, error_msg);
+ _err_print_error_asap("Last breadcrumb ID found: " + itos(breadcrumb_ptr32[biggest_id * 2u]));
+
+ last_breadcrumb_offset = biggest_id * sizeof(uint32_t) * 2u;
+ }
+
+ const size_t entries_to_print = 8u; // Note: The value is arbitrary.
+ for (size_t i = 0u; i < entries_to_print; ++i) {
+ const uint32_t last_breadcrumb = *reinterpret_cast<uint32_t *>(breadcrumb_ptr + last_breadcrumb_offset + sizeof(uint32_t));
+ const uint32_t phase = last_breadcrumb & uint32_t(~((1 << 16) - 1));
+ const uint32_t user_data = last_breadcrumb & ((1 << 16) - 1);
+ String error_msg = "Last known breadcrumb: ";
+
+ switch (phase) {
+ case BreadcrumbMarker::ALPHA_PASS:
+ error_msg += "ALPHA_PASS";
+ break;
+ case BreadcrumbMarker::BLIT_PASS:
+ error_msg += "BLIT_PASS";
+ break;
+ case BreadcrumbMarker::DEBUG_PASS:
+ error_msg += "DEBUG_PASS";
+ break;
+ case BreadcrumbMarker::LIGHTMAPPER_PASS:
+ error_msg += "LIGHTMAPPER_PASS";
+ break;
+ case BreadcrumbMarker::OPAQUE_PASS:
+ error_msg += "OPAQUE_PASS";
+ break;
+ case BreadcrumbMarker::POST_PROCESSING_PASS:
+ error_msg += "POST_PROCESSING_PASS";
+ break;
+ case BreadcrumbMarker::REFLECTION_PROBES:
+ error_msg += "REFLECTION_PROBES";
+ break;
+ case BreadcrumbMarker::SHADOW_PASS_CUBE:
+ error_msg += "SHADOW_PASS_CUBE";
+ break;
+ case BreadcrumbMarker::SHADOW_PASS_DIRECTIONAL:
+ error_msg += "SHADOW_PASS_DIRECTIONAL";
+ break;
+ case BreadcrumbMarker::SKY_PASS:
+ error_msg += "SKY_PASS";
+ break;
+ case BreadcrumbMarker::TRANSPARENT_PASS:
+ error_msg += "TRANSPARENT_PASS";
+ break;
+ case BreadcrumbMarker::UI_PASS:
+ error_msg += "UI_PASS";
+ break;
+ default:
+ error_msg += "UNKNOWN_BREADCRUMB(" + itos((uint32_t)phase) + ')';
+ break;
+ }
+
+ if (user_data != 0) {
+ error_msg += " | User data: " + itos(user_data);
+ }
+
+ _err_print_error_asap(error_msg);
+
+ if (last_breadcrumb_offset == 0u) {
+ // Decrement last_breadcrumb_idx, wrapping underflow.
+ last_breadcrumb_offset = BREADCRUMB_BUFFER_ENTRIES * sizeof(uint32_t) * 2u;
+ }
+ last_breadcrumb_offset -= sizeof(uint32_t) * 2u;
+ }
+
+ vmaUnmapMemory(allocator, ((BufferInfo *)breadcrumb_buffer.id)->allocation.handle);
+ breadcrumb_ptr = nullptr;
+ } else {
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Couldn't map breadcrumb buffer. VkResult = " + itos(map_result));
+ }
#endif
on_device_lost();
}
+inline String RenderingDeviceDriverVulkan::get_vulkan_result(VkResult err) {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ if (err == VK_ERROR_OUT_OF_HOST_MEMORY) {
+ return "VK_ERROR_OUT_OF_HOST_MEMORY";
+ } else if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
+ return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
+ } else if (err == VK_ERROR_DEVICE_LOST) {
+ return "VK_ERROR_DEVICE_LOST";
+ } else if (err == VK_ERROR_SURFACE_LOST_KHR) {
+ return "VK_ERROR_SURFACE_LOST_KHR";
+ } else if (err == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) {
+ return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
+ }
+#endif
+ return itos(err);
+}
+
/********************/
/**** SUBMISSION ****/
/********************/
@@ -5389,7 +5719,9 @@ RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverV
}
RenderingDeviceDriverVulkan::~RenderingDeviceDriverVulkan() {
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
buffer_free(breadcrumb_buffer);
+#endif
while (small_allocs_pools.size()) {
HashMap<uint32_t, VmaPool>::Iterator E = small_allocs_pools.begin();
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 58f7a97ec0..06cd2a31be 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -143,6 +143,11 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
#if defined(VK_TRACK_DEVICE_MEMORY)
bool device_memory_report_support = false;
#endif
+#if defined(SWAPPY_FRAME_PACING_ENABLED)
+ // Swappy frame pacer for Android.
+ bool swappy_frame_pacer_enable = false;
+ uint8_t swappy_mode = 2; // See default value for display/window/frame_pacing/android/swappy_mode.
+#endif
DeviceFunctions device_functions;
void _register_requested_device_extension(const CharString &p_extension_name, bool p_required);
@@ -172,7 +177,12 @@ private:
VmaPool _find_or_create_small_allocs_pool(uint32_t p_mem_type_index);
private:
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+ // It's a circular buffer.
BufferID breadcrumb_buffer;
+ uint32_t breadcrumb_offset = 0u;
+ uint32_t breadcrumb_id = 0u;
+#endif
public:
/*****************/
@@ -349,7 +359,11 @@ private:
LocalVector<CommandQueue *> command_queues_acquired;
LocalVector<uint32_t> command_queues_acquired_semaphores;
RenderPassID render_pass;
+ int pre_transform_rotation_degrees = 0;
uint32_t image_index = 0;
+#ifdef ANDROID_ENABLED
+ uint64_t refresh_duration = 0;
+#endif
};
void _swap_chain_release(SwapChain *p_swap_chain);
@@ -359,13 +373,24 @@ public:
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final;
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
+ virtual int swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
+ virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
/*********************/
/**** FRAMEBUFFER ****/
/*********************/
+ struct Framebuffer {
+ VkFramebuffer vk_framebuffer = VK_NULL_HANDLE;
+
+ // Only filled in by a framebuffer created by a swap chain. Unused otherwise.
+ VkImage swap_chain_image = VK_NULL_HANDLE;
+ VkImageSubresourceRange swap_chain_image_subresource_range = {};
+ bool swap_chain_acquired = false;
+ };
+
virtual FramebufferID framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height) override final;
virtual void framebuffer_free(FramebufferID p_framebuffer) override final;
@@ -463,7 +488,7 @@ private:
struct UniformSetInfo {
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
VkDescriptorPool vk_descriptor_pool = VK_NULL_HANDLE;
- DescriptorSetPools::Iterator pool_sets_it = {};
+ DescriptorSetPools::Iterator pool_sets_it;
};
public:
@@ -630,6 +655,7 @@ public:
virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
void print_lost_device_info();
void on_device_lost() const;
+ static String get_vulkan_result(VkResult err);
/********************/
/**** SUBMISSION ****/
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 4e7c9695a5..b800e5f1f4 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -85,7 +85,7 @@ public:
_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__
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index f7632842ed..3ddbde72c4 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -230,7 +230,7 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const {
return cdir;
} else {
if (_get_root_string().is_empty()) {
- int pos = cdir.find(":");
+ int pos = cdir.find_char(':');
if (pos != -1) {
return cdir.substr(pos + 1);
}
@@ -282,7 +282,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) {
uint64_t id = OS::get_singleton()->get_ticks_usec();
while (true) {
tmpfile_utf16 = (path + itos(id++) + ".tmp").utf16();
- HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
break;
@@ -344,7 +344,7 @@ String DirAccessWindows::get_filesystem_type() const {
return "Network Share";
}
- int unit_end = path.find(":");
+ int unit_end = path.find_char(':');
ERR_FAIL_COND_V(unit_end == -1, String());
String unit = path.substr(0, unit_end + 1) + "\\";
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index a8a2ea6b5e..4a0e5e5f49 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -64,7 +64,7 @@ bool FileAccessWindows::is_path_invalid(const String &p_path) {
// Check for invalid operating system file.
String fname = p_path.get_file().to_lower();
- int dot = fname.find(".");
+ int dot = fname.find_char('.');
if (dot != -1) {
fname = fname.substr(0, dot);
}
@@ -127,7 +127,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
}
#ifdef TOOLS_ENABLED
- // Windows is case insensitive, but all other platforms are sensitive to it
+ // Windows is case insensitive in the default configuration, but other platforms can be sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows, but won't on other
// platforms), we only check for relative paths, or paths in res:// or user://,
@@ -193,7 +193,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
uint64_t id = OS::get_singleton()->get_ticks_usec();
while (true) {
tmpfile = path + itos(id++) + ".tmp";
- HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
break;
diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp
index 9bf0f4d852..7348b29a92 100644
--- a/drivers/windows/file_access_windows_pipe.cpp
+++ b/drivers/windows/file_access_windows_pipe.cpp
@@ -40,7 +40,7 @@ Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd, bool p_bl
_close();
path_src = String();
- ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
fd[0] = p_rfd;
fd[1] = p_wfd;
@@ -58,18 +58,18 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
_close();
path_src = p_path;
- ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
- HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE) {
h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 4096, 4096, 0, nullptr);
if (h == INVALID_HANDLE_VALUE) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
- ConnectNamedPipe(h, NULL);
+ ConnectNamedPipe(h, nullptr);
}
fd[0] = h;
fd[1] = h;
@@ -79,19 +79,19 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
}
void FileAccessWindowsPipe::_close() {
- if (fd[0] == 0) {
+ if (fd[0] == nullptr) {
return;
}
if (fd[1] != fd[0]) {
CloseHandle(fd[1]);
}
CloseHandle(fd[0]);
- fd[0] = 0;
- fd[1] = 0;
+ fd[0] = nullptr;
+ fd[1] = nullptr;
}
bool FileAccessWindowsPipe::is_open() const {
- return (fd[0] != 0 || fd[1] != 0);
+ return (fd[0] != nullptr || fd[1] != nullptr);
}
String FileAccessWindowsPipe::get_path() const {
@@ -103,7 +103,7 @@ String FileAccessWindowsPipe::get_path_absolute() const {
}
uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
- ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
+ ERR_FAIL_COND_V_MSG(fd[0] == nullptr, -1, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
DWORD read = 0;
@@ -120,7 +120,7 @@ Error FileAccessWindowsPipe::get_error() const {
}
void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
- ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND_MSG(fd[1] == nullptr, "Pipe must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
DWORD read = -1;
diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h
index 1eb3c6ef2f..5edf0500a5 100644
--- a/drivers/windows/file_access_windows_pipe.h
+++ b/drivers/windows/file_access_windows_pipe.h
@@ -39,7 +39,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class FileAccessWindowsPipe : public FileAccess {
- HANDLE fd[2] = { 0, 0 };
+ HANDLE fd[2] = { nullptr, nullptr };
mutable Error last_error = OK;
diff --git a/drivers/windows/ip_windows.cpp b/drivers/windows/ip_windows.cpp
new file mode 100644
index 0000000000..4ff368d3f4
--- /dev/null
+++ b/drivers/windows/ip_windows.cpp
@@ -0,0 +1,164 @@
+/**************************************************************************/
+/* ip_windows.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#if defined(WINDOWS_ENABLED)
+
+#include "ip_windows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <iphlpapi.h>
+
+#include <stdio.h>
+
+#include <string.h>
+
+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;
+ ip.set_ipv4((uint8_t *)&(addr->sin_addr));
+ } else if (p_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
+ ip.set_ipv6(addr6->sin6_addr.s6_addr);
+ }
+
+ return ip;
+}
+
+void IPWindows::_resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type) const {
+ struct addrinfo hints;
+ struct addrinfo *result = nullptr;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ if (p_type == TYPE_IPV4) {
+ hints.ai_family = AF_INET;
+ } else if (p_type == TYPE_IPV6) {
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = 0;
+ } 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) {
+ print_verbose("getaddrinfo failed! Cannot resolve hostname.");
+ return;
+ }
+
+ if (result == nullptr || result->ai_addr == nullptr) {
+ print_verbose("Invalid response from getaddrinfo.");
+ if (result) {
+ freeaddrinfo(result);
+ }
+ return;
+ }
+
+ struct addrinfo *next = 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);
+
+ freeaddrinfo(result);
+}
+
+void IPWindows::get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const {
+ ULONG buf_size = 1024;
+ IP_ADAPTER_ADDRESSES *addrs;
+
+ while (true) {
+ addrs = (IP_ADAPTER_ADDRESSES *)memalloc(buf_size);
+ int err = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME,
+ 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;
+
+ while (adapter != nullptr) {
+ Interface_Info info;
+ info.name = adapter->AdapterName;
+ info.name_friendly = adapter->FriendlyName;
+ info.index = String::num_uint64(adapter->IfIndex);
+
+ IP_ADAPTER_UNICAST_ADDRESS *address = adapter->FirstUnicastAddress;
+ while (address != nullptr) {
+ int family = address->Address.lpSockaddr->sa_family;
+ 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) {
+ r_interfaces->insert(info.name, info);
+ }
+ }
+
+ memfree(addrs);
+}
+
+void IPWindows::make_default() {
+ _create = _create_unix;
+}
+
+IP *IPWindows::_create_unix() {
+ return memnew(IPWindows);
+}
+
+IPWindows::IPWindows() {
+}
+
+#endif // WINDOWS_ENABLED
diff --git a/drivers/windows/ip_windows.h b/drivers/windows/ip_windows.h
new file mode 100644
index 0000000000..5788d33ee2
--- /dev/null
+++ b/drivers/windows/ip_windows.h
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* ip_windows.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 IP_WINDOWS_H
+#define IP_WINDOWS_H
+
+#if defined(WINDOWS_ENABLED)
+
+#include "core/io/ip.h"
+
+class IPWindows : public IP {
+ GDCLASS(IPWindows, IP);
+
+ virtual void _resolve_hostname(List<IPAddress> &r_addresses, const String &p_hostname, Type p_type = TYPE_ANY) const override;
+
+ static IP *_create_unix();
+
+public:
+ virtual void get_local_interfaces(HashMap<String, Interface_Info> *r_interfaces) const override;
+
+ static void make_default();
+ IPWindows();
+};
+
+#endif // WINDOWS_ENABLED
+
+#endif // IP_WINDOWS_H
diff --git a/drivers/windows/net_socket_winsock.cpp b/drivers/windows/net_socket_winsock.cpp
new file mode 100644
index 0000000000..3fe7fc619e
--- /dev/null
+++ b/drivers/windows/net_socket_winsock.cpp
@@ -0,0 +1,613 @@
+/**************************************************************************/
+/* net_socket_winsock.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 WINDOWS_ENABLED
+
+#include "net_socket_winsock.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <mswsock.h>
+// Workaround missing flag in MinGW
+#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
+#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
+#endif
+
+size_t NetSocketWinSock::_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.
+
+ // IPv6 only socket with IPv4 address.
+ ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0);
+
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = htons(p_port);
+ if (p_ip.is_valid()) {
+ memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16);
+ } else {
+ addr6->sin6_addr = in6addr_any;
+ }
+ return sizeof(sockaddr_in6);
+ } else { // IPv4 socket.
+
+ // IPv4 socket with IPv6 address.
+ ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0);
+
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_port = htons(p_port); // Short, network byte order.
+
+ if (p_ip.is_valid()) {
+ memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4);
+ } else {
+ addr4->sin_addr.s_addr = INADDR_ANY;
+ }
+
+ return sizeof(sockaddr_in);
+ }
+}
+
+void NetSocketWinSock::_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;
+ 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;
+ if (r_ip) {
+ r_ip->set_ipv6(addr6->sin6_addr.s6_addr);
+ }
+ if (r_port) {
+ *r_port = ntohs(addr6->sin6_port);
+ }
+ }
+}
+
+NetSocket *NetSocketWinSock::_create_func() {
+ return memnew(NetSocketWinSock);
+}
+
+void NetSocketWinSock::make_default() {
+ ERR_FAIL_COND(_create != nullptr);
+
+ WSADATA data;
+ WSAStartup(MAKEWORD(2, 2), &data);
+ _create = _create_func;
+}
+
+void NetSocketWinSock::cleanup() {
+ ERR_FAIL_COND(_create == nullptr);
+
+ WSACleanup();
+ _create = nullptr;
+}
+
+NetSocketWinSock::NetSocketWinSock() {
+}
+
+NetSocketWinSock::~NetSocketWinSock() {
+ close();
+}
+
+NetSocketWinSock::NetError NetSocketWinSock::_get_socket_error() const {
+ int err = WSAGetLastError();
+ if (err == WSAEISCONN) {
+ return ERR_NET_IS_CONNECTED;
+ }
+ if (err == WSAEINPROGRESS || err == WSAEALREADY) {
+ return ERR_NET_IN_PROGRESS;
+ }
+ 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;
+ }
+ if (err == WSAEMSGSIZE || err == WSAENOBUFS) {
+ return ERR_NET_BUFFER_TOO_SMALL;
+ }
+ print_verbose("Socket error: " + itos(err) + ".");
+ return ERR_NET_OTHER;
+}
+
+bool NetSocketWinSock::_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()) {
+ return false;
+ }
+ // Check if socket support this IP type.
+ IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
+ return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
+}
+
+_FORCE_INLINE_ Error NetSocketWinSock::_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);
+
+ // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4.
+ IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type;
+ // This needs to be the proper level for the multicast group, no matter if the socket is dual stacking.
+ int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6;
+ int ret = -1;
+
+ IPAddress if_ip;
+ uint32_t if_v6id = 0;
+ HashMap<String, IP::Interface_Info> if_info;
+ IP::get_singleton()->get_local_interfaces(&if_info);
+ for (KeyValue<String, IP::Interface_Info> &E : if_info) {
+ IP::Interface_Info &c = E.value;
+ if (c.name != p_if_name) {
+ continue;
+ }
+
+ if_v6id = (uint32_t)c.index.to_int();
+ if (type == IP::TYPE_IPV6) {
+ break; // IPv6 uses index.
+ }
+
+ for (const IPAddress &F : c.ip_addresses) {
+ if (!F.is_ipv4()) {
+ continue; // Wrong IP type.
+ }
+ if_ip = F;
+ break;
+ }
+ break;
+ }
+
+ if (level == IPPROTO_IP) {
+ 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;
+ 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;
+ 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));
+ }
+ ERR_FAIL_COND_V(ret != 0, FAILED);
+
+ return OK;
+}
+
+void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream) {
+ _sock = p_sock;
+ _ip_type = p_ip_type;
+ _is_stream = p_is_stream;
+}
+
+Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) {
+ ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER);
+
+ int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6;
+ int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP;
+ int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM;
+ _sock = socket(family, type, protocol);
+
+ if (_sock == INVALID_SOCKET && ip_type == IP::TYPE_ANY) {
+ // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket
+ // in place of a dual stack one, and further calls to _set_sock_addr will work as expected.
+ ip_type = IP::TYPE_IPV4;
+ family = AF_INET;
+ _sock = socket(family, type, protocol);
+ }
+
+ ERR_FAIL_COND_V(_sock == INVALID_SOCKET, FAILED);
+ _ip_type = ip_type;
+
+ if (family == AF_INET6) {
+ // Select IPv4 over IPv6 mapping.
+ set_ipv6_only_enabled(ip_type != IP::TYPE_ANY);
+ }
+
+ if (protocol == IPPROTO_UDP) {
+ // Make sure to disable broadcasting for UDP sockets.
+ // Depending on the OS, this option might or might not be enabled by default. Let's normalize it.
+ set_broadcasting_enabled(false);
+ }
+
+ _is_stream = p_sock_type == TYPE_TCP;
+
+ if (!_is_stream) {
+ // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when
+ // recv/recvfrom and an ICMP reply was received from a previous send/sendto.
+ unsigned long disable = 0;
+ if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) {
+ print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows.");
+ }
+ if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) {
+ // This feature seems not to be supported on wine.
+ print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows.");
+ }
+ }
+ return OK;
+}
+
+void NetSocketWinSock::close() {
+ if (_sock != INVALID_SOCKET) {
+ closesocket(_sock);
+ }
+
+ _sock = INVALID_SOCKET;
+ _ip_type = IP::TYPE_NONE;
+ _is_stream = false;
+}
+
+Error NetSocketWinSock::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);
+
+ sockaddr_storage addr;
+ size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
+
+ if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
+ NetError err = _get_socket_error();
+ print_verbose("Failed to bind socket. Error: " + itos(err) + ".");
+ close();
+ return ERR_UNAVAILABLE;
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::listen(int p_max_pending) {
+ ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
+
+ if (::listen(_sock, p_max_pending) != 0) {
+ _get_socket_error();
+ print_verbose("Failed to listen from socket.");
+ close();
+ return FAILED;
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::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);
+
+ struct sockaddr_storage addr;
+ size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type);
+
+ if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) {
+ NetError err = _get_socket_error();
+
+ switch (err) {
+ // We are already connected.
+ case ERR_NET_IS_CONNECTED:
+ return OK;
+ // Still waiting to connect, try again in a while.
+ case ERR_NET_WOULD_BLOCK:
+ case ERR_NET_IN_PROGRESS:
+ return ERR_BUSY;
+ default:
+ print_verbose("Connection to remote host failed.");
+ close();
+ return FAILED;
+ }
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::poll(PollType p_type, int p_timeout) const {
+ ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
+
+ bool ready = false;
+ fd_set rd, wr, ex;
+ fd_set *rdp = nullptr;
+ fd_set *wrp = nullptr;
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+ FD_SET(_sock, &ex);
+ 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.
+ tp = &timeout;
+ }
+
+ switch (p_type) {
+ case POLL_TYPE_IN:
+ FD_SET(_sock, &rd);
+ rdp = &rd;
+ break;
+ case POLL_TYPE_OUT:
+ FD_SET(_sock, &wr);
+ wrp = &wr;
+ break;
+ case POLL_TYPE_IN_OUT:
+ FD_SET(_sock, &rd);
+ FD_SET(_sock, &wr);
+ rdp = &rd;
+ wrp = &wr;
+ }
+ // WSAPoll is broken: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/.
+ int ret = select(1, rdp, wrp, &ex, tp);
+
+ if (ret == SOCKET_ERROR) {
+ return FAILED;
+ }
+
+ if (ret == 0) {
+ return ERR_BUSY;
+ }
+
+ if (FD_ISSET(_sock, &ex)) {
+ _get_socket_error();
+ print_verbose("Exception when polling socket.");
+ return FAILED;
+ }
+
+ if (rdp && FD_ISSET(_sock, rdp)) {
+ ready = true;
+ }
+ if (wrp && FD_ISSET(_sock, wrp)) {
+ ready = true;
+ }
+
+ return ready ? OK : ERR_BUSY;
+}
+
+Error NetSocketWinSock::recv(uint8_t *p_buffer, int p_len, int &r_read) {
+ ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
+
+ r_read = ::recv(_sock, (char *)p_buffer, p_len, 0);
+
+ if (r_read < 0) {
+ NetError err = _get_socket_error();
+ if (err == ERR_NET_WOULD_BLOCK) {
+ return ERR_BUSY;
+ }
+
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return FAILED;
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::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;
+ socklen_t len = sizeof(struct sockaddr_storage);
+ memset(&from, 0, len);
+
+ r_read = ::recvfrom(_sock, (char *)p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len);
+
+ if (r_read < 0) {
+ NetError err = _get_socket_error();
+ if (err == ERR_NET_WOULD_BLOCK) {
+ return ERR_BUSY;
+ }
+
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return FAILED;
+ }
+
+ if (from.ss_family == AF_INET) {
+ struct sockaddr_in *sin_from = (struct sockaddr_in *)&from;
+ r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr);
+ r_port = ntohs(sin_from->sin_port);
+ } else if (from.ss_family == AF_INET6) {
+ struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from;
+ r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr);
+ r_port = ntohs(s6_from->sin6_port);
+ } else {
+ // Unsupported socket family, should never happen.
+ ERR_FAIL_V(FAILED);
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
+ ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
+
+ int flags = 0;
+ r_sent = ::send(_sock, (const char *)p_buffer, p_len, flags);
+
+ if (r_sent < 0) {
+ NetError err = _get_socket_error();
+ if (err == ERR_NET_WOULD_BLOCK) {
+ return ERR_BUSY;
+ }
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return FAILED;
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::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;
+ size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type);
+ r_sent = ::sendto(_sock, (const char *)p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size);
+
+ if (r_sent < 0) {
+ NetError err = _get_socket_error();
+ if (err == ERR_NET_WOULD_BLOCK) {
+ return ERR_BUSY;
+ }
+ if (err == ERR_NET_BUFFER_TOO_SMALL) {
+ return ERR_OUT_OF_MEMORY;
+ }
+
+ return FAILED;
+ }
+
+ return OK;
+}
+
+Error NetSocketWinSock::set_broadcasting_enabled(bool p_enabled) {
+ ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
+ // IPv6 has no broadcast support.
+ if (_ip_type == IP::TYPE_IPV6) {
+ return ERR_UNAVAILABLE;
+ }
+
+ int par = p_enabled ? 1 : 0;
+ if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, (const char *)&par, sizeof(int)) != 0) {
+ WARN_PRINT("Unable to change broadcast setting.");
+ return FAILED;
+ }
+ return OK;
+}
+
+void NetSocketWinSock::set_blocking_enabled(bool p_enabled) {
+ ERR_FAIL_COND(!is_open());
+
+ int ret = 0;
+ unsigned long par = p_enabled ? 0 : 1;
+ ret = ioctlsocket(_sock, FIONBIO, &par);
+ if (ret != 0) {
+ WARN_PRINT("Unable to change non-block mode.");
+ }
+}
+
+void NetSocketWinSock::set_ipv6_only_enabled(bool p_enabled) {
+ ERR_FAIL_COND(!is_open());
+ // This option is only available in IPv6 sockets.
+ ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4);
+
+ int par = p_enabled ? 1 : 0;
+ if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&par, sizeof(int)) != 0) {
+ WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option.");
+ }
+}
+
+void NetSocketWinSock::set_tcp_no_delay_enabled(bool p_enabled) {
+ ERR_FAIL_COND(!is_open());
+ ERR_FAIL_COND(!_is_stream); // Not TCP.
+
+ int par = p_enabled ? 1 : 0;
+ if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&par, sizeof(int)) < 0) {
+ WARN_PRINT("Unable to set TCP no delay option.");
+ }
+}
+
+void NetSocketWinSock::set_reuse_address_enabled(bool p_enabled) {
+ ERR_FAIL_COND(!is_open());
+
+ // On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying...
+ // Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT
+}
+
+bool NetSocketWinSock::is_open() const {
+ return _sock != INVALID_SOCKET;
+}
+
+int NetSocketWinSock::get_available_bytes() const {
+ ERR_FAIL_COND_V(!is_open(), -1);
+
+ unsigned long len;
+ int ret = ioctlsocket(_sock, FIONREAD, &len);
+ if (ret == -1) {
+ _get_socket_error();
+ print_verbose("Error when checking available bytes on socket.");
+ return -1;
+ }
+ return len;
+}
+
+Error NetSocketWinSock::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> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) {
+ Ref<NetSocket> out;
+ ERR_FAIL_COND_V(!is_open(), out);
+
+ struct sockaddr_storage their_addr;
+ socklen_t size = sizeof(their_addr);
+ SOCKET fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size);
+ if (fd == INVALID_SOCKET) {
+ _get_socket_error();
+ print_verbose("Error when accepting socket connection.");
+ return out;
+ }
+
+ _set_ip_port(&their_addr, &r_ip, &r_port);
+
+ NetSocketWinSock *ns = memnew(NetSocketWinSock);
+ ns->_set_socket(fd, _ip_type, _is_stream);
+ ns->set_blocking_enabled(false);
+ return Ref<NetSocket>(ns);
+}
+
+Error NetSocketWinSock::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
+ return _change_multicast_group(p_multi_address, p_if_name, true);
+}
+
+Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) {
+ return _change_multicast_group(p_multi_address, p_if_name, false);
+}
+
+#endif // WINDOWS_ENABLED
diff --git a/drivers/windows/net_socket_winsock.h b/drivers/windows/net_socket_winsock.h
new file mode 100644
index 0000000000..5c3445b8cb
--- /dev/null
+++ b/drivers/windows/net_socket_winsock.h
@@ -0,0 +1,102 @@
+/**************************************************************************/
+/* net_socket_winsock.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 NET_SOCKET_WINSOCK_H
+#define NET_SOCKET_WINSOCK_H
+
+#ifdef WINDOWS_ENABLED
+
+#include "core/io/net_socket.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+class NetSocketWinSock : public NetSocket {
+private:
+ SOCKET _sock = INVALID_SOCKET;
+ IP::Type _ip_type = IP::TYPE_NONE;
+ bool _is_stream = false;
+
+ enum NetError {
+ ERR_NET_WOULD_BLOCK,
+ ERR_NET_IS_CONNECTED,
+ ERR_NET_IN_PROGRESS,
+ ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
+ ERR_NET_UNAUTHORIZED,
+ ERR_NET_BUFFER_TOO_SMALL,
+ ERR_NET_OTHER,
+ };
+
+ NetError _get_socket_error() const;
+ void _set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream);
+ _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add);
+
+protected:
+ static NetSocket *_create_func();
+
+ 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, 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) override;
+ virtual void close() override;
+ virtual Error bind(IPAddress p_addr, uint16_t p_port) override;
+ virtual Error listen(int p_max_pending) override;
+ virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override;
+ virtual Error poll(PollType p_type, int timeout) const override;
+ virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override;
+ virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override;
+ virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override;
+ virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override;
+ virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override;
+
+ virtual bool is_open() const override;
+ virtual int get_available_bytes() const override;
+ virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override;
+
+ virtual Error set_broadcasting_enabled(bool p_enabled) override;
+ virtual void set_blocking_enabled(bool p_enabled) override;
+ virtual void set_ipv6_only_enabled(bool p_enabled) override;
+ virtual void set_tcp_no_delay_enabled(bool p_enabled) override;
+ virtual void set_reuse_address_enabled(bool p_enabled) override;
+ virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
+ virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override;
+
+ NetSocketWinSock();
+ ~NetSocketWinSock() override;
+};
+
+#endif // WINDOWS_ENABLED
+
+#endif // NET_SOCKET_WINSOCK_H