Add 'engine/' from commit 'a8e37fc010'
git-subtree-dir: engine git-subtree-mainline:b74841629egit-subtree-split:a8e37fc010
This commit is contained in:
commit
c3f9669b10
14113 changed files with 7458101 additions and 0 deletions
74
engine/drivers/SCsub
Normal file
74
engine/drivers/SCsub
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
from methods import print_error
|
||||
|
||||
Import("env")
|
||||
|
||||
env.drivers_sources = []
|
||||
supported = env.get("supported", [])
|
||||
|
||||
# OS drivers
|
||||
SConscript("unix/SCsub")
|
||||
SConscript("windows/SCsub")
|
||||
|
||||
# Sounds drivers
|
||||
SConscript("alsa/SCsub")
|
||||
SConscript("pulseaudio/SCsub")
|
||||
if env["platform"] == "windows":
|
||||
SConscript("wasapi/SCsub")
|
||||
if not env.msvc:
|
||||
SConscript("backtrace/SCsub")
|
||||
if env["xaudio2"]:
|
||||
if "xaudio2" not in supported:
|
||||
print_error("Target platform '{}' does not support the XAudio2 audio driver".format(env["platform"]))
|
||||
Exit(255)
|
||||
SConscript("xaudio2/SCsub")
|
||||
|
||||
# Shared Apple platform drivers
|
||||
if env["platform"] in ["macos", "ios", "visionos"]:
|
||||
SConscript("apple/SCsub")
|
||||
SConscript("coreaudio/SCsub")
|
||||
if env["platform"] in ["ios", "visionos"]:
|
||||
SConscript("apple_embedded/SCsub")
|
||||
|
||||
# Accessibility
|
||||
if env["accesskit"] and env["platform"] in ["macos", "windows", "linuxbsd"]:
|
||||
SConscript("accesskit/SCsub")
|
||||
|
||||
# Midi drivers
|
||||
SConscript("alsamidi/SCsub")
|
||||
if env["platform"] in ["macos"]:
|
||||
SConscript("coremidi/SCsub")
|
||||
SConscript("winmidi/SCsub")
|
||||
|
||||
# Graphics drivers
|
||||
if env["vulkan"]:
|
||||
SConscript("vulkan/SCsub")
|
||||
if env["d3d12"]:
|
||||
if "d3d12" not in supported:
|
||||
print_error("Target platform '{}' does not support the D3D12 rendering driver".format(env["platform"]))
|
||||
Exit(255)
|
||||
SConscript("d3d12/SCsub")
|
||||
if env["opengl3"]:
|
||||
SConscript("gl_context/SCsub")
|
||||
SConscript("gles3/SCsub")
|
||||
SConscript("egl/SCsub")
|
||||
if env["metal"]:
|
||||
if "metal" not in supported:
|
||||
print_error("Target platform '{}' does not support the Metal rendering driver".format(env["platform"]))
|
||||
Exit(255)
|
||||
SConscript("metal/SCsub")
|
||||
|
||||
# Input drivers
|
||||
if env["sdl"] and env["platform"] in ["linuxbsd", "macos", "windows", "ios", "visionos"]:
|
||||
# TODO: Evaluate support for Android and Web.
|
||||
SConscript("sdl/SCsub")
|
||||
|
||||
# Core dependencies
|
||||
SConscript("png/SCsub")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
|
||||
lib = env.add_library("drivers", env.drivers_sources)
|
||||
env.Prepend(LIBS=[lib])
|
||||
7
engine/drivers/accesskit/SCsub
Normal file
7
engine/drivers/accesskit/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "accessibility_server_accesskit.cpp")
|
||||
1793
engine/drivers/accesskit/accessibility_server_accesskit.cpp
Normal file
1793
engine/drivers/accesskit/accessibility_server_accesskit.cpp
Normal file
File diff suppressed because it is too large
Load diff
200
engine/drivers/accesskit/accessibility_server_accesskit.h
Normal file
200
engine/drivers/accesskit/accessibility_server_accesskit.h
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/**************************************************************************/
|
||||
/* accessibility_server_accesskit.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ACCESSKIT_ENABLED
|
||||
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/display/accessibility_server.h"
|
||||
|
||||
#include <accesskit.h>
|
||||
|
||||
class AccessibilityServerAccessKit : public AccessibilityServer {
|
||||
GDSOFTCLASS(AccessibilityServerAccessKit, AccessibilityServer);
|
||||
|
||||
static AccessibilityServer *create_func(Error &r_error);
|
||||
|
||||
struct AccessibilityElement {
|
||||
HashMap<accesskit_action, Callable> actions;
|
||||
|
||||
DisplayServerEnums::WindowID window_id = DisplayServerEnums::INVALID_WINDOW_ID;
|
||||
RID parent;
|
||||
LocalVector<RID> children;
|
||||
Vector3i run;
|
||||
Variant meta;
|
||||
String name;
|
||||
String name_extra_info;
|
||||
Variant value;
|
||||
int64_t flags = 0;
|
||||
|
||||
accesskit_role role = ACCESSKIT_ROLE_UNKNOWN;
|
||||
accesskit_node *node = nullptr;
|
||||
};
|
||||
mutable RID_PtrOwner<AccessibilityElement> rid_owner;
|
||||
|
||||
struct WindowData {
|
||||
// Adapter.
|
||||
#ifdef WINDOWS_ENABLED
|
||||
accesskit_windows_subclassing_adapter *adapter = nullptr;
|
||||
#endif
|
||||
#ifdef MACOS_ENABLED
|
||||
accesskit_macos_subclassing_adapter *adapter = nullptr;
|
||||
#endif
|
||||
#ifdef LINUXBSD_ENABLED
|
||||
accesskit_unix_adapter *adapter = nullptr;
|
||||
#endif
|
||||
|
||||
RID root_id;
|
||||
bool initial_update_completed = false;
|
||||
HashSet<RID> update;
|
||||
Callable activate;
|
||||
Callable deactivate;
|
||||
bool activated = false;
|
||||
};
|
||||
|
||||
RID focus;
|
||||
|
||||
HashMap<DisplayServerEnums::WindowID, WindowData> windows;
|
||||
|
||||
HashMap<AccessibilityServerEnums::AccessibilityRole, accesskit_role> role_map;
|
||||
HashMap<AccessibilityServerEnums::AccessibilityAction, accesskit_action> action_map;
|
||||
|
||||
_FORCE_INLINE_ accesskit_role _accessibility_role(AccessibilityServerEnums::AccessibilityRole p_role) const;
|
||||
_FORCE_INLINE_ accesskit_action _accessibility_action(AccessibilityServerEnums::AccessibilityAction p_action) const;
|
||||
|
||||
void _free_recursive(WindowData *p_wd, const RID &p_id);
|
||||
_FORCE_INLINE_ void _ensure_node(const RID &p_id, AccessibilityElement *p_ae);
|
||||
|
||||
static void _accessibility_action_callback(struct accesskit_action_request *p_request, void *p_user_data);
|
||||
static accesskit_tree_update *_accessibility_initial_tree_update_callback(void *p_user_data);
|
||||
static void _accessibility_deactivation_callback(void *p_user_data);
|
||||
static accesskit_tree_update *_accessibility_build_tree_update(void *p_user_data);
|
||||
|
||||
bool in_accessibility_update = false;
|
||||
Callable update_cb;
|
||||
|
||||
public:
|
||||
bool is_supported() const override { return true; }
|
||||
|
||||
bool window_create(DisplayServerEnums::WindowID p_window_id, void *p_handle) override;
|
||||
void window_destroy(DisplayServerEnums::WindowID p_window_id) override;
|
||||
|
||||
RID create_element(DisplayServerEnums::WindowID p_window_id, AccessibilityServerEnums::AccessibilityRole p_role) override;
|
||||
RID create_sub_element(const RID &p_parent_rid, AccessibilityServerEnums::AccessibilityRole p_role, int p_insert_pos = -1) override;
|
||||
virtual RID create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1, bool p_is_last_line = false) override;
|
||||
bool has_element(const RID &p_id) const override;
|
||||
void free_element(const RID &p_id) override;
|
||||
|
||||
void element_set_meta(const RID &p_id, const Variant &p_meta) override;
|
||||
Variant element_get_meta(const RID &p_id) const override;
|
||||
|
||||
void update_if_active(const Callable &p_callable) override;
|
||||
|
||||
void update_set_focus(const RID &p_id) override;
|
||||
RID get_window_root(DisplayServerEnums::WindowID p_window_id) const override;
|
||||
|
||||
void set_window_rect(DisplayServerEnums::WindowID p_window_id, const Rect2 &p_rect_out, const Rect2 &p_rect_in) override;
|
||||
void set_window_focused(DisplayServerEnums::WindowID p_window_id, bool p_focused) override;
|
||||
void set_window_callbacks(DisplayServerEnums::WindowID p_window_id, const Callable &p_activate_callable, const Callable &p_deativate_callable) override;
|
||||
void window_activation_completed(DisplayServerEnums::WindowID p_window_id) override;
|
||||
void window_deactivation_completed(DisplayServerEnums::WindowID p_window_id) override;
|
||||
|
||||
void update_set_role(const RID &p_id, AccessibilityServerEnums::AccessibilityRole p_role) override;
|
||||
void update_set_name(const RID &p_id, const String &p_name) override;
|
||||
void update_set_braille_label(const RID &p_id, const String &p_name) override;
|
||||
void update_set_braille_role_description(const RID &p_id, const String &p_description) override;
|
||||
void update_set_extra_info(const RID &p_id, const String &p_name_extra_info) override;
|
||||
void update_set_description(const RID &p_id, const String &p_description) override;
|
||||
void update_set_value(const RID &p_id, const String &p_value) override;
|
||||
void update_set_tooltip(const RID &p_id, const String &p_tooltip) override;
|
||||
void update_set_bounds(const RID &p_id, const Rect2 &p_rect) override;
|
||||
void update_set_transform(const RID &p_id, const Transform2D &p_transform) override;
|
||||
void update_add_child(const RID &p_id, const RID &p_child_id) override;
|
||||
void update_add_related_controls(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_add_related_details(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_add_related_described_by(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_add_related_flow_to(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_add_related_labeled_by(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_add_related_radio_group(const RID &p_id, const RID &p_related_id) override;
|
||||
void update_set_active_descendant(const RID &p_id, const RID &p_other_id) override;
|
||||
void update_set_next_on_line(const RID &p_id, const RID &p_other_id) override;
|
||||
void update_set_previous_on_line(const RID &p_id, const RID &p_other_id) override;
|
||||
void update_set_member_of(const RID &p_id, const RID &p_group_id) override;
|
||||
void update_set_in_page_link_target(const RID &p_id, const RID &p_other_id) override;
|
||||
void update_set_error_message(const RID &p_id, const RID &p_other_id) override;
|
||||
void update_set_live(const RID &p_id, AccessibilityServerEnums::AccessibilityLiveMode p_live) override;
|
||||
void update_add_action(const RID &p_id, AccessibilityServerEnums::AccessibilityAction p_action, const Callable &p_callable) override;
|
||||
void update_add_custom_action(const RID &p_id, int p_action_id, const String &p_action_description) override;
|
||||
void update_set_table_row_count(const RID &p_id, int p_count) override;
|
||||
void update_set_table_column_count(const RID &p_id, int p_count) override;
|
||||
void update_set_table_row_index(const RID &p_id, int p_index) override;
|
||||
void update_set_table_column_index(const RID &p_id, int p_index) override;
|
||||
void update_set_table_cell_position(const RID &p_id, int p_row_index, int p_column_index) override;
|
||||
void update_set_table_cell_span(const RID &p_id, int p_row_span, int p_column_span) override;
|
||||
void update_set_list_item_count(const RID &p_id, int p_size) override;
|
||||
void update_set_list_item_index(const RID &p_id, int p_index) override;
|
||||
void update_set_list_item_level(const RID &p_id, int p_level) override;
|
||||
void update_set_list_item_selected(const RID &p_id, bool p_selected) override;
|
||||
void update_set_list_item_expanded(const RID &p_id, bool p_expanded) override;
|
||||
void update_set_popup_type(const RID &p_id, AccessibilityServerEnums::AccessibilityPopupType p_popup) override;
|
||||
void update_set_checked(const RID &p_id, bool p_checekd) override;
|
||||
void update_set_num_value(const RID &p_id, double p_position) override;
|
||||
void update_set_num_range(const RID &p_id, double p_min, double p_max) override;
|
||||
void update_set_num_step(const RID &p_id, double p_step) override;
|
||||
void update_set_num_jump(const RID &p_id, double p_jump) override;
|
||||
void update_set_scroll_x(const RID &p_id, double p_position) override;
|
||||
void update_set_scroll_x_range(const RID &p_id, double p_min, double p_max) override;
|
||||
void update_set_scroll_y(const RID &p_id, double p_position) override;
|
||||
void update_set_scroll_y_range(const RID &p_id, double p_min, double p_max) override;
|
||||
void update_set_text_decorations(const RID &p_id, bool p_underline, bool p_strikethrough, bool p_overline, const Color &p_color) override;
|
||||
void update_set_text_align(const RID &p_id, HorizontalAlignment p_align) override;
|
||||
void update_set_text_selection(const RID &p_id, const RID &p_text_start_id, int p_start_char, const RID &p_text_end_id, int p_end_char) override;
|
||||
void update_set_flag(const RID &p_id, AccessibilityServerEnums::AccessibilityFlags p_flag, bool p_value) override;
|
||||
void update_set_classname(const RID &p_id, const String &p_classname) override;
|
||||
void update_set_placeholder(const RID &p_id, const String &p_placeholder) override;
|
||||
void update_set_language(const RID &p_id, const String &p_language) override;
|
||||
void update_set_text_orientation(const RID &p_id, bool p_vertical) override;
|
||||
void update_set_list_orientation(const RID &p_id, bool p_vertical) override;
|
||||
void update_set_shortcut(const RID &p_id, const String &p_shortcut) override;
|
||||
void update_set_url(const RID &p_id, const String &p_url) override;
|
||||
void update_set_role_description(const RID &p_id, const String &p_description) override;
|
||||
void update_set_state_description(const RID &p_id, const String &p_description) override;
|
||||
void update_set_color_value(const RID &p_id, const Color &p_color) override;
|
||||
void update_set_background_color(const RID &p_id, const Color &p_color) override;
|
||||
void update_set_foreground_color(const RID &p_id, const Color &p_color) override;
|
||||
|
||||
static void register_create_func();
|
||||
|
||||
AccessibilityServerAccessKit();
|
||||
~AccessibilityServerAccessKit();
|
||||
};
|
||||
|
||||
#endif // ACCESSKIT_ENABLED
|
||||
10
engine/drivers/alsa/SCsub
Normal file
10
engine/drivers/alsa/SCsub
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
if "alsa" in env and env["alsa"]:
|
||||
if env["use_sowrap"]:
|
||||
env.add_source_files(env.drivers_sources, "asound-so_wrap.c")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
14004
engine/drivers/alsa/asound-so_wrap.c
Normal file
14004
engine/drivers/alsa/asound-so_wrap.c
Normal file
File diff suppressed because it is too large
Load diff
5102
engine/drivers/alsa/asound-so_wrap.h
Normal file
5102
engine/drivers/alsa/asound-so_wrap.h
Normal file
File diff suppressed because it is too large
Load diff
351
engine/drivers/alsa/audio_driver_alsa.cpp
Normal file
351
engine/drivers/alsa/audio_driver_alsa.cpp
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_alsa.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "audio_driver_alsa.h"
|
||||
|
||||
#ifdef ALSA_ENABLED
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/math_funcs_binary.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#if defined(PULSEAUDIO_ENABLED) && defined(SOWRAP_ENABLED)
|
||||
extern "C" {
|
||||
extern int initialize_pulse(int verbose);
|
||||
}
|
||||
#endif
|
||||
|
||||
Error AudioDriverALSA::init_output_device() {
|
||||
mix_rate = _get_configured_mix_rate();
|
||||
|
||||
speaker_mode = SPEAKER_MODE_STEREO;
|
||||
channels = 2;
|
||||
|
||||
// If there is a specified output device check that it is really present
|
||||
if (output_device_name != "Default") {
|
||||
PackedStringArray list = get_output_device_list();
|
||||
if (!list.has(output_device_name)) {
|
||||
output_device_name = "Default";
|
||||
new_output_device = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
int status;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
|
||||
#define CHECK_FAIL(m_cond) \
|
||||
if (m_cond) { \
|
||||
fprintf(stderr, "ALSA ERR: %s\n", snd_strerror(status)); \
|
||||
if (pcm_handle) { \
|
||||
snd_pcm_close(pcm_handle); \
|
||||
pcm_handle = nullptr; \
|
||||
} \
|
||||
ERR_FAIL_COND_V(m_cond, ERR_CANT_OPEN); \
|
||||
}
|
||||
|
||||
//todo, add
|
||||
//6 chans - "plug:surround51"
|
||||
//4 chans - "plug:surround40";
|
||||
|
||||
if (output_device_name == "Default") {
|
||||
status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
} else {
|
||||
String device = output_device_name;
|
||||
int pos = device.find_char(';');
|
||||
if (pos != -1) {
|
||||
device = device.substr(0, pos);
|
||||
}
|
||||
status = snd_pcm_open(&pcm_handle, device.utf8().get_data(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN);
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
status = snd_pcm_hw_params_any(pcm_handle, hwparams);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
//not interested in anything else
|
||||
status = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
//todo: support 4 and 6
|
||||
status = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &mix_rate, nullptr);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
// In ALSA the period size seems to be the one that will determine the actual latency
|
||||
// Ref: https://www.alsa-project.org/main/index.php/FramesPeriods
|
||||
unsigned int periods = 2;
|
||||
int latency = Engine::get_singleton()->get_audio_output_latency();
|
||||
buffer_frames = Math::closest_power_of_2(latency * mix_rate / 1000);
|
||||
buffer_size = buffer_frames * periods;
|
||||
period_size = buffer_frames;
|
||||
|
||||
// set buffer size from project settings
|
||||
status = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size, nullptr);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
print_verbose("Audio buffer frames: " + itos(period_size) + " calculated latency: " + itos(period_size * 1000 / mix_rate) + "ms");
|
||||
|
||||
status = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, nullptr);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_hw_params(pcm_handle, hwparams);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
//snd_pcm_hw_params_free(&hwparams);
|
||||
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
status = snd_pcm_sw_params_current(pcm_handle, swparams);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
status = snd_pcm_sw_params(pcm_handle, swparams);
|
||||
CHECK_FAIL(status < 0);
|
||||
|
||||
samples_in.resize(period_size * channels);
|
||||
samples_out.resize(period_size * channels);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverALSA::init() {
|
||||
#ifdef SOWRAP_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
int dylibloader_verbose = 1;
|
||||
#else
|
||||
int dylibloader_verbose = 0;
|
||||
#endif
|
||||
#ifdef PULSEAUDIO_ENABLED
|
||||
// On pulse enabled systems Alsa will silently use pulse.
|
||||
// It doesn't matter if this fails as that likely means there is no pulse
|
||||
initialize_pulse(dylibloader_verbose);
|
||||
#endif
|
||||
|
||||
if (initialize_asound(dylibloader_verbose)) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
#endif
|
||||
bool ver_ok = false;
|
||||
String version = String::utf8(snd_asoundlib_version());
|
||||
Vector<String> ver_parts = version.split(".");
|
||||
if (ver_parts.size() >= 2) {
|
||||
ver_ok = ((ver_parts[0].to_int() == 1 && ver_parts[1].to_int() >= 1)) || (ver_parts[0].to_int() > 1); // 1.1.0
|
||||
}
|
||||
print_verbose(vformat("ALSA %s detected.", version));
|
||||
if (!ver_ok) {
|
||||
print_verbose("Unsupported ALSA library version!");
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
active.clear();
|
||||
exit_thread.clear();
|
||||
|
||||
Error err = init_output_device();
|
||||
if (err == OK) {
|
||||
thread.start(AudioDriverALSA::thread_func, this);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void AudioDriverALSA::thread_func(void *p_udata) {
|
||||
AudioDriverALSA *ad = static_cast<AudioDriverALSA *>(p_udata);
|
||||
|
||||
while (!ad->exit_thread.is_set()) {
|
||||
ad->lock();
|
||||
ad->start_counting_ticks();
|
||||
|
||||
if (!ad->active.is_set()) {
|
||||
for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
|
||||
ad->samples_out.write[i] = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
ad->audio_server_process(ad->period_size, ad->samples_in.ptrw());
|
||||
|
||||
for (uint64_t i = 0; i < ad->period_size * ad->channels; i++) {
|
||||
ad->samples_out.write[i] = ad->samples_in[i] >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
int todo = ad->period_size;
|
||||
int total = 0;
|
||||
|
||||
while (todo && !ad->exit_thread.is_set()) {
|
||||
int16_t *src = (int16_t *)ad->samples_out.ptr();
|
||||
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
|
||||
|
||||
if (wrote > 0) {
|
||||
total += wrote;
|
||||
todo -= wrote;
|
||||
} else if (wrote == -EAGAIN) {
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
|
||||
ad->lock();
|
||||
ad->start_counting_ticks();
|
||||
} else {
|
||||
wrote = snd_pcm_recover(ad->pcm_handle, wrote, 0);
|
||||
if (wrote < 0) {
|
||||
ERR_PRINT("ALSA: Failed and can't recover: " + String(snd_strerror(wrote)));
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// User selected a new output device, finish the current one so we'll init the new device.
|
||||
if (ad->output_device_name != ad->new_output_device) {
|
||||
ad->output_device_name = ad->new_output_device;
|
||||
ad->finish_output_device();
|
||||
|
||||
Error err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("ALSA: init_output_device error");
|
||||
ad->output_device_name = "Default";
|
||||
ad->new_output_device = "Default";
|
||||
|
||||
err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverALSA::start() {
|
||||
active.set();
|
||||
}
|
||||
|
||||
int AudioDriverALSA::get_mix_rate() const {
|
||||
return mix_rate;
|
||||
}
|
||||
|
||||
AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const {
|
||||
return speaker_mode;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverALSA::get_output_device_list() {
|
||||
PackedStringArray list;
|
||||
|
||||
list.push_back("Default");
|
||||
|
||||
void **hints;
|
||||
|
||||
if (snd_device_name_hint(-1, "pcm", &hints) < 0) {
|
||||
return list;
|
||||
}
|
||||
|
||||
for (void **n = hints; *n != nullptr; n++) {
|
||||
char *name = snd_device_name_get_hint(*n, "NAME");
|
||||
char *desc = snd_device_name_get_hint(*n, "DESC");
|
||||
|
||||
if (name != nullptr && !strncmp(name, "plughw", 6)) {
|
||||
if (desc) {
|
||||
list.push_back(String::utf8(name) + ";" + String::utf8(desc));
|
||||
} else {
|
||||
list.push_back(String::utf8(name));
|
||||
}
|
||||
}
|
||||
|
||||
if (desc != nullptr) {
|
||||
free(desc);
|
||||
}
|
||||
if (name != nullptr) {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
String AudioDriverALSA::get_output_device() {
|
||||
return output_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverALSA::set_output_device(const String &p_name) {
|
||||
lock();
|
||||
new_output_device = p_name;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void AudioDriverALSA::lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void AudioDriverALSA::unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void AudioDriverALSA::finish_output_device() {
|
||||
if (pcm_handle) {
|
||||
snd_pcm_close(pcm_handle);
|
||||
pcm_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverALSA::finish() {
|
||||
exit_thread.set();
|
||||
if (thread.is_started()) {
|
||||
thread.wait_to_finish();
|
||||
}
|
||||
|
||||
finish_output_device();
|
||||
}
|
||||
|
||||
#endif // ALSA_ENABLED
|
||||
96
engine/drivers/alsa/audio_driver_alsa.h
Normal file
96
engine/drivers/alsa/audio_driver_alsa.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_alsa.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ALSA_ENABLED
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#include "servers/audio/audio_server.h"
|
||||
|
||||
#ifdef SOWRAP_ENABLED
|
||||
#include "asound-so_wrap.h"
|
||||
#else
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
class AudioDriverALSA : public AudioDriver {
|
||||
Thread thread;
|
||||
Mutex mutex;
|
||||
|
||||
snd_pcm_t *pcm_handle = nullptr;
|
||||
|
||||
String output_device_name = "Default";
|
||||
String new_output_device = "Default";
|
||||
|
||||
Vector<int32_t> samples_in;
|
||||
Vector<int16_t> samples_out;
|
||||
|
||||
Error init_output_device();
|
||||
void finish_output_device();
|
||||
|
||||
static void thread_func(void *p_udata);
|
||||
|
||||
unsigned int mix_rate = 0;
|
||||
SpeakerMode speaker_mode;
|
||||
|
||||
snd_pcm_uframes_t buffer_frames;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_uframes_t period_size;
|
||||
int channels = 0;
|
||||
|
||||
SafeFlag active;
|
||||
SafeFlag exit_thread;
|
||||
|
||||
public:
|
||||
virtual const char *get_name() const override {
|
||||
return "ALSA";
|
||||
}
|
||||
|
||||
virtual Error init() override;
|
||||
virtual void start() override;
|
||||
virtual int get_mix_rate() const override;
|
||||
virtual SpeakerMode get_speaker_mode() const override;
|
||||
|
||||
virtual void lock() override;
|
||||
virtual void unlock() override;
|
||||
virtual void finish() override;
|
||||
|
||||
virtual PackedStringArray get_output_device_list() override;
|
||||
virtual String get_output_device() override;
|
||||
virtual void set_output_device(const String &p_name) override;
|
||||
|
||||
AudioDriverALSA() {}
|
||||
~AudioDriverALSA() {}
|
||||
};
|
||||
|
||||
#endif // ALSA_ENABLED
|
||||
7
engine/drivers/alsamidi/SCsub
Normal file
7
engine/drivers/alsamidi/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
147
engine/drivers/alsamidi/midi_driver_alsamidi.cpp
Normal file
147
engine/drivers/alsamidi/midi_driver_alsamidi.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**************************************************************************/
|
||||
/* midi_driver_alsamidi.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 ALSAMIDI_ENABLED
|
||||
|
||||
#include "midi_driver_alsamidi.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
MIDIDriverALSAMidi::InputConnection::InputConnection(int p_device_index,
|
||||
snd_rawmidi_t *p_rawmidi) :
|
||||
parser(p_device_index), rawmidi_ptr(p_rawmidi) {}
|
||||
|
||||
void MIDIDriverALSAMidi::InputConnection::read() {
|
||||
int read_count;
|
||||
do {
|
||||
uint8_t buffer[32];
|
||||
read_count = snd_rawmidi_read(rawmidi_ptr, buffer, sizeof(buffer));
|
||||
|
||||
if (read_count < 0) {
|
||||
if (read_count != -EAGAIN) {
|
||||
ERR_PRINT("snd_rawmidi_read error: " + String(snd_strerror(read_count)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < read_count; i++) {
|
||||
parser.parse_fragment(buffer[i]);
|
||||
}
|
||||
}
|
||||
} while (read_count > 0);
|
||||
}
|
||||
|
||||
void MIDIDriverALSAMidi::thread_func(void *p_udata) {
|
||||
MIDIDriverALSAMidi *md = static_cast<MIDIDriverALSAMidi *>(p_udata);
|
||||
|
||||
while (!md->exit_thread.is_set()) {
|
||||
md->lock();
|
||||
for (InputConnection &conn : md->connected_inputs) {
|
||||
conn.read();
|
||||
}
|
||||
md->unlock();
|
||||
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Error MIDIDriverALSAMidi::open() {
|
||||
void **hints;
|
||||
|
||||
if (snd_device_name_hint(-1, "rawmidi", &hints) < 0) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
lock();
|
||||
int device_index = 0;
|
||||
for (void **h = hints; *h != nullptr; h++) {
|
||||
char *name = snd_device_name_get_hint(*h, "NAME");
|
||||
|
||||
if (name != nullptr) {
|
||||
snd_rawmidi_t *midi_in;
|
||||
int ret = snd_rawmidi_open(&midi_in, nullptr, name, SND_RAWMIDI_NONBLOCK);
|
||||
if (ret >= 0) {
|
||||
// Get display name.
|
||||
snd_rawmidi_info_t *info;
|
||||
snd_rawmidi_info_malloc(&info);
|
||||
snd_rawmidi_info(midi_in, info);
|
||||
connected_input_names.push_back(snd_rawmidi_info_get_name(info));
|
||||
snd_rawmidi_info_free(info);
|
||||
|
||||
connected_inputs.push_back(InputConnection(device_index, midi_in));
|
||||
// Only increment device_index for successfully connected devices.
|
||||
device_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (name != nullptr) {
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
snd_device_name_free_hint(hints);
|
||||
unlock();
|
||||
|
||||
exit_thread.clear();
|
||||
thread.start(MIDIDriverALSAMidi::thread_func, this);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void MIDIDriverALSAMidi::close() {
|
||||
exit_thread.set();
|
||||
if (thread.is_started()) {
|
||||
thread.wait_to_finish();
|
||||
}
|
||||
|
||||
for (const InputConnection &conn : connected_inputs) {
|
||||
snd_rawmidi_close(conn.rawmidi_ptr);
|
||||
}
|
||||
|
||||
connected_inputs.clear();
|
||||
connected_input_names.clear();
|
||||
}
|
||||
|
||||
void MIDIDriverALSAMidi::lock() const {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void MIDIDriverALSAMidi::unlock() const {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
|
||||
exit_thread.clear();
|
||||
}
|
||||
|
||||
MIDIDriverALSAMidi::~MIDIDriverALSAMidi() {
|
||||
close();
|
||||
}
|
||||
|
||||
#endif // ALSAMIDI_ENABLED
|
||||
79
engine/drivers/alsamidi/midi_driver_alsamidi.h
Normal file
79
engine/drivers/alsamidi/midi_driver_alsamidi.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**************************************************************************/
|
||||
/* midi_driver_alsamidi.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ALSAMIDI_ENABLED
|
||||
|
||||
#include "core/os/midi_driver.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
#ifdef SOWRAP_ENABLED
|
||||
#include "../alsa/asound-so_wrap.h"
|
||||
#else
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
|
||||
class MIDIDriverALSAMidi : public MIDIDriver {
|
||||
Thread thread;
|
||||
Mutex mutex;
|
||||
|
||||
struct InputConnection {
|
||||
InputConnection() = default;
|
||||
InputConnection(int p_device_index, snd_rawmidi_t *p_rawmidi);
|
||||
|
||||
Parser parser;
|
||||
snd_rawmidi_t *rawmidi_ptr = nullptr;
|
||||
|
||||
// Read in and parse available data, forwarding complete events to Input.
|
||||
void read();
|
||||
};
|
||||
|
||||
Vector<InputConnection> connected_inputs;
|
||||
|
||||
SafeFlag exit_thread;
|
||||
|
||||
static void thread_func(void *p_udata);
|
||||
|
||||
void lock() const;
|
||||
void unlock() const;
|
||||
|
||||
public:
|
||||
virtual Error open() override;
|
||||
virtual void close() override;
|
||||
|
||||
MIDIDriverALSAMidi();
|
||||
virtual ~MIDIDriverALSAMidi();
|
||||
};
|
||||
|
||||
#endif // ALSAMIDI_ENABLED
|
||||
8
engine/drivers/apple/SCsub
Normal file
8
engine/drivers/apple/SCsub
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.mm")
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
87
engine/drivers/apple/foundation_helpers.h
Normal file
87
engine/drivers/apple/foundation_helpers.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/**************************************************************************/
|
||||
/* foundation_helpers.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/NSString.h>
|
||||
#else
|
||||
#include <Foundation/NSString.hpp>
|
||||
#endif
|
||||
|
||||
class String;
|
||||
template <typename T>
|
||||
class CharStringT;
|
||||
|
||||
using CharString = CharStringT<char>;
|
||||
|
||||
template <typename T>
|
||||
class Span;
|
||||
|
||||
namespace conv {
|
||||
|
||||
#ifdef __OBJC__
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NSString *to_nsstring(const String &p_str);
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NSString *to_nsstring(Span<char> p_str);
|
||||
/**
|
||||
* Converts a Godot CharString to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NSString *to_nsstring(const CharString &p_str);
|
||||
/**
|
||||
* Converts an NSString to a Godot String without allocating intermediate buffers.
|
||||
* */
|
||||
String to_string(NSString *p_str);
|
||||
#else
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(const String &p_str);
|
||||
/**
|
||||
* Converts a Godot String to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(Span<char> p_str);
|
||||
/**
|
||||
* Converts a Godot CharString to an NSString without allocating an intermediate UTF-8 buffer.
|
||||
* */
|
||||
NS::String *to_nsstring(const CharString &p_str);
|
||||
/**
|
||||
* Converts an NSString to a Godot String without allocating intermediate buffers.
|
||||
* */
|
||||
String to_string(NS::String *p_str);
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace conv
|
||||
92
engine/drivers/apple/foundation_helpers.mm
Normal file
92
engine/drivers/apple/foundation_helpers.mm
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**************************************************************************/
|
||||
/* foundation_helpers.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "foundation_helpers.h"
|
||||
|
||||
#import "core/string/ustring.h"
|
||||
#import "core/templates/span.h"
|
||||
|
||||
#import <CoreFoundation/CFString.h>
|
||||
|
||||
namespace conv {
|
||||
|
||||
NSString *to_nsstring(const String &p_str) {
|
||||
return [[NSString alloc] initWithBytes:(const void *)p_str.ptr()
|
||||
length:p_str.length() * sizeof(char32_t)
|
||||
encoding:NSUTF32LittleEndianStringEncoding];
|
||||
}
|
||||
|
||||
NSString *to_nsstring(Span<char> p_str) {
|
||||
return [[NSString alloc] initWithBytes:(const void *)p_str.ptr()
|
||||
length:p_str.size()
|
||||
encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
|
||||
NSString *to_nsstring(const CharString &p_str) {
|
||||
return [[NSString alloc] initWithBytes:(const void *)p_str.ptr()
|
||||
length:p_str.length()
|
||||
encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
String to_string(NSString *p_str) {
|
||||
CFStringRef str = (__bridge CFStringRef)p_str;
|
||||
CFStringEncoding fastest = CFStringGetFastestEncoding(str);
|
||||
// Sometimes, CFString will return a pointer to it's encoded data,
|
||||
// so we can create the string without allocating intermediate buffers.
|
||||
const char *p = CFStringGetCStringPtr(str, fastest);
|
||||
if (p) {
|
||||
switch (fastest) {
|
||||
case kCFStringEncodingASCII:
|
||||
return String::ascii(Span(p, CFStringGetLength(str)));
|
||||
case kCFStringEncodingUTF8:
|
||||
return String::utf8(p);
|
||||
case kCFStringEncodingUTF32LE:
|
||||
return String::utf32(Span((char32_t *)p, CFStringGetLength(str)));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFRange range = CFRangeMake(0, CFStringGetLength(str));
|
||||
CFIndex byte_len = 0;
|
||||
// Try to losslessly convert the string directly into a String's buffer to avoid intermediate allocations.
|
||||
CFIndex n = CFStringGetBytes(str, range, kCFStringEncodingUTF32LE, 0, NO, nil, 0, &byte_len);
|
||||
if (n == range.length) {
|
||||
String res;
|
||||
res.resize_uninitialized((byte_len / sizeof(char32_t)) + 1);
|
||||
res[n] = 0;
|
||||
n = CFStringGetBytes(str, range, kCFStringEncodingUTF32LE, 0, NO, (UInt8 *)res.ptrw(), res.length() * sizeof(char32_t), nil);
|
||||
return res;
|
||||
}
|
||||
|
||||
return String::utf8(p_str.UTF8String);
|
||||
}
|
||||
|
||||
} //namespace conv
|
||||
127
engine/drivers/apple/os_log_logger.cpp
Normal file
127
engine/drivers/apple/os_log_logger.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/**************************************************************************/
|
||||
/* os_log_logger.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "os_log_logger.h"
|
||||
|
||||
#include "core/object/script_backtrace.h"
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
#include <cstdio> // For vsnprintf.
|
||||
#include <cstdlib> // For malloc/free.
|
||||
|
||||
OsLogLogger::OsLogLogger(const char *p_subsystem) {
|
||||
const char *subsystem = p_subsystem;
|
||||
if (!subsystem) {
|
||||
subsystem = "org.godotengine.godot";
|
||||
os_log_info(OS_LOG_DEFAULT, "Missing subsystem for os_log logging; using %{public}s", subsystem);
|
||||
}
|
||||
|
||||
log = os_log_create(subsystem, "engine");
|
||||
error_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_ERROR));
|
||||
warning_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_WARNING));
|
||||
script_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_SCRIPT));
|
||||
shader_log = os_log_create(subsystem, error_type_string(ErrorType::ERR_SHADER));
|
||||
}
|
||||
|
||||
void OsLogLogger::logv(const char *p_format, va_list p_list, bool p_err) {
|
||||
constexpr int static_buf_size = 1024;
|
||||
char static_buf[static_buf_size] = { '\0' };
|
||||
char *buf = static_buf;
|
||||
va_list list_copy;
|
||||
va_copy(list_copy, p_list);
|
||||
int len = vsnprintf(buf, static_buf_size, p_format, p_list);
|
||||
if (len >= static_buf_size) {
|
||||
buf = (char *)Memory::alloc_static(len + 1);
|
||||
vsnprintf(buf, len + 1, p_format, list_copy);
|
||||
}
|
||||
va_end(list_copy);
|
||||
|
||||
// Choose appropriate log type based on error flag.
|
||||
os_log_type_t log_type = p_err ? OS_LOG_TYPE_ERROR : OS_LOG_TYPE_INFO;
|
||||
os_log_with_type(log, log_type, "%{public}s", buf);
|
||||
|
||||
if (len >= static_buf_size) {
|
||||
Memory::free_static(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void OsLogLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
|
||||
os_log_t selected_log;
|
||||
switch (p_type) {
|
||||
case ERR_WARNING:
|
||||
selected_log = warning_log;
|
||||
break;
|
||||
case ERR_SCRIPT:
|
||||
selected_log = script_log;
|
||||
break;
|
||||
case ERR_SHADER:
|
||||
selected_log = shader_log;
|
||||
break;
|
||||
case ERR_ERROR:
|
||||
default:
|
||||
selected_log = error_log;
|
||||
break;
|
||||
}
|
||||
const char *err_details;
|
||||
if (p_rationale && *p_rationale) {
|
||||
err_details = p_rationale;
|
||||
} else {
|
||||
err_details = p_code;
|
||||
}
|
||||
|
||||
// Choose log level based on error type.
|
||||
os_log_type_t log_type;
|
||||
switch (p_type) {
|
||||
case ERR_WARNING:
|
||||
log_type = OS_LOG_TYPE_DEFAULT;
|
||||
break;
|
||||
case ERR_ERROR:
|
||||
case ERR_SCRIPT:
|
||||
case ERR_SHADER:
|
||||
default:
|
||||
log_type = OS_LOG_TYPE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Append script backtraces, if any.
|
||||
String back_trace;
|
||||
for (const Ref<ScriptBacktrace> &backtrace : p_script_backtraces) {
|
||||
if (backtrace.is_valid() && !backtrace->is_empty()) {
|
||||
back_trace += "\n";
|
||||
back_trace += backtrace->format(strlen(error_type_indent(p_type)));
|
||||
}
|
||||
}
|
||||
|
||||
if (back_trace.is_empty()) {
|
||||
os_log_with_type(selected_log, log_type, "%{public}s:%d:%{public}s(): %{public}s %{public}s", p_file, p_line, p_function, err_details, p_code);
|
||||
} else {
|
||||
os_log_with_type(selected_log, log_type, "%{public}s:%d:%{public}s(): %{public}s %{public}s%{public}s", p_file, p_line, p_function, err_details, p_code, back_trace.utf8().ptr());
|
||||
}
|
||||
}
|
||||
55
engine/drivers/apple/os_log_logger.h
Normal file
55
engine/drivers/apple/os_log_logger.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************/
|
||||
/* os_log_logger.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/logger.h"
|
||||
|
||||
#include <os/log.h>
|
||||
|
||||
/**
|
||||
* @brief Apple unified logging system integration for Godot Engine.
|
||||
*/
|
||||
class OsLogLogger : public Logger {
|
||||
os_log_t log;
|
||||
os_log_t error_log;
|
||||
os_log_t warning_log;
|
||||
os_log_t script_log;
|
||||
os_log_t shader_log;
|
||||
|
||||
public:
|
||||
void logv(const char *p_format, va_list p_list, bool p_err) override _PRINTF_FORMAT_ATTRIBUTE_2_0;
|
||||
void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {}) override;
|
||||
|
||||
/**
|
||||
* @brief Constructs an OsLogLogger with the specified subsystem identifier, which is normally the bundle identifier.
|
||||
*/
|
||||
OsLogLogger(const char *p_subsystem);
|
||||
};
|
||||
140
engine/drivers/apple/thread_apple.cpp
Normal file
140
engine/drivers/apple/thread_apple.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/**************************************************************************/
|
||||
/* thread_apple.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "thread_apple.h"
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1.
|
||||
thread_local Thread::ID Thread::caller_id = Thread::id_counter.increment();
|
||||
|
||||
struct ThreadData {
|
||||
Thread::Callback callback;
|
||||
void *userdata;
|
||||
Thread::ID caller_id;
|
||||
};
|
||||
|
||||
void *Thread::thread_callback(void *p_data) {
|
||||
ThreadData *thread_data = static_cast<ThreadData *>(p_data);
|
||||
|
||||
// Set the caller ID for this thread
|
||||
caller_id = thread_data->caller_id;
|
||||
|
||||
ScriptServer::thread_enter(); // Scripts may need to attach a stack.
|
||||
|
||||
// Call the actual callback
|
||||
thread_data->callback(thread_data->userdata);
|
||||
|
||||
ScriptServer::thread_exit();
|
||||
|
||||
// Clean up
|
||||
memdelete(thread_data);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Error Thread::set_name(const String &p_name) {
|
||||
int err = pthread_setname_np(p_name.utf8().get_data());
|
||||
return err == 0 ? OK : ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
|
||||
ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it.");
|
||||
id = id_counter.increment();
|
||||
|
||||
ThreadData *thread_data = memnew(ThreadData);
|
||||
thread_data->callback = p_callback;
|
||||
thread_data->userdata = p_user;
|
||||
thread_data->caller_id = id;
|
||||
|
||||
// Create the thread
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
switch (p_settings.priority) {
|
||||
case PRIORITY_LOW:
|
||||
pthread_attr_set_qos_class_np(&attr, QOS_CLASS_UTILITY, 0);
|
||||
break;
|
||||
case PRIORITY_NORMAL:
|
||||
pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INITIATED, 0);
|
||||
break;
|
||||
case PRIORITY_HIGH:
|
||||
pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// The default stack size for secondary threads on Apple platforms is 512KiB.
|
||||
// This is insufficient when using a library like SPIRV-Cross, which can generate deep stacks and result in a stack overflow.
|
||||
// It also creates a problematic discrepancy with other platforms, where secondary threads are often at least 1 MiB.
|
||||
pthread_attr_setstacksize(&attr,
|
||||
#if __has_feature(address_sanitizer) || __has_feature(thread_sanitizer)
|
||||
// ASan (and to some degree TSan) needs a lot of extra stack size.
|
||||
4 * 1024 * 1024 // 4 MiB
|
||||
#elif !defined(__OPTIMIZE__)
|
||||
// Unoptimized builds also need a larger stack size.
|
||||
2 * 1024 * 1024 // 2 MiB
|
||||
#else
|
||||
1 * 1024 * 1024 // 1 MiB
|
||||
#endif
|
||||
);
|
||||
|
||||
// Create the thread
|
||||
pthread_create(&pthread, &attr, thread_callback, thread_data);
|
||||
|
||||
// Clean up attributes
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void Thread::wait_to_finish() {
|
||||
ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started.");
|
||||
ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait.");
|
||||
|
||||
int err = pthread_join(pthread, nullptr);
|
||||
if (err != 0) {
|
||||
ERR_FAIL_MSG("Thread::wait_to_finish() failed to join thread.");
|
||||
}
|
||||
pthread = pthread_t();
|
||||
id = UNASSIGNED_ID;
|
||||
}
|
||||
|
||||
Thread::~Thread() {
|
||||
if (id != UNASSIGNED_ID) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
WARN_PRINT(
|
||||
"A Thread object is being destroyed without its completion having been realized.\n"
|
||||
"Please call wait_to_finish() on it to ensure correct cleanup.");
|
||||
#endif
|
||||
pthread_detach(pthread);
|
||||
}
|
||||
}
|
||||
108
engine/drivers/apple/thread_apple.h
Normal file
108
engine/drivers/apple/thread_apple.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**************************************************************************/
|
||||
/* thread_apple.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <new> // For hardware interference size
|
||||
|
||||
class String;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
typedef void (*Callback)(void *p_userdata);
|
||||
|
||||
typedef uint64_t ID;
|
||||
|
||||
enum : ID {
|
||||
UNASSIGNED_ID = 0,
|
||||
MAIN_ID = 1
|
||||
};
|
||||
|
||||
enum Priority {
|
||||
PRIORITY_LOW,
|
||||
PRIORITY_NORMAL,
|
||||
PRIORITY_HIGH
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
Priority priority;
|
||||
Settings() { priority = PRIORITY_NORMAL; }
|
||||
};
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size)
|
||||
GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Winterference-size")
|
||||
static constexpr size_t CACHE_LINE_BYTES = std::hardware_destructive_interference_size;
|
||||
GODOT_GCC_WARNING_POP
|
||||
#else
|
||||
// At a negligible memory cost, we use a conservatively high value.
|
||||
static constexpr size_t CACHE_LINE_BYTES = 128;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class Main;
|
||||
|
||||
ID id = UNASSIGNED_ID;
|
||||
pthread_t pthread;
|
||||
|
||||
static SafeNumeric<uint64_t> id_counter;
|
||||
static thread_local ID caller_id;
|
||||
|
||||
static void *thread_callback(void *p_data);
|
||||
|
||||
static void make_main_thread() { caller_id = MAIN_ID; }
|
||||
static void release_main_thread() { caller_id = id_counter.increment(); }
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ static void yield() { pthread_yield_np(); }
|
||||
|
||||
_FORCE_INLINE_ ID get_id() const { return id; }
|
||||
// get the ID of the caller thread
|
||||
_FORCE_INLINE_ static ID get_caller_id() {
|
||||
return caller_id;
|
||||
}
|
||||
// get the ID of the main thread
|
||||
_FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; }
|
||||
|
||||
_FORCE_INLINE_ static bool is_main_thread() { return caller_id == MAIN_ID; }
|
||||
|
||||
static Error set_name(const String &p_name);
|
||||
|
||||
ID start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
|
||||
bool is_started() const { return id != UNASSIGNED_ID; }
|
||||
/// Waits until thread is finished, and deallocates it.
|
||||
void wait_to_finish();
|
||||
|
||||
Thread() = default;
|
||||
~Thread();
|
||||
};
|
||||
35
engine/drivers/apple_embedded/SCsub
Normal file
35
engine/drivers/apple_embedded/SCsub
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
from SCons.Script import Glob
|
||||
|
||||
from platform_methods import setup_swift_builder
|
||||
|
||||
Import("env")
|
||||
|
||||
env_apple_embedded = env.Clone()
|
||||
|
||||
# Enable module support
|
||||
env_apple_embedded.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# Configure Swift builder
|
||||
apple_platform = env["APPLE_PLATFORM"]
|
||||
sdk_path = env["APPLE_SDK_PATH"]
|
||||
current_path = Dir(".").abspath
|
||||
bridging_header_filename = "bridging_header_apple_embedded.h"
|
||||
swift_files = Glob("*.swift")
|
||||
swift_file_names = list(map(lambda f: f.name, swift_files))
|
||||
setup_swift_builder(
|
||||
env_apple_embedded, apple_platform, sdk_path, current_path, bridging_header_filename, swift_file_names
|
||||
)
|
||||
|
||||
# Use bundled Vulkan headers
|
||||
vulkan_dir = "#thirdparty/vulkan"
|
||||
env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])
|
||||
|
||||
# Use bundled metal-cpp headers
|
||||
env_apple_embedded.Prepend(CPPPATH=["#thirdparty/metal-cpp"])
|
||||
|
||||
# Driver source files
|
||||
env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.mm")
|
||||
env_apple_embedded.add_source_files(env_apple_embedded.drivers_sources, "*.swift")
|
||||
58
engine/drivers/apple_embedded/app.swift
Normal file
58
engine/drivers/apple_embedded/app.swift
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**************************************************************************/
|
||||
/* app.swift */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct GodotSwiftUIViewController: UIViewControllerRepresentable {
|
||||
|
||||
func makeUIViewController(context: Context) -> GDTViewController {
|
||||
let viewController = GDTViewController()
|
||||
GDTAppDelegateService.viewController = viewController
|
||||
return viewController
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: GDTViewController, context: Context) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@main
|
||||
struct SwiftUIApp: App {
|
||||
@UIApplicationDelegateAdaptor(GDTApplicationDelegate.self) var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
GodotSwiftUIViewController()
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
}
|
||||
41
engine/drivers/apple_embedded/app_delegate_service.h
Normal file
41
engine/drivers/apple_embedded/app_delegate_service.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**************************************************************************/
|
||||
/* app_delegate_service.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class GDTViewController;
|
||||
|
||||
@interface GDTAppDelegateService : NSObject <UIApplicationDelegate, UIWindowSceneDelegate>
|
||||
|
||||
@property(strong, class, nonatomic) GDTViewController *viewController;
|
||||
|
||||
@end
|
||||
203
engine/drivers/apple_embedded/app_delegate_service.mm
Normal file
203
engine/drivers/apple_embedded/app_delegate_service.mm
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/**************************************************************************/
|
||||
/* app_delegate_service.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_controller.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/main_loop.h"
|
||||
#include "core/os/os.h"
|
||||
#import "drivers/coreaudio/audio_driver_coreaudio.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
int gargc;
|
||||
char **gargv;
|
||||
|
||||
extern int apple_embedded_main(int, char **);
|
||||
extern void apple_embedded_finish();
|
||||
|
||||
@implementation GDTAppDelegateService
|
||||
|
||||
enum {
|
||||
SESSION_CATEGORY_AMBIENT,
|
||||
SESSION_CATEGORY_MULTI_ROUTE,
|
||||
SESSION_CATEGORY_PLAY_AND_RECORD,
|
||||
SESSION_CATEGORY_PLAYBACK,
|
||||
SESSION_CATEGORY_RECORD,
|
||||
SESSION_CATEGORY_SOLO_AMBIENT
|
||||
};
|
||||
|
||||
static GDTViewController *mainViewController = nil;
|
||||
|
||||
+ (GDTViewController *)viewController {
|
||||
return mainViewController;
|
||||
}
|
||||
|
||||
+ (void)setViewController:(GDTViewController *)viewController {
|
||||
mainViewController = viewController;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// TODO: might be required to make an early return, so app wouldn't crash because of timeout.
|
||||
// TODO: logo screen is not displayed while shaders are compiling
|
||||
// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
|
||||
|
||||
// Fetch the command-line arguments from NSProcessInfo
|
||||
NSArray *arguments = [[NSProcessInfo processInfo] arguments];
|
||||
gargc = (int)[arguments count];
|
||||
gargv = (char **)malloc(sizeof(char *) * gargc);
|
||||
for (int i = 0; i < gargc; i++) {
|
||||
NSString *arg = arguments[i];
|
||||
gargv[i] = strdup([arg UTF8String]);
|
||||
}
|
||||
|
||||
int err = apple_embedded_main(gargc, gargv);
|
||||
|
||||
if (err != 0) {
|
||||
// bail, things did not go very well for us, should probably output a message on screen with our error code...
|
||||
exit(0);
|
||||
return NO;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(onAudioInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:[AVAudioSession sharedInstance]];
|
||||
|
||||
int sessionCategorySetting = GLOBAL_GET("audio/general/ios/session_category");
|
||||
|
||||
// Initialize with default Ambient category.
|
||||
AVAudioSessionCategory category = AVAudioSessionCategoryAmbient;
|
||||
AVAudioSessionCategoryOptions options = 0;
|
||||
|
||||
if (GLOBAL_GET("audio/general/ios/mix_with_others")) {
|
||||
options |= AVAudioSessionCategoryOptionMixWithOthers;
|
||||
}
|
||||
|
||||
if (sessionCategorySetting == SESSION_CATEGORY_MULTI_ROUTE) {
|
||||
category = AVAudioSessionCategoryMultiRoute;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAY_AND_RECORD) {
|
||||
category = AVAudioSessionCategoryPlayAndRecord;
|
||||
options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
|
||||
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
|
||||
options |= AVAudioSessionCategoryOptionAllowAirPlay;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAYBACK) {
|
||||
category = AVAudioSessionCategoryPlayback;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_RECORD) {
|
||||
category = AVAudioSessionCategoryRecord;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_SOLO_AMBIENT) {
|
||||
category = AVAudioSessionCategorySoloAmbient;
|
||||
}
|
||||
|
||||
[[AVAudioSession sharedInstance] setCategory:category withOptions:options error:nil];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)onAudioInterruption:(NSNotification *)notification {
|
||||
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
|
||||
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
|
||||
NSLog(@"Audio interruption began");
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
|
||||
NSLog(@"Audio interruption ended");
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_in();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
apple_embedded_finish();
|
||||
}
|
||||
|
||||
// When application goes to background (e.g. user switches to another app or presses Home),
|
||||
// then applicationWillResignActive -> applicationDidEnterBackground are called.
|
||||
// When user opens the inactive app again,
|
||||
// applicationWillEnterForeground -> applicationDidBecomeActive are called.
|
||||
|
||||
// There are cases when applicationWillResignActive -> applicationDidBecomeActive
|
||||
// sequence is called without the app going to background. For example, that happens
|
||||
// if you open the app list without switching to another app or open/close the
|
||||
// notification panel by swiping from the upper part of the screen.
|
||||
|
||||
- (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
}
|
||||
|
||||
- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
}
|
||||
|
||||
- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_in();
|
||||
}
|
||||
|
||||
- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
OS_AppleEmbedded::get_singleton()->on_enter_background();
|
||||
}
|
||||
|
||||
- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
OS_AppleEmbedded::get_singleton()->on_exit_background();
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_in();
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_enter_background();
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_exit_background();
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.window = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
59
engine/drivers/apple_embedded/apple_embedded.h
Normal file
59
engine/drivers/apple_embedded/apple_embedded.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/object.h"
|
||||
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
|
||||
class AppleEmbedded : public Object {
|
||||
GDCLASS(AppleEmbedded, Object);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
CHHapticEngine *haptic_engine API_AVAILABLE(ios(13)) = nullptr;
|
||||
|
||||
CHHapticEngine *get_haptic_engine_instance() API_AVAILABLE(ios(13));
|
||||
void start_haptic_engine();
|
||||
void stop_haptic_engine();
|
||||
|
||||
public:
|
||||
static void alert(const char *p_alert, const char *p_title);
|
||||
|
||||
bool supports_haptic_engine();
|
||||
void vibrate_haptic_engine(float p_duration_seconds, float p_amplitude);
|
||||
|
||||
String get_model() const;
|
||||
String get_rate_url(int p_app_id) const;
|
||||
|
||||
AppleEmbedded();
|
||||
};
|
||||
202
engine/drivers/apple_embedded/apple_embedded.mm
Normal file
202
engine/drivers/apple_embedded/apple_embedded.mm
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/**************************************************************************/
|
||||
/* apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "apple_embedded.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "godot_view_controller.h"
|
||||
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
void AppleEmbedded::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &AppleEmbedded::get_rate_url);
|
||||
ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &AppleEmbedded::supports_haptic_engine);
|
||||
ClassDB::bind_method(D_METHOD("start_haptic_engine"), &AppleEmbedded::start_haptic_engine);
|
||||
ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &AppleEmbedded::stop_haptic_engine);
|
||||
}
|
||||
|
||||
bool AppleEmbedded::supports_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
id<CHHapticDeviceCapability> capabilities = [CHHapticEngine capabilitiesForHardware];
|
||||
return capabilities.supportsHaptics;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CHHapticEngine *AppleEmbedded::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
|
||||
if (haptic_engine == nullptr) {
|
||||
NSError *error = nullptr;
|
||||
haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error];
|
||||
|
||||
if (!error) {
|
||||
[haptic_engine setAutoShutdownEnabled:true];
|
||||
} else {
|
||||
haptic_engine = nullptr;
|
||||
NSLog(@"Could not initialize haptic engine: %@", error);
|
||||
}
|
||||
}
|
||||
|
||||
return haptic_engine;
|
||||
}
|
||||
|
||||
void AppleEmbedded::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) {
|
||||
if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy...
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
NSDictionary *hapticDict;
|
||||
if (p_amplitude < 0) {
|
||||
hapticDict = @{
|
||||
CHHapticPatternKeyPattern : @[
|
||||
@{CHHapticPatternKeyEvent : @{
|
||||
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
|
||||
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
|
||||
CHHapticPatternKeyEventDuration : @(p_duration_seconds),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
hapticDict = @{
|
||||
CHHapticPatternKeyPattern : @[
|
||||
@{CHHapticPatternKeyEvent : @{
|
||||
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
|
||||
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
|
||||
CHHapticPatternKeyEventDuration : @(p_duration_seconds),
|
||||
CHHapticPatternKeyEventParameters : @[
|
||||
@{
|
||||
CHHapticPatternKeyParameterID : @("HapticIntensity"),
|
||||
CHHapticPatternKeyParameterValue : @(p_amplitude)
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
|
||||
|
||||
[[cur_haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error];
|
||||
|
||||
NSLog(@"Could not vibrate using haptic engine: %@", error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::start_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
[cur_haptic_engine startWithCompletionHandler:^(NSError *returnedError) {
|
||||
if (returnedError) {
|
||||
NSLog(@"Could not start haptic engine: %@", returnedError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::stop_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
[cur_haptic_engine stopWithCompletionHandler:^(NSError *returnedError) {
|
||||
if (returnedError) {
|
||||
NSLog(@"Could not stop haptic engine: %@", returnedError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::alert(const char *p_alert, const char *p_title) {
|
||||
NSString *title = [NSString stringWithUTF8String:p_title];
|
||||
NSString *message = [NSString stringWithUTF8String:p_alert];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *button = [UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(id){
|
||||
}];
|
||||
|
||||
[alert addAction:button];
|
||||
|
||||
[GDTAppDelegateService.viewController presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
String AppleEmbedded::get_model() const {
|
||||
// [[UIDevice currentDevice] model] only returns "iPad" or "iPhone".
|
||||
size_t size;
|
||||
sysctlbyname("hw.machine", nullptr, &size, nullptr, 0);
|
||||
char *model = (char *)malloc(size);
|
||||
if (model == nullptr) {
|
||||
return "";
|
||||
}
|
||||
sysctlbyname("hw.machine", model, &size, nullptr, 0);
|
||||
NSString *platform = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
|
||||
free(model);
|
||||
const char *str = [platform UTF8String];
|
||||
return String::utf8(str != nullptr ? str : "");
|
||||
}
|
||||
|
||||
String AppleEmbedded::get_rate_url(int p_app_id) const {
|
||||
String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
|
||||
|
||||
String ret = app_url_path.replace("APP_ID", String::num_int64(p_app_id));
|
||||
|
||||
print_verbose(vformat("Returning rate url %s", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
AppleEmbedded::AppleEmbedded() {}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**************************************************************************/
|
||||
/* bridging_header_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "godot_app_delegate.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_controller.h"
|
||||
42
engine/drivers/apple_embedded/display_layer_apple_embedded.h
Normal file
42
engine/drivers/apple_embedded/display_layer_apple_embedded.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**************************************************************************/
|
||||
/* display_layer_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
@protocol GDTDisplayLayer <NSObject>
|
||||
|
||||
- (void)startRenderDisplayLayer;
|
||||
- (void)stopRenderDisplayLayer;
|
||||
- (void)initializeDisplayLayer;
|
||||
- (void)layoutDisplayLayer;
|
||||
|
||||
@end
|
||||
266
engine/drivers/apple_embedded/display_server_apple_embedded.h
Normal file
266
engine/drivers/apple_embedded/display_server_apple_embedded.h
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/**************************************************************************/
|
||||
/* display_server_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display/display_server.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
|
||||
#include "drivers/vulkan/godot_vulkan.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
|
||||
#if defined(METAL_ENABLED)
|
||||
#import "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif // METAL_ENABLED
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
class InputEvent;
|
||||
class NativeMenu;
|
||||
|
||||
/// "Embedded" as in "Embedded Device".
|
||||
class DisplayServerAppleEmbedded : public DisplayServer {
|
||||
GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer);
|
||||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
NativeMenu *native_menu = nullptr;
|
||||
|
||||
id tts = nullptr;
|
||||
|
||||
DisplayServerEnums::ScreenOrientation screen_orientation;
|
||||
|
||||
ObjectID window_attached_instance_id;
|
||||
|
||||
Callable window_event_callback;
|
||||
Callable window_resize_callback;
|
||||
Callable input_event_callback;
|
||||
Callable input_text_callback;
|
||||
|
||||
Callable system_theme_changed;
|
||||
|
||||
int virtual_keyboard_height = 0;
|
||||
|
||||
void perform_event(const Ref<InputEvent> &p_event);
|
||||
|
||||
void initialize_tts() const;
|
||||
|
||||
bool edr_requested = false;
|
||||
void _update_hdr_output();
|
||||
float _calculate_current_reference_luminance() const;
|
||||
|
||||
protected:
|
||||
virtual bool _screen_hdr_is_supported() const { return false; }
|
||||
virtual float _screen_potential_edr_headroom() const { return 1.0f; }
|
||||
virtual float _screen_current_edr_headroom() const { return 1.0f; }
|
||||
float hardware_reference_luminance_nits = 100.0f;
|
||||
|
||||
DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServerEnums::WindowMode p_mode, DisplayServerEnums::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, DisplayServerEnums::Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerAppleEmbedded();
|
||||
|
||||
public:
|
||||
String rendering_driver;
|
||||
|
||||
static DisplayServerAppleEmbedded *get_singleton();
|
||||
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
|
||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||
void send_input_event(const Ref<InputEvent> &p_event) const;
|
||||
void send_input_text(const String &p_text) const;
|
||||
void send_window_event(DisplayServerEnums::WindowEvent p_event) const;
|
||||
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
|
||||
|
||||
void emit_system_theme_changed();
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches and Apple Pencil
|
||||
|
||||
void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click);
|
||||
void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt);
|
||||
void touches_canceled(int p_idx);
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
void key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location);
|
||||
bool is_keyboard_active() const;
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
void update_gravity(const Vector3 &p_gravity);
|
||||
void update_accelerometer(const Vector3 &p_accelerometer);
|
||||
void update_magnetometer(const Vector3 &p_magnetometer);
|
||||
void update_gyroscope(const Vector3 &p_gyroscope);
|
||||
|
||||
// MARK: -
|
||||
|
||||
virtual bool has_feature(DisplayServerEnums::Feature p_feature) const override;
|
||||
|
||||
virtual bool tts_is_speaking() const override;
|
||||
virtual bool tts_is_paused() const override;
|
||||
virtual TypedArray<Dictionary> tts_get_voices() const override;
|
||||
|
||||
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int64_t p_utterance_id = 0, bool p_interrupt = false) override;
|
||||
virtual void tts_pause() override;
|
||||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
|
||||
|
||||
virtual Rect2i get_display_safe_area() const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = DisplayServerEnums::SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = DisplayServerEnums::SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = DisplayServerEnums::SCREEN_OF_MAIN_WINDOW) const override;
|
||||
|
||||
virtual Vector<DisplayServerEnums::WindowID> get_window_list() const override;
|
||||
|
||||
virtual DisplayServerEnums::WindowID get_window_at_screen_position(const Point2i &p_position) const override;
|
||||
|
||||
virtual int64_t window_get_native_handle(DisplayServerEnums::HandleType p_handle_type, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual ObjectID window_get_attached_instance_id(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_title(const String &p_title, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int window_get_current_screen(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_current_screen(int p_screen, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Point2i window_get_position(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual Point2i window_get_position_with_decorations(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(DisplayServerEnums::WindowID p_window, DisplayServerEnums::WindowID p_parent) override;
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_max_size(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_min_size(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_size(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual Size2i window_get_size_with_decorations(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(DisplayServerEnums::WindowMode p_mode, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual DisplayServerEnums::WindowMode window_get_mode(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(DisplayServerEnums::WindowFlags p_flag, bool p_enabled, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(DisplayServerEnums::WindowFlags p_flag, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual float screen_get_max_scale() const override;
|
||||
|
||||
virtual void screen_set_orientation(DisplayServerEnums::ScreenOrientation p_orientation, int p_screen) override;
|
||||
virtual DisplayServerEnums::ScreenOrientation screen_get_orientation(int p_screen) const override;
|
||||
|
||||
virtual bool window_can_draw(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool can_any_window_draw() const override;
|
||||
|
||||
virtual void window_set_vsync_mode(DisplayServerEnums::VSyncMode p_vsync_mode, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual DisplayServerEnums::VSyncMode window_get_vsync_mode(DisplayServerEnums::WindowID p_vsync_mode) const override;
|
||||
|
||||
virtual bool is_touchscreen_available() const override;
|
||||
|
||||
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, DisplayServerEnums::VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) override;
|
||||
virtual void virtual_keyboard_hide() override;
|
||||
|
||||
void virtual_keyboard_set_height(int height);
|
||||
virtual int virtual_keyboard_get_height() const override;
|
||||
virtual bool has_hardware_keyboard() const override;
|
||||
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
|
||||
virtual void screen_set_keep_on(bool p_enable) override;
|
||||
virtual bool screen_is_kept_on() const override;
|
||||
|
||||
// MARK: - HDR / EDR
|
||||
|
||||
void current_edr_headroom_changed();
|
||||
virtual bool window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual void window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_hdr_output_requested(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual bool window_is_hdr_output_enabled(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual float window_get_hdr_output_reference_luminance(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual float window_get_hdr_output_current_reference_luminance(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) override;
|
||||
virtual float window_get_hdr_output_max_luminance(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
virtual float window_get_hdr_output_current_max_luminance(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual float window_get_output_max_linear_value(DisplayServerEnums::WindowID p_window = DisplayServerEnums::MAIN_WINDOW_ID) const override;
|
||||
|
||||
void resize_window(CGSize size);
|
||||
virtual void swap_buffers() override {}
|
||||
|
||||
virtual void set_native_icon(const String &p_filename) override;
|
||||
virtual void set_icon(const Ref<Image> &p_icon) override;
|
||||
};
|
||||
931
engine/drivers/apple_embedded/display_server_apple_embedded.mm
Normal file
931
engine/drivers/apple_embedded/display_server_apple_embedded.mm
Normal file
|
|
@ -0,0 +1,931 @@
|
|||
/**************************************************************************/
|
||||
/* display_server_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "apple_embedded.h"
|
||||
#import "godot_keyboard_input_view.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_controller.h"
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
#import "tts_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/os/os.h"
|
||||
#include "servers/display/native_menu.h"
|
||||
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
static const float kDisplayServerIOSAcceleration = 1.f;
|
||||
|
||||
DisplayServerAppleEmbedded *DisplayServerAppleEmbedded::get_singleton() {
|
||||
return (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServerEnums::WindowMode p_mode, DisplayServerEnums::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, DisplayServerEnums::Context p_context, int64_t p_parent_window, Error &r_error) {
|
||||
KeyMappingAppleEmbedded::initialize();
|
||||
|
||||
rendering_driver = p_rendering_driver;
|
||||
|
||||
// Init TTS
|
||||
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
|
||||
if (tts_enabled) {
|
||||
initialize_tts();
|
||||
}
|
||||
native_menu = memnew(NativeMenu);
|
||||
|
||||
bool has_made_render_compositor_current = false;
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
rendering_context = nullptr;
|
||||
rendering_device = nullptr;
|
||||
|
||||
CALayer *layer = nullptr;
|
||||
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanAppleEmbedded::WindowPlatformData vulkan;
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
// Eliminate "RenderingContextDriverMetal is only available on iOS 14.0 or newer".
|
||||
RenderingContextDriverMetal::WindowPlatformData metal;
|
||||
GODOT_CLANG_WARNING_POP
|
||||
#endif
|
||||
} wpd;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
if (rendering_driver == "vulkan") {
|
||||
layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"vulkan"];
|
||||
if (!layer) {
|
||||
ERR_FAIL_MSG("Failed to create iOS Vulkan rendering layer.");
|
||||
}
|
||||
wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
|
||||
rendering_context = memnew(RenderingContextDriverVulkanAppleEmbedded);
|
||||
}
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"metal"];
|
||||
wpd.metal.layer = (__bridge CA::MetalLayer *)layer;
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
} else {
|
||||
OS::get_singleton()->alert("Metal is only supported on iOS 14.0 and later.");
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
#if defined(GLES3_ENABLED)
|
||||
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
|
||||
if (fallback_to_opengl3 && rendering_driver != "opengl3") {
|
||||
WARN_PRINT("Your device does not seem to support MoltenVK or Metal, switching to OpenGL 3.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_method("gl_compatibility", OS::RENDERING_SOURCE_FALLBACK);
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver, OS::RENDERING_SOURCE_FALLBACK);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->window_create(DisplayServerEnums::MAIN_WINDOW_ID, &wpd) != OK) {
|
||||
ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale();
|
||||
rendering_context->window_set_size(DisplayServerEnums::MAIN_WINDOW_ID, size.width, size.height);
|
||||
rendering_context->window_set_vsync_mode(DisplayServerEnums::MAIN_WINDOW_ID, p_vsync_mode);
|
||||
|
||||
rendering_device = memnew(RenderingDevice);
|
||||
if (rendering_device->initialize(rendering_context, DisplayServerEnums::MAIN_WINDOW_ID) != OK) {
|
||||
rendering_device = nullptr;
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
rendering_device->screen_create(DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
|
||||
RendererCompositorRD::make_current();
|
||||
has_made_render_compositor_current = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3") {
|
||||
CALayer *layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"opengl3"];
|
||||
|
||||
if (!layer) {
|
||||
ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer.");
|
||||
}
|
||||
|
||||
RasterizerGLES3::make_current(false);
|
||||
has_made_render_compositor_current = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ERR_FAIL_COND_MSG(!has_made_render_compositor_current, vformat("Failed to make RendererCompositor current for rendering driver %s", rendering_driver));
|
||||
|
||||
bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
|
||||
screen_set_keep_on(keep_screen_on);
|
||||
|
||||
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
|
||||
|
||||
r_error = OK;
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::~DisplayServerAppleEmbedded() {
|
||||
if (native_menu) {
|
||||
memdelete(native_menu);
|
||||
native_menu = nullptr;
|
||||
}
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device) {
|
||||
rendering_device->screen_free(DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
memdelete(rendering_device);
|
||||
rendering_device = nullptr;
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
rendering_context->window_destroy(DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector<String> DisplayServerAppleEmbedded::get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
if (@available(ios 14.0, *)) {
|
||||
drivers.push_back("metal");
|
||||
}
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
drivers.push_back("opengl3");
|
||||
#endif
|
||||
|
||||
return drivers;
|
||||
}
|
||||
|
||||
// MARK: Events
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_rect_changed_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window) {
|
||||
window_resize_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_window_event_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window) {
|
||||
window_event_callback = p_callable;
|
||||
}
|
||||
void DisplayServerAppleEmbedded::window_set_input_event_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window) {
|
||||
input_event_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_input_text_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window) {
|
||||
input_text_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_drop_files_callback(const Callable &p_callable, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::process_events() {
|
||||
Input::get_singleton()->flush_buffered_events();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::_dispatch_input_events(const Ref<InputEvent> &p_event) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_input_event(p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_input_event(const Ref<InputEvent> &p_event) const {
|
||||
_window_callback(input_event_callback, p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_input_text(const String &p_text) const {
|
||||
_window_callback(input_text_callback, p_text);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_window_event(DisplayServerEnums::WindowEvent p_event) const {
|
||||
_window_callback(window_event_callback, int(p_event));
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
|
||||
if (p_callable.is_valid()) {
|
||||
p_callable.call(p_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches
|
||||
|
||||
void DisplayServerAppleEmbedded::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instantiate();
|
||||
|
||||
ev->set_index(p_idx);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_position(Vector2(p_x, p_y));
|
||||
ev->set_double_tap(p_double_click);
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt) {
|
||||
Ref<InputEventScreenDrag> ev;
|
||||
ev.instantiate();
|
||||
ev->set_index(p_idx);
|
||||
ev->set_pressure(p_pressure);
|
||||
ev->set_tilt(p_tilt);
|
||||
ev->set_position(Vector2(p_x, p_y));
|
||||
ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
|
||||
ev->set_relative_screen_position(ev->get_relative());
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::perform_event(const Ref<InputEvent> &p_event) {
|
||||
Input *input_singleton = Input::get_singleton();
|
||||
if (input_singleton == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_singleton->parse_input_event(p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::touches_canceled(int p_idx) {
|
||||
touch_press(p_idx, -1, -1, false, false);
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
void DisplayServerAppleEmbedded::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location) {
|
||||
Ref<InputEventKey> ev;
|
||||
ev.instantiate();
|
||||
ev->set_echo(false);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_keycode(fix_keycode(p_char, p_key));
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (p_key != Key::SHIFT) {
|
||||
ev->set_shift_pressed(p_modifier & UIKeyModifierShift);
|
||||
}
|
||||
if (p_key != Key::CTRL) {
|
||||
ev->set_ctrl_pressed(p_modifier & UIKeyModifierControl);
|
||||
}
|
||||
if (p_key != Key::ALT) {
|
||||
ev->set_alt_pressed(p_modifier & UIKeyModifierAlternate);
|
||||
}
|
||||
if (p_key != Key::META) {
|
||||
ev->set_meta_pressed(p_modifier & UIKeyModifierCommand);
|
||||
}
|
||||
}
|
||||
ev->set_key_label(p_unshifted);
|
||||
ev->set_physical_keycode(p_physical);
|
||||
ev->set_unicode(fix_unicode(p_char));
|
||||
ev->set_location(p_location);
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
void DisplayServerAppleEmbedded::update_gravity(const Vector3 &p_gravity) {
|
||||
Input::get_singleton()->set_gravity(p_gravity);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_accelerometer(const Vector3 &p_accelerometer) {
|
||||
Input::get_singleton()->set_accelerometer(p_accelerometer / kDisplayServerIOSAcceleration);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_magnetometer(const Vector3 &p_magnetometer) {
|
||||
Input::get_singleton()->set_magnetometer(p_magnetometer);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_gyroscope(const Vector3 &p_gyroscope) {
|
||||
Input::get_singleton()->set_gyroscope(p_gyroscope);
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
bool DisplayServerAppleEmbedded::has_feature(DisplayServerEnums::Feature p_feature) const {
|
||||
switch (p_feature) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
case DisplayServerEnums::FEATURE_GLOBAL_MENU: {
|
||||
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
|
||||
} break;
|
||||
#endif
|
||||
// case DisplayServerEnums::FEATURE_CURSOR_SHAPE:
|
||||
// case DisplayServerEnums::FEATURE_CUSTOM_CURSOR_SHAPE:
|
||||
// case DisplayServerEnums::FEATURE_HIDPI:
|
||||
// case DisplayServerEnums::FEATURE_ICON:
|
||||
// case DisplayServerEnums::FEATURE_IME:
|
||||
// case DisplayServerEnums::FEATURE_MOUSE:
|
||||
// case DisplayServerEnums::FEATURE_MOUSE_WARP:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_DIALOG:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_DIALOG_INPUT:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_DIALOG_FILE:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_DIALOG_FILE_MIME:
|
||||
// case DisplayServerEnums::FEATURE_NATIVE_ICON:
|
||||
// case DisplayServerEnums::FEATURE_WINDOW_TRANSPARENCY:
|
||||
case DisplayServerEnums::FEATURE_CLIPBOARD:
|
||||
case DisplayServerEnums::FEATURE_HDR_OUTPUT:
|
||||
case DisplayServerEnums::FEATURE_KEEP_SCREEN_ON:
|
||||
case DisplayServerEnums::FEATURE_ORIENTATION:
|
||||
case DisplayServerEnums::FEATURE_TOUCHSCREEN:
|
||||
case DisplayServerEnums::FEATURE_VIRTUAL_KEYBOARD:
|
||||
case DisplayServerEnums::FEATURE_TEXT_TO_SPEECH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::initialize_tts() const {
|
||||
const_cast<DisplayServerAppleEmbedded *>(this)->tts = [[GDTTTS alloc] init];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::tts_is_speaking() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::tts_is_paused() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isPaused];
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> DisplayServerAppleEmbedded::tts_get_voices() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
|
||||
return [tts getVoices];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int64_t p_utterance_id, bool p_interrupt) {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_pause() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts pauseSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_resume() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts resumeSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_stop() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts stopSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_dark_mode_supported() const {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_dark_mode() const {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::set_system_theme_change_callback(const Callable &p_callable) {
|
||||
system_theme_changed = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::emit_system_theme_changed() {
|
||||
if (system_theme_changed.is_valid()) {
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
system_theme_changed.callp(nullptr, 0, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAppleEmbedded::get_display_safe_area() const {
|
||||
UIEdgeInsets insets = UIEdgeInsetsZero;
|
||||
UIView *view = GDTAppDelegateService.viewController.godotView;
|
||||
if ([view respondsToSelector:@selector(safeAreaInsets)]) {
|
||||
insets = [view safeAreaInsets];
|
||||
}
|
||||
float scale = screen_get_scale();
|
||||
Size2i insets_position = Size2i(insets.left, insets.top) * scale;
|
||||
Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
|
||||
return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size);
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::get_screen_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::get_primary_screen() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::screen_get_position(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
|
||||
|
||||
return Point2i(0, 0);
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::screen_get_size(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
|
||||
|
||||
CALayer *layer = GDTAppDelegateService.viewController.godotView.renderingLayer;
|
||||
|
||||
if (!layer) {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAppleEmbedded::screen_get_usable_rect(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
|
||||
|
||||
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
|
||||
}
|
||||
|
||||
Vector<DisplayServerEnums::WindowID> DisplayServerAppleEmbedded::get_window_list() const {
|
||||
Vector<DisplayServerEnums::WindowID> list;
|
||||
list.push_back(DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
return list;
|
||||
}
|
||||
|
||||
DisplayServerEnums::WindowID DisplayServerAppleEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
|
||||
return DisplayServerEnums::MAIN_WINDOW_ID;
|
||||
}
|
||||
|
||||
int64_t DisplayServerAppleEmbedded::window_get_native_handle(DisplayServerEnums::HandleType p_handle_type, DisplayServerEnums::WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != DisplayServerEnums::MAIN_WINDOW_ID, 0);
|
||||
switch (p_handle_type) {
|
||||
case DisplayServerEnums::DISPLAY_HANDLE: {
|
||||
return 0; // Not supported.
|
||||
}
|
||||
case DisplayServerEnums::WINDOW_HANDLE: {
|
||||
return (int64_t)GDTAppDelegateService.viewController;
|
||||
}
|
||||
case DisplayServerEnums::WINDOW_VIEW: {
|
||||
return (int64_t)GDTAppDelegateService.viewController.godotView;
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_attach_instance_id(ObjectID p_instance, DisplayServerEnums::WindowID p_window) {
|
||||
window_attached_instance_id = p_instance;
|
||||
}
|
||||
|
||||
ObjectID DisplayServerAppleEmbedded::window_get_attached_instance_id(DisplayServerEnums::WindowID p_window) const {
|
||||
return window_attached_instance_id;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_title(const String &p_title, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::window_get_current_screen(DisplayServerEnums::WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != DisplayServerEnums::MAIN_WINDOW_ID, DisplayServerEnums::INVALID_SCREEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_current_screen(int p_screen, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::window_get_position(DisplayServerEnums::WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::window_get_position_with_decorations(DisplayServerEnums::WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_position(const Point2i &p_position, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for single window iOS app
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_transient(DisplayServerEnums::WindowID p_window, DisplayServerEnums::WindowID p_parent) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_max_size(const Size2i p_size, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_max_size(DisplayServerEnums::WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_min_size(const Size2i p_size, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_min_size(DisplayServerEnums::WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_size(const Size2i p_size, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_size(DisplayServerEnums::WindowID p_window) const {
|
||||
CGRect viewBounds = GDTAppDelegateService.viewController.view.bounds;
|
||||
return Size2i(viewBounds.size.width, viewBounds.size.height) * screen_get_max_scale();
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_size_with_decorations(DisplayServerEnums::WindowID p_window) const {
|
||||
return window_get_size(p_window);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_mode(DisplayServerEnums::WindowMode p_mode, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
DisplayServerEnums::WindowMode DisplayServerAppleEmbedded::window_get_mode(DisplayServerEnums::WindowID p_window) const {
|
||||
return DisplayServerEnums::WindowMode::WINDOW_MODE_FULLSCREEN;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_maximize_allowed(DisplayServerEnums::WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_flag(DisplayServerEnums::WindowFlags p_flag, bool p_enabled, DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_get_flag(DisplayServerEnums::WindowFlags p_flag, DisplayServerEnums::WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_request_attention(DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_move_to_foreground(DisplayServerEnums::WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_focused(DisplayServerEnums::WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::screen_get_max_scale() const {
|
||||
return screen_get_scale(DisplayServerEnums::SCREEN_OF_MAIN_WINDOW);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::screen_set_orientation(DisplayServerEnums::ScreenOrientation p_orientation, int p_screen) {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX(p_screen, screen_count);
|
||||
|
||||
screen_orientation = p_orientation;
|
||||
if (@available(iOS 16.0, *)) {
|
||||
[GDTAppDelegateService.viewController setNeedsUpdateOfSupportedInterfaceOrientations];
|
||||
}
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
else {
|
||||
[UIViewController attemptRotationToDeviceOrientation];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServerEnums::ScreenOrientation DisplayServerAppleEmbedded::screen_get_orientation(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, DisplayServerEnums::SCREEN_LANDSCAPE);
|
||||
|
||||
return screen_orientation;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_can_draw(DisplayServerEnums::WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::can_any_window_draw() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_touchscreen_available() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text, int p_pos) {
|
||||
int limit = p_pos;
|
||||
for (int i = 0; i < MIN(p_existing_text.length(), p_pos); i++) {
|
||||
if (p_existing_text[i] > 0xffff) {
|
||||
limit++;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, DisplayServerEnums::VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) {
|
||||
NSString *existingString = [[NSString alloc] initWithUTF8String:p_existing_text.utf8().get_data()];
|
||||
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = nil;
|
||||
switch (p_type) {
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_DEFAULT: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_MULTILINE: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_NUMBER: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeNumberPad;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_NUMBER_DECIMAL: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDecimalPad;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_PHONE: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypePhonePad;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeTelephoneNumber;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_EMAIL_ADDRESS: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeEmailAddress;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_PASSWORD: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypePassword;
|
||||
} break;
|
||||
case DisplayServerEnums::KEYBOARD_TYPE_URL: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeWebSearch;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeURL;
|
||||
} break;
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.keyboardView
|
||||
becomeFirstResponderWithString:existingString
|
||||
cursorStart:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_start)
|
||||
cursorEnd:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_end)];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_keyboard_active() const {
|
||||
return [GDTAppDelegateService.viewController.keyboardView isFirstResponder];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_hide() {
|
||||
[GDTAppDelegateService.viewController.keyboardView resignFirstResponder];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_set_height(int height) {
|
||||
virtual_keyboard_height = height * screen_get_max_scale();
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::virtual_keyboard_get_height() const {
|
||||
return virtual_keyboard_height;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::has_hardware_keyboard() const {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
return [GCKeyboard coalescedKeyboard];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::clipboard_set(const String &p_text) {
|
||||
[UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8().get_data()];
|
||||
}
|
||||
|
||||
String DisplayServerAppleEmbedded::clipboard_get() const {
|
||||
NSString *text = [UIPasteboard generalPasteboard].string;
|
||||
|
||||
return String::utf8([text UTF8String]);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::screen_set_keep_on(bool p_enable) {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = p_enable;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::screen_is_kept_on() const {
|
||||
return [UIApplication sharedApplication].idleTimerDisabled;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::resize_window(CGSize viewSize) {
|
||||
Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_size(DisplayServerEnums::MAIN_WINDOW_ID, size.x, size.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
Variant resize_rect = Rect2i(Point2i(), size);
|
||||
_window_callback(window_resize_callback, resize_rect);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_vsync_mode(DisplayServerEnums::VSyncMode p_vsync_mode, DisplayServerEnums::WindowID p_window) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServerEnums::VSyncMode DisplayServerAppleEmbedded::window_get_vsync_mode(DisplayServerEnums::WindowID p_window) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
#endif
|
||||
return DisplayServerEnums::VSYNC_ENABLED;
|
||||
}
|
||||
|
||||
// MARK: - HDR / EDR
|
||||
|
||||
void DisplayServerAppleEmbedded::_update_hdr_output() {
|
||||
#ifdef RD_ENABLED
|
||||
if (!rendering_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool desired = edr_requested && _screen_hdr_is_supported();
|
||||
if (rendering_context->window_get_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID) != desired) {
|
||||
rendering_context->window_set_hdr_output_enabled(DisplayServerEnums::MAIN_WINDOW_ID, desired);
|
||||
}
|
||||
|
||||
float reference_luminance = _calculate_current_reference_luminance();
|
||||
rendering_context->window_set_hdr_output_reference_luminance(DisplayServerEnums::MAIN_WINDOW_ID, reference_luminance);
|
||||
|
||||
float max_luminance = _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
|
||||
rendering_context->window_set_hdr_output_max_luminance(DisplayServerEnums::MAIN_WINDOW_ID, max_luminance);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::current_edr_headroom_changed() {
|
||||
_update_hdr_output();
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_hdr_output_supported(DisplayServerEnums::WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return _screen_hdr_is_supported();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_request_hdr_output(const bool p_enabled, DisplayServerEnums::WindowID p_window) {
|
||||
#if defined(RD_ENABLED)
|
||||
ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device.");
|
||||
#endif
|
||||
|
||||
edr_requested = p_enabled;
|
||||
_update_hdr_output();
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_hdr_output_requested(DisplayServerEnums::WindowID p_window) const {
|
||||
return edr_requested;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_hdr_output_enabled(DisplayServerEnums::WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_hdr_output_enabled(p_window);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_hdr_output_reference_luminance(const float p_reference_luminance, DisplayServerEnums::WindowID p_window) {
|
||||
ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance.");
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::window_get_hdr_output_reference_luminance(DisplayServerEnums::WindowID p_window) const {
|
||||
return -1.0f; // Always auto-adjusted by the OS on Apple platforms.
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::_calculate_current_reference_luminance() const {
|
||||
float potential = _screen_potential_edr_headroom();
|
||||
float current = _screen_current_edr_headroom();
|
||||
return potential * hardware_reference_luminance_nits / current;
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::window_get_hdr_output_current_reference_luminance(DisplayServerEnums::WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_hdr_output_reference_luminance(p_window);
|
||||
}
|
||||
#endif
|
||||
return 200.0f;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_hdr_output_max_luminance(const float p_max_luminance, DisplayServerEnums::WindowID p_window) {
|
||||
ERR_PRINT_ONCE("Manually setting max luminance is not supported on Apple embedded devices as they provide accurate max luminance values for their built-in screens.");
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::window_get_hdr_output_max_luminance(DisplayServerEnums::WindowID p_window) const {
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::window_get_hdr_output_current_max_luminance(DisplayServerEnums::WindowID p_window) const {
|
||||
return _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::window_get_output_max_linear_value(DisplayServerEnums::WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_output_max_linear_value(p_window);
|
||||
}
|
||||
#endif
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::set_native_icon(const String &p_filename) {
|
||||
// Not supported on Apple embedded platforms.
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::set_icon(const Ref<Image> &p_icon) {
|
||||
// Not supported on Apple embedded platforms.
|
||||
}
|
||||
43
engine/drivers/apple_embedded/godot_app_delegate.h
Normal file
43
engine/drivers/apple_embedded/godot_app_delegate.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**************************************************************************/
|
||||
/* godot_app_delegate.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NSObject<UIApplicationDelegate, UIWindowSceneDelegate> GDTAppDelegateServiceProtocol;
|
||||
|
||||
@interface GDTApplicationDelegate : NSObject <UIApplicationDelegate, UIWindowSceneDelegate>
|
||||
|
||||
@property(class, readonly, strong) NSArray<GDTAppDelegateServiceProtocol *> *services;
|
||||
|
||||
+ (void)addService:(GDTAppDelegateServiceProtocol *)service;
|
||||
|
||||
@end
|
||||
520
engine/drivers/apple_embedded/godot_app_delegate.mm
Normal file
520
engine/drivers/apple_embedded/godot_app_delegate.mm
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
/**************************************************************************/
|
||||
/* godot_app_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_app_delegate.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
@implementation GDTApplicationDelegate
|
||||
|
||||
static NSMutableArray<GDTAppDelegateServiceProtocol *> *services = nil;
|
||||
|
||||
+ (NSArray<GDTAppDelegateServiceProtocol *> *)services {
|
||||
return services;
|
||||
}
|
||||
|
||||
+ (void)load {
|
||||
services = [NSMutableArray new];
|
||||
[services addObject:[GDTAppDelegateService new]];
|
||||
}
|
||||
|
||||
+ (void)addService:(GDTAppDelegateServiceProtocol *)service {
|
||||
if (!services || !service) {
|
||||
return;
|
||||
}
|
||||
[services addObject:service];
|
||||
}
|
||||
|
||||
// UIApplicationDelegate documentation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
|
||||
|
||||
// MARK: Window
|
||||
|
||||
- (UIWindow *)window {
|
||||
UIWindow *result = nil;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIWindow *value = [service window];
|
||||
|
||||
if (value) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MARK: Initializing
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application willFinishLaunchingWithOptions:launchOptions]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application didFinishLaunchingWithOptions:launchOptions]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MARK: Scene
|
||||
|
||||
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
UISceneConfiguration *config = [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
|
||||
config.delegateClass = [GDTApplicationDelegate class];
|
||||
return config;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
}
|
||||
|
||||
// MARK: Life-Cycle
|
||||
|
||||
- (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service sceneDidDisconnect:scene];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service sceneDidBecomeActive:scene];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service sceneWillResignActive:scene];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service sceneDidEnterBackground:scene];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0), visionos(1.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service sceneWillEnterForeground:scene];
|
||||
}
|
||||
}
|
||||
|
||||
// UIApplication lifecycle has become deprecated in favor of UIScene lifecycle
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations")
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidBecomeActive:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillResignActive:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidEnterBackground:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillEnterForeground:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillTerminate:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Environment Changes
|
||||
|
||||
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationProtectedDataDidBecomeAvailable:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationProtectedDataWillBecomeUnavailable:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidReceiveMemoryWarning:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationSignificantTimeChange:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationSignificantTimeChange:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: App State Restoration
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldSaveSecureApplicationState:coder]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldRestoreSecureApplicationState:coder]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIViewController *controller = [service application:application viewControllerWithRestorationIdentifierPath:identifierComponents coder:coder];
|
||||
|
||||
if (controller) {
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application willEncodeRestorableStateWithCoder:coder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didDecodeRestorableStateWithCoder:coder];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Download Data in Background
|
||||
|
||||
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
// MARK: Remote Notification
|
||||
|
||||
// Moved to the iOS Plugin
|
||||
|
||||
// MARK: User Activity and Handling Quick Actions
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application willContinueUserActivityWithType:userActivityType]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application continueUserActivity:userActivity restorationHandler:restorationHandler]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didUpdateUserActivity:userActivity];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didFailToContinueUserActivityWithType:userActivityType error:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application performActionForShortcutItem:shortcutItem completionHandler:completionHandler];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: WatchKit
|
||||
|
||||
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application handleWatchKitExtensionRequest:userInfo reply:reply];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: HealthKit
|
||||
|
||||
- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationShouldRequestHealthAuthorization:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Opening an URL
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:app openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// MARK: Disallowing Specified App Extension Types
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldAllowExtensionPointIdentifier:extensionPointIdentifier]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MARK: SiriKit
|
||||
|
||||
- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
id result = [service application:application handlerForIntent:intent];
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// MARK: CloudKit
|
||||
|
||||
- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application userDidAcceptCloudKitShareWithMetadata:cloudKitShareMetadata];
|
||||
}
|
||||
}
|
||||
|
||||
/* Handled By Info.plist file for now
|
||||
|
||||
// MARK: Interface Geometry
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {}
|
||||
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
39
engine/drivers/apple_embedded/godot_keyboard_input_view.h
Normal file
39
engine/drivers/apple_embedded/godot_keyboard_input_view.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**************************************************************************/
|
||||
/* godot_keyboard_input_view.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GDTKeyboardInputView : UITextView
|
||||
|
||||
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;
|
||||
|
||||
@end
|
||||
201
engine/drivers/apple_embedded/godot_keyboard_input_view.mm
Normal file
201
engine/drivers/apple_embedded/godot_keyboard_input_view.mm
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/**************************************************************************/
|
||||
/* godot_keyboard_input_view.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_keyboard_input_view.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
@interface GDTKeyboardInputView () <UITextViewDelegate>
|
||||
|
||||
@property(nonatomic, copy) NSString *previousText;
|
||||
@property(nonatomic, assign) NSRange previousSelectedRange;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTKeyboardInputView
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer {
|
||||
self = [super initWithFrame:frame textContainer:textContainer];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.hidden = YES;
|
||||
self.delegate = self;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(observeTextChange:)
|
||||
name:UITextViewTextDidChangeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.delegate = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
- (BOOL)canBecomeFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end {
|
||||
self.text = existingString;
|
||||
self.previousText = existingString;
|
||||
|
||||
NSInteger safeStartIndex = MAX(start, 0);
|
||||
|
||||
NSRange textRange;
|
||||
|
||||
// Either a simple cursor or a selection.
|
||||
if (end > 0) {
|
||||
textRange = NSMakeRange(safeStartIndex, end - start);
|
||||
} else {
|
||||
textRange = NSMakeRange(safeStartIndex, 0);
|
||||
}
|
||||
|
||||
self.selectedRange = textRange;
|
||||
self.previousSelectedRange = textRange;
|
||||
|
||||
return [self becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder {
|
||||
self.text = nil;
|
||||
self.previousText = nil;
|
||||
return [super resignFirstResponder];
|
||||
}
|
||||
|
||||
// MARK: OS Messages
|
||||
|
||||
- (void)deleteText:(NSInteger)charactersToDelete {
|
||||
for (int i = 0; i < charactersToDelete; i++) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enterText:(NSString *)substring {
|
||||
String characters = String::utf8([substring UTF8String]);
|
||||
|
||||
for (int i = 0; i < characters.size(); i++) {
|
||||
int character = characters[i];
|
||||
Key key = Key::NONE;
|
||||
|
||||
if (character == '\t') { // 0x09
|
||||
key = Key::TAB;
|
||||
} else if (character == '\n') { // 0x0A
|
||||
key = Key::ENTER;
|
||||
} else if (character == 0x2006) {
|
||||
key = Key::SPACE;
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Observer
|
||||
|
||||
- (void)observeTextChange:(NSNotification *)notification {
|
||||
if (notification.object != self) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *substringToDelete = nil;
|
||||
if (self.previousSelectedRange.length == 0) {
|
||||
// Get previous text to delete.
|
||||
substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location];
|
||||
} else {
|
||||
// If text was previously selected we are sending only one `backspace`. It will remove all text from text input.
|
||||
[self deleteText:1];
|
||||
}
|
||||
|
||||
NSString *substringToEnter = nil;
|
||||
if (self.selectedRange.length == 0) {
|
||||
// If previous cursor had a selection we have to calculate an inserted text.
|
||||
if (self.previousSelectedRange.length != 0) {
|
||||
NSInteger rangeEnd = self.selectedRange.location + self.selectedRange.length;
|
||||
NSInteger rangeStart = MIN(self.previousSelectedRange.location, self.selectedRange.location);
|
||||
NSInteger rangeLength = MAX(0, rangeEnd - rangeStart);
|
||||
|
||||
NSRange calculatedRange;
|
||||
|
||||
if (rangeLength >= 0) {
|
||||
calculatedRange = NSMakeRange(rangeStart, rangeLength);
|
||||
} else {
|
||||
calculatedRange = NSMakeRange(rangeStart, 0);
|
||||
}
|
||||
|
||||
substringToEnter = [self.text substringWithRange:calculatedRange];
|
||||
} else {
|
||||
substringToEnter = [self.text substringToIndex:self.selectedRange.location];
|
||||
}
|
||||
} else {
|
||||
substringToEnter = [self.text substringWithRange:self.selectedRange];
|
||||
}
|
||||
|
||||
NSInteger skip = 0;
|
||||
if (substringToDelete != nil) {
|
||||
for (NSUInteger i = 0; i < MIN([substringToDelete length], [substringToEnter length]); i++) {
|
||||
if ([substringToDelete characterAtIndex:i] == [substringToEnter characterAtIndex:i]) {
|
||||
skip++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
[self deleteText:[substringToDelete length] - skip]; // Delete changed part of previous text.
|
||||
}
|
||||
[self enterText:[substringToEnter substringFromIndex:skip]]; // Enter changed part of new text.
|
||||
|
||||
self.previousText = self.text;
|
||||
self.previousSelectedRange = self.selectedRange;
|
||||
}
|
||||
|
||||
@end
|
||||
72
engine/drivers/apple_embedded/godot_view_apple_embedded.h
Normal file
72
engine/drivers/apple_embedded/godot_view_apple_embedded.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
class String;
|
||||
|
||||
@class GDTView;
|
||||
@protocol GDTDisplayLayer;
|
||||
@protocol GDTViewRendererProtocol;
|
||||
|
||||
@protocol GDTViewDelegate
|
||||
|
||||
- (BOOL)godotViewFinishedSetup:(GDTView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@interface GDTView : UIView
|
||||
|
||||
@property(assign, nonatomic) id<GDTViewRendererProtocol> renderer;
|
||||
@property(assign, nonatomic) id<GDTViewDelegate> delegate;
|
||||
|
||||
@property(assign, readonly, nonatomic) BOOL isActive;
|
||||
|
||||
@property(assign, nonatomic) BOOL useCADisplayLink;
|
||||
@property(strong, readonly, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
|
||||
@property(assign, readonly, nonatomic) BOOL canRender;
|
||||
|
||||
@property(assign, nonatomic) float preferredFrameRate;
|
||||
|
||||
// Can be extended by subclasses
|
||||
- (void)godot_commonInit;
|
||||
|
||||
// Implemented in subclasses
|
||||
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
|
||||
|
||||
- (void)startRendering;
|
||||
- (void)stopRendering;
|
||||
|
||||
@end
|
||||
|
||||
// Implemented in subclasses
|
||||
extern GDTView *GDTViewCreate();
|
||||
471
engine/drivers/apple_embedded/godot_view_apple_embedded.mm
Normal file
471
engine/drivers/apple_embedded/godot_view_apple_embedded.mm
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_view_apple_embedded.h"
|
||||
|
||||
#import "display_layer_apple_embedded.h"
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_view_renderer.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
static const int max_touches = 32;
|
||||
static const float earth_gravity = 9.80665;
|
||||
|
||||
@interface GDTView () {
|
||||
UITouch *godot_touches[max_touches];
|
||||
CGFloat last_edr_headroom;
|
||||
}
|
||||
|
||||
@property(assign, nonatomic) BOOL isActive;
|
||||
|
||||
// CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
|
||||
@property(strong, nonatomic) CADisplayLink *displayLink;
|
||||
|
||||
// An animation timer that, when animation is started, will periodically call -drawView at the given rate.
|
||||
// Only used if CADisplayLink is not
|
||||
@property(strong, nonatomic) NSTimer *animationTimer;
|
||||
|
||||
@property(strong, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
|
||||
|
||||
@property(strong, nonatomic) CMMotionManager *motionManager;
|
||||
|
||||
@property(assign, nonatomic) BOOL delegateDidFinishSetUp;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTView
|
||||
|
||||
// Implemented in subclasses
|
||||
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopRendering];
|
||||
|
||||
self.renderer = nil;
|
||||
self.delegate = nil;
|
||||
|
||||
if (self.renderingLayer) {
|
||||
[self.renderingLayer removeFromSuperlayer];
|
||||
self.renderingLayer = nil;
|
||||
}
|
||||
|
||||
if (self.motionManager) {
|
||||
[self.motionManager stopDeviceMotionUpdates];
|
||||
self.motionManager = nil;
|
||||
}
|
||||
|
||||
if (self.displayLink) {
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
}
|
||||
|
||||
if (self.animationTimer) {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.preferredFrameRate = 60;
|
||||
self.useCADisplayLink = bool(GLOBAL_DEF("display.AppleEmbedded/use_cadisplaylink", true)) ? YES : NO;
|
||||
last_edr_headroom = 0.0;
|
||||
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
self.contentScaleFactor = [UIScreen mainScreen].scale;
|
||||
#endif
|
||||
|
||||
if (@available(iOS 17.0, *)) {
|
||||
[self registerForTraitChanges:@[ [UITraitUserInterfaceStyle class] ] withTarget:self action:@selector(traitCollectionDidChangeWithView:previousTraitCollection:)];
|
||||
}
|
||||
|
||||
[self initTouches];
|
||||
|
||||
self.multipleTouchEnabled = YES;
|
||||
|
||||
// Configure and start accelerometer
|
||||
if (!self.motionManager) {
|
||||
self.motionManager = [[CMMotionManager alloc] init];
|
||||
if (self.motionManager.deviceMotionAvailable) {
|
||||
self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
|
||||
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
|
||||
} else {
|
||||
self.motionManager = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)system_theme_changed {
|
||||
DisplayServerAppleEmbedded *ds = (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
ds->emit_system_theme_changed();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
#endif
|
||||
[self traitCollectionDidChangeWithView:self
|
||||
previousTraitCollection:previousTraitCollection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChangeWithView:(UIView *)view previousTraitCollection:(UITraitCollection *)previousTraitCollection {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if ([UITraitCollection currentTraitCollection].userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
|
||||
[self system_theme_changed];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopRendering {
|
||||
if (!self.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isActive = NO;
|
||||
|
||||
print_verbose("Stop animation!");
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
} else {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
}
|
||||
|
||||
[self clearTouches];
|
||||
}
|
||||
|
||||
- (void)startRendering {
|
||||
if (self.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isActive = YES;
|
||||
|
||||
print_verbose("Start animation!");
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
|
||||
self.displayLink.preferredFramesPerSecond = self.preferredFrameRate;
|
||||
|
||||
// Setup DisplayLink in main thread
|
||||
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||
} else {
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / self.preferredFrameRate) target:self selector:@selector(drawView) userInfo:nil repeats:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawView {
|
||||
if (!self.isActive) {
|
||||
print_verbose("Draw view not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
// Pause the CADisplayLink to avoid recursion
|
||||
[self.displayLink setPaused:YES];
|
||||
|
||||
// Process all input events
|
||||
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource) {
|
||||
// Continue.
|
||||
}
|
||||
|
||||
// We are good to go, resume the CADisplayLink
|
||||
[self.displayLink setPaused:NO];
|
||||
}
|
||||
|
||||
[self.renderingLayer startRenderDisplayLayer];
|
||||
|
||||
if (!self.renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self.renderer setupView:self]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.delegate && !self.delegateDidFinishSetUp) {
|
||||
[self layoutRenderingLayer]; // Trigger DisplayServerVisionOS::resize_window after Main::start()
|
||||
self.delegateDidFinishSetUp = [self.delegate godotViewFinishedSetup:self];
|
||||
if (!_delegateDidFinishSetUp) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self handleMotion];
|
||||
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
if (@available(iOS 16.0, *)) {
|
||||
CGFloat edr_headroom = UIScreen.mainScreen.currentEDRHeadroom;
|
||||
if (last_edr_headroom != edr_headroom) {
|
||||
last_edr_headroom = edr_headroom;
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->current_edr_headroom_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
[self.renderer renderOnView:self];
|
||||
|
||||
[self.renderingLayer stopRenderDisplayLayer];
|
||||
}
|
||||
|
||||
- (BOOL)canRender {
|
||||
if (self.useCADisplayLink) {
|
||||
return self.displayLink != nil;
|
||||
} else {
|
||||
return self.animationTimer != nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPreferredFrameRate:(float)preferredFrameRate {
|
||||
_preferredFrameRate = preferredFrameRate;
|
||||
|
||||
if (self.canRender) {
|
||||
[self stopRendering];
|
||||
[self startRendering];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
[self layoutRenderingLayer];
|
||||
}
|
||||
|
||||
- (void)layoutRenderingLayer {
|
||||
if (self.renderingLayer) {
|
||||
self.renderingLayer.frame = self.bounds;
|
||||
[self.renderingLayer layoutDisplayLayer];
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->resize_window(self.bounds.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches
|
||||
|
||||
- (void)initTouches {
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
godot_touches[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
- (int)getTouchIDForTouch:(UITouch *)p_touch {
|
||||
int first = -1;
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
if (first == -1 && godot_touches[i] == nullptr) {
|
||||
first = i;
|
||||
continue;
|
||||
}
|
||||
if (godot_touches[i] == p_touch) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first != -1) {
|
||||
godot_touches[first] = p_touch;
|
||||
return first;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (int)removeTouch:(UITouch *)p_touch {
|
||||
int remaining = 0;
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
if (godot_touches[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (godot_touches[i] == p_touch) {
|
||||
godot_touches[i] = nullptr;
|
||||
} else {
|
||||
++remaining;
|
||||
}
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
- (void)clearTouches {
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
godot_touches[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
CGPoint prev_point = [touch previousLocationInView:self];
|
||||
CGFloat alt = [touch altitudeAngle];
|
||||
CGVector azim = [touch azimuthUnitVectorInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
[self removeTouch:touch];
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
DisplayServerAppleEmbedded::get_singleton()->touches_canceled(tid);
|
||||
}
|
||||
[self clearTouches];
|
||||
}
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
- (void)handleMotion {
|
||||
if (!self.motionManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Just using polling approach for now, we can set this up so it sends
|
||||
// data to us in intervals, might be better. See Apple reference pages
|
||||
// for more details:
|
||||
// https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
|
||||
|
||||
// Apple splits our accelerometer date into a gravity and user movement
|
||||
// component. We add them back together.
|
||||
CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
|
||||
CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration;
|
||||
|
||||
// To be consistent with Android we convert the unit of measurement from g (Earth's gravity)
|
||||
// to m/s^2.
|
||||
gravity.x *= earth_gravity;
|
||||
gravity.y *= earth_gravity;
|
||||
gravity.z *= earth_gravity;
|
||||
acceleration.x *= earth_gravity;
|
||||
acceleration.y *= earth_gravity;
|
||||
acceleration.z *= earth_gravity;
|
||||
|
||||
///@TODO We don't seem to be getting data here, is my device broken or
|
||||
/// is this code incorrect?
|
||||
CMMagneticField magnetic = self.motionManager.deviceMotion.magneticField.field;
|
||||
|
||||
///@TODO we can access rotationRate as a CMRotationRate variable
|
||||
///(processed date) or CMGyroData (raw data), have to see what works
|
||||
/// best
|
||||
CMRotationRate rotation = self.motionManager.deviceMotion.rotationRate;
|
||||
|
||||
// Adjust for screen orientation.
|
||||
// [[UIDevice currentDevice] orientation] changes even if we've fixed
|
||||
// our orientation which is not a good thing when you're trying to get
|
||||
// your user to move the screen in all directions and want consistent
|
||||
// output
|
||||
#if defined(VISIONOS_ENABLED)
|
||||
UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.effectiveGeometry.interfaceOrientation;
|
||||
#else
|
||||
UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
|
||||
#endif
|
||||
|
||||
switch (interfaceOrientation) {
|
||||
case UIInterfaceOrientationLandscapeLeft: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
} break;
|
||||
case UIInterfaceOrientationLandscapeRight: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
} break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
} break;
|
||||
default: { // assume portrait
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
43
engine/drivers/apple_embedded/godot_view_controller.h
Normal file
43
engine/drivers/apple_embedded/godot_view_controller.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_controller.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class GDTView;
|
||||
@class GDTKeyboardInputView;
|
||||
|
||||
@interface GDTViewController : UIViewController
|
||||
|
||||
@property(nonatomic, readonly, strong) GDTView *godotView;
|
||||
@property(nonatomic, readonly, strong) GDTKeyboardInputView *keyboardView;
|
||||
|
||||
@end
|
||||
361
engine/drivers/apple_embedded/godot_view_controller.mm
Normal file
361
engine/drivers/apple_embedded/godot_view_controller.mm
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_controller.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_view_controller.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_keyboard_input_view.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_renderer.h"
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "servers/camera/camera_server.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
@interface GDTViewController () <GDTViewDelegate>
|
||||
|
||||
@property(strong, nonatomic) GDTViewRenderer *renderer;
|
||||
@property(strong, nonatomic) GDTKeyboardInputView *keyboardView;
|
||||
|
||||
@property(strong, nonatomic) UIView *godotLoadingOverlay;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTViewController
|
||||
|
||||
- (GDTView *)godotView {
|
||||
return (GDTView *)self.view;
|
||||
}
|
||||
|
||||
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
|
||||
[super pressesBegan:presses withEvent:event];
|
||||
|
||||
if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UIPress *press in presses) {
|
||||
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
|
||||
String u32text = String::utf8([press.key.characters UTF8String]);
|
||||
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
|
||||
|
||||
if (press.key.keyCode == 0 && u32text.is_empty() && u32lbl.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char32_t us = 0;
|
||||
if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
|
||||
us = u32lbl[0];
|
||||
}
|
||||
|
||||
KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
|
||||
|
||||
if (!u32text.is_empty() && !u32text.begins_with("UIKey")) {
|
||||
for (int i = 0; i < u32text.length(); i++) {
|
||||
const char32_t c = u32text[i];
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
|
||||
}
|
||||
} else {
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
|
||||
[super pressesEnded:presses withEvent:event];
|
||||
|
||||
if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UIPress *press in presses) {
|
||||
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
|
||||
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
|
||||
|
||||
if (press.key.keyCode == 0 && u32lbl.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char32_t us = 0;
|
||||
if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
|
||||
us = u32lbl[0];
|
||||
}
|
||||
|
||||
KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
|
||||
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
GDTView *view = GDTViewCreate();
|
||||
GDTViewRenderer *renderer = [[GDTViewRenderer alloc] init];
|
||||
|
||||
self.renderer = renderer;
|
||||
self.view = view;
|
||||
|
||||
view.renderer = self.renderer;
|
||||
view.delegate = self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
// Initialize view controller values.
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
print_verbose("Did receive memory warning!");
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
[self observeKeyboard];
|
||||
[self displayLoadingOverlay];
|
||||
|
||||
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
[self.godotView startRendering];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated {
|
||||
[self.godotView stopRendering];
|
||||
[super viewDidDisappear:animated];
|
||||
}
|
||||
|
||||
- (void)observeKeyboard {
|
||||
print_verbose("Setting up keyboard input view.");
|
||||
self.keyboardView = [GDTKeyboardInputView new];
|
||||
[self.view addSubview:self.keyboardView];
|
||||
|
||||
print_verbose("Adding observer for keyboard show/hide.");
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(keyboardOnScreen:)
|
||||
name:UIKeyboardDidShowNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(keyboardHidden:)
|
||||
name:UIKeyboardDidHideNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)displayLoadingOverlay {
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSString *storyboardName = @"Launch Screen";
|
||||
|
||||
if ([bundle pathForResource:storyboardName ofType:@"storyboardc"] == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle];
|
||||
|
||||
UIViewController *controller = [launchStoryboard instantiateInitialViewController];
|
||||
self.godotLoadingOverlay = controller.view;
|
||||
self.godotLoadingOverlay.frame = self.view.bounds;
|
||||
self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
[self.view addSubview:self.godotLoadingOverlay];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)godotViewFinishedSetup:(GDTView *)view {
|
||||
[self.godotLoadingOverlay removeFromSuperview];
|
||||
self.godotLoadingOverlay = nil;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.keyboardView = nil;
|
||||
|
||||
self.renderer = nil;
|
||||
|
||||
if (self.godotLoadingOverlay) {
|
||||
[self.godotLoadingOverlay removeFromSuperview];
|
||||
self.godotLoadingOverlay = nil;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
// MARK: Orientation
|
||||
|
||||
#ifdef IOS_ENABLED
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
|
||||
[coordinator animateAlongsideTransition:nil
|
||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
// Get the new interface orientation after rotation completes (iOS only)
|
||||
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
|
||||
|
||||
// Notify camera server of orientation change
|
||||
CameraServer *camera_server = CameraServer::get_singleton();
|
||||
if (camera_server) {
|
||||
camera_server->handle_display_rotation_change((int)orientation);
|
||||
}
|
||||
|
||||
DisplayServer *display_server = DisplayServer::get_singleton();
|
||||
if (display_server) {
|
||||
int out = 0;
|
||||
if (UIInterfaceOrientationIsPortrait(orientation)) {
|
||||
out = 1;
|
||||
} else if (UIInterfaceOrientationIsLandscape(orientation)) {
|
||||
out = 2;
|
||||
}
|
||||
display_server->emit_signal("orientation_changed", out);
|
||||
}
|
||||
}];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
||||
if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) {
|
||||
return UIRectEdgeAll;
|
||||
} else {
|
||||
return UIRectEdgeNone;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate {
|
||||
if (!DisplayServerAppleEmbedded::get_singleton()) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServerEnums::SCREEN_OF_MAIN_WINDOW)) {
|
||||
case DisplayServerEnums::SCREEN_SENSOR:
|
||||
case DisplayServerEnums::SCREEN_SENSOR_LANDSCAPE:
|
||||
case DisplayServerEnums::SCREEN_SENSOR_PORTRAIT:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
||||
if (!DisplayServerAppleEmbedded::get_singleton()) {
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServerEnums::SCREEN_OF_MAIN_WINDOW)) {
|
||||
case DisplayServerEnums::SCREEN_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
case DisplayServerEnums::SCREEN_REVERSE_LANDSCAPE:
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
return UIInterfaceOrientationMaskLandscapeLeft;
|
||||
} else {
|
||||
return UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
case DisplayServerEnums::SCREEN_REVERSE_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
case DisplayServerEnums::SCREEN_SENSOR_LANDSCAPE:
|
||||
return UIInterfaceOrientationMaskLandscape;
|
||||
case DisplayServerEnums::SCREEN_SENSOR_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
case DisplayServerEnums::SCREEN_SENSOR:
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
case DisplayServerEnums::SCREEN_LANDSCAPE:
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
return UIInterfaceOrientationMaskLandscapeRight;
|
||||
} else {
|
||||
return UIInterfaceOrientationMaskLandscapeLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
if (GLOBAL_GET("display/window/ios/hide_status_bar")) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prefersHomeIndicatorAutoHidden {
|
||||
if (GLOBAL_GET("display/window/ios/hide_home_indicator")) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
- (void)keyboardOnScreen:(NSNotification *)notification {
|
||||
NSDictionary *info = notification.userInfo;
|
||||
NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
|
||||
|
||||
CGRect rawFrame = [value CGRectValue];
|
||||
CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyboardHidden:(NSNotification *)notification {
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(0);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
46
engine/drivers/apple_embedded/godot_view_renderer.h
Normal file
46
engine/drivers/apple_embedded/godot_view_renderer.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_renderer.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol GDTViewRendererProtocol <NSObject>
|
||||
|
||||
@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
|
||||
|
||||
- (BOOL)setupView:(UIView *)view;
|
||||
- (void)renderOnView:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@interface GDTViewRenderer : NSObject <GDTViewRendererProtocol>
|
||||
|
||||
@end
|
||||
120
engine/drivers/apple_embedded/godot_view_renderer.mm
Normal file
120
engine/drivers/apple_embedded/godot_view_renderer.mm
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**************************************************************************/
|
||||
/* godot_view_renderer.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_view_renderer.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "main/main.h"
|
||||
#include "servers/audio/audio_server.h"
|
||||
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <GameController/GameController.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GDTViewRenderer ()
|
||||
|
||||
@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
|
||||
@property(assign, nonatomic) BOOL hasStartedMain;
|
||||
@property(assign, nonatomic) BOOL hasFinishedSetup;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTViewRenderer
|
||||
|
||||
- (BOOL)setupView:(UIView *)view {
|
||||
if (self.hasFinishedSetup) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!OS::get_singleton()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!self.hasFinishedProjectDataSetup) {
|
||||
[self setupProjectData];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (!self.hasStartedMain) {
|
||||
self.hasStartedMain = YES;
|
||||
OS_AppleEmbedded::get_singleton()->start();
|
||||
return YES;
|
||||
}
|
||||
|
||||
self.hasFinishedSetup = YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setupProjectData {
|
||||
self.hasFinishedProjectDataSetup = YES;
|
||||
|
||||
Main::setup2();
|
||||
|
||||
// this might be necessary before here
|
||||
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
|
||||
for (NSString *key in dict) {
|
||||
NSObject *value = [dict objectForKey:key];
|
||||
String ukey = String::utf8([key UTF8String]);
|
||||
|
||||
// we need a NSObject to Variant conversor
|
||||
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
NSString *str = (NSString *)value;
|
||||
String uval = String::utf8([str UTF8String]);
|
||||
|
||||
ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
|
||||
|
||||
} else if ([value isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *n = (NSNumber *)value;
|
||||
double dval = [n doubleValue];
|
||||
|
||||
ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
|
||||
}
|
||||
// do stuff
|
||||
}
|
||||
}
|
||||
|
||||
- (void)renderOnView:(UIView *)view {
|
||||
if (!OS_AppleEmbedded::get_singleton()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::get_singleton()->iterate();
|
||||
}
|
||||
|
||||
@end
|
||||
44
engine/drivers/apple_embedded/key_mapping_apple_embedded.h
Normal file
44
engine/drivers/apple_embedded/key_mapping_apple_embedded.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**************************************************************************/
|
||||
/* key_mapping_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
class KeyMappingAppleEmbedded {
|
||||
KeyMappingAppleEmbedded() {}
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static Key remap_key(CFIndex p_keycode);
|
||||
static KeyLocation key_location(CFIndex p_keycode);
|
||||
};
|
||||
206
engine/drivers/apple_embedded/key_mapping_apple_embedded.mm
Normal file
206
engine/drivers/apple_embedded/key_mapping_apple_embedded.mm
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/**************************************************************************/
|
||||
/* key_mapping_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
struct HashMapHasherKeys {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const CFIndex p_key) { return hash_fmix32(p_key); }
|
||||
};
|
||||
|
||||
HashMap<CFIndex, Key, HashMapHasherKeys> keyusage_map;
|
||||
HashMap<CFIndex, KeyLocation, HashMapHasherKeys> location_map;
|
||||
|
||||
void KeyMappingAppleEmbedded::initialize() {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardA] = Key::A;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardB] = Key::B;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardC] = Key::C;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardD] = Key::D;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardE] = Key::E;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF] = Key::F;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardG] = Key::G;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardH] = Key::H;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardI] = Key::I;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardJ] = Key::J;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardK] = Key::K;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardL] = Key::L;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardM] = Key::M;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardN] = Key::N;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardO] = Key::O;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardP] = Key::P;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardQ] = Key::Q;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardR] = Key::R;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardS] = Key::S;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardT] = Key::T;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardU] = Key::U;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardV] = Key::V;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardW] = Key::W;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardX] = Key::X;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardY] = Key::Y;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardZ] = Key::Z;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard0] = Key::KEY_0;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard1] = Key::KEY_1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard2] = Key::KEY_2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard3] = Key::KEY_3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard4] = Key::KEY_4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard5] = Key::KEY_5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard6] = Key::KEY_6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard7] = Key::KEY_7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard8] = Key::KEY_8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard9] = Key::KEY_9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardBackslash] = Key::BACKSLASH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardCloseBracket] = Key::BRACKETRIGHT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardComma] = Key::COMMA;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEqualSign] = Key::EQUAL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHyphen] = Key::MINUS;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardNonUSBackslash] = Key::SECTION;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardNonUSPound] = Key::ASCIITILDE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardOpenBracket] = Key::BRACKETLEFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPeriod] = Key::PERIOD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardQuote] = Key::QUOTEDBL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSemicolon] = Key::SEMICOLON;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSeparator] = Key::SECTION;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSlash] = Key::SLASH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSpacebar] = Key::SPACE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardCapsLock] = Key::CAPSLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftAlt] = Key::ALT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftControl] = Key::CTRL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftShift] = Key::SHIFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightAlt] = Key::ALT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightControl] = Key::CTRL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightShift] = Key::SHIFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardScrollLock] = Key::SCROLLLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftArrow] = Key::LEFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightArrow] = Key::RIGHT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardUpArrow] = Key::UP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDownArrow] = Key::DOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPageUp] = Key::PAGEUP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPageDown] = Key::PAGEDOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHome] = Key::HOME;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEnd] = Key::END;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDeleteForward] = Key::KEY_DELETE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDeleteOrBackspace] = Key::BACKSPACE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEscape] = Key::ESCAPE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardInsert] = Key::INSERT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardReturn] = Key::ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardTab] = Key::TAB;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF1] = Key::F1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF2] = Key::F2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF3] = Key::F3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF4] = Key::F4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF5] = Key::F5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF6] = Key::F6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF7] = Key::F7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF8] = Key::F8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF9] = Key::F9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF10] = Key::F10;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF11] = Key::F11;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF12] = Key::F12;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF13] = Key::F13;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF14] = Key::F14;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF15] = Key::F15;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF16] = Key::F16;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF17] = Key::F17;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF18] = Key::F18;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF19] = Key::F19;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF20] = Key::F20;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF21] = Key::F21;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF22] = Key::F22;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF23] = Key::F23;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF24] = Key::F24;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad0] = Key::KP_0;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad1] = Key::KP_1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad2] = Key::KP_2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad3] = Key::KP_3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad4] = Key::KP_4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad5] = Key::KP_5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad6] = Key::KP_6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad7] = Key::KP_7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad8] = Key::KP_8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad9] = Key::KP_9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadAsterisk] = Key::KP_MULTIPLY;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardGraveAccentAndTilde] = Key::BAR;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadEnter] = Key::KP_ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadHyphen] = Key::KP_SUBTRACT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadNumLock] = Key::NUMLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadPeriod] = Key::KP_PERIOD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadPlus] = Key::KP_ADD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadSlash] = Key::KP_DIVIDE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPause] = Key::PAUSE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardStop] = Key::STOP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardMute] = Key::VOLUMEMUTE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardVolumeUp] = Key::VOLUMEUP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardVolumeDown] = Key::VOLUMEDOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardFind] = Key::SEARCH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHelp] = Key::HELP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftGUI] = Key::META;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightGUI] = Key::META;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardMenu] = Key::MENU;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPrintScreen] = Key::PRINT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardReturnOrEnter] = Key::ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSysReqOrAttention] = Key::SYSREQ;
|
||||
keyusage_map[0x01AE] = Key::KEYBOARD; // On-screen keyboard key on smart connector keyboard.
|
||||
keyusage_map[0x029D] = Key::GLOBE; // "Globe" key on smart connector / Mac keyboard.
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLANG1] = Key::JIS_EISU;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLANG2] = Key::JIS_KANA;
|
||||
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftAlt] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightAlt] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftControl] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightControl] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftShift] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightShift] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftGUI] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightGUI] = KeyLocation::RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
Key KeyMappingAppleEmbedded::remap_key(CFIndex p_keycode) {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
const Key *key = keyusage_map.getptr(p_keycode);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
}
|
||||
return Key::NONE;
|
||||
}
|
||||
|
||||
KeyLocation KeyMappingAppleEmbedded::key_location(CFIndex p_keycode) {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
const KeyLocation *location = location_map.getptr(p_keycode);
|
||||
if (location) {
|
||||
return *location;
|
||||
}
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
||||
35
engine/drivers/apple_embedded/main_utilities.h
Normal file
35
engine/drivers/apple_embedded/main_utilities.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**************************************************************************/
|
||||
/* main_utilities.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void change_to_launch_dir(char **p_args);
|
||||
|
||||
int process_args(int p_argc, char **p_args, char **r_args);
|
||||
81
engine/drivers/apple_embedded/main_utilities.mm
Normal file
81
engine/drivers/apple_embedded/main_utilities.mm
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/**************************************************************************/
|
||||
/* main_utilities.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
|
||||
void change_to_launch_dir(char **p_args) {
|
||||
size_t len = strlen(p_args[0]);
|
||||
|
||||
while (len--) {
|
||||
if (p_args[0][len] == '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len >= 0) {
|
||||
char path[512];
|
||||
memcpy(path, p_args[0], len > sizeof(path) ? sizeof(path) : len);
|
||||
path[len] = 0;
|
||||
chdir(path);
|
||||
}
|
||||
}
|
||||
|
||||
int add_cmdline(int p_argc, char **p_args) {
|
||||
NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
|
||||
if (!arr) {
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
for (NSUInteger i = 0; i < [arr count]; i++) {
|
||||
NSString *str = [arr objectAtIndex:i];
|
||||
if (!str) {
|
||||
continue;
|
||||
}
|
||||
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
p_args[p_argc] = nullptr;
|
||||
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
int process_args(int p_argc, char **p_args, char **r_args) {
|
||||
for (int i = 0; i < p_argc; i++) {
|
||||
r_args[i] = p_args[i];
|
||||
}
|
||||
r_args[p_argc] = nullptr;
|
||||
p_argc = add_cmdline(p_argc, r_args);
|
||||
return p_argc;
|
||||
}
|
||||
160
engine/drivers/apple_embedded/os_apple_embedded.h
Normal file
160
engine/drivers/apple_embedded/os_apple_embedded.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/**************************************************************************/
|
||||
/* os_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef APPLE_EMBEDDED_ENABLED
|
||||
|
||||
#import "apple_embedded.h"
|
||||
|
||||
#import "drivers/coreaudio/audio_driver_coreaudio.h"
|
||||
#include "drivers/unix/os_unix.h"
|
||||
#include "servers/audio/audio_server.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class JoypadSDL;
|
||||
|
||||
class OS_AppleEmbedded : public OS_Unix {
|
||||
private:
|
||||
static HashMap<String, void *> dynamic_symbol_lookup_table;
|
||||
friend void register_dynamic_symbol(char *name, void *address);
|
||||
|
||||
AudioDriverCoreAudio audio_driver;
|
||||
|
||||
AppleEmbedded *apple_embedded = nullptr;
|
||||
|
||||
#ifdef SDL_ENABLED
|
||||
JoypadSDL *joypad_sdl = nullptr;
|
||||
#endif
|
||||
|
||||
MainLoop *main_loop = nullptr;
|
||||
|
||||
virtual void initialize_core() override;
|
||||
virtual void initialize() override;
|
||||
|
||||
virtual void initialize_joypads() override;
|
||||
|
||||
virtual void set_main_loop(MainLoop *p_main_loop) override;
|
||||
virtual MainLoop *get_main_loop() const override;
|
||||
|
||||
virtual void delete_main_loop() override;
|
||||
|
||||
virtual void finalize() override;
|
||||
|
||||
bool is_focused = false;
|
||||
|
||||
CGFloat _weight_to_ct(int p_weight) const;
|
||||
CGFloat _stretch_to_ct(int p_stretch) const;
|
||||
String _get_default_fontname(const String &p_font_name) const;
|
||||
|
||||
static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
|
||||
|
||||
void deinitialize_modules();
|
||||
|
||||
mutable String remote_fs_dir;
|
||||
|
||||
public:
|
||||
static OS_AppleEmbedded *get_singleton();
|
||||
|
||||
OS_AppleEmbedded();
|
||||
~OS_AppleEmbedded();
|
||||
|
||||
void initialize_modules();
|
||||
|
||||
bool iterate();
|
||||
|
||||
void start();
|
||||
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
|
||||
|
||||
virtual Vector<String> get_system_fonts() const override;
|
||||
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
|
||||
virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) override;
|
||||
virtual Error close_dynamic_library(void *p_library_handle) override;
|
||||
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override;
|
||||
|
||||
virtual String get_distribution_name() const override;
|
||||
virtual String get_version() const override;
|
||||
virtual String get_model_name() const override;
|
||||
|
||||
virtual Error shell_open(const String &p_uri) override;
|
||||
|
||||
virtual String get_user_data_dir(const String &p_user_dir) const override;
|
||||
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_temp_path() const override;
|
||||
virtual String get_resource_dir() const override;
|
||||
virtual String get_bundle_resource_dir() const override;
|
||||
|
||||
virtual String get_locale() const override;
|
||||
|
||||
virtual String get_unique_id() const override;
|
||||
virtual String get_processor_name() const override;
|
||||
|
||||
virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) override;
|
||||
|
||||
virtual bool _check_internal_feature_support(const String &p_feature) override;
|
||||
|
||||
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
|
||||
|
||||
void on_focus_out();
|
||||
void on_focus_in();
|
||||
|
||||
void on_enter_background();
|
||||
void on_exit_background();
|
||||
|
||||
virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const override;
|
||||
|
||||
virtual bool request_permission(const String &p_name) override;
|
||||
virtual Vector<String> get_granted_permissions() const override;
|
||||
|
||||
virtual String get_platform_string(PlatformString p_platform_string) const override {
|
||||
switch (p_platform_string) {
|
||||
case OS::PlatformString::PLATFORM_STRING_FILE_MANAGER_OPEN:
|
||||
return ETR("Open in Files");
|
||||
case OS::PlatformString::PLATFORM_STRING_FILE_MANAGER_SHOW:
|
||||
return ETR("Show in Files");
|
||||
default:
|
||||
return OS::get_platform_string(p_platform_string);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // APPLE_EMBEDDED_ENABLED
|
||||
865
engine/drivers/apple_embedded/os_apple_embedded.mm
Normal file
865
engine/drivers/apple_embedded/os_apple_embedded.mm
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
/**************************************************************************/
|
||||
/* os_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#ifdef APPLE_EMBEDDED_ENABLED
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_controller.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/main_loop.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/profiling/profiling.h"
|
||||
#import "drivers/apple/os_log_logger.h"
|
||||
#ifdef SDL_ENABLED
|
||||
#include "drivers/sdl/joypad_sdl.h"
|
||||
#endif
|
||||
#include "main/main.h"
|
||||
|
||||
#import <AVFoundation/AVFAudio.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <dlfcn.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <iterator>
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#include "drivers/vulkan/godot_vulkan.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#endif
|
||||
|
||||
// Initialization order between compilation units is not guaranteed,
|
||||
// so we use this as a hack to ensure certain code is called before
|
||||
// everything else, but after all units are initialized.
|
||||
typedef void (*init_callback)();
|
||||
static init_callback *apple_init_callbacks = nullptr;
|
||||
static int apple_embedded_platform_init_callbacks_count = 0;
|
||||
static int apple_embedded_platform_init_callbacks_capacity = 0;
|
||||
HashMap<String, void *> OS_AppleEmbedded::dynamic_symbol_lookup_table;
|
||||
|
||||
void add_apple_embedded_platform_init_callback(init_callback cb) {
|
||||
if (apple_embedded_platform_init_callbacks_count == apple_embedded_platform_init_callbacks_capacity) {
|
||||
void *new_ptr = realloc(apple_init_callbacks, sizeof(cb) * (apple_embedded_platform_init_callbacks_capacity + 32));
|
||||
if (new_ptr) {
|
||||
apple_init_callbacks = (init_callback *)(new_ptr);
|
||||
apple_embedded_platform_init_callbacks_capacity += 32;
|
||||
} else {
|
||||
ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
|
||||
}
|
||||
}
|
||||
apple_init_callbacks[apple_embedded_platform_init_callbacks_count++] = cb;
|
||||
}
|
||||
|
||||
void register_dynamic_symbol(char *name, void *address) {
|
||||
OS_AppleEmbedded::dynamic_symbol_lookup_table[String(name)] = address;
|
||||
}
|
||||
|
||||
Rect2 fit_keep_aspect_centered(const Vector2 &p_container, const Vector2 &p_rect) {
|
||||
real_t available_ratio = p_container.width / p_container.height;
|
||||
real_t fit_ratio = p_rect.width / p_rect.height;
|
||||
Rect2 result;
|
||||
if (fit_ratio < available_ratio) {
|
||||
// Fit height - we'll have horizontal gaps
|
||||
result.size.height = p_container.height;
|
||||
result.size.width = p_container.height * fit_ratio;
|
||||
result.position.y = 0;
|
||||
result.position.x = (p_container.width - result.size.width) * 0.5f;
|
||||
} else {
|
||||
// Fit width - we'll have vertical gaps
|
||||
result.size.width = p_container.width;
|
||||
result.size.height = p_container.width / fit_ratio;
|
||||
result.position.x = 0;
|
||||
result.position.y = (p_container.height - result.size.height) * 0.5f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) {
|
||||
real_t available_ratio = p_container.width / p_container.height;
|
||||
real_t fit_ratio = p_rect.width / p_rect.height;
|
||||
Rect2 result;
|
||||
if (fit_ratio < available_ratio) {
|
||||
// Need to scale up to fit width, and crop height
|
||||
result.size.width = p_container.width;
|
||||
result.size.height = p_container.width / fit_ratio;
|
||||
result.position.x = 0;
|
||||
result.position.y = (p_container.height - result.size.height) * 0.5f;
|
||||
} else {
|
||||
// Need to scale up to fit height, and crop width
|
||||
result.size.width = p_container.height * fit_ratio;
|
||||
result.size.height = p_container.height;
|
||||
result.position.x = (p_container.width - result.size.width) * 0.5f;
|
||||
result.position.y = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_AppleEmbedded *OS_AppleEmbedded::get_singleton() {
|
||||
return (OS_AppleEmbedded *)OS::get_singleton();
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::OS_AppleEmbedded() {
|
||||
for (int i = 0; i < apple_embedded_platform_init_callbacks_count; ++i) {
|
||||
apple_init_callbacks[i]();
|
||||
}
|
||||
free(apple_init_callbacks);
|
||||
apple_init_callbacks = nullptr;
|
||||
apple_embedded_platform_init_callbacks_count = 0;
|
||||
apple_embedded_platform_init_callbacks_capacity = 0;
|
||||
|
||||
main_loop = nullptr;
|
||||
|
||||
Vector<Logger *> loggers;
|
||||
loggers.push_back(memnew(OsLogLogger(NSBundle.mainBundle.bundleIdentifier.UTF8String)));
|
||||
_set_logger(memnew(CompositeLogger(loggers)));
|
||||
|
||||
AudioDriverManager::add_driver(&audio_driver);
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::~OS_AppleEmbedded() {}
|
||||
|
||||
void OS_AppleEmbedded::alert(const String &p_alert, const String &p_title) {
|
||||
const CharString utf8_alert = p_alert.utf8();
|
||||
const CharString utf8_title = p_title.utf8();
|
||||
AppleEmbedded::alert(utf8_alert.get_data(), utf8_title.get_data());
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_core() {
|
||||
OS_Unix::initialize_core();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize() {
|
||||
initialize_core();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_joypads() {
|
||||
#ifdef SDL_ENABLED
|
||||
joypad_sdl = memnew(JoypadSDL());
|
||||
if (joypad_sdl->initialize() != OK) {
|
||||
ERR_PRINT("Couldn't initialize SDL joypad input driver.");
|
||||
memdelete(joypad_sdl);
|
||||
joypad_sdl = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_modules() {
|
||||
apple_embedded = memnew(AppleEmbedded);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("AppleEmbedded", apple_embedded));
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::deinitialize_modules() {
|
||||
#ifdef SDL_ENABLED
|
||||
if (joypad_sdl) {
|
||||
memdelete(joypad_sdl);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (apple_embedded) {
|
||||
memdelete(apple_embedded);
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::set_main_loop(MainLoop *p_main_loop) {
|
||||
main_loop = p_main_loop;
|
||||
}
|
||||
|
||||
MainLoop *OS_AppleEmbedded::get_main_loop() const {
|
||||
return main_loop;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::delete_main_loop() {
|
||||
if (main_loop) {
|
||||
main_loop->finalize();
|
||||
memdelete(main_loop);
|
||||
}
|
||||
|
||||
main_loop = nullptr;
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::iterate() {
|
||||
if (!main_loop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GodotProfileFrameMark;
|
||||
GodotProfileZone("OS_AppleEmbedded::iterate");
|
||||
|
||||
if (DisplayServer::get_singleton()) {
|
||||
DisplayServer::get_singleton()->process_events();
|
||||
}
|
||||
|
||||
#ifdef SDL_ENABLED
|
||||
if (joypad_sdl) {
|
||||
joypad_sdl->process_events();
|
||||
}
|
||||
#endif
|
||||
|
||||
return Main::iteration();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::start() {
|
||||
if (Main::start() == EXIT_SUCCESS) {
|
||||
main_loop->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::finalize() {
|
||||
deinitialize_modules();
|
||||
|
||||
// Already gets called
|
||||
//delete_main_loop();
|
||||
}
|
||||
|
||||
// MARK: Dynamic Libraries
|
||||
|
||||
_FORCE_INLINE_ String OS_AppleEmbedded::get_framework_executable(const String &p_path) {
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
// Read framework bundle to get executable name.
|
||||
NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
|
||||
NSBundle *bundle = [NSBundle bundleWithURL:url];
|
||||
if (bundle) {
|
||||
String exe_path = String::utf8([[bundle executablePath] UTF8String]);
|
||||
if (da->file_exists(exe_path)) {
|
||||
return exe_path;
|
||||
}
|
||||
}
|
||||
|
||||
// Try default executable name (invalid framework).
|
||||
if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) {
|
||||
return p_path.path_join(p_path.get_file().get_basename());
|
||||
}
|
||||
|
||||
// Not a framework, try loading as .dylib.
|
||||
return p_path;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) {
|
||||
if (p_path.length() == 0) {
|
||||
// Static xcframework.
|
||||
p_library_handle = RTLD_SELF;
|
||||
|
||||
if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
|
||||
*p_data->r_resolved_path = p_path;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String path = get_framework_executable(p_path);
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib or framework from within the executable path.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file()));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib converted to framework from within the executable path.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework"));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib from within the executable path.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".dylib"));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib or framework from a standard iOS location.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file()));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib converted to framework from a standard iOS location.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework"));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib from a standard iOS location.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".dylib"));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path) && (p_path.ends_with(".a") || p_path.ends_with(".xcframework"))) {
|
||||
path = String(); // Try loading static library.
|
||||
} else {
|
||||
ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
|
||||
}
|
||||
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
|
||||
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
|
||||
|
||||
if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
|
||||
*p_data->r_resolved_path = path;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::close_dynamic_library(void *p_library_handle) {
|
||||
if (p_library_handle == RTLD_SELF) {
|
||||
return OK;
|
||||
}
|
||||
return OS_Unix::close_dynamic_library(p_library_handle);
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional) {
|
||||
if (p_library_handle == RTLD_SELF) {
|
||||
void **ptr = OS_AppleEmbedded::dynamic_symbol_lookup_table.getptr(p_name);
|
||||
if (ptr) {
|
||||
p_symbol_handle = *ptr;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_distribution_name() const {
|
||||
return get_name();
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_version() const {
|
||||
NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
|
||||
return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_model_name() const {
|
||||
String model = apple_embedded->get_model();
|
||||
if (model != "") {
|
||||
return model;
|
||||
}
|
||||
|
||||
return OS_Unix::get_model_name();
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::shell_open(const String &p_uri) {
|
||||
NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
|
||||
NSURL *url = [NSURL URLWithString:urlPath];
|
||||
|
||||
if (![[UIApplication sharedApplication] canOpenURL:url]) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
print_verbose(vformat("Opening URL %s", p_uri));
|
||||
|
||||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_user_data_dir(const String &p_user_dir) const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
if (paths && [paths count] >= 1) {
|
||||
ret.append_utf8([[paths firstObject] UTF8String]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_cache_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
if (paths && [paths count] >= 1) {
|
||||
ret.append_utf8([[paths firstObject] UTF8String]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_temp_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
|
||||
isDirectory:YES];
|
||||
if (url) {
|
||||
ret = String::utf8([url.path UTF8String]);
|
||||
ret = ret.trim_prefix("file://");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_resource_dir() const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return OS_Unix::get_resource_dir();
|
||||
#else
|
||||
if (remote_fs_dir.is_empty()) {
|
||||
return OS_Unix::get_resource_dir();
|
||||
} else {
|
||||
return remote_fs_dir;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_bundle_resource_dir() const {
|
||||
NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
|
||||
if (!str) {
|
||||
return OS_Unix::get_bundle_resource_dir();
|
||||
} else {
|
||||
return String::utf8([str cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_locale() const {
|
||||
NSString *preferredLanguage = [NSLocale preferredLanguages].firstObject;
|
||||
|
||||
if (preferredLanguage) {
|
||||
return String::utf8([preferredLanguage UTF8String]).replace_char('-', '_');
|
||||
}
|
||||
|
||||
NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier];
|
||||
return String::utf8([localeIdentifier UTF8String]).replace_char('-', '_');
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_unique_id() const {
|
||||
NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||
return String::utf8([uuid UTF8String]);
|
||||
}
|
||||
|
||||
struct _ModelInfo {
|
||||
Vector<String> model;
|
||||
String soc;
|
||||
};
|
||||
|
||||
static const _ModelInfo _models[] = {
|
||||
{ { "iPhone1,1", "iPhone1,2", "iPod1,1" }, "Samsung S5L8900" },
|
||||
{ { "iPod2,1" }, "Samsung S5L8720" },
|
||||
{ { "iPhone2,1" }, "Samsung S5L8920" },
|
||||
{ { "iPod3,1" }, "Samsung S5L8922" },
|
||||
{ { "iPhone3,1", "iPhone3,2", "iPhone3,3", "iPad1,1", "iPad1,2", "iPod4,1", "AppleTV2,1" }, "Apple A4" },
|
||||
{ { "iPhone4,1", "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4", "iPad2,5", "iPad2,6", "iPad2,7", "iPod5,1", "AppleTV3,1", "AppleTV3,2" }, "Apple A5" },
|
||||
{ { "iPad3,1", "iPad3,2", "iPad3,3" }, "Apple A5X" },
|
||||
{ { "iPhone5,1", "iPhone5,2", "iPhone5,3", "iPhone5,4" }, "Apple A6" },
|
||||
{ { "iPad3,4", "iPad3,5", "iPad3,6" }, "Apple A6X" },
|
||||
{ { "iPhone6,1", "iPhone6,2", "iPad4,1", "iPad4,2", "iPad4,3", "iPad4,4", "iPad4,5", "iPad4,6", "iPad4,7", "iPad4,8", "iPad4,9" }, "Apple A7" },
|
||||
{ { "iPhone7,1", "iPhone7,2", "iPad5,1", "iPad5,2", "iPod7,1", "AppleTV5,3" }, "Apple A8" },
|
||||
{ { "iPad5,3", "iPad5,4" }, "Apple A8X" },
|
||||
{ { "iPhone8,1", "iPhone8,2", "iPhone8,4", "iPad6,11", "iPad6,12" }, "Apple A9" },
|
||||
{ { "iPad6,3", "iPad6,4", "iPad6,7", "iPad6,8" }, "Apple A9X" },
|
||||
{ { "iPhone9,1", "iPhone9,2", "iPhone9,3", "iPhone9,4", "iPad7,5", "iPad7,6", "iPad7,11", "iPad7,12", "iPod9,1" }, "Apple A10 Fusion" },
|
||||
{ { "iPad7,1", "iPad7,2", "iPad7,3", "iPad7,4", "AppleTV6,2" }, "Apple A10X Fusion" },
|
||||
{ { "iPhone10,1", "iPhone10,2", "iPhone10,3", "iPhone10,4", "iPhone10,5", "iPhone10,6" }, "Apple A11 Bionic" },
|
||||
{ { "iPhone11,2", "iPhone11,4", "iPhone11,6", "iPhone11,8", "iPad11,1", "iPad11,2", "iPad11,3", "iPad11,4", "iPad11,6", "iPad11,7", "AppleTV11,1" }, "Apple A12 Bionic" },
|
||||
{ { "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4", "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8" }, "Apple A12X Bionic" },
|
||||
{ { "iPad8,9", "iPad8,10", "iPad8,11", "iPad8,12" }, "Apple A12Z Bionic" },
|
||||
{ { "iPhone12,1", "iPhone12,3", "iPhone12,5", "iPhone12,8", "iPad12,1", "iPad12,2" }, "Apple A13 Bionic" },
|
||||
{ { "iPhone13,1", "iPhone13,2", "iPhone13,3", "iPhone13,4", "iPad13,1", "iPad13,2", "iPad13,18", "iPad13,19" }, "Apple A14 Bionic" },
|
||||
{ { "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7", "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11", "iPad13,16", "iPad13,17" }, "Apple M1" },
|
||||
{ { "iPhone14,2", "iPhone14,3", "iPhone14,4", "iPhone14,5", "iPhone14,6", "iPhone14,7", "iPhone14,8", "iPad14,1", "iPad14,2", "AppleTV14,1" }, "Apple A15 Bionic" },
|
||||
{ { "iPhone15,2", "iPhone15,3", "iPhone15,4", "iPhone15,5", "iPad15,7", "iPad15,8" }, "Apple A16 Bionic" },
|
||||
{ { "iPad14,3", "iPad14,4", "iPad14,5", "iPad14,6", "iPad14,8", "iPad14,9", "iPad14,10", "iPad14,11", "RealityDevice14,1" }, "Apple M2" },
|
||||
{ { "iPhone16,1", "iPhone16,2", "iPad16,1", "iPad16,2" }, "Apple A17 Pro" },
|
||||
{ { "iPad15,3", "iPad15,4", "iPad15,5", "iPad15,6" }, "Apple M3" },
|
||||
{ { "iPad16,3", "iPad16,4", "iPad16,5", "iPad16,6" }, "Apple M4" },
|
||||
{ { "iPad17,1", "iPad17,2", "iPad17,3", "iPad17,4", "RealityDevice17,1" }, "Apple M5" },
|
||||
{ { "iPhone17,3", "iPhone17,4", "iPhone17,5" }, "Apple A18" },
|
||||
{ { "iPhone17,1", "iPhone17,2" }, "Apple A18 Pro" },
|
||||
{ { "iPhone18,3" }, "Apple A19" },
|
||||
{ { "iPhone18,1", "iPhone18,2", "iPhone18,4" }, "Apple A19 Pro" },
|
||||
};
|
||||
|
||||
String OS_AppleEmbedded::get_processor_name() const {
|
||||
#if defined(IOS_SIMULATOR) || defined(VISIONOS_SIMULATOR)
|
||||
return "Simulator";
|
||||
#else
|
||||
if (apple_embedded) {
|
||||
String model = apple_embedded->get_model();
|
||||
for (unsigned int i = 0; i < std::size(_models); i++) {
|
||||
for (const String &m : _models[i].model) {
|
||||
if (model.contains(m)) {
|
||||
return _models[i].soc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return OS::get_processor_name();
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_system_fonts() const {
|
||||
HashSet<String> font_names;
|
||||
CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
if (fonts) {
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
|
||||
CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
|
||||
if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
|
||||
NSString *ns_name = (__bridge NSString *)cf_name;
|
||||
font_names.insert(String::utf8([ns_name UTF8String]));
|
||||
}
|
||||
}
|
||||
CFRelease(fonts);
|
||||
}
|
||||
|
||||
Vector<String> ret;
|
||||
for (const String &E : font_names) {
|
||||
ret.push_back(E);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::_get_default_fontname(const String &p_font_name) const {
|
||||
String font_name = p_font_name;
|
||||
if (font_name.to_lower() == "sans-serif") {
|
||||
font_name = "Helvetica";
|
||||
} else if (font_name.to_lower() == "serif") {
|
||||
font_name = "Times";
|
||||
} else if (font_name.to_lower() == "monospace") {
|
||||
font_name = "Courier";
|
||||
} else if (font_name.to_lower() == "fantasy") {
|
||||
font_name = "Papyrus";
|
||||
} else if (font_name.to_lower() == "cursive") {
|
||||
font_name = "Apple Chancery";
|
||||
};
|
||||
return font_name;
|
||||
}
|
||||
|
||||
CGFloat OS_AppleEmbedded::_weight_to_ct(int p_weight) const {
|
||||
if (p_weight < 150) {
|
||||
return -0.80;
|
||||
} else if (p_weight < 250) {
|
||||
return -0.60;
|
||||
} else if (p_weight < 350) {
|
||||
return -0.40;
|
||||
} else if (p_weight < 450) {
|
||||
return 0.0;
|
||||
} else if (p_weight < 550) {
|
||||
return 0.23;
|
||||
} else if (p_weight < 650) {
|
||||
return 0.30;
|
||||
} else if (p_weight < 750) {
|
||||
return 0.40;
|
||||
} else if (p_weight < 850) {
|
||||
return 0.56;
|
||||
} else if (p_weight < 925) {
|
||||
return 0.62;
|
||||
} else {
|
||||
return 1.00;
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat OS_AppleEmbedded::_stretch_to_ct(int p_stretch) const {
|
||||
if (p_stretch < 56) {
|
||||
return -0.5;
|
||||
} else if (p_stretch < 69) {
|
||||
return -0.37;
|
||||
} else if (p_stretch < 81) {
|
||||
return -0.25;
|
||||
} else if (p_stretch < 93) {
|
||||
return -0.13;
|
||||
} else if (p_stretch < 106) {
|
||||
return 0.0;
|
||||
} else if (p_stretch < 137) {
|
||||
return 0.13;
|
||||
} else if (p_stretch < 144) {
|
||||
return 0.25;
|
||||
} else if (p_stretch < 162) {
|
||||
return 0.37;
|
||||
} else {
|
||||
return 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const {
|
||||
Vector<String> ret;
|
||||
String font_name = _get_default_fontname(p_font_name);
|
||||
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
CTFontSymbolicTraits traits = 0;
|
||||
if (p_weight >= 700) {
|
||||
traits |= kCTFontBoldTrait;
|
||||
}
|
||||
if (p_italic) {
|
||||
traits |= kCTFontItalicTrait;
|
||||
}
|
||||
if (p_stretch < 100) {
|
||||
traits |= kCTFontCondensedTrait;
|
||||
} else if (p_stretch > 100) {
|
||||
traits |= kCTFontExpandedTrait;
|
||||
}
|
||||
|
||||
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
|
||||
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
|
||||
|
||||
CGFloat weight = _weight_to_ct(p_weight);
|
||||
CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
|
||||
|
||||
CGFloat stretch = _stretch_to_ct(p_stretch);
|
||||
CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
|
||||
|
||||
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
|
||||
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
|
||||
|
||||
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
if (font) {
|
||||
CTFontRef family = CTFontCreateWithFontDescriptor(font, 0, nullptr);
|
||||
if (family) {
|
||||
CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, p_text.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
CFRange range = CFRangeMake(0, CFStringGetLength(string));
|
||||
CTFontRef fallback_family = CTFontCreateForString(family, string, range);
|
||||
if (fallback_family) {
|
||||
CTFontDescriptorRef fallback_font = CTFontCopyFontDescriptor(fallback_family);
|
||||
if (fallback_font) {
|
||||
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fallback_font, kCTFontURLAttribute);
|
||||
if (url) {
|
||||
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
|
||||
ret.push_back(String::utf8([font_path UTF8String]));
|
||||
CFRelease(url);
|
||||
}
|
||||
CFRelease(fallback_font);
|
||||
}
|
||||
CFRelease(fallback_family);
|
||||
}
|
||||
CFRelease(string);
|
||||
CFRelease(family);
|
||||
}
|
||||
CFRelease(font);
|
||||
}
|
||||
|
||||
CFRelease(attributes);
|
||||
CFRelease(traits_dict);
|
||||
CFRelease(sym_traits);
|
||||
CFRelease(font_stretch);
|
||||
CFRelease(font_weight);
|
||||
CFRelease(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_system_font_path(const String &p_font_name, int p_weight, int p_stretch, bool p_italic) const {
|
||||
String ret;
|
||||
String font_name = _get_default_fontname(p_font_name);
|
||||
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
|
||||
CTFontSymbolicTraits traits = 0;
|
||||
if (p_weight >= 700) {
|
||||
traits |= kCTFontBoldTrait;
|
||||
}
|
||||
if (p_italic) {
|
||||
traits |= kCTFontItalicTrait;
|
||||
}
|
||||
if (p_stretch < 100) {
|
||||
traits |= kCTFontCondensedTrait;
|
||||
} else if (p_stretch > 100) {
|
||||
traits |= kCTFontExpandedTrait;
|
||||
}
|
||||
|
||||
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
|
||||
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
|
||||
|
||||
CGFloat weight = _weight_to_ct(p_weight);
|
||||
CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
|
||||
|
||||
CGFloat stretch = _stretch_to_ct(p_stretch);
|
||||
CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
|
||||
|
||||
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
|
||||
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
|
||||
|
||||
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
if (font) {
|
||||
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
|
||||
if (url) {
|
||||
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
|
||||
ret = String::utf8([font_path UTF8String]);
|
||||
CFRelease(url);
|
||||
}
|
||||
CFRelease(font);
|
||||
}
|
||||
|
||||
CFRelease(attributes);
|
||||
CFRelease(traits_dict);
|
||||
CFRelease(sym_traits);
|
||||
CFRelease(font_stretch);
|
||||
CFRelease(font_weight);
|
||||
CFRelease(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::vibrate_handheld(int p_duration_ms, float p_amplitude) {
|
||||
if (apple_embedded->supports_haptic_engine()) {
|
||||
if (p_amplitude > 0.0) {
|
||||
p_amplitude = CLAMP(p_amplitude, 0.0, 1.0);
|
||||
}
|
||||
|
||||
apple_embedded->vibrate_haptic_engine((float)p_duration_ms / 1000.f, p_amplitude);
|
||||
} else {
|
||||
// iOS <13 does not support duration for vibration
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::_check_internal_feature_support(const String &p_feature) {
|
||||
if (p_feature == "system_fonts") {
|
||||
return true;
|
||||
}
|
||||
if (p_feature == "mobile") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
|
||||
r_project_path = OS::get_user_data_dir();
|
||||
Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path);
|
||||
if (err == OK) {
|
||||
remote_fs_dir = r_project_path;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_focus_out() {
|
||||
if (is_focused) {
|
||||
is_focused = false;
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_OUT);
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.godotView stopRendering];
|
||||
|
||||
audio_driver.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_focus_in() {
|
||||
if (!is_focused) {
|
||||
is_focused = true;
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_IN);
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.godotView startRendering];
|
||||
|
||||
audio_driver.start();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_enter_background() {
|
||||
// Do not check for is_focused, because on_focus_out will always be fired first by applicationWillResignActive.
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
|
||||
}
|
||||
|
||||
on_focus_out();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_exit_background() {
|
||||
if (!is_focused) {
|
||||
on_focus_in();
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect2 OS_AppleEmbedded::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
|
||||
String scalemodestr = GLOBAL_GET("ios/launch_screen_image_mode");
|
||||
|
||||
if (scalemodestr == "scaleAspectFit") {
|
||||
return fit_keep_aspect_centered(p_window_size, p_imgrect_size);
|
||||
} else if (scalemodestr == "scaleAspectFill") {
|
||||
return fit_keep_aspect_covered(p_window_size, p_imgrect_size);
|
||||
} else if (scalemodestr == "scaleToFill") {
|
||||
return Rect2(Point2(), p_window_size);
|
||||
} else if (scalemodestr == "center") {
|
||||
return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
|
||||
} else {
|
||||
WARN_PRINT(vformat("Boot screen scale mode mismatch between iOS and Godot: %s not supported", scalemodestr));
|
||||
return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::request_permission(const String &p_name) {
|
||||
if (p_name == "appleembedded.permission.AUDIO_RECORD") {
|
||||
if (@available(iOS 17.0, *)) {
|
||||
AVAudioApplicationRecordPermission permission = [AVAudioApplication sharedInstance].recordPermission;
|
||||
if (permission == AVAudioApplicationRecordPermissionGranted) {
|
||||
// Permission already granted, you can start recording.
|
||||
return true;
|
||||
} else if (permission == AVAudioApplicationRecordPermissionDenied) {
|
||||
// Permission denied, or not yet granted.
|
||||
return false;
|
||||
} else {
|
||||
// Request the permission, but for now return false as documented.
|
||||
[AVAudioApplication requestRecordPermissionWithCompletionHandler:^(BOOL granted) {
|
||||
get_main_loop()->emit_signal(SNAME("on_request_permissions_result"), p_name, granted);
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_granted_permissions() const {
|
||||
Vector<String> ret;
|
||||
|
||||
if (@available(iOS 17.0, *)) {
|
||||
if ([AVAudioApplication sharedInstance].recordPermission == AVAudioApplicationRecordPermissionGranted) {
|
||||
ret.push_back("appleembedded.permission.AUDIO_RECORD");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // APPLE_EMBEDDED_ENABLED
|
||||
44
engine/drivers/apple_embedded/platform_config.h
Normal file
44
engine/drivers/apple_embedded/platform_config.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**************************************************************************/
|
||||
/* platform_config.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
|
||||
#define PLATFORM_THREAD_OVERRIDE
|
||||
|
||||
#define PTHREAD_RENAME_SELF
|
||||
|
||||
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
|
||||
#define _strongify(var) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
__strong typeof(var) var = GDWeak_##var; \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
|
||||
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
class RenderingContextDriverVulkanAppleEmbedded : public RenderingContextDriverVulkan {
|
||||
private:
|
||||
virtual const char *_get_platform_surface_extension() const override final;
|
||||
|
||||
protected:
|
||||
SurfaceID surface_create(const void *p_platform_data) override final;
|
||||
|
||||
public:
|
||||
struct WindowPlatformData {
|
||||
CAMetalLayer *const *layer_ptr;
|
||||
};
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded();
|
||||
~RenderingContextDriverVulkanAppleEmbedded();
|
||||
};
|
||||
|
||||
#endif // VULKAN_ENABLED
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#ifdef USE_VOLK
|
||||
#include <volk.h>
|
||||
#else
|
||||
#include <vulkan/vulkan_metal.h>
|
||||
#endif
|
||||
|
||||
const char *RenderingContextDriverVulkanAppleEmbedded::_get_platform_surface_extension() const {
|
||||
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAppleEmbedded::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
|
||||
VkMetalSurfaceCreateInfoEXT create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
create_info.pLayer = *wpd->layer_ptr;
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateMetalSurfaceEXT(instance_get(), &create_info, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR), &vk_surface);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID());
|
||||
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->vk_surface = vk_surface;
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded::RenderingContextDriverVulkanAppleEmbedded() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded::~RenderingContextDriverVulkanAppleEmbedded() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
#endif // VULKAN_ENABLED
|
||||
61
engine/drivers/apple_embedded/tts_apple_embedded.h
Normal file
61
engine/drivers/apple_embedded/tts_apple_embedded.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/**************************************************************************/
|
||||
/* tts_apple_embedded.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/variant/array.h"
|
||||
|
||||
#if __has_include(<AVFAudio/AVSpeechSynthesis.h>)
|
||||
#import <AVFAudio/AVSpeechSynthesis.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#endif
|
||||
|
||||
struct TTSUtterance;
|
||||
|
||||
@interface GDTTTS : NSObject <AVSpeechSynthesizerDelegate> {
|
||||
bool speaking;
|
||||
HashMap<id, int64_t> ids;
|
||||
|
||||
AVSpeechSynthesizer *av_synth;
|
||||
List<TTSUtterance> queue;
|
||||
}
|
||||
|
||||
- (void)pauseSpeaking;
|
||||
- (void)resumeSpeaking;
|
||||
- (void)stopSpeaking;
|
||||
- (bool)isSpeaking;
|
||||
- (bool)isPaused;
|
||||
- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int64_t)utterance_id interrupt:(bool)interrupt;
|
||||
- (Array)getVoices;
|
||||
@end
|
||||
165
engine/drivers/apple_embedded/tts_apple_embedded.mm
Normal file
165
engine/drivers/apple_embedded/tts_apple_embedded.mm
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/**************************************************************************/
|
||||
/* tts_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "tts_apple_embedded.h"
|
||||
|
||||
#include "servers/display/display_server.h"
|
||||
|
||||
@implementation GDTTTS
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
self->speaking = false;
|
||||
self->av_synth = [[AVSpeechSynthesizer alloc] init];
|
||||
[self->av_synth setDelegate:self];
|
||||
print_verbose("Text-to-Speech: AVSpeechSynthesizer initialized.");
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance {
|
||||
NSString *string = [utterance speechString];
|
||||
|
||||
// Convert from UTF-16 to UTF-32 position.
|
||||
int pos = 0;
|
||||
for (NSUInteger i = 0; i < MIN(characterRange.location, string.length); i++) {
|
||||
unichar c = [string characterAtIndex:i];
|
||||
if ((c & 0xfffffc00) == 0xd800) {
|
||||
i++;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_BOUNDARY, ids[utterance], pos);
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didCancelSpeechUtterance:(AVSpeechUtterance *)utterance {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_CANCELED, ids[utterance]);
|
||||
ids.erase(utterance);
|
||||
speaking = false;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didFinishSpeechUtterance:(AVSpeechUtterance *)utterance {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_ENDED, ids[utterance]);
|
||||
ids.erase(utterance);
|
||||
speaking = false;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)update {
|
||||
if (!speaking && queue.size() > 0) {
|
||||
TTSUtterance &message = queue.front()->get();
|
||||
|
||||
AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]];
|
||||
[new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]];
|
||||
if (message.rate > 1.f) {
|
||||
[new_utterance setRate:Math::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
|
||||
} else if (message.rate < 1.f) {
|
||||
[new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
|
||||
}
|
||||
[new_utterance setPitchMultiplier:message.pitch];
|
||||
[new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
|
||||
|
||||
ids[new_utterance] = message.id;
|
||||
[av_synth speakUtterance:new_utterance];
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_STARTED, message.id);
|
||||
|
||||
queue.pop_front();
|
||||
speaking = true;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pauseSpeaking {
|
||||
[av_synth pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
||||
}
|
||||
|
||||
- (void)resumeSpeaking {
|
||||
[av_synth continueSpeaking];
|
||||
}
|
||||
|
||||
- (void)stopSpeaking {
|
||||
for (TTSUtterance &message : queue) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_CANCELED, message.id);
|
||||
}
|
||||
queue.clear();
|
||||
[av_synth stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
||||
speaking = false;
|
||||
}
|
||||
|
||||
- (bool)isSpeaking {
|
||||
return speaking || (queue.size() > 0);
|
||||
}
|
||||
|
||||
- (bool)isPaused {
|
||||
return [av_synth isPaused];
|
||||
}
|
||||
|
||||
- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int64_t)utterance_id interrupt:(bool)interrupt {
|
||||
if (interrupt) {
|
||||
[self stopSpeaking];
|
||||
}
|
||||
|
||||
if (text.is_empty()) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServerEnums::TTS_UTTERANCE_CANCELED, utterance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
TTSUtterance message;
|
||||
message.text = text;
|
||||
message.voice = voice;
|
||||
message.volume = CLAMP(volume, 0, 100);
|
||||
message.pitch = CLAMP(pitch, 0.f, 2.f);
|
||||
message.rate = CLAMP(rate, 0.1f, 10.f);
|
||||
message.id = utterance_id;
|
||||
queue.push_back(message);
|
||||
|
||||
if ([self isPaused]) {
|
||||
[self resumeSpeaking];
|
||||
} else {
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (Array)getVoices {
|
||||
Array list;
|
||||
for (AVSpeechSynthesisVoice *voice in [AVSpeechSynthesisVoice speechVoices]) {
|
||||
NSString *voiceIdentifierString = [voice identifier];
|
||||
NSString *voiceLocaleIdentifier = [voice language];
|
||||
NSString *voiceName = [voice name];
|
||||
Dictionary voice_d;
|
||||
voice_d["name"] = String::utf8([voiceName UTF8String]);
|
||||
voice_d["id"] = String::utf8([voiceIdentifierString UTF8String]);
|
||||
voice_d["language"] = String::utf8([voiceLocaleIdentifier UTF8String]);
|
||||
list.push_back(voice_d);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@end
|
||||
45
engine/drivers/backtrace/SCsub
Normal file
45
engine/drivers/backtrace/SCsub
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env_backtrace = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
thirdparty_dir = "#thirdparty/libbacktrace/"
|
||||
thirdparty_sources = [
|
||||
"atomic.c",
|
||||
"dwarf.c",
|
||||
"fileline.c",
|
||||
"posix.c",
|
||||
"print.c",
|
||||
"sort.c",
|
||||
"state.c",
|
||||
"backtrace.c",
|
||||
"simple.c",
|
||||
"pecoff.c",
|
||||
"read.c",
|
||||
"alloc.c",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_backtrace.Prepend(CPPPATH=[thirdparty_dir])
|
||||
|
||||
env_thirdparty = env_backtrace.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
|
||||
env.drivers_sources += thirdparty_obj
|
||||
|
||||
# Godot source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_backtrace.add_source_files(driver_obj, "*.cpp")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty library is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
||||
7
engine/drivers/coreaudio/SCsub
Normal file
7
engine/drivers/coreaudio/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.mm")
|
||||
127
engine/drivers/coreaudio/audio_driver_coreaudio.h
Normal file
127
engine/drivers/coreaudio/audio_driver_coreaudio.h
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_coreaudio.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef COREAUDIO_ENABLED
|
||||
|
||||
#include "servers/audio/audio_server.h"
|
||||
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#ifdef MACOS_ENABLED
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#endif
|
||||
|
||||
class AudioDriverCoreAudio : public AudioDriver {
|
||||
AudioComponentInstance audio_unit = nullptr;
|
||||
AudioComponentInstance input_unit = nullptr;
|
||||
|
||||
bool active = false;
|
||||
Mutex mutex;
|
||||
|
||||
String output_device_name = "Default";
|
||||
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;
|
||||
unsigned int buffer_size = 0;
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
PackedStringArray _get_device_list(bool capture = false);
|
||||
void _set_device(const String &output_device, bool capture = false);
|
||||
|
||||
static OSStatus input_device_address_cb(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
|
||||
void *inClientData);
|
||||
|
||||
static OSStatus output_device_address_cb(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
|
||||
void *inClientData);
|
||||
#endif
|
||||
|
||||
static OSStatus output_callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData);
|
||||
|
||||
static OSStatus input_callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData);
|
||||
|
||||
Error init_input_device();
|
||||
void finish_input_device();
|
||||
|
||||
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;
|
||||
virtual void unlock() override;
|
||||
virtual void finish() override;
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
virtual PackedStringArray get_output_device_list() override;
|
||||
virtual String get_output_device() override;
|
||||
virtual void set_output_device(const String &p_name) override;
|
||||
|
||||
virtual PackedStringArray get_input_device_list() override;
|
||||
virtual String get_input_device() override;
|
||||
virtual void set_input_device(const String &p_name) override;
|
||||
#endif
|
||||
|
||||
virtual Error input_start() override;
|
||||
virtual Error input_stop() override;
|
||||
|
||||
bool try_lock();
|
||||
void stop();
|
||||
|
||||
AudioDriverCoreAudio();
|
||||
~AudioDriverCoreAudio() {}
|
||||
};
|
||||
|
||||
#endif // COREAUDIO_ENABLED
|
||||
730
engine/drivers/coreaudio/audio_driver_coreaudio.mm
Normal file
730
engine/drivers/coreaudio/audio_driver_coreaudio.mm
Normal file
|
|
@ -0,0 +1,730 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_coreaudio.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "audio_driver_coreaudio.h"
|
||||
|
||||
#ifdef COREAUDIO_ENABLED
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/math_funcs_binary.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#define kOutputBus 0
|
||||
#define kInputBus 1
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
|
||||
void *inClientData) {
|
||||
AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData);
|
||||
|
||||
// If our selected input device is the Default, call set_input_device to update the
|
||||
// kAudioOutputUnitProperty_CurrentDevice property
|
||||
if (driver->input_device_name == "Default") {
|
||||
driver->set_input_device("Default");
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID,
|
||||
UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
|
||||
void *inClientData) {
|
||||
AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData);
|
||||
|
||||
// If our selected output device is the Default call set_output_device to update the
|
||||
// kAudioOutputUnitProperty_CurrentDevice property
|
||||
if (driver->output_device_name == "Default") {
|
||||
driver->set_output_device("Default");
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// Switch to kAudioObjectPropertyElementMain everywhere to remove deprecated warnings.
|
||||
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 120000) || (TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED < 150000)
|
||||
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Error AudioDriverCoreAudio::init() {
|
||||
AudioComponentDescription desc;
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#ifdef MACOS_ENABLED
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
||||
AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
|
||||
ERR_FAIL_NULL_V(comp, FAILED);
|
||||
|
||||
OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
AudioObjectPropertyAddress prop;
|
||||
prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#endif
|
||||
|
||||
AudioStreamBasicDescription strdesc;
|
||||
|
||||
memset(&strdesc, 0, sizeof(strdesc));
|
||||
UInt32 size = sizeof(strdesc);
|
||||
result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
switch (strdesc.mChannelsPerFrame) {
|
||||
case 2: // Stereo
|
||||
case 4: // Surround 3.1
|
||||
case 6: // Surround 5.1
|
||||
case 8: // Surround 7.1
|
||||
channels = strdesc.mChannelsPerFrame;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown number of channels, default to stereo
|
||||
channels = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
#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;
|
||||
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
strdesc.mChannelsPerFrame = channels;
|
||||
strdesc.mSampleRate = mix_rate;
|
||||
strdesc.mFramesPerPacket = 1;
|
||||
strdesc.mBitsPerChannel = 16;
|
||||
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
|
||||
strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
|
||||
|
||||
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
uint32_t 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)
|
||||
buffer_frames = Math::closest_power_of_2(latency * (uint32_t)mix_rate / (uint32_t)1000);
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
result = AudioUnitSetProperty(audio_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, kOutputBus, &buffer_frames, sizeof(UInt32));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#endif
|
||||
|
||||
unsigned int buffer_size = buffer_frames * channels;
|
||||
samples_in.resize(buffer_size);
|
||||
|
||||
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
|
||||
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));
|
||||
callback.inputProc = &AudioDriverCoreAudio::output_callback;
|
||||
callback.inputProcRefCon = this;
|
||||
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
result = AudioUnitInitialize(audio_unit);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
if (GLOBAL_GET("audio/driver/enable_input")) {
|
||||
return init_input_device();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) {
|
||||
AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon);
|
||||
|
||||
if (!ad->active || !ad->try_lock()) {
|
||||
for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
|
||||
AudioBuffer *abuf = &ioData->mBuffers[i];
|
||||
memset(abuf->mData, 0, abuf->mDataByteSize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ad->start_counting_ticks();
|
||||
|
||||
for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
|
||||
AudioBuffer *abuf = &ioData->mBuffers[i];
|
||||
unsigned int frames_left = inNumberFrames;
|
||||
int16_t *out = (int16_t *)abuf->mData;
|
||||
|
||||
while (frames_left) {
|
||||
unsigned int frames = MIN(frames_left, ad->buffer_frames);
|
||||
ad->audio_server_process(frames, ad->samples_in.ptrw());
|
||||
|
||||
for (unsigned int j = 0; j < frames * ad->channels; j++) {
|
||||
out[j] = ad->samples_in[j] >> 16;
|
||||
}
|
||||
|
||||
frames_left -= frames;
|
||||
out += frames * ad->channels;
|
||||
}
|
||||
}
|
||||
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) {
|
||||
AudioDriverCoreAudio *ad = static_cast<AudioDriverCoreAudio *>(inRefCon);
|
||||
if (!ad->active) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ad->lock();
|
||||
ad->start_counting_ticks();
|
||||
|
||||
AudioBufferList bufferList;
|
||||
bufferList.mNumberBuffers = 1;
|
||||
bufferList.mBuffers[0].mData = nullptr;
|
||||
bufferList.mBuffers[0].mNumberChannels = ad->capture_channels;
|
||||
bufferList.mBuffers[0].mDataByteSize = ad->buffer_size * sizeof(int16_t);
|
||||
|
||||
OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
|
||||
if (result == noErr) {
|
||||
int16_t *data = (int16_t *)bufferList.mBuffers[0].mData;
|
||||
for (unsigned int i = 0; i < inNumberFrames * ad->capture_channels; i++) {
|
||||
int32_t sample = data[i] << 16;
|
||||
ad->input_buffer_write(sample);
|
||||
|
||||
if (ad->capture_channels == 1) {
|
||||
// In case input device is single channel convert it to Stereo
|
||||
ad->input_buffer_write(sample);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ERR_PRINT("AudioUnitRender failed, code: " + itos(result));
|
||||
}
|
||||
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::start() {
|
||||
if (!active && audio_unit != nullptr) {
|
||||
OSStatus result = AudioOutputUnitStart(audio_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result));
|
||||
} else {
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::stop() {
|
||||
if (active) {
|
||||
OSStatus result = AudioOutputUnitStop(audio_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioOutputUnitStop failed, code: " + itos(result));
|
||||
} else {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
bool AudioDriverCoreAudio::try_lock() {
|
||||
return mutex.try_lock();
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::finish() {
|
||||
finish_input_device();
|
||||
|
||||
if (audio_unit) {
|
||||
OSStatus result;
|
||||
|
||||
lock();
|
||||
|
||||
AURenderCallbackStruct callback;
|
||||
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
||||
result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioUnitSetProperty failed");
|
||||
}
|
||||
|
||||
if (active) {
|
||||
result = AudioOutputUnitStop(audio_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioOutputUnitStop failed");
|
||||
}
|
||||
|
||||
active = false;
|
||||
}
|
||||
|
||||
result = AudioUnitUninitialize(audio_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioUnitUninitialize failed");
|
||||
}
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
AudioObjectPropertyAddress prop;
|
||||
prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioObjectRemovePropertyListener failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
result = AudioComponentInstanceDispose(audio_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioComponentInstanceDispose failed");
|
||||
}
|
||||
|
||||
audio_unit = nullptr;
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
Error AudioDriverCoreAudio::init_input_device() {
|
||||
AudioComponentDescription desc;
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#ifdef MACOS_ENABLED
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#endif
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
||||
AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
|
||||
ERR_FAIL_NULL_V(comp, FAILED);
|
||||
|
||||
OSStatus result = AudioComponentInstanceNew(comp, &input_unit);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
AudioObjectPropertyAddress prop;
|
||||
prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#endif
|
||||
|
||||
UInt32 flag = 1;
|
||||
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#ifdef MACOS_ENABLED
|
||||
flag = 0;
|
||||
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#endif
|
||||
|
||||
UInt32 size;
|
||||
#ifdef MACOS_ENABLED
|
||||
AudioDeviceID device_id;
|
||||
size = sizeof(AudioDeviceID);
|
||||
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
||||
|
||||
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, &device_id, sizeof(AudioDeviceID));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
#endif
|
||||
|
||||
AudioStreamBasicDescription strdesc;
|
||||
memset(&strdesc, 0, sizeof(strdesc));
|
||||
size = sizeof(strdesc);
|
||||
result = AudioUnitGetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
switch (strdesc.mChannelsPerFrame) {
|
||||
case 1: // Mono
|
||||
capture_channels = 1;
|
||||
break;
|
||||
|
||||
case 2: // Stereo
|
||||
capture_channels = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown number of channels, default to stereo
|
||||
capture_channels = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
#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 = capture_mix_rate;
|
||||
strdesc.mFramesPerPacket = 1;
|
||||
strdesc.mBitsPerChannel = 16;
|
||||
strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
|
||||
strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
|
||||
|
||||
result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
uint32_t 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 = Math::closest_power_of_2(latency * (uint32_t)capture_mix_rate / (uint32_t)1000);
|
||||
|
||||
buffer_size = capture_buffer_frames * capture_channels;
|
||||
|
||||
AURenderCallbackStruct callback;
|
||||
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
||||
callback.inputProc = &AudioDriverCoreAudio::input_callback;
|
||||
callback.inputProcRefCon = this;
|
||||
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::finish_input_device() {
|
||||
if (input_unit) {
|
||||
lock();
|
||||
|
||||
AURenderCallbackStruct callback;
|
||||
memset(&callback, 0, sizeof(AURenderCallbackStruct));
|
||||
OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioUnitSetProperty failed");
|
||||
}
|
||||
|
||||
result = AudioUnitUninitialize(input_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioUnitUninitialize failed");
|
||||
}
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
AudioObjectPropertyAddress prop;
|
||||
prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioObjectRemovePropertyListener failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
result = AudioComponentInstanceDispose(input_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioComponentInstanceDispose failed");
|
||||
}
|
||||
|
||||
input_unit = nullptr;
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
Error AudioDriverCoreAudio::input_start() {
|
||||
ERR_FAIL_NULL_V(input_unit, FAILED);
|
||||
|
||||
input_buffer_init(capture_buffer_frames);
|
||||
|
||||
OSStatus result = AudioOutputUnitStart(input_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result));
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverCoreAudio::input_stop() {
|
||||
if (input_unit) {
|
||||
OSStatus result = AudioOutputUnitStop(input_unit);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("AudioOutputUnitStop failed, code: " + itos(result));
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) {
|
||||
PackedStringArray list;
|
||||
|
||||
list.push_back("Default");
|
||||
|
||||
AudioObjectPropertyAddress prop;
|
||||
|
||||
prop.mSelector = kAudioHardwarePropertyDevices;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
UInt32 size = 0;
|
||||
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
|
||||
AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size);
|
||||
ERR_FAIL_NULL_V_MSG(audioDevices, list, "Out of memory.");
|
||||
AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices);
|
||||
|
||||
UInt32 deviceCount = size / sizeof(AudioDeviceID);
|
||||
for (UInt32 i = 0; i < deviceCount; i++) {
|
||||
prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
|
||||
AudioBufferList *bufferList = (AudioBufferList *)memalloc(size);
|
||||
ERR_FAIL_NULL_V_MSG(bufferList, list, "Out of memory.");
|
||||
AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList);
|
||||
|
||||
UInt32 channelCount = 0;
|
||||
for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) {
|
||||
channelCount += bufferList->mBuffers[j].mNumberChannels;
|
||||
}
|
||||
|
||||
memfree(bufferList);
|
||||
|
||||
if (channelCount >= 1) {
|
||||
CFStringRef cfname;
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
prop.mSelector = kAudioObjectPropertyName;
|
||||
|
||||
AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, &cfname);
|
||||
|
||||
CFIndex length = CFStringGetLength(cfname);
|
||||
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
||||
char *buffer = (char *)memalloc(maxSize);
|
||||
ERR_FAIL_NULL_V_MSG(buffer, list, "Out of memory.");
|
||||
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
|
||||
// Append the ID to the name in case we have devices with duplicate name
|
||||
list.push_back(String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")");
|
||||
}
|
||||
|
||||
memfree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
memfree(audioDevices);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) {
|
||||
AudioDeviceID device_id;
|
||||
bool found = false;
|
||||
if (output_device != "Default") {
|
||||
AudioObjectPropertyAddress prop;
|
||||
|
||||
prop.mSelector = kAudioHardwarePropertyDevices;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMain;
|
||||
|
||||
UInt32 size = 0;
|
||||
AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size);
|
||||
AudioDeviceID *audioDevices = (AudioDeviceID *)memalloc(size);
|
||||
ERR_FAIL_NULL_MSG(audioDevices, "Out of memory.");
|
||||
AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices);
|
||||
|
||||
UInt32 deviceCount = size / sizeof(AudioDeviceID);
|
||||
for (UInt32 i = 0; i < deviceCount && !found; i++) {
|
||||
prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
|
||||
prop.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size);
|
||||
AudioBufferList *bufferList = (AudioBufferList *)memalloc(size);
|
||||
ERR_FAIL_NULL_MSG(bufferList, "Out of memory.");
|
||||
AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList);
|
||||
|
||||
UInt32 channelCount = 0;
|
||||
for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) {
|
||||
channelCount += bufferList->mBuffers[j].mNumberChannels;
|
||||
}
|
||||
|
||||
memfree(bufferList);
|
||||
|
||||
if (channelCount >= 1) {
|
||||
CFStringRef cfname;
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
prop.mSelector = kAudioObjectPropertyName;
|
||||
|
||||
AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, &cfname);
|
||||
|
||||
CFIndex length = CFStringGetLength(cfname);
|
||||
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
||||
char *buffer = (char *)memalloc(maxSize);
|
||||
ERR_FAIL_NULL_MSG(buffer, "Out of memory.");
|
||||
if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
|
||||
String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")";
|
||||
if (name == output_device) {
|
||||
device_id = audioDevices[i];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
memfree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
memfree(audioDevices);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// If we haven't found the desired device get the system default one
|
||||
UInt32 size = sizeof(AudioDeviceID);
|
||||
UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
|
||||
AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain };
|
||||
|
||||
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, &device_id, sizeof(AudioDeviceID));
|
||||
ERR_FAIL_COND(result != noErr);
|
||||
|
||||
if (input) {
|
||||
// Reset audio input to keep synchronization.
|
||||
input_position = 0;
|
||||
input_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::get_output_device_list() {
|
||||
return _get_device_list();
|
||||
}
|
||||
|
||||
String AudioDriverCoreAudio::get_output_device() {
|
||||
return output_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::set_output_device(const String &p_name) {
|
||||
output_device_name = p_name;
|
||||
if (active) {
|
||||
_set_device(output_device_name);
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverCoreAudio::get_input_device_list() {
|
||||
return _get_device_list(true);
|
||||
}
|
||||
|
||||
String AudioDriverCoreAudio::get_input_device() {
|
||||
return input_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverCoreAudio::set_input_device(const String &p_name) {
|
||||
input_device_name = p_name;
|
||||
if (active) {
|
||||
_set_device(input_device_name, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
AudioDriverCoreAudio::AudioDriverCoreAudio() {
|
||||
samples_in.clear();
|
||||
}
|
||||
|
||||
#endif // COREAUDIO_ENABLED
|
||||
7
engine/drivers/coremidi/SCsub
Normal file
7
engine/drivers/coremidi/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.mm")
|
||||
68
engine/drivers/coremidi/midi_driver_coremidi.h
Normal file
68
engine/drivers/coremidi/midi_driver_coremidi.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/**************************************************************************/
|
||||
/* midi_driver_coremidi.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef COREMIDI_ENABLED
|
||||
|
||||
#include "core/os/midi_driver.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
#import <CoreMIDI/CoreMIDI.h>
|
||||
#include <cstdio>
|
||||
|
||||
class MIDIDriverCoreMidi : public MIDIDriver {
|
||||
MIDIClientRef client = 0;
|
||||
MIDIPortRef port_in;
|
||||
|
||||
struct InputConnection {
|
||||
InputConnection() = default;
|
||||
InputConnection(int p_device_index, MIDIEndpointRef p_source);
|
||||
Parser parser;
|
||||
MIDIEndpointRef source;
|
||||
};
|
||||
|
||||
Vector<InputConnection *> connected_sources;
|
||||
|
||||
static Mutex mutex;
|
||||
static bool core_midi_closed;
|
||||
|
||||
static void read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con);
|
||||
|
||||
public:
|
||||
virtual Error open() override;
|
||||
virtual void close() override;
|
||||
|
||||
MIDIDriverCoreMidi() = default;
|
||||
virtual ~MIDIDriverCoreMidi();
|
||||
};
|
||||
|
||||
#endif // COREMIDI_ENABLED
|
||||
132
engine/drivers/coremidi/midi_driver_coremidi.mm
Normal file
132
engine/drivers/coremidi/midi_driver_coremidi.mm
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**************************************************************************/
|
||||
/* midi_driver_coremidi.mm */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "midi_driver_coremidi.h"
|
||||
|
||||
#ifdef COREMIDI_ENABLED
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
Mutex MIDIDriverCoreMidi::mutex;
|
||||
bool MIDIDriverCoreMidi::core_midi_closed = false;
|
||||
|
||||
MIDIDriverCoreMidi::InputConnection::InputConnection(int p_device_index, MIDIEndpointRef p_source) :
|
||||
parser(p_device_index), source(p_source) {}
|
||||
|
||||
void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) {
|
||||
MutexLock lock(mutex);
|
||||
if (!core_midi_closed) {
|
||||
InputConnection *source = static_cast<InputConnection *>(src_conn_ref_con);
|
||||
const MIDIPacket *packet = packet_list->packet;
|
||||
for (UInt32 packet_index = 0; packet_index < packet_list->numPackets; packet_index++) {
|
||||
for (UInt16 data_index = 0; data_index < packet->length; data_index++) {
|
||||
source->parser.parse_fragment(packet->data[data_index]);
|
||||
}
|
||||
packet = MIDIPacketNext(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error MIDIDriverCoreMidi::open() {
|
||||
ERR_FAIL_COND_V_MSG(client || core_midi_closed, FAILED,
|
||||
"MIDIDriverCoreMidi cannot be reopened.");
|
||||
|
||||
CFStringRef name = CFStringCreateWithCString(nullptr, "Godot", kCFStringEncodingASCII);
|
||||
OSStatus result = MIDIClientCreate(name, nullptr, nullptr, &client);
|
||||
CFRelease(name);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("MIDIClientCreate failed, code: " + itos(result));
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
result = MIDIInputPortCreate(client, CFSTR("Godot Input"), MIDIDriverCoreMidi::read, (void *)this, &port_in);
|
||||
if (result != noErr) {
|
||||
ERR_PRINT("MIDIInputPortCreate failed, code: " + itos(result));
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
int source_count = MIDIGetNumberOfSources();
|
||||
int connection_index = 0;
|
||||
for (int i = 0; i < source_count; i++) {
|
||||
MIDIEndpointRef source = MIDIGetSource(i);
|
||||
if (source) {
|
||||
InputConnection *conn = memnew(InputConnection(connection_index, source));
|
||||
const OSStatus res = MIDIPortConnectSource(port_in, source, static_cast<void *>(conn));
|
||||
if (res != noErr) {
|
||||
memdelete(conn);
|
||||
} else {
|
||||
connected_sources.push_back(conn);
|
||||
|
||||
CFStringRef nameRef = nullptr;
|
||||
char name[256];
|
||||
MIDIObjectGetStringProperty(source, kMIDIPropertyDisplayName, &nameRef);
|
||||
CFStringGetCString(nameRef, name, sizeof(name), kCFStringEncodingUTF8);
|
||||
CFRelease(nameRef);
|
||||
connected_input_names.push_back(name);
|
||||
|
||||
connection_index++; // Contiguous index for successfully connected inputs.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void MIDIDriverCoreMidi::close() {
|
||||
mutex.lock();
|
||||
core_midi_closed = true;
|
||||
mutex.unlock();
|
||||
|
||||
for (InputConnection *conn : connected_sources) {
|
||||
MIDIPortDisconnectSource(port_in, conn->source);
|
||||
memdelete(conn);
|
||||
}
|
||||
|
||||
connected_sources.clear();
|
||||
connected_input_names.clear();
|
||||
|
||||
if (port_in != 0) {
|
||||
MIDIPortDispose(port_in);
|
||||
port_in = 0;
|
||||
}
|
||||
|
||||
if (client != 0) {
|
||||
MIDIClientDispose(client);
|
||||
client = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MIDIDriverCoreMidi::~MIDIDriverCoreMidi() {
|
||||
close();
|
||||
}
|
||||
|
||||
#endif // COREMIDI_ENABLED
|
||||
188
engine/drivers/d3d12/SCsub
Normal file
188
engine/drivers/d3d12/SCsub
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import methods
|
||||
from methods import print_error
|
||||
|
||||
# Sync with `misc/scripts/install_d3d12_sdk_windows.py` when updating Mesa.
|
||||
# Check for latest version: https://github.com/godotengine/godot-nir-static/releases/latest
|
||||
req_version_major = 25
|
||||
req_version_minor = 3
|
||||
|
||||
Import("env")
|
||||
env_d3d12_rdd = env.Clone()
|
||||
|
||||
# Agility SDK.
|
||||
if env["agility_sdk_path"] != "" and os.path.exists(env["agility_sdk_path"]):
|
||||
env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_ENABLED"])
|
||||
if env["agility_sdk_multiarch"]:
|
||||
env_d3d12_rdd.Append(CPPDEFINES=["AGILITY_SDK_MULTIARCH_ENABLED"])
|
||||
|
||||
# PIX.
|
||||
if env["use_pix"]:
|
||||
env_d3d12_rdd.Append(CPPDEFINES=["PIX_ENABLED"])
|
||||
env_d3d12_rdd.Append(CPPPATH=[env["pix_path"] + "/Include"])
|
||||
|
||||
# Direct composition.
|
||||
if "dcomp" in env.get("supported", []):
|
||||
env_d3d12_rdd.Append(CPPDEFINES=["DCOMP_ENABLED"])
|
||||
env.Append(CPPDEFINES=["DCOMP_ENABLED"]) # Used in header included in platform.
|
||||
|
||||
# Mesa (SPIR-V to DXIL functionality).
|
||||
mesa_libs = env["mesa_libs"]
|
||||
if env.msvc and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-msvc"):
|
||||
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-msvc"
|
||||
elif env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-llvm"):
|
||||
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-llvm"
|
||||
elif not env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-gcc"):
|
||||
mesa_libs = env["mesa_libs"] + "-" + env["arch"] + "-gcc"
|
||||
|
||||
mesa_dir = (mesa_libs + "/godot-mesa").replace("\\", "/")
|
||||
mesa_gen_dir = (mesa_libs + "/godot-mesa/generated").replace("\\", "/")
|
||||
mesa_absdir = Dir(mesa_dir).abspath
|
||||
mesa_gen_absdir = Dir(mesa_dir + "/generated").abspath
|
||||
|
||||
custom_build_steps = [
|
||||
[
|
||||
"src/compiler",
|
||||
"glsl/ir_expression_operation.py enum > %s/ir_expression_operation.h",
|
||||
"ir_expression_operation.h",
|
||||
],
|
||||
["src/compiler/nir", "nir_builder_opcodes_h.py > %s/nir_builder_opcodes.h", "nir_builder_opcodes.h"],
|
||||
["src/compiler/nir", "nir_constant_expressions.py > %s/nir_constant_expressions.c", "nir_constant_expressions.c"],
|
||||
["src/compiler/nir", "nir_intrinsics_h.py --outdir %s", "nir_intrinsics.h"],
|
||||
["src/compiler/nir", "nir_intrinsics_c.py --outdir %s", "nir_intrinsics.c"],
|
||||
["src/compiler/nir", "nir_intrinsics_indices_h.py --outdir %s", "nir_intrinsics_indices.h"],
|
||||
["src/compiler/nir", "nir_opcodes_h.py > %s/nir_opcodes.h", "nir_opcodes.h"],
|
||||
["src/compiler/nir", "nir_opcodes_c.py > %s/nir_opcodes.c", "nir_opcodes.c"],
|
||||
["src/compiler/nir", "nir_opt_algebraic.py > %s/nir_opt_algebraic.c", "nir_opt_algebraic.c"],
|
||||
["src/compiler/spirv", "vtn_generator_ids_h.py spir-v.xml %s/vtn_generator_ids.h", "vtn_generator_ids.h"],
|
||||
[
|
||||
"src/microsoft/compiler",
|
||||
"dxil_nir_algebraic.py -p ../../../src/compiler/nir > %s/dxil_nir_algebraic.c",
|
||||
"dxil_nir_algebraic.c",
|
||||
],
|
||||
["src/util", "format_srgb.py > %s/format_srgb.c", "format_srgb.c"],
|
||||
["src/util/format", "u_format_table.py u_format.csv --header > %s/u_format_pack.h", "u_format_pack.h"],
|
||||
["src/util/format", "u_format_table.py u_format.csv > %s/u_format_table.c", "u_format_table.c"],
|
||||
]
|
||||
|
||||
mesa_gen_include_paths = [mesa_gen_dir + "/src"]
|
||||
|
||||
# See update_mesa.sh for explanation.
|
||||
for v in custom_build_steps:
|
||||
subdir = v[0]
|
||||
cmd = v[1]
|
||||
gen_filename = v[2]
|
||||
|
||||
in_dir = str(Path(mesa_absdir + "/" + subdir))
|
||||
out_full_path = mesa_dir + "/generated/" + subdir
|
||||
out_file_full_path = out_full_path + "/" + gen_filename
|
||||
|
||||
if gen_filename.endswith(".h"):
|
||||
mesa_gen_include_paths += [out_full_path.replace("\\", "/")]
|
||||
|
||||
mesa_private_inc_paths = [v[0] for v in os.walk(mesa_absdir)]
|
||||
mesa_private_inc_paths = [v.replace(mesa_absdir, mesa_dir) for v in mesa_private_inc_paths]
|
||||
mesa_private_inc_paths = [v.replace("\\", "/") for v in mesa_private_inc_paths]
|
||||
# Avoid build results depending on if generated files already exist.
|
||||
mesa_private_inc_paths = [v for v in mesa_private_inc_paths if not v.startswith(mesa_gen_dir)]
|
||||
mesa_private_inc_paths.sort()
|
||||
# Include the list of the generated ones now, so out-of-the-box sources can include generated headers.
|
||||
mesa_private_inc_paths += mesa_gen_include_paths
|
||||
# We have to blacklist some because we are bindly adding every Mesa directory
|
||||
# to the include path and in some cases that causes the wrong header to be included.
|
||||
mesa_blacklist_inc_paths = [
|
||||
"src/c11",
|
||||
]
|
||||
mesa_blacklist_inc_paths = [mesa_dir + "/" + v for v in mesa_blacklist_inc_paths]
|
||||
mesa_private_inc_paths = [v for v in mesa_private_inc_paths if v not in mesa_blacklist_inc_paths]
|
||||
|
||||
# Added by ourselves.
|
||||
extra_defines = [
|
||||
"WINDOWS_NO_FUTEX",
|
||||
]
|
||||
|
||||
mesa_ver = Path(mesa_absdir + "/VERSION.info")
|
||||
if not mesa_ver.is_file():
|
||||
mesa_ver = Path(mesa_absdir + "/VERSION")
|
||||
|
||||
match = re.match(r"([0-9]*).([0-9]*).([0-9]*)-?([0-9.+]*)", mesa_ver.read_text().strip())
|
||||
if (
|
||||
match is None
|
||||
or int(match.group(1)) != req_version_major
|
||||
or (int(match.group(1)) == req_version_major and int(match.group(2)) < req_version_minor)
|
||||
):
|
||||
print_error(
|
||||
"The Direct3D 12 rendering driver dependencies are outdated or missing.\n"
|
||||
f"You can re-install them by running `python {os.path.join('misc', 'scripts', 'install_d3d12_sdk_windows.py')}`.\n"
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
# These defines are inspired by the Meson build scripts in the original repo.
|
||||
extra_defines += [
|
||||
"__STDC_CONSTANT_MACROS",
|
||||
"__STDC_FORMAT_MACROS",
|
||||
"__STDC_LIMIT_MACROS",
|
||||
("PACKAGE_VERSION", '\\"' + mesa_ver.read_text().strip() + '\\"'),
|
||||
("PACKAGE_BUGREPORT", '\\"https://gitlab.freedesktop.org/mesa/mesa/-/issues\\"'),
|
||||
"PIPE_SUBSYSTEM_WINDOWS_USER",
|
||||
("_Static_assert", "static_assert"),
|
||||
"HAVE_STRUCT_TIMESPEC",
|
||||
]
|
||||
|
||||
if env.msvc:
|
||||
extra_defines += [
|
||||
"_USE_MATH_DEFINES",
|
||||
"VC_EXTRALEAN",
|
||||
"_CRT_SECURE_NO_WARNINGS",
|
||||
"_CRT_SECURE_NO_DEPRECATE",
|
||||
"_SCL_SECURE_NO_WARNINGS",
|
||||
"_SCL_SECURE_NO_DEPRECATE",
|
||||
"_ALLOW_KEYWORD_MACROS",
|
||||
"NOMINMAX",
|
||||
]
|
||||
else:
|
||||
extra_defines += [
|
||||
# Match current version used by MinGW. MSVC and Direct3D 12 headers use 500.
|
||||
("__REQUIRED_RPCNDR_H_VERSION__", 475),
|
||||
]
|
||||
if methods.using_gcc(env) and methods.get_compiler_version(env)["major"] < 13:
|
||||
# `region` & `endregion` not recognized as valid pragmas.
|
||||
env_d3d12_rdd.Append(CCFLAGS=["-Wno-unknown-pragmas"])
|
||||
|
||||
# This is needed since rendering_device_d3d12.cpp needs to include some Mesa internals.
|
||||
env_d3d12_rdd.Prepend(CPPPATH=mesa_private_inc_paths)
|
||||
# For the same reason as above, the defines must be the same as in the 3rd-party code itself.
|
||||
env_d3d12_rdd.Append(CPPDEFINES=extra_defines)
|
||||
|
||||
# Thirdparty.
|
||||
thirdparty_obj = []
|
||||
|
||||
env_thirdparty = env_d3d12_rdd.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
|
||||
env_thirdparty.Prepend(
|
||||
CPPPATH=[
|
||||
"#thirdparty/directx_headers/include/directx",
|
||||
"#thirdparty/directx_headers/include/dxguids",
|
||||
"#thirdparty/d3d12ma",
|
||||
]
|
||||
)
|
||||
|
||||
env_thirdparty.add_source_files(thirdparty_obj, "#thirdparty/d3d12ma/D3D12MemAlloc.cpp")
|
||||
env.drivers_sources += thirdparty_obj
|
||||
|
||||
# Godot source files.
|
||||
|
||||
driver_obj = []
|
||||
env_d3d12_rdd.add_source_files(driver_obj, "*.cpp")
|
||||
env.drivers_sources += driver_obj
|
||||
|
||||
# Needed to force rebuilding the driver files when the thirdparty code is updated.
|
||||
env.Depends(driver_obj, thirdparty_obj)
|
||||
61
engine/drivers/d3d12/d3d12_godot_nir_bridge.h
Normal file
61
engine/drivers/d3d12/d3d12_godot_nir_bridge.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/**************************************************************************/
|
||||
/* d3d12_godot_nir_bridge.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// This one leaves room for potentially extremely copious bindings in a set.
|
||||
static const uint32_t GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER = 100000000;
|
||||
// This one leaves room for potentially big sized arrays.
|
||||
static const uint32_t GODOT_NIR_BINDING_MULTIPLIER = 100000;
|
||||
|
||||
static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC = 0x45678900; // This must be as big as to be VBR-ed as a 32 bits number.
|
||||
static const uint64_t GODOT_NIR_SC_SENTINEL_MAGIC_MASK = 0xffffffffffffff00;
|
||||
static const uint64_t GODOT_NIR_SC_SENTINEL_ID_MASK = 0x00000000000000ff;
|
||||
|
||||
typedef struct GodotNirCallbacks {
|
||||
void *data;
|
||||
void (*report_resource)(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data);
|
||||
void (*report_sc_bit_offset_fn)(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data);
|
||||
void (*report_bitcode_bit_offset_fn)(uint64_t p_bit_offset, void *p_data);
|
||||
} GodotNirCallbacks;
|
||||
|
||||
extern void *godot_nir_malloc(size_t p_size);
|
||||
extern void *godot_nir_realloc(void *p_block, size_t p_size);
|
||||
extern void godot_nir_free(void *p_block);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
45
engine/drivers/d3d12/d3d12_hooks.cpp
Normal file
45
engine/drivers/d3d12/d3d12_hooks.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**************************************************************************/
|
||||
/* d3d12_hooks.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "d3d12_hooks.h"
|
||||
|
||||
D3D12Hooks *D3D12Hooks::singleton = nullptr;
|
||||
|
||||
D3D12Hooks::D3D12Hooks() {
|
||||
if (singleton == nullptr) {
|
||||
singleton = this;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12Hooks::~D3D12Hooks() {
|
||||
if (singleton == this) {
|
||||
singleton = nullptr;
|
||||
}
|
||||
}
|
||||
48
engine/drivers/d3d12/d3d12_hooks.h
Normal file
48
engine/drivers/d3d12/d3d12_hooks.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**************************************************************************/
|
||||
/* d3d12_hooks.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rendering_device_driver_d3d12.h"
|
||||
|
||||
class D3D12Hooks {
|
||||
private:
|
||||
static D3D12Hooks *singleton;
|
||||
|
||||
public:
|
||||
D3D12Hooks();
|
||||
virtual ~D3D12Hooks();
|
||||
virtual D3D_FEATURE_LEVEL get_feature_level() const = 0;
|
||||
virtual LUID get_adapter_luid() const = 0;
|
||||
virtual void set_device(ID3D12Device *p_device) = 0;
|
||||
virtual void set_command_queue(ID3D12CommandQueue *p_queue) = 0;
|
||||
virtual void cleanup_device() = 0;
|
||||
static D3D12Hooks *get_singleton() { return singleton; }
|
||||
};
|
||||
209
engine/drivers/d3d12/dxil_hash.cpp
Normal file
209
engine/drivers/d3d12/dxil_hash.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/**************************************************************************/
|
||||
/* dxil_hash.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
// Based on the patched public domain implementation released by Microsoft here:
|
||||
// https://github.com/microsoft/hlsl-specs/blob/main/proposals/infra/INF-0004-validator-hashing.md
|
||||
|
||||
#include "dxil_hash.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
static const BYTE padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static void FF(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += ((b & c) | (~b & d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void GG(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += ((b & d) | (c & ~d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void HH(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += (b ^ c ^ d) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void II(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += (c ^ (b | ~d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash) {
|
||||
UINT leftOver = byteCount & 0x3f;
|
||||
UINT padAmount;
|
||||
bool bTwoRowsPadding = false;
|
||||
if (leftOver < 56) {
|
||||
padAmount = 56 - leftOver;
|
||||
} else {
|
||||
padAmount = 120 - leftOver;
|
||||
bTwoRowsPadding = true;
|
||||
}
|
||||
UINT padAmountPlusSize = padAmount + 8;
|
||||
UINT state[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
|
||||
UINT N = (byteCount + padAmountPlusSize) >> 6;
|
||||
UINT offset = 0;
|
||||
UINT NextEndState = bTwoRowsPadding ? N - 2 : N - 1;
|
||||
const BYTE *pCurrData = pData;
|
||||
for (UINT i = 0; i < N; i++, offset += 64, pCurrData += 64) {
|
||||
UINT x[16] = {};
|
||||
const UINT *pX;
|
||||
if (i == NextEndState) {
|
||||
if (!bTwoRowsPadding && i == N - 1) {
|
||||
UINT remainder = byteCount - offset;
|
||||
x[0] = byteCount << 3;
|
||||
memcpy((BYTE *)x + 4, pCurrData, remainder);
|
||||
memcpy((BYTE *)x + 4 + remainder, padding, padAmount);
|
||||
x[15] = 1 | (byteCount << 1);
|
||||
} else if (bTwoRowsPadding) {
|
||||
if (i == N - 2) {
|
||||
UINT remainder = byteCount - offset;
|
||||
memcpy(x, pCurrData, remainder);
|
||||
memcpy((BYTE *)x + remainder, padding, padAmount - 56);
|
||||
NextEndState = N - 1;
|
||||
} else if (i == N - 1) {
|
||||
x[0] = byteCount << 3;
|
||||
memcpy((BYTE *)x + 4, padding + padAmount - 56, 56);
|
||||
x[15] = 1 | (byteCount << 1);
|
||||
}
|
||||
}
|
||||
pX = x;
|
||||
} else {
|
||||
pX = (const UINT *)pCurrData;
|
||||
}
|
||||
|
||||
UINT a = state[0];
|
||||
UINT b = state[1];
|
||||
UINT c = state[2];
|
||||
UINT d = state[3];
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, pX[0], S11, 0xd76aa478); /* 1 */
|
||||
FF(d, a, b, c, pX[1], S12, 0xe8c7b756); /* 2 */
|
||||
FF(c, d, a, b, pX[2], S13, 0x242070db); /* 3 */
|
||||
FF(b, c, d, a, pX[3], S14, 0xc1bdceee); /* 4 */
|
||||
FF(a, b, c, d, pX[4], S11, 0xf57c0faf); /* 5 */
|
||||
FF(d, a, b, c, pX[5], S12, 0x4787c62a); /* 6 */
|
||||
FF(c, d, a, b, pX[6], S13, 0xa8304613); /* 7 */
|
||||
FF(b, c, d, a, pX[7], S14, 0xfd469501); /* 8 */
|
||||
FF(a, b, c, d, pX[8], S11, 0x698098d8); /* 9 */
|
||||
FF(d, a, b, c, pX[9], S12, 0x8b44f7af); /* 10 */
|
||||
FF(c, d, a, b, pX[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF(b, c, d, a, pX[11], S14, 0x895cd7be); /* 12 */
|
||||
FF(a, b, c, d, pX[12], S11, 0x6b901122); /* 13 */
|
||||
FF(d, a, b, c, pX[13], S12, 0xfd987193); /* 14 */
|
||||
FF(c, d, a, b, pX[14], S13, 0xa679438e); /* 15 */
|
||||
FF(b, c, d, a, pX[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, pX[1], S21, 0xf61e2562); /* 17 */
|
||||
GG(d, a, b, c, pX[6], S22, 0xc040b340); /* 18 */
|
||||
GG(c, d, a, b, pX[11], S23, 0x265e5a51); /* 19 */
|
||||
GG(b, c, d, a, pX[0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG(a, b, c, d, pX[5], S21, 0xd62f105d); /* 21 */
|
||||
GG(d, a, b, c, pX[10], S22, 0x2441453); /* 22 */
|
||||
GG(c, d, a, b, pX[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG(b, c, d, a, pX[4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG(a, b, c, d, pX[9], S21, 0x21e1cde6); /* 25 */
|
||||
GG(d, a, b, c, pX[14], S22, 0xc33707d6); /* 26 */
|
||||
GG(c, d, a, b, pX[3], S23, 0xf4d50d87); /* 27 */
|
||||
GG(b, c, d, a, pX[8], S24, 0x455a14ed); /* 28 */
|
||||
GG(a, b, c, d, pX[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG(d, a, b, c, pX[2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG(c, d, a, b, pX[7], S23, 0x676f02d9); /* 31 */
|
||||
GG(b, c, d, a, pX[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, pX[5], S31, 0xfffa3942); /* 33 */
|
||||
HH(d, a, b, c, pX[8], S32, 0x8771f681); /* 34 */
|
||||
HH(c, d, a, b, pX[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH(b, c, d, a, pX[14], S34, 0xfde5380c); /* 36 */
|
||||
HH(a, b, c, d, pX[1], S31, 0xa4beea44); /* 37 */
|
||||
HH(d, a, b, c, pX[4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH(c, d, a, b, pX[7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH(b, c, d, a, pX[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH(a, b, c, d, pX[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH(d, a, b, c, pX[0], S32, 0xeaa127fa); /* 42 */
|
||||
HH(c, d, a, b, pX[3], S33, 0xd4ef3085); /* 43 */
|
||||
HH(b, c, d, a, pX[6], S34, 0x4881d05); /* 44 */
|
||||
HH(a, b, c, d, pX[9], S31, 0xd9d4d039); /* 45 */
|
||||
HH(d, a, b, c, pX[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH(c, d, a, b, pX[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH(b, c, d, a, pX[2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II(a, b, c, d, pX[0], S41, 0xf4292244); /* 49 */
|
||||
II(d, a, b, c, pX[7], S42, 0x432aff97); /* 50 */
|
||||
II(c, d, a, b, pX[14], S43, 0xab9423a7); /* 51 */
|
||||
II(b, c, d, a, pX[5], S44, 0xfc93a039); /* 52 */
|
||||
II(a, b, c, d, pX[12], S41, 0x655b59c3); /* 53 */
|
||||
II(d, a, b, c, pX[3], S42, 0x8f0ccc92); /* 54 */
|
||||
II(c, d, a, b, pX[10], S43, 0xffeff47d); /* 55 */
|
||||
II(b, c, d, a, pX[1], S44, 0x85845dd1); /* 56 */
|
||||
II(a, b, c, d, pX[8], S41, 0x6fa87e4f); /* 57 */
|
||||
II(d, a, b, c, pX[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II(c, d, a, b, pX[6], S43, 0xa3014314); /* 59 */
|
||||
II(b, c, d, a, pX[13], S44, 0x4e0811a1); /* 60 */
|
||||
II(a, b, c, d, pX[4], S41, 0xf7537e82); /* 61 */
|
||||
II(d, a, b, c, pX[11], S42, 0xbd3af235); /* 62 */
|
||||
II(c, d, a, b, pX[2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II(b, c, d, a, pX[9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
}
|
||||
|
||||
memcpy(pOutHash, state, 16);
|
||||
}
|
||||
36
engine/drivers/d3d12/dxil_hash.h
Normal file
36
engine/drivers/d3d12/dxil_hash.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**************************************************************************/
|
||||
/* dxil_hash.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash);
|
||||
405
engine/drivers/d3d12/rendering_context_driver_d3d12.cpp
Normal file
405
engine/drivers/d3d12/rendering_context_driver_d3d12.cpp
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_context_driver_d3d12.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "rendering_context_driver_d3d12.h"
|
||||
|
||||
#include "d3d12_hooks.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/version.h"
|
||||
|
||||
GODOT_GCC_WARNING_PUSH
|
||||
GODOT_GCC_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wshadow")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wswitch")
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wstring-plus-int")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wswitch")
|
||||
|
||||
#include <dxcapi.h>
|
||||
#include <dxgi1_6.h>
|
||||
|
||||
GODOT_GCC_WARNING_POP
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
#include <guiddef.h>
|
||||
|
||||
#include <thirdparty/directx_headers/include/dxguids/dxguids.h>
|
||||
#endif
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
// Note: symbols are not available in MinGW and old MSVC import libraries.
|
||||
// GUID values from https://github.com/microsoft/DirectX-Headers/blob/7a9f4d06911d30eecb56a4956dab29dcca2709ed/include/directx/d3d12.idl#L5877-L5881
|
||||
const GUID CLSID_D3D12DebugGodot = { 0xf2352aeb, 0xdd84, 0x49fe, { 0xb9, 0x7b, 0xa9, 0xdc, 0xfd, 0xcc, 0x1b, 0x4f } };
|
||||
const GUID CLSID_D3D12SDKConfigurationGodot = { 0x7cda6aca, 0xa03e, 0x49c8, { 0x94, 0x58, 0x03, 0x34, 0xd2, 0x0e, 0x07, 0xce } };
|
||||
|
||||
#ifdef PIX_ENABLED
|
||||
#if defined(__GNUC__)
|
||||
#define _MSC_VER 1800
|
||||
#endif
|
||||
#define USE_PIX
|
||||
#include <WinPixEventRuntime/pix3.h>
|
||||
#if defined(__GNUC__)
|
||||
#undef _MSC_VER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RenderingContextDriverD3D12::RenderingContextDriverD3D12() {}
|
||||
|
||||
RenderingContextDriverD3D12::~RenderingContextDriverD3D12() {
|
||||
// Let's release manually everything that may still be holding
|
||||
// onto the DLLs before freeing them.
|
||||
device_factory.Reset();
|
||||
dxgi_factory.Reset();
|
||||
|
||||
if (lib_d3d12) {
|
||||
FreeLibrary(lib_d3d12);
|
||||
}
|
||||
if (lib_dxgi) {
|
||||
FreeLibrary(lib_dxgi);
|
||||
}
|
||||
#ifdef DCOMP_ENABLED
|
||||
if (lib_dcomp) {
|
||||
FreeLibrary(lib_dcomp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Error RenderingContextDriverD3D12::_init_device_factory() {
|
||||
uint32_t agility_sdk_version = GLOBAL_GET("rendering/rendering_device/d3d12/agility_sdk_version");
|
||||
String agility_sdk_path = String(".\\") + Engine::get_singleton()->get_architecture_name();
|
||||
|
||||
lib_d3d12 = LoadLibraryW(L"D3D12.dll");
|
||||
ERR_FAIL_NULL_V(lib_d3d12, ERR_CANT_CREATE);
|
||||
|
||||
lib_dxgi = LoadLibraryW(L"DXGI.dll");
|
||||
ERR_FAIL_NULL_V(lib_dxgi, ERR_CANT_CREATE);
|
||||
|
||||
#ifdef DCOMP_ENABLED
|
||||
lib_dcomp = LoadLibraryW(L"Dcomp.dll");
|
||||
ERR_FAIL_NULL_V(lib_dcomp, ERR_CANT_CREATE);
|
||||
#endif
|
||||
|
||||
// Note: symbol is not available in MinGW import library.
|
||||
PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetInterface");
|
||||
if (!d3d_D3D12GetInterface) {
|
||||
return OK; // Fallback to the system loader.
|
||||
}
|
||||
|
||||
ComPtr<ID3D12SDKConfiguration1> sdk_config;
|
||||
HRESULT hr = d3d_D3D12GetInterface(CLSID_D3D12SDKConfigurationGodot, IID_PPV_ARGS(sdk_config.GetAddressOf()));
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = sdk_config->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf()));
|
||||
if (FAILED(hr)) {
|
||||
sdk_config->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf()));
|
||||
}
|
||||
// If both calls failed, device factory is going to be nullptr, and D3D12CreateDevice is going to be used as fallback.
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingContextDriverD3D12::_initialize_debug_layers() {
|
||||
ComPtr<ID3D12Debug> debug_controller;
|
||||
HRESULT res;
|
||||
|
||||
if (device_factory) {
|
||||
res = device_factory->GetConfigurationInterface(CLSID_D3D12DebugGodot, IID_PPV_ARGS(&debug_controller));
|
||||
} else {
|
||||
PFN_D3D12_GET_DEBUG_INTERFACE d3d_D3D12GetDebugInterface = (PFN_D3D12_GET_DEBUG_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetDebugInterface");
|
||||
ERR_FAIL_NULL_V(d3d_D3D12GetDebugInterface, ERR_CANT_CREATE);
|
||||
|
||||
res = d3d_D3D12GetDebugInterface(IID_PPV_ARGS(&debug_controller));
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_QUERY_FAILED);
|
||||
debug_controller->EnableDebugLayer();
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingContextDriverD3D12::_create_dxgi_factory() {
|
||||
const UINT dxgi_factory_flags = use_validation_layers() ? DXGI_CREATE_FACTORY_DEBUG : 0;
|
||||
|
||||
typedef HRESULT(WINAPI * PFN_DXGI_CREATE_DXGI_FACTORY2)(UINT, REFIID, void **);
|
||||
PFN_DXGI_CREATE_DXGI_FACTORY2 dxgi_CreateDXGIFactory2 = (PFN_DXGI_CREATE_DXGI_FACTORY2)(void *)GetProcAddress(lib_dxgi, "CreateDXGIFactory2");
|
||||
ERR_FAIL_NULL_V(dxgi_CreateDXGIFactory2, ERR_CANT_CREATE);
|
||||
|
||||
HRESULT res = dxgi_CreateDXGIFactory2(dxgi_factory_flags, IID_PPV_ARGS(&dxgi_factory));
|
||||
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error RenderingContextDriverD3D12::_initialize_devices() {
|
||||
// Create the initial DXGI factory.
|
||||
Error err = _create_dxgi_factory();
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
|
||||
HRESULT res;
|
||||
|
||||
// Enumerate all possible adapters.
|
||||
LocalVector<IDXGIAdapter1 *> adapters;
|
||||
IDXGIAdapter1 *adapter = nullptr;
|
||||
do {
|
||||
adapter = create_adapter(adapters.size());
|
||||
if (adapter != nullptr) {
|
||||
adapters.push_back(adapter);
|
||||
}
|
||||
} while (adapter != nullptr);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(adapters.is_empty(), ERR_CANT_CREATE, "Adapters enumeration reported zero accessible devices.");
|
||||
|
||||
// Fill the device descriptions with the adapters.
|
||||
driver_devices.resize(adapters.size());
|
||||
for (uint32_t i = 0; i < adapters.size(); ++i) {
|
||||
DXGI_ADAPTER_DESC1 desc = {};
|
||||
adapters[i]->GetDesc1(&desc);
|
||||
|
||||
Device &device = driver_devices[i];
|
||||
device.name = desc.Description;
|
||||
device.vendor = desc.VendorId;
|
||||
device.workarounds = Workarounds();
|
||||
|
||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
|
||||
device.type = DEVICE_TYPE_CPU;
|
||||
} else {
|
||||
const bool has_dedicated_vram = desc.DedicatedVideoMemory > 0;
|
||||
device.type = has_dedicated_vram ? DEVICE_TYPE_DISCRETE_GPU : DEVICE_TYPE_INTEGRATED_GPU;
|
||||
}
|
||||
}
|
||||
|
||||
// Release all created adapters.
|
||||
for (uint32_t i = 0; i < adapters.size(); ++i) {
|
||||
adapters[i]->Release();
|
||||
}
|
||||
|
||||
ComPtr<IDXGIFactory5> factory_5;
|
||||
dxgi_factory.As(&factory_5);
|
||||
if (factory_5 != nullptr) {
|
||||
// The type is important as in general, sizeof(bool) != sizeof(BOOL).
|
||||
BOOL feature_supported = FALSE;
|
||||
res = factory_5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &feature_supported, sizeof(feature_supported));
|
||||
if (SUCCEEDED(res)) {
|
||||
tearing_supported = feature_supported;
|
||||
} else {
|
||||
ERR_PRINT("CheckFeatureSupport failed with error " + vformat("0x%08ux", (uint64_t)res) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::use_validation_layers() const {
|
||||
return Engine::get_singleton()->is_validation_layers_enabled();
|
||||
}
|
||||
|
||||
Error RenderingContextDriverD3D12::initialize() {
|
||||
Error err = _init_device_factory();
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
|
||||
if (use_validation_layers()) {
|
||||
err = _initialize_debug_layers();
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
}
|
||||
|
||||
err = _initialize_devices();
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
const RenderingContextDriver::Device &RenderingContextDriverD3D12::device_get(uint32_t p_device_index) const {
|
||||
DEV_ASSERT(p_device_index < driver_devices.size());
|
||||
return driver_devices[p_device_index];
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverD3D12::device_get_count() const {
|
||||
return driver_devices.size();
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const {
|
||||
// All devices should support presenting to any surface.
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderingDeviceDriver *RenderingContextDriverD3D12::driver_create() {
|
||||
return memnew(RenderingDeviceDriverD3D12(this));
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::driver_free(RenderingDeviceDriver *p_driver) {
|
||||
memdelete(p_driver);
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverD3D12::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->hwnd = wpd->window;
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->width = p_width;
|
||||
surface->height = p_height;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_vsync_mode(SurfaceID p_surface, DisplayServerEnums::VSyncMode p_vsync_mode) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->vsync_mode = p_vsync_mode;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
DisplayServerEnums::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->vsync_mode;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->hdr_output = p_enabled;
|
||||
surface->needs_resize = true;
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->hdr_output;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->hdr_reference_luminance = p_reference_luminance;
|
||||
}
|
||||
|
||||
float RenderingContextDriverD3D12::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->hdr_reference_luminance;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->hdr_max_luminance = p_max_luminance;
|
||||
}
|
||||
|
||||
float RenderingContextDriverD3D12::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->hdr_max_luminance;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_hdr_output_linear_luminance_scale(SurfaceID p_surface, float p_linear_luminance_scale) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->hdr_linear_luminance_scale = p_linear_luminance_scale;
|
||||
}
|
||||
|
||||
float RenderingContextDriverD3D12::surface_get_hdr_output_linear_luminance_scale(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->hdr_linear_luminance_scale;
|
||||
}
|
||||
|
||||
float RenderingContextDriverD3D12::surface_get_hdr_output_max_value(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return MAX(surface->hdr_max_luminance / MAX(surface->hdr_reference_luminance, 1.0f), 1.0f);
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverD3D12::surface_get_width(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->width;
|
||||
}
|
||||
|
||||
uint32_t RenderingContextDriverD3D12::surface_get_height(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->height;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
surface->needs_resize = p_needs_resize;
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::surface_get_needs_resize(SurfaceID p_surface) const {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
return surface->needs_resize;
|
||||
}
|
||||
|
||||
void RenderingContextDriverD3D12::surface_destroy(SurfaceID p_surface) {
|
||||
Surface *surface = (Surface *)(p_surface);
|
||||
memdelete(surface);
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::is_debug_utils_enabled() const {
|
||||
#ifdef PIX_ENABLED
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
IDXGIAdapter1 *RenderingContextDriverD3D12::create_adapter(uint32_t p_adapter_index) const {
|
||||
ComPtr<IDXGIFactory6> factory_6;
|
||||
dxgi_factory.As(&factory_6);
|
||||
|
||||
// TODO: Use IDXCoreAdapterList, which gives more comprehensive information.
|
||||
IDXGIAdapter1 *adapter = nullptr;
|
||||
if (factory_6) {
|
||||
if (factory_6->EnumAdapterByGpuPreference(p_adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)) == DXGI_ERROR_NOT_FOUND) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (dxgi_factory->EnumAdapters1(p_adapter_index, &adapter) == DXGI_ERROR_NOT_FOUND) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
|
||||
ID3D12DeviceFactory *RenderingContextDriverD3D12::device_factory_get() const {
|
||||
return device_factory.Get();
|
||||
}
|
||||
|
||||
IDXGIFactory2 *RenderingContextDriverD3D12::dxgi_factory_get() {
|
||||
// Check if the factory is still current. It can become invalid if displays change
|
||||
if (dxgi_factory && !dxgi_factory->IsCurrent()) {
|
||||
dxgi_factory.Reset();
|
||||
_create_dxgi_factory();
|
||||
}
|
||||
return dxgi_factory.Get();
|
||||
}
|
||||
|
||||
bool RenderingContextDriverD3D12::get_tearing_supported() const {
|
||||
return tearing_supported;
|
||||
}
|
||||
155
engine/drivers/d3d12/rendering_context_driver_d3d12.h
Normal file
155
engine/drivers/d3d12/rendering_context_driver_d3d12.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_context_driver_d3d12.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "rendering_device_driver_d3d12.h"
|
||||
#include "servers/display/display_server_enums.h"
|
||||
#include "servers/rendering/rendering_context_driver.h"
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__REQUIRED_RPCNDR_H_VERSION__)
|
||||
// Match current version used by MinGW, MSVC and Direct3D 12 headers use 500.
|
||||
#define __REQUIRED_RPCNDR_H_VERSION__ 475
|
||||
#endif // !defined(_MSC_VER) && !defined(__REQUIRED_RPCNDR_H_VERSION__)
|
||||
|
||||
GODOT_GCC_WARNING_PUSH
|
||||
GODOT_GCC_WARNING_IGNORE("-Wimplicit-fallthrough")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wshadow")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wswitch")
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wimplicit-fallthrough")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wstring-plus-int")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wswitch")
|
||||
|
||||
#include <thirdparty/directx_headers/include/directx/d3dx12.h>
|
||||
|
||||
GODOT_GCC_WARNING_POP
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#if defined(AS)
|
||||
#undef AS
|
||||
#endif
|
||||
|
||||
#ifdef DCOMP_ENABLED
|
||||
#include <dcomp.h>
|
||||
#endif
|
||||
|
||||
#include <wrl/client.h>
|
||||
|
||||
#define ARRAY_SIZE(a) std_size(a)
|
||||
|
||||
class RenderingContextDriverD3D12 : public RenderingContextDriver {
|
||||
Microsoft::WRL::ComPtr<ID3D12DeviceFactory> device_factory;
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
|
||||
TightLocalVector<Device> driver_devices;
|
||||
bool tearing_supported = false;
|
||||
|
||||
Error _init_device_factory();
|
||||
Error _initialize_debug_layers();
|
||||
Error _create_dxgi_factory();
|
||||
Error _initialize_devices();
|
||||
|
||||
public:
|
||||
virtual Error initialize() override;
|
||||
virtual const Device &device_get(uint32_t p_device_index) const override;
|
||||
virtual uint32_t device_get_count() const override;
|
||||
virtual bool device_supports_present(uint32_t p_device_index, SurfaceID p_surface) const override;
|
||||
virtual RenderingDeviceDriver *driver_create() override;
|
||||
virtual void driver_free(RenderingDeviceDriver *p_driver) override;
|
||||
virtual SurfaceID surface_create(const void *p_platform_data) override;
|
||||
virtual void surface_set_size(SurfaceID p_surface, uint32_t p_width, uint32_t p_height) override;
|
||||
virtual void surface_set_vsync_mode(SurfaceID p_surface, DisplayServerEnums::VSyncMode p_vsync_mode) override;
|
||||
virtual DisplayServerEnums::VSyncMode surface_get_vsync_mode(SurfaceID p_surface) const override;
|
||||
virtual void surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) override;
|
||||
virtual bool surface_get_hdr_output_enabled(SurfaceID p_surface) const override;
|
||||
virtual void surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) override;
|
||||
virtual float surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const override;
|
||||
virtual void surface_set_hdr_output_max_luminance(SurfaceID p_surface, float p_max_luminance) override;
|
||||
virtual float surface_get_hdr_output_max_luminance(SurfaceID p_surface) const override;
|
||||
virtual void surface_set_hdr_output_linear_luminance_scale(SurfaceID p_surface, float p_linear_luminance_scale) override;
|
||||
virtual float surface_get_hdr_output_linear_luminance_scale(SurfaceID p_surface) const override;
|
||||
virtual float surface_get_hdr_output_max_value(SurfaceID p_surface) const override;
|
||||
virtual uint32_t surface_get_width(SurfaceID p_surface) const override;
|
||||
virtual uint32_t surface_get_height(SurfaceID p_surface) const override;
|
||||
virtual void surface_set_needs_resize(SurfaceID p_surface, bool p_needs_resize) override;
|
||||
virtual bool surface_get_needs_resize(SurfaceID p_surface) const override;
|
||||
virtual void surface_destroy(SurfaceID p_surface) override;
|
||||
virtual bool is_debug_utils_enabled() const override;
|
||||
|
||||
// Platform-specific data for the Windows embedded in this driver.
|
||||
struct WindowPlatformData {
|
||||
HWND window;
|
||||
};
|
||||
|
||||
// D3D12-only methods.
|
||||
struct Surface {
|
||||
HWND hwnd = nullptr;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
DisplayServerEnums::VSyncMode vsync_mode = DisplayServerEnums::VSYNC_ENABLED;
|
||||
bool needs_resize = false;
|
||||
|
||||
bool hdr_output = false;
|
||||
// BT.2408 recommendation of 203 nits for HDR Reference White, rounded to 200
|
||||
// to be a more pleasant player-facing value. This value is used by Steam
|
||||
// Deck and other Windows emulation that does not provide an SDRWhiteLevel.
|
||||
float hdr_reference_luminance = 200.0f;
|
||||
float hdr_max_luminance = 1000.0f;
|
||||
float hdr_linear_luminance_scale = 80.0f;
|
||||
|
||||
#ifdef DCOMP_ENABLED
|
||||
Microsoft::WRL::ComPtr<IDCompositionDevice> composition_device;
|
||||
Microsoft::WRL::ComPtr<IDCompositionTarget> composition_target;
|
||||
Microsoft::WRL::ComPtr<IDCompositionVisual> composition_visual;
|
||||
#endif
|
||||
};
|
||||
|
||||
HMODULE lib_d3d12 = nullptr;
|
||||
HMODULE lib_dxgi = nullptr;
|
||||
#ifdef DCOMP_ENABLED
|
||||
HMODULE lib_dcomp = nullptr;
|
||||
#endif
|
||||
|
||||
IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const;
|
||||
ID3D12DeviceFactory *device_factory_get() const;
|
||||
IDXGIFactory2 *dxgi_factory_get();
|
||||
bool get_tearing_supported() const;
|
||||
bool use_validation_layers() const;
|
||||
|
||||
RenderingContextDriverD3D12();
|
||||
virtual ~RenderingContextDriverD3D12() override;
|
||||
};
|
||||
6429
engine/drivers/d3d12/rendering_device_driver_d3d12.cpp
Normal file
6429
engine/drivers/d3d12/rendering_device_driver_d3d12.cpp
Normal file
File diff suppressed because it is too large
Load diff
981
engine/drivers/d3d12/rendering_device_driver_d3d12.h
Normal file
981
engine/drivers/d3d12/rendering_device_driver_d3d12.h
Normal file
|
|
@ -0,0 +1,981 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_device_driver_d3d12.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/a_hash_map.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "rendering_shader_container_d3d12.h"
|
||||
#include "servers/rendering/rendering_device_driver.h"
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__REQUIRED_RPCNDR_H_VERSION__)
|
||||
// Match current version used by MinGW, MSVC and Direct3D 12 headers use 500.
|
||||
#define __REQUIRED_RPCNDR_H_VERSION__ 475
|
||||
#endif // !defined(_MSC_VER) && !defined(__REQUIRED_RPCNDR_H_VERSION__)
|
||||
|
||||
GODOT_GCC_WARNING_PUSH
|
||||
GODOT_GCC_WARNING_IGNORE("-Wimplicit-fallthrough")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wshadow")
|
||||
GODOT_GCC_WARNING_IGNORE("-Wswitch")
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wimplicit-fallthrough")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wmissing-field-initializers")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wstring-plus-int")
|
||||
GODOT_CLANG_WARNING_IGNORE("-Wswitch")
|
||||
|
||||
#include <thirdparty/directx_headers/include/directx/d3dx12.h>
|
||||
|
||||
GODOT_GCC_WARNING_POP
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#include <wrl/client.h>
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
#define CUSTOM_INFO_QUEUE_ENABLED 0
|
||||
#endif
|
||||
|
||||
class RenderingContextDriverD3D12;
|
||||
|
||||
namespace D3D12MA {
|
||||
class Allocation;
|
||||
class Allocator;
|
||||
class VirtualBlock;
|
||||
}; // namespace D3D12MA
|
||||
|
||||
struct IDXGIAdapter;
|
||||
struct IDXGISwapChain3;
|
||||
|
||||
// Design principles:
|
||||
// - D3D12 structs are zero-initialized and fields not requiring a non-zero value are omitted (except in cases where expresivity reasons apply).
|
||||
class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
|
||||
/*****************/
|
||||
/**** GENERIC ****/
|
||||
/*****************/
|
||||
|
||||
struct D3D12Format {
|
||||
DXGI_FORMAT family = DXGI_FORMAT_UNKNOWN;
|
||||
DXGI_FORMAT general_format = DXGI_FORMAT_UNKNOWN;
|
||||
UINT swizzle = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
DXGI_FORMAT dsv_format = DXGI_FORMAT_UNKNOWN;
|
||||
};
|
||||
|
||||
static const D3D12Format RD_TO_D3D12_FORMAT[RDD::DATA_FORMAT_MAX];
|
||||
static const DXGI_COLOR_SPACE_TYPE RD_TO_DXGI_COLOR_SPACE_TYPE[RDD::COLOR_SPACE_MAX];
|
||||
|
||||
struct DeviceLimits {
|
||||
uint64_t max_srvs_per_shader_stage = 0;
|
||||
uint64_t max_cbvs_per_shader_stage = 0;
|
||||
uint64_t max_samplers_across_all_stages = 0;
|
||||
uint64_t max_uavs_across_all_stages = 0;
|
||||
uint64_t timestamp_frequency = 0;
|
||||
};
|
||||
|
||||
struct SubgroupCapabilities {
|
||||
uint32_t size = 0;
|
||||
bool wave_ops_supported = false;
|
||||
uint32_t supported_stages_flags_rd() const;
|
||||
uint32_t supported_operations_flags_rd() const;
|
||||
};
|
||||
|
||||
struct ShaderCapabilities {
|
||||
D3D_SHADER_MODEL shader_model = (D3D_SHADER_MODEL)0;
|
||||
bool native_16bit_ops = false;
|
||||
};
|
||||
|
||||
struct FormatCapabilities {
|
||||
bool relaxed_casting_supported = false;
|
||||
};
|
||||
|
||||
struct BarrierCapabilities {
|
||||
bool enhanced_barriers_supported = false;
|
||||
};
|
||||
|
||||
struct MiscFeaturesSupport {
|
||||
bool depth_bounds_supported = false;
|
||||
};
|
||||
|
||||
struct SamplerCapabilities {
|
||||
bool aniso_filter_with_point_mip_supported = false;
|
||||
};
|
||||
|
||||
RenderingContextDriverD3D12 *context_driver = nullptr;
|
||||
RenderingContextDriver::Device context_device;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
|
||||
Microsoft::WRL::ComPtr<ID3D12Device> device;
|
||||
DeviceLimits device_limits;
|
||||
RDD::Capabilities device_capabilities;
|
||||
uint32_t feature_level = 0; // Major * 10 + minor.
|
||||
SubgroupCapabilities subgroup_capabilities;
|
||||
RDD::MultiviewCapabilities multiview_capabilities;
|
||||
FragmentShadingRateCapabilities fsr_capabilities;
|
||||
FragmentDensityMapCapabilities fdm_capabilities;
|
||||
ShaderCapabilities shader_capabilities;
|
||||
FormatCapabilities format_capabilities;
|
||||
BarrierCapabilities barrier_capabilities;
|
||||
MiscFeaturesSupport misc_features_support;
|
||||
SamplerCapabilities sampler_capabilities;
|
||||
RenderingShaderContainerFormatD3D12 shader_container_format;
|
||||
String pipeline_cache_id;
|
||||
D3D12_HEAP_TYPE dynamic_persistent_upload_heap = D3D12_HEAP_TYPE_UPLOAD;
|
||||
|
||||
struct DescriptorHeap {
|
||||
struct Allocation {
|
||||
uint64_t virtual_alloc_handle = {}; // This is the handle value in "D3D12MA::VirtualAllocation".
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle = {};
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle = {};
|
||||
};
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> heap;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle = {};
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle = {};
|
||||
uint32_t increment_size = 0;
|
||||
|
||||
Microsoft::WRL::ComPtr<D3D12MA::VirtualBlock> virtual_block;
|
||||
|
||||
Error initialize(ID3D12Device *p_device, D3D12_DESCRIPTOR_HEAP_TYPE p_type, uint32_t p_num_descriptors, bool p_shader_visible);
|
||||
|
||||
Error allocate(uint32_t p_descriptor_count, Allocation &r_allocation);
|
||||
void free(const Allocation &p_allocation);
|
||||
};
|
||||
|
||||
// Some IHVs do not allow creating descriptor heaps beyond a certain limit, so they must be pooled.
|
||||
struct CPUDescriptorHeapPool {
|
||||
struct Allocation : DescriptorHeap::Allocation {
|
||||
uint32_t heap_index = UINT_MAX;
|
||||
};
|
||||
|
||||
BinaryMutex mutex;
|
||||
LocalVector<DescriptorHeap> heaps;
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_TYPE type = {};
|
||||
uint32_t increment_size = 0;
|
||||
|
||||
void initialize(ID3D12Device *p_device, D3D12_DESCRIPTOR_HEAP_TYPE p_type);
|
||||
|
||||
Error allocate(uint32_t p_descriptor_count, ID3D12Device *p_device, Allocation &r_allocation);
|
||||
void free(const Allocation &p_allocation);
|
||||
};
|
||||
|
||||
DescriptorHeap resource_descriptor_heap;
|
||||
DescriptorHeap sampler_descriptor_heap;
|
||||
CPUDescriptorHeapPool resource_descriptor_heap_pool;
|
||||
CPUDescriptorHeapPool rtv_descriptor_heap_pool;
|
||||
CPUDescriptorHeapPool dsv_descriptor_heap_pool;
|
||||
|
||||
CPUDescriptorHeapPool::Allocation null_rtv_alloc;
|
||||
|
||||
struct {
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandSignature> draw;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandSignature> draw_indexed;
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandSignature> dispatch;
|
||||
} indirect_cmd_signatures;
|
||||
|
||||
static void STDMETHODCALLTYPE _debug_message_func(D3D12_MESSAGE_CATEGORY p_category, D3D12_MESSAGE_SEVERITY p_severity, D3D12_MESSAGE_ID p_id, LPCSTR p_description, void *p_context);
|
||||
void _set_object_name(ID3D12Object *p_object, String p_object_name);
|
||||
Error _initialize_device();
|
||||
Error _check_capabilities();
|
||||
Error _get_device_limits();
|
||||
Error _initialize_allocator();
|
||||
Error _initialize_frames(uint32_t p_frame_count);
|
||||
Error _initialize_command_signatures();
|
||||
|
||||
public:
|
||||
Error initialize(uint32_t p_device_index, uint32_t p_frame_count) override final;
|
||||
|
||||
private:
|
||||
/****************/
|
||||
/**** MEMORY ****/
|
||||
/****************/
|
||||
|
||||
Microsoft::WRL::ComPtr<D3D12MA::Allocator> allocator;
|
||||
|
||||
/******************/
|
||||
/**** RESOURCE ****/
|
||||
/******************/
|
||||
|
||||
struct ResourceInfo {
|
||||
struct States {
|
||||
// As many subresources as mipmaps * layers; planes (for depth-stencil) are tracked together.
|
||||
TightLocalVector<D3D12_RESOURCE_STATES> subresource_states; // Used only if not a view.
|
||||
uint32_t last_batch_with_uav_barrier = 0;
|
||||
};
|
||||
|
||||
ID3D12Resource *resource = nullptr; // Non-null even if not owned.
|
||||
struct {
|
||||
Microsoft::WRL::ComPtr<ID3D12Resource> resource;
|
||||
Microsoft::WRL::ComPtr<D3D12MA::Allocation> allocation;
|
||||
States states;
|
||||
} owner_info; // All empty if the resource is not owned.
|
||||
States *states_ptr = nullptr; // Own or from another if it doesn't own the D3D12 resource.
|
||||
};
|
||||
|
||||
struct BarrierRequest {
|
||||
static const uint32_t MAX_GROUPS = 4;
|
||||
// Maybe this is too much data to have it locally. Benchmarking may reveal that
|
||||
// cache would be used better by having a maximum of local subresource masks and beyond
|
||||
// that have an allocated vector with the rest.
|
||||
static const uint32_t MAX_SUBRESOURCES = 4096;
|
||||
ID3D12Resource *dx_resource = nullptr;
|
||||
uint8_t subres_mask_qwords = 0;
|
||||
uint8_t planes = 0;
|
||||
struct Group {
|
||||
D3D12_RESOURCE_STATES states = {};
|
||||
static_assert(MAX_SUBRESOURCES % 64 == 0);
|
||||
uint64_t subres_mask[MAX_SUBRESOURCES / 64] = {};
|
||||
} groups[MAX_GROUPS];
|
||||
uint8_t groups_count = 0;
|
||||
static const D3D12_RESOURCE_STATES DELETED_GROUP = D3D12_RESOURCE_STATES(0xFFFFFFFFU);
|
||||
};
|
||||
|
||||
struct CommandBufferInfo;
|
||||
|
||||
void _resource_transition_batch(CommandBufferInfo *p_command_buffer, ResourceInfo *p_resource, uint32_t p_subresource, uint32_t p_num_planes, D3D12_RESOURCE_STATES p_new_state);
|
||||
void _resource_transitions_flush(CommandBufferInfo *p_command_buffer);
|
||||
|
||||
/*****************/
|
||||
/**** BUFFERS ****/
|
||||
/*****************/
|
||||
|
||||
struct BufferInfo : public ResourceInfo {
|
||||
D3D12_GPU_VIRTUAL_ADDRESS gpu_virtual_address = {};
|
||||
DataFormat texel_format = DATA_FORMAT_MAX;
|
||||
uint64_t size = 0;
|
||||
struct {
|
||||
bool is_dynamic : 1; // Only used for tracking (e.g. Vulkan needs these checks).
|
||||
} flags = {};
|
||||
|
||||
bool is_dynamic() const { return flags.is_dynamic; }
|
||||
};
|
||||
|
||||
struct BufferDynamicInfo : BufferInfo {
|
||||
uint32_t frame_idx = UINT32_MAX;
|
||||
uint8_t *persistent_ptr = nullptr;
|
||||
#ifdef DEBUG_ENABLED
|
||||
// For tracking that a persistent buffer isn't mapped twice in the same frame.
|
||||
uint64_t last_frame_mapped = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type, uint64_t p_frames_drawn) override final;
|
||||
virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) override final;
|
||||
virtual void buffer_free(BufferID p_buffer) override final;
|
||||
virtual uint64_t buffer_get_allocation_size(BufferID p_buffer) override final;
|
||||
virtual uint8_t *buffer_map(BufferID p_buffer) override final;
|
||||
virtual void buffer_unmap(BufferID p_buffer) override final;
|
||||
virtual uint8_t *buffer_persistent_map_advance(BufferID p_buffer, uint64_t p_frames_drawn) override final;
|
||||
virtual uint64_t buffer_get_dynamic_offsets(Span<BufferID> p_buffers) override final;
|
||||
virtual uint64_t buffer_get_device_address(BufferID p_buffer) override final;
|
||||
|
||||
/*****************/
|
||||
/**** TEXTURE ****/
|
||||
/*****************/
|
||||
private:
|
||||
struct TextureInfo : public ResourceInfo {
|
||||
DataFormat format = DATA_FORMAT_MAX;
|
||||
CD3DX12_RESOURCE_DESC desc = {};
|
||||
uint32_t base_layer = 0;
|
||||
uint32_t layers = 0;
|
||||
uint32_t base_mip = 0;
|
||||
uint32_t mipmaps = 0;
|
||||
|
||||
struct {
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv;
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC uav;
|
||||
} view_descs = {};
|
||||
|
||||
TextureInfo *main_texture = nullptr;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool created_from_extension = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
HashMap<DXGI_FORMAT, uint32_t> format_sample_counts_mask_cache;
|
||||
Mutex format_sample_counts_mask_cache_mutex;
|
||||
|
||||
uint32_t _find_max_common_supported_sample_count(VectorView<DXGI_FORMAT> p_formats);
|
||||
UINT _compute_component_mapping(const TextureView &p_view);
|
||||
UINT _compute_plane_slice(DataFormat p_format, BitField<TextureAspectBits> p_aspect_bits);
|
||||
UINT _compute_plane_slice(DataFormat p_format, TextureAspect p_aspect);
|
||||
UINT _compute_subresource_from_layers(TextureInfo *p_texture, const TextureSubresourceLayers &p_layers, uint32_t p_layer_offset);
|
||||
|
||||
void _discard_texture_subresources(const TextureInfo *p_tex_info, const CommandBufferInfo *p_cmd_buf_info);
|
||||
|
||||
protected:
|
||||
virtual bool _unordered_access_supported_by_format(DataFormat p_format);
|
||||
|
||||
public:
|
||||
virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_from_extension(uint64_t p_native_texture, TextureType p_type, DataFormat p_format, uint32_t p_array_layers, bool p_depth_stencil, uint32_t p_mipmaps) override final;
|
||||
virtual TextureID texture_create_shared(TextureID p_original_texture, const TextureView &p_view) override final;
|
||||
virtual TextureID texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps) override final;
|
||||
virtual void texture_free(TextureID p_texture) override final;
|
||||
virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final;
|
||||
virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final;
|
||||
virtual Vector<uint8_t> texture_get_data(TextureID p_texture, uint32_t p_layer) override final;
|
||||
virtual BitField<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;
|
||||
virtual bool texture_can_make_shared_with_format(TextureID p_texture, DataFormat p_format, bool &r_raw_reinterpretation) override final;
|
||||
|
||||
private:
|
||||
TextureID _texture_create_shared_from_slice(TextureID p_original_texture, const TextureView &p_view, TextureSliceType p_slice_type, uint32_t p_layer, uint32_t p_layers, uint32_t p_mipmap, uint32_t p_mipmaps);
|
||||
|
||||
public:
|
||||
/*****************/
|
||||
/**** SAMPLER ****/
|
||||
/*****************/
|
||||
private:
|
||||
LocalVector<D3D12_SAMPLER_DESC> samplers;
|
||||
|
||||
struct SamplerDescriptorHeapAllocation : DescriptorHeap::Allocation {
|
||||
uint32_t key = 0;
|
||||
uint32_t use_count = 1;
|
||||
};
|
||||
|
||||
RBMap<uint32_t, SamplerDescriptorHeapAllocation> sampler_descriptor_heap_allocations;
|
||||
|
||||
public:
|
||||
virtual SamplerID sampler_create(const SamplerState &p_state) final override;
|
||||
virtual void sampler_free(SamplerID p_sampler) final override;
|
||||
virtual bool sampler_is_format_supported_for_filter(DataFormat p_format, SamplerFilter p_filter) override final;
|
||||
|
||||
/**********************/
|
||||
/**** VERTEX ARRAY ****/
|
||||
/**********************/
|
||||
private:
|
||||
struct VertexFormatInfo {
|
||||
TightLocalVector<D3D12_INPUT_ELEMENT_DESC> input_elem_descs;
|
||||
TightLocalVector<UINT> vertex_buffer_strides;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual VertexFormatID vertex_format_create(Span<VertexAttribute> p_vertex_attribs, const VertexAttributeBindingsMap &p_vertex_bindings) override final;
|
||||
virtual void vertex_format_free(VertexFormatID p_vertex_format) override final;
|
||||
|
||||
/******************/
|
||||
/**** BARRIERS ****/
|
||||
/******************/
|
||||
|
||||
virtual void command_pipeline_barrier(
|
||||
CommandBufferID p_cmd_buffer,
|
||||
BitField<PipelineStageBits> p_src_stages,
|
||||
BitField<PipelineStageBits> p_dst_stages,
|
||||
VectorView<RDD::MemoryAccessBarrier> p_memory_barriers,
|
||||
VectorView<RDD::BufferBarrier> p_buffer_barriers,
|
||||
VectorView<RDD::TextureBarrier> p_texture_barriers,
|
||||
VectorView<AccelerationStructureBarrier> p_acceleration_structure_barriers) override final;
|
||||
|
||||
private:
|
||||
/****************/
|
||||
/**** FENCES ****/
|
||||
/****************/
|
||||
|
||||
struct FenceInfo {
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> d3d_fence = nullptr;
|
||||
HANDLE event_handle = nullptr;
|
||||
UINT64 fence_value = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual FenceID fence_create() override;
|
||||
virtual Error fence_wait(FenceID p_fence) override;
|
||||
virtual void fence_free(FenceID p_fence) override;
|
||||
|
||||
private:
|
||||
/********************/
|
||||
/**** SEMAPHORES ****/
|
||||
/********************/
|
||||
|
||||
struct SemaphoreInfo {
|
||||
Microsoft::WRL::ComPtr<ID3D12Fence> d3d_fence = nullptr;
|
||||
UINT64 fence_value = 0;
|
||||
};
|
||||
|
||||
virtual SemaphoreID semaphore_create() override;
|
||||
virtual void semaphore_free(SemaphoreID p_semaphore) override;
|
||||
|
||||
/******************/
|
||||
/**** COMMANDS ****/
|
||||
/******************/
|
||||
|
||||
// ----- QUEUE FAMILY -----
|
||||
|
||||
virtual CommandQueueFamilyID command_queue_family_get(BitField<CommandQueueFamilyBits> p_cmd_queue_family_bits, RenderingContextDriver::SurfaceID p_surface = 0) override;
|
||||
|
||||
private:
|
||||
// ----- QUEUE -----
|
||||
|
||||
struct CommandQueueInfo {
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandQueue> d3d_queue;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual CommandQueueID command_queue_create(CommandQueueFamilyID p_cmd_queue_family, bool p_identify_as_main_queue = false) override;
|
||||
virtual Error command_queue_execute_and_present(CommandQueueID p_cmd_queue, VectorView<SemaphoreID> p_wait_semaphores, VectorView<CommandBufferID> p_cmd_buffers, VectorView<SemaphoreID> p_cmd_semaphores, FenceID p_cmd_fence, VectorView<SwapChainID> p_swap_chains) override;
|
||||
virtual void command_queue_free(CommandQueueID p_cmd_queue) override;
|
||||
|
||||
private:
|
||||
// ----- POOL -----
|
||||
struct CommandPoolInfo {
|
||||
CommandQueueFamilyID queue_family;
|
||||
CommandBufferType buffer_type = COMMAND_BUFFER_TYPE_PRIMARY;
|
||||
// Since there are no command pools in D3D12, we need to track the command buffers created by this pool
|
||||
// so that we can free them when the pool is freed.
|
||||
SelfList<CommandBufferInfo>::List command_buffers;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual CommandPoolID command_pool_create(CommandQueueFamilyID p_cmd_queue_family, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual bool command_pool_reset(CommandPoolID p_cmd_pool) override final;
|
||||
virtual void command_pool_free(CommandPoolID p_cmd_pool) override final;
|
||||
|
||||
// ----- BUFFER -----
|
||||
|
||||
private:
|
||||
// Belongs to RENDERING-SUBPASS, but needed here.
|
||||
struct FramebufferInfo;
|
||||
struct RenderPassInfo;
|
||||
struct RenderPassState {
|
||||
struct AttachmentLayout {
|
||||
struct AspectLayout {
|
||||
TextureLayout cur_layout = TEXTURE_LAYOUT_UNDEFINED;
|
||||
TextureLayout expected_layout = TEXTURE_LAYOUT_UNDEFINED;
|
||||
};
|
||||
|
||||
AspectLayout aspect_layouts[TEXTURE_ASPECT_MAX];
|
||||
};
|
||||
|
||||
uint32_t current_subpass = UINT32_MAX;
|
||||
const FramebufferInfo *fb_info = nullptr;
|
||||
const RenderPassInfo *pass_info = nullptr;
|
||||
CD3DX12_RECT region_rect = {};
|
||||
bool region_is_all = false;
|
||||
LocalVector<AttachmentLayout> attachment_layouts;
|
||||
|
||||
const VertexFormatInfo *vf_info = nullptr;
|
||||
D3D12_VERTEX_BUFFER_VIEW vertex_buffer_views[8] = {};
|
||||
uint32_t vertex_buffer_count = 0;
|
||||
};
|
||||
|
||||
struct DynParams {
|
||||
D3D12_PRIMITIVE_TOPOLOGY primitive_topology = {};
|
||||
Color blend_constant;
|
||||
float depth_bounds_min = 0.0f;
|
||||
float depth_bounds_max = 1.0f;
|
||||
uint32_t stencil_reference = 0;
|
||||
};
|
||||
|
||||
// Leveraging knowledge of actual usage and D3D12 specifics (namely, command lists from the same allocator
|
||||
// can't be freely begun and ended), an allocator per list works better.
|
||||
struct CommandBufferInfo {
|
||||
// Store a self list reference to be used by the command pool.
|
||||
SelfList<CommandBufferInfo> command_buffer_info_elem{ this };
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> cmd_allocator;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> cmd_list;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList1> cmd_list_1;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList5> cmd_list_5;
|
||||
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList7> cmd_list_7;
|
||||
|
||||
ID3D12PipelineState *graphics_pso = nullptr;
|
||||
ID3D12PipelineState *compute_pso = nullptr;
|
||||
|
||||
DynParams dyn_params;
|
||||
bool pending_dyn_params = true;
|
||||
|
||||
uint32_t graphics_root_signature_crc = 0;
|
||||
uint32_t compute_root_signature_crc = 0;
|
||||
|
||||
RenderPassState render_pass_state;
|
||||
bool descriptor_heaps_set = false;
|
||||
|
||||
HashMap<ResourceInfo::States *, BarrierRequest> res_barriers_requests;
|
||||
LocalVector<D3D12_RESOURCE_BARRIER> res_barriers;
|
||||
uint32_t res_barriers_count = 0;
|
||||
uint32_t res_barriers_batch = 0;
|
||||
|
||||
CPUDescriptorHeapPool::Allocation uav_alloc;
|
||||
CPUDescriptorHeapPool::Allocation rtv_alloc;
|
||||
CPUDescriptorHeapPool::Allocation dsv_alloc;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual CommandBufferID command_buffer_create(CommandPoolID p_cmd_pool) override final;
|
||||
virtual bool command_buffer_begin(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual bool command_buffer_begin_secondary(CommandBufferID p_cmd_buffer, RenderPassID p_render_pass, uint32_t p_subpass, FramebufferID p_framebuffer) override final;
|
||||
virtual void command_buffer_end(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_buffer_execute_secondary(CommandBufferID p_cmd_buffer, VectorView<CommandBufferID> p_secondary_cmd_buffers) override final;
|
||||
|
||||
private:
|
||||
/********************/
|
||||
/**** SWAP CHAIN ****/
|
||||
/********************/
|
||||
|
||||
struct SwapChain {
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain3> d3d_swap_chain;
|
||||
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
|
||||
UINT present_flags = 0;
|
||||
UINT sync_interval = 1;
|
||||
UINT creation_flags = 0;
|
||||
RenderPassID render_pass;
|
||||
TightLocalVector<ID3D12Resource *> render_targets;
|
||||
TightLocalVector<TextureInfo> render_targets_info;
|
||||
TightLocalVector<FramebufferID> framebuffers;
|
||||
RDD::DataFormat data_format = DATA_FORMAT_MAX;
|
||||
RDD::ColorSpace color_space = COLOR_SPACE_MAX;
|
||||
};
|
||||
|
||||
void _swap_chain_release(SwapChain *p_swap_chain);
|
||||
void _swap_chain_release_buffers(SwapChain *p_swap_chain);
|
||||
RenderPassID _swap_chain_create_render_pass(RDD::DataFormat p_format);
|
||||
void _determine_swap_chain_format(SwapChain *p_swap_chain, DataFormat &r_format, ColorSpace &r_color_space);
|
||||
|
||||
public:
|
||||
virtual SwapChainID swap_chain_create(RenderingContextDriver::SurfaceID p_surface) override;
|
||||
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override;
|
||||
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override;
|
||||
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override;
|
||||
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override;
|
||||
virtual ColorSpace swap_chain_get_color_space(SwapChainID p_swap_chain) override;
|
||||
virtual void swap_chain_free(SwapChainID p_swap_chain) override;
|
||||
|
||||
/*********************/
|
||||
/**** FRAMEBUFFER ****/
|
||||
/*********************/
|
||||
private:
|
||||
struct FramebufferInfo {
|
||||
bool is_screen = false;
|
||||
Size2i size;
|
||||
|
||||
TightLocalVector<uint32_t> attachments_handle_inds; // RTV heap index for color; DSV heap index for DSV.
|
||||
CPUDescriptorHeapPool::Allocation rtv_alloc;
|
||||
CPUDescriptorHeapPool::Allocation dsv_alloc; // Used only for depth-stencil attachments.
|
||||
|
||||
TightLocalVector<TextureID> attachments; // Color and depth-stencil. Used if not screen.
|
||||
TextureID vrs_attachment;
|
||||
};
|
||||
|
||||
D3D12_RENDER_TARGET_VIEW_DESC _make_rtv_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true);
|
||||
D3D12_UNORDERED_ACCESS_VIEW_DESC _make_ranged_uav_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true);
|
||||
D3D12_DEPTH_STENCIL_VIEW_DESC _make_dsv_for_texture(const TextureInfo *p_texture_info, uint32_t p_mipmap_offset, uint32_t p_layer_offset, uint32_t p_layers, bool p_add_bases = true);
|
||||
|
||||
FramebufferID _framebuffer_create(RenderPassID p_render_pass, VectorView<TextureID> p_attachments, uint32_t p_width, uint32_t p_height, bool p_is_screen);
|
||||
|
||||
public:
|
||||
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;
|
||||
|
||||
/****************/
|
||||
/**** SHADER ****/
|
||||
/****************/
|
||||
|
||||
private:
|
||||
static const uint32_t ROOT_SIGNATURE_SIZE = 256;
|
||||
static const uint32_t PUSH_CONSTANT_SIZE = 128; // Mimicking Vulkan.
|
||||
|
||||
enum {
|
||||
// We can only aim to set a maximum here, since depending on the shader
|
||||
// there may be more or less root signature free for descriptor tables.
|
||||
// Therefore, we'll have to rely on the final check at runtime, when building
|
||||
// the root signature structure for a given shader.
|
||||
// To be precise, these may be present or not, and their size vary statically:
|
||||
// - Push constant (we'll assume this is always present to avoid reserving much
|
||||
// more space for descriptor sets than needed for almost any imaginable case,
|
||||
// given that most shader templates feature push constants).
|
||||
// - NIR-DXIL runtime data.
|
||||
MAX_UNIFORM_SETS = (ROOT_SIGNATURE_SIZE - PUSH_CONSTANT_SIZE) / sizeof(uint32_t),
|
||||
};
|
||||
|
||||
struct ShaderInfo {
|
||||
uint32_t dxil_push_constant_size = 0;
|
||||
uint32_t nir_runtime_data_root_param_idx = UINT32_MAX;
|
||||
PipelineType pipeline_type = PIPELINE_TYPE_RASTERIZATION;
|
||||
|
||||
struct UniformBindingInfo {
|
||||
uint32_t stages = 0; // Actual shader stages using the uniform (0 if totally optimized out).
|
||||
ResourceClass res_class = RES_CLASS_INVALID;
|
||||
UniformType type = UNIFORM_TYPE_MAX;
|
||||
uint32_t length = UINT32_MAX;
|
||||
bool writable = false;
|
||||
uint32_t resource_descriptor_offset = UINT32_MAX;
|
||||
uint32_t sampler_descriptor_offset = UINT32_MAX;
|
||||
uint32_t root_param_idx = UINT32_MAX;
|
||||
};
|
||||
|
||||
struct UniformSet {
|
||||
TightLocalVector<UniformBindingInfo> bindings;
|
||||
uint32_t resource_root_param_idx = UINT32_MAX;
|
||||
uint32_t resource_descriptor_count = 0;
|
||||
uint32_t sampler_root_param_idx = UINT32_MAX;
|
||||
uint32_t sampler_descriptor_count = 0;
|
||||
};
|
||||
|
||||
TightLocalVector<UniformSet> sets;
|
||||
|
||||
struct SpecializationConstant {
|
||||
uint32_t constant_id = UINT32_MAX;
|
||||
uint32_t int_value = UINT32_MAX;
|
||||
uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {};
|
||||
};
|
||||
|
||||
TightLocalVector<SpecializationConstant> specialization_constants;
|
||||
uint32_t spirv_specialization_constants_ids_mask = 0;
|
||||
|
||||
HashMap<ShaderStage, Vector<uint8_t>> stages_bytecode;
|
||||
|
||||
Microsoft::WRL::ComPtr<ID3D12RootSignature> root_signature;
|
||||
Microsoft::WRL::ComPtr<ID3D12RootSignatureDeserializer> root_signature_deserializer;
|
||||
const D3D12_ROOT_SIGNATURE_DESC *root_signature_desc = nullptr; // Owned by the deserializer.
|
||||
uint32_t root_signature_crc = 0;
|
||||
};
|
||||
|
||||
bool _shader_apply_specialization_constants(
|
||||
const ShaderInfo *p_shader_info,
|
||||
VectorView<PipelineSpecializationConstant> p_specialization_constants,
|
||||
HashMap<ShaderStage, Vector<uint8_t>> &r_final_stages_bytecode);
|
||||
|
||||
public:
|
||||
virtual ShaderID shader_create_from_container(const Ref<RenderingShaderContainer> &p_shader_container, const Vector<ImmutableSampler> &p_immutable_samplers) override final;
|
||||
virtual uint32_t shader_get_layout_hash(ShaderID p_shader) override final;
|
||||
virtual void shader_free(ShaderID p_shader) override final;
|
||||
virtual void shader_destroy_modules(ShaderID p_shader) override final;
|
||||
|
||||
/*********************/
|
||||
/**** UNIFORM SET ****/
|
||||
/*********************/
|
||||
|
||||
private:
|
||||
struct UniformSetInfo {
|
||||
DescriptorHeap::Allocation resource_descriptor_heap_alloc;
|
||||
SamplerDescriptorHeapAllocation *sampler_descriptor_heap_alloc = nullptr;
|
||||
|
||||
struct DynamicBuffer {
|
||||
BufferDynamicInfo const *info = nullptr;
|
||||
uint32_t binding = UINT_MAX;
|
||||
};
|
||||
|
||||
TightLocalVector<DynamicBuffer> dynamic_buffers;
|
||||
|
||||
struct StateRequirement {
|
||||
ResourceInfo *resource = nullptr;
|
||||
bool is_buffer = false;
|
||||
D3D12_RESOURCE_STATES states = {};
|
||||
uint64_t shader_uniform_idx_mask = 0;
|
||||
};
|
||||
|
||||
TightLocalVector<StateRequirement> resource_states;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual UniformSetID uniform_set_create(VectorView<BoundUniform> p_uniforms, ShaderID p_shader, uint32_t p_set_index, int p_linear_pool_index) override final;
|
||||
virtual void uniform_set_free(UniformSetID p_uniform_set) override final;
|
||||
virtual uint32_t uniform_sets_get_dynamic_offsets(VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count) const override final;
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
virtual void command_uniform_set_prepare_for_use(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
|
||||
private:
|
||||
void _command_check_descriptor_sets(CommandBufferID p_cmd_buffer);
|
||||
DescriptorHeap::Allocation _command_allocate_per_frame_descriptor();
|
||||
|
||||
public:
|
||||
/******************/
|
||||
/**** TRANSFER ****/
|
||||
/******************/
|
||||
|
||||
virtual void command_clear_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, uint64_t p_offset, uint64_t p_size) override final;
|
||||
virtual void command_copy_buffer(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, BufferID p_dst_buffer, VectorView<BufferCopyRegion> p_regions) override final;
|
||||
|
||||
virtual void command_copy_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<TextureCopyRegion> p_regions) override final;
|
||||
virtual void command_resolve_texture(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, uint32_t p_src_layer, uint32_t p_src_mipmap, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, uint32_t p_dst_layer, uint32_t p_dst_mipmap) override final;
|
||||
virtual void command_clear_color_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, const Color &p_color, const TextureSubresourceRange &p_subresources) override final;
|
||||
virtual void command_clear_depth_stencil_texture(CommandBufferID p_cmd_buffer, TextureID p_texture, TextureLayout p_texture_layout, float p_depth, uint8_t p_stencil, const TextureSubresourceRange &p_subresources) override final;
|
||||
|
||||
public:
|
||||
virtual void command_copy_buffer_to_texture(CommandBufferID p_cmd_buffer, BufferID p_src_buffer, TextureID p_dst_texture, TextureLayout p_dst_texture_layout, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
virtual void command_copy_texture_to_buffer(CommandBufferID p_cmd_buffer, TextureID p_src_texture, TextureLayout p_src_texture_layout, BufferID p_dst_buffer, VectorView<BufferTextureCopyRegion> p_regions) override final;
|
||||
|
||||
/******************/
|
||||
/**** PIPELINE ****/
|
||||
/******************/
|
||||
|
||||
struct RenderPipelineInfo {
|
||||
const VertexFormatInfo *vf_info = nullptr;
|
||||
DynParams dyn_params;
|
||||
};
|
||||
|
||||
struct PipelineInfo {
|
||||
Microsoft::WRL::ComPtr<ID3D12PipelineState> pso;
|
||||
const ShaderInfo *shader_info = nullptr;
|
||||
RenderPipelineInfo render_info;
|
||||
};
|
||||
|
||||
virtual void pipeline_free(PipelineID p_pipeline) override final;
|
||||
|
||||
public:
|
||||
// ----- BINDING -----
|
||||
|
||||
virtual void command_bind_push_constants(CommandBufferID p_cmd_buffer, ShaderID p_shader, uint32_t p_dst_first_index, VectorView<uint32_t> p_data) override final;
|
||||
|
||||
// ----- CACHE -----
|
||||
|
||||
virtual bool pipeline_cache_create(const Vector<uint8_t> &p_data) override final;
|
||||
virtual void pipeline_cache_free() override final;
|
||||
virtual size_t pipeline_cache_query_size() override final;
|
||||
virtual Vector<uint8_t> pipeline_cache_serialize() override final;
|
||||
|
||||
/*******************/
|
||||
/**** RENDERING ****/
|
||||
/*******************/
|
||||
|
||||
// ----- SUBPASS -----
|
||||
|
||||
private:
|
||||
struct RenderPassInfo {
|
||||
TightLocalVector<Attachment> attachments;
|
||||
TightLocalVector<Subpass> subpasses;
|
||||
uint32_t view_count = 0;
|
||||
uint32_t max_supported_sample_count = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual RenderPassID render_pass_create(VectorView<Attachment> p_attachments, VectorView<Subpass> p_subpasses, VectorView<SubpassDependency> p_subpass_dependencies, uint32_t p_view_count, AttachmentReference p_fragment_density_map_attachment) override final;
|
||||
virtual void render_pass_free(RenderPassID p_render_pass) override final;
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
virtual void 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) override final;
|
||||
|
||||
private:
|
||||
void _render_pass_enhanced_barriers_flush(CommandBufferID p_cmd_buffer);
|
||||
void _end_render_pass(CommandBufferID p_cmd_buffer);
|
||||
|
||||
public:
|
||||
virtual void command_end_render_pass(CommandBufferID p_cmd_buffer) override final;
|
||||
virtual void command_next_render_subpass(CommandBufferID p_cmd_buffer, CommandBufferType p_cmd_buffer_type) override final;
|
||||
virtual void command_render_set_viewport(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_viewports) override final;
|
||||
virtual void command_render_set_scissor(CommandBufferID p_cmd_buffer, VectorView<Rect2i> p_scissors) override final;
|
||||
|
||||
virtual void command_render_clear_attachments(CommandBufferID p_cmd_buffer, VectorView<AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) override final;
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_render_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_render_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, uint32_t p_dynamic_offsets) override final;
|
||||
|
||||
// Drawing.
|
||||
virtual void command_render_draw(CommandBufferID p_cmd_buffer, uint32_t p_vertex_count, uint32_t p_instance_count, uint32_t p_base_vertex, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed(CommandBufferID p_cmd_buffer, uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index, int32_t p_vertex_offset, uint32_t p_first_instance) override final;
|
||||
virtual void command_render_draw_indexed_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indexed_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) override final;
|
||||
virtual void command_render_draw_indirect_count(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset, BufferID p_count_buffer, uint64_t p_count_buffer_offset, uint32_t p_max_draw_count, uint32_t p_stride) override final;
|
||||
|
||||
// Buffer binding.
|
||||
virtual void command_render_bind_vertex_buffers(CommandBufferID p_cmd_buffer, uint32_t p_binding_count, const BufferID *p_buffers, const uint64_t *p_offsets, uint64_t p_dynamic_offsets) override final;
|
||||
virtual void command_render_bind_index_buffer(CommandBufferID p_cmd_buffer, BufferID p_buffer, IndexBufferFormat p_format, uint64_t p_offset) override final;
|
||||
|
||||
private:
|
||||
void _bind_vertex_buffers(CommandBufferInfo *p_cmd_buf_info);
|
||||
|
||||
public:
|
||||
// Dynamic state.
|
||||
virtual void command_render_set_blend_constants(CommandBufferID p_cmd_buffer, const Color &p_constants) override final;
|
||||
virtual void command_render_set_line_width(CommandBufferID p_cmd_buffer, float p_width) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
public:
|
||||
virtual PipelineID render_pipeline_create(
|
||||
ShaderID p_shader,
|
||||
VertexFormatID p_vertex_format,
|
||||
RenderPrimitive p_render_primitive,
|
||||
PipelineRasterizationState p_rasterization_state,
|
||||
PipelineMultisampleState p_multisample_state,
|
||||
PipelineDepthStencilState p_depth_stencil_state,
|
||||
PipelineColorBlendState p_blend_state,
|
||||
VectorView<int32_t> p_color_attachments,
|
||||
BitField<PipelineDynamicStateFlags> p_dynamic_state,
|
||||
RenderPassID p_render_pass,
|
||||
uint32_t p_render_subpass,
|
||||
VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
/*****************/
|
||||
/**** COMPUTE ****/
|
||||
/*****************/
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
// Binding.
|
||||
virtual void command_bind_compute_pipeline(CommandBufferID p_cmd_buffer, PipelineID p_pipeline) override final;
|
||||
virtual void command_bind_compute_uniform_sets(CommandBufferID p_cmd_buffer, VectorView<UniformSetID> p_uniform_sets, ShaderID p_shader, uint32_t p_first_set_index, uint32_t p_set_count, uint32_t p_dynamic_offsets) override final;
|
||||
|
||||
// Dispatching.
|
||||
virtual void command_compute_dispatch(CommandBufferID p_cmd_buffer, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) override final;
|
||||
virtual void command_compute_dispatch_indirect(CommandBufferID p_cmd_buffer, BufferID p_indirect_buffer, uint64_t p_offset) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual PipelineID compute_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
|
||||
/********************/
|
||||
/**** RAYTRACING ****/
|
||||
/********************/
|
||||
|
||||
// ---- ACCELERATION STRUCTURES ----
|
||||
|
||||
virtual AccelerationStructureID blas_create(BufferID p_vertex_buffer, uint64_t p_vertex_offset, VertexFormatID p_vertex_format, uint32_t p_vertex_count, uint32_t p_position_attribute_location, BufferID p_index_buffer, IndexBufferFormat p_index_format, uint64_t p_index_offset, uint32_t p_index_count, BitField<AccelerationStructureGeometryBits> p_geometry_bits) override final;
|
||||
virtual uint32_t tlas_instances_buffer_get_size_bytes(uint32_t p_instance_count) override final;
|
||||
virtual void tlas_instances_buffer_fill(BufferID p_instances_buffer, VectorView<AccelerationStructureID> p_blases, VectorView<Transform3D> p_transforms) override final;
|
||||
virtual AccelerationStructureID tlas_create(BufferID p_instances_buffer) override final;
|
||||
virtual void acceleration_structure_free(AccelerationStructureID p_acceleration_structure) override final;
|
||||
virtual uint32_t acceleration_structure_get_scratch_size_bytes(AccelerationStructureID p_acceleration_structure) override final;
|
||||
|
||||
// ----- PIPELINE -----
|
||||
|
||||
virtual RaytracingPipelineID raytracing_pipeline_create(ShaderID p_shader, VectorView<PipelineSpecializationConstant> p_specialization_constants) override final;
|
||||
virtual void raytracing_pipeline_free(RaytracingPipelineID p_pipeline) override final;
|
||||
|
||||
// ----- COMMANDS -----
|
||||
|
||||
virtual void command_build_acceleration_structure(CommandBufferID p_cmd_buffer, AccelerationStructureID p_acceleration_structure, BufferID p_scratch_buffer) override final;
|
||||
virtual void command_bind_raytracing_pipeline(CommandBufferID p_cmd_buffer, RaytracingPipelineID p_pipeline) override final;
|
||||
virtual void command_bind_raytracing_uniform_set(CommandBufferID p_cmd_buffer, UniformSetID p_uniform_set, ShaderID p_shader, uint32_t p_set_index) override final;
|
||||
virtual void command_trace_rays(CommandBufferID p_cmd_buffer, uint32_t p_width, uint32_t p_height) override final;
|
||||
|
||||
/*****************/
|
||||
/**** QUERIES ****/
|
||||
/*****************/
|
||||
|
||||
// ----- TIMESTAMP -----
|
||||
|
||||
private:
|
||||
struct TimestampQueryPoolInfo {
|
||||
Microsoft::WRL::ComPtr<ID3D12QueryHeap> query_heap;
|
||||
uint32_t query_count = 0;
|
||||
Microsoft::WRL::ComPtr<D3D12MA::Allocation> results_buffer_allocation;
|
||||
};
|
||||
|
||||
public:
|
||||
// Basic.
|
||||
virtual QueryPoolID timestamp_query_pool_create(uint32_t p_query_count) override final;
|
||||
virtual void timestamp_query_pool_free(QueryPoolID p_pool_id) override final;
|
||||
virtual void timestamp_query_pool_get_results(QueryPoolID p_pool_id, uint32_t p_query_count, uint64_t *r_results) override final;
|
||||
virtual uint64_t timestamp_query_result_to_time(uint64_t p_result) override final;
|
||||
|
||||
// Commands.
|
||||
virtual void command_timestamp_query_pool_reset(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_query_count) override final;
|
||||
virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final;
|
||||
|
||||
/****************/
|
||||
/**** LABELS ****/
|
||||
/****************/
|
||||
|
||||
virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
|
||||
virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
|
||||
|
||||
/****************/
|
||||
/**** DEBUG *****/
|
||||
/****************/
|
||||
virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
|
||||
|
||||
/********************/
|
||||
/**** SUBMISSION ****/
|
||||
/********************/
|
||||
private:
|
||||
struct FrameInfo {
|
||||
LocalVector<DescriptorHeap::Allocation> descriptor_allocations;
|
||||
uint32_t descriptor_allocation_count = 0;
|
||||
};
|
||||
TightLocalVector<FrameInfo> frames;
|
||||
uint32_t frame_idx = 0;
|
||||
uint32_t frames_drawn = 0;
|
||||
bool segment_begun = false;
|
||||
HashMap<uint64_t, bool> has_comp_alpha;
|
||||
|
||||
public:
|
||||
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
|
||||
virtual void end_segment() override final;
|
||||
|
||||
/**************/
|
||||
/**** MISC ****/
|
||||
/**************/
|
||||
|
||||
virtual void set_object_name(ObjectType p_type, ID p_driver_id, const String &p_name) override final;
|
||||
virtual uint64_t get_resource_native_handle(DriverResource p_type, ID p_driver_id) override final;
|
||||
virtual uint64_t get_total_memory_used() override final;
|
||||
virtual uint64_t get_lazily_memory_used() override final;
|
||||
virtual uint64_t limit_get(Limit p_limit) override final;
|
||||
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 const FragmentShadingRateCapabilities &get_fragment_shading_rate_capabilities() override final;
|
||||
virtual const FragmentDensityMapCapabilities &get_fragment_density_map_capabilities() override final;
|
||||
virtual String get_api_name() const override final;
|
||||
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;
|
||||
virtual const RenderingShaderContainerFormat &get_shader_container_format() const override final;
|
||||
|
||||
virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
|
||||
|
||||
static bool is_in_developer_mode();
|
||||
|
||||
private:
|
||||
/*********************/
|
||||
/**** BOOKKEEPING ****/
|
||||
/*********************/
|
||||
|
||||
using VersatileResource = VersatileResourceTemplate<
|
||||
BufferInfo,
|
||||
TextureInfo,
|
||||
TextureInfo,
|
||||
TextureInfo,
|
||||
VertexFormatInfo,
|
||||
CommandBufferInfo,
|
||||
FramebufferInfo,
|
||||
ShaderInfo,
|
||||
UniformSetInfo,
|
||||
RenderPassInfo,
|
||||
TimestampQueryPoolInfo>;
|
||||
PagedAllocator<VersatileResource, true> resources_allocator;
|
||||
|
||||
/******************/
|
||||
|
||||
public:
|
||||
RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver);
|
||||
virtual ~RenderingDeviceDriverD3D12();
|
||||
};
|
||||
1046
engine/drivers/d3d12/rendering_shader_container_d3d12.cpp
Normal file
1046
engine/drivers/d3d12/rendering_shader_container_d3d12.cpp
Normal file
File diff suppressed because it is too large
Load diff
185
engine/drivers/d3d12/rendering_shader_container_d3d12.h
Normal file
185
engine/drivers/d3d12/rendering_shader_container_d3d12.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/**************************************************************************/
|
||||
/* rendering_shader_container_d3d12.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/rendering/rendering_shader_container.h"
|
||||
|
||||
#define NIR_ENABLED 1
|
||||
|
||||
#ifdef SHADER_BAKER_RUNTIME_ENABLED
|
||||
#undef NIR_ENABLED
|
||||
#endif
|
||||
|
||||
#include "d3d12_godot_nir_bridge.h"
|
||||
|
||||
#define D3D12_BITCODE_OFFSETS_NUM_STAGES 3
|
||||
|
||||
#if NIR_ENABLED
|
||||
struct nir_shader;
|
||||
struct nir_shader_compiler_options;
|
||||
#endif
|
||||
|
||||
enum RootSignatureLocationType {
|
||||
RS_LOC_TYPE_RESOURCE,
|
||||
RS_LOC_TYPE_SAMPLER,
|
||||
};
|
||||
|
||||
enum ResourceClass {
|
||||
RES_CLASS_INVALID,
|
||||
RES_CLASS_CBV,
|
||||
RES_CLASS_SRV,
|
||||
RES_CLASS_UAV,
|
||||
};
|
||||
|
||||
struct RenderingDXIL {
|
||||
static uint32_t patch_specialization_constant(
|
||||
RenderingDeviceCommons::PipelineSpecializationConstantType p_type,
|
||||
const void *p_value,
|
||||
const uint64_t (&p_stages_bit_offsets)[D3D12_BITCODE_OFFSETS_NUM_STAGES],
|
||||
HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_stages_bytecodes,
|
||||
bool p_is_first_patch);
|
||||
|
||||
static void sign_bytecode(RenderingDeviceCommons::ShaderStage p_stage, Vector<uint8_t> &r_dxil_blob);
|
||||
};
|
||||
|
||||
class RenderingShaderContainerD3D12 : public RenderingShaderContainer {
|
||||
GDSOFTCLASS(RenderingShaderContainerD3D12, RenderingShaderContainer);
|
||||
|
||||
public:
|
||||
static constexpr uint32_t REQUIRED_SHADER_MODEL = 0x62; // D3D_SHADER_MODEL_6_2
|
||||
static constexpr uint32_t ROOT_CONSTANT_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RenderingDeviceCommons::MAX_UNIFORM_SETS + 1);
|
||||
static constexpr uint32_t RUNTIME_DATA_REGISTER = GODOT_NIR_DESCRIPTOR_SET_MULTIPLIER * (RenderingDeviceCommons::MAX_UNIFORM_SETS + 2);
|
||||
static constexpr uint32_t FORMAT_VERSION = 1;
|
||||
static constexpr uint32_t SHADER_STAGES_BIT_OFFSET_INDICES[RenderingDeviceCommons::SHADER_STAGE_MAX] = {
|
||||
0, // SHADER_STAGE_VERTEX
|
||||
1, // SHADER_STAGE_FRAGMENT
|
||||
UINT32_MAX, // SHADER_STAGE_TESSELATION_CONTROL
|
||||
UINT32_MAX, // SHADER_STAGE_TESSELATION_EVALUATION
|
||||
2, // SHADER_STAGE_COMPUTE
|
||||
};
|
||||
|
||||
struct ReflectionBindingSetDataD3D12 {
|
||||
uint32_t resource_root_param_idx = UINT32_MAX;
|
||||
uint32_t resource_descriptor_count = 0;
|
||||
uint32_t sampler_root_param_idx = UINT32_MAX;
|
||||
uint32_t sampler_descriptor_count = 0;
|
||||
};
|
||||
|
||||
struct ReflectionBindingDataD3D12 {
|
||||
uint32_t resource_class = 0;
|
||||
uint32_t has_sampler = 0;
|
||||
uint32_t dxil_stages = 0;
|
||||
uint32_t resource_descriptor_offset = UINT32_MAX;
|
||||
uint32_t sampler_descriptor_offset = UINT32_MAX;
|
||||
uint32_t root_param_idx = UINT32_MAX; // Root descriptor only.
|
||||
};
|
||||
|
||||
struct ReflectionSpecializationDataD3D12 {
|
||||
uint64_t stages_bit_offsets[D3D12_BITCODE_OFFSETS_NUM_STAGES] = {};
|
||||
};
|
||||
|
||||
protected:
|
||||
struct ReflectionDataD3D12 {
|
||||
uint32_t spirv_specialization_constants_ids_mask = 0;
|
||||
uint32_t dxil_push_constant_stages = 0;
|
||||
uint32_t nir_runtime_data_root_param_idx = 0;
|
||||
};
|
||||
|
||||
struct ContainerFooterD3D12 {
|
||||
uint32_t root_signature_length = 0;
|
||||
uint32_t root_signature_crc = 0;
|
||||
};
|
||||
|
||||
void *lib_d3d12 = nullptr;
|
||||
ReflectionDataD3D12 reflection_data_d3d12;
|
||||
Vector<ReflectionBindingSetDataD3D12> reflection_binding_set_data_d3d12;
|
||||
Vector<ReflectionBindingDataD3D12> reflection_binding_set_uniforms_data_d3d12;
|
||||
Vector<ReflectionSpecializationDataD3D12> reflection_specialization_data_d3d12;
|
||||
Vector<uint8_t> root_signature_bytes;
|
||||
uint32_t root_signature_crc = 0;
|
||||
|
||||
#if NIR_ENABLED
|
||||
bool _convert_spirv_to_nir(Span<ReflectShaderStage> p_spirv, const nir_shader_compiler_options *p_compiler_options, HashMap<int, nir_shader *> &r_stages_nir_shaders, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
|
||||
bool _convert_nir_to_dxil(const HashMap<int, nir_shader *> &p_stages_nir_shaders, BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs);
|
||||
bool _convert_spirv_to_dxil(Span<ReflectShaderStage> p_spirv, HashMap<RenderingDeviceCommons::ShaderStage, Vector<uint8_t>> &r_dxil_blobs, Vector<RenderingDeviceCommons::ShaderStage> &r_stages, BitField<RenderingDeviceCommons::ShaderStage> &r_stages_processed);
|
||||
bool _generate_root_signature(BitField<RenderingDeviceCommons::ShaderStage> p_stages_processed);
|
||||
|
||||
// GodotNirCallbacks.
|
||||
static void _nir_report_resource(uint32_t p_register, uint32_t p_space, uint32_t p_dxil_type, void *p_data);
|
||||
static void _nir_report_sc_bit_offset(uint32_t p_sc_id, uint64_t p_bit_offset, void *p_data);
|
||||
static void _nir_report_bitcode_bit_offset(uint64_t p_bit_offset, void *p_data);
|
||||
#endif
|
||||
|
||||
// RenderingShaderContainer overrides.
|
||||
virtual uint32_t _format() const override;
|
||||
virtual uint32_t _format_version() const override;
|
||||
virtual uint32_t _from_bytes_reflection_extra_data(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data_start(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_binding_uniform_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
|
||||
virtual uint32_t _from_bytes_reflection_specialization_extra_data_start(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _from_bytes_reflection_specialization_extra_data(const uint8_t *p_bytes, uint32_t p_index) override;
|
||||
virtual uint32_t _from_bytes_footer_extra_data(const uint8_t *p_bytes) override;
|
||||
virtual uint32_t _to_bytes_reflection_extra_data(uint8_t *p_bytes) const override;
|
||||
virtual uint32_t _to_bytes_reflection_binding_uniform_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
|
||||
virtual uint32_t _to_bytes_reflection_specialization_extra_data(uint8_t *p_bytes, uint32_t p_index) const override;
|
||||
virtual uint32_t _to_bytes_footer_extra_data(uint8_t *p_bytes) const override;
|
||||
virtual void _set_from_shader_reflection_post(const ReflectShader &p_shader) override;
|
||||
virtual bool _set_code_from_spirv(const ReflectShader &p_shader) override;
|
||||
|
||||
public:
|
||||
struct ShaderReflectionD3D12 {
|
||||
uint32_t spirv_specialization_constants_ids_mask = 0;
|
||||
uint32_t dxil_push_constant_stages = 0;
|
||||
uint32_t nir_runtime_data_root_param_idx = 0;
|
||||
Vector<ReflectionBindingSetDataD3D12> reflection_binding_sets_d3d12;
|
||||
Vector<Vector<ReflectionBindingDataD3D12>> reflection_binding_set_uniforms_d3d12;
|
||||
Vector<ReflectionSpecializationDataD3D12> reflection_specialization_data_d3d12;
|
||||
Vector<uint8_t> root_signature_bytes;
|
||||
uint32_t root_signature_crc = 0;
|
||||
};
|
||||
|
||||
RenderingShaderContainerD3D12();
|
||||
RenderingShaderContainerD3D12(void *p_lib_d3d12);
|
||||
ShaderReflectionD3D12 get_shader_reflection_d3d12() const;
|
||||
};
|
||||
|
||||
class RenderingShaderContainerFormatD3D12 : public RenderingShaderContainerFormat {
|
||||
protected:
|
||||
void *lib_d3d12 = nullptr;
|
||||
|
||||
public:
|
||||
void set_lib_d3d12(void *p_lib_d3d12);
|
||||
virtual Ref<RenderingShaderContainer> create_container() const override;
|
||||
virtual ShaderLanguageVersion get_shader_language_version() const override;
|
||||
virtual ShaderSpirvVersion get_shader_spirv_version() const override;
|
||||
RenderingShaderContainerFormatD3D12();
|
||||
virtual ~RenderingShaderContainerFormatD3D12();
|
||||
};
|
||||
7
engine/drivers/egl/SCsub
Normal file
7
engine/drivers/egl/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
# Godot source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
544
engine/drivers/egl/egl_manager.cpp
Normal file
544
engine/drivers/egl/egl_manager.cpp
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
/**************************************************************************/
|
||||
/* egl_manager.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "egl_manager.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
#endif
|
||||
|
||||
#ifdef EGL_ENABLED
|
||||
|
||||
#if defined(EGL_STATIC)
|
||||
|
||||
#define GLAD_EGL_VERSION_1_5 true
|
||||
|
||||
#ifdef EGL_EXT_platform_base
|
||||
#define GLAD_EGL_EXT_platform_base 1
|
||||
#endif
|
||||
|
||||
#define KHRONOS_STATIC 1
|
||||
extern "C" EGLAPI void EGLAPIENTRY eglSetBlobCacheFuncsANDROID(EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get);
|
||||
extern "C" EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplayEXT(EGLenum platform, void *native_display, const EGLint *attrib_list);
|
||||
#undef KHRONOS_STATIC
|
||||
|
||||
#endif // defined(EGL_STATIC)
|
||||
|
||||
#ifndef EGL_EXT_platform_base
|
||||
#define GLAD_EGL_EXT_platform_base 0
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
// Unofficial ANGLE extension: EGL_ANGLE_surface_orientation
|
||||
#ifndef EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE
|
||||
#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7
|
||||
#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8
|
||||
#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001
|
||||
#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Creates and caches a GLDisplay. Returns -1 on error.
|
||||
int EGLManager::_get_gldisplay_id(void *p_display) {
|
||||
// Look for a cached GLDisplay.
|
||||
for (unsigned int i = 0; i < displays.size(); i++) {
|
||||
if (displays[i].display == p_display) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find any, so we'll have to create one, along with its own
|
||||
// EGLDisplay and EGLContext.
|
||||
GLDisplay new_gldisplay;
|
||||
new_gldisplay.display = p_display;
|
||||
|
||||
if (GLAD_EGL_VERSION_1_5) {
|
||||
Vector<EGLAttrib> attribs = _get_platform_display_attributes();
|
||||
new_gldisplay.egl_display = eglGetPlatformDisplay(_get_platform_extension_enum(), new_gldisplay.display, (attribs.size() > 0) ? attribs.ptr() : nullptr);
|
||||
} else if (GLAD_EGL_EXT_platform_base) {
|
||||
#ifdef EGL_EXT_platform_base
|
||||
// eglGetPlatformDisplayEXT wants its attributes as EGLint, so we'll truncate
|
||||
// what we already have. It's a bit naughty but I'm really not sure what else
|
||||
// we could do here.
|
||||
Vector<EGLint> attribs;
|
||||
for (const EGLAttrib &attrib : _get_platform_display_attributes()) {
|
||||
attribs.push_back((EGLint)attrib);
|
||||
}
|
||||
|
||||
new_gldisplay.egl_display = eglGetPlatformDisplayEXT(_get_platform_extension_enum(), new_gldisplay.display, (attribs.size() > 0) ? attribs.ptr() : nullptr);
|
||||
#endif // EGL_EXT_platform_base
|
||||
} else {
|
||||
NativeDisplayType *native_display_type = (NativeDisplayType *)new_gldisplay.display;
|
||||
new_gldisplay.egl_display = eglGetDisplay(*native_display_type);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, -1);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(new_gldisplay.egl_display == EGL_NO_DISPLAY, -1, "Can't create an EGL display.");
|
||||
|
||||
if (!eglInitialize(new_gldisplay.egl_display, nullptr, nullptr)) {
|
||||
ERR_FAIL_V_MSG(-1, "Can't initialize an EGL display.");
|
||||
}
|
||||
|
||||
if (!eglBindAPI(_get_platform_api_enum())) {
|
||||
ERR_FAIL_V_MSG(-1, "OpenGL not supported.");
|
||||
}
|
||||
|
||||
Error err = _gldisplay_create_context(new_gldisplay);
|
||||
|
||||
if (err != OK) {
|
||||
eglTerminate(new_gldisplay.egl_display);
|
||||
ERR_FAIL_V(-1);
|
||||
}
|
||||
|
||||
#ifdef EGL_ANDROID_blob_cache
|
||||
#if defined(EGL_STATIC)
|
||||
bool has_blob_cache = true;
|
||||
#else
|
||||
bool has_blob_cache = (eglSetBlobCacheFuncsANDROID != nullptr);
|
||||
#endif
|
||||
if (has_blob_cache && !shader_cache_dir.is_empty()) {
|
||||
eglSetBlobCacheFuncsANDROID(new_gldisplay.egl_display, &EGLManager::_set_cache, &EGLManager::_get_cache);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
String client_extensions_string = eglQueryString(new_gldisplay.egl_display, EGL_EXTENSIONS);
|
||||
if (eglGetError() == EGL_SUCCESS) {
|
||||
Vector<String> egl_extensions = client_extensions_string.split(" ");
|
||||
|
||||
if (egl_extensions.has("EGL_ANGLE_surface_orientation")) {
|
||||
new_gldisplay.has_EGL_ANGLE_surface_orientation = true;
|
||||
print_verbose("EGL: EGL_ANGLE_surface_orientation is supported.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
displays.push_back(new_gldisplay);
|
||||
|
||||
// Return the new GLDisplay's ID.
|
||||
return displays.size() - 1;
|
||||
}
|
||||
|
||||
#ifdef EGL_ANDROID_blob_cache
|
||||
String EGLManager::shader_cache_dir;
|
||||
|
||||
void EGLManager::_set_cache(const void *p_key, EGLsizeiANDROID p_key_size, const void *p_value, EGLsizeiANDROID p_value_size) {
|
||||
String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace_char('/', '_');
|
||||
String path = shader_cache_dir.path_join(name) + ".cache";
|
||||
|
||||
Error err = OK;
|
||||
Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
file->store_buffer((const uint8_t *)p_value, p_value_size);
|
||||
}
|
||||
|
||||
EGLsizeiANDROID EGLManager::_get_cache(const void *p_key, EGLsizeiANDROID p_key_size, void *p_value, EGLsizeiANDROID p_value_size) {
|
||||
String name = CryptoCore::b64_encode_str((const uint8_t *)p_key, p_key_size).replace_char('/', '_');
|
||||
String path = shader_cache_dir.path_join(name) + ".cache";
|
||||
|
||||
Error err = OK;
|
||||
Ref<FileAccess> file = FileAccess::open(path, FileAccess::READ, &err);
|
||||
if (err != OK) {
|
||||
return 0;
|
||||
}
|
||||
EGLsizeiANDROID len = file->get_length();
|
||||
if (len <= p_value_size) {
|
||||
file->get_buffer((uint8_t *)p_value, len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
Error EGLManager::_gldisplay_create_context(GLDisplay &p_gldisplay) {
|
||||
EGLint attribs[] = {
|
||||
EGL_RED_SIZE,
|
||||
1,
|
||||
EGL_BLUE_SIZE,
|
||||
1,
|
||||
EGL_GREEN_SIZE,
|
||||
1,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
EGLint attribs_layered[] = {
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
EGLint config_count = 0;
|
||||
|
||||
if (OS::get_singleton()->is_layered_allowed()) {
|
||||
eglChooseConfig(p_gldisplay.egl_display, attribs_layered, &p_gldisplay.egl_config, 1, &config_count);
|
||||
} else {
|
||||
eglChooseConfig(p_gldisplay.egl_display, attribs, &p_gldisplay.egl_config, 1, &config_count);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(eglGetError() != EGL_SUCCESS, ERR_BUG);
|
||||
ERR_FAIL_COND_V(config_count == 0, ERR_UNCONFIGURED);
|
||||
|
||||
Vector<EGLint> context_attribs = _get_platform_context_attribs();
|
||||
p_gldisplay.egl_context = eglCreateContext(p_gldisplay.egl_display, p_gldisplay.egl_config, EGL_NO_CONTEXT, (context_attribs.size() > 0) ? context_attribs.ptr() : nullptr);
|
||||
ERR_FAIL_COND_V_MSG(p_gldisplay.egl_context == EGL_NO_CONTEXT, ERR_CANT_CREATE, vformat("Can't create an EGL context. Error code: %d", eglGetError()));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EGLManager::open_display(void *p_display) {
|
||||
int gldisplay_id = _get_gldisplay_id(p_display);
|
||||
if (gldisplay_id < 0) {
|
||||
return ERR_CANT_CREATE;
|
||||
} else {
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
|
||||
int EGLManager::display_get_native_visual_id(void *p_display) {
|
||||
int gldisplay_id = _get_gldisplay_id(p_display);
|
||||
ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
|
||||
|
||||
GLDisplay gldisplay = displays[gldisplay_id];
|
||||
|
||||
EGLint native_visual_id = -1;
|
||||
|
||||
if (!eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_NATIVE_VISUAL_ID, &native_visual_id)) {
|
||||
ERR_FAIL_V(-1);
|
||||
}
|
||||
|
||||
return native_visual_id;
|
||||
}
|
||||
|
||||
Error EGLManager::window_create(DisplayServerEnums::WindowID p_window_id, void *p_display, void *p_native_window, int p_width, int p_height) {
|
||||
int gldisplay_id = _get_gldisplay_id(p_display);
|
||||
ERR_FAIL_COND_V(gldisplay_id < 0, ERR_CANT_CREATE);
|
||||
|
||||
GLDisplay &gldisplay = displays[gldisplay_id];
|
||||
|
||||
// In order to ensure a fast lookup, make sure we got enough elements in the
|
||||
// windows local vector to use the window id as an index.
|
||||
if (p_window_id >= (int)windows.size()) {
|
||||
windows.resize(p_window_id + 1);
|
||||
}
|
||||
|
||||
GLWindow &glwindow = windows[p_window_id];
|
||||
glwindow.gldisplay_id = gldisplay_id;
|
||||
|
||||
Vector<EGLAttrib> egl_attribs;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
|
||||
EGLint optimal_orientation;
|
||||
if (eglGetConfigAttrib(gldisplay.egl_display, gldisplay.egl_config, EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, &optimal_orientation)) {
|
||||
// We only need to support inverting Y for optimizing ANGLE on D3D11.
|
||||
if (optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(optimal_orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
|
||||
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_ANGLE);
|
||||
egl_attribs.push_back(EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
|
||||
}
|
||||
} else {
|
||||
ERR_PRINT(vformat("Failed to get EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!egl_attribs.is_empty()) {
|
||||
egl_attribs.push_back(EGL_NONE);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (GLAD_EGL_VERSION_1_5) {
|
||||
glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, egl_attribs.ptr());
|
||||
} else {
|
||||
EGLNativeWindowType *native_window_type = (EGLNativeWindowType *)p_native_window;
|
||||
glwindow.egl_surface = eglCreateWindowSurface(gldisplay.egl_display, gldisplay.egl_config, *native_window_type, nullptr);
|
||||
}
|
||||
|
||||
if (glwindow.egl_surface == EGL_NO_SURFACE) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
glwindow.initialized = true;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
if (gldisplay.has_EGL_ANGLE_surface_orientation) {
|
||||
EGLint orientation;
|
||||
if (eglQuerySurface(gldisplay.egl_display, glwindow.egl_surface, EGL_SURFACE_ORIENTATION_ANGLE, &orientation)) {
|
||||
if (orientation & EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE && !(orientation & EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE)) {
|
||||
glwindow.flipped_y = true;
|
||||
print_verbose("EGL: Using optimal surface orientation: Invert Y");
|
||||
}
|
||||
} else {
|
||||
ERR_PRINT(vformat("Failed to get EGL_SURFACE_ORIENTATION_ANGLE, error: 0x%08X", eglGetError()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
window_make_current(p_window_id);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void EGLManager::window_destroy(DisplayServerEnums::WindowID p_window_id) {
|
||||
ERR_FAIL_INDEX(p_window_id, (int)windows.size());
|
||||
|
||||
GLWindow &glwindow = windows[p_window_id];
|
||||
|
||||
if (!glwindow.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
glwindow.initialized = false;
|
||||
|
||||
ERR_FAIL_INDEX(glwindow.gldisplay_id, (int)displays.size());
|
||||
GLDisplay &gldisplay = displays[glwindow.gldisplay_id];
|
||||
|
||||
if (glwindow.egl_surface != EGL_NO_SURFACE) {
|
||||
eglDestroySurface(gldisplay.egl_display, glwindow.egl_surface);
|
||||
glwindow.egl_surface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void EGLManager::release_current() {
|
||||
if (!current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLDisplay ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
void EGLManager::swap_buffers() {
|
||||
if (!current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current_window->initialized) {
|
||||
WARN_PRINT("Current OpenGL window is uninitialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
GLDisplay ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglSwapBuffers(current_display.egl_display, current_window->egl_surface);
|
||||
}
|
||||
|
||||
void EGLManager::window_make_current(DisplayServerEnums::WindowID p_window_id) {
|
||||
if (p_window_id == DisplayServerEnums::INVALID_WINDOW_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLWindow &glwindow = windows[p_window_id];
|
||||
|
||||
if (&glwindow == current_window || !glwindow.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_window = &glwindow;
|
||||
|
||||
GLDisplay ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
RasterizerGLES3::set_screen_flipped_y(glwindow.flipped_y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EGLManager::set_use_vsync(bool p_use) {
|
||||
// We need an active window to get a display to set the vsync.
|
||||
if (!current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLDisplay &disp = displays[current_window->gldisplay_id];
|
||||
|
||||
int swap_interval = p_use ? 1 : 0;
|
||||
|
||||
if (!eglSwapInterval(disp.egl_display, swap_interval)) {
|
||||
WARN_PRINT("Could not set V-Sync mode.");
|
||||
}
|
||||
|
||||
use_vsync = p_use;
|
||||
}
|
||||
|
||||
bool EGLManager::is_using_vsync() const {
|
||||
return use_vsync;
|
||||
}
|
||||
|
||||
EGLContext EGLManager::get_context(DisplayServerEnums::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_context;
|
||||
}
|
||||
|
||||
EGLDisplay EGLManager::get_display(DisplayServerEnums::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(DisplayServerEnums::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
|
||||
// have to temporarily get a proper display and reload EGL once again to
|
||||
// initialize everything else.
|
||||
if (!gladLoaderLoadEGL(EGL_NO_DISPLAY)) {
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
|
||||
}
|
||||
|
||||
EGLDisplay tmp_display = EGL_NO_DISPLAY;
|
||||
|
||||
if (GLAD_EGL_EXT_platform_base) {
|
||||
#ifdef EGL_EXT_platform_base
|
||||
// eglGetPlatformDisplayEXT wants its attributes as EGLint.
|
||||
Vector<EGLint> attribs;
|
||||
for (const EGLAttrib &attrib : _get_platform_display_attributes()) {
|
||||
attribs.push_back((EGLint)attrib);
|
||||
}
|
||||
tmp_display = eglGetPlatformDisplayEXT(_get_platform_extension_enum(), p_native_display, attribs.ptr());
|
||||
#endif // EGL_EXT_platform_base
|
||||
} else {
|
||||
WARN_PRINT("EGL: EGL_EXT_platform_base not found during init, using default platform.");
|
||||
EGLNativeDisplayType *native_display_type = (EGLNativeDisplayType *)p_native_display;
|
||||
tmp_display = eglGetDisplay(*native_display_type);
|
||||
}
|
||||
|
||||
if (tmp_display == EGL_NO_DISPLAY) {
|
||||
eglTerminate(tmp_display);
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't get a valid initial EGL display.");
|
||||
}
|
||||
|
||||
eglInitialize(tmp_display, nullptr, nullptr);
|
||||
|
||||
int version = gladLoaderLoadEGL(tmp_display);
|
||||
if (!version) {
|
||||
eglTerminate(tmp_display);
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Can't load EGL dynamic library.");
|
||||
}
|
||||
|
||||
int major = GLAD_VERSION_MAJOR(version);
|
||||
int minor = GLAD_VERSION_MINOR(version);
|
||||
|
||||
print_verbose(vformat("Loaded EGL %d.%d", major, minor));
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!GLAD_EGL_VERSION_1_4, ERR_UNAVAILABLE, vformat("EGL version is too old! %d.%d < 1.4", major, minor));
|
||||
|
||||
eglTerminate(tmp_display);
|
||||
#endif
|
||||
|
||||
#ifdef EGL_ANDROID_blob_cache
|
||||
shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
|
||||
if (shader_cache_dir.is_empty()) {
|
||||
shader_cache_dir = "user://";
|
||||
}
|
||||
Error err = OK;
|
||||
Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
|
||||
if (da.is_null()) {
|
||||
ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
shader_cache_dir = String();
|
||||
} else {
|
||||
err = da->change_dir(String("shader_cache").path_join("EGL"));
|
||||
if (err != OK) {
|
||||
err = da->make_dir_recursive(String("shader_cache").path_join("EGL"));
|
||||
}
|
||||
if (err != OK) {
|
||||
ERR_PRINT("EGL: Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
shader_cache_dir = String();
|
||||
} else {
|
||||
shader_cache_dir = shader_cache_dir.path_join(String("shader_cache").path_join("EGL"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
String client_extensions_string = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
|
||||
// If the above method fails, we don't support client extensions, so there's nothing to check.
|
||||
if (eglGetError() == EGL_SUCCESS) {
|
||||
const char *platform = _get_platform_extension_name();
|
||||
if (!client_extensions_string.split(" ").has(platform)) {
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, vformat("EGL platform extension \"%s\" not found.", platform));
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
EGLManager::EGLManager() {
|
||||
}
|
||||
|
||||
EGLManager::~EGLManager() {
|
||||
for (unsigned int i = 0; i < displays.size(); i++) {
|
||||
eglTerminate(displays[i].egl_display);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EGL_ENABLED
|
||||
120
engine/drivers/egl/egl_manager.h
Normal file
120
engine/drivers/egl/egl_manager.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**************************************************************************/
|
||||
/* egl_manager.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef EGL_ENABLED
|
||||
|
||||
// These must come first to avoid windows.h mess.
|
||||
#include "platform_gl.h"
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "servers/display/display_server_enums.h"
|
||||
|
||||
class EGLManager {
|
||||
private:
|
||||
// An EGL-side representation of a display with its own rendering
|
||||
// context.
|
||||
struct GLDisplay {
|
||||
void *display = nullptr;
|
||||
|
||||
EGLDisplay egl_display = EGL_NO_DISPLAY;
|
||||
EGLContext egl_context = EGL_NO_CONTEXT;
|
||||
EGLConfig egl_config = nullptr;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
bool has_EGL_ANGLE_surface_orientation = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
// EGL specific window data.
|
||||
struct GLWindow {
|
||||
bool initialized = false;
|
||||
#ifdef WINDOWS_ENABLED
|
||||
bool flipped_y = false;
|
||||
#endif
|
||||
|
||||
// An handle to the GLDisplay associated with this window.
|
||||
int gldisplay_id = -1;
|
||||
|
||||
EGLSurface egl_surface = EGL_NO_SURFACE;
|
||||
};
|
||||
|
||||
LocalVector<GLDisplay> displays;
|
||||
LocalVector<GLWindow> windows;
|
||||
|
||||
GLWindow *current_window = nullptr;
|
||||
|
||||
// On EGL the default swap interval is 1 and thus vsync is on by default.
|
||||
bool use_vsync = true;
|
||||
|
||||
virtual const char *_get_platform_extension_name() const = 0;
|
||||
virtual EGLenum _get_platform_extension_enum() const = 0;
|
||||
virtual EGLenum _get_platform_api_enum() const = 0;
|
||||
virtual Vector<EGLAttrib> _get_platform_display_attributes() const = 0;
|
||||
virtual Vector<EGLint> _get_platform_context_attribs() const = 0;
|
||||
|
||||
#ifdef EGL_ANDROID_blob_cache
|
||||
static String shader_cache_dir;
|
||||
|
||||
static void _set_cache(const void *p_key, EGLsizeiANDROID p_key_size, const void *p_value, EGLsizeiANDROID p_value_size);
|
||||
static EGLsizeiANDROID _get_cache(const void *p_key, EGLsizeiANDROID p_key_size, void *p_value, EGLsizeiANDROID p_value_size);
|
||||
#endif
|
||||
|
||||
int _get_gldisplay_id(void *p_display);
|
||||
Error _gldisplay_create_context(GLDisplay &p_gldisplay);
|
||||
|
||||
public:
|
||||
int display_get_native_visual_id(void *p_display);
|
||||
|
||||
Error open_display(void *p_display);
|
||||
Error window_create(DisplayServerEnums::WindowID p_window_id, void *p_display, void *p_native_window, int p_width, int p_height);
|
||||
|
||||
void window_destroy(DisplayServerEnums::WindowID p_window_id);
|
||||
|
||||
void release_current();
|
||||
void swap_buffers();
|
||||
|
||||
void window_make_current(DisplayServerEnums::WindowID p_window_id);
|
||||
|
||||
void set_use_vsync(bool p_use);
|
||||
bool is_using_vsync() const;
|
||||
|
||||
EGLContext get_context(DisplayServerEnums::WindowID p_window_id);
|
||||
EGLDisplay get_display(DisplayServerEnums::WindowID p_window_id);
|
||||
EGLConfig get_config(DisplayServerEnums::WindowID p_window_id);
|
||||
|
||||
Error initialize(void *p_native_display = nullptr);
|
||||
|
||||
EGLManager();
|
||||
virtual ~EGLManager();
|
||||
};
|
||||
|
||||
#endif // EGL_ENABLED
|
||||
26
engine/drivers/gl_context/SCsub
Normal file
26
engine/drivers/gl_context/SCsub
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
if env["platform"] in ["macos", "windows", "linuxbsd"]:
|
||||
# Thirdparty source files
|
||||
thirdparty_dir = "#thirdparty/glad/"
|
||||
thirdparty_sources = ["gl.c"]
|
||||
|
||||
if not env.get("angle_libs"):
|
||||
thirdparty_sources += ["egl.c"]
|
||||
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env.Prepend(CPPPATH=[thirdparty_dir])
|
||||
|
||||
env.Append(CPPDEFINES=["GLAD_ENABLED"])
|
||||
env.Append(CPPDEFINES=["EGL_ENABLED"])
|
||||
|
||||
env_thirdparty = env.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources)
|
||||
|
||||
# Godot source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
11
engine/drivers/gles3/SCsub
Normal file
11
engine/drivers/gles3/SCsub
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
|
||||
SConscript("shaders/SCsub")
|
||||
SConscript("storage/SCsub")
|
||||
SConscript("effects/SCsub")
|
||||
SConscript("environment/SCsub")
|
||||
6
engine/drivers/gles3/effects/SCsub
Normal file
6
engine/drivers/gles3/effects/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
336
engine/drivers/gles3/effects/copy_effects.cpp
Normal file
336
engine/drivers/gles3/effects/copy_effects.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/**************************************************************************/
|
||||
/* copy_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "copy_effects.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
CopyEffects *CopyEffects::singleton = nullptr;
|
||||
|
||||
CopyEffects *CopyEffects::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
CopyEffects::CopyEffects() {
|
||||
singleton = this;
|
||||
|
||||
copy.shader.initialize();
|
||||
copy.shader_version = copy.shader.version_create();
|
||||
copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT);
|
||||
|
||||
{ // Screen Triangle.
|
||||
glGenBuffers(1, &screen_triangle);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
|
||||
|
||||
const float qv[6] = {
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
3.0f,
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
3.0f,
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
|
||||
glGenVertexArrays(1, &screen_triangle_array);
|
||||
glBindVertexArray(screen_triangle_array);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
|
||||
glVertexAttribPointer(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::ARRAY_VERTEX);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
}
|
||||
|
||||
{ // Screen Quad
|
||||
|
||||
glGenBuffers(1, &quad);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quad);
|
||||
|
||||
const float qv[12] = {
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
|
||||
glGenVertexArrays(1, &quad_array);
|
||||
glBindVertexArray(quad_array);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quad);
|
||||
glVertexAttribPointer(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::ARRAY_VERTEX);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
}
|
||||
}
|
||||
|
||||
CopyEffects::~CopyEffects() {
|
||||
singleton = nullptr;
|
||||
glDeleteBuffers(1, &screen_triangle);
|
||||
glDeleteVertexArrays(1, &screen_triangle_array);
|
||||
glDeleteBuffers(1, &quad);
|
||||
glDeleteVertexArrays(1, &quad_array);
|
||||
copy.shader.version_free(copy.shader_version);
|
||||
}
|
||||
|
||||
void CopyEffects::copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb) {
|
||||
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
|
||||
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION, specializations);
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod, bool p_linear_to_srgb) {
|
||||
ERR_FAIL_COND(p_type != Texture::TYPE_LAYERED && p_type != Texture::TYPE_3D);
|
||||
|
||||
CopyShaderGLES3::ShaderVariant variant = p_type == Texture::TYPE_LAYERED
|
||||
? CopyShaderGLES3::MODE_COPY_SECTION_2D_ARRAY
|
||||
: CopyShaderGLES3::MODE_COPY_SECTION_3D;
|
||||
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
|
||||
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant, specializations);
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb) {
|
||||
CopyShaderGLES3::ShaderVariant variant = CopyShaderGLES3::MODE_LENS_DISTORTION;
|
||||
|
||||
uint64_t specializations = p_linear_to_srgb ? CopyShaderGLES3::CONVERT_LINEAR_TO_SRGB : 0;
|
||||
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, variant, specializations);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::LOD, 0.0, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::EYE_CENTER, p_eye_center.x, p_eye_center.y, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::K1, p_k1, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::K2, p_k1, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::UPSCALE, p_upscale, copy.shader_version, variant, specializations);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::ASPECT_RATIO, p_aspect_ration, copy.shader_version, variant, specializations);
|
||||
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) {
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE);
|
||||
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_screen(float p_multiply) {
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SCREEN);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::MULTIPLY, p_multiply, copy.shader_version, CopyShaderGLES3::MODE_SCREEN);
|
||||
|
||||
draw_screen_triangle();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_cube_to_rect(const Rect2 &p_rect) {
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_OCTAHEDRAL);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_OCTAHEDRAL);
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::copy_cube_to_panorama(float p_mip_level) {
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_PANORAMA);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::MIP_LEVEL, p_mip_level, copy.shader_version, CopyShaderGLES3::MODE_CUBE_TO_PANORAMA);
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
// Intended for efficiently mipmapping textures.
|
||||
void CopyEffects::bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region) {
|
||||
GLuint framebuffers[2];
|
||||
glGenFramebuffers(2, framebuffers);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, 0);
|
||||
|
||||
Rect2i source_region = p_region;
|
||||
Rect2i dest_region = p_region;
|
||||
for (int i = 1; i < p_mipmap_count; i++) {
|
||||
dest_region.position.x >>= 1;
|
||||
dest_region.position.y >>= 1;
|
||||
dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[i % 2]);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
|
||||
glBlitFramebuffer(source_region.position.x, source_region.position.y, source_region.position.x + source_region.size.x, source_region.position.y + source_region.size.y,
|
||||
dest_region.position.x, dest_region.position.y, dest_region.position.x + dest_region.size.x, dest_region.position.y + dest_region.size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[i % 2]);
|
||||
source_region = dest_region;
|
||||
}
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
glDeleteFramebuffers(2, framebuffers);
|
||||
}
|
||||
|
||||
// Intended for approximating a gaussian blur. Used for 2D backbuffer mipmaps. Slightly less efficient than bilinear_blur().
|
||||
void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size) {
|
||||
GLuint framebuffer;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, p_source_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
Size2i base_size = p_size;
|
||||
|
||||
Rect2i source_region = p_region;
|
||||
Rect2i dest_region = p_region;
|
||||
|
||||
Size2 float_size = Size2(p_size);
|
||||
Rect2 normalized_source_region = Rect2(p_region);
|
||||
normalized_source_region.position = normalized_source_region.position / float_size;
|
||||
normalized_source_region.size = normalized_source_region.size / float_size;
|
||||
Rect2 normalized_dest_region = Rect2(p_region);
|
||||
for (int i = 1; i < p_mipmap_count; i++) {
|
||||
dest_region.position.x >>= 1;
|
||||
dest_region.position.y >>= 1;
|
||||
dest_region.size = Size2i(dest_region.size.x >> 1, dest_region.size.y >> 1).maxi(1);
|
||||
base_size.x >>= 1;
|
||||
base_size.y >>= 1;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, p_source_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i - 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
|
||||
#ifdef DEV_ENABLED
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
WARN_PRINT("Could not bind Gaussian blur framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
|
||||
}
|
||||
#endif
|
||||
|
||||
glViewport(0, 0, base_size.x, base_size.y);
|
||||
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
float_size = Size2(base_size);
|
||||
normalized_dest_region.position = Size2(dest_region.position) / float_size;
|
||||
normalized_dest_region.size = Size2(dest_region.size) / float_size;
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, normalized_dest_region.position.x, normalized_dest_region.position.y, normalized_dest_region.size.x, normalized_dest_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, normalized_source_region.position.x, normalized_source_region.position.y, normalized_source_region.size.x, normalized_source_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::PIXEL_SIZE, 1.0 / float_size.x, 1.0 / float_size.y, copy.shader_version, CopyShaderGLES3::MODE_GAUSSIAN_BLUR);
|
||||
|
||||
draw_screen_quad();
|
||||
|
||||
source_region = dest_region;
|
||||
normalized_source_region = normalized_dest_region;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, p_mipmap_count - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glViewport(0, 0, p_size.x, p_size.y);
|
||||
}
|
||||
|
||||
void CopyEffects::set_color(const Color &p_color, const Rect2i &p_region) {
|
||||
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_region.position.x, p_region.position.y, p_region.size.x, p_region.size.y, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
|
||||
copy.shader.version_set_uniform(CopyShaderGLES3::COLOR_IN, p_color, copy.shader_version, CopyShaderGLES3::MODE_SIMPLE_COLOR);
|
||||
draw_screen_quad();
|
||||
}
|
||||
|
||||
void CopyEffects::draw_screen_triangle() {
|
||||
glBindVertexArray(screen_triangle_array);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void CopyEffects::draw_screen_quad() {
|
||||
glBindVertexArray(quad_array);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
79
engine/drivers/gles3/effects/copy_effects.h
Normal file
79
engine/drivers/gles3/effects/copy_effects.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**************************************************************************/
|
||||
/* copy_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/shaders/effects/copy.glsl.gen.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class CopyEffects {
|
||||
private:
|
||||
struct Copy {
|
||||
CopyShaderGLES3 shader;
|
||||
RID shader_version;
|
||||
} copy;
|
||||
|
||||
static CopyEffects *singleton;
|
||||
|
||||
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
|
||||
GLuint screen_triangle = 0;
|
||||
GLuint screen_triangle_array = 0;
|
||||
|
||||
// Use for rect-based effects.
|
||||
GLuint quad = 0;
|
||||
GLuint quad_array = 0;
|
||||
|
||||
public:
|
||||
static CopyEffects *get_singleton();
|
||||
|
||||
CopyEffects();
|
||||
~CopyEffects();
|
||||
|
||||
// These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array.
|
||||
void copy_to_rect(const Rect2 &p_rect, bool p_linear_to_srgb = false);
|
||||
void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f, bool p_linear_to_srgb = false);
|
||||
void copy_with_lens_distortion(const Rect2 &p_rect, float p_layer, const Vector2 &p_eye_center, float p_k1, float p_k2, float p_upscale, float p_aspect_ration, bool p_linear_to_srgb = false);
|
||||
void copy_to_and_from_rect(const Rect2 &p_rect);
|
||||
void copy_screen(float p_multiply = 1.0);
|
||||
void copy_cube_to_rect(const Rect2 &p_rect);
|
||||
void copy_cube_to_panorama(float p_mip_level);
|
||||
void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region);
|
||||
void gaussian_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region, const Size2i &p_size);
|
||||
void set_color(const Color &p_color, const Rect2i &p_region);
|
||||
void draw_screen_triangle();
|
||||
void draw_screen_quad();
|
||||
};
|
||||
|
||||
} //namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
213
engine/drivers/gles3/effects/cubemap_filter.cpp
Normal file
213
engine/drivers/gles3/effects/cubemap_filter.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/**************************************************************************/
|
||||
/* cubemap_filter.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "cubemap_filter.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
CubemapFilter *CubemapFilter::singleton = nullptr;
|
||||
|
||||
CubemapFilter::CubemapFilter() {
|
||||
singleton = this;
|
||||
// Use a factor 4 larger for the compatibility renderer to make up for the fact
|
||||
// That we don't use an array texture. We will reduce samples on low roughness
|
||||
// to compensate.
|
||||
ggx_samples = 4 * uint32_t(GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples"));
|
||||
|
||||
{
|
||||
String defines;
|
||||
defines += "\n#define MAX_SAMPLE_COUNT " + itos(ggx_samples) + "\n";
|
||||
cubemap_filter.shader.initialize(defines);
|
||||
cubemap_filter.shader_version = cubemap_filter.shader.version_create();
|
||||
}
|
||||
|
||||
{ // Screen Triangle.
|
||||
glGenBuffers(1, &screen_triangle);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
|
||||
|
||||
const float qv[6] = {
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
3.0f,
|
||||
3.0f,
|
||||
-1.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(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::ARRAY_VERTEX);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
}
|
||||
}
|
||||
|
||||
CubemapFilter::~CubemapFilter() {
|
||||
glDeleteBuffers(1, &screen_triangle);
|
||||
glDeleteVertexArrays(1, &screen_triangle_array);
|
||||
|
||||
cubemap_filter.shader.version_free(cubemap_filter.shader_version);
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
// Helper functions for IBL filtering
|
||||
|
||||
Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
|
||||
// Compute distribution direction
|
||||
float phi = 2.0 * Math::PI * xi.x;
|
||||
float cos_theta = std::sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
|
||||
float sin_theta = std::sqrt(1.0 - cos_theta * cos_theta);
|
||||
|
||||
// Convert to spherical direction
|
||||
Vector3 half_vector;
|
||||
half_vector.x = sin_theta * std::cos(phi);
|
||||
half_vector.y = sin_theta * std::sin(phi);
|
||||
half_vector.z = cos_theta;
|
||||
|
||||
return half_vector;
|
||||
}
|
||||
|
||||
float distribution_GGX(float NdotH, float roughness4) {
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
|
||||
denom = Math::PI * denom * denom;
|
||||
|
||||
return roughness4 / denom;
|
||||
}
|
||||
|
||||
float radical_inverse_vdC(uint32_t bits) {
|
||||
bits = (bits << 16) | (bits >> 16);
|
||||
bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
|
||||
bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
|
||||
bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
|
||||
bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
|
||||
|
||||
return float(bits) * 2.3283064365386963e-10;
|
||||
}
|
||||
|
||||
Vector2 hammersley(uint32_t i, uint32_t N) {
|
||||
return Vector2(float(i) / float(N), radical_inverse_vdC(i));
|
||||
}
|
||||
|
||||
void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer) {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, p_source_cubemap);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
|
||||
|
||||
CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
|
||||
|
||||
if (p_layer == 0) {
|
||||
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
// Copy over base layer without filtering.
|
||||
mode = CubemapFilterShaderGLES3::MODE_COPY;
|
||||
}
|
||||
|
||||
int size = p_source_size >> p_layer;
|
||||
glViewport(0, 0, size, size);
|
||||
glBindVertexArray(screen_triangle_array);
|
||||
|
||||
bool success = cubemap_filter.shader.version_bind_shader(cubemap_filter.shader_version, mode);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_layer > 0) {
|
||||
const uint32_t sample_counts[5] = { 1, ggx_samples / 16, ggx_samples / 8, ggx_samples / 4, ggx_samples };
|
||||
uint32_t sample_count = sample_counts[MIN(4, p_layer)];
|
||||
|
||||
float roughness = float(p_layer) / (p_mipmap_count - 1);
|
||||
roughness *= roughness; // Convert to non-perceptual roughness.
|
||||
float roughness4 = roughness * roughness;
|
||||
roughness4 *= roughness4;
|
||||
|
||||
float solid_angle_texel = 4.0 * Math::PI / float(6 * size * size);
|
||||
|
||||
LocalVector<float> sample_directions;
|
||||
sample_directions.resize(4 * sample_count);
|
||||
|
||||
uint32_t index = 0;
|
||||
float weight = 0.0;
|
||||
for (uint32_t i = 0; i < sample_count; i++) {
|
||||
Vector2 xi = hammersley(i, sample_count);
|
||||
Vector3 dir = importance_sample_GGX(xi, roughness4);
|
||||
Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
|
||||
|
||||
if (light_vec.z <= 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sample_directions[index * 4] = light_vec.x;
|
||||
sample_directions[index * 4 + 1] = light_vec.y;
|
||||
sample_directions[index * 4 + 2] = light_vec.z;
|
||||
|
||||
float D = distribution_GGX(dir.z, roughness4);
|
||||
float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
|
||||
|
||||
float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
|
||||
|
||||
float mip_level = MAX(0.5 * std::log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_layer - 3)), 1.0);
|
||||
|
||||
sample_directions[index * 4 + 3] = mip_level;
|
||||
weight += light_vec.z;
|
||||
index++;
|
||||
}
|
||||
|
||||
glUniform4fv(cubemap_filter.shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, cubemap_filter.shader_version, mode), sample_count, sample_directions.ptr());
|
||||
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, cubemap_filter.shader_version, mode);
|
||||
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, cubemap_filter.shader_version, mode);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_dest_cubemap, p_layer);
|
||||
#ifdef DEBUG_ENABLED
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
|
||||
}
|
||||
#endif
|
||||
cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, cubemap_filter.shader_version, mode);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
67
engine/drivers/gles3/effects/cubemap_filter.h
Normal file
67
engine/drivers/gles3/effects/cubemap_filter.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************/
|
||||
/* cubemap_filter.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class CubemapFilter {
|
||||
private:
|
||||
struct CMF {
|
||||
CubemapFilterShaderGLES3 shader;
|
||||
RID shader_version;
|
||||
} cubemap_filter;
|
||||
|
||||
static CubemapFilter *singleton;
|
||||
|
||||
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
|
||||
GLuint screen_triangle = 0;
|
||||
GLuint screen_triangle_array = 0;
|
||||
|
||||
uint32_t ggx_samples = 128;
|
||||
|
||||
public:
|
||||
static CubemapFilter *get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
CubemapFilter();
|
||||
~CubemapFilter();
|
||||
|
||||
void filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer);
|
||||
};
|
||||
|
||||
} //namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
125
engine/drivers/gles3/effects/feed_effects.cpp
Normal file
125
engine/drivers/gles3/effects/feed_effects.cpp
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "feed_effects.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "servers/rendering/rendering_server_enums.h"
|
||||
|
||||
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(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::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
|
||||
65
engine/drivers/gles3/effects/feed_effects.h
Normal file
65
engine/drivers/gles3/effects/feed_effects.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
174
engine/drivers/gles3/effects/glow.cpp
Normal file
174
engine/drivers/gles3/effects/glow.cpp
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/**************************************************************************/
|
||||
/* glow.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "glow.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
Glow *Glow::singleton = nullptr;
|
||||
|
||||
Glow *Glow::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
Glow::Glow() {
|
||||
singleton = this;
|
||||
|
||||
glow.shader.initialize();
|
||||
glow.shader_version = glow.shader.version_create();
|
||||
|
||||
{ // 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(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::ARRAY_VERTEX);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
}
|
||||
}
|
||||
|
||||
Glow::~Glow() {
|
||||
glDeleteBuffers(1, &screen_triangle);
|
||||
glDeleteVertexArrays(1, &screen_triangle_array);
|
||||
|
||||
glow.shader.version_free(glow.shader_version);
|
||||
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void Glow::_draw_screen_triangle() {
|
||||
glBindVertexArray(screen_triangle_array);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Glow::process_glow(GLuint p_source_color, Size2i p_size, const Glow::Level *p_glow_buffers, uint32_t p_view, bool p_use_multiview) {
|
||||
ERR_FAIL_COND(p_source_color == 0);
|
||||
ERR_FAIL_COND(p_glow_buffers[3].color == 0);
|
||||
|
||||
// Reset some OpenGL state...
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// Start with our filter pass
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[0].fbo);
|
||||
glViewport(0, 0, p_glow_buffers[0].size.x, p_glow_buffers[0].size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D, p_source_color);
|
||||
|
||||
uint64_t specialization = p_use_multiview ? GlowShaderGLES3::USE_MULTIVIEW : 0;
|
||||
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[0].size.x, 1.0 / p_glow_buffers[0].size.y, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::VIEW, float(p_view), glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::LUMINANCE_MULTIPLIER, luminance_multiplier, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_BLOOM, glow_bloom, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_THRESHOLD, glow_hdr_bleed_threshold, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_SCALE, glow_hdr_bleed_scale, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_LUMINANCE_CAP, glow_hdr_luminance_cap, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization);
|
||||
|
||||
_draw_screen_triangle();
|
||||
}
|
||||
|
||||
// Continue with downsampling
|
||||
{
|
||||
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE, 0);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo);
|
||||
glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i - 1].color);
|
||||
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE);
|
||||
|
||||
_draw_screen_triangle();
|
||||
}
|
||||
}
|
||||
|
||||
// Now upsample
|
||||
{
|
||||
bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE, 0);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 2; i >= 0; i--) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo);
|
||||
glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i + 1].color);
|
||||
|
||||
glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE);
|
||||
|
||||
_draw_screen_triangle();
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glUseProgram(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
86
engine/drivers/gles3/effects/glow.h
Normal file
86
engine/drivers/gles3/effects/glow.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**************************************************************************/
|
||||
/* glow.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/shaders/effects/glow.glsl.gen.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class Glow {
|
||||
private:
|
||||
static Glow *singleton;
|
||||
|
||||
struct GlowShader {
|
||||
GlowShaderGLES3 shader;
|
||||
RID shader_version;
|
||||
} glow;
|
||||
|
||||
float luminance_multiplier = 1.0;
|
||||
|
||||
float glow_intensity = 1.0;
|
||||
float glow_bloom = 0.0;
|
||||
float glow_hdr_bleed_threshold = 1.0;
|
||||
float glow_hdr_bleed_scale = 2.0;
|
||||
float glow_hdr_luminance_cap = 12.0;
|
||||
|
||||
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
|
||||
GLuint screen_triangle = 0;
|
||||
GLuint screen_triangle_array = 0;
|
||||
|
||||
void _draw_screen_triangle();
|
||||
|
||||
public:
|
||||
struct Level {
|
||||
Size2i size;
|
||||
GLuint color = 0;
|
||||
GLuint fbo = 0;
|
||||
};
|
||||
|
||||
static Glow *get_singleton();
|
||||
|
||||
Glow();
|
||||
~Glow();
|
||||
|
||||
void set_intensity(float p_value) { glow_intensity = p_value; }
|
||||
void set_luminance_multiplier(float p_luminance_multiplier) { luminance_multiplier = p_luminance_multiplier; }
|
||||
void set_glow_bloom(float p_bloom) { glow_bloom = p_bloom; }
|
||||
void set_glow_hdr_bleed_threshold(float p_threshold) { glow_hdr_bleed_threshold = p_threshold; }
|
||||
void set_glow_hdr_bleed_scale(float p_scale) { glow_hdr_bleed_scale = p_scale; }
|
||||
void set_glow_hdr_luminance_cap(float p_cap) { glow_hdr_luminance_cap = p_cap; }
|
||||
|
||||
void process_glow(GLuint p_source_color, Size2i p_size, const Level *p_glow_buffers, uint32_t p_view = 0, bool p_use_multiview = false);
|
||||
};
|
||||
|
||||
} //namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
190
engine/drivers/gles3/effects/post_effects.cpp
Normal file
190
engine/drivers/gles3/effects/post_effects.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/**************************************************************************/
|
||||
/* post_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "post_effects.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
PostEffects *PostEffects::singleton = nullptr;
|
||||
|
||||
PostEffects *PostEffects::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
PostEffects::PostEffects() {
|
||||
singleton = this;
|
||||
|
||||
post.shader.initialize();
|
||||
post.shader_version = post.shader.version_create();
|
||||
post.shader.version_bind_shader(post.shader_version, PostShaderGLES3::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(RSE::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
|
||||
glEnableVertexAttribArray(RSE::ARRAY_VERTEX);
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
|
||||
}
|
||||
}
|
||||
|
||||
PostEffects::~PostEffects() {
|
||||
singleton = nullptr;
|
||||
glDeleteBuffers(1, &screen_triangle);
|
||||
glDeleteVertexArrays(1, &screen_triangle_array);
|
||||
post.shader.version_free(post.shader_version);
|
||||
}
|
||||
|
||||
void PostEffects::_draw_screen_triangle() {
|
||||
glBindVertexArray(screen_triangle_array);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void PostEffects::post_copy(
|
||||
GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color,
|
||||
GLuint p_source_depth, bool p_ssao_enabled, int p_ssao_quality_level, float p_ssao_strength, float p_ssao_radius,
|
||||
Size2i p_source_size, float p_luminance_multiplier, const Glow::Level *p_glow_buffers, float p_glow_intensity,
|
||||
float p_srgb_white, uint32_t p_view, bool p_use_multiview, uint64_t p_spec_constants) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
|
||||
glViewport(0, 0, p_dest_size.x, p_dest_size.y);
|
||||
|
||||
PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT;
|
||||
uint64_t flags = p_spec_constants;
|
||||
if (p_use_multiview) {
|
||||
flags |= PostShaderGLES3::USE_MULTIVIEW;
|
||||
}
|
||||
if (p_glow_buffers != nullptr) {
|
||||
flags |= PostShaderGLES3::USE_GLOW;
|
||||
}
|
||||
if (p_ssao_enabled) {
|
||||
if (p_ssao_quality_level == RSE::ENV_SSAO_QUALITY_VERY_LOW) {
|
||||
flags |= PostShaderGLES3::USE_SSAO_ABYSS;
|
||||
} else if (p_ssao_quality_level == RSE::ENV_SSAO_QUALITY_LOW) {
|
||||
flags |= PostShaderGLES3::USE_SSAO_LOW;
|
||||
} else if (p_ssao_quality_level == RSE::ENV_SSAO_QUALITY_HIGH) {
|
||||
flags |= PostShaderGLES3::USE_SSAO_HIGH;
|
||||
} else if (p_ssao_quality_level == RSE::ENV_SSAO_QUALITY_ULTRA) {
|
||||
flags |= PostShaderGLES3::USE_SSAO_MEGA;
|
||||
} else {
|
||||
flags |= PostShaderGLES3::USE_SSAO_MED;
|
||||
}
|
||||
}
|
||||
if (p_luminance_multiplier != 1.0) {
|
||||
flags |= PostShaderGLES3::USE_LUMINANCE_MULTIPLIER;
|
||||
}
|
||||
|
||||
bool success = post.shader.version_bind_shader(post.shader_version, mode, flags);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLenum texture_target = p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(texture_target, p_source_color);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
if (p_ssao_enabled) {
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(texture_target, p_source_depth);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Thanks to mrjustaguy!
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
post.shader.version_set_uniform(PostShaderGLES3::SSAO_INTENSITY, p_ssao_strength, post.shader_version, mode, flags);
|
||||
post.shader.version_set_uniform(PostShaderGLES3::SSAO_RADIUS_FRAC, p_ssao_radius, post.shader_version, mode, flags);
|
||||
post.shader.version_set_uniform(PostShaderGLES3::SSAO_PRN_UV, // This converts the UV coordinate into a pseudo-random number.
|
||||
p_source_size.x * 1.087f * ((1.0f + sqrt(5.0f)) / 2.0f),
|
||||
p_source_size.y * 1.087f * ((9.0f + sqrt(221.0f)) / 10.0f),
|
||||
post.shader_version, mode, flags);
|
||||
}
|
||||
|
||||
if (p_glow_buffers != nullptr) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, p_glow_buffers[0].color);
|
||||
|
||||
post.shader.version_set_uniform(PostShaderGLES3::PIXEL_SIZE, 1.0 / p_source_size.x, 1.0 / p_source_size.y, post.shader_version, mode, flags);
|
||||
post.shader.version_set_uniform(PostShaderGLES3::GLOW_INTENSITY, p_glow_intensity, post.shader_version, mode, flags);
|
||||
post.shader.version_set_uniform(PostShaderGLES3::SRGB_WHITE, p_srgb_white, post.shader_version, mode, flags);
|
||||
}
|
||||
|
||||
post.shader.version_set_uniform(PostShaderGLES3::VIEW, float(p_view), post.shader_version, mode, flags);
|
||||
post.shader.version_set_uniform(PostShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, post.shader_version, mode, flags);
|
||||
|
||||
_draw_screen_triangle();
|
||||
|
||||
// Reset state
|
||||
if (p_glow_buffers != nullptr) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
if (p_ssao_enabled) {
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(texture_target, 0);
|
||||
}
|
||||
|
||||
// Return back to nearest
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glBindTexture(texture_target, 0);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_TRUE);
|
||||
glUseProgram(0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
69
engine/drivers/gles3/effects/post_effects.h
Normal file
69
engine/drivers/gles3/effects/post_effects.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**************************************************************************/
|
||||
/* post_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/effects/glow.h"
|
||||
#include "drivers/gles3/shaders/effects/post.glsl.gen.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class PostEffects {
|
||||
private:
|
||||
struct Post {
|
||||
PostShaderGLES3 shader;
|
||||
RID shader_version;
|
||||
} post;
|
||||
|
||||
static PostEffects *singleton;
|
||||
|
||||
// Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal.
|
||||
GLuint screen_triangle = 0;
|
||||
GLuint screen_triangle_array = 0;
|
||||
|
||||
void _draw_screen_triangle();
|
||||
|
||||
public:
|
||||
static PostEffects *get_singleton();
|
||||
|
||||
PostEffects();
|
||||
~PostEffects();
|
||||
|
||||
void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color,
|
||||
GLuint p_source_depth, bool p_ssao_enabled, int p_ssao_quality_level, float p_ssao_strength, float p_ssao_radius,
|
||||
Size2i p_source_size, float p_luminance_multiplier, const Glow::Level *p_glow_buffers, float p_glow_intensity,
|
||||
float p_srgb_white, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
|
||||
};
|
||||
|
||||
} //namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
6
engine/drivers/gles3/environment/SCsub
Normal file
6
engine/drivers/gles3/environment/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
69
engine/drivers/gles3/environment/fog.cpp
Normal file
69
engine/drivers/gles3/environment/fog.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**************************************************************************/
|
||||
/* fog.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "fog.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/math/aabb.h"
|
||||
#include "core/templates/rid.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
/* FOG */
|
||||
|
||||
RID Fog::fog_volume_allocate() {
|
||||
return RID();
|
||||
}
|
||||
|
||||
void Fog::fog_volume_initialize(RID p_rid) {
|
||||
}
|
||||
|
||||
void Fog::fog_volume_free(RID p_rid) {
|
||||
}
|
||||
|
||||
void Fog::fog_volume_set_shape(RID p_fog_volume, RSE::FogVolumeShape p_shape) {
|
||||
}
|
||||
|
||||
void Fog::fog_volume_set_size(RID p_fog_volume, const Vector3 &p_size) {
|
||||
}
|
||||
|
||||
void Fog::fog_volume_set_material(RID p_fog_volume, RID p_material) {
|
||||
}
|
||||
|
||||
AABB Fog::fog_volume_get_aabb(RID p_fog_volume) const {
|
||||
return AABB();
|
||||
}
|
||||
|
||||
RSE::FogVolumeShape Fog::fog_volume_get_shape(RID p_fog_volume) const {
|
||||
return RSE::FOG_VOLUME_SHAPE_BOX;
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
56
engine/drivers/gles3/environment/fog.h
Normal file
56
engine/drivers/gles3/environment/fog.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**************************************************************************/
|
||||
/* fog.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "servers/rendering/environment/renderer_fog.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class Fog : public RendererFog {
|
||||
public:
|
||||
/* FOG VOLUMES */
|
||||
|
||||
virtual RID fog_volume_allocate() override;
|
||||
virtual void fog_volume_initialize(RID p_rid) override;
|
||||
virtual void fog_volume_free(RID p_rid) override;
|
||||
|
||||
virtual void fog_volume_set_shape(RID p_fog_volume, RSE::FogVolumeShape p_shape) override;
|
||||
virtual void fog_volume_set_size(RID p_fog_volume, const Vector3 &p_size) override;
|
||||
virtual void fog_volume_set_material(RID p_fog_volume, RID p_material) override;
|
||||
virtual AABB fog_volume_get_aabb(RID p_fog_volume) const override;
|
||||
virtual RSE::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override;
|
||||
};
|
||||
|
||||
} // namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
149
engine/drivers/gles3/environment/gi.cpp
Normal file
149
engine/drivers/gles3/environment/gi.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/**************************************************************************/
|
||||
/* gi.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gi.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/math/aabb.h"
|
||||
#include "core/math/transform_3d.h"
|
||||
#include "core/math/vector3i.h"
|
||||
#include "core/templates/rid.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
using namespace GLES3;
|
||||
|
||||
/* VOXEL GI API */
|
||||
|
||||
RID GI::voxel_gi_allocate() {
|
||||
return RID();
|
||||
}
|
||||
|
||||
void GI::voxel_gi_free(RID p_rid) {
|
||||
}
|
||||
|
||||
void GI::voxel_gi_initialize(RID p_rid) {
|
||||
}
|
||||
|
||||
void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
|
||||
}
|
||||
|
||||
AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const {
|
||||
return AABB();
|
||||
}
|
||||
|
||||
Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const {
|
||||
return Vector3i();
|
||||
}
|
||||
|
||||
Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
|
||||
return Vector<uint8_t>();
|
||||
}
|
||||
|
||||
Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const {
|
||||
return Vector<uint8_t>();
|
||||
}
|
||||
|
||||
Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const {
|
||||
return Vector<uint8_t>();
|
||||
}
|
||||
|
||||
Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const {
|
||||
return Vector<int>();
|
||||
}
|
||||
|
||||
Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
|
||||
return Transform3D();
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_propagation(RID p_voxel_gi) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_range) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_energy(RID p_voxel_gi) const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_bias(RID p_voxel_gi) const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) {
|
||||
}
|
||||
|
||||
float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
|
||||
}
|
||||
|
||||
bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
|
||||
}
|
||||
|
||||
bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GI::sdfgi_reset() {
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
88
engine/drivers/gles3/environment/gi.h
Normal file
88
engine/drivers/gles3/environment/gi.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**************************************************************************/
|
||||
/* gi.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "servers/rendering/environment/renderer_gi.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class GI : public RendererGI {
|
||||
public:
|
||||
/* VOXEL GI API */
|
||||
|
||||
virtual RID voxel_gi_allocate() override;
|
||||
virtual void voxel_gi_free(RID p_rid) override;
|
||||
virtual void voxel_gi_initialize(RID p_rid) override;
|
||||
virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
|
||||
|
||||
virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
|
||||
virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
|
||||
virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
|
||||
|
||||
virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
|
||||
virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_energy(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override;
|
||||
virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_bias(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
|
||||
virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
|
||||
virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
|
||||
virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
|
||||
|
||||
virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override;
|
||||
|
||||
virtual void sdfgi_reset() override;
|
||||
};
|
||||
|
||||
}; // namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
2940
engine/drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
2940
engine/drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
File diff suppressed because it is too large
Load diff
384
engine/drivers/gles3/rasterizer_canvas_gles3.h
Normal file
384
engine/drivers/gles3/rasterizer_canvas_gles3.h
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_canvas_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
|
||||
#include "drivers/gles3/shaders/canvas_occlusion.glsl.gen.h"
|
||||
#include "drivers/gles3/storage/material_storage.h"
|
||||
#include "servers/rendering/renderer_canvas_render.h"
|
||||
#include "servers/rendering/rendering_server_enums.h"
|
||||
|
||||
class RasterizerCanvasGLES3 : public RendererCanvasRender {
|
||||
static RasterizerCanvasGLES3 *singleton;
|
||||
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
|
||||
|
||||
_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
|
||||
_FORCE_INLINE_ void _update_transform_to_mat4(const Transform3D &p_transform, float *p_mat4);
|
||||
|
||||
enum {
|
||||
INSTANCE_FLAGS_LIGHT_COUNT_SHIFT = 0, // 4 bits for light count.
|
||||
|
||||
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),
|
||||
|
||||
INSTANCE_FLAGS_NINEPACH_DRAW_CENTER = (1 << 8),
|
||||
INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT = 9,
|
||||
INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT = 11,
|
||||
|
||||
INSTANCE_FLAGS_SHADOW_MASKED_SHIFT = 13, // 16 bits.
|
||||
};
|
||||
|
||||
enum {
|
||||
BATCH_FLAGS_INSTANCING_MASK = 0x7F,
|
||||
BATCH_FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
|
||||
BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
|
||||
|
||||
BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 9),
|
||||
BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 10),
|
||||
};
|
||||
|
||||
enum {
|
||||
LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
|
||||
LIGHT_FLAGS_BLEND_SHIFT = 16,
|
||||
LIGHT_FLAGS_BLEND_MASK = (3 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_ADD = (0 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_SUB = (1 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_MIX = (2 << 16),
|
||||
LIGHT_FLAGS_BLEND_MODE_MASK = (3 << 16),
|
||||
LIGHT_FLAGS_HAS_SHADOW = (1 << 20),
|
||||
LIGHT_FLAGS_FILTER_SHIFT = 22
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_RENDER_ITEMS = 256 * 1024,
|
||||
MAX_LIGHT_TEXTURES = 1024,
|
||||
MAX_LIGHTS_PER_ITEM = 16,
|
||||
DEFAULT_MAX_LIGHTS_PER_RENDER = 256,
|
||||
};
|
||||
|
||||
/******************/
|
||||
/**** LIGHTING ****/
|
||||
/******************/
|
||||
|
||||
struct CanvasLight {
|
||||
RID texture;
|
||||
struct {
|
||||
bool enabled = false;
|
||||
float z_far;
|
||||
float y_offset;
|
||||
Transform2D directional_xform;
|
||||
} shadow;
|
||||
};
|
||||
|
||||
RID_Owner<CanvasLight> canvas_light_owner;
|
||||
|
||||
struct OccluderPolygon {
|
||||
RSE::CanvasOccluderPolygonCullMode cull_mode = RSE::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
|
||||
int line_point_count = 0;
|
||||
GLuint vertex_buffer = 0;
|
||||
GLuint vertex_array = 0;
|
||||
GLuint index_buffer = 0;
|
||||
|
||||
int sdf_point_count = 0;
|
||||
int sdf_index_count = 0;
|
||||
GLuint sdf_vertex_buffer = 0;
|
||||
GLuint sdf_vertex_array = 0;
|
||||
GLuint sdf_index_buffer = 0;
|
||||
bool sdf_is_lines = false;
|
||||
};
|
||||
|
||||
RID_Owner<OccluderPolygon> occluder_polygon_owner;
|
||||
|
||||
void _update_shadow_atlas();
|
||||
|
||||
struct {
|
||||
CanvasOcclusionShaderGLES3 shader;
|
||||
RID shader_version;
|
||||
} shadow_render;
|
||||
|
||||
struct LightUniform {
|
||||
float matrix[8]; //light to texture coordinate matrix
|
||||
float shadow_matrix[8]; //light to shadow coordinate matrix
|
||||
float color[4];
|
||||
|
||||
uint8_t shadow_color[4];
|
||||
uint32_t flags; //index to light texture
|
||||
float shadow_pixel_size;
|
||||
float height;
|
||||
|
||||
float position[2];
|
||||
float shadow_z_far_inv;
|
||||
float shadow_y_ofs;
|
||||
|
||||
float atlas_rect[4];
|
||||
};
|
||||
|
||||
static_assert(sizeof(LightUniform) % 16 == 0, "2D light UBO size must be a multiple of 16 bytes");
|
||||
|
||||
public:
|
||||
enum {
|
||||
BASE_UNIFORM_LOCATION = 0,
|
||||
GLOBAL_UNIFORM_LOCATION = 1,
|
||||
LIGHT_UNIFORM_LOCATION = 2,
|
||||
INSTANCE_UNIFORM_LOCATION = 3,
|
||||
MATERIAL_UNIFORM_LOCATION = 4,
|
||||
};
|
||||
|
||||
struct StateBuffer {
|
||||
float canvas_transform[16];
|
||||
float screen_transform[16];
|
||||
float canvas_normal_transform[16];
|
||||
float canvas_modulate[4];
|
||||
|
||||
float screen_pixel_size[2];
|
||||
float time;
|
||||
uint32_t use_pixel_snap;
|
||||
|
||||
float sdf_to_tex[4];
|
||||
float sdf_to_screen[2];
|
||||
float screen_to_sdf[2];
|
||||
|
||||
uint32_t directional_light_count;
|
||||
float tex_to_sdf;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(StateBuffer) % 16 == 0, "2D state UBO size must be a multiple of 16 bytes");
|
||||
|
||||
struct PolygonBuffers {
|
||||
GLuint vertex_buffer = 0;
|
||||
GLuint vertex_array = 0;
|
||||
GLuint index_buffer = 0;
|
||||
int count = 0;
|
||||
bool color_disabled = false;
|
||||
Color color = Color(1.0, 1.0, 1.0, 1.0);
|
||||
};
|
||||
|
||||
struct {
|
||||
HashMap<PolygonID, PolygonBuffers> polygons;
|
||||
PolygonID last_id = 0;
|
||||
} polygon_buffers;
|
||||
|
||||
RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), int p_count = -1) override;
|
||||
void free_polygon(PolygonID p_polygon) override;
|
||||
|
||||
struct InstanceData {
|
||||
float world[6];
|
||||
float color_texture_pixel_size[2];
|
||||
union {
|
||||
//rect
|
||||
struct {
|
||||
float modulation[4];
|
||||
union {
|
||||
float msdf[4];
|
||||
float ninepatch_margins[4];
|
||||
};
|
||||
float dst_rect[4];
|
||||
float src_rect[4];
|
||||
float pad[2];
|
||||
};
|
||||
//primitive
|
||||
struct {
|
||||
float points[6]; // vec2 points[3]
|
||||
float uvs[6]; // vec2 points[3]
|
||||
uint32_t colors[6]; // colors encoded as half
|
||||
};
|
||||
};
|
||||
uint32_t flags;
|
||||
uint32_t instance_uniforms_ofs;
|
||||
uint32_t lights[4];
|
||||
};
|
||||
|
||||
static_assert(sizeof(InstanceData) == 128, "2D instance data struct size must be 128 bytes");
|
||||
|
||||
struct Data {
|
||||
GLuint canvas_quad_vertices;
|
||||
GLuint canvas_quad_array;
|
||||
|
||||
GLuint indexed_quad_buffer;
|
||||
GLuint indexed_quad_array;
|
||||
|
||||
GLuint particle_quad_vertices;
|
||||
GLuint particle_quad_array;
|
||||
|
||||
GLuint ninepatch_vertices;
|
||||
GLuint ninepatch_elements;
|
||||
|
||||
RID canvas_shader_default_version;
|
||||
|
||||
uint32_t max_lights_per_render = 256;
|
||||
uint32_t max_lights_per_item = 16;
|
||||
uint32_t max_instances_per_buffer = 16384;
|
||||
uint32_t max_instance_buffer_size = 16384 * 128;
|
||||
} data;
|
||||
|
||||
struct Batch {
|
||||
// Position in the UBO measured in bytes
|
||||
uint32_t start = 0;
|
||||
uint32_t instance_count = 0;
|
||||
uint32_t instance_buffer_index = 0;
|
||||
|
||||
RID tex;
|
||||
RSE::CanvasItemTextureFilter filter = RSE::CANVAS_ITEM_TEXTURE_FILTER_MAX;
|
||||
RSE::CanvasItemTextureRepeat repeat = RSE::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
|
||||
|
||||
GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
|
||||
Color blend_color = Color(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
Item *clip = nullptr;
|
||||
|
||||
RID material;
|
||||
GLES3::CanvasMaterialData *material_data = nullptr;
|
||||
uint64_t vertex_input_mask = RSE::ARRAY_FORMAT_VERTEX | RSE::ARRAY_FORMAT_COLOR | RSE::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;
|
||||
|
||||
uint32_t flags = 0;
|
||||
uint32_t specular_shininess = 0.0;
|
||||
|
||||
bool lights_disabled = false;
|
||||
};
|
||||
|
||||
// DataBuffer contains our per-frame data. I.e. the resources that are updated each frame.
|
||||
// We track them and ensure that they don't get reused until at least 2 frames have passed
|
||||
// to avoid the GPU stalling to wait for a resource to become available.
|
||||
struct DataBuffer {
|
||||
Vector<GLuint> instance_buffers;
|
||||
GLuint light_ubo = 0;
|
||||
GLuint state_ubo = 0;
|
||||
uint64_t last_frame_used = -3;
|
||||
GLsync fence = GLsync();
|
||||
};
|
||||
|
||||
struct State {
|
||||
LocalVector<DataBuffer> canvas_instance_data_buffers;
|
||||
LocalVector<Batch> canvas_instance_batches;
|
||||
uint32_t current_data_buffer_index = 0;
|
||||
uint32_t current_instance_buffer_index = 0;
|
||||
uint32_t current_batch_index = 0;
|
||||
uint32_t last_item_index = 0;
|
||||
|
||||
InstanceData *instance_data_array = nullptr;
|
||||
|
||||
LightUniform *light_uniforms = nullptr;
|
||||
|
||||
GLuint shadow_texture = 0;
|
||||
GLuint shadow_depth_buffer = 0;
|
||||
GLuint shadow_fb = 0;
|
||||
int shadow_texture_size = 2048;
|
||||
|
||||
bool using_directional_lights = false;
|
||||
|
||||
RID current_tex;
|
||||
RSE::CanvasItemTextureFilter current_filter_mode = RSE::CANVAS_ITEM_TEXTURE_FILTER_MAX;
|
||||
RSE::CanvasItemTextureRepeat current_repeat_mode = RSE::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
|
||||
|
||||
bool transparent_render_target = false;
|
||||
|
||||
double time = 0.0;
|
||||
|
||||
RSE::CanvasItemTextureFilter default_filter = RSE::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
|
||||
RSE::CanvasItemTextureRepeat default_repeat = RSE::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
|
||||
} state;
|
||||
|
||||
Item *items[MAX_RENDER_ITEMS];
|
||||
|
||||
RID default_canvas_texture;
|
||||
RID default_canvas_group_material;
|
||||
RID default_canvas_group_shader;
|
||||
RID default_clip_children_material;
|
||||
RID default_clip_children_shader;
|
||||
|
||||
typedef void Texture;
|
||||
|
||||
void canvas_begin(RID p_to_render_target, bool p_to_backbuffer, bool p_backbuffer_has_mipmaps);
|
||||
|
||||
//virtual void draw_window_margins(int *black_margin, RID *black_image) override;
|
||||
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
|
||||
|
||||
void reset_canvas();
|
||||
|
||||
RID light_create() override;
|
||||
void light_set_texture(RID p_rid, RID p_texture) override;
|
||||
void light_set_use_shadow(RID p_rid, bool p_enable) override;
|
||||
void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, const Rect2 &p_light_rect) override;
|
||||
void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) override;
|
||||
|
||||
void render_sdf(RID p_render_target, LightOccluderInstance *p_occluders) override;
|
||||
RID occluder_polygon_create() override;
|
||||
void occluder_polygon_set_shape(RID p_occluder, const Vector<Vector2> &p_points, bool p_closed) override;
|
||||
void occluder_polygon_set_cull_mode(RID p_occluder, RSE::CanvasOccluderPolygonCullMode p_mode) override;
|
||||
void set_shadow_texture_size(int p_size) override;
|
||||
|
||||
bool free(RID p_rid) override;
|
||||
void update() override;
|
||||
|
||||
void _bind_canvas_texture(RID p_texture, RSE::CanvasItemTextureFilter p_base_filter, RSE::CanvasItemTextureRepeat p_base_repeat);
|
||||
void _prepare_canvas_texture(RID p_texture, RSE::CanvasItemTextureFilter p_base_filter, RSE::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
|
||||
|
||||
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RSE::CanvasItemTextureFilter p_default_filter, RSE::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingServerTypes::RenderInfo *r_render_info = nullptr) override;
|
||||
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingServerTypes::RenderInfo *r_render_info = nullptr, bool p_backbuffer_has_mipmaps = false);
|
||||
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset);
|
||||
void _render_batch(Light *p_lights, uint32_t p_index, RenderingServerTypes::RenderInfo *r_render_info = nullptr);
|
||||
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
|
||||
void _new_batch(bool &r_batch_broken);
|
||||
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken);
|
||||
void _allocate_instance_data_buffer();
|
||||
void _allocate_instance_buffer();
|
||||
void _enable_attributes(uint32_t p_start, bool p_primitive, uint32_t p_rate = 1);
|
||||
|
||||
void set_time(double p_time);
|
||||
|
||||
virtual void set_debug_redraw(bool p_enabled, double p_time, const Color &p_color) override {
|
||||
if (p_enabled) {
|
||||
WARN_PRINT_ONCE("Debug CanvasItem Redraw is not available yet when using the Compatibility renderer.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual uint32_t get_pipeline_compilations(RSE::PipelineSource p_source) override { return 0; }
|
||||
|
||||
static RasterizerCanvasGLES3 *get_singleton();
|
||||
RasterizerCanvasGLES3();
|
||||
~RasterizerCanvasGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
506
engine/drivers/gles3/rasterizer_gles3.cpp
Normal file
506
engine/drivers/gles3/rasterizer_gles3.cpp
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "rasterizer_gles3.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "drivers/gles3/rasterizer_util_gles3.h"
|
||||
#include "servers/display/display_server.h"
|
||||
#include "servers/rendering/rendering_server.h"
|
||||
#include "servers/rendering/rendering_server_types.h"
|
||||
|
||||
#define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
|
||||
#define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
|
||||
#define _EXT_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
|
||||
#define _EXT_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
|
||||
#define _EXT_DEBUG_SOURCE_API_ARB 0x8246
|
||||
#define _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
|
||||
#define _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
|
||||
#define _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
|
||||
#define _EXT_DEBUG_SOURCE_APPLICATION_ARB 0x824A
|
||||
#define _EXT_DEBUG_SOURCE_OTHER_ARB 0x824B
|
||||
#define _EXT_DEBUG_TYPE_ERROR_ARB 0x824C
|
||||
#define _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
|
||||
#define _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
|
||||
#define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F
|
||||
#define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
|
||||
#define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251
|
||||
#define _EXT_DEBUG_TYPE_MARKER_ARB 0x8268
|
||||
#define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
|
||||
#define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
|
||||
#define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145
|
||||
#define _EXT_DEBUG_SEVERITY_HIGH_ARB 0x9146
|
||||
#define _EXT_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
|
||||
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
|
||||
#define _EXT_DEBUG_OUTPUT 0x92E0
|
||||
|
||||
#ifndef GL_FRAMEBUFFER_SRGB
|
||||
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||
#endif
|
||||
|
||||
#ifndef GLAPIENTRY
|
||||
#if defined(WINDOWS_ENABLED)
|
||||
#define GLAPIENTRY APIENTRY
|
||||
#else
|
||||
#define GLAPIENTRY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED)
|
||||
// We include EGL below to get debug callback on GLES2 platforms,
|
||||
// but EGL is not available on iOS or the web.
|
||||
#define CAN_DEBUG
|
||||
#endif
|
||||
|
||||
#include "platform_gl.h"
|
||||
|
||||
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
|
||||
#define strcpy strcpy_s
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
bool RasterizerGLES3::screen_flipped_y = false;
|
||||
#endif
|
||||
|
||||
void RasterizerGLES3::begin_frame(double frame_step) {
|
||||
frame++;
|
||||
delta = frame_step;
|
||||
|
||||
time_total += frame_step;
|
||||
|
||||
double time_roll_over = GLOBAL_GET_CACHED(double, "rendering/limits/time/time_rollover_secs");
|
||||
time_total = Math::fmod(time_total, time_roll_over);
|
||||
|
||||
canvas->set_time(time_total);
|
||||
scene->set_time(time_total, frame_step);
|
||||
|
||||
GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
|
||||
utils->_capture_timestamps_begin();
|
||||
|
||||
//scene->iteration();
|
||||
}
|
||||
|
||||
void RasterizerGLES3::end_frame(bool p_swap_buffers) {
|
||||
GLES3::Utilities *utils = GLES3::Utilities::get_singleton();
|
||||
utils->capture_timestamps_end();
|
||||
}
|
||||
|
||||
void RasterizerGLES3::gl_end_frame(bool p_swap_buffers) {
|
||||
if (p_swap_buffers) {
|
||||
DisplayServer::get_singleton()->swap_buffers();
|
||||
} else {
|
||||
glFinish();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CAN_DEBUG
|
||||
static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam) {
|
||||
// These are ultimately annoying, so removing for now.
|
||||
if (type == _EXT_DEBUG_TYPE_OTHER_ARB || type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB || type == _EXT_DEBUG_TYPE_MARKER_ARB) {
|
||||
return;
|
||||
}
|
||||
|
||||
char debSource[256], debType[256], debSev[256];
|
||||
|
||||
if (source == _EXT_DEBUG_SOURCE_API_ARB) {
|
||||
strcpy(debSource, "OpenGL");
|
||||
} else if (source == _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB) {
|
||||
strcpy(debSource, "Windows");
|
||||
} else if (source == _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB) {
|
||||
strcpy(debSource, "Shader Compiler");
|
||||
} else if (source == _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB) {
|
||||
strcpy(debSource, "Third Party");
|
||||
} else if (source == _EXT_DEBUG_SOURCE_APPLICATION_ARB) {
|
||||
strcpy(debSource, "Application");
|
||||
} else if (source == _EXT_DEBUG_SOURCE_OTHER_ARB) {
|
||||
strcpy(debSource, "Other");
|
||||
} else {
|
||||
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled source '%d' in debug callback.", source));
|
||||
}
|
||||
|
||||
if (type == _EXT_DEBUG_TYPE_ERROR_ARB) {
|
||||
strcpy(debType, "Error");
|
||||
} else if (type == _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB) {
|
||||
strcpy(debType, "Deprecated behavior");
|
||||
} else if (type == _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB) {
|
||||
strcpy(debType, "Undefined behavior");
|
||||
} else if (type == _EXT_DEBUG_TYPE_PORTABILITY_ARB) {
|
||||
strcpy(debType, "Portability");
|
||||
} else {
|
||||
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled type '%d' in debug callback.", type));
|
||||
}
|
||||
|
||||
if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) {
|
||||
strcpy(debSev, "High");
|
||||
} else if (severity == _EXT_DEBUG_SEVERITY_MEDIUM_ARB) {
|
||||
strcpy(debSev, "Medium");
|
||||
} else if (severity == _EXT_DEBUG_SEVERITY_LOW_ARB) {
|
||||
strcpy(debSev, "Low");
|
||||
} else {
|
||||
ERR_FAIL_MSG(vformat("GL ERROR: Invalid or unhandled severity '%d' in debug callback.", severity));
|
||||
}
|
||||
|
||||
String output = String() + "GL ERROR: Source: " + debSource + "\tType: " + debType + "\tID: " + itos(id) + "\tSeverity: " + debSev + "\tMessage: " + message;
|
||||
|
||||
ERR_PRINT(output);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef void(GLAPIENTRY *DEBUGPROCARB)(GLenum source,
|
||||
GLenum type,
|
||||
GLuint id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const char *message,
|
||||
const void *userParam);
|
||||
|
||||
typedef void(GLAPIENTRY *DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam);
|
||||
|
||||
void RasterizerGLES3::initialize() {
|
||||
Engine::get_singleton()->print_header(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name()));
|
||||
}
|
||||
|
||||
void RasterizerGLES3::finalize() {
|
||||
// Has to be a separate call due to TextureStorage & MaterialStorage needing to interact for TexBlit Shaders
|
||||
texture_storage->_tex_blit_shader_free();
|
||||
memdelete(scene);
|
||||
memdelete(canvas);
|
||||
memdelete(gi);
|
||||
memdelete(fog);
|
||||
memdelete(post_effects);
|
||||
memdelete(glow);
|
||||
memdelete(cubemap_filter);
|
||||
memdelete(copy_effects);
|
||||
memdelete(feed_effects);
|
||||
memdelete(light_storage);
|
||||
memdelete(particles_storage);
|
||||
memdelete(mesh_storage);
|
||||
memdelete(material_storage);
|
||||
memdelete(texture_storage);
|
||||
memdelete(utilities);
|
||||
memdelete(config);
|
||||
}
|
||||
|
||||
void RasterizerGLES3::make_current(bool p_gles_over_gl) {
|
||||
RasterizerUtilGLES3::set_gles_over_gl(p_gles_over_gl);
|
||||
OS::get_singleton()->set_gles_over_gl(p_gles_over_gl);
|
||||
_create_func = _create_current;
|
||||
low_end = true;
|
||||
}
|
||||
|
||||
RasterizerGLES3 *RasterizerGLES3::singleton = nullptr;
|
||||
|
||||
#ifdef EGL_ENABLED
|
||||
void *_egl_load_function_wrapper(const char *p_name) {
|
||||
return (void *)eglGetProcAddress(p_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
RasterizerGLES3::RasterizerGLES3() {
|
||||
singleton = this;
|
||||
|
||||
#ifdef GLAD_ENABLED
|
||||
bool glad_loaded = false;
|
||||
|
||||
#ifdef EGL_ENABLED
|
||||
// There should be a more flexible system for getting the GL pointer, as
|
||||
// different DisplayServers can have different ways. We can just use the GLAD
|
||||
// version global to see if it loaded for now though, otherwise we fall back to
|
||||
// the generic loader below.
|
||||
#if defined(EGL_STATIC)
|
||||
bool has_egl = true;
|
||||
#else
|
||||
bool has_egl = (eglGetProcAddress != nullptr);
|
||||
#endif
|
||||
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
if (has_egl && !glad_loaded && gladLoadGL((GLADloadfunc)&_egl_load_function_wrapper)) {
|
||||
glad_loaded = true;
|
||||
}
|
||||
} else {
|
||||
if (has_egl && !glad_loaded && gladLoadGLES2((GLADloadfunc)&_egl_load_function_wrapper)) {
|
||||
glad_loaded = true;
|
||||
}
|
||||
}
|
||||
#endif // EGL_ENABLED
|
||||
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
if (!glad_loaded && gladLoaderLoadGL()) {
|
||||
glad_loaded = true;
|
||||
}
|
||||
} else {
|
||||
if (!glad_loaded && gladLoaderLoadGLES2()) {
|
||||
glad_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME this is an early return from a constructor. Any other code using this instance will crash or the finalizer will crash, because none of
|
||||
// the members of this instance are initialized, so this just makes debugging harder. It should either crash here intentionally,
|
||||
// or we need to actually test for this situation before constructing this.
|
||||
ERR_FAIL_COND_MSG(!glad_loaded, "Error initializing GLAD.");
|
||||
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
if (GLAD_GL_ARB_debug_output) {
|
||||
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
glDebugMessageCallbackARB((GLDEBUGPROCARB)_gl_debug_print, nullptr);
|
||||
glEnable(_EXT_DEBUG_OUTPUT);
|
||||
} else {
|
||||
print_line("OpenGL debugging not supported!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // GLAD_ENABLED
|
||||
|
||||
// For debugging
|
||||
#ifdef CAN_DEBUG
|
||||
#ifdef GL_API_ENABLED
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose() && GLAD_GL_ARB_debug_output) {
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_ERROR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PORTABILITY_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_PERFORMANCE_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageControlARB(_EXT_DEBUG_SOURCE_API_ARB, _EXT_DEBUG_TYPE_OTHER_ARB, _EXT_DEBUG_SEVERITY_HIGH_ARB, 0, nullptr, GL_TRUE);
|
||||
}
|
||||
}
|
||||
#endif // GL_API_ENABLED
|
||||
#ifdef GLES_API_ENABLED
|
||||
if (!RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback");
|
||||
if (!callback) {
|
||||
callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR");
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
print_line("godot: ENABLING GL DEBUG");
|
||||
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
callback((DEBUGPROCARB)_gl_debug_print, nullptr);
|
||||
glEnable(_EXT_DEBUG_OUTPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // GLES_API_ENABLED
|
||||
#endif // CAN_DEBUG
|
||||
|
||||
{
|
||||
String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
|
||||
if (shader_cache_dir.is_empty()) {
|
||||
shader_cache_dir = "user://";
|
||||
}
|
||||
Ref<DirAccess> da = DirAccess::open(shader_cache_dir);
|
||||
if (da.is_null()) {
|
||||
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
} else {
|
||||
Error err = da->change_dir("shader_cache");
|
||||
if (err != OK) {
|
||||
err = da->make_dir("shader_cache");
|
||||
}
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
|
||||
} else {
|
||||
shader_cache_dir = shader_cache_dir.path_join("shader_cache");
|
||||
|
||||
bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled");
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) {
|
||||
shader_cache_dir = String(); //disable only if not editor
|
||||
}
|
||||
|
||||
if (!shader_cache_dir.is_empty()) {
|
||||
ShaderGLES3::set_shader_cache_dir(shader_cache_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OpenGL needs to be initialized before initializing the Rasterizers
|
||||
config = memnew(GLES3::Config);
|
||||
utilities = memnew(GLES3::Utilities);
|
||||
texture_storage = memnew(GLES3::TextureStorage);
|
||||
material_storage = memnew(GLES3::MaterialStorage);
|
||||
mesh_storage = memnew(GLES3::MeshStorage);
|
||||
particles_storage = memnew(GLES3::ParticlesStorage);
|
||||
light_storage = memnew(GLES3::LightStorage);
|
||||
copy_effects = memnew(GLES3::CopyEffects);
|
||||
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());
|
||||
scene = memnew(RasterizerSceneGLES3());
|
||||
// Has to be a separate call due to TextureStorage & MaterialStorage needing to interact for TexBlit Shaders
|
||||
texture_storage->_tex_blit_shader_initialize();
|
||||
|
||||
// Disable OpenGL linear to sRGB conversion, because Godot will always do this conversion itself.
|
||||
if (config->srgb_framebuffer_supported) {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
}
|
||||
|
||||
RasterizerGLES3::~RasterizerGLES3() {
|
||||
}
|
||||
|
||||
void RasterizerGLES3::_blit_render_target_to_screen(DisplayServerEnums::WindowID p_screen, const RenderingServerTypes::BlitToScreen &p_blit, bool p_first) {
|
||||
GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_blit.render_target);
|
||||
|
||||
ERR_FAIL_NULL(rt);
|
||||
|
||||
// We normally render to the render target upside down, so flip Y when blitting to the screen.
|
||||
bool flip_y = true;
|
||||
bool linear_to_srgb = false;
|
||||
if (rt->overridden.color.is_valid()) {
|
||||
// If we've overridden the render target's color texture, that means we
|
||||
// didn't render upside down, so we don't need to flip it.
|
||||
// We're probably rendering directly to an XR device.
|
||||
flip_y = false;
|
||||
|
||||
// It is 99% likely our texture uses the GL_SRGB8_ALPHA8 texture format in
|
||||
// which case we have a GPU sRGB to Linear conversion on texture read.
|
||||
// We need to counter this.
|
||||
// Unfortunately we do not have an API to check this as Godot does not
|
||||
// track this.
|
||||
linear_to_srgb = true;
|
||||
}
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
if (screen_flipped_y) {
|
||||
flip_y = !flip_y;
|
||||
}
|
||||
#endif
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
|
||||
if (p_first) {
|
||||
if (p_blit.dst_rect.position != Vector2() || p_blit.dst_rect.size != rt->size) {
|
||||
// Viewport doesn't cover entire window so clear window to black before blitting.
|
||||
// Querying the actual window size from the DisplayServer would deadlock in separate render thread mode,
|
||||
// so let's set the biggest viewport the implementation supports, to be sure the window is fully covered.
|
||||
Size2i max_vp = GLES3::Utilities::get_singleton()->get_maximum_viewport_size();
|
||||
glViewport(0, 0, max_vp[0], max_vp[1]);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 screen_rect_end = p_blit.dst_rect.get_end();
|
||||
|
||||
Vector2 p1 = Vector2(p_blit.dst_rect.position.x, flip_y ? screen_rect_end.y : p_blit.dst_rect.position.y);
|
||||
Vector2 p2 = Vector2(screen_rect_end.x, flip_y ? p_blit.dst_rect.position.y : screen_rect_end.y);
|
||||
Vector2 size = p2 - p1;
|
||||
|
||||
Rect2 screenrect = Rect2(Vector2(0.0, flip_y ? 1.0 : 0.0), Vector2(1.0, flip_y ? -1.0 : 1.0));
|
||||
|
||||
glViewport(int(MIN(p1.x, p2.x)), int(MIN(p1.y, p2.y)), Math::abs(size.x), Math::abs(size.y));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLenum target = rt->view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||
glBindTexture(target, rt->color);
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
|
||||
if (p_blit.lens_distortion.apply && (p_blit.lens_distortion.k1 != 0.0 || p_blit.lens_distortion.k2)) {
|
||||
copy_effects->copy_with_lens_distortion(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, p_blit.lens_distortion.eye_center, p_blit.lens_distortion.k1, p_blit.lens_distortion.k2, p_blit.lens_distortion.upscale, p_blit.lens_distortion.aspect_ratio, linear_to_srgb);
|
||||
} else if (rt->view_count > 1) {
|
||||
copy_effects->copy_to_rect_3d(screenrect, p_blit.multi_view.use_layer ? p_blit.multi_view.layer : 0, GLES3::Texture::TYPE_LAYERED, 0.0, linear_to_srgb);
|
||||
} else {
|
||||
copy_effects->copy_to_rect(screenrect, linear_to_srgb);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// is this p_screen useless in a multi window environment?
|
||||
void RasterizerGLES3::blit_render_targets_to_screen(DisplayServerEnums::WindowID p_screen, const RenderingServerTypes::BlitToScreen *p_render_targets, int p_amount) {
|
||||
for (int i = 0; i < p_amount; i++) {
|
||||
_blit_render_target_to_screen(p_screen, p_render_targets[i], i == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerGLES3::set_boot_image_with_stretch(const Ref<Image> &p_image, const Color &p_color, RSE::SplashStretchMode p_stretch_mode, bool p_use_filter) {
|
||||
if (p_image.is_null() || p_image->is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Size2i win_size = DisplayServer::get_singleton()->window_get_size();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
glViewport(0, 0, win_size.width, win_size.height);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
|
||||
glDepthMask(GL_FALSE);
|
||||
glClearColor(p_color.r, p_color.g, p_color.b, OS::get_singleton()->is_layered_allowed() ? p_color.a : 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
RID texture = texture_storage->texture_allocate();
|
||||
texture_storage->texture_2d_initialize(texture, p_image);
|
||||
|
||||
Rect2 screenrect = RenderingServerTypes::get_splash_stretched_screen_rect(p_image->get_size(), win_size, p_stretch_mode);
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
if (!screen_flipped_y)
|
||||
#endif
|
||||
{
|
||||
// Flip Y.
|
||||
screenrect.position.y = win_size.y - screenrect.position.y;
|
||||
screenrect.size.y = -screenrect.size.y;
|
||||
}
|
||||
|
||||
// Normalize texture coordinates to window size.
|
||||
screenrect.position /= win_size;
|
||||
screenrect.size /= win_size;
|
||||
|
||||
GLES3::Texture *t = texture_storage->get_texture(texture);
|
||||
t->gl_set_filter(p_use_filter ? RSE::CANVAS_ITEM_TEXTURE_FILTER_LINEAR : RSE::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, t->tex_id);
|
||||
copy_effects->copy_to_rect(screenrect);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
gl_end_frame(true);
|
||||
|
||||
texture_storage->texture_free(texture);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
132
engine/drivers/gles3/rasterizer_gles3.h
Normal file
132
engine/drivers/gles3/rasterizer_gles3.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "drivers/gles3/effects/copy_effects.h"
|
||||
#include "drivers/gles3/effects/cubemap_filter.h"
|
||||
#include "drivers/gles3/effects/feed_effects.h"
|
||||
#include "drivers/gles3/effects/glow.h"
|
||||
#include "drivers/gles3/effects/post_effects.h"
|
||||
#include "drivers/gles3/environment/fog.h"
|
||||
#include "drivers/gles3/environment/gi.h"
|
||||
#include "drivers/gles3/rasterizer_canvas_gles3.h"
|
||||
#include "drivers/gles3/rasterizer_scene_gles3.h"
|
||||
#include "drivers/gles3/storage/config.h"
|
||||
#include "drivers/gles3/storage/light_storage.h"
|
||||
#include "drivers/gles3/storage/material_storage.h"
|
||||
#include "drivers/gles3/storage/mesh_storage.h"
|
||||
#include "drivers/gles3/storage/particles_storage.h"
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
#include "drivers/gles3/storage/utilities.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
|
||||
class RasterizerGLES3 : public RendererCompositor {
|
||||
private:
|
||||
uint64_t frame = 1;
|
||||
float delta = 0;
|
||||
|
||||
double time_total = 0.0;
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
static bool screen_flipped_y;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
GLES3::Config *config = nullptr;
|
||||
GLES3::Utilities *utilities = nullptr;
|
||||
GLES3::TextureStorage *texture_storage = nullptr;
|
||||
GLES3::MaterialStorage *material_storage = nullptr;
|
||||
GLES3::MeshStorage *mesh_storage = nullptr;
|
||||
GLES3::ParticlesStorage *particles_storage = nullptr;
|
||||
GLES3::LightStorage *light_storage = nullptr;
|
||||
GLES3::GI *gi = nullptr;
|
||||
GLES3::Fog *fog = nullptr;
|
||||
GLES3::CopyEffects *copy_effects = nullptr;
|
||||
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;
|
||||
|
||||
void _blit_render_target_to_screen(DisplayServerEnums::WindowID p_screen, const RenderingServerTypes::BlitToScreen &p_blit, bool p_first = true);
|
||||
|
||||
public:
|
||||
RendererUtilities *get_utilities() { return utilities; }
|
||||
RendererLightStorage *get_light_storage() { return light_storage; }
|
||||
RendererMaterialStorage *get_material_storage() { return material_storage; }
|
||||
RendererMeshStorage *get_mesh_storage() { return mesh_storage; }
|
||||
RendererParticlesStorage *get_particles_storage() { return particles_storage; }
|
||||
RendererTextureStorage *get_texture_storage() { return texture_storage; }
|
||||
RendererGI *get_gi() { return gi; }
|
||||
RendererFog *get_fog() { return fog; }
|
||||
RendererCanvasRender *get_canvas() { return canvas; }
|
||||
RendererSceneRender *get_scene() { return scene; }
|
||||
|
||||
void set_boot_image_with_stretch(const Ref<Image> &p_image, const Color &p_color, RSE::SplashStretchMode p_stretch_mode, bool p_use_filter = true);
|
||||
|
||||
void initialize();
|
||||
void begin_frame(double frame_step);
|
||||
|
||||
void blit_render_targets_to_screen(DisplayServerEnums::WindowID p_screen, const RenderingServerTypes::BlitToScreen *p_render_targets, int p_amount);
|
||||
|
||||
bool is_opengl() { return true; }
|
||||
void gl_end_frame(bool p_swap_buffers);
|
||||
void end_frame(bool p_swap_buffers);
|
||||
|
||||
void finalize();
|
||||
|
||||
static RendererCompositor *_create_current() {
|
||||
return memnew(RasterizerGLES3);
|
||||
}
|
||||
|
||||
static void make_current(bool p_gles_over_gl);
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
static void set_screen_flipped_y(bool p_flipped) {
|
||||
screen_flipped_y = p_flipped;
|
||||
}
|
||||
#endif
|
||||
|
||||
_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
|
||||
_ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
|
||||
_ALWAYS_INLINE_ double get_total_time() const { return time_total; }
|
||||
_ALWAYS_INLINE_ bool can_create_resources_async() const { return false; }
|
||||
|
||||
static RasterizerGLES3 *get_singleton() { return singleton; }
|
||||
RasterizerGLES3();
|
||||
~RasterizerGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
4627
engine/drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
4627
engine/drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
File diff suppressed because it is too large
Load diff
965
engine/drivers/gles3/rasterizer_scene_gles3.h
Normal file
965
engine/drivers/gles3/rasterizer_scene_gles3.h
Normal file
|
|
@ -0,0 +1,965 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_scene_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/math/projection.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "drivers/gles3/storage/light_storage.h"
|
||||
#include "drivers/gles3/storage/material_storage.h"
|
||||
#include "servers/rendering/renderer_scene_render.h"
|
||||
#include "servers/rendering/rendering_server_enums.h"
|
||||
#include "servers/rendering/rendering_server_types.h"
|
||||
|
||||
class RenderSceneBuffersGLES3;
|
||||
|
||||
enum RenderListType {
|
||||
RENDER_LIST_OPAQUE, //used for opaque objects
|
||||
RENDER_LIST_ALPHA, //used for transparent objects
|
||||
RENDER_LIST_SECONDARY, //used for shadows and other objects
|
||||
RENDER_LIST_MAX
|
||||
};
|
||||
|
||||
enum PassMode {
|
||||
PASS_MODE_COLOR,
|
||||
PASS_MODE_COLOR_TRANSPARENT,
|
||||
PASS_MODE_SHADOW,
|
||||
PASS_MODE_DEPTH,
|
||||
PASS_MODE_MATERIAL,
|
||||
PASS_MODE_MOTION_VECTORS,
|
||||
};
|
||||
|
||||
// These should share as much as possible with SkyUniform Location
|
||||
enum SceneUniformLocation {
|
||||
SCENE_TONEMAP_UNIFORM_LOCATION,
|
||||
SCENE_GLOBALS_UNIFORM_LOCATION,
|
||||
SCENE_DATA_UNIFORM_LOCATION,
|
||||
SCENE_MATERIAL_UNIFORM_LOCATION,
|
||||
SCENE_EMPTY1, // Unused, put here to avoid conflicts with SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION.
|
||||
SCENE_OMNILIGHT_UNIFORM_LOCATION,
|
||||
SCENE_SPOTLIGHT_UNIFORM_LOCATION,
|
||||
SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
|
||||
SCENE_MULTIVIEW_UNIFORM_LOCATION,
|
||||
SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION,
|
||||
SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION,
|
||||
SCENE_EMPTY2, // Unused, put here to avoid conflicts with SKY_MULTIVIEW_UNIFORM_LOCATION.
|
||||
SCENE_PREV_DATA_UNIFORM_LOCATION,
|
||||
SCENE_PREV_MULTIVIEW_UNIFORM_LOCATION,
|
||||
};
|
||||
|
||||
enum SkyUniformLocation {
|
||||
SKY_TONEMAP_UNIFORM_LOCATION,
|
||||
SKY_GLOBALS_UNIFORM_LOCATION,
|
||||
SKY_EMPTY1, // Unused, put here to avoid conflicts with SCENE_DATA_UNIFORM_LOCATION.
|
||||
SKY_MATERIAL_UNIFORM_LOCATION,
|
||||
SKY_DIRECTIONAL_LIGHT_UNIFORM_LOCATION,
|
||||
SKY_EMPTY2, // Unused, put here to avoid conflicts with SCENE_OMNILIGHT_UNIFORM_LOCATION.
|
||||
SKY_EMPTY3, // Unused, put here to avoid conflicts with SCENE_SPOTLIGHT_UNIFORM_LOCATION.
|
||||
SKY_EMPTY4, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION.
|
||||
SKY_EMPTY5, // Unused, put here to avoid conflicts with SCENE_MULTIVIEW_UNIFORM_LOCATION.
|
||||
SKY_EMPTY6, // Unused, put here to avoid conflicts with SCENE_POSITIONAL_SHADOW_UNIFORM_LOCATION.
|
||||
SKY_EMPTY7, // Unused, put here to avoid conflicts with SCENE_DIRECTIONAL_SHADOW_UNIFORM_LOCATION.
|
||||
SKY_MULTIVIEW_UNIFORM_LOCATION,
|
||||
SKY_EMPTY8, // Unused, put here to avoid conflicts with SCENE_PREV_DATA_UNIFORM_LOCATION.
|
||||
SKY_EMPTY9, // Unused, put here to avoid conflicts with SCENE_PREV_MULTIVIEW_UNIFORM_LOCATION.
|
||||
};
|
||||
|
||||
struct RenderDataGLES3 {
|
||||
Ref<RenderSceneBuffersGLES3> render_buffers;
|
||||
bool transparent_bg = false;
|
||||
Rect2i render_region;
|
||||
|
||||
Transform3D cam_transform;
|
||||
Transform3D inv_cam_transform;
|
||||
Projection cam_projection;
|
||||
bool cam_orthogonal = false;
|
||||
uint32_t camera_visible_layers = 0xFFFFFFFF;
|
||||
|
||||
// For billboards to cast correct shadows.
|
||||
Transform3D main_cam_transform;
|
||||
|
||||
// For stereo rendering
|
||||
uint32_t view_count = 1;
|
||||
Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
|
||||
float z_near = 0.0;
|
||||
float z_far = 0.0;
|
||||
|
||||
const PagedArray<RenderGeometryInstance *> *instances = nullptr;
|
||||
const PagedArray<RID> *lights = nullptr;
|
||||
const PagedArray<RID> *reflection_probes = nullptr;
|
||||
RID environment;
|
||||
RID camera_attributes;
|
||||
RID shadow_atlas;
|
||||
RID reflection_probe;
|
||||
int reflection_probe_pass = 0;
|
||||
|
||||
float lod_distance_multiplier = 0.0;
|
||||
float screen_mesh_lod_threshold = 0.0;
|
||||
|
||||
uint32_t directional_light_count = 0;
|
||||
uint32_t directional_shadow_count = 0;
|
||||
|
||||
uint32_t spot_light_count = 0;
|
||||
uint32_t omni_light_count = 0;
|
||||
|
||||
float luminance_multiplier = 1.0;
|
||||
|
||||
RenderingServerTypes::RenderInfo *render_info = nullptr;
|
||||
|
||||
/* Shadow data */
|
||||
const RendererSceneRender::RenderShadowData *render_shadows = nullptr;
|
||||
int render_shadow_count = 0;
|
||||
};
|
||||
|
||||
class RasterizerCanvasGLES3;
|
||||
|
||||
class RasterizerSceneGLES3 : public RendererSceneRender {
|
||||
private:
|
||||
static RasterizerSceneGLES3 *singleton;
|
||||
RSE::ViewportDebugDraw debug_draw = RSE::VIEWPORT_DEBUG_DRAW_DISABLED;
|
||||
uint64_t scene_pass = 0;
|
||||
|
||||
template <typename T>
|
||||
struct InstanceSort {
|
||||
float depth;
|
||||
T *instance = nullptr;
|
||||
bool operator<(const InstanceSort &p_sort) const {
|
||||
return depth < p_sort.depth;
|
||||
}
|
||||
};
|
||||
|
||||
struct SceneGlobals {
|
||||
RID shader_default_version;
|
||||
RID default_material;
|
||||
RID default_shader;
|
||||
RID overdraw_material;
|
||||
RID overdraw_shader;
|
||||
} scene_globals;
|
||||
|
||||
GLES3::SceneMaterialData *default_material_data_ptr = nullptr;
|
||||
GLES3::SceneMaterialData *overdraw_material_data_ptr = nullptr;
|
||||
|
||||
/* LIGHT INSTANCE */
|
||||
|
||||
struct LightData {
|
||||
float position[3];
|
||||
float inv_radius;
|
||||
|
||||
float direction[3]; // Only used by SpotLight
|
||||
float size;
|
||||
|
||||
float color[3];
|
||||
float attenuation;
|
||||
|
||||
float inv_spot_attenuation;
|
||||
float cos_spot_angle;
|
||||
float specular_amount;
|
||||
float shadow_opacity;
|
||||
|
||||
float pad[3];
|
||||
uint32_t bake_mode;
|
||||
};
|
||||
static_assert(sizeof(LightData) % 16 == 0, "LightData size must be a multiple of 16 bytes");
|
||||
|
||||
struct DirectionalLightData {
|
||||
float direction[3];
|
||||
float energy;
|
||||
|
||||
float color[3];
|
||||
float size;
|
||||
|
||||
uint32_t enabled : 1; // For use by SkyShaders
|
||||
uint32_t bake_mode : 2;
|
||||
float shadow_opacity;
|
||||
float specular;
|
||||
uint32_t mask;
|
||||
};
|
||||
static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes");
|
||||
|
||||
struct ShadowData {
|
||||
float shadow_matrix[16];
|
||||
|
||||
float light_position[3];
|
||||
float shadow_normal_bias;
|
||||
|
||||
float pad[3];
|
||||
float shadow_atlas_pixel_size;
|
||||
};
|
||||
static_assert(sizeof(ShadowData) % 16 == 0, "ShadowData size must be a multiple of 16 bytes");
|
||||
|
||||
struct DirectionalShadowData {
|
||||
float direction[3];
|
||||
float shadow_atlas_pixel_size;
|
||||
float shadow_normal_bias[4];
|
||||
float shadow_split_offsets[4];
|
||||
float shadow_matrices[4][16];
|
||||
float fade_from;
|
||||
float fade_to;
|
||||
uint32_t blend_splits; // Not exposed to the shader.
|
||||
uint32_t pad;
|
||||
};
|
||||
static_assert(sizeof(DirectionalShadowData) % 16 == 0, "DirectionalShadowData size must be a multiple of 16 bytes");
|
||||
|
||||
class GeometryInstanceGLES3;
|
||||
|
||||
// Cached data for drawing surfaces
|
||||
struct GeometryInstanceSurface {
|
||||
enum {
|
||||
FLAG_PASS_DEPTH = 1,
|
||||
FLAG_PASS_OPAQUE = 2,
|
||||
FLAG_PASS_ALPHA = 4,
|
||||
FLAG_PASS_SHADOW = 8,
|
||||
FLAG_USES_SHARED_SHADOW_MATERIAL = 128,
|
||||
FLAG_USES_SCREEN_TEXTURE = 2048,
|
||||
FLAG_USES_DEPTH_TEXTURE = 4096,
|
||||
FLAG_USES_NORMAL_TEXTURE = 8192,
|
||||
FLAG_USES_DOUBLE_SIDED_SHADOWS = 16384,
|
||||
FLAG_USES_STENCIL = 32768,
|
||||
};
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint64_t sort_key1;
|
||||
uint64_t sort_key2;
|
||||
};
|
||||
struct {
|
||||
uint64_t lod_index : 8;
|
||||
uint64_t surface_index : 8;
|
||||
uint64_t geometry_id : 32;
|
||||
uint64_t material_id_low : 16;
|
||||
|
||||
uint64_t material_id_hi : 16;
|
||||
uint64_t shader_id : 32;
|
||||
uint64_t uses_softshadow : 1;
|
||||
uint64_t uses_projector : 1;
|
||||
uint64_t uses_forward_gi : 1;
|
||||
uint64_t uses_lightmap : 1;
|
||||
uint64_t depth_layer : 4;
|
||||
uint64_t priority : 8;
|
||||
};
|
||||
} sort;
|
||||
|
||||
RSE::PrimitiveType primitive = RSE::PRIMITIVE_MAX;
|
||||
uint32_t flags = 0;
|
||||
uint32_t surface_index = 0;
|
||||
uint32_t lod_index = 0;
|
||||
uint32_t index_count = 0;
|
||||
int32_t light_pass_index = -1;
|
||||
bool finished_base_pass = false;
|
||||
|
||||
void *surface = nullptr;
|
||||
GLES3::SceneShaderData *shader = nullptr;
|
||||
GLES3::SceneMaterialData *material = nullptr;
|
||||
|
||||
void *surface_shadow = nullptr;
|
||||
GLES3::SceneShaderData *shader_shadow = nullptr;
|
||||
GLES3::SceneMaterialData *material_shadow = nullptr;
|
||||
|
||||
GeometryInstanceSurface *next = nullptr;
|
||||
GeometryInstanceGLES3 *owner = nullptr;
|
||||
};
|
||||
|
||||
struct GeometryInstanceLightmapSH {
|
||||
Color sh[9];
|
||||
};
|
||||
|
||||
class GeometryInstanceGLES3 : public RenderGeometryInstanceBase {
|
||||
public:
|
||||
//used during rendering
|
||||
bool store_transform_cache = true;
|
||||
|
||||
// Used for generating motion vectors.
|
||||
Transform3D prev_transform;
|
||||
bool is_prev_transform_stored = false;
|
||||
|
||||
int32_t instance_count = 0;
|
||||
|
||||
bool can_sdfgi = false;
|
||||
bool using_projectors = false;
|
||||
bool using_softshadows = false;
|
||||
|
||||
struct LightPass {
|
||||
int32_t light_id = -1; // Position in the light uniform buffer.
|
||||
int32_t shadow_id = -1; // Position in the shadow uniform buffer.
|
||||
RID light_instance_rid;
|
||||
bool is_omni = false;
|
||||
};
|
||||
|
||||
LocalVector<LightPass> light_passes;
|
||||
|
||||
uint32_t paired_omni_light_count = 0;
|
||||
uint32_t paired_spot_light_count = 0;
|
||||
LocalVector<RID> paired_omni_lights;
|
||||
LocalVector<RID> paired_spot_lights;
|
||||
LocalVector<uint32_t> omni_light_gl_cache;
|
||||
LocalVector<uint32_t> spot_light_gl_cache;
|
||||
|
||||
LocalVector<RID> paired_reflection_probes;
|
||||
LocalVector<RID> reflection_probe_rid_cache;
|
||||
LocalVector<Transform3D> reflection_probes_local_transform_cache;
|
||||
|
||||
RID lightmap_instance;
|
||||
Rect2 lightmap_uv_scale;
|
||||
uint32_t lightmap_slice_index;
|
||||
GeometryInstanceLightmapSH *lightmap_sh = nullptr;
|
||||
|
||||
// Used during setup.
|
||||
GeometryInstanceSurface *surface_caches = nullptr;
|
||||
SelfList<GeometryInstanceGLES3> dirty_list_element;
|
||||
|
||||
GeometryInstanceGLES3() :
|
||||
dirty_list_element(this) {}
|
||||
|
||||
virtual void _mark_dirty() override;
|
||||
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
|
||||
virtual void set_lightmap_capture(const Color *p_sh9) override;
|
||||
|
||||
virtual void clear_light_instances() override;
|
||||
virtual void pair_light_instance(const RID p_light_instance, RSE::LightType light_type, uint32_t placement_idx) override;
|
||||
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
|
||||
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
|
||||
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
|
||||
|
||||
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override {}
|
||||
};
|
||||
|
||||
virtual uint32_t get_max_lights_total() override;
|
||||
virtual uint32_t get_max_lights_per_mesh() override;
|
||||
|
||||
enum {
|
||||
INSTANCE_DATA_FLAGS_DYNAMIC = 1 << 3,
|
||||
INSTANCE_DATA_FLAGS_NON_UNIFORM_SCALE = 1 << 4,
|
||||
INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 5,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 7,
|
||||
INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 8,
|
||||
INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 9,
|
||||
INSTANCE_DATA_FLAG_USE_VOXEL_GI = 1 << 10,
|
||||
INSTANCE_DATA_FLAG_PARTICLES = 1 << 11,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH = 1 << 12,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D = 1 << 13,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR = 1 << 14,
|
||||
INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA = 1 << 15,
|
||||
};
|
||||
|
||||
static void _geometry_instance_dependency_changed(Dependency::DependencyChangedNotification p_notification, DependencyTracker *p_tracker);
|
||||
static void _geometry_instance_dependency_deleted(const RID &p_dependency, DependencyTracker *p_tracker);
|
||||
|
||||
SelfList<GeometryInstanceGLES3>::List geometry_instance_dirty_list;
|
||||
|
||||
// Use PagedAllocator instead of RID to maximize performance
|
||||
PagedAllocator<GeometryInstanceGLES3> geometry_instance_alloc;
|
||||
PagedAllocator<GeometryInstanceSurface> geometry_instance_surface_alloc;
|
||||
|
||||
void _geometry_instance_add_surface_with_material(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, uint32_t p_material_id, uint32_t p_shader_id, RID p_mesh);
|
||||
void _geometry_instance_add_surface_with_material_chain(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, GLES3::SceneMaterialData *p_material, RID p_mat_src, RID p_mesh);
|
||||
void _geometry_instance_add_surface(GeometryInstanceGLES3 *ginstance, uint32_t p_surface, RID p_material, RID p_mesh);
|
||||
void _geometry_instance_update(RenderGeometryInstance *p_geometry_instance);
|
||||
void _update_dirty_geometry_instances();
|
||||
|
||||
struct SceneState {
|
||||
struct UBO {
|
||||
float projection_matrix[16];
|
||||
float inv_projection_matrix[16];
|
||||
float inv_view_matrix[16];
|
||||
float view_matrix[16];
|
||||
|
||||
float main_cam_inv_view_matrix[16];
|
||||
|
||||
float viewport_size[2];
|
||||
float screen_pixel_size[2];
|
||||
|
||||
float ambient_light_color_energy[4];
|
||||
|
||||
float ambient_color_sky_mix;
|
||||
uint32_t directional_shadow_count;
|
||||
float emissive_exposure_normalization;
|
||||
uint32_t use_ambient_light = 0;
|
||||
|
||||
uint32_t use_ambient_cubemap = 0;
|
||||
uint32_t use_reflection_cubemap = 0;
|
||||
float fog_aerial_perspective;
|
||||
float time;
|
||||
|
||||
float radiance_inverse_xform[12];
|
||||
|
||||
uint32_t directional_light_count;
|
||||
float z_far;
|
||||
float z_near;
|
||||
float IBL_exposure_normalization;
|
||||
|
||||
uint32_t fog_enabled;
|
||||
uint32_t fog_mode;
|
||||
float fog_density;
|
||||
float fog_height;
|
||||
|
||||
float fog_height_density;
|
||||
float fog_depth_curve;
|
||||
float fog_sun_scatter;
|
||||
float fog_depth_begin;
|
||||
|
||||
float fog_light_color[3];
|
||||
float fog_depth_end;
|
||||
|
||||
float shadow_bias;
|
||||
float luminance_multiplier;
|
||||
uint32_t camera_visible_layers;
|
||||
bool pancake_shadows;
|
||||
};
|
||||
static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
|
||||
static_assert(sizeof(UBO) < 16384, "Scene UBO size must be 16384 bytes or smaller");
|
||||
|
||||
struct MultiviewUBO {
|
||||
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
|
||||
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
|
||||
float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
|
||||
};
|
||||
static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
|
||||
static_assert(sizeof(MultiviewUBO) < 16384, "MultiviewUBO size must be 16384 bytes or smaller");
|
||||
|
||||
struct TonemapUBO {
|
||||
float exposure = 1.0;
|
||||
int32_t tonemapper = 0;
|
||||
int32_t pad = 0;
|
||||
int32_t pad2 = 0;
|
||||
float tonemapper_params[4] = { 0.0, 0.0, 0.0, 0.0 };
|
||||
float brightness = 1.0;
|
||||
float contrast = 1.0;
|
||||
float saturation = 1.0;
|
||||
int32_t pad3 = 0;
|
||||
};
|
||||
static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes");
|
||||
|
||||
UBO data;
|
||||
UBO prev_data;
|
||||
GLuint ubo_buffer = 0;
|
||||
GLuint prev_ubo_buffer = 0;
|
||||
MultiviewUBO multiview_data;
|
||||
MultiviewUBO prev_multiview_data;
|
||||
GLuint multiview_buffer = 0;
|
||||
GLuint prev_multiview_buffer = 0;
|
||||
GLuint tonemap_buffer = 0;
|
||||
|
||||
int prev_data_state = 0; // 0 = Motion vectors not used, 1 = use data (first frame only), 2 = use previous data
|
||||
|
||||
bool used_depth_prepass = false;
|
||||
|
||||
GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
|
||||
RSE::CullMode cull_mode = RSE::CULL_MODE_BACK;
|
||||
GLenum current_depth_function = GL_GEQUAL;
|
||||
|
||||
bool current_blend_enabled = false;
|
||||
bool current_depth_draw_enabled = false;
|
||||
bool current_depth_test_enabled = false;
|
||||
bool current_scissor_test_enabled = false;
|
||||
|
||||
void reset_gl_state() {
|
||||
glDisable(GL_BLEND);
|
||||
current_blend_enabled = false;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
current_scissor_test_enabled = false;
|
||||
|
||||
glCullFace(GL_BACK);
|
||||
glEnable(GL_CULL_FACE);
|
||||
cull_mode = RSE::CULL_MODE_BACK;
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
current_depth_draw_enabled = false;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
current_depth_test_enabled = false;
|
||||
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
current_depth_function = GL_GEQUAL;
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
current_stencil_test_enabled = false;
|
||||
glStencilMask(255);
|
||||
current_stencil_write_mask = 255;
|
||||
glStencilFunc(GL_ALWAYS, 0, 255);
|
||||
current_stencil_compare = GL_ALWAYS;
|
||||
current_stencil_reference = 0;
|
||||
current_stencil_compare_mask = 255;
|
||||
}
|
||||
|
||||
void set_gl_cull_mode(RSE::CullMode p_mode) {
|
||||
if (cull_mode != p_mode) {
|
||||
if (p_mode == RSE::CULL_MODE_DISABLED) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
if (cull_mode == RSE::CULL_MODE_DISABLED) {
|
||||
// Last time was disabled, so enable and set proper face.
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
glCullFace(p_mode == RSE::CULL_MODE_FRONT ? GL_FRONT : GL_BACK);
|
||||
}
|
||||
cull_mode = p_mode;
|
||||
}
|
||||
}
|
||||
|
||||
void enable_gl_blend(bool p_enabled) {
|
||||
if (current_blend_enabled != p_enabled) {
|
||||
if (p_enabled) {
|
||||
glEnable(GL_BLEND);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
current_blend_enabled = p_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void enable_gl_scissor_test(bool p_enabled) {
|
||||
if (current_scissor_test_enabled != p_enabled) {
|
||||
if (p_enabled) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
current_scissor_test_enabled = p_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void enable_gl_depth_draw(bool p_enabled) {
|
||||
if (current_depth_draw_enabled != p_enabled) {
|
||||
glDepthMask(p_enabled ? GL_TRUE : GL_FALSE);
|
||||
current_depth_draw_enabled = p_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void enable_gl_depth_test(bool p_enabled) {
|
||||
if (current_depth_test_enabled != p_enabled) {
|
||||
if (p_enabled) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
current_depth_test_enabled = p_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void set_gl_depth_func(GLenum p_depth_func) {
|
||||
if (current_depth_function != p_depth_func) {
|
||||
glDepthFunc(p_depth_func);
|
||||
current_depth_function = p_depth_func;
|
||||
}
|
||||
}
|
||||
|
||||
void enable_gl_stencil_test(bool p_enabled) {
|
||||
if (current_stencil_test_enabled != p_enabled) {
|
||||
if (p_enabled) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
} else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
current_stencil_test_enabled = p_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void set_gl_stencil_func(GLenum p_compare, GLint p_reference, GLenum p_compare_mask) {
|
||||
if (current_stencil_compare != p_compare || current_stencil_reference != p_reference || current_stencil_compare_mask != p_compare_mask) {
|
||||
glStencilFunc(p_compare, p_reference, p_compare_mask);
|
||||
current_stencil_compare = p_compare;
|
||||
current_stencil_reference = p_reference;
|
||||
current_stencil_compare_mask = p_compare_mask;
|
||||
}
|
||||
}
|
||||
|
||||
void set_gl_stencil_write_mask(GLuint p_mask) {
|
||||
if (current_stencil_write_mask != p_mask) {
|
||||
glStencilMask(p_mask);
|
||||
current_stencil_write_mask = p_mask;
|
||||
}
|
||||
}
|
||||
|
||||
void set_gl_stencil_op(GLenum p_op_fail, GLenum p_op_dpfail, GLenum p_op_dppass) {
|
||||
if (current_stencil_op_fail != p_op_fail || current_stencil_op_dpfail != p_op_dpfail || current_stencil_op_dppass != p_op_dppass) {
|
||||
glStencilOp(p_op_fail, p_op_dpfail, p_op_dppass);
|
||||
current_stencil_op_fail = p_op_fail;
|
||||
current_stencil_op_dpfail = p_op_dpfail;
|
||||
current_stencil_op_dppass = p_op_dppass;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum current_stencil_compare = GL_ALWAYS;
|
||||
GLuint current_stencil_compare_mask = 255;
|
||||
GLuint current_stencil_write_mask = 255;
|
||||
GLint current_stencil_reference = 0;
|
||||
GLenum current_stencil_op_fail = GL_KEEP;
|
||||
GLenum current_stencil_op_dpfail = GL_KEEP;
|
||||
GLenum current_stencil_op_dppass = GL_KEEP;
|
||||
bool current_stencil_test_enabled = false;
|
||||
|
||||
bool texscreen_copied = false;
|
||||
bool used_screen_texture = false;
|
||||
bool used_normal_texture = false;
|
||||
bool used_depth_texture = false;
|
||||
bool used_opaque_stencil = false;
|
||||
|
||||
LightData *omni_lights = nullptr;
|
||||
LightData *spot_lights = nullptr;
|
||||
ShadowData *positional_shadows = nullptr;
|
||||
|
||||
InstanceSort<GLES3::LightInstance> *omni_light_sort;
|
||||
InstanceSort<GLES3::LightInstance> *spot_light_sort;
|
||||
GLuint omni_light_buffer = 0;
|
||||
GLuint spot_light_buffer = 0;
|
||||
GLuint positional_shadow_buffer = 0;
|
||||
uint32_t omni_light_count = 0;
|
||||
uint32_t spot_light_count = 0;
|
||||
RSE::ShadowQuality positional_shadow_quality = RSE::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
|
||||
|
||||
DirectionalLightData *directional_lights = nullptr;
|
||||
GLuint directional_light_buffer = 0;
|
||||
DirectionalShadowData *directional_shadows = nullptr;
|
||||
GLuint directional_shadow_buffer = 0;
|
||||
RSE::ShadowQuality directional_shadow_quality = RSE::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
|
||||
} scene_state;
|
||||
|
||||
struct RenderListParameters {
|
||||
GeometryInstanceSurface **elements = nullptr;
|
||||
int element_count = 0;
|
||||
bool reverse_cull = false;
|
||||
uint64_t spec_constant_base_flags = 0;
|
||||
bool force_wireframe = false;
|
||||
Vector2 uv_offset = Vector2(0, 0);
|
||||
|
||||
RenderListParameters(GeometryInstanceSurface **p_elements, int p_element_count, bool p_reverse_cull, uint64_t p_spec_constant_base_flags, bool p_force_wireframe = false, Vector2 p_uv_offset = Vector2()) {
|
||||
elements = p_elements;
|
||||
element_count = p_element_count;
|
||||
reverse_cull = p_reverse_cull;
|
||||
spec_constant_base_flags = p_spec_constant_base_flags;
|
||||
force_wireframe = p_force_wireframe;
|
||||
uv_offset = p_uv_offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderList {
|
||||
LocalVector<GeometryInstanceSurface *> elements;
|
||||
|
||||
void clear() {
|
||||
elements.clear();
|
||||
}
|
||||
|
||||
//should eventually be replaced by radix
|
||||
|
||||
struct SortByKey {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
|
||||
return (A->sort.sort_key2 == B->sort.sort_key2) ? (A->sort.sort_key1 < B->sort.sort_key1) : (A->sort.sort_key2 < B->sort.sort_key2);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_key() {
|
||||
SortArray<GeometryInstanceSurface *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
void sort_by_key_range(uint32_t p_from, uint32_t p_size) {
|
||||
SortArray<GeometryInstanceSurface *, SortByKey> sorter;
|
||||
sorter.sort(elements.ptr() + p_from, p_size);
|
||||
}
|
||||
|
||||
struct SortByDepth {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
|
||||
return (A->owner->depth < B->owner->depth);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_depth() { //used for shadows
|
||||
|
||||
SortArray<GeometryInstanceSurface *, SortByDepth> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
struct SortByReverseDepthAndPriority {
|
||||
_FORCE_INLINE_ bool operator()(const GeometryInstanceSurface *A, const GeometryInstanceSurface *B) const {
|
||||
return (A->sort.priority == B->sort.priority) ? (A->owner->depth > B->owner->depth) : (A->sort.priority < B->sort.priority);
|
||||
}
|
||||
};
|
||||
|
||||
void sort_by_reverse_depth_and_priority() { //used for alpha
|
||||
|
||||
SortArray<GeometryInstanceSurface *, SortByReverseDepthAndPriority> sorter;
|
||||
sorter.sort(elements.ptr(), elements.size());
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_element(GeometryInstanceSurface *p_element) {
|
||||
elements.push_back(p_element);
|
||||
}
|
||||
};
|
||||
|
||||
RenderList render_list[RENDER_LIST_MAX];
|
||||
|
||||
void _update_scene_ubo(GLuint &p_ubo_buffer, GLuint p_index, uint32_t p_size, const void *p_source_data, String p_name = "");
|
||||
|
||||
void _setup_lights(const RenderDataGLES3 *p_render_data, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_omni_light_count, uint32_t &r_spot_light_count, uint32_t &r_directional_shadow_count);
|
||||
void _setup_environment(const RenderDataGLES3 *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_pancake_shadows, float p_shadow_bias = 0.0);
|
||||
void _fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append = false);
|
||||
void _render_shadows(const RenderDataGLES3 *p_render_data, const Size2i &p_viewport_size = Size2i(1, 1));
|
||||
void _render_shadow_pass(RID p_light, RID p_shadow_atlas, int p_pass, const PagedArray<RenderGeometryInstance *> &p_instances, float p_lod_distance_multiplier = 0, float p_screen_mesh_lod_threshold = 0.0, RenderingServerTypes::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1), const Transform3D &p_main_cam_transform = Transform3D());
|
||||
void _render_post_processing(const RenderDataGLES3 *p_render_data);
|
||||
|
||||
template <PassMode p_pass_mode>
|
||||
_FORCE_INLINE_ void _render_list_template(RenderListParameters *p_params, const RenderDataGLES3 *p_render_data, uint32_t p_from_element, uint32_t p_to_element, bool p_alpha_pass = false);
|
||||
|
||||
protected:
|
||||
double time;
|
||||
double time_step = 0;
|
||||
|
||||
bool screen_space_roughness_limiter = false;
|
||||
float screen_space_roughness_limiter_amount = 0.25;
|
||||
float screen_space_roughness_limiter_limit = 0.18;
|
||||
|
||||
void _render_buffers_debug_draw(Ref<RenderSceneBuffersGLES3> p_render_buffers, RID p_shadow_atlas, GLuint p_fbo);
|
||||
|
||||
/* Camera Attributes */
|
||||
|
||||
struct CameraAttributes {
|
||||
float exposure_multiplier = 1.0;
|
||||
float exposure_normalization = 1.0;
|
||||
};
|
||||
|
||||
bool use_physical_light_units = false;
|
||||
mutable RID_Owner<CameraAttributes, true> camera_attributes_owner;
|
||||
|
||||
/* Environment */
|
||||
|
||||
RSE::EnvironmentSSAOQuality ssao_quality = RSE::ENV_SSAO_QUALITY_MEDIUM;
|
||||
bool ssao_half_size = false;
|
||||
float ssao_adaptive_target = 0.5;
|
||||
int ssao_blur_passes = 2;
|
||||
float ssao_fadeout_from = 50.0;
|
||||
float ssao_fadeout_to = 300.0;
|
||||
|
||||
bool glow_bicubic_upscale = false;
|
||||
|
||||
bool lightmap_bicubic_upscale = false;
|
||||
|
||||
/* Sky */
|
||||
|
||||
struct SkyGlobals {
|
||||
float fog_aerial_perspective = 0.0;
|
||||
Color fog_light_color;
|
||||
float fog_sun_scatter = 0.0;
|
||||
bool fog_enabled = false;
|
||||
float fog_density = 0.0;
|
||||
float z_far = 0.0;
|
||||
uint32_t directional_light_count = 0;
|
||||
|
||||
DirectionalLightData *directional_lights = nullptr;
|
||||
DirectionalLightData *last_frame_directional_lights = nullptr;
|
||||
uint32_t last_frame_directional_light_count = 0;
|
||||
GLuint directional_light_buffer = 0;
|
||||
|
||||
RID shader_default_version;
|
||||
RID default_material;
|
||||
RID default_shader;
|
||||
RID fog_material;
|
||||
RID fog_shader;
|
||||
GLuint screen_triangle = 0;
|
||||
GLuint screen_triangle_array = 0;
|
||||
uint32_t max_directional_lights = 4;
|
||||
uint32_t roughness_layers = 8;
|
||||
} sky_globals;
|
||||
|
||||
struct Sky {
|
||||
// Screen Buffers
|
||||
GLuint half_res_pass = 0;
|
||||
GLuint half_res_framebuffer = 0;
|
||||
GLuint quarter_res_pass = 0;
|
||||
GLuint quarter_res_framebuffer = 0;
|
||||
Size2i screen_size = Size2i(0, 0);
|
||||
|
||||
// Radiance Cubemap
|
||||
GLuint radiance = 0;
|
||||
GLuint radiance_framebuffer = 0;
|
||||
GLuint raw_radiance = 0;
|
||||
|
||||
RID material;
|
||||
GLuint uniform_buffer;
|
||||
|
||||
int radiance_size = 256;
|
||||
int mipmap_count = 1;
|
||||
|
||||
RSE::SkyMode mode = RSE::SKY_MODE_AUTOMATIC;
|
||||
|
||||
//ReflectionData reflection;
|
||||
bool reflection_dirty = false;
|
||||
bool dirty = false;
|
||||
int processing_layer = 0;
|
||||
Sky *dirty_list = nullptr;
|
||||
float baked_exposure = 1.0;
|
||||
|
||||
//State to track when radiance cubemap needs updating
|
||||
GLES3::SkyMaterialData *prev_material = nullptr;
|
||||
Vector3 prev_position = Vector3(0.0, 0.0, 0.0);
|
||||
float prev_time = 0.0f;
|
||||
};
|
||||
|
||||
Sky *dirty_sky_list = nullptr;
|
||||
mutable RID_Owner<Sky, true> sky_owner;
|
||||
|
||||
void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size);
|
||||
void _invalidate_sky(Sky *p_sky);
|
||||
void _update_dirty_skys();
|
||||
void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier);
|
||||
void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post);
|
||||
void _free_sky_data(Sky *p_sky);
|
||||
|
||||
// Needed for a single argument calls (material and uv2).
|
||||
PagedArrayPool<RenderGeometryInstance *> cull_argument_pool;
|
||||
PagedArray<RenderGeometryInstance *> cull_argument;
|
||||
|
||||
public:
|
||||
static RasterizerSceneGLES3 *get_singleton() { return singleton; }
|
||||
|
||||
RasterizerCanvasGLES3 *canvas = nullptr;
|
||||
|
||||
RenderGeometryInstance *geometry_instance_create(RID p_base) override;
|
||||
void geometry_instance_free(RenderGeometryInstance *p_geometry_instance) override;
|
||||
|
||||
uint32_t geometry_instance_get_pair_mask() override;
|
||||
|
||||
/* PIPELINES */
|
||||
|
||||
virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) override {}
|
||||
virtual uint32_t get_pipeline_compilations(RSE::PipelineSource p_source) override { return 0; }
|
||||
|
||||
/* SDFGI UPDATE */
|
||||
|
||||
void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {}
|
||||
int sdfgi_get_pending_region_count(const Ref<RenderSceneBuffers> &p_render_buffers) const override {
|
||||
return 0;
|
||||
}
|
||||
AABB sdfgi_get_pending_region_bounds(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override {
|
||||
return AABB();
|
||||
}
|
||||
uint32_t sdfgi_get_pending_region_cascade(const Ref<RenderSceneBuffers> &p_render_buffers, int p_region) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SKY API */
|
||||
|
||||
RID sky_allocate() override;
|
||||
void sky_initialize(RID p_rid) override;
|
||||
void sky_set_radiance_size(RID p_sky, int p_radiance_size) override;
|
||||
void sky_set_mode(RID p_sky, RSE::SkyMode p_mode) override;
|
||||
void sky_set_material(RID p_sky, RID p_material) override;
|
||||
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override;
|
||||
float sky_get_baked_exposure(RID p_sky) const;
|
||||
|
||||
/* ENVIRONMENT API */
|
||||
|
||||
void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
|
||||
|
||||
void environment_set_ssr_half_size(bool p_half_size) override;
|
||||
void environment_set_ssr_roughness_quality(RSE::EnvironmentSSRRoughnessQuality p_quality) override;
|
||||
|
||||
void environment_set_ssao_quality(RSE::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
|
||||
|
||||
void environment_set_ssil_quality(RSE::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
|
||||
|
||||
void environment_set_sdfgi_ray_count(RSE::EnvironmentSDFGIRayCount p_ray_count) override;
|
||||
void environment_set_sdfgi_frames_to_converge(RSE::EnvironmentSDFGIFramesToConverge p_frames) override;
|
||||
void environment_set_sdfgi_frames_to_update_light(RSE::EnvironmentSDFGIFramesToUpdateLight p_update) override;
|
||||
|
||||
void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) override;
|
||||
void environment_set_volumetric_fog_filter_active(bool p_enable) override;
|
||||
|
||||
Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override;
|
||||
|
||||
_FORCE_INLINE_ bool is_using_physical_light_units() {
|
||||
return use_physical_light_units;
|
||||
}
|
||||
|
||||
void positional_soft_shadow_filter_set_quality(RSE::ShadowQuality p_quality) override;
|
||||
void directional_soft_shadow_filter_set_quality(RSE::ShadowQuality p_quality) override;
|
||||
|
||||
RID fog_volume_instance_create(RID p_fog_volume) override;
|
||||
void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override;
|
||||
void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override;
|
||||
RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override;
|
||||
Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override;
|
||||
|
||||
RID voxel_gi_instance_create(RID p_voxel_gi) override;
|
||||
void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override;
|
||||
bool voxel_gi_needs_update(RID p_probe) const override;
|
||||
void voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RenderGeometryInstance *> &p_dynamic_objects) override;
|
||||
|
||||
void voxel_gi_set_quality(RSE::VoxelGIQuality) override;
|
||||
|
||||
void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, float p_window_output_max_value, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingServerTypes::RenderInfo *r_render_info = nullptr) override;
|
||||
void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
|
||||
void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override;
|
||||
|
||||
void set_scene_pass(uint64_t p_pass) override {
|
||||
scene_pass = p_pass;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint64_t get_scene_pass() {
|
||||
return scene_pass;
|
||||
}
|
||||
|
||||
void set_time(double p_time, double p_step) override;
|
||||
void set_debug_draw_mode(RSE::ViewportDebugDraw p_debug_draw) override;
|
||||
_FORCE_INLINE_ RSE::ViewportDebugDraw get_debug_draw_mode() const {
|
||||
return debug_draw;
|
||||
}
|
||||
|
||||
Ref<RenderSceneBuffers> render_buffers_create() override;
|
||||
void gi_set_use_half_resolution(bool p_enable) override;
|
||||
|
||||
void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override;
|
||||
bool screen_space_roughness_limiter_is_active() const override;
|
||||
|
||||
void sub_surface_scattering_set_quality(RSE::SubSurfaceScatteringQuality p_quality) override;
|
||||
void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override;
|
||||
|
||||
TypedArray<Image> bake_render_uv2(RID p_base, const TypedArray<RID> &p_material_overrides, const Size2i &p_image_size) override;
|
||||
void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, GLuint p_framebuffer, const Rect2i &p_region);
|
||||
|
||||
bool free(RID p_rid) override;
|
||||
void update() override;
|
||||
void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) override;
|
||||
|
||||
void decals_set_filter(RSE::DecalFilter p_filter) override;
|
||||
void light_projectors_set_filter(RSE::LightProjectorFilter p_filter) override;
|
||||
virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
|
||||
virtual void material_set_use_debanding(bool p_enable) override;
|
||||
|
||||
RasterizerSceneGLES3();
|
||||
~RasterizerSceneGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
52
engine/drivers/gles3/rasterizer_util_gles3.cpp
Normal file
52
engine/drivers/gles3/rasterizer_util_gles3.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_util_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "rasterizer_util_gles3.h"
|
||||
|
||||
#include "platform_gl.h"
|
||||
|
||||
bool RasterizerUtilGLES3::gles_over_gl = true;
|
||||
|
||||
void RasterizerUtilGLES3::clear_depth(float p_depth) {
|
||||
#ifdef GL_API_ENABLED
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
glClearDepth(p_depth);
|
||||
}
|
||||
#endif // GL_API_ENABLED
|
||||
#ifdef GLES_API_ENABLED
|
||||
if (!RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
glClearDepthf(p_depth);
|
||||
}
|
||||
#endif // GLES_API_ENABLED
|
||||
}
|
||||
|
||||
void RasterizerUtilGLES3::clear_stencil(int32_t p_stencil) {
|
||||
glClearStencil(p_stencil);
|
||||
}
|
||||
51
engine/drivers/gles3/rasterizer_util_gles3.h
Normal file
51
engine/drivers/gles3/rasterizer_util_gles3.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**************************************************************************/
|
||||
/* rasterizer_util_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// This class is meant to hold static utility methods with minimal dependencies.
|
||||
|
||||
class RasterizerUtilGLES3 {
|
||||
private:
|
||||
static bool gles_over_gl;
|
||||
|
||||
public:
|
||||
static void set_gles_over_gl(bool p_gles_over_gl) {
|
||||
gles_over_gl = p_gles_over_gl;
|
||||
}
|
||||
static bool is_gles_over_gl() {
|
||||
return gles_over_gl;
|
||||
}
|
||||
|
||||
static void clear_depth(float p_depth);
|
||||
static void clear_stencil(int32_t p_stencil);
|
||||
};
|
||||
841
engine/drivers/gles3/shader_gles3.cpp
Normal file
841
engine/drivers/gles3/shader_gles3.cpp
Normal file
|
|
@ -0,0 +1,841 @@
|
|||
/**************************************************************************/
|
||||
/* shader_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "shader_gles3.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/string/string_builder.h"
|
||||
#include "drivers/gles3/rasterizer_util_gles3.h"
|
||||
#include "drivers/gles3/storage/config.h"
|
||||
|
||||
static String _mkid(const String &p_id) {
|
||||
String id = "m_" + p_id.replace("__", "_dus_");
|
||||
return id.replace("__", "_dus_"); //doubleunderscore is reserved in glsl
|
||||
}
|
||||
|
||||
void ShaderGLES3::_add_stage(const char *p_code, StageType p_stage_type) {
|
||||
Vector<String> lines = String::utf8(p_code).split("\n");
|
||||
|
||||
String text;
|
||||
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
const String &l = lines[i];
|
||||
bool push_chunk = false;
|
||||
|
||||
StageTemplate::Chunk chunk;
|
||||
|
||||
if (l.begins_with("#GLOBALS")) {
|
||||
switch (p_stage_type) {
|
||||
case STAGE_TYPE_VERTEX:
|
||||
chunk.type = StageTemplate::Chunk::TYPE_VERTEX_GLOBALS;
|
||||
break;
|
||||
case STAGE_TYPE_FRAGMENT:
|
||||
chunk.type = StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS;
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
push_chunk = true;
|
||||
} else if (l.begins_with("#MATERIAL_UNIFORMS")) {
|
||||
chunk.type = StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS;
|
||||
push_chunk = true;
|
||||
} else if (l.begins_with("#CODE")) {
|
||||
chunk.type = StageTemplate::Chunk::TYPE_CODE;
|
||||
push_chunk = true;
|
||||
chunk.code = l.replace_first("#CODE", String()).remove_char(':').strip_edges().to_upper();
|
||||
} else {
|
||||
text += l + "\n";
|
||||
}
|
||||
|
||||
if (push_chunk) {
|
||||
if (text != String()) {
|
||||
StageTemplate::Chunk text_chunk;
|
||||
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
|
||||
text_chunk.text = text.utf8();
|
||||
stage_templates[p_stage_type].chunks.push_back(text_chunk);
|
||||
text = String();
|
||||
}
|
||||
stage_templates[p_stage_type].chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
if (text != String()) {
|
||||
StageTemplate::Chunk text_chunk;
|
||||
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
|
||||
text_chunk.text = text.utf8();
|
||||
stage_templates[p_stage_type].chunks.push_back(text_chunk);
|
||||
text = String();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants) {
|
||||
name = p_name;
|
||||
|
||||
if (p_vertex_code) {
|
||||
_add_stage(p_vertex_code, STAGE_TYPE_VERTEX);
|
||||
}
|
||||
if (p_fragment_code) {
|
||||
_add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
|
||||
}
|
||||
|
||||
uniform_names = p_uniform_names;
|
||||
uniform_count = p_uniform_count;
|
||||
ubo_pairs = p_ubos;
|
||||
ubo_count = p_ubo_count;
|
||||
texunit_pairs = p_tex_units;
|
||||
texunit_pair_count = p_texture_count;
|
||||
specializations = p_specializations;
|
||||
specialization_count = p_specialization_count;
|
||||
specialization_default_mask = 0;
|
||||
for (int i = 0; i < specialization_count; i++) {
|
||||
if (specializations[i].default_value) {
|
||||
specialization_default_mask |= (uint64_t(1) << uint64_t(i));
|
||||
}
|
||||
}
|
||||
variant_defines = p_variants;
|
||||
variant_count = p_variant_count;
|
||||
feedbacks = p_feedback;
|
||||
feedback_count = p_feedback_count;
|
||||
|
||||
StringBuilder tohash;
|
||||
tohash.append("[Vertex]");
|
||||
tohash.append(p_vertex_code ? String::utf8(p_vertex_code) : "");
|
||||
tohash.append("[Fragment]");
|
||||
tohash.append(p_fragment_code ? String::utf8(p_fragment_code) : "");
|
||||
|
||||
tohash.append("[gl_implementation]");
|
||||
const String &vendor = String::utf8((const char *)glGetString(GL_VENDOR));
|
||||
tohash.append(vendor.is_empty() ? "unknown" : vendor);
|
||||
const String &renderer = String::utf8((const char *)glGetString(GL_RENDERER));
|
||||
tohash.append(renderer.is_empty() ? "unknown" : renderer);
|
||||
const String &version = String::utf8((const char *)glGetString(GL_VERSION));
|
||||
tohash.append(version.is_empty() ? "unknown" : version);
|
||||
|
||||
base_sha256 = tohash.as_string().sha256_text();
|
||||
}
|
||||
|
||||
RID ShaderGLES3::version_create() {
|
||||
//initialize() was never called
|
||||
ERR_FAIL_COND_V(variant_count == 0, RID());
|
||||
|
||||
Version version;
|
||||
return version_owner.make_rid(version);
|
||||
}
|
||||
|
||||
void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) {
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
builder.append("#version 330\n");
|
||||
builder.append("#define USE_GLES_OVER_GL\n");
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
}
|
||||
if (p_version->uniforms.size()) {
|
||||
builder.append("#define MATERIAL_UNIFORMS_USED\n");
|
||||
}
|
||||
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
|
||||
builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
|
||||
}
|
||||
|
||||
builder.append("\n"); //make sure defines begin at newline
|
||||
builder.append(general_defines.get_data());
|
||||
builder.append(variant_defines[p_variant]);
|
||||
builder.append("\n");
|
||||
for (int j = 0; j < p_version->custom_defines.size(); j++) {
|
||||
builder.append(p_version->custom_defines[j].get_data());
|
||||
}
|
||||
builder.append("\n"); //make sure defines begin at newline
|
||||
|
||||
// Optional support for external textures.
|
||||
if (GLES3::Config::get_singleton()->external_texture_supported) {
|
||||
builder.append("#extension GL_OES_EGL_image_external : enable\n");
|
||||
builder.append("#extension GL_OES_EGL_image_external_essl3 : enable\n");
|
||||
} else {
|
||||
builder.append("#define samplerExternalOES sampler2D\n");
|
||||
}
|
||||
|
||||
// Insert multiview extension loading, because it needs to appear before
|
||||
// any non-preprocessor code (like the "precision highp..." lines below).
|
||||
builder.append("#ifdef USE_MULTIVIEW\n");
|
||||
builder.append("#if defined(GL_OVR_multiview2)\n");
|
||||
builder.append("#extension GL_OVR_multiview2 : require\n");
|
||||
builder.append("#elif defined(GL_OVR_multiview)\n");
|
||||
builder.append("#extension GL_OVR_multiview : require\n");
|
||||
builder.append("#endif\n");
|
||||
if (p_stage_type == StageType::STAGE_TYPE_VERTEX) {
|
||||
builder.append("layout(num_views=2) in;\n");
|
||||
}
|
||||
builder.append("#define ViewIndex gl_ViewID_OVR\n");
|
||||
builder.append("#define MAX_VIEWS 2\n");
|
||||
builder.append("#else\n");
|
||||
builder.append("#define ViewIndex uint(0)\n");
|
||||
builder.append("#define MAX_VIEWS 1\n");
|
||||
builder.append("#endif\n");
|
||||
|
||||
// Default to highp precision unless specified otherwise.
|
||||
builder.append("precision highp float;\n");
|
||||
builder.append("precision highp int;\n");
|
||||
if (!RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
builder.append("precision highp sampler2D;\n");
|
||||
builder.append("precision highp samplerCube;\n");
|
||||
builder.append("precision highp sampler2DArray;\n");
|
||||
builder.append("precision highp sampler3D;\n");
|
||||
}
|
||||
|
||||
const StageTemplate &stage_template = stage_templates[p_stage_type];
|
||||
for (uint32_t i = 0; i < stage_template.chunks.size(); i++) {
|
||||
const StageTemplate::Chunk &chunk = stage_template.chunks[i];
|
||||
switch (chunk.type) {
|
||||
case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: {
|
||||
builder.append(String::utf8(p_version->uniforms.get_data())); //uniforms (same for vertex and fragment)
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
|
||||
builder.append(String::utf8(p_version->vertex_globals.get_data())); // vertex globals
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
|
||||
builder.append(String::utf8(p_version->fragment_globals.get_data())); // fragment globals
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_CODE: {
|
||||
if (p_version->code_sections.has(chunk.code)) {
|
||||
builder.append(String::utf8(p_version->code_sections[chunk.code].get_data()));
|
||||
}
|
||||
} break;
|
||||
case StageTemplate::Chunk::TYPE_TEXT: {
|
||||
builder.append(String::utf8(chunk.text.get_data()));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _display_error_with_code(const String &p_error, const String &p_code) {
|
||||
int line = 1;
|
||||
Vector<String> lines = p_code.split("\n");
|
||||
|
||||
for (int j = 0; j < lines.size(); j++) {
|
||||
print_line(itos(line) + ": " + lines[j]);
|
||||
line++;
|
||||
}
|
||||
|
||||
ERR_PRINT(p_error);
|
||||
}
|
||||
|
||||
void ShaderGLES3::_get_uniform_locations(Version::Specialization &spec, Version *p_version) {
|
||||
glUseProgram(spec.id);
|
||||
|
||||
spec.uniform_location.resize(uniform_count);
|
||||
for (int i = 0; i < uniform_count; i++) {
|
||||
spec.uniform_location[i] = glGetUniformLocation(spec.id, uniform_names[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < texunit_pair_count; i++) {
|
||||
GLint loc = glGetUniformLocation(spec.id, texunit_pairs[i].name);
|
||||
if (loc >= 0) {
|
||||
if (texunit_pairs[i].index < 0) {
|
||||
glUniform1i(loc, max_image_units + texunit_pairs[i].index);
|
||||
} else {
|
||||
glUniform1i(loc, texunit_pairs[i].index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ubo_count; i++) {
|
||||
GLint loc = glGetUniformBlockIndex(spec.id, ubo_pairs[i].name);
|
||||
if (loc >= 0) {
|
||||
glUniformBlockBinding(spec.id, loc, ubo_pairs[i].index);
|
||||
}
|
||||
}
|
||||
// textures
|
||||
int texture_index = 0;
|
||||
for (uint32_t i = 0; i < p_version->texture_uniforms.size(); i++) {
|
||||
String native_uniform_name = _mkid(p_version->texture_uniforms[i].name);
|
||||
GLint location = glGetUniformLocation(spec.id, (native_uniform_name).ascii().get_data());
|
||||
Vector<int32_t> texture_uniform_bindings;
|
||||
int texture_count = p_version->texture_uniforms[i].array_size;
|
||||
for (int j = 0; j < texture_count; j++) {
|
||||
texture_uniform_bindings.append(texture_index + base_texture_index);
|
||||
texture_index++;
|
||||
}
|
||||
glUniform1iv(location, texture_uniform_bindings.size(), texture_uniform_bindings.ptr());
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization) {
|
||||
spec.id = glCreateProgram();
|
||||
spec.ok = false;
|
||||
GLint status;
|
||||
|
||||
//vertex stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization);
|
||||
|
||||
spec.vert_id = glCreateShader(GL_VERTEX_SHADER);
|
||||
String builder_string = builder.as_string();
|
||||
CharString cs = builder_string.utf8();
|
||||
const char *cstr = cs.ptr();
|
||||
GLint cstr_len = cs.length();
|
||||
glShaderSource(spec.vert_id, 1, &cstr, &cstr_len);
|
||||
glCompileShader(spec.vert_id);
|
||||
|
||||
glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLsizei iloglen;
|
||||
glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen);
|
||||
|
||||
if (iloglen < 0) {
|
||||
glDeleteShader(spec.vert_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
|
||||
ERR_PRINT("No OpenGL vertex shader compiler log.");
|
||||
} else {
|
||||
if (iloglen == 0) {
|
||||
iloglen = 4096; // buggy driver (Adreno 220+)
|
||||
}
|
||||
|
||||
char *ilogmem = (char *)Memory::alloc_static_zeroed(iloglen + 1);
|
||||
glGetShaderInfoLog(spec.vert_id, iloglen, &iloglen, ilogmem);
|
||||
|
||||
String err_string = name + ": Vertex shader compilation failed:\n";
|
||||
|
||||
err_string += ilogmem;
|
||||
|
||||
_display_error_with_code(err_string, builder_string);
|
||||
|
||||
Memory::free_static(ilogmem);
|
||||
glDeleteShader(spec.vert_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
}
|
||||
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
//fragment stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization);
|
||||
|
||||
spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
String builder_string = builder.as_string();
|
||||
CharString cs = builder_string.utf8();
|
||||
const char *cstr = cs.ptr();
|
||||
GLint cstr_len = cs.length();
|
||||
glShaderSource(spec.frag_id, 1, &cstr, &cstr_len);
|
||||
glCompileShader(spec.frag_id);
|
||||
|
||||
glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLsizei iloglen;
|
||||
glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen);
|
||||
|
||||
if (iloglen < 0) {
|
||||
glDeleteShader(spec.frag_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
|
||||
ERR_PRINT("No OpenGL fragment shader compiler log.");
|
||||
} else {
|
||||
if (iloglen == 0) {
|
||||
iloglen = 4096; // buggy driver (Adreno 220+)
|
||||
}
|
||||
|
||||
char *ilogmem = (char *)Memory::alloc_static_zeroed(iloglen + 1);
|
||||
glGetShaderInfoLog(spec.frag_id, iloglen, &iloglen, ilogmem);
|
||||
|
||||
String err_string = name + ": Fragment shader compilation failed:\n";
|
||||
|
||||
err_string += ilogmem;
|
||||
|
||||
_display_error_with_code(err_string, builder_string);
|
||||
|
||||
Memory::free_static(ilogmem);
|
||||
glDeleteShader(spec.frag_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
}
|
||||
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
glAttachShader(spec.id, spec.frag_id);
|
||||
glAttachShader(spec.id, spec.vert_id);
|
||||
|
||||
// If feedback exists, set it up.
|
||||
|
||||
if (feedback_count) {
|
||||
Vector<const char *> feedback;
|
||||
for (int i = 0; i < feedback_count; i++) {
|
||||
if (feedbacks[i].specialization == 0 || (feedbacks[i].specialization & p_specialization)) {
|
||||
// Specialization for this feedback is enabled
|
||||
feedback.push_back(feedbacks[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
if (feedback.size()) {
|
||||
glTransformFeedbackVaryings(spec.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS);
|
||||
}
|
||||
}
|
||||
|
||||
glLinkProgram(spec.id);
|
||||
|
||||
glGetProgramiv(spec.id, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE) {
|
||||
GLsizei iloglen;
|
||||
glGetProgramiv(spec.id, GL_INFO_LOG_LENGTH, &iloglen);
|
||||
|
||||
if (iloglen < 0) {
|
||||
glDeleteShader(spec.frag_id);
|
||||
glDeleteShader(spec.vert_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
|
||||
ERR_PRINT("No OpenGL program link log. Something is wrong.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
if (iloglen == 0) {
|
||||
iloglen = 4096; // buggy driver (Adreno 220+)
|
||||
}
|
||||
|
||||
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
|
||||
ilogmem[iloglen] = '\0';
|
||||
glGetProgramInfoLog(spec.id, iloglen, &iloglen, ilogmem);
|
||||
|
||||
String err_string = name + ": Program linking failed:\n";
|
||||
|
||||
err_string += ilogmem;
|
||||
|
||||
_display_error_with_code(err_string, String());
|
||||
|
||||
Memory::free_static(ilogmem);
|
||||
glDeleteShader(spec.frag_id);
|
||||
glDeleteShader(spec.vert_id);
|
||||
glDeleteProgram(spec.id);
|
||||
spec.id = 0;
|
||||
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
_get_uniform_locations(spec, p_version);
|
||||
|
||||
spec.ok = true;
|
||||
}
|
||||
|
||||
RenderingServerTypes::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
RenderingServerTypes::ShaderNativeSourceCode source_code;
|
||||
ERR_FAIL_NULL_V(version, source_code);
|
||||
|
||||
source_code.versions.resize(variant_count);
|
||||
|
||||
for (int i = 0; i < source_code.versions.size(); i++) {
|
||||
//vertex stage
|
||||
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask);
|
||||
|
||||
RenderingServerTypes::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "vertex";
|
||||
stage.code = builder.as_string();
|
||||
|
||||
source_code.versions.write[i].stages.push_back(stage);
|
||||
}
|
||||
|
||||
//fragment stage
|
||||
{
|
||||
StringBuilder builder;
|
||||
_build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask);
|
||||
|
||||
RenderingServerTypes::ShaderNativeSourceCode::Version::Stage stage;
|
||||
stage.name = "fragment";
|
||||
stage.code = builder.as_string();
|
||||
|
||||
source_code.versions.write[i].stages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
return source_code;
|
||||
}
|
||||
|
||||
String ShaderGLES3::_version_get_sha1(Version *p_version) const {
|
||||
StringBuilder hash_build;
|
||||
|
||||
hash_build.append("[uniforms]");
|
||||
hash_build.append(p_version->uniforms.get_data());
|
||||
hash_build.append("[vertex_globals]");
|
||||
hash_build.append(p_version->vertex_globals.get_data());
|
||||
hash_build.append("[fragment_globals]");
|
||||
hash_build.append(p_version->fragment_globals.get_data());
|
||||
|
||||
Vector<StringName> code_sections;
|
||||
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
|
||||
code_sections.push_back(E.key);
|
||||
}
|
||||
code_sections.sort_custom<StringName::AlphCompare>();
|
||||
|
||||
for (int i = 0; i < code_sections.size(); i++) {
|
||||
hash_build.append(String("[code:") + String(code_sections[i]) + "]");
|
||||
hash_build.append(p_version->code_sections[code_sections[i]].get_data());
|
||||
}
|
||||
for (int i = 0; i < p_version->custom_defines.size(); i++) {
|
||||
hash_build.append("[custom_defines:" + itos(i) + "]");
|
||||
hash_build.append(p_version->custom_defines[i].get_data());
|
||||
}
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl()) {
|
||||
hash_build.append("[gl]");
|
||||
} else {
|
||||
hash_build.append("[gles]");
|
||||
}
|
||||
|
||||
return hash_build.as_string().sha1_text();
|
||||
}
|
||||
|
||||
#ifndef WEB_ENABLED // not supported in webgl
|
||||
static const char *shader_file_header = "GLSC";
|
||||
static const uint32_t cache_file_version = 3;
|
||||
#endif
|
||||
|
||||
bool ShaderGLES3::_load_from_cache(Version *p_version) {
|
||||
#ifdef WEB_ENABLED // not supported in webgl
|
||||
return false;
|
||||
#else
|
||||
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl() && (glProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
String sha1 = _version_get_sha1(p_version);
|
||||
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char header[5] = {};
|
||||
f->get_buffer((uint8_t *)header, 4);
|
||||
ERR_FAIL_COND_V(header != String(shader_file_header), false);
|
||||
|
||||
uint32_t file_version = f->get_32();
|
||||
if (file_version != cache_file_version) {
|
||||
return false; // wrong version
|
||||
}
|
||||
|
||||
int cache_variant_count = static_cast<int>(f->get_32());
|
||||
ERR_FAIL_COND_V_MSG(cache_variant_count != variant_count, false, "shader cache variant count mismatch, expected " + itos(variant_count) + " got " + itos(cache_variant_count)); //should not happen but check
|
||||
|
||||
LocalVector<AHashMap<uint64_t, Version::Specialization>> variants;
|
||||
for (int i = 0; i < cache_variant_count; i++) {
|
||||
uint32_t cache_specialization_count = f->get_32();
|
||||
AHashMap<uint64_t, Version::Specialization> variant;
|
||||
for (uint32_t j = 0; j < cache_specialization_count; j++) {
|
||||
uint64_t specialization_key = f->get_64();
|
||||
uint32_t variant_size = f->get_32();
|
||||
if (variant_size == 0) {
|
||||
continue;
|
||||
}
|
||||
uint32_t variant_format = f->get_32();
|
||||
Vector<uint8_t> variant_bytes;
|
||||
variant_bytes.resize(variant_size);
|
||||
|
||||
uint32_t br = f->get_buffer(variant_bytes.ptrw(), variant_size);
|
||||
|
||||
ERR_FAIL_COND_V(br != variant_size, false);
|
||||
|
||||
Version::Specialization specialization;
|
||||
|
||||
specialization.id = glCreateProgram();
|
||||
if (feedback_count) {
|
||||
Vector<const char *> feedback;
|
||||
for (int feedback_index = 0; feedback_index < feedback_count; feedback_index++) {
|
||||
if (feedbacks[feedback_index].specialization == 0 || (feedbacks[feedback_index].specialization & specialization_key)) {
|
||||
// Specialization for this feedback is enabled.
|
||||
feedback.push_back(feedbacks[feedback_index].name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!feedback.is_empty()) {
|
||||
glTransformFeedbackVaryings(specialization.id, feedback.size(), feedback.ptr(), GL_INTERLEAVED_ATTRIBS);
|
||||
}
|
||||
}
|
||||
glProgramBinary(specialization.id, variant_format, variant_bytes.ptr(), variant_bytes.size());
|
||||
|
||||
GLint link_status = 0;
|
||||
glGetProgramiv(specialization.id, GL_LINK_STATUS, &link_status);
|
||||
if (link_status != GL_TRUE) {
|
||||
WARN_PRINT_ONCE("Failed to load cached shader, recompiling.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_get_uniform_locations(specialization, p_version);
|
||||
|
||||
specialization.ok = true;
|
||||
|
||||
variant.insert(specialization_key, specialization);
|
||||
}
|
||||
variants.push_back(std::move(variant));
|
||||
}
|
||||
p_version->variants = variants;
|
||||
|
||||
return true;
|
||||
#endif // WEB_ENABLED
|
||||
}
|
||||
|
||||
void ShaderGLES3::_save_to_cache(Version *p_version) {
|
||||
#ifdef WEB_ENABLED // not supported in webgl
|
||||
return;
|
||||
#else
|
||||
ERR_FAIL_COND(!shader_cache_dir_valid);
|
||||
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
|
||||
if (RasterizerUtilGLES3::is_gles_over_gl() && (glGetProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
String sha1 = _version_get_sha1(p_version);
|
||||
String path = shader_cache_dir.path_join(name).path_join(base_sha256).path_join(sha1) + ".cache";
|
||||
|
||||
Error error;
|
||||
Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE, &error);
|
||||
ERR_FAIL_COND(f.is_null());
|
||||
f->store_buffer((const uint8_t *)shader_file_header, 4);
|
||||
f->store_32(cache_file_version);
|
||||
f->store_32(variant_count);
|
||||
|
||||
for (int i = 0; i < variant_count; i++) {
|
||||
int cache_specialization_count = p_version->variants[i].size();
|
||||
f->store_32(cache_specialization_count);
|
||||
|
||||
for (KeyValue<uint64_t, ShaderGLES3::Version::Specialization> &kv : p_version->variants[i]) {
|
||||
const uint64_t specialization_key = kv.key;
|
||||
f->store_64(specialization_key);
|
||||
|
||||
const Version::Specialization *specialization = &kv.value;
|
||||
GLint program_size = 0;
|
||||
glGetProgramiv(specialization->id, GL_PROGRAM_BINARY_LENGTH, &program_size);
|
||||
if (program_size == 0) {
|
||||
f->store_32(0);
|
||||
continue;
|
||||
}
|
||||
PackedByteArray compiled_program;
|
||||
compiled_program.resize(program_size);
|
||||
GLenum binary_format = 0;
|
||||
glGetProgramBinary(specialization->id, program_size, nullptr, &binary_format, compiled_program.ptrw());
|
||||
if (program_size != compiled_program.size()) {
|
||||
f->store_32(0);
|
||||
continue;
|
||||
}
|
||||
f->store_32(program_size);
|
||||
f->store_32(binary_format);
|
||||
f->store_buffer(compiled_program.ptr(), compiled_program.size());
|
||||
}
|
||||
}
|
||||
#endif // WEB_ENABLED
|
||||
}
|
||||
|
||||
void ShaderGLES3::_clear_version(Version *p_version) {
|
||||
// Variants not compiled yet, just return
|
||||
if (p_version->variants.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < variant_count; i++) {
|
||||
for (KeyValue<uint64_t, Version::Specialization> &kv : p_version->variants[i]) {
|
||||
if (kv.value.id != 0) {
|
||||
glDeleteShader(kv.value.vert_id);
|
||||
glDeleteShader(kv.value.frag_id);
|
||||
glDeleteProgram(kv.value.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_version->variants.clear();
|
||||
}
|
||||
|
||||
void ShaderGLES3::_initialize_version(Version *p_version) {
|
||||
ERR_FAIL_COND(p_version->variants.size() > 0);
|
||||
bool use_cache = shader_cache_dir_valid && !(feedback_count > 0 && GLES3::Config::get_singleton()->disable_transform_feedback_shader_cache);
|
||||
if (use_cache && _load_from_cache(p_version)) {
|
||||
return;
|
||||
}
|
||||
p_version->variants.reserve(variant_count);
|
||||
for (int i = 0; i < variant_count; i++) {
|
||||
p_version->variants.push_back(AHashMap<uint64_t, Version::Specialization>());
|
||||
Version::Specialization spec;
|
||||
_compile_specialization(spec, i, p_version, specialization_default_mask);
|
||||
p_version->variants[i].insert(specialization_default_mask, spec);
|
||||
}
|
||||
if (use_cache) {
|
||||
_save_to_cache(p_version);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGLES3::version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL(version);
|
||||
|
||||
_clear_version(version); //clear if existing
|
||||
|
||||
version->vertex_globals = p_vertex_globals.utf8();
|
||||
version->fragment_globals = p_fragment_globals.utf8();
|
||||
version->uniforms = p_uniforms.utf8();
|
||||
version->code_sections.clear();
|
||||
version->texture_uniforms = p_texture_uniforms;
|
||||
for (const KeyValue<String, String> &E : p_code) {
|
||||
version->code_sections[StringName(E.key.to_upper())] = E.value.utf8();
|
||||
}
|
||||
|
||||
version->custom_defines.clear();
|
||||
for (int i = 0; i < p_custom_defines.size(); i++) {
|
||||
version->custom_defines.push_back(p_custom_defines[i].utf8());
|
||||
}
|
||||
|
||||
if (p_initialize) {
|
||||
_initialize_version(version);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderGLES3::version_is_valid(RID p_version) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
return version != nullptr;
|
||||
}
|
||||
|
||||
bool ShaderGLES3::version_free(RID p_version) {
|
||||
if (version_owner.owns(p_version)) {
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
_clear_version(version);
|
||||
version_owner.free(p_version);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderGLES3::shader_cache_cleanup_on_start = false;
|
||||
|
||||
ShaderGLES3::ShaderGLES3() {
|
||||
}
|
||||
|
||||
void ShaderGLES3::initialize(const String &p_general_defines, int p_base_texture_index) {
|
||||
general_defines = p_general_defines.utf8();
|
||||
base_texture_index = p_base_texture_index;
|
||||
|
||||
_init();
|
||||
|
||||
if (shader_cache_dir != String()) {
|
||||
StringBuilder hash_build;
|
||||
|
||||
hash_build.append("[base_hash]");
|
||||
hash_build.append(base_sha256);
|
||||
hash_build.append("[general_defines]");
|
||||
hash_build.append(general_defines.get_data());
|
||||
for (int i = 0; i < variant_count; i++) {
|
||||
hash_build.append("[variant_defines:" + itos(i) + "]");
|
||||
hash_build.append(variant_defines[i]);
|
||||
}
|
||||
|
||||
base_sha256 = hash_build.as_string().sha256_text();
|
||||
|
||||
Ref<DirAccess> d = DirAccess::open(shader_cache_dir);
|
||||
ERR_FAIL_COND(d.is_null());
|
||||
if (d->change_dir(name) != OK) {
|
||||
Error err = d->make_dir(name);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
d->change_dir(name);
|
||||
}
|
||||
|
||||
//erase other versions?
|
||||
if (shader_cache_cleanup_on_start) {
|
||||
}
|
||||
//
|
||||
if (d->change_dir(base_sha256) != OK) {
|
||||
Error err = d->make_dir(base_sha256);
|
||||
ERR_FAIL_COND(err != OK);
|
||||
}
|
||||
shader_cache_dir_valid = true;
|
||||
|
||||
print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
|
||||
}
|
||||
|
||||
GLES3::Config *config = GLES3::Config::get_singleton();
|
||||
ERR_FAIL_NULL(config);
|
||||
max_image_units = config->max_texture_image_units;
|
||||
}
|
||||
|
||||
void ShaderGLES3::set_shader_cache_dir(const String &p_dir) {
|
||||
shader_cache_dir = p_dir;
|
||||
}
|
||||
|
||||
void ShaderGLES3::set_shader_cache_save_compressed(bool p_enable) {
|
||||
shader_cache_save_compressed = p_enable;
|
||||
}
|
||||
|
||||
void ShaderGLES3::set_shader_cache_save_compressed_zstd(bool p_enable) {
|
||||
shader_cache_save_compressed_zstd = p_enable;
|
||||
}
|
||||
|
||||
void ShaderGLES3::set_shader_cache_save_debug(bool p_enable) {
|
||||
shader_cache_save_debug = p_enable;
|
||||
}
|
||||
|
||||
String ShaderGLES3::shader_cache_dir;
|
||||
bool ShaderGLES3::shader_cache_save_compressed = true;
|
||||
bool ShaderGLES3::shader_cache_save_compressed_zstd = true;
|
||||
bool ShaderGLES3::shader_cache_save_debug = true;
|
||||
|
||||
ShaderGLES3::~ShaderGLES3() {
|
||||
LocalVector<RID> remaining = version_owner.get_owned_list();
|
||||
if (remaining.size()) {
|
||||
ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
|
||||
for (RID &rid : remaining) {
|
||||
version_free(rid);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
257
engine/drivers/gles3/shader_gles3.h
Normal file
257
engine/drivers/gles3/shader_gles3.h
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
/**************************************************************************/
|
||||
/* shader_gles3.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "core/templates/a_hash_map.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/rendering/rendering_server_types.h"
|
||||
|
||||
#include "platform_gl.h"
|
||||
|
||||
class StringBuilder;
|
||||
|
||||
class ShaderGLES3 {
|
||||
public:
|
||||
struct TextureUniformData {
|
||||
StringName name;
|
||||
int array_size;
|
||||
};
|
||||
|
||||
protected:
|
||||
struct TexUnitPair {
|
||||
const char *name;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct UBOPair {
|
||||
const char *name;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct Specialization {
|
||||
const char *name;
|
||||
bool default_value = false;
|
||||
};
|
||||
|
||||
struct Feedback {
|
||||
const char *name;
|
||||
uint64_t specialization;
|
||||
};
|
||||
|
||||
private:
|
||||
//versions
|
||||
CharString general_defines;
|
||||
|
||||
// A version is a high-level construct which is a combination of built-in and user-defined shader code, Each user-created Shader makes one version
|
||||
// Variants use #ifdefs to toggle behavior on and off to change behavior of the shader
|
||||
// All variants are compiled each time a new version is created
|
||||
// Specializations use #ifdefs to toggle behavior on and off for performance, on supporting hardware, they will compile a version with everything enabled, and then compile more copies to improve performance
|
||||
// Use specializations to enable and disabled advanced features, use variants to toggle behavior when different data may be used (e.g. using a samplerArray vs a sampler, or doing a depth prepass vs a color pass)
|
||||
struct Version {
|
||||
LocalVector<TextureUniformData> texture_uniforms;
|
||||
CharString uniforms;
|
||||
CharString vertex_globals;
|
||||
CharString fragment_globals;
|
||||
HashMap<StringName, CharString> code_sections;
|
||||
Vector<CharString> custom_defines;
|
||||
|
||||
struct Specialization {
|
||||
GLuint id;
|
||||
GLuint vert_id;
|
||||
GLuint frag_id;
|
||||
LocalVector<GLint> uniform_location;
|
||||
LocalVector<GLint> texture_uniform_locations;
|
||||
bool build_queued = false;
|
||||
bool ok = false;
|
||||
Specialization() {
|
||||
id = 0;
|
||||
vert_id = 0;
|
||||
frag_id = 0;
|
||||
}
|
||||
};
|
||||
|
||||
LocalVector<AHashMap<uint64_t, Specialization>> variants;
|
||||
};
|
||||
|
||||
void _get_uniform_locations(Version::Specialization &spec, Version *p_version);
|
||||
void _compile_specialization(Version::Specialization &spec, uint32_t p_variant, Version *p_version, uint64_t p_specialization);
|
||||
|
||||
void _clear_version(Version *p_version);
|
||||
void _initialize_version(Version *p_version);
|
||||
|
||||
RID_Owner<Version, true> version_owner;
|
||||
|
||||
struct StageTemplate {
|
||||
struct Chunk {
|
||||
enum Type {
|
||||
TYPE_MATERIAL_UNIFORMS,
|
||||
TYPE_VERTEX_GLOBALS,
|
||||
TYPE_FRAGMENT_GLOBALS,
|
||||
TYPE_CODE,
|
||||
TYPE_TEXT
|
||||
};
|
||||
|
||||
Type type;
|
||||
StringName code;
|
||||
CharString text;
|
||||
};
|
||||
LocalVector<Chunk> chunks;
|
||||
};
|
||||
|
||||
String name;
|
||||
|
||||
String base_sha256;
|
||||
|
||||
static String shader_cache_dir;
|
||||
static bool shader_cache_cleanup_on_start;
|
||||
static bool shader_cache_save_compressed;
|
||||
static bool shader_cache_save_compressed_zstd;
|
||||
static bool shader_cache_save_debug;
|
||||
bool shader_cache_dir_valid = false;
|
||||
|
||||
GLint max_image_units = 0;
|
||||
|
||||
enum StageType {
|
||||
STAGE_TYPE_VERTEX,
|
||||
STAGE_TYPE_FRAGMENT,
|
||||
STAGE_TYPE_MAX,
|
||||
};
|
||||
|
||||
StageTemplate stage_templates[STAGE_TYPE_MAX];
|
||||
|
||||
void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization);
|
||||
|
||||
void _add_stage(const char *p_code, StageType p_stage_type);
|
||||
|
||||
String _version_get_sha1(Version *p_version) const;
|
||||
bool _load_from_cache(Version *p_version);
|
||||
void _save_to_cache(Version *p_version);
|
||||
|
||||
const char **uniform_names = nullptr;
|
||||
int uniform_count = 0;
|
||||
const UBOPair *ubo_pairs = nullptr;
|
||||
int ubo_count = 0;
|
||||
const Feedback *feedbacks;
|
||||
int feedback_count = 0;
|
||||
const TexUnitPair *texunit_pairs = nullptr;
|
||||
int texunit_pair_count = 0;
|
||||
int specialization_count = 0;
|
||||
const Specialization *specializations = nullptr;
|
||||
uint64_t specialization_default_mask = 0;
|
||||
const char **variant_defines = nullptr;
|
||||
int variant_count = 0;
|
||||
|
||||
int base_texture_index = 0;
|
||||
Version::Specialization *current_shader = nullptr;
|
||||
|
||||
protected:
|
||||
ShaderGLES3();
|
||||
void _setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_feedback_count, const Feedback *p_feedback, int p_texture_count, const TexUnitPair *p_tex_units, int p_specialization_count, const Specialization *p_specializations, int p_variant_count, const char **p_variants);
|
||||
|
||||
_FORCE_INLINE_ bool _version_bind_shader(RID p_version, int p_variant, uint64_t p_specialization) {
|
||||
ERR_FAIL_INDEX_V(p_variant, variant_count, false);
|
||||
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL_V(version, false);
|
||||
|
||||
if (version->variants.is_empty()) {
|
||||
_initialize_version(version); //may lack initialization
|
||||
}
|
||||
|
||||
Version::Specialization *spec = version->variants[p_variant].getptr(p_specialization);
|
||||
if (!spec) {
|
||||
if (false) {
|
||||
// Queue load this specialization and use defaults in the meantime (TODO)
|
||||
|
||||
spec = version->variants[p_variant].getptr(specialization_default_mask);
|
||||
} else {
|
||||
// Compile on the spot
|
||||
Version::Specialization s;
|
||||
_compile_specialization(s, p_variant, version, p_specialization);
|
||||
version->variants[p_variant].insert(p_specialization, s);
|
||||
spec = version->variants[p_variant].getptr(p_specialization);
|
||||
if (shader_cache_dir_valid) {
|
||||
_save_to_cache(version);
|
||||
}
|
||||
}
|
||||
} else if (spec->build_queued) {
|
||||
// Still queued, wait
|
||||
spec = version->variants[p_variant].getptr(specialization_default_mask);
|
||||
}
|
||||
|
||||
if (!spec || !spec->ok) {
|
||||
WARN_PRINT_ONCE("shader failed to compile, unable to bind shader.");
|
||||
return false;
|
||||
}
|
||||
|
||||
glUseProgram(spec->id);
|
||||
current_shader = spec;
|
||||
return true;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int _version_get_uniform(int p_which, RID p_version, int p_variant, uint64_t p_specialization) {
|
||||
ERR_FAIL_INDEX_V(p_which, uniform_count, -1);
|
||||
Version *version = version_owner.get_or_null(p_version);
|
||||
ERR_FAIL_NULL_V(version, -1);
|
||||
ERR_FAIL_INDEX_V(p_variant, int(version->variants.size()), -1);
|
||||
Version::Specialization *spec = version->variants[p_variant].getptr(p_specialization);
|
||||
ERR_FAIL_NULL_V(spec, -1);
|
||||
ERR_FAIL_INDEX_V(p_which, int(spec->uniform_location.size()), -1);
|
||||
return spec->uniform_location[p_which];
|
||||
}
|
||||
|
||||
virtual void _init() = 0;
|
||||
|
||||
public:
|
||||
RID version_create();
|
||||
|
||||
void version_set_code(RID p_version, const HashMap<String, String> &p_code, const String &p_uniforms, const String &p_vertex_globals, const String &p_fragment_globals, const Vector<String> &p_custom_defines, const LocalVector<ShaderGLES3::TextureUniformData> &p_texture_uniforms, bool p_initialize = false);
|
||||
|
||||
bool version_is_valid(RID p_version);
|
||||
|
||||
bool version_free(RID p_version);
|
||||
|
||||
static void set_shader_cache_dir(const String &p_dir);
|
||||
static void set_shader_cache_save_compressed(bool p_enable);
|
||||
static void set_shader_cache_save_compressed_zstd(bool p_enable);
|
||||
static void set_shader_cache_save_debug(bool p_enable);
|
||||
|
||||
RenderingServerTypes::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
|
||||
|
||||
void initialize(const String &p_general_defines = "", int p_base_texture_index = 0);
|
||||
virtual ~ShaderGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue