Initial commit

This commit is contained in:
hertog 2025-03-13 08:40:48 +00:00
commit 65227bf3a5
12416 changed files with 6001067 additions and 0 deletions

61
engine/drivers/SCsub Normal file
View file

@ -0,0 +1,61 @@
#!/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")
if env["platform"] == "ios" or env["platform"] == "macos":
SConscript("coreaudio/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"]:
SConscript("apple/SCsub")
# Midi drivers
SConscript("alsamidi/SCsub")
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")
# 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])

10
engine/drivers/alsa/SCsub Normal file
View 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")

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,349 @@
/**************************************************************************/
/* 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/project_settings.h"
#include "core/os/os.h"
#include <errno.h>
#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 = 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

View file

@ -0,0 +1,99 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef AUDIO_DRIVER_ALSA_H
#define AUDIO_DRIVER_ALSA_H
#ifdef ALSA_ENABLED
#include "core/os/mutex.h"
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
#include "servers/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
#endif // AUDIO_DRIVER_ALSA_H

View 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")

View 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 <errno.h>
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

View file

@ -0,0 +1,82 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef MIDI_DRIVER_ALSAMIDI_H
#define MIDI_DRIVER_ALSAMIDI_H
#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
#endif // MIDI_DRIVER_ALSAMIDI_H

View 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")

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* joypad_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. */
/**************************************************************************/
#include "core/input/input.h"
#define Key _QKey
#import <GameController/GameController.h>
#undef Key
@class GCController;
class RumbleContext;
struct GameController {
int joy_id;
GCController *controller;
RumbleContext *rumble_context API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) = nil;
NSInteger ff_effect_timestamp = 0;
bool force_feedback = false;
GameController(int p_joy_id, GCController *p_controller);
~GameController();
};
class JoypadApple {
private:
id<NSObject> connect_observer = nil;
id<NSObject> disconnect_observer = nil;
HashMap<int, GameController *> joypads;
HashMap<GCController *, int> controller_to_joy_id;
GCControllerPlayerIndex get_free_player_index();
void add_joypad(GCController *p_controller);
void remove_joypad(GCController *p_controller);
public:
JoypadApple();
~JoypadApple();
void joypad_vibration_start(GameController &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0));
void joypad_vibration_stop(GameController &p_joypad, uint64_t p_timestamp) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0));
void process_joypads();
};

View file

@ -0,0 +1,427 @@
/**************************************************************************/
/* joypad_apple.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 "joypad_apple.h"
#include <CoreHaptics/CoreHaptics.h>
#import <os/log.h>
#include "core/config/project_settings.h"
#include "main/main.h"
class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleMotor {
CHHapticEngine *engine;
id<CHHapticPatternPlayer> player;
bool is_started;
RumbleMotor(GCController *p_controller, GCHapticsLocality p_locality) {
engine = [p_controller.haptics createEngineWithLocality:p_locality];
engine.autoShutdownEnabled = YES;
}
public:
static RumbleMotor *create(GCController *p_controller, GCHapticsLocality p_locality) {
if ([p_controller.haptics.supportedLocalities containsObject:p_locality]) {
return memnew(RumbleMotor(p_controller, p_locality));
}
return nullptr;
}
_ALWAYS_INLINE_ bool has_active_player() {
return player != nil;
}
void execute_pattern(CHHapticPattern *p_pattern) {
NSError *error;
if (!is_started) {
ERR_FAIL_COND_MSG(![engine startAndReturnError:&error], "Couldn't start controller haptic engine: " + String::utf8(error.localizedDescription.UTF8String));
is_started = YES;
}
player = [engine createPlayerWithPattern:p_pattern error:&error];
ERR_FAIL_COND_MSG(error, "Couldn't create controller haptic pattern player: " + String::utf8(error.localizedDescription.UTF8String));
ERR_FAIL_COND_MSG(![player startAtTime:CHHapticTimeImmediate error:&error], "Couldn't execute controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
}
void stop() {
id<CHHapticPatternPlayer> old_player = player;
player = nil;
NSError *error;
ERR_FAIL_COND_MSG(![old_player stopAtTime:CHHapticTimeImmediate error:&error], "Couldn't stop controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
}
};
class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleContext {
RumbleMotor *weak_motor;
RumbleMotor *strong_motor;
public:
RumbleContext(GCController *p_controller) {
weak_motor = RumbleMotor::create(p_controller, GCHapticsLocalityRightHandle);
strong_motor = RumbleMotor::create(p_controller, GCHapticsLocalityLeftHandle);
}
~RumbleContext() {
if (weak_motor) {
memdelete(weak_motor);
}
if (strong_motor) {
memdelete(strong_motor);
}
}
_ALWAYS_INLINE_ bool has_motors() {
return weak_motor != nullptr && strong_motor != nullptr;
}
_ALWAYS_INLINE_ bool has_active_players() {
if (!has_motors()) {
return false;
}
return (weak_motor && weak_motor->has_active_player()) || (strong_motor && strong_motor->has_active_player());
}
void stop() {
if (weak_motor) {
weak_motor->stop();
}
if (strong_motor) {
strong_motor->stop();
}
}
void play_weak_pattern(CHHapticPattern *p_pattern) {
if (weak_motor) {
weak_motor->execute_pattern(p_pattern);
}
}
void play_strong_pattern(CHHapticPattern *p_pattern) {
if (strong_motor) {
strong_motor->execute_pattern(p_pattern);
}
}
};
GameController::GameController(int p_joy_id, GCController *p_controller) :
joy_id(p_joy_id), controller(p_controller) {
force_feedback = NO;
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
if (controller.haptics != nil) {
// Create a rumble context for the controller.
rumble_context = memnew(RumbleContext(p_controller));
// If the rumble motors aren't available, disable force feedback.
force_feedback = rumble_context->has_motors();
}
}
int l_joy_id = joy_id;
auto BUTTON = [l_joy_id](JoyButton p_button) {
return ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_button(l_joy_id, p_button, pressed);
};
};
if (controller.extendedGamepad != nil) {
GCExtendedGamepad *gamepad = controller.extendedGamepad;
gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::B);
gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::Y);
gamepad.leftShoulder.pressedChangedHandler = BUTTON(JoyButton::LEFT_SHOULDER);
gamepad.rightShoulder.pressedChangedHandler = BUTTON(JoyButton::RIGHT_SHOULDER);
gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
gamepad.leftThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_X, xValue);
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_Y, -yValue);
};
gamepad.rightThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_X, xValue);
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_Y, -yValue);
};
gamepad.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_LEFT, value);
};
gamepad.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_RIGHT, value);
};
if (@available(macOS 10.14.1, iOS 12.1, tvOS 12.1, *)) {
gamepad.leftThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::LEFT_STICK);
gamepad.rightThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::RIGHT_STICK);
}
if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
gamepad.buttonOptions.pressedChangedHandler = BUTTON(JoyButton::BACK);
gamepad.buttonMenu.pressedChangedHandler = BUTTON(JoyButton::START);
}
if (@available(macOS 11, iOS 14.0, tvOS 14.0, *)) {
gamepad.buttonHome.pressedChangedHandler = BUTTON(JoyButton::GUIDE);
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
xboxGamepad.paddleButton1.pressedChangedHandler = BUTTON(JoyButton::PADDLE1);
xboxGamepad.paddleButton2.pressedChangedHandler = BUTTON(JoyButton::PADDLE2);
xboxGamepad.paddleButton3.pressedChangedHandler = BUTTON(JoyButton::PADDLE3);
xboxGamepad.paddleButton4.pressedChangedHandler = BUTTON(JoyButton::PADDLE4);
}
}
if (@available(macOS 12, iOS 15.0, tvOS 15.0, *)) {
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
xboxGamepad.buttonShare.pressedChangedHandler = BUTTON(JoyButton::MISC1);
}
}
} else if (controller.microGamepad != nil) {
GCMicroGamepad *gamepad = controller.microGamepad;
gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
}
// TODO: Need to add support for controller.motion which gives us access to
// the orientation of the device (if supported).
}
GameController::~GameController() {
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
if (rumble_context) {
memdelete(rumble_context);
}
}
}
JoypadApple::JoypadApple() {
connect_observer = [NSNotificationCenter.defaultCenter
addObserverForName:GCControllerDidConnectNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
GCController *controller = notification.object;
if (!controller) {
return;
}
add_joypad(controller);
}];
disconnect_observer = [NSNotificationCenter.defaultCenter
addObserverForName:GCControllerDidDisconnectNotification
object:nil
queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
GCController *controller = notification.object;
if (!controller) {
return;
}
remove_joypad(controller);
}];
if (@available(macOS 11.3, iOS 14.5, tvOS 14.5, *)) {
GCController.shouldMonitorBackgroundEvents = YES;
}
}
JoypadApple::~JoypadApple() {
for (KeyValue<int, GameController *> &E : joypads) {
memdelete(E.value);
E.value = nullptr;
}
[NSNotificationCenter.defaultCenter removeObserver:connect_observer];
[NSNotificationCenter.defaultCenter removeObserver:disconnect_observer];
}
// Finds the rightmost set bit in a number, n.
// variation of https://www.geeksforgeeks.org/position-of-rightmost-set-bit/
int rightmost_one(int n) {
return __builtin_ctz(n & -n) + 1;
}
GCControllerPlayerIndex JoypadApple::get_free_player_index() {
// player_set will be a bitfield where each bit represents a player index.
__block uint32_t player_set = 0;
for (const KeyValue<GCController *, int> &E : controller_to_joy_id) {
player_set |= 1U << E.key.playerIndex;
}
// invert, as we want to find the first unset player index.
int n = rightmost_one((int)(~player_set));
if (n >= 5) {
return GCControllerPlayerIndexUnset;
}
return (GCControllerPlayerIndex)(n - 1);
}
void JoypadApple::add_joypad(GCController *p_controller) {
if (controller_to_joy_id.has(p_controller)) {
return;
}
// Get a new id for our controller.
int joy_id = Input::get_singleton()->get_unused_joy_id();
if (joy_id == -1) {
print_verbose("Couldn't retrieve new joy ID.");
return;
}
// Assign our player index.
if (p_controller.playerIndex == GCControllerPlayerIndexUnset) {
p_controller.playerIndex = get_free_player_index();
}
// Tell Godot about our new controller.
Input::get_singleton()->joy_connection_changed(joy_id, true, String::utf8(p_controller.vendorName.UTF8String));
// Assign our player index.
joypads.insert(joy_id, memnew(GameController(joy_id, p_controller)));
controller_to_joy_id.insert(p_controller, joy_id);
}
void JoypadApple::remove_joypad(GCController *p_controller) {
if (!controller_to_joy_id.has(p_controller)) {
return;
}
int joy_id = controller_to_joy_id[p_controller];
controller_to_joy_id.erase(p_controller);
// Tell Godot this joystick is no longer there.
Input::get_singleton()->joy_connection_changed(joy_id, false, "");
// And remove it from our dictionary.
GameController **old = joypads.getptr(joy_id);
memdelete(*old);
*old = nullptr;
joypads.erase(joy_id);
}
API_AVAILABLE(macos(10.15), ios(13.0), tvos(14.0))
CHHapticPattern *get_vibration_pattern(float p_magnitude, float p_duration) {
// Creates a vibration pattern with an intensity and duration.
NSDictionary *hapticDict = @{
CHHapticPatternKeyPattern : @[
@{
CHHapticPatternKeyEvent : @{
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
CHHapticPatternKeyEventDuration : [NSNumber numberWithFloat:p_duration],
CHHapticPatternKeyEventParameters : @[
@{
CHHapticPatternKeyParameterID : CHHapticEventParameterIDHapticIntensity,
CHHapticPatternKeyParameterValue : [NSNumber numberWithFloat:p_magnitude]
},
],
},
},
],
};
NSError *error;
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
return pattern;
}
void JoypadApple::joypad_vibration_start(GameController &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
if (!p_joypad.force_feedback || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
return;
}
// If there is active vibration players, stop them.
if (p_joypad.rumble_context->has_active_players()) {
joypad_vibration_stop(p_joypad, p_timestamp);
}
// Gets the default vibration pattern and creates a player for each motor.
CHHapticPattern *weak_pattern = get_vibration_pattern(p_weak_magnitude, p_duration);
CHHapticPattern *strong_pattern = get_vibration_pattern(p_strong_magnitude, p_duration);
p_joypad.rumble_context->play_weak_pattern(weak_pattern);
p_joypad.rumble_context->play_strong_pattern(strong_pattern);
p_joypad.ff_effect_timestamp = p_timestamp;
}
void JoypadApple::joypad_vibration_stop(GameController &p_joypad, uint64_t p_timestamp) {
if (!p_joypad.force_feedback) {
return;
}
// If there is no active vibration players, exit.
if (!p_joypad.rumble_context->has_active_players()) {
return;
}
p_joypad.rumble_context->stop();
p_joypad.ff_effect_timestamp = p_timestamp;
}
void JoypadApple::process_joypads() {
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
for (KeyValue<int, GameController *> &E : joypads) {
int id = E.key;
GameController &joypad = *E.value;
if (joypad.force_feedback) {
Input *input = Input::get_singleton();
uint64_t timestamp = input->get_joy_vibration_timestamp(id);
if (timestamp > (unsigned)joypad.ff_effect_timestamp) {
Vector2 strength = input->get_joy_vibration_strength(id);
float duration = input->get_joy_vibration_duration(id);
if (duration == 0) {
duration = GCHapticDurationInfinite;
}
if (strength.x == 0 && strength.y == 0) {
joypad_vibration_stop(joypad, timestamp);
} else {
joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
}
}
}
}
}
}

View 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)

View 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")

View file

@ -0,0 +1,130 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef AUDIO_DRIVER_COREAUDIO_H
#define AUDIO_DRIVER_COREAUDIO_H
#ifdef COREAUDIO_ENABLED
#include "servers/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;
Vector<int16_t> input_buf;
#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
#endif // AUDIO_DRIVER_COREAUDIO_H

View file

@ -0,0 +1,724 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#include "audio_driver_coreaudio.h"
#ifdef COREAUDIO_ENABLED
#include "core/config/project_settings.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);
int latency = Engine::get_singleton()->get_audio_output_latency();
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
buffer_frames = closest_power_of_2(latency * mix_rate / 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 = ad->input_buf.ptrw();
bufferList.mBuffers[0].mNumberChannels = ad->capture_channels;
bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t);
OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
if (result == noErr) {
for (unsigned int i = 0; i < inNumberFrames * ad->capture_channels; i++) {
int32_t sample = ad->input_buf[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);
flag = 0;
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
ERR_FAIL_COND_V(result != noErr, FAILED);
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);
int latency = Engine::get_singleton()->get_audio_output_latency();
// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
capture_buffer_frames = closest_power_of_2(latency * capture_mix_rate / 1000);
unsigned int buffer_size = capture_buffer_frames * capture_channels;
input_buf.resize(buffer_size);
AURenderCallbackStruct callback;
memset(&callback, 0, sizeof(AURenderCallbackStruct));
callback.inputProc = &AudioDriverCoreAudio::input_callback;
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() {
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

View 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")

View file

@ -0,0 +1,133 @@
/**************************************************************************/
/* midi_driver_coremidi.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 "midi_driver_coremidi.h"
#ifdef COREMIDI_ENABLED
#include "core/string/print_string.h"
#import <CoreAudio/HostTime.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

View file

@ -0,0 +1,71 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef MIDI_DRIVER_COREMIDI_H
#define MIDI_DRIVER_COREMIDI_H
#ifdef COREMIDI_ENABLED
#include "core/os/midi_driver.h"
#include "core/os/mutex.h"
#include "core/templates/vector.h"
#import <CoreMIDI/CoreMIDI.h>
#include <stdio.h>
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
#endif // MIDI_DRIVER_COREMIDI_H

167
engine/drivers/d3d12/SCsub Normal file
View file

@ -0,0 +1,167 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import os
from pathlib import Path
import methods
Import("env")
env_d3d12_rdd = env.Clone()
thirdparty_obj = []
# DirectX Headers (must take precedence over Windows SDK's).
env.Prepend(CPPPATH=["#thirdparty/directx_headers/include/directx"])
env_d3d12_rdd.Prepend(CPPPATH=["#thirdparty/directx_headers/include/directx"])
env_d3d12_rdd.Prepend(CPPPATH=["#thirdparty/directx_headers/include/dxguids"])
# Direct3D 12 Memory Allocator.
env.Append(CPPPATH=["#thirdparty/d3d12ma"])
env_d3d12_rdd.Append(CPPPATH=["#thirdparty/d3d12ma"])
# 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"])
# Mesa (SPIR-V to DXIL functionality).
mesa_dir = (env["mesa_libs"] + "/godot-mesa").replace("\\", "/")
mesa_gen_dir = (env["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")
# 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"),
]
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",
("_HAS_EXCEPTIONS", 0),
"NOMINMAX",
"HAVE_STRUCT_TIMESPEC",
]
else:
extra_defines += [
"HAVE_STRUCT_TIMESPEC",
]
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"])
env.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)
# Add all.
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)

View file

@ -0,0 +1,60 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef D3D12_GODOT_NIR_BRIDGE_H
#define D3D12_GODOT_NIR_BRIDGE_H
#include <stdint.h>
#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;
#ifdef __cplusplus
}
#endif
#endif // D3D12_GODOT_NIR_BRIDGE_H

View file

@ -0,0 +1,64 @@
/**************************************************************************/
/* d3d12ma.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"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#pragma GCC diagnostic ignored "-Wduplicated-branches"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wnonnull-compare"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#pragma clang diagnostic ignored "-Wstring-plus-int"
#pragma clang diagnostic ignored "-Wswitch"
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wunused-private-field"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#if defined(_MSC_VER)
#pragma warning(disable : 4189 4505)
#endif
#include "thirdparty/d3d12ma/D3D12MemAlloc.cpp"

View 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);
}

View file

@ -0,0 +1,39 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef DXIL_HASH_H
#define DXIL_HASH_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash);
#endif // DXIL_HASH_H

View file

@ -0,0 +1,347 @@
/**************************************************************************/
/* 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 "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/local_vector.h"
#include "core/version.h"
#include "servers/rendering/rendering_device.h"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#pragma clang diagnostic ignored "-Wstring-plus-int"
#pragma clang diagnostic ignored "-Wswitch"
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "dxcapi.h"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic pop
#endif
#if !defined(_MSC_VER)
#include <guiddef.h>
#include <dxguids.h>
#endif
// 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_D3D12DeviceFactoryGodot = { 0x114863bf, 0xc386, 0x4aee, { 0xb3, 0x9d, 0x8f, 0x0b, 0xbb, 0x06, 0x29, 0x55 } };
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);
}
if (lib_dcomp) {
FreeLibrary(lib_dcomp);
}
}
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);
lib_dcomp = LoadLibraryW(L"Dcomp.dll");
ERR_FAIL_NULL_V(lib_dcomp, ERR_CANT_CREATE);
// 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.
}
ID3D12SDKConfiguration *sdk_config = nullptr;
if (SUCCEEDED(d3d_D3D12GetInterface(CLSID_D3D12SDKConfigurationGodot, IID_PPV_ARGS(&sdk_config)))) {
ID3D12SDKConfiguration1 *sdk_config1 = nullptr;
if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) {
if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf())))) {
d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
} else if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf())))) {
d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
}
sdk_config1->Release();
}
sdk_config->Release();
}
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::_initialize_devices() {
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);
// 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, DisplayServer::VSyncMode p_vsync_mode) {
Surface *surface = (Surface *)(p_surface);
surface->vsync_mode = p_vsync_mode;
surface->needs_resize = true;
}
DisplayServer::VSyncMode RenderingContextDriverD3D12::surface_get_vsync_mode(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->vsync_mode;
}
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() const {
return dxgi_factory.Get();
}
bool RenderingContextDriverD3D12::get_tearing_supported() const {
return tearing_supported;
}

View file

@ -0,0 +1,150 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef RENDERING_CONTEXT_DRIVER_D3D12_H
#define RENDERING_CONTEXT_DRIVER_D3D12_H
#include "core/error/error_list.h"
#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_server.h"
#include "servers/rendering/rendering_context_driver.h"
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wswitch"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#pragma clang diagnostic ignored "-Wstring-plus-int"
#pragma clang diagnostic ignored "-Wswitch"
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#if defined(AS)
#undef AS
#endif
#if (WINVER < _WIN32_WINNT_WIN8) && defined(_MSC_VER)
#pragma push_macro("NTDDI_VERSION")
#pragma push_macro("WINVER")
#undef NTDDI_VERSION
#undef WINVER
#define NTDDI_VERSION NTDDI_WIN8
#define WINVER _WIN32_WINNT_WIN8
#include <dcomp.h>
#pragma pop_macro("WINVER")
#pragma pop_macro("NTDDI_VERSION")
#else
#include <dcomp.h>
#endif
#include "d3dx12.h"
#include <dxgi1_6.h>
#include <wrl/client.h>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(__clang__)
#pragma clang diagnostic pop
#endif
using Microsoft::WRL::ComPtr;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
class RenderingContextDriverD3D12 : public RenderingContextDriver {
ComPtr<ID3D12DeviceFactory> device_factory;
ComPtr<IDXGIFactory2> dxgi_factory;
TightLocalVector<Device> driver_devices;
bool tearing_supported = false;
Error _init_device_factory();
Error _initialize_debug_layers();
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, DisplayServer::VSyncMode p_vsync_mode) override;
virtual DisplayServer::VSyncMode surface_get_vsync_mode(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;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
ComPtr<IDCompositionDevice> composition_device;
ComPtr<IDCompositionTarget> composition_target;
ComPtr<IDCompositionVisual> composition_visual;
};
HMODULE lib_d3d12 = nullptr;
HMODULE lib_dxgi = nullptr;
HMODULE lib_dcomp = nullptr;
IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const;
ID3D12DeviceFactory *device_factory_get() const;
IDXGIFactory2 *dxgi_factory_get() const;
bool get_tearing_supported() const;
bool use_validation_layers() const;
RenderingContextDriverD3D12();
virtual ~RenderingContextDriverD3D12() override;
};
#endif // RENDERING_CONTEXT_DRIVER_D3D12_H

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

7
engine/drivers/egl/SCsub Normal file
View 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")

View file

@ -0,0 +1,538 @@
/**************************************************************************/
/* 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/crypto/crypto_core.h"
#include "core/io/dir_access.h"
#include "drivers/gles3/rasterizer_gles3.h"
#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("/", "_");
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("/", "_");
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(DisplayServer::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(DisplayServer::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 &current_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 &current_display = displays[current_window->gldisplay_id];
eglSwapBuffers(current_display.egl_display, current_window->egl_surface);
}
void EGLManager::window_make_current(DisplayServer::WindowID p_window_id) {
if (p_window_id == DisplayServer::INVALID_WINDOW_ID) {
return;
}
GLWindow &glwindow = windows[p_window_id];
if (&glwindow == current_window || !glwindow.initialized) {
return;
}
current_window = &glwindow;
GLDisplay &current_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(DisplayServer::WindowID p_window_id) {
GLWindow &glwindow = windows[p_window_id];
if (!glwindow.initialized) {
return EGL_NO_CONTEXT;
}
GLDisplay &display = displays[glwindow.gldisplay_id];
return display.egl_context;
}
EGLDisplay EGLManager::get_display(DisplayServer::WindowID p_window_id) {
GLWindow &glwindow = windows[p_window_id];
if (!glwindow.initialized) {
return EGL_NO_CONTEXT;
}
GLDisplay &display = displays[glwindow.gldisplay_id];
return display.egl_display;
}
EGLConfig EGLManager::get_config(DisplayServer::WindowID p_window_id) {
GLWindow &glwindow = windows[p_window_id];
if (!glwindow.initialized) {
return nullptr;
}
GLDisplay &display = displays[glwindow.gldisplay_id];
return display.egl_config;
}
Error EGLManager::initialize(void *p_native_display) {
#if defined(GLAD_ENABLED) && !defined(EGL_STATIC)
// Loading EGL with a new display gets us just the bare minimum API. We'll then
// 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

View file

@ -0,0 +1,123 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef EGL_MANAGER_H
#define EGL_MANAGER_H
#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_server.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(DisplayServer::WindowID p_window_id, void *p_display, void *p_native_window, int p_width, int p_height);
void window_destroy(DisplayServer::WindowID p_window_id);
void release_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
void set_use_vsync(bool p_use);
bool is_using_vsync() const;
EGLContext get_context(DisplayServer::WindowID p_window_id);
EGLDisplay get_display(DisplayServer::WindowID p_window_id);
EGLConfig get_config(DisplayServer::WindowID p_window_id);
Error initialize(void *p_native_display = nullptr);
EGLManager();
virtual ~EGLManager();
};
#endif // EGL_ENABLED
#endif // EGL_MANAGER_H

View file

@ -0,0 +1,30 @@
#!/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]
# Treat glad headers as system headers to avoid raising warnings. Not supported on MSVC.
if not env.msvc:
env.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
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")

View 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")

View 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")

View file

@ -0,0 +1,310 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "copy_effects.h"
#include "../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(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
{ // Screen Quad
glGenBuffers(1, &quad);
glBindBuffer(GL_ARRAY_BUFFER, quad);
const float qv[12] = {
-1.0f,
-1.0f,
1.0f,
-1.0f,
1.0f,
1.0f,
-1.0f,
-1.0f,
1.0f,
1.0f,
-1.0f,
1.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &quad_array);
glBindVertexArray(quad_array);
glBindBuffer(GL_ARRAY_BUFFER, quad);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
CopyEffects::~CopyEffects() {
singleton = nullptr;
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
glDeleteBuffers(1, &quad);
glDeleteVertexArrays(1, &quad_array);
copy.shader.version_free(copy.shader_version);
}
void CopyEffects::copy_to_rect(const Rect2 &p_rect) {
bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION);
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);
draw_screen_quad();
}
void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod) {
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;
bool success = copy.shader.version_bind_shader(copy.shader_version, variant);
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);
copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant);
copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant);
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 - 1);
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

View file

@ -0,0 +1,81 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef COPY_EFFECTS_GLES3_H
#define COPY_EFFECTS_GLES3_H
#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);
void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f);
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
#endif // COPY_EFFECTS_GLES3_H

View 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "cubemap_filter.h"
#include "../storage/texture_storage.h"
#include "core/config/project_settings.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(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::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 = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
// Convert to spherical direction
Vector3 half_vector;
half_vector.x = sin_theta * cos(phi);
half_vector.y = sin_theta * sin(phi);
half_vector.z = cos_theta;
return half_vector;
}
float distribution_GGX(float NdotH, float roughness4) {
float NdotH2 = NdotH * NdotH;
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
denom = Math_PI * denom * denom;
return roughness4 / denom;
}
float radical_inverse_vdC(uint32_t bits) {
bits = (bits << 16) | (bits >> 16);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
return float(bits) * 2.3283064365386963e-10;
}
Vector2 hammersley(uint32_t i, uint32_t N) {
return Vector2(float(i) / float(N), radical_inverse_vdC(i));
}
void 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 * 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

View file

@ -0,0 +1,70 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef CUBEMAP_FILTER_GLES3_H
#define CUBEMAP_FILTER_GLES3_H
#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
#endif // CUBEMAP_FILTER_GLES3_H

View file

@ -0,0 +1,128 @@
/**************************************************************************/
/* feed_effects.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "feed_effects.h"
#ifdef ANDROID_ENABLED
#include <GLES3/gl3ext.h>
#endif
#define GL_PROGRAM_POINT_SIZE 0x8642
using namespace GLES3;
FeedEffects *FeedEffects::singleton = nullptr;
FeedEffects *FeedEffects::get_singleton() {
return singleton;
}
FeedEffects::FeedEffects() {
singleton = this;
feed.shader.initialize();
feed.shader_version = feed.shader.version_create();
feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT);
{ // Screen Triangle.
glGenBuffers(1, &screen_triangle);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
const float qv[6] = {
-1.0f,
-1.0f,
3.0f,
-1.0f,
-1.0f,
3.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
glGenVertexArrays(1, &screen_triangle_array);
glBindVertexArray(screen_triangle_array);
glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
}
FeedEffects::~FeedEffects() {
singleton = nullptr;
glDeleteBuffers(1, &screen_triangle);
glDeleteVertexArrays(1, &screen_triangle_array);
feed.shader.version_free(feed.shader_version);
}
Transform3D transform3D_from_mat4(const float *p_mat4) {
Transform3D res;
res.basis.rows[0][0] = p_mat4[0];
res.basis.rows[1][0] = p_mat4[1];
res.basis.rows[2][0] = p_mat4[2];
// p_mat4[3] = 0;
res.basis.rows[0][1] = p_mat4[4];
res.basis.rows[1][1] = p_mat4[5];
res.basis.rows[2][1] = p_mat4[6];
// p_mat4[7] = 0;
res.basis.rows[0][2] = p_mat4[8];
res.basis.rows[1][2] = p_mat4[9];
res.basis.rows[2][2] = p_mat4[10];
// p_mat4[11] = 0;
res.origin.x = p_mat4[12];
res.origin.y = p_mat4[13];
res.origin.z = p_mat4[14];
// p_mat4[15] = 1;
return res;
}
void FeedEffects::draw() {
bool success = feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT, FeedShaderGLES3::USE_EXTERNAL_SAMPLER);
if (!success) {
OS::get_singleton()->print("Godot : FeedShaderGLES3 Could not bind version_bind_shader USE_EXTERNAL_SAMPLER");
return;
}
draw_screen_triangle();
}
void FeedEffects::draw_screen_triangle() {
glBindVertexArray(screen_triangle_array);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,69 @@
/**************************************************************************/
/* feed_effects.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FEED_EFFECTS_GLES3_H
#define FEED_EFFECTS_GLES3_H
#ifdef GLES3_ENABLED
#include "drivers/gles3/shader_gles3.h"
#include "drivers/gles3/shaders/feed.glsl.gen.h"
namespace GLES3 {
class FeedEffects {
private:
struct Feed {
FeedShaderGLES3 shader;
RID shader_version;
} feed;
static FeedEffects *singleton;
GLuint screen_triangle = 0;
GLuint screen_triangle_array = 0;
public:
static FeedEffects *get_singleton();
FeedEffects();
~FeedEffects();
void draw();
private:
void draw_screen_triangle();
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // FEED_EFFECTS_GLES3_H

View file

@ -0,0 +1,173 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "glow.h"
#include "../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(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::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::GLOWLEVEL *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

View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef GLOW_GLES3_H
#define GLOW_GLES3_H
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/glow.glsl.gen.h"
namespace GLES3 {
class Glow {
private:
static Glow *singleton;
struct GLOW {
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 GLOWLEVEL {
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 GLOWLEVEL *p_glow_buffers, uint32_t p_view = 0, bool p_use_multiview = false);
};
} //namespace GLES3
#endif // GLES3_ENABLED
#endif // GLOW_GLES3_H

View file

@ -0,0 +1,153 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "post_effects.h"
#include "../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(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
glEnableVertexAttribArray(RS::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, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, 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_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_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::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);
}
// 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

View 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. */
/**************************************************************************/
#ifndef POST_EFFECTS_GLES3_H
#define POST_EFFECTS_GLES3_H
#ifdef GLES3_ENABLED
#include "drivers/gles3/shaders/effects/post.glsl.gen.h"
#include "glow.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, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false, uint64_t p_spec_constants = 0);
};
} //namespace GLES3
#endif // GLES3_ENABLED
#endif // POST_EFFECTS_GLES3_H

View 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")

View file

@ -0,0 +1,66 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "fog.h"
using namespace GLES3;
/* FOG */
RID Fog::fog_volume_allocate() {
return RID();
}
void Fog::fog_volume_initialize(RID p_rid) {
}
void Fog::fog_volume_free(RID p_rid) {
}
void Fog::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) {
}
void Fog::fog_volume_set_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();
}
RS::FogVolumeShape Fog::fog_volume_get_shape(RID p_fog_volume) const {
return RS::FOG_VOLUME_SHAPE_BOX;
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,59 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef FOG_GLES3_H
#define FOG_GLES3_H
#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, RS::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 RS::FogVolumeShape fog_volume_get_shape(RID p_fog_volume) const override;
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // FOG_GLES3_H

View file

@ -0,0 +1,143 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "gi.h"
using namespace GLES3;
/* VOXEL GI API */
RID GI::voxel_gi_allocate() {
return RID();
}
void GI::voxel_gi_free(RID p_rid) {
}
void GI::voxel_gi_initialize(RID p_rid) {
}
void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
}
AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const {
return AABB();
}
Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const {
return Vector3i();
}
Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const {
return Vector<uint8_t>();
}
Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const {
return Vector<int>();
}
Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
return Transform3D();
}
void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
return 0;
}
void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_propagation(RID p_voxel_gi) const {
return 0;
}
void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_energy(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) {
}
float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const {
return 1.0;
}
void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_bias(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) {
}
float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
return 0.0;
}
void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
}
bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
return false;
}
void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
}
bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
return false;
}
uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const {
return 0;
}
void GI::sdfgi_reset() {
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,91 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef GI_GLES3_H
#define GI_GLES3_H
#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
#endif // GI_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,392 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef RASTERIZER_CANVAS_GLES3_H
#define RASTERIZER_CANVAS_GLES3_H
#ifdef GLES3_ENABLED
#include "rasterizer_scene_gles3.h"
#include "servers/rendering/renderer_canvas_render.h"
#include "servers/rendering/renderer_compositor.h"
#include "storage/material_storage.h"
#include "storage/texture_storage.h"
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
#include "drivers/gles3/shaders/canvas_occlusion.glsl.gen.h"
class RasterizerSceneGLES3;
class RasterizerCanvasGLES3 : public RendererCanvasRender {
static RasterizerCanvasGLES3 *singleton;
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);
_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 {
RS::CanvasOccluderPolygonCullMode cull_mode = RS::CANVAS_OCCLUDER_POLYGON_CULL_DISABLED;
int line_point_count = 0;
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
GLuint index_buffer = 0;
int sdf_point_count = 0;
int sdf_index_count = 0;
GLuint sdf_vertex_buffer = 0;
GLuint sdf_vertex_array = 0;
GLuint sdf_index_buffer = 0;
bool sdf_is_lines = false;
};
RID_Owner<OccluderPolygon> occluder_polygon_owner;
void _update_shadow_atlas();
struct {
CanvasOcclusionShaderGLES3 shader;
RID shader_version;
} shadow_render;
struct LightUniform {
float matrix[8]; //light to texture coordinate matrix
float shadow_matrix[8]; //light to shadow coordinate matrix
float color[4];
uint8_t shadow_color[4];
uint32_t flags; //index to light texture
float shadow_pixel_size;
float height;
float position[2];
float shadow_z_far_inv;
float shadow_y_ofs;
float atlas_rect[4];
};
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>()) 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;
RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
Color blend_color = Color(1.0, 1.0, 1.0, 1.0);
Item *clip = nullptr;
RID material;
GLES3::CanvasMaterialData *material_data = nullptr;
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
uint64_t specialization = 0;
const Item::Command *command = nullptr;
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
uint32_t primitive_points = 0;
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;
RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
bool transparent_render_target = false;
double time = 0.0;
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
} state;
Item *items[MAX_RENDER_ITEMS];
RID default_canvas_texture;
RID default_canvas_group_material;
RID default_canvas_group_shader;
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, RS::CanvasOccluderPolygonCullMode p_mode) override;
void set_shadow_texture_size(int p_size) override;
bool free(RID p_rid) override;
void update() override;
void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat);
void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::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, RenderingMethod::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 *&current_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, RenderingMethod::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(RS::PipelineSource p_source) override { return 0; }
static RasterizerCanvasGLES3 *get_singleton();
RasterizerCanvasGLES3();
~RasterizerCanvasGLES3();
};
#endif // GLES3_ENABLED
#endif // RASTERIZER_CANVAS_GLES3_H

View file

@ -0,0 +1,526 @@
/**************************************************************************/
/* 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"
#include "storage/utilities.h"
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "storage/texture_storage.h"
#define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
#define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243
#define _EXT_DEBUG_CALLBACK_FUNCTION_ARB 0x8244
#define _EXT_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245
#define _EXT_DEBUG_SOURCE_API_ARB 0x8246
#define _EXT_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247
#define _EXT_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248
#define _EXT_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249
#define _EXT_DEBUG_SOURCE_APPLICATION_ARB 0x824A
#define _EXT_DEBUG_SOURCE_OTHER_ARB 0x824B
#define _EXT_DEBUG_TYPE_ERROR_ARB 0x824C
#define _EXT_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D
#define _EXT_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E
#define _EXT_DEBUG_TYPE_PORTABILITY_ARB 0x824F
#define _EXT_DEBUG_TYPE_PERFORMANCE_ARB 0x8250
#define _EXT_DEBUG_TYPE_OTHER_ARB 0x8251
#define _EXT_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143
#define _EXT_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144
#define _EXT_DEBUG_LOGGED_MESSAGES_ARB 0x9145
#define _EXT_DEBUG_SEVERITY_HIGH_ARB 0x9146
#define _EXT_DEBUG_SEVERITY_MEDIUM_ARB 0x9147
#define _EXT_DEBUG_SEVERITY_LOW_ARB 0x9148
#define _EXT_DEBUG_OUTPUT 0x92E0
#ifndef 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
bool RasterizerGLES3::gles_over_gl = true;
void RasterizerGLES3::begin_frame(double frame_step) {
frame++;
delta = frame_step;
time_total += frame_step;
double time_roll_over = GLOBAL_GET("rendering/limits/time/time_rollover_secs");
time_total = Math::fmod(time_total, time_roll_over);
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();
}
}
void RasterizerGLES3::clear_depth(float p_depth) {
#ifdef GL_API_ENABLED
if (is_gles_over_gl()) {
glClearDepth(p_depth);
}
#endif // GL_API_ENABLED
#ifdef GLES_API_ENABLED
if (!is_gles_over_gl()) {
glClearDepthf(p_depth);
}
#endif // GLES_API_ENABLED
}
#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) {
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()));
// FLIP XY Bug: Are more devices affected?
// Confirmed so far: all Adreno 3xx with old driver (until 2018)
// ok on some tested Adreno devices: 4xx, 5xx and 6xx
flip_xy_workaround = GLES3::Config::get_singleton()->flip_xy_workaround;
}
void RasterizerGLES3::finalize() {
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);
}
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 (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 (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 (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 (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 (!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());
// 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(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, bool p_first) {
GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_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;
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;
}
#ifdef WINDOWS_ENABLED
if (screen_flipped_y) {
flip_y = !flip_y;
}
#endif
GLuint read_fbo = 0;
glGenFramebuffers(1, &read_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo);
if (rt->view_count > 1) {
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer);
} else {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
if (p_first) {
if (p_screen_rect.position != Vector2() || p_screen_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);
}
}
Vector2i screen_rect_end = p_screen_rect.get_end();
// Adreno (TM) 3xx devices have a bug that create wrong Landscape rotation of 180 degree
// Reversing both the X and Y axis is equivalent to rotating 180 degrees
bool flip_x = false;
if (flip_xy_workaround && screen_rect_end.x > screen_rect_end.y) {
flip_y = !flip_y;
flip_x = !flip_x;
}
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
flip_x ? screen_rect_end.x : p_screen_rect.position.x, flip_y ? screen_rect_end.y : p_screen_rect.position.y,
flip_x ? p_screen_rect.position.x : screen_rect_end.x, flip_y ? p_screen_rect.position.y : screen_rect_end.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (read_fbo != 0) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
glDeleteFramebuffers(1, &read_fbo);
}
}
// is this p_screen useless in a multi window environment?
void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) {
for (int i = 0; i < p_amount; i++) {
const BlitToScreen &blit = p_render_targets[i];
RID rid_rt = blit.render_target;
Rect2 dst_rect = blit.dst_rect;
_blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0, i == 0);
}
}
void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) {
if (p_image.is_null() || p_image->is_empty()) {
return;
}
Size2i win_size = DisplayServer::get_singleton()->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 imgrect(0, 0, p_image->get_width(), p_image->get_height());
Rect2 screenrect;
if (p_scale) {
if (win_size.width > win_size.height) {
//scale horizontally
screenrect.size.y = win_size.height;
screenrect.size.x = imgrect.size.x * win_size.height / imgrect.size.y;
screenrect.position.x = (win_size.width - screenrect.size.x) / 2;
} else {
//scale vertically
screenrect.size.x = win_size.width;
screenrect.size.y = imgrect.size.y * win_size.width / imgrect.size.x;
screenrect.position.y = (win_size.height - screenrect.size.y) / 2;
}
} else {
screenrect = imgrect;
screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor();
}
#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 ? RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR : RS::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

View file

@ -0,0 +1,145 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef RASTERIZER_GLES3_H
#define RASTERIZER_GLES3_H
#ifdef GLES3_ENABLED
#include "effects/copy_effects.h"
#include "effects/cubemap_filter.h"
#include "effects/feed_effects.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
#include "environment/gi.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
#include "servers/rendering/renderer_compositor.h"
#include "storage/config.h"
#include "storage/light_storage.h"
#include "storage/material_storage.h"
#include "storage/mesh_storage.h"
#include "storage/particles_storage.h"
#include "storage/texture_storage.h"
#include "storage/utilities.h"
class RasterizerGLES3 : public RendererCompositor {
private:
uint64_t frame = 1;
float delta = 0;
double time_total = 0.0;
bool flip_xy_workaround = false;
#ifdef WINDOWS_ENABLED
static bool screen_flipped_y;
#endif
static bool gles_over_gl;
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(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, 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(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true);
void initialize();
void begin_frame(double frame_step);
void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount);
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 bool is_gles_over_gl() { return gles_over_gl; }
static void clear_depth(float p_depth);
static void make_current(bool p_gles_over_gl) {
gles_over_gl = p_gles_over_gl;
OS::get_singleton()->set_gles_over_gl(gles_over_gl);
_create_func = _create_current;
low_end = true;
}
#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
#endif // RASTERIZER_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,883 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef RASTERIZER_SCENE_GLES3_H
#define RASTERIZER_SCENE_GLES3_H
#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/shaders/effects/cubemap_filter.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
#include "scene/resources/mesh.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering_server.h"
#include "shader_gles3.h"
#include "storage/light_storage.h"
#include "storage/material_storage.h"
#include "storage/render_scene_buffers_gles3.h"
#include "storage/utilities.h"
enum RenderListType {
RENDER_LIST_OPAQUE, //used for opaque objects
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,
};
// 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.
};
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,
};
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;
bool cam_frustum = 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;
RenderingMethod::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;
RS::ViewportDebugDraw debug_draw = RS::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; // For use by SkyShaders
uint32_t bake_mode;
float shadow_opacity;
float specular;
};
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,
};
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;
RS::PrimitiveType primitive = RS::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;
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 pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
virtual void set_softshadow_projector_pairing(bool p_softshadow, bool p_projector) override {}
};
enum {
INSTANCE_DATA_FLAGS_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 pad2;
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;
float white = 1.0;
int32_t tonemapper = 0;
int32_t pad = 0;
int32_t pad2 = 0;
float brightness = 1.0;
float contrast = 1.0;
float saturation = 1.0;
};
static_assert(sizeof(TonemapUBO) % 16 == 0, "Tonemap UBO size must be a multiple of 16 bytes");
UBO ubo;
GLuint ubo_buffer = 0;
MultiviewUBO multiview_ubo;
GLuint multiview_buffer = 0;
GLuint tonemap_buffer = 0;
bool used_depth_prepass = false;
GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX;
RS::CullMode cull_mode = RS::CULL_MODE_BACK;
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 = RS::CULL_MODE_BACK;
glDepthMask(GL_FALSE);
current_depth_draw_enabled = false;
glDisable(GL_DEPTH_TEST);
current_depth_test_enabled = false;
}
void set_gl_cull_mode(RS::CullMode p_mode) {
if (cull_mode != p_mode) {
if (p_mode == RS::CULL_MODE_DISABLED) {
glDisable(GL_CULL_FACE);
} else {
if (cull_mode == RS::CULL_MODE_DISABLED) {
// Last time was disabled, so enable and set proper face.
glEnable(GL_CULL_FACE);
}
glCullFace(p_mode == RS::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;
}
}
bool texscreen_copied = false;
bool used_screen_texture = false;
bool used_normal_texture = false;
bool used_depth_texture = false;
LightData *omni_lights = nullptr;
LightData *spot_lights = nullptr;
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;
RS::ShadowQuality positional_shadow_quality = RS::ShadowQuality::SHADOW_QUALITY_SOFT_LOW;
DirectionalLightData *directional_lights = nullptr;
GLuint directional_light_buffer = 0;
DirectionalShadowData *directional_shadows = nullptr;
GLuint directional_shadow_buffer = 0;
RS::ShadowQuality directional_shadow_quality = RS::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 _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, RenderingMethod::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 */
RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM;
bool ssao_half_size = false;
float ssao_adaptive_target = 0.5;
int ssao_blur_passes = 2;
float ssao_fadeout_from = 50.0;
float ssao_fadeout_to = 300.0;
bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
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;
RS::SkyMode mode = RS::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;
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(RS::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, RS::SkyMode p_mode) override;
void sky_set_material(RID p_sky, RID p_material) override;
Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override;
float sky_get_baked_exposure(RID p_sky) const;
/* ENVIRONMENT API */
void environment_glow_set_use_bicubic_upscale(bool p_enable) override;
void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) override;
void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override;
void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override;
void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override;
void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override;
void environment_set_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(RS::ShadowQuality p_quality) override;
void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override;
RID fog_volume_instance_create(RID p_fog_volume) override;
void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override;
void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override;
RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override;
Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override;
RID 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(RS::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, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override;
void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override;
void set_scene_pass(uint64_t p_pass) override {
scene_pass = p_pass;
}
_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(RS::ViewportDebugDraw p_debug_draw) override;
_FORCE_INLINE_ RS::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(RS::SubSurfaceScatteringQuality p_quality) override;
void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) override;
TypedArray<Image> bake_render_uv2(RID p_base, const 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(RS::DecalFilter p_filter) override;
void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
RasterizerSceneGLES3();
~RasterizerSceneGLES3();
};
#endif // GLES3_ENABLED
#endif // RASTERIZER_SCENE_GLES3_H

View file

@ -0,0 +1,854 @@
/**************************************************************************/
/* 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 "drivers/gles3/rasterizer_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(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()).replace(":", "").strip_edges().to_upper();
} else {
text += l + "\n";
}
if (push_chunk) {
if (text != String()) {
StageTemplate::Chunk text_chunk;
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
text_chunk.text = text.utf8();
stage_templates[p_stage_type].chunks.push_back(text_chunk);
text = String();
}
stage_templates[p_stage_type].chunks.push_back(chunk);
}
if (text != String()) {
StageTemplate::Chunk text_chunk;
text_chunk.type = StageTemplate::Chunk::TYPE_TEXT;
text_chunk.text = text.utf8();
stage_templates[p_stage_type].chunks.push_back(text_chunk);
text = String();
}
}
}
void ShaderGLES3::_setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_name, int p_uniform_count, const char **p_uniform_names, int p_ubo_count, const UBOPair *p_ubos, int p_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("[SpirvCacheKey]");
tohash.append(RenderingDevice::get_singleton()->shader_get_spirv_cache_key());
tohash.append("[BinaryCacheKey]");
tohash.append(RenderingDevice::get_singleton()->shader_get_binary_cache_key());
*/
tohash.append("[Vertex]");
tohash.append(p_vertex_code ? p_vertex_code : "");
tohash.append("[Fragment]");
tohash.append(p_fragment_code ? p_fragment_code : "");
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 (RasterizerGLES3::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 (!RasterizerGLES3::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(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment)
} break;
case StageTemplate::Chunk::TYPE_VERTEX_GLOBALS: {
builder.append(p_version->vertex_globals.get_data()); // vertex globals
} break;
case StageTemplate::Chunk::TYPE_FRAGMENT_GLOBALS: {
builder.append(p_version->fragment_globals.get_data()); // fragment globals
} break;
case StageTemplate::Chunk::TYPE_CODE: {
if (p_version->code_sections.has(chunk.code)) {
builder.append(p_version->code_sections[chunk.code].get_data());
}
} break;
case StageTemplate::Chunk::TYPE_TEXT: {
builder.append(chunk.text.get_data());
} break;
}
}
}
static void _display_error_with_code(const String &p_error, const String &p_code) {
int line = 1;
Vector<String> lines = p_code.split("\n");
for (int j = 0; j < lines.size(); j++) {
print_line(itos(line) + ": " + lines[j]);
line++;
}
ERR_PRINT(p_error);
}
void ShaderGLES3::_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();
glShaderSource(spec.vert_id, 1, &cstr, nullptr);
glCompileShader(spec.vert_id);
glGetShaderiv(spec.vert_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLsizei iloglen;
glGetShaderiv(spec.vert_id, GL_INFO_LOG_LENGTH, &iloglen);
if (iloglen < 0) {
glDeleteShader(spec.vert_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_PRINT("No OpenGL vertex shader compiler log.");
} else {
if (iloglen == 0) {
iloglen = 4096; // buggy driver (Adreno 220+)
}
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
memset(ilogmem, 0, 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();
glShaderSource(spec.frag_id, 1, &cstr, nullptr);
glCompileShader(spec.frag_id);
glGetShaderiv(spec.frag_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
GLsizei iloglen;
glGetShaderiv(spec.frag_id, GL_INFO_LOG_LENGTH, &iloglen);
if (iloglen < 0) {
glDeleteShader(spec.frag_id);
glDeleteProgram(spec.id);
spec.id = 0;
ERR_PRINT("No OpenGL fragment shader compiler log.");
} else {
if (iloglen == 0) {
iloglen = 4096; // buggy driver (Adreno 220+)
}
char *ilogmem = (char *)Memory::alloc_static(iloglen + 1);
memset(ilogmem, 0, 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;
}
RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_version) {
Version *version = version_owner.get_or_null(p_version);
RS::ShaderNativeSourceCode source_code;
ERR_FAIL_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);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "vertex";
stage.code = builder.as_string();
source_code.versions.write[i].stages.push_back(stage);
}
//fragment stage
{
StringBuilder builder;
_build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask);
RS::ShaderNativeSourceCode::Version::Stage stage;
stage.name = "fragment";
stage.code = builder.as_string();
source_code.versions.write[i].stages.push_back(stage);
}
}
return source_code;
}
String ShaderGLES3::_version_get_sha1(Version *p_version) const {
StringBuilder hash_build;
hash_build.append("[uniforms]");
hash_build.append(p_version->uniforms.get_data());
hash_build.append("[vertex_globals]");
hash_build.append(p_version->vertex_globals.get_data());
hash_build.append("[fragment_globals]");
hash_build.append(p_version->fragment_globals.get_data());
Vector<StringName> code_sections;
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
code_sections.push_back(E.key);
}
code_sections.sort_custom<StringName::AlphCompare>();
for (int i = 0; i < code_sections.size(); i++) {
hash_build.append(String("[code:") + String(code_sections[i]) + "]");
hash_build.append(p_version->code_sections[code_sections[i]].get_data());
}
for (int i = 0; i < p_version->custom_defines.size(); i++) {
hash_build.append("[custom_defines:" + itos(i) + "]");
hash_build.append(p_version->custom_defines[i].get_data());
}
if (RasterizerGLES3::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 (RasterizerGLES3::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<OAHashMap<uint64_t, Version::Specialization>> variants;
for (int i = 0; i < cache_variant_count; i++) {
uint32_t cache_specialization_count = f->get_32();
OAHashMap<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(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 (RasterizerGLES3::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].get_num_elements();
f->store_32(cache_specialization_count);
for (OAHashMap<uint64_t, ShaderGLES3::Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) {
const uint64_t specialization_key = *it.key;
f->store_64(specialization_key);
const Version::Specialization *specialization = it.value;
if (specialization == nullptr) {
f->store_32(0);
continue;
}
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.size() == 0) {
return;
}
for (int i = 0; i < variant_count; i++) {
for (OAHashMap<uint64_t, Version::Specialization>::Iterator it = p_version->variants[i].iter(); it.valid; it = p_version->variants[i].next_iter(it)) {
if (it.value->id != 0) {
glDeleteShader(it.value->vert_id);
glDeleteShader(it.value->frag_id);
glDeleteProgram(it.value->id);
}
}
}
p_version->variants.clear();
}
void ShaderGLES3::_initialize_version(Version *p_version) {
ERR_FAIL_COND(p_version->variants.size() > 0);
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++) {
OAHashMap<uint64_t, Version::Specialization> variant;
p_version->variants.push_back(variant);
Version::Specialization spec;
_compile_specialization(spec, i, p_version, specialization_default_mask);
p_version->variants[i].insert(specialization_default_mask, spec);
}
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() {
List<RID> remaining;
version_owner.get_owned_list(&remaining);
if (remaining.size()) {
ERR_PRINT(itos(remaining.size()) + " shaders of type " + name + " were never freed");
while (remaining.size()) {
version_free(remaining.front()->get());
remaining.pop_front();
}
}
}
#endif

View file

@ -0,0 +1,262 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef SHADER_GLES3_H
#define SHADER_GLES3_H
#include "core/math/projection.h"
#include "core/os/mutex.h"
#include "core/string/string_builder.h"
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering_server.h"
#ifdef GLES3_ENABLED
#include "platform_gl.h"
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<OAHashMap<uint64_t, Specialization>> variants;
};
Mutex variant_set_mutex;
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.size() == 0) {
_initialize_version(version); //may lack initialization
}
Version::Specialization *spec = version->variants[p_variant].lookup_ptr(p_specialization);
if (!spec) {
if (false) {
// Queue load this specialization and use defaults in the meantime (TODO)
spec = version->variants[p_variant].lookup_ptr(specialization_default_mask);
} else {
// Compile on the spot
Version::Specialization s;
_compile_specialization(s, p_variant, version, p_specialization);
version->variants[p_variant].insert(p_specialization, s);
spec = version->variants[p_variant].lookup_ptr(p_specialization);
if (shader_cache_dir_valid) {
_save_to_cache(version);
}
}
} else if (spec->build_queued) {
// Still queued, wait
spec = version->variants[p_variant].lookup_ptr(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].lookup_ptr(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);
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
void initialize(const String &p_general_defines = "", int p_base_texture_index = 0);
virtual ~ShaderGLES3();
};
#endif // GLES3_ENABLED
#endif // SHADER_GLES3_H

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if "GLES3_GLSL" in env["BUILDERS"]:
# find all include files
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
# find all shader code(all glsl files excluding our include files)
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
# make sure we recompile shaders if include files change
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"])
# compile shaders
# as we have a few, not yet, converted files we name the ones we want to include:
env.GLES3_GLSL("canvas.glsl")
env.GLES3_GLSL("feed.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")
env.GLES3_GLSL("canvas_sdf.glsl")
env.GLES3_GLSL("particles.glsl")
env.GLES3_GLSL("particles_copy.glsl")
env.GLES3_GLSL("skeleton.glsl")
# once we finish conversion we can introduce this to cover all files:
# for glsl_file in glsl_files:
# env.GLES3_GLSL(glsl_file)
SConscript("effects/SCsub")

View file

@ -0,0 +1,863 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
DISABLE_LIGHTING = true
USE_RGBA_SHADOWS = false
USE_NINEPATCH = false
USE_PRIMITIVE = false
USE_ATTRIBUTES = false
USE_INSTANCING = false
#[vertex]
#ifdef USE_ATTRIBUTES
layout(location = 0) in vec2 vertex_attrib;
layout(location = 3) in vec4 color_attrib;
layout(location = 4) in vec2 uv_attrib;
#ifdef USE_INSTANCING
layout(location = 1) in highp vec4 instance_xform0;
layout(location = 2) in highp vec4 instance_xform1;
layout(location = 5) in highp uvec4 instance_color_custom_data; // Color packed into xy, custom_data packed into zw for compatibility with 3D
#endif // USE_INSTANCING
#endif // USE_ATTRIBUTES
#include "stdlib_inc.glsl"
#if defined(CUSTOM0_USED)
layout(location = 6) in highp vec4 custom0_attrib;
#endif
#if defined(CUSTOM1_USED)
layout(location = 7) in highp vec4 custom1_attrib;
#endif
layout(location = 8) in highp vec4 attrib_A;
layout(location = 9) in highp vec4 attrib_B;
layout(location = 10) in highp vec4 attrib_C;
layout(location = 11) in highp vec4 attrib_D;
layout(location = 12) in highp vec4 attrib_E;
#ifdef USE_PRIMITIVE
layout(location = 13) in highp uvec4 attrib_F;
#else
layout(location = 13) in highp vec4 attrib_F;
#endif
layout(location = 14) in highp uvec4 attrib_G;
layout(location = 15) in highp uvec4 attrib_H;
#define read_draw_data_world_x attrib_A.xy
#define read_draw_data_world_y attrib_A.zw
#define read_draw_data_world_ofs attrib_B.xy
#define read_draw_data_color_texture_pixel_size attrib_B.zw
#ifdef USE_PRIMITIVE
#define read_draw_data_point_a attrib_C.xy
#define read_draw_data_point_b attrib_C.zw
#define read_draw_data_point_c attrib_D.xy
#define read_draw_data_uv_a attrib_D.zw
#define read_draw_data_uv_b attrib_E.xy
#define read_draw_data_uv_c attrib_E.zw
#define read_draw_data_color_a_rg attrib_F.x
#define read_draw_data_color_a_ba attrib_F.y
#define read_draw_data_color_b_rg attrib_F.z
#define read_draw_data_color_b_ba attrib_F.w
#define read_draw_data_color_c_rg attrib_G.x
#define read_draw_data_color_c_ba attrib_G.y
#else
#define read_draw_data_modulation attrib_C
#define read_draw_data_ninepatch_margins attrib_D
#define read_draw_data_dst_rect attrib_E
#define read_draw_data_src_rect attrib_F
#endif
#define read_draw_data_flags attrib_G.z
#define read_draw_data_instance_offset attrib_G.w
#define read_draw_data_lights attrib_H
// Varyings so the per-instance info can be used in the fragment shader
flat out vec4 varying_A;
flat out vec2 varying_B;
#ifndef USE_PRIMITIVE
flat out vec4 varying_C;
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
flat out vec2 varying_D;
#endif
flat out vec4 varying_E;
#endif
#endif
flat out uvec2 varying_F;
flat out uvec4 varying_G;
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:4
#MATERIAL_UNIFORMS
};
#endif
uniform mediump uint batch_flags;
/* clang-format on */
#include "canvas_uniforms_inc.glsl"
out vec2 uv_interp;
out vec4 color_interp;
out vec2 vertex_interp;
#ifdef USE_NINEPATCH
out vec2 pixel_size_interp;
#endif
#GLOBALS
void main() {
varying_A = vec4(read_draw_data_world_x, read_draw_data_world_y);
varying_B = read_draw_data_color_texture_pixel_size;
#ifndef USE_PRIMITIVE
varying_C = read_draw_data_ninepatch_margins;
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
varying_D = vec2(read_draw_data_dst_rect.z, read_draw_data_dst_rect.w);
#endif // USE_NINEPATCH
varying_E = read_draw_data_src_rect;
#endif // !USE_ATTRIBUTES
#endif // USE_PRIMITIVE
varying_F = uvec2(read_draw_data_flags, read_draw_data_instance_offset);
varying_G = read_draw_data_lights;
vec4 instance_custom = vec4(0.0);
#if defined(CUSTOM0_USED)
vec4 custom0 = vec4(0.0);
#endif
#if defined(CUSTOM1_USED)
vec4 custom1 = vec4(0.0);
#endif
#ifdef USE_PRIMITIVE
vec2 vertex;
vec2 uv;
vec4 color;
if (gl_VertexID % 3 == 0) {
vertex = read_draw_data_point_a;
uv = read_draw_data_uv_a;
color.xy = unpackHalf2x16(read_draw_data_color_a_rg);
color.zw = unpackHalf2x16(read_draw_data_color_a_ba);
} else if (gl_VertexID % 3 == 1) {
vertex = read_draw_data_point_b;
uv = read_draw_data_uv_b;
color.xy = unpackHalf2x16(read_draw_data_color_b_rg);
color.zw = unpackHalf2x16(read_draw_data_color_b_ba);
} else {
vertex = read_draw_data_point_c;
uv = read_draw_data_uv_c;
color.xy = unpackHalf2x16(read_draw_data_color_c_rg);
color.zw = unpackHalf2x16(read_draw_data_color_c_ba);
}
#elif defined(USE_ATTRIBUTES)
vec2 vertex = vertex_attrib;
vec4 color = color_attrib * read_draw_data_modulation;
vec2 uv = uv_attrib;
#ifdef USE_INSTANCING
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_COLORS)) {
vec4 instance_color;
instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
color *= instance_color;
}
if (bool(batch_flags & BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
instance_custom.xy = unpackHalf2x16(instance_color_custom_data.z);
instance_custom.zw = unpackHalf2x16(instance_color_custom_data.w);
}
#endif // !USE_INSTANCING
#else // !USE_ATTRIBUTES
// crash on Adreno 320/330
//vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0));
//vec2 vertex_base = vertex_base_arr[gl_VertexID % 6];
//-----------------------------------------
// ID | 0 | 1 | 2 | 3 | 4 | 5 |
//-----------------------------------------
// X | 0.0 | 0.0 | 1.0 | 1.0 | 0.0 | 1.0 |
// Y | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 1.0 |
//-----------------------------------------
// no crash or freeze on all Adreno 3xx with 'if / else if' and slightly faster!
int vertex_id = gl_VertexID % 6;
vec2 vertex_base;
if (vertex_id == 0) {
vertex_base = vec2(0.0, 0.0);
} else if (vertex_id == 1) {
vertex_base = vec2(0.0, 1.0);
} else if (vertex_id == 2) {
vertex_base = vec2(1.0, 1.0);
} else if (vertex_id == 3) {
vertex_base = vec2(1.0, 0.0);
} else if (vertex_id == 4) {
vertex_base = vec2(0.0, 0.0);
} else if (vertex_id == 5) {
vertex_base = vec2(1.0, 1.0);
}
vec2 uv = read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) * ((read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = read_draw_data_modulation;
vec2 vertex = read_draw_data_dst_rect.xy + abs(read_draw_data_dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(read_draw_data_src_rect.zw, vec2(0.0, 0.0)));
#endif // USE_ATTRIBUTES
#if defined(CUSTOM0_USED)
custom0 = custom0_attrib;
#endif
#if defined(CUSTOM1_USED)
custom1 = custom1_attrib;
#endif
mat4 model_matrix = mat4(vec4(read_draw_data_world_x, 0.0, 0.0), vec4(read_draw_data_world_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(read_draw_data_world_ofs, 0.0, 1.0));
#ifdef USE_INSTANCING
model_matrix = model_matrix * transpose(mat4(instance_xform0, instance_xform1, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif // USE_INSTANCING
vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size;
#ifdef USE_POINT_SIZE
float point_size = 1.0;
#endif
#ifdef USE_WORLD_VERTEX_COORDS
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
{
#CODE : VERTEX
}
#ifdef USE_NINEPATCH
pixel_size_interp = abs(read_draw_data_dst_rect.zw) * vertex_base;
#endif
#if !defined(SKIP_TRANSFORM_USED) && !defined(USE_WORLD_VERTEX_COORDS)
vertex = (model_matrix * vec4(vertex, 0.0, 1.0)).xy;
#endif
color_interp = color;
vertex = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
if (use_pixel_snap) {
vertex = floor(vertex + 0.5);
// precision issue on some hardware creates artifacts within texture
// offset uv by a small amount to avoid
uv += 1e-5;
}
vertex_interp = vertex;
uv_interp = uv;
gl_Position = screen_transform * vec4(vertex, 0.0, 1.0);
#ifdef USE_POINT_SIZE
gl_PointSize = point_size;
#endif
}
#[fragment]
#include "canvas_uniforms_inc.glsl"
#include "stdlib_inc.glsl"
in vec2 uv_interp;
in vec2 vertex_interp;
in vec4 color_interp;
#ifdef USE_NINEPATCH
in vec2 pixel_size_interp;
#endif
// Can all be flat as they are the same for the whole batched instance
flat in vec4 varying_A;
flat in vec2 varying_B;
#define read_draw_data_world_x varying_A.xy
#define read_draw_data_world_y varying_A.zw
#define read_draw_data_color_texture_pixel_size varying_B
#ifndef USE_PRIMITIVE
flat in vec4 varying_C;
#define read_draw_data_ninepatch_margins varying_C
#ifndef USE_ATTRIBUTES
#ifdef USE_NINEPATCH
flat in vec2 varying_D;
#define read_draw_data_dst_rect_z varying_D.x
#define read_draw_data_dst_rect_w varying_D.y
#endif
flat in vec4 varying_E;
#define read_draw_data_src_rect varying_E
#endif // USE_ATTRIBUTES
#endif // USE_PRIMITIVE
flat in uvec2 varying_F;
flat in uvec4 varying_G;
#define read_draw_data_flags varying_F.x
#define read_draw_data_instance_offset varying_F.y
#define read_draw_data_lights varying_G
#ifndef DISABLE_LIGHTING
uniform sampler2D atlas_texture; //texunit:-2
uniform sampler2D shadow_atlas_texture; //texunit:-3
#endif // DISABLE_LIGHTING
uniform sampler2D color_buffer; //texunit:-4
uniform sampler2D sdf_texture; //texunit:-5
uniform sampler2D normal_texture; //texunit:-6
uniform sampler2D specular_texture; //texunit:-7
uniform sampler2D color_texture; //texunit:0
uniform mediump uint batch_flags;
uniform highp uint specular_shininess_in;
layout(location = 0) out vec4 frag_color;
/* clang-format off */
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:4
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#GLOBALS
float vec4_to_float(vec4 p_vec) {
return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
}
vec2 screen_uv_to_sdf(vec2 p_uv) {
return screen_to_sdf * p_uv;
}
float texture_sdf(vec2 p_sdf) {
vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
float d = vec4_to_float(texture(sdf_texture, uv));
d *= SDF_MAX_LENGTH;
return d * tex_to_sdf;
}
vec2 texture_sdf_normal(vec2 p_sdf) {
vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw;
const float EPSILON = 0.001;
return normalize(vec2(
vec4_to_float(texture(sdf_texture, uv + vec2(EPSILON, 0.0))) - vec4_to_float(texture(sdf_texture, uv - vec2(EPSILON, 0.0))),
vec4_to_float(texture(sdf_texture, uv + vec2(0.0, EPSILON))) - vec4_to_float(texture(sdf_texture, uv - vec2(0.0, EPSILON)))));
}
vec2 sdf_to_screen_uv(vec2 p_sdf) {
return p_sdf * sdf_to_screen;
}
#ifndef DISABLE_LIGHTING
#ifdef LIGHT_CODE_USED
vec4 light_compute(
vec3 light_vertex,
vec3 light_position,
vec3 normal,
vec4 light_color,
float light_energy,
vec4 specular_shininess,
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
vec3 light_direction = vec3(0.0);
if (is_directional) {
light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z));
light_position = vec3(0.0);
} else {
light_direction = normalize(light_position - light_vertex);
}
#CODE : LIGHT
return light;
}
#endif
vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
float cNdotL = max(0.0, dot(normal, light_vec));
if (specular_shininess_used) {
//blinn
vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
vec3 half_vec = normalize(view + light_vec);
float cNdotV = max(dot(normal, view), 0.0);
float cNdotH = max(dot(normal, half_vec), 0.0);
float cVdotH = max(dot(view, half_vec), 0.0);
float cLdotH = max(dot(light_vec, half_vec), 0.0);
float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
float blinn = pow(cNdotH, shininess);
blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
} else {
return light_color * base_color * cNdotL;
}
}
#ifdef USE_RGBA_SHADOWS
#define SHADOW_DEPTH(m_uv) (dot(textureLod(shadow_atlas_texture, (m_uv), 0.0), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0)
#else
#define SHADOW_DEPTH(m_uv) (textureLod(shadow_atlas_texture, (m_uv), 0.0).r)
#endif
/* clang-format off */
#define SHADOW_TEST(m_uv) { highp float sd = SHADOW_DEPTH(m_uv); shadow += step(sd, shadow_uv.z / shadow_uv.w); }
/* clang-format on */
//float distance = length(shadow_pos);
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
#ifdef LIGHT_CODE_USED
,
vec3 shadow_modulate
#endif
) {
float shadow = 0.0;
uint shadow_mode = light_array[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
SHADOW_TEST(shadow_uv.xy);
} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
shadow /= 5.0;
} else { //PCF13
vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 6.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 5.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 4.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 3.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy - shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 3.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 4.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 5.0);
SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 6.0);
shadow /= 13.0;
}
vec4 shadow_color = godot_unpackUnorm4x8(light_array[light_base].shadow_color);
#ifdef LIGHT_CODE_USED
shadow_color.rgb *= shadow_modulate;
#endif
shadow_color.a *= light_color.a; //respect light alpha
return mix(light_color, shadow_color, shadow);
}
void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
if (blend_mode == LIGHT_FLAGS_BLEND_MODE_ADD) {
color.rgb += light_color.rgb * light_color.a;
} else if (blend_mode == LIGHT_FLAGS_BLEND_MODE_SUB) {
color.rgb -= light_color.rgb * light_color.a;
} else if (blend_mode == LIGHT_FLAGS_BLEND_MODE_MIX) {
color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
}
}
#endif
#ifdef USE_NINEPATCH
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
float tex_size = 1.0 / tex_pixel_size;
if (pixel < margin_begin) {
return pixel * tex_pixel_size;
} else if (pixel >= draw_size - margin_end) {
return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else {
if (!bool(read_draw_data_flags & INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER)) {
draw_center--;
}
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
if (np_repeat == 0) { // Stretch.
// Convert to ratio.
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
} else if (np_repeat == 1) { // Tile.
// Convert to offset.
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
// Scale to source texture.
return (margin_begin + ofs) * tex_pixel_size;
} else if (np_repeat == 2) { // Tile Fit.
// Calculate scale.
float src_area = draw_size - margin_begin - margin_end;
float dst_area = tex_size - margin_begin - margin_end;
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
// Convert to ratio.
float ratio = (pixel - margin_begin) / src_area;
ratio = mod(ratio * scale, 1.0);
// Scale to source texture.
return (margin_begin + ratio * dst_area) * tex_pixel_size;
} else { // Shouldn't happen, but silences compiler warning.
return 0.0;
}
}
}
#endif
float msdf_median(float r, float g, float b, float a) {
return min(max(min(r, g), min(max(r, g), b)), a);
}
void main() {
vec4 color = color_interp;
vec2 uv = uv_interp;
vec2 vertex = vertex_interp;
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
#ifdef USE_NINEPATCH
int draw_center = 2;
uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(read_draw_data_dst_rect_z), read_draw_data_color_texture_pixel_size.x, read_draw_data_ninepatch_margins.x, read_draw_data_ninepatch_margins.z, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
if (draw_center == 0) {
color.a = 0.0;
}
uv = uv * read_draw_data_src_rect.zw + read_draw_data_src_rect.xy; //apply region if needed
#endif
if (bool(read_draw_data_flags & INSTANCE_FLAGS_CLIP_RECT_UV)) {
vec2 half_texpixel = read_draw_data_color_texture_pixel_size * 0.5;
uv = clamp(uv, read_draw_data_src_rect.xy + half_texpixel, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw) - half_texpixel);
}
#endif
#ifndef USE_PRIMITIVE
if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_MSDF)) {
float px_range = read_draw_data_ninepatch_margins.x;
float outline_thickness = read_draw_data_ninepatch_margins.y;
vec4 msdf_sample = texture(color_texture, uv);
vec2 msdf_size = vec2(textureSize(color_texture, 0));
vec2 dest_size = vec2(1.0) / fwidth(uv);
float px_size = max(0.5 * dot((vec2(px_range) / msdf_size), dest_size), 1.0);
float d = msdf_median(msdf_sample.r, msdf_sample.g, msdf_sample.b, msdf_sample.a) - 0.5;
if (outline_thickness > 0.0) {
float cr = clamp(outline_thickness, 0.0, px_range / 2.0) / px_range;
float a = clamp((d + cr) * px_size, 0.0, 1.0);
color.a = a * color.a;
} else {
float a = clamp(d * px_size + 0.5, 0.0, 1.0);
color.a = a * color.a;
}
} else if (bool(read_draw_data_flags & INSTANCE_FLAGS_USE_LCD)) {
vec4 lcd_sample = texture(color_texture, uv);
if (lcd_sample.a == 1.0) {
color.rgb = lcd_sample.rgb * color.a;
} else {
color = vec4(0.0, 0.0, 0.0, 0.0);
}
} else {
#else
{
#endif
color *= texture(color_texture, uv);
}
uint light_count = read_draw_data_flags & uint(0xF); // Max 16 lights.
bool using_light = light_count > 0u || directional_light_count > 0u;
vec3 normal;
#if defined(NORMAL_USED)
bool normal_used = true;
#else
bool normal_used = false;
#endif
if (normal_used || (using_light && bool(batch_flags & BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED))) {
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
if (bool(read_draw_data_flags & INSTANCE_FLAGS_TRANSPOSE_RECT)) {
normal.xy = normal.yx;
}
normal.xy *= sign(read_draw_data_src_rect.zw);
#endif
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
normal_used = true;
} else {
normal = vec3(0.0, 0.0, 1.0);
}
vec4 specular_shininess;
#if defined(SPECULAR_SHININESS_USED)
bool specular_shininess_used = true;
#else
bool specular_shininess_used = false;
#endif
if (specular_shininess_used || (using_light && normal_used && bool(batch_flags & BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(specular_texture, uv);
specular_shininess *= godot_unpackUnorm4x8(specular_shininess_in);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);
}
#if defined(SCREEN_UV_USED)
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
#else
vec2 screen_uv = vec2(0.0);
#endif
vec2 color_texture_pixel_size = read_draw_data_color_texture_pixel_size.xy;
vec3 light_vertex = vec3(vertex, 0.0);
vec2 shadow_vertex = vertex;
{
float normal_map_depth = 1.0;
#if defined(NORMAL_MAP_USED)
vec3 normal_map = vec3(0.0, 0.0, 1.0);
normal_used = true;
#endif
#CODE : FRAGMENT
#if defined(NORMAL_MAP_USED)
normal = mix(vec3(0.0, 0.0, 1.0), normal_map * vec3(2.0, -2.0, 1.0) - vec3(1.0, -1.0, 0.0), normal_map_depth);
#endif
}
if (normal_used) {
//convert by item transform
normal.xy = mat2(normalize(read_draw_data_world_x), normalize(read_draw_data_world_y)) * normal.xy;
//convert by canvas transform
normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz);
}
vec4 base_color = color;
#ifdef MODE_LIGHT_ONLY
float light_only_alpha = 0.0;
#elif !defined(MODE_UNSHADED)
color *= canvas_modulation;
#endif
#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED)
// Directional Lights
for (uint i = 0u; i < directional_light_count; i++) {
uint light_base = i;
vec2 direction = light_array[light_base].position;
vec4 light_color = light_array[light_base].color;
#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
light_color = light_compute(light_vertex, vec3(direction, light_array[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true);
#else
if (normal_used) {
vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array[light_base].height));
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
} else {
light_color.rgb *= base_color.rgb;
}
#endif
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
light_only_alpha += light_color.a;
#endif
}
// Positional Lights
for (uint i = 0u; i < MAX_LIGHTS_PER_ITEM; i++) {
if (i >= light_count) {
break;
}
uint light_base;
if (i < 8u) {
if (i < 4u) {
light_base = read_draw_data_lights[0];
} else {
light_base = read_draw_data_lights[1];
}
} else {
if (i < 12u) {
light_base = read_draw_data_lights[2];
} else {
light_base = read_draw_data_lights[3];
}
}
light_base >>= (i & 3u) * 8u;
light_base &= uint(0xFF);
vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy;
if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
//if outside the light texture, light color is zero
continue;
}
vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0);
vec4 light_base_color = light_array[light_base].color;
#ifdef LIGHT_CODE_USED
vec4 shadow_modulate = vec4(1.0);
vec3 light_position = vec3(light_array[light_base].position, light_array[light_base].height);
light_color.rgb *= light_base_color.rgb;
light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false);
#else
light_color.rgb *= light_base_color.rgb * light_base_color.a;
if (normal_used) {
vec3 light_pos = vec3(light_array[light_base].position, light_array[light_base].height);
vec3 pos = light_vertex;
vec3 light_vec = normalize(light_pos - pos);
light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used);
} else {
light_color.rgb *= base_color.rgb;
}
#endif
if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW) && bool(read_draw_data_flags & uint(INSTANCE_FLAGS_SHADOW_MASKED << i))) {
vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
vec2 pos_norm = normalize(shadow_pos);
vec2 pos_abs = abs(pos_norm);
vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y);
vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot?
float tex_ofs;
float dist;
if (pos_rot.y > 0.0) {
if (pos_rot.x > 0.0) {
tex_ofs = pos_box.y * 0.125 + 0.125;
dist = shadow_pos.x;
} else {
tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125);
dist = shadow_pos.y;
}
} else {
if (pos_rot.x < 0.0) {
tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125);
dist = -shadow_pos.x;
} else {
tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125);
dist = -shadow_pos.y;
}
}
dist *= light_array[light_base].shadow_zfar_inv;
//float distance = length(shadow_pos);
vec4 shadow_uv = vec4(tex_ofs, light_array[light_base].shadow_y_ofs, dist, 1.0);
light_color = light_shadow_compute(light_base, light_color, shadow_uv
#ifdef LIGHT_CODE_USED
,
shadow_modulate.rgb
#endif
);
}
light_blend_compute(light_base, light_color, color.rgb);
#ifdef MODE_LIGHT_ONLY
light_only_alpha += light_color.a;
#endif
}
#endif
#ifdef MODE_LIGHT_ONLY
color.a *= light_only_alpha;
#endif
frag_color = color;
}

View file

@ -0,0 +1,68 @@
/* clang-format off */
#[modes]
mode_sdf =
mode_shadow = #define MODE_SHADOW
mode_shadow_RGBA = #define MODE_SHADOW \n#define USE_RGBA_SHADOWS
#[specializations]
#[vertex]
layout(location = 0) in vec3 vertex;
uniform highp mat4 projection;
uniform highp vec4 modelview1;
uniform highp vec4 modelview2;
uniform highp vec2 direction;
uniform highp float z_far;
#ifdef MODE_SHADOW
out float depth;
#endif
void main() {
highp vec4 vtx = vec4(vertex, 1.0) * mat4(modelview1, modelview2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
#ifdef MODE_SHADOW
depth = dot(direction, vtx.xy);
#endif
gl_Position = projection * vtx;
}
#[fragment]
uniform highp mat4 projection;
uniform highp vec4 modelview1;
uniform highp vec4 modelview2;
uniform highp vec2 direction;
uniform highp float z_far;
#ifdef MODE_SHADOW
in highp float depth;
#endif
#ifdef USE_RGBA_SHADOWS
layout(location = 0) out lowp vec4 out_buf;
#else
layout(location = 0) out highp float out_buf;
#endif
void main() {
float out_depth = 1.0;
#ifdef MODE_SHADOW
out_depth = depth / z_far;
#endif
#ifdef USE_RGBA_SHADOWS
out_depth = clamp(out_depth, -1.0, 1.0);
out_depth = out_depth * 0.5 + 0.5;
highp vec4 comp = fract(out_depth * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
out_buf = comp;
#else
out_buf = out_depth;
#endif
}

View file

@ -0,0 +1,205 @@
/* clang-format off */
#[modes]
mode_load = #define MODE_LOAD
mode_load_shrink = #define MODE_LOAD_SHRINK
mode_process = #define MODE_PROCESS
mode_store = #define MODE_STORE
mode_store_shrink = #define MODE_STORE_SHRINK
#[specializations]
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
void main() {
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
#define SDF_MAX_LENGTH 16384.0
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK)
uniform lowp sampler2D src_pixels;//texunit:0
#else
uniform highp isampler2D src_process;//texunit:0
#endif
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) || defined(MODE_PROCESS)
layout(location = 0) out ivec4 distance_field;
#else
layout(location = 0) out vec4 distance_field;
#endif
vec4 float_to_vec4(float p_float) {
highp vec4 comp = fract(p_float * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
return comp;
}
void main() {
ivec2 pos = ivec2(gl_FragCoord.xy);
#ifdef MODE_LOAD
bool solid = texelFetch(src_pixels, pos, 0).r > 0.5;
distance_field = solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0);
#endif
#ifdef MODE_LOAD_SHRINK
int s = 1 << shift;
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = ivec2(32767);
float d = 1e20;
int found = 0;
int solid_found = 0;
for (int i = 0; i < s; i++) {
for (int j = 0; j < s; j++) {
ivec2 src_pos = base + ivec2(i, j);
if (any(greaterThanEqual(src_pos, base_size))) {
continue;
}
bool solid = texelFetch(src_pixels, src_pos, 0).r > 0.5;
if (solid) {
float dist = length(vec2(src_pos - center));
if (dist < d) {
d = dist;
rel = src_pos;
}
solid_found++;
}
found++;
}
}
if (solid_found == found) {
//mark solid only if all are solid
rel = ivec2(-32767);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_PROCESS
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
if (center != rel) {
//only process if it does not point to itself
const int ofs_table_size = 8;
const ivec2 ofs_table[ofs_table_size] = ivec2[](
ivec2(-1, -1),
ivec2(0, -1),
ivec2(+1, -1),
ivec2(-1, 0),
ivec2(+1, 0),
ivec2(-1, +1),
ivec2(0, +1),
ivec2(+1, +1));
float dist = length(vec2(rel - center));
for (int i = 0; i < ofs_table_size; i++) {
ivec2 src_pos = pos + ofs_table[i] * stride;
if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, size))) {
continue;
}
ivec2 src_rel = texelFetch(src_process, src_pos, 0).xy;
bool src_solid = src_rel.x < 0;
if (src_solid) {
src_rel = -src_rel - ivec2(1);
}
if (src_solid != solid) {
src_rel = ivec2(src_pos << shift); //point to itself if of different type
}
float src_dist = length(vec2(src_rel - center));
if (src_dist < dist) {
dist = src_dist;
rel = src_rel;
}
}
}
if (solid) {
rel = -rel - ivec2(1);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_STORE
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - pos));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
#ifdef MODE_STORE_SHRINK
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - center));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
}

View file

@ -0,0 +1,87 @@
#define MAX_LIGHTS_PER_ITEM uint(16)
#define M_PI 3.14159265359
#define SDF_MAX_LENGTH 16384.0
#define INSTANCE_FLAGS_LIGHT_COUNT_SHIFT 0 // 4 bits.
#define INSTANCE_FLAGS_CLIP_RECT_UV uint(1 << 4)
#define INSTANCE_FLAGS_TRANSPOSE_RECT uint(1 << 5)
#define INSTANCE_FLAGS_USE_MSDF uint(1 << 6)
#define INSTANCE_FLAGS_USE_LCD uint(1 << 7)
#define INSTANCE_FLAGS_NINEPATCH_DRAW_CENTER uint(1 << 8)
#define INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT 9
#define INSTANCE_FLAGS_NINEPATCH_V_MODE_SHIFT 11
#define INSTANCE_FLAGS_SHADOW_MASKED_SHIFT 13u // 16 bits.
#define INSTANCE_FLAGS_SHADOW_MASKED uint(1 << INSTANCE_FLAGS_SHADOW_MASKED_SHIFT)
// 1 means enabled, 2+ means trails in use
#define BATCH_FLAGS_INSTANCING_MASK uint(0x7F)
#define BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT 7
#define BATCH_FLAGS_INSTANCING_HAS_COLORS uint(1 << BATCH_FLAGS_INSTANCING_HAS_COLORS_SHIFT)
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT 8
#define BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << BATCH_FLAGS_INSTANCING_HAS_CUSTOM_DATA_SHIFT)
#define BATCH_FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 9)
#define BATCH_FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 10)
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
layout(std140) uniform CanvasData { //ubo:0
mat4 canvas_transform;
mat4 screen_transform;
mat4 canvas_normal_transform;
vec4 canvas_modulation;
vec2 screen_pixel_size;
float time;
bool use_pixel_snap;
vec4 sdf_to_tex;
vec2 screen_to_sdf;
vec2 sdf_to_screen;
uint directional_light_count;
float tex_to_sdf;
uint pad1;
uint pad2;
};
#ifndef DISABLE_LIGHTING
#define LIGHT_FLAGS_BLEND_MASK uint(3 << 16)
#define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16)
#define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MIX uint(2 << 16)
#define LIGHT_FLAGS_BLEND_MODE_MASK uint(3 << 16)
#define LIGHT_FLAGS_HAS_SHADOW uint(1 << 20)
#define LIGHT_FLAGS_FILTER_SHIFT 22
#define LIGHT_FLAGS_FILTER_MASK uint(3 << 22)
#define LIGHT_FLAGS_SHADOW_NEAREST uint(0 << 22)
#define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22)
#define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22)
struct Light {
mat2x4 texture_matrix; //light to texture coordinate matrix (transposed)
mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed)
vec4 color;
uint shadow_color; // packed
uint flags; //index to light texture
float shadow_pixel_size;
float height;
vec2 position;
float shadow_zfar_inv;
float shadow_y_ofs;
vec4 atlas_rect;
};
layout(std140) uniform LightData { //ubo:2
Light light_array[MAX_LIGHTS];
};
#endif // DISABLE_LIGHTING

View file

@ -0,0 +1,100 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision mediump float;
precision mediump int;
#endif
layout(location = 0) in highp vec4 vertex_attrib;
/* clang-format on */
layout(location = 4) in vec2 uv_in;
out vec2 uv_interp;
void main() {
uv_interp = uv_in;
gl_Position = vertex_attrib;
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
uniform highp samplerCube source_cube; //texunit:0
/* clang-format on */
in vec2 uv_interp;
uniform bool z_flip;
uniform highp float z_far;
uniform highp float z_near;
uniform highp float bias;
void main() {
highp vec3 normal = vec3(uv_interp * 2.0 - 1.0, 0.0);
/*
if (z_flip) {
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
} else {
normal.z = -0.5 + 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
}
*/
//normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
//normal.xy *= 1.0 + normal.z;
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
normal = normalize(normal);
/*
normal.z = 0.5;
normal = normalize(normal);
*/
if (!z_flip) {
normal.z = -normal.z;
}
//normal = normalize(vec3( uv_interp * 2.0 - 1.0, 1.0 ));
float depth = textureCube(source_cube, normal).r;
// absolute values for direction cosines, bigger value equals closer to basis axis
vec3 unorm = abs(normal);
if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
// x code
unorm = normal.x > 0.0 ? vec3(1.0, 0.0, 0.0) : vec3(-1.0, 0.0, 0.0);
} else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
// y code
unorm = normal.y > 0.0 ? vec3(0.0, 1.0, 0.0) : vec3(0.0, -1.0, 0.0);
} else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
// z code
unorm = normal.z > 0.0 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 0.0, -1.0);
} else {
// oh-no we messed up code
// has to be
unorm = vec3(1.0, 0.0, 0.0);
}
float depth_fix = 1.0 / dot(normal, unorm);
depth = 2.0 * depth - 1.0;
float linear_depth = 2.0 * z_near * z_far / (z_far + z_near + depth * (z_far - z_near));
gl_FragDepth = (z_far - (linear_depth * depth_fix + bias)) / z_far;
}

View file

@ -0,0 +1,291 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision highp float;
precision highp int;
#endif
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
layout(location = 4) in vec2 uv_in;
out vec2 uv_interp;
#ifdef USE_BLUR_SECTION
uniform vec4 blur_section;
#endif
void main() {
uv_interp = uv_in;
gl_Position = vec4(vertex_attrib, 0.0, 1.0);
#ifdef USE_BLUR_SECTION
uv_interp = blur_section.xy + uv_interp * blur_section.zw;
gl_Position.xy = (blur_section.xy + (gl_Position.xy * 0.5 + 0.5) * blur_section.zw) * 2.0 - 1.0;
#endif
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
in vec2 uv_interp;
/* clang-format on */
uniform sampler2D source_color; //texunit:0
uniform float lod;
uniform vec2 pixel_size;
#if defined(GLOW_GAUSSIAN_HORIZONTAL) || defined(GLOW_GAUSSIAN_VERTICAL)
uniform float glow_strength;
#endif
#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
#ifdef USE_GLES_OVER_GL
#ifdef DOF_QUALITY_LOW
const int dof_kernel_size = 5;
const int dof_kernel_from = 2;
const float dof_kernel[5] = float[](0.153388, 0.221461, 0.250301, 0.221461, 0.153388);
#endif
#ifdef DOF_QUALITY_MEDIUM
const int dof_kernel_size = 11;
const int dof_kernel_from = 5;
const float dof_kernel[11] = float[](0.055037, 0.072806, 0.090506, 0.105726, 0.116061, 0.119726, 0.116061, 0.105726, 0.090506, 0.072806, 0.055037);
#endif
#ifdef DOF_QUALITY_HIGH
const int dof_kernel_size = 21;
const int dof_kernel_from = 10;
const float dof_kernel[21] = float[](0.028174, 0.032676, 0.037311, 0.041944, 0.046421, 0.050582, 0.054261, 0.057307, 0.059587, 0.060998, 0.061476, 0.060998, 0.059587, 0.057307, 0.054261, 0.050582, 0.046421, 0.041944, 0.037311, 0.032676, 0.028174);
#endif
#endif
uniform sampler2D dof_source_depth; //texunit:1
uniform float dof_begin;
uniform float dof_end;
uniform vec2 dof_dir;
uniform float dof_radius;
#endif
#ifdef GLOW_FIRST_PASS
uniform highp float luminance_cap;
uniform float glow_bloom;
uniform float glow_hdr_threshold;
uniform float glow_hdr_scale;
#endif
uniform float camera_z_far;
uniform float camera_z_near;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef GLOW_GAUSSIAN_HORIZONTAL
vec2 pix_size = pixel_size;
pix_size *= 0.5; //reading from larger buffer, so use more samples
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pix_size, lod) * 0.174938;
color += textureLod(source_color, uv_interp + vec2(1.0, 0.0) * pix_size, lod) * 0.165569;
color += textureLod(source_color, uv_interp + vec2(2.0, 0.0) * pix_size, lod) * 0.140367;
color += textureLod(source_color, uv_interp + vec2(3.0, 0.0) * pix_size, lod) * 0.106595;
color += textureLod(source_color, uv_interp + vec2(-1.0, 0.0) * pix_size, lod) * 0.165569;
color += textureLod(source_color, uv_interp + vec2(-2.0, 0.0) * pix_size, lod) * 0.140367;
color += textureLod(source_color, uv_interp + vec2(-3.0, 0.0) * pix_size, lod) * 0.106595;
color *= glow_strength;
frag_color = color;
#endif
#ifdef GLOW_GAUSSIAN_VERTICAL
vec4 color = textureLod(source_color, uv_interp + vec2(0.0, 0.0) * pixel_size, lod) * 0.288713;
color += textureLod(source_color, uv_interp + vec2(0.0, 1.0) * pixel_size, lod) * 0.233062;
color += textureLod(source_color, uv_interp + vec2(0.0, 2.0) * pixel_size, lod) * 0.122581;
color += textureLod(source_color, uv_interp + vec2(0.0, -1.0) * pixel_size, lod) * 0.233062;
color += textureLod(source_color, uv_interp + vec2(0.0, -2.0) * pixel_size, lod) * 0.122581;
color *= glow_strength;
frag_color = color;
#endif
#ifndef USE_GLES_OVER_GL
#if defined(DOF_FAR_BLUR) || defined(DOF_NEAR_BLUR)
#ifdef DOF_QUALITY_LOW
const int dof_kernel_size = 5;
const int dof_kernel_from = 2;
float dof_kernel[5];
dof_kernel[0] = 0.153388;
dof_kernel[1] = 0.221461;
dof_kernel[2] = 0.250301;
dof_kernel[3] = 0.221461;
dof_kernel[4] = 0.153388;
#endif
#ifdef DOF_QUALITY_MEDIUM
const int dof_kernel_size = 11;
const int dof_kernel_from = 5;
float dof_kernel[11];
dof_kernel[0] = 0.055037;
dof_kernel[1] = 0.072806;
dof_kernel[2] = 0.090506;
dof_kernel[3] = 0.105726;
dof_kernel[4] = 0.116061;
dof_kernel[5] = 0.119726;
dof_kernel[6] = 0.116061;
dof_kernel[7] = 0.105726;
dof_kernel[8] = 0.090506;
dof_kernel[9] = 0.072806;
dof_kernel[10] = 0.055037;
#endif
#ifdef DOF_QUALITY_HIGH
const int dof_kernel_size = 21;
const int dof_kernel_from = 10;
float dof_kernel[21];
dof_kernel[0] = 0.028174;
dof_kernel[1] = 0.032676;
dof_kernel[2] = 0.037311;
dof_kernel[3] = 0.041944;
dof_kernel[4] = 0.046421;
dof_kernel[5] = 0.050582;
dof_kernel[6] = 0.054261;
dof_kernel[7] = 0.057307;
dof_kernel[8] = 0.059587;
dof_kernel[9] = 0.060998;
dof_kernel[10] = 0.061476;
dof_kernel[11] = 0.060998;
dof_kernel[12] = 0.059587;
dof_kernel[13] = 0.057307;
dof_kernel[14] = 0.054261;
dof_kernel[15] = 0.050582;
dof_kernel[16] = 0.046421;
dof_kernel[17] = 0.041944;
dof_kernel[18] = 0.037311;
dof_kernel[19] = 0.032676;
dof_kernel[20] = 0.028174;
#endif
#endif
#endif //!USE_GLES_OVER_GL
#ifdef DOF_FAR_BLUR
vec4 color_accum = vec4(0.0);
float depth = textureLod(dof_source_depth, uv_interp, 0.0).r;
depth = depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
depth = ((depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near));
#endif
float amount = smoothstep(dof_begin, dof_end, depth);
float k_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * amount * dof_radius;
float tap_k = dof_kernel[i];
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
#endif
float tap_amount = int_ofs == 0 ? 1.0 : smoothstep(dof_begin, dof_end, tap_depth);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
vec4 tap_color = textureLod(source_color, tap_uv, 0.0) * tap_k;
k_accum += tap_k * tap_amount;
color_accum += tap_color * tap_amount;
}
if (k_accum > 0.0) {
color_accum /= k_accum;
}
frag_color = color_accum; ///k_accum;
#endif
#ifdef DOF_NEAR_BLUR
vec4 color_accum = vec4(0.0);
float max_accum = 0.0;
for (int i = 0; i < dof_kernel_size; i++) {
int int_ofs = i - dof_kernel_from;
vec2 tap_uv = uv_interp + dof_dir * float(int_ofs) * dof_radius;
float ofs_influence = max(0.0, 1.0 - abs(float(int_ofs)) / float(dof_kernel_from));
float tap_k = dof_kernel[i];
vec4 tap_color = textureLod(source_color, tap_uv, 0.0);
float tap_depth = texture(dof_source_depth, tap_uv, 0.0).r;
tap_depth = tap_depth * 2.0 - 1.0;
#ifdef USE_ORTHOGONAL_PROJECTION
tap_depth = ((tap_depth + (camera_z_far + camera_z_near) / (camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near)) / 2.0;
#else
tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near));
#endif
float tap_amount = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
tap_amount *= tap_amount * tap_amount; //prevent undesired glow effect
#ifdef DOF_NEAR_FIRST_TAP
tap_color.a = 1.0 - smoothstep(dof_end, dof_begin, tap_depth);
#endif
max_accum = max(max_accum, tap_amount * ofs_influence);
color_accum += tap_color * tap_k;
}
color_accum.a = max(color_accum.a, sqrt(max_accum));
frag_color = color_accum;
#endif
#ifdef GLOW_FIRST_PASS
float luminance = max(frag_color.r, max(frag_color.g, frag_color.b));
float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, luminance), glow_bloom);
frag_color = min(frag_color * feedback, vec4(luminance_cap));
#endif
}

View file

@ -0,0 +1,18 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
if "GLES3_GLSL" in env["BUILDERS"]:
# find all include files
gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
# find all shader code(all glsl files excluding our include files)
glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
# make sure we recompile shaders if include files change
env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"])
# compile shaders
for glsl_file in glsl_files:
env.GLES3_GLSL(glsl_file)

View file

@ -0,0 +1,176 @@
/* clang-format off */
#[modes]
mode_default = #define MODE_SIMPLE_COPY
mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY
mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM
mode_copy_section_3d = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_3D
mode_copy_section_2d_array = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY
mode_screen = #define MODE_SIMPLE_COPY \n#define MODE_MULTIPLY
mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR
mode_mipmap = #define MODE_MIPMAP
mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION
mode_cube_to_octahedral = #define CUBE_TO_OCTAHEDRAL \n#define USE_COPY_SECTION
mode_cube_to_panorama = #define CUBE_TO_PANORAMA
#[specializations]
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* clang-format on */
#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
// Defined in 0-1 coords.
uniform highp vec4 copy_section;
#endif
#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM)
uniform highp vec4 source_section;
#endif
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
#if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR)
gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0;
#endif
#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM)
uv_interp = source_section.xy + uv_interp * source_section.zw;
#endif
}
/* clang-format off */
#[fragment]
in vec2 uv_interp;
/* clang-format on */
#if defined(USE_TEXTURE_3D) || defined(USE_TEXTURE_2D_ARRAY)
uniform float layer;
uniform float lod;
#endif
#ifdef MODE_SIMPLE_COLOR
uniform vec4 color_in;
#endif
#ifdef MODE_MULTIPLY
uniform float multiply;
#endif
#ifdef MODE_GAUSSIAN_BLUR
// Defined in 0-1 coords.
uniform highp vec2 pixel_size;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
#endif
#ifdef CUBE_TO_PANORAMA
uniform lowp float mip_level;
#endif
#if defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA)
uniform samplerCube source_cube; // texunit:0
#else // ~(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA))
#if defined(USE_TEXTURE_3D)
uniform sampler3D source_3d; // texunit:0
#elif defined(USE_TEXTURE_2D_ARRAY)
uniform sampler2DArray source_2d_array; // texunit:0
#else
uniform sampler2D source; // texunit:0
#endif
#endif // !(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA))
layout(location = 0) out vec4 frag_color;
// This expects 0-1 range input, outside that range it behaves poorly.
vec3 srgb_to_linear(vec3 color) {
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
void main() {
#ifdef MODE_SIMPLE_COPY
#ifdef USE_TEXTURE_3D
vec4 color = textureLod(source_3d, vec3(uv_interp, layer), lod);
#elif defined(USE_TEXTURE_2D_ARRAY)
vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), lod);
#else
vec4 color = texture(source, uv_interp);
#endif // USE_TEXTURE_3D
#ifdef MODE_MULTIPLY
color *= multiply;
#endif // MODE_MULTIPLY
frag_color = color;
#endif // MODE_SIMPLE_COPY
#ifdef MODE_SIMPLE_COLOR
frag_color = color_in;
#endif
// Efficient box filter from Jimenez: http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
// Approximates a Gaussian in a single pass.
#ifdef MODE_GAUSSIAN_BLUR
vec4 A = textureLod(source, uv_interp + pixel_size * vec2(-1.0, -1.0), 0.0);
vec4 B = textureLod(source, uv_interp + pixel_size * vec2(0.0, -1.0), 0.0);
vec4 C = textureLod(source, uv_interp + pixel_size * vec2(1.0, -1.0), 0.0);
vec4 D = textureLod(source, uv_interp + pixel_size * vec2(-0.5, -0.5), 0.0);
vec4 E = textureLod(source, uv_interp + pixel_size * vec2(0.5, -0.5), 0.0);
vec4 F = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 0.0), 0.0);
vec4 G = textureLod(source, uv_interp, 0.0);
vec4 H = textureLod(source, uv_interp + pixel_size * vec2(1.0, 0.0), 0.0);
vec4 I = textureLod(source, uv_interp + pixel_size * vec2(-0.5, 0.5), 0.0);
vec4 J = textureLod(source, uv_interp + pixel_size * vec2(0.5, 0.5), 0.0);
vec4 K = textureLod(source, uv_interp + pixel_size * vec2(-1.0, 1.0), 0.0);
vec4 L = textureLod(source, uv_interp + pixel_size * vec2(0.0, 1.0), 0.0);
vec4 M = textureLod(source, uv_interp + pixel_size * vec2(1.0, 1.0), 0.0);
float weight = 0.5 / 4.0;
float lesser_weight = 0.125 / 4.0;
frag_color = (D + E + I + J) * weight;
frag_color += (A + B + G + F) * lesser_weight;
frag_color += (B + C + H + G) * lesser_weight;
frag_color += (F + G + L + K) * lesser_weight;
frag_color += (G + H + M + L) * lesser_weight;
#endif
#ifdef CUBE_TO_OCTAHEDRAL
// Treat the UV coordinates as 0-1 encoded octahedral coordinates.
vec3 dir = oct_to_vec3(uv_interp * 2.0 - 1.0);
frag_color = texture(source_cube, dir);
#endif
#ifdef CUBE_TO_PANORAMA
const float PI = 3.14159265359;
float phi = uv_interp.x * 2.0 * PI;
float theta = uv_interp.y * PI;
vec3 normal;
normal.x = sin(phi) * sin(theta) * -1.0;
normal.y = cos(theta);
normal.z = cos(phi) * sin(theta) * -1.0;
vec3 color = srgb_to_linear(textureLod(source_cube, normal, mip_level).rgb);
frag_color = vec4(color, 1.0);
#endif
}

View file

@ -0,0 +1,122 @@
/* clang-format off */
#[modes]
mode_default =
mode_copy = #define MODE_DIRECT_WRITE
#[specializations]
#[vertex]
layout(location = 0) in highp vec2 vertex_attrib;
/* clang-format on */
out highp vec2 uv_interp;
void main() {
uv_interp = vertex_attrib;
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
/* clang-format off */
#[fragment]
#define M_PI 3.14159265359
uniform samplerCube source_cube; //texunit:0
/* clang-format on */
uniform int face_id;
#ifndef MODE_DIRECT_WRITE
uniform uint sample_count;
uniform vec4 sample_directions_mip[MAX_SAMPLE_COUNT];
uniform float weight;
#endif
in highp vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
#define M_PI 3.14159265359
// Don't include tonemap_inc.glsl because all we want is these functions, we don't want the uniforms
vec3 linear_to_srgb(vec3 color) {
return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
vec3 srgb_to_linear(vec3 color) {
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
vec3 texelCoordToVec(vec2 uv, int faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
void main() {
vec3 color = vec3(0.0);
vec2 uv = uv_interp;
vec3 N = texelCoordToVec(uv, face_id);
#ifdef MODE_DIRECT_WRITE
frag_color = vec4(textureLod(source_cube, N, 0.0).rgb, 1.0);
#else
vec4 sum = vec4(0.0);
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
mat3 T;
T[0] = normalize(cross(UpVector, N));
T[1] = cross(N, T[0]);
T[2] = N;
for (uint sample_num = 0u; sample_num < sample_count; sample_num++) {
vec4 sample_direction_mip = sample_directions_mip[sample_num];
vec3 L = T * sample_direction_mip.xyz;
vec3 val = textureLod(source_cube, L, sample_direction_mip.w).rgb;
// Mix using linear
val = srgb_to_linear(val);
sum.rgb += val * sample_direction_mip.z;
}
sum /= weight;
sum.rgb = linear_to_srgb(sum.rgb);
frag_color = vec4(sum.rgb, 1.0);
#endif
}

View file

@ -0,0 +1,113 @@
/* clang-format off */
#[modes]
// Based on Dual filtering glow as explained in Marius Bjørge presentation at Siggraph 2015 "Bandwidth-Efficient Rendering"
mode_filter = #define MODE_FILTER
mode_downsample = #define MODE_DOWNSAMPLE
mode_upsample = #define MODE_UPSAMPLE
#[specializations]
USE_MULTIVIEW = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
/* clang-format on */
#ifdef MODE_FILTER
#ifdef USE_MULTIVIEW
uniform sampler2DArray source_color; // texunit:0
#else
uniform sampler2D source_color; // texunit:0
#endif // USE_MULTIVIEW
uniform float view;
uniform vec2 pixel_size;
uniform float luminance_multiplier;
uniform float glow_bloom;
uniform float glow_hdr_threshold;
uniform float glow_hdr_scale;
uniform float glow_luminance_cap;
#endif // MODE_FILTER
#ifdef MODE_DOWNSAMPLE
uniform sampler2D source_color; // texunit:0
uniform vec2 pixel_size;
#endif // MODE_DOWNSAMPLE
#ifdef MODE_UPSAMPLE
uniform sampler2D source_color; // texunit:0
uniform vec2 pixel_size;
#endif // MODE_UPSAMPLE
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef MODE_FILTER
// Note, we read from an image with double resolution, so we average those out
#ifdef USE_MULTIVIEW
vec2 half_pixel = pixel_size * 0.5;
vec3 uv = vec3(uv_interp, view);
vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0;
color += textureLod(source_color, uv - vec3(half_pixel, 0.0), 0.0).rgb;
color += textureLod(source_color, uv + vec3(half_pixel, 0.0), 0.0).rgb;
color += textureLod(source_color, uv - vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb;
color += textureLod(source_color, uv + vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb;
#else
vec2 half_pixel = pixel_size * 0.5;
vec2 uv = uv_interp;
vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0;
color += textureLod(source_color, uv - half_pixel, 0.0).rgb;
color += textureLod(source_color, uv + half_pixel, 0.0).rgb;
color += textureLod(source_color, uv - vec2(half_pixel.x, -half_pixel.y), 0.0).rgb;
color += textureLod(source_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0).rgb;
#endif // USE_MULTIVIEW
color /= luminance_multiplier * 8.0;
float feedback_factor = max(color.r, max(color.g, color.b));
float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, feedback_factor), glow_bloom);
color = min(color * feedback, vec3(glow_luminance_cap));
frag_color = vec4(luminance_multiplier * color, 1.0);
#endif // MODE_FILTER
#ifdef MODE_DOWNSAMPLE
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(source_color, uv_interp, 0.0) * 4.0;
color += textureLod(source_color, uv_interp - half_pixel, 0.0);
color += textureLod(source_color, uv_interp + half_pixel, 0.0);
color += textureLod(source_color, uv_interp - vec2(half_pixel.x, -half_pixel.y), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0);
frag_color = color / 8.0;
#endif // MODE_DOWNSAMPLE
#ifdef MODE_UPSAMPLE
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(source_color, uv_interp + vec2(-half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(0.0, half_pixel.y * 2.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0;
color += textureLod(source_color, uv_interp + vec2(0.0, -half_pixel.y * 2.0), 0.0);
color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0;
frag_color = color / 12.0;
#endif // MODE_UPSAMPLE
}

View file

@ -0,0 +1,130 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
USE_MULTIVIEW = false
USE_GLOW = false
USE_LUMINANCE_MULTIPLIER = false
USE_BCS = false
USE_COLOR_CORRECTION = false
USE_1D_LUT = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
/* clang-format on */
// If we reach this code, we always tonemap.
#define APPLY_TONEMAPPING
#include "../tonemap_inc.glsl"
#ifdef USE_MULTIVIEW
uniform sampler2DArray source_color; // texunit:0
#else
uniform sampler2D source_color; // texunit:0
#endif // USE_MULTIVIEW
uniform float view;
uniform float luminance_multiplier;
#ifdef USE_GLOW
uniform sampler2D glow_color; // texunit:1
uniform vec2 pixel_size;
uniform float glow_intensity;
vec4 get_glow_color(vec2 uv) {
vec2 half_pixel = pixel_size * 0.5;
vec4 color = textureLod(glow_color, uv + vec2(-half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(glow_color, uv + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(0.0, half_pixel.y * 2.0), 0.0);
color += textureLod(glow_color, uv + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(half_pixel.x * 2.0, 0.0), 0.0);
color += textureLod(glow_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0;
color += textureLod(glow_color, uv + vec2(0.0, -half_pixel.y * 2.0), 0.0);
color += textureLod(glow_color, uv + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0;
return color / 12.0;
}
#endif // USE_GLOW
#ifdef USE_COLOR_CORRECTION
#ifdef USE_1D_LUT
uniform sampler2D source_color_correction; //texunit:2
vec3 apply_color_correction(vec3 color) {
color.r = texture(source_color_correction, vec2(color.r, 0.0f)).r;
color.g = texture(source_color_correction, vec2(color.g, 0.0f)).g;
color.b = texture(source_color_correction, vec2(color.b, 0.0f)).b;
return color;
}
#else
uniform sampler3D source_color_correction; //texunit:2
vec3 apply_color_correction(vec3 color) {
return textureLod(source_color_correction, color, 0.0).rgb;
}
#endif // USE_1D_LUT
#endif // USE_COLOR_CORRECTION
#ifdef USE_BCS
vec3 apply_bcs(vec3 color) {
color = mix(vec3(0.0), color, brightness);
color = mix(vec3(0.5), color, contrast);
color = mix(vec3(dot(vec3(1.0), color) * 0.33333), color, saturation);
return color;
}
#endif
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
#ifdef USE_MULTIVIEW
vec4 color = texture(source_color, vec3(uv_interp, view));
#else
vec4 color = texture(source_color, uv_interp);
#endif
#ifdef USE_GLOW
vec4 glow = get_glow_color(uv_interp) * glow_intensity;
// Just use softlight...
glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f));
color.rgb = max((color.rgb + glow.rgb) - (color.rgb * glow.rgb), vec3(0.0));
#endif // USE_GLOW
#ifdef USE_LUMINANCE_MULTIPLIER
color = color / luminance_multiplier;
#endif
color.rgb = srgb_to_linear(color.rgb);
color.rgb = apply_tonemapping(color.rgb, white);
color.rgb = linear_to_srgb(color.rgb);
#ifdef USE_BCS
color.rgb = apply_bcs(color.rgb);
#endif
#ifdef USE_COLOR_CORRECTION
color.rgb = apply_color_correction(color.rgb);
#endif
frag_color = color;
}

View file

@ -0,0 +1,39 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
USE_EXTERNAL_SAMPLER = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
void main() {
uv_interp = vertex_attrib * 0.5 + 0.5;
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
layout(location = 0) out vec4 frag_color;
in vec2 uv_interp;
/* clang-format on */
#ifdef USE_EXTERNAL_SAMPLER
uniform samplerExternalOES sourceFeed; // texunit:0
#else
uniform sampler2D sourceFeed; // texunit:0
#endif
void main() {
vec4 color = texture(sourceFeed, uv_interp);
frag_color = color;
}

View file

@ -0,0 +1,86 @@
/* clang-format off */
[vertex]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
precision highp float;
precision highp int;
#endif
layout(location = 0) in highp vec2 vertex;
/* clang-format on */
uniform vec2 offset;
uniform vec2 scale;
out vec2 uv_interp;
void main() {
uv_interp = vertex.xy * 2.0 - 1.0;
vec2 v = vertex.xy * scale + offset;
gl_Position = vec4(v, 0.0, 1.0);
}
/* clang-format off */
[fragment]
#ifdef USE_GLES_OVER_GL
#define lowp
#define mediump
#define highp
#else
#if defined(USE_HIGHP_PRECISION)
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
#endif
uniform sampler2D source; //texunit:0
/* clang-format on */
uniform vec2 eye_center;
uniform float k1;
uniform float k2;
uniform float upscale;
uniform float aspect_ratio;
in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
void main() {
vec2 coords = uv_interp;
vec2 offset = coords - eye_center;
// take aspect ratio into account
offset.y /= aspect_ratio;
// distort
vec2 offset_sq = offset * offset;
float radius_sq = offset_sq.x + offset_sq.y;
float radius_s4 = radius_sq * radius_sq;
float distortion_scale = 1.0 + (k1 * radius_sq) + (k2 * radius_s4);
offset *= distortion_scale;
// reapply aspect ratio
offset.y *= aspect_ratio;
// add our eye center back in
coords = offset + eye_center;
coords /= upscale;
// and check our color
if (coords.x < -1.0 || coords.y < -1.0 || coords.x > 1.0 || coords.y > 1.0) {
frag_color = vec4(0.0, 0.0, 0.0, 1.0);
} else {
coords = (coords + vec2(1.0)) / vec2(2.0);
frag_color = texture(source, coords);
}
}

View file

@ -0,0 +1,512 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
MODE_3D = false
USERDATA1_USED = false
USERDATA2_USED = false
USERDATA3_USED = false
USERDATA4_USED = false
USERDATA5_USED = false
USERDATA6_USED = false
#[vertex]
#define SDF_MAX_LENGTH 16384.0
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
// This needs to be outside clang-format so the ubo comment is in the right place
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:2
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#define MAX_ATTRACTORS 32
#define ATTRACTOR_TYPE_SPHERE uint(0)
#define ATTRACTOR_TYPE_BOX uint(1)
#define ATTRACTOR_TYPE_VECTOR_FIELD uint(2)
struct Attractor {
mat4 transform;
vec4 extents; // Extents or radius. w-channel is padding.
uint type;
float strength;
float attenuation;
float directionality;
};
#define MAX_COLLIDERS 32
#define COLLIDER_TYPE_SPHERE uint(0)
#define COLLIDER_TYPE_BOX uint(1)
#define COLLIDER_TYPE_SDF uint(2)
#define COLLIDER_TYPE_HEIGHT_FIELD uint(3)
#define COLLIDER_TYPE_2D_SDF uint(4)
struct Collider {
mat4 transform;
vec4 extents; // Extents or radius. w-channel is padding.
uint type;
float scale;
float pad0;
float pad1;
};
layout(std140) uniform FrameData { //ubo:0
bool emitting;
uint cycle;
float system_phase;
float prev_system_phase;
float explosiveness;
float randomness;
float time;
float delta;
float particle_size;
float amount_ratio;
float pad1;
float pad2;
uint random_seed;
uint attractor_count;
uint collider_count;
uint frame;
mat4 emission_transform;
vec3 emitter_velocity;
float interp_to_end;
Attractor attractors[MAX_ATTRACTORS];
Collider colliders[MAX_COLLIDERS];
};
#define PARTICLE_FLAG_ACTIVE uint(1)
#define PARTICLE_FLAG_STARTED uint(2)
#define PARTICLE_FLAG_TRAILED uint(4)
#define PARTICLE_FRAME_MASK uint(0xFFFF)
#define PARTICLE_FRAME_SHIFT uint(16)
// ParticleData
layout(location = 0) in highp vec4 color;
layout(location = 1) in highp vec4 velocity_flags;
layout(location = 2) in highp vec4 custom;
layout(location = 3) in highp vec4 xform_1;
layout(location = 4) in highp vec4 xform_2;
#ifdef MODE_3D
layout(location = 5) in highp vec4 xform_3;
#endif
#ifdef USERDATA1_USED
in highp vec4 userdata1;
#endif
#ifdef USERDATA2_USED
in highp vec4 userdata2;
#endif
#ifdef USERDATA3_USED
in highp vec4 userdata3;
#endif
#ifdef USERDATA4_USED
in highp vec4 userdata4;
#endif
#ifdef USERDATA5_USED
in highp vec4 userdata5;
#endif
#ifdef USERDATA6_USED
in highp vec4 userdata6;
#endif
out highp vec4 out_color; //tfb:
out highp vec4 out_velocity_flags; //tfb:
out highp vec4 out_custom; //tfb:
out highp vec4 out_xform_1; //tfb:
out highp vec4 out_xform_2; //tfb:
#ifdef MODE_3D
out highp vec4 out_xform_3; //tfb:MODE_3D
#endif
#ifdef USERDATA1_USED
out highp vec4 out_userdata1; //tfb:USERDATA1_USED
#endif
#ifdef USERDATA2_USED
out highp vec4 out_userdata2; //tfb:USERDATA2_USED
#endif
#ifdef USERDATA3_USED
out highp vec4 out_userdata3; //tfb:USERDATA3_USED
#endif
#ifdef USERDATA4_USED
out highp vec4 out_userdata4; //tfb:USERDATA4_USED
#endif
#ifdef USERDATA5_USED
out highp vec4 out_userdata5; //tfb:USERDATA5_USED
#endif
#ifdef USERDATA6_USED
out highp vec4 out_userdata6; //tfb:USERDATA6_USED
#endif
uniform sampler2D height_field_texture; //texunit:0
uniform float lifetime;
uniform bool clear;
uniform uint total_particles;
uniform bool use_fractional_delta;
uint hash(uint x) {
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = ((x >> uint(16)) ^ x) * uint(0x45d9f3b);
x = (x >> uint(16)) ^ x;
return x;
}
vec3 safe_normalize(vec3 direction) {
const float EPSILON = 0.001;
if (length(direction) < EPSILON) {
return vec3(0.0);
}
return normalize(direction);
}
// Needed whenever 2D sdf texture is read from as it is packed in RGBA8.
float vec4_to_float(vec4 p_vec) {
return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0;
}
#GLOBALS
void main() {
bool apply_forces = true;
bool apply_velocity = true;
float local_delta = delta;
float mass = 1.0;
bool restart = false;
bool restart_position = false;
bool restart_rotation_scale = false;
bool restart_velocity = false;
bool restart_color = false;
bool restart_custom = false;
mat4 xform = mat4(1.0);
uint flags = 0u;
if (clear) {
out_color = vec4(1.0);
out_custom = vec4(0.0);
out_velocity_flags = vec4(0.0);
} else {
out_color = color;
out_velocity_flags = velocity_flags;
out_custom = custom;
xform[0] = xform_1;
xform[1] = xform_2;
#ifdef MODE_3D
xform[2] = xform_3;
#endif
xform = transpose(xform);
flags = floatBitsToUint(velocity_flags.w);
#ifdef USERDATA1_USED
out_userdata1 = userdata1;
#endif
#ifdef USERDATA2_USED
out_userdata2 = userdata2;
#endif
#ifdef USERDATA3_USED
out_userdata3 = userdata3;
#endif
#ifdef USERDATA4_USED
out_userdata4 = userdata4;
#endif
#ifdef USERDATA5_USED
out_userdata5 = userdata5;
#endif
#ifdef USERDATA6_USED
out_userdata6 = userdata6;
#endif
}
//clear started flag if set
flags &= ~PARTICLE_FLAG_STARTED;
bool collided = false;
vec3 collision_normal = vec3(0.0);
float collision_depth = 0.0;
vec3 attractor_force = vec3(0.0);
#if !defined(DISABLE_VELOCITY)
if (bool(flags & PARTICLE_FLAG_ACTIVE)) {
xform[3].xyz += out_velocity_flags.xyz * local_delta;
}
#endif
uint index = uint(gl_VertexID);
if (emitting) {
float restart_phase = float(index) / float(total_particles);
if (randomness > 0.0) {
uint seed = cycle;
if (restart_phase >= system_phase) {
seed -= uint(1);
}
seed *= uint(total_particles);
seed += index;
float random = float(hash(seed) % uint(65536)) / 65536.0;
restart_phase += randomness * random * 1.0 / float(total_particles);
}
restart_phase *= (1.0 - explosiveness);
if (system_phase > prev_system_phase) {
// restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
if (restart_phase >= prev_system_phase && restart_phase < system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (system_phase - restart_phase) * lifetime;
}
}
} else if (delta > 0.0) {
if (restart_phase >= prev_system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
}
} else if (restart_phase < system_phase) {
restart = true;
if (use_fractional_delta) {
local_delta = (system_phase - restart_phase) * lifetime;
}
}
}
if (restart) {
flags = emitting ? (PARTICLE_FLAG_ACTIVE | PARTICLE_FLAG_STARTED | (cycle << PARTICLE_FRAME_SHIFT)) : 0u;
restart_position = true;
restart_rotation_scale = true;
restart_velocity = true;
restart_color = true;
restart_custom = true;
}
}
bool particle_active = bool(flags & PARTICLE_FLAG_ACTIVE);
uint particle_number = (flags >> PARTICLE_FRAME_SHIFT) * uint(total_particles) + index;
if (restart && particle_active) {
#CODE : START
}
if (particle_active) {
for (uint i = 0u; i < attractor_count; i++) {
vec3 dir;
float amount;
vec3 rel_vec = xform[3].xyz - attractors[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(attractors[i].transform);
if (attractors[i].type == ATTRACTOR_TYPE_SPHERE) {
dir = safe_normalize(rel_vec);
float d = length(local_pos) / attractors[i].extents.x;
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} else if (attractors[i].type == ATTRACTOR_TYPE_BOX) {
dir = safe_normalize(rel_vec);
vec3 abs_pos = abs(local_pos / attractors[i].extents.xyz);
float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
if (d > 1.0) {
continue;
}
amount = max(0.0, 1.0 - d);
} else if (attractors[i].type == ATTRACTOR_TYPE_VECTOR_FIELD) {
}
mediump float attractor_attenuation = attractors[i].attenuation;
amount = pow(amount, attractor_attenuation);
dir = safe_normalize(mix(dir, attractors[i].transform[2].xyz, attractors[i].directionality));
attractor_force -= mass * amount * dir * attractors[i].strength;
}
float particle_size = particle_size;
#ifdef USE_COLLISION_SCALE
particle_size *= dot(vec3(length(xform[0].xyz), length(xform[1].xyz), length(xform[2].xyz)), vec3(0.33333333333));
#endif
if (collider_count == 1u && colliders[0].type == COLLIDER_TYPE_2D_SDF) {
//2D collision
vec2 pos = xform[3].xy;
vec4 to_sdf_x = colliders[0].transform[0];
vec4 to_sdf_y = colliders[0].transform[1];
vec2 sdf_pos = vec2(dot(vec4(pos, 0, 1), to_sdf_x), dot(vec4(pos, 0, 1), to_sdf_y));
vec4 sdf_to_screen = vec4(colliders[0].extents.xyz, colliders[0].scale);
vec2 uv_pos = sdf_pos * sdf_to_screen.xy + sdf_to_screen.zw;
if (all(greaterThan(uv_pos, vec2(0.0))) && all(lessThan(uv_pos, vec2(1.0)))) {
vec2 pos2 = pos + vec2(0, particle_size);
vec2 sdf_pos2 = vec2(dot(vec4(pos2, 0, 1), to_sdf_x), dot(vec4(pos2, 0, 1), to_sdf_y));
float sdf_particle_size = distance(sdf_pos, sdf_pos2);
float d = vec4_to_float(texture(height_field_texture, uv_pos)) * SDF_MAX_LENGTH;
// Allowing for a small epsilon to allow particle just touching colliders to count as collided
const float EPSILON = 0.001;
d -= sdf_particle_size;
if (d < EPSILON) {
vec2 n = normalize(vec2(
vec4_to_float(texture(height_field_texture, uv_pos + vec2(EPSILON, 0.0))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(EPSILON, 0.0))),
vec4_to_float(texture(height_field_texture, uv_pos + vec2(0.0, EPSILON))) - vec4_to_float(texture(height_field_texture, uv_pos - vec2(0.0, EPSILON)))));
collided = true;
sdf_pos2 = sdf_pos + n * d;
pos2 = vec2(dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[2]), dot(vec4(sdf_pos2, 0, 1), colliders[0].transform[3]));
n = pos - pos2;
collision_normal = normalize(vec3(n, 0.0));
collision_depth = length(n);
}
}
} else {
for (uint i = 0u; i < collider_count; i++) {
vec3 normal;
float depth;
bool col = false;
vec3 rel_vec = xform[3].xyz - colliders[i].transform[3].xyz;
vec3 local_pos = rel_vec * mat3(colliders[i].transform);
// Allowing for a small epsilon to allow particle just touching colliders to count as collided
const float EPSILON = 0.001;
if (colliders[i].type == COLLIDER_TYPE_SPHERE) {
float d = length(rel_vec) - (particle_size + colliders[i].extents.x);
if (d < EPSILON) {
col = true;
depth = -d;
normal = normalize(rel_vec);
}
} else if (colliders[i].type == COLLIDER_TYPE_BOX) {
vec3 abs_pos = abs(local_pos);
vec3 sgn_pos = sign(local_pos);
if (any(greaterThan(abs_pos, colliders[i].extents.xyz))) {
//point outside box
vec3 closest = min(abs_pos, colliders[i].extents.xyz);
vec3 rel = abs_pos - closest;
depth = length(rel) - particle_size;
if (depth < EPSILON) {
col = true;
normal = mat3(colliders[i].transform) * (normalize(rel) * sgn_pos);
depth = -depth;
}
} else {
//point inside box
vec3 axis_len = colliders[i].extents.xyz - abs_pos;
// there has to be a faster way to do this?
if (all(lessThan(axis_len.xx, axis_len.yz))) {
normal = vec3(1, 0, 0);
} else if (all(lessThan(axis_len.yy, axis_len.xz))) {
normal = vec3(0, 1, 0);
} else {
normal = vec3(0, 0, 1);
}
col = true;
depth = dot(normal * axis_len, vec3(1)) + particle_size;
normal = mat3(colliders[i].transform) * (normal * sgn_pos);
}
} else if (colliders[i].type == COLLIDER_TYPE_SDF) {
} else if (colliders[i].type == COLLIDER_TYPE_HEIGHT_FIELD) {
vec3 local_pos_bottom = local_pos;
local_pos_bottom.y -= particle_size;
if (any(greaterThan(abs(local_pos_bottom), colliders[i].extents.xyz))) {
continue;
}
const float DELTA = 1.0 / 8192.0;
vec3 uvw_pos = vec3(local_pos_bottom / colliders[i].extents.xyz) * 0.5 + 0.5;
float y = texture(height_field_texture, uvw_pos.xz).r;
if (y + EPSILON > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(height_field_texture, uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * colliders[i].extents.xyz;
vec3 pos3 = (vec3(uvw_pos.x, texture(height_field_texture, uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * colliders[i].extents.xyz;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / colliders[i].extents.xyz) * 0.5 + 0.5).y;
col = true;
depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
}
}
if (col) {
if (!collided) {
collided = true;
collision_normal = normal;
collision_depth = depth;
} else {
vec3 c = collision_normal * collision_depth;
c += normal * max(0.0, depth - dot(normal, c));
collision_normal = normalize(c);
collision_depth = length(c);
}
}
}
}
}
if (particle_active) {
#CODE : PROCESS
}
flags &= ~PARTICLE_FLAG_ACTIVE;
if (particle_active) {
flags |= PARTICLE_FLAG_ACTIVE;
}
xform = transpose(xform);
out_xform_1 = xform[0];
out_xform_2 = xform[1];
#ifdef MODE_3D
out_xform_3 = xform[2];
#endif
out_velocity_flags.w = uintBitsToFloat(flags);
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

View file

@ -0,0 +1,121 @@
/* clang-format off */
#[modes]
mode_default =
#[specializations]
MODE_3D = false
#[vertex]
#include "stdlib_inc.glsl"
// ParticleData
layout(location = 0) in highp vec4 color;
layout(location = 1) in highp vec4 velocity_flags;
layout(location = 2) in highp vec4 custom;
layout(location = 3) in highp vec4 xform_1;
layout(location = 4) in highp vec4 xform_2;
#ifdef MODE_3D
layout(location = 5) in highp vec4 xform_3;
#endif
/* clang-format on */
out highp vec4 out_xform_1; //tfb:
out highp vec4 out_xform_2; //tfb:
#ifdef MODE_3D
out highp vec4 out_xform_3; //tfb:MODE_3D
#endif
flat out highp uvec4 instance_color_custom_data; //tfb:
uniform lowp vec3 sort_direction;
uniform highp float frame_remainder;
uniform highp vec3 align_up;
uniform highp uint align_mode;
uniform highp mat4 inv_emission_transform;
#define TRANSFORM_ALIGN_DISABLED uint(0)
#define TRANSFORM_ALIGN_Z_BILLBOARD uint(1)
#define TRANSFORM_ALIGN_Y_TO_VELOCITY uint(2)
#define TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY uint(3)
#define PARTICLE_FLAG_ACTIVE uint(1)
#define FLT_MAX float(3.402823466e+38)
void main() {
// Set scale to zero and translate to -INF so particle will be invisible
// even for materials that ignore rotation/scale (i.e. billboards).
mat4 txform = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(-FLT_MAX, -FLT_MAX, -FLT_MAX, 0.0));
if (bool(floatBitsToUint(velocity_flags.w) & PARTICLE_FLAG_ACTIVE)) {
#ifdef MODE_3D
txform = transpose(mat4(xform_1, xform_2, xform_3, vec4(0.0, 0.0, 0.0, 1.0)));
#else
txform = transpose(mat4(xform_1, xform_2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)));
#endif
if (align_mode == TRANSFORM_ALIGN_DISABLED) {
// nothing
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD) {
mat3 local = mat3(normalize(cross(align_up, sort_direction)), align_up, sort_direction);
local = local * mat3(txform);
txform[0].xyz = local[0];
txform[1].xyz = local[1];
txform[2].xyz = local[2];
} else if (align_mode == TRANSFORM_ALIGN_Y_TO_VELOCITY) {
vec3 v = velocity_flags.xyz;
float s = (length(txform[0]) + length(txform[1]) + length(txform[2])) / 3.0;
if (length(v) > 0.0) {
txform[1].xyz = normalize(v);
} else {
txform[1].xyz = normalize(txform[1].xyz);
}
txform[0].xyz = normalize(cross(txform[1].xyz, txform[2].xyz));
txform[2].xyz = vec3(0.0, 0.0, 1.0) * s;
txform[0].xyz *= s;
txform[1].xyz *= s;
} else if (align_mode == TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
vec3 sv = velocity_flags.xyz - sort_direction * dot(sort_direction, velocity_flags.xyz); //screen velocity
if (length(sv) == 0.0) {
sv = align_up;
}
sv = normalize(sv);
txform[0].xyz = normalize(cross(sv, sort_direction)) * length(txform[0]);
txform[1].xyz = sv * length(txform[1]);
txform[2].xyz = sort_direction * length(txform[2]);
}
txform[3].xyz += velocity_flags.xyz * frame_remainder;
#ifndef MODE_3D
// In global mode, bring 2D particles to local coordinates
// as they will be drawn with the node position as origin.
txform = inv_emission_transform * txform;
#endif
}
txform = transpose(txform);
instance_color_custom_data.x = packHalf2x16(color.xy);
instance_color_custom_data.y = packHalf2x16(color.zw);
instance_color_custom_data.z = packHalf2x16(custom.xy);
instance_color_custom_data.w = packHalf2x16(custom.zw);
out_xform_1 = txform[0];
out_xform_2 = txform[1];
#ifdef MODE_3D
out_xform_3 = txform[2];
#endif
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
/* clang-format off */
#[modes]
mode_base_pass =
mode_blend_pass = #define MODE_BLEND_PASS
#[specializations]
MODE_2D = true
USE_BLEND_SHAPES = false
USE_SKELETON = false
USE_NORMAL = false
USE_TANGENT = false
FINAL_PASS = false
USE_EIGHT_WEIGHTS = false
#[vertex]
#include "stdlib_inc.glsl"
#ifdef MODE_2D
#define VFORMAT vec2
#else
#define VFORMAT vec3
#endif
#ifdef FINAL_PASS
#define OFORMAT vec2
#else
#define OFORMAT uvec2
#endif
// These come from the source mesh and the output from previous passes.
layout(location = 0) in highp VFORMAT in_vertex;
#ifdef MODE_BLEND_PASS
#ifdef USE_NORMAL
layout(location = 1) in highp uvec2 in_normal;
#endif
#ifdef USE_TANGENT
layout(location = 2) in highp uvec2 in_tangent;
#endif
#else // MODE_BLEND_PASS
#ifdef USE_NORMAL
layout(location = 1) in highp vec2 in_normal;
#endif
#ifdef USE_TANGENT
layout(location = 2) in highp vec2 in_tangent;
#endif
#endif // MODE_BLEND_PASS
#ifdef USE_SKELETON
#ifdef USE_EIGHT_WEIGHTS
layout(location = 10) in highp uvec4 in_bone_attrib;
layout(location = 11) in highp uvec4 in_bone_attrib2;
layout(location = 12) in mediump vec4 in_weight_attrib;
layout(location = 13) in mediump vec4 in_weight_attrib2;
#else
layout(location = 10) in highp uvec4 in_bone_attrib;
layout(location = 11) in mediump vec4 in_weight_attrib;
#endif
uniform highp sampler2D skeleton_texture; // texunit:0
#endif
/* clang-format on */
#ifdef MODE_BLEND_PASS
layout(location = 3) in highp VFORMAT blend_vertex;
#ifdef USE_NORMAL
layout(location = 4) in highp vec2 blend_normal;
#endif
#ifdef USE_TANGENT
layout(location = 5) in highp vec2 blend_tangent;
#endif
#endif // MODE_BLEND_PASS
out highp VFORMAT out_vertex; //tfb:
#ifdef USE_NORMAL
flat out highp OFORMAT out_normal; //tfb:USE_NORMAL
#endif
#ifdef USE_TANGENT
flat out highp OFORMAT out_tangent; //tfb:USE_TANGENT
#endif
#ifdef USE_BLEND_SHAPES
uniform highp float blend_weight;
uniform lowp float blend_shape_count;
#endif
#ifdef USE_SKELETON
uniform mediump vec2 skeleton_transform_x;
uniform mediump vec2 skeleton_transform_y;
uniform mediump vec2 skeleton_transform_offset;
uniform mediump vec2 inverse_transform_x;
uniform mediump vec2 inverse_transform_y;
uniform mediump vec2 inverse_transform_offset;
#endif
vec2 signNotZero(vec2 v) {
return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0)));
}
vec3 oct_to_vec3(vec2 oct) {
oct = oct * 2.0 - 1.0;
vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y));
if (v.z < 0.0) {
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
}
return normalize(v);
}
vec2 vec3_to_oct(vec3 e) {
e /= abs(e.x) + abs(e.y) + abs(e.z);
vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy);
return oct * 0.5f + 0.5f;
}
vec4 oct_to_tang(vec2 oct_sign_encoded) {
// Binormal sign encoded in y component
vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0);
return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y));
}
vec2 tang_to_oct(vec4 base) {
vec2 oct = vec3_to_oct(base.xyz);
// Encode binormal sign in y component
oct.y = oct.y * 0.5f + 0.5f;
oct.y = base.w >= 0.0f ? oct.y : 1.0 - oct.y;
return oct;
}
// Our original input for normals and tangents is 2 16-bit floats.
// Transform Feedback has to write out 32-bits per channel.
// Octahedral compression requires normalized vectors, but we need to store
// non-normalized vectors until the very end.
// Therefore, we will compress our normals into 16 bits using signed-normalized
// fixed point precision. This works well, because we know that each normal
// is no larger than |1| so we can normalize by dividing by the number of blend
// shapes.
uvec2 vec4_to_vec2(vec4 p_vec) {
return uvec2(packSnorm2x16(p_vec.xy), packSnorm2x16(p_vec.zw));
}
vec4 vec2_to_vec4(uvec2 p_vec) {
return vec4(unpackSnorm2x16(p_vec.x), unpackSnorm2x16(p_vec.y));
}
void main() {
#ifdef MODE_2D
out_vertex = in_vertex;
#ifdef USE_BLEND_SHAPES
#ifdef MODE_BLEND_PASS
out_vertex = in_vertex + blend_vertex * blend_weight;
#else
out_vertex = in_vertex * blend_weight;
#endif
#ifdef FINAL_PASS
out_vertex = normalize(out_vertex);
#endif
#endif // USE_BLEND_SHAPES
#ifdef USE_SKELETON
#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
#define GET_BONE_MATRIX(a, b, w) mat2x4(TEX(a), TEX(b)) * w
uvec4 bones = in_bone_attrib * uvec4(2u);
uvec4 bones_a = bones + uvec4(1u);
highp mat2x4 m = GET_BONE_MATRIX(bones.x, bones_a.x, in_weight_attrib.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, in_weight_attrib.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w);
mat4 skeleton_matrix = mat4(vec4(skeleton_transform_x, 0.0, 0.0), vec4(skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(skeleton_transform_offset, 0.0, 1.0));
mat4 inverse_matrix = mat4(vec4(inverse_transform_x, 0.0, 0.0), vec4(inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(inverse_transform_offset, 0.0, 1.0));
mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
bone_matrix = skeleton_matrix * transpose(bone_matrix) * inverse_matrix;
out_vertex = (bone_matrix * vec4(out_vertex, 0.0, 1.0)).xy;
#endif // USE_SKELETON
#else // MODE_2D
#ifdef USE_BLEND_SHAPES
#ifdef MODE_BLEND_PASS
out_vertex = in_vertex + blend_vertex * blend_weight;
#ifdef USE_NORMAL
vec3 normal = vec2_to_vec4(in_normal).xyz * blend_shape_count;
vec3 normal_blend = oct_to_vec3(blend_normal) * blend_weight;
#ifdef FINAL_PASS
out_normal = vec3_to_oct(normalize(normal + normal_blend));
#else
out_normal = vec4_to_vec2(vec4(normal + normal_blend, 0.0) / blend_shape_count);
#endif
#endif // USE_NORMAL
#ifdef USE_TANGENT
vec4 tangent = vec2_to_vec4(in_tangent) * blend_shape_count;
vec4 tangent_blend = oct_to_tang(blend_tangent) * blend_weight;
#ifdef FINAL_PASS
out_tangent = tang_to_oct(vec4(normalize(tangent.xyz + tangent_blend.xyz), tangent.w));
#else
out_tangent = vec4_to_vec2(vec4((tangent.xyz + tangent_blend.xyz) / blend_shape_count, tangent.w));
#endif
#endif // USE_TANGENT
#else // MODE_BLEND_PASS
out_vertex = in_vertex * blend_weight;
#ifdef USE_NORMAL
vec3 normal = oct_to_vec3(in_normal);
out_normal = vec4_to_vec2(vec4(normal * blend_weight / blend_shape_count, 0.0));
#endif
#ifdef USE_TANGENT
vec4 tangent = oct_to_tang(in_tangent);
out_tangent = vec4_to_vec2(vec4(tangent.rgb * blend_weight / blend_shape_count, tangent.w));
#endif
#endif // MODE_BLEND_PASS
#else // USE_BLEND_SHAPES
// Make attributes available to the skeleton shader if not written by blend shapes.
out_vertex = in_vertex;
#ifdef USE_NORMAL
out_normal = in_normal;
#endif
#ifdef USE_TANGENT
out_tangent = in_tangent;
#endif
#endif // USE_BLEND_SHAPES
#ifdef USE_SKELETON
#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0)
#define GET_BONE_MATRIX(a, b, c, w) mat4(TEX(a), TEX(b), TEX(c), vec4(0.0, 0.0, 0.0, 1.0)) * w
uvec4 bones = in_bone_attrib * uvec4(3);
uvec4 bones_a = bones + uvec4(1);
uvec4 bones_b = bones + uvec4(2);
highp mat4 m;
m = GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib.w);
#ifdef USE_EIGHT_WEIGHTS
bones = in_bone_attrib2 * uvec4(3);
bones_a = bones + uvec4(1);
bones_b = bones + uvec4(2);
m += GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib2.x);
m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib2.y);
m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib2.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib2.w);
#endif
// Reverse order because its transposed.
out_vertex = (vec4(out_vertex, 1.0) * m).xyz;
#ifdef USE_NORMAL
vec3 vertex_normal = oct_to_vec3(out_normal);
out_normal = vec3_to_oct(normalize((vec4(vertex_normal, 0.0) * m).xyz));
#endif // USE_NORMAL
#ifdef USE_TANGENT
vec4 vertex_tangent = oct_to_tang(out_tangent);
out_tangent = tang_to_oct(vec4(normalize((vec4(vertex_tangent.xyz, 0.0) * m).xyz), vertex_tangent.w));
#endif // USE_TANGENT
#endif // USE_SKELETON
#endif // MODE_2D
}
/* clang-format off */
#[fragment]
void main() {
}
/* clang-format on */

View file

@ -0,0 +1,247 @@
/* clang-format off */
#[modes]
mode_background =
mode_cubemap = #define USE_CUBEMAP_PASS
#[specializations]
USE_MULTIVIEW = false
USE_INVERTED_Y = true
APPLY_TONEMAPPING = true
USE_QUARTER_RES_PASS = false
USE_HALF_RES_PASS = false
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
out vec2 uv_interp;
/* clang-format on */
void main() {
#ifdef USE_INVERTED_Y
uv_interp = vertex_attrib;
#else
// We're doing clockwise culling so flip the order
uv_interp = vec2(vertex_attrib.x, vertex_attrib.y * -1.0);
#endif
gl_Position = vec4(uv_interp, -1.0, 1.0);
}
/* clang-format off */
#[fragment]
#define M_PI 3.14159265359
#include "tonemap_inc.glsl"
in vec2 uv_interp;
/* clang-format on */
uniform samplerCube radiance; //texunit:-1
#ifdef USE_CUBEMAP_PASS
uniform samplerCube half_res; //texunit:-2
uniform samplerCube quarter_res; //texunit:-3
#elif defined(USE_MULTIVIEW)
uniform sampler2DArray half_res; //texunit:-2
uniform sampler2DArray quarter_res; //texunit:-3
#else
uniform sampler2D half_res; //texunit:-2
uniform sampler2D quarter_res; //texunit:-3
#endif
layout(std140) uniform GlobalShaderUniformData { //ubo:1
vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
};
struct DirectionalLightData {
vec4 direction_energy;
vec4 color_size;
bool enabled;
};
layout(std140) uniform DirectionalLights { //ubo:4
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
}
directional_lights;
/* clang-format off */
#ifdef MATERIAL_UNIFORMS_USED
layout(std140) uniform MaterialUniforms{ //ubo:3
#MATERIAL_UNIFORMS
};
#endif
/* clang-format on */
#GLOBALS
#ifdef USE_CUBEMAP_PASS
#define AT_CUBEMAP_PASS true
#else
#define AT_CUBEMAP_PASS false
#endif
#ifdef USE_HALF_RES_PASS
#define AT_HALF_RES_PASS true
#else
#define AT_HALF_RES_PASS false
#endif
#ifdef USE_QUARTER_RES_PASS
#define AT_QUARTER_RES_PASS true
#else
#define AT_QUARTER_RES_PASS false
#endif
// mat4 is a waste of space, but we don't have an easy way to set a mat3 uniform for now
uniform mat4 orientation;
uniform vec4 projection;
uniform vec3 position;
uniform float time;
uniform float sky_energy_multiplier;
uniform float luminance_multiplier;
uniform float fog_aerial_perspective;
uniform vec4 fog_light_color;
uniform float fog_sun_scatter;
uniform bool fog_enabled;
uniform float fog_density;
uniform float fog_sky_affect;
uniform uint directional_light_count;
#ifdef USE_MULTIVIEW
layout(std140) uniform MultiviewData { // ubo:11
highp mat4 projection_matrix_view[MAX_VIEWS];
highp mat4 inv_projection_matrix_view[MAX_VIEWS];
highp vec4 eye_offset[MAX_VIEWS];
}
multiview_data;
#endif
layout(location = 0) out vec4 frag_color;
#ifdef USE_DEBANDING
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
vec3 interleaved_gradient_noise(vec2 pos) {
const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);
float res = fract(magic.z * fract(dot(pos, magic.xy))) * 2.0 - 1.0;
return vec3(res, -res, res) / 255.0;
}
#endif
#if !defined(DISABLE_FOG)
vec4 fog_process(vec3 view, vec3 sky_color) {
vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective);
if (fog_sun_scatter > 0.001) {
vec4 sun_scatter = vec4(0.0);
float sun_total = 0.0;
for (uint i = 0u; i < directional_light_count; i++) {
vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w;
float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0);
fog_color += light_color * light_amount * fog_sun_scatter;
}
}
return vec4(fog_color, 1.0);
}
#endif // !DISABLE_FOG
void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
vec4 unproject = vec4(uv_interp.xy, -1.0, 1.0); // unproject at the far plane
vec4 unprojected = multiview_data.inv_projection_matrix_view[ViewIndex] * unproject;
cube_normal = unprojected.xyz / unprojected.w;
// Unproject will give us the position between the eyes, need to re-offset.
cube_normal += multiview_data.eye_offset[ViewIndex].xyz;
#else
cube_normal.z = -1.0;
cube_normal.x = (uv_interp.x + projection.x) / projection.y;
cube_normal.y = (-uv_interp.y - projection.z) / projection.w;
#endif
cube_normal = mat3(orientation) * cube_normal;
cube_normal = normalize(cube_normal);
vec2 uv = gl_FragCoord.xy; // uv_interp * 0.5 + 0.5;
vec2 panorama_coords = vec2(atan(cube_normal.x, -cube_normal.z), acos(cube_normal.y));
if (panorama_coords.x < 0.0) {
panorama_coords.x += M_PI * 2.0;
}
panorama_coords /= vec2(M_PI * 2.0, M_PI);
vec3 color = vec3(0.0, 0.0, 0.0);
float alpha = 1.0; // Only available to subpasses
vec4 half_res_color = vec4(1.0);
vec4 quarter_res_color = vec4(1.0);
vec4 custom_fog = vec4(0.0);
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal);
#endif
#else
#ifdef USES_HALF_RES_COLOR
#ifdef USE_MULTIVIEW
half_res_color = textureLod(sampler2DArray(half_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
half_res_color = textureLod(sampler2D(half_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#ifdef USES_QUARTER_RES_COLOR
#ifdef USE_MULTIVIEW
quarter_res_color = textureLod(sampler2DArray(quarter_res, SAMPLER_LINEAR_CLAMP), vec3(uv, ViewIndex), 0.0);
#else
quarter_res_color = textureLod(sampler2D(quarter_res, SAMPLER_LINEAR_CLAMP), uv, 0.0);
#endif
#endif
#endif
{
#CODE : SKY
}
color *= sky_energy_multiplier;
// Convert to Linear for tonemapping so color matches scene shader better
color = srgb_to_linear(color);
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
if (fog_enabled) {
vec4 fog = fog_process(cube_normal, color.rgb);
color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect);
}
if (custom_fog.a > 0.0) {
color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a);
}
#endif // DISABLE_FOG
color *= exposure;
#ifdef APPLY_TONEMAPPING
color = apply_tonemapping(color, white);
#endif
color = linear_to_srgb(color);
frag_color.rgb = color * luminance_multiplier;
frag_color.a = alpha;
#ifdef USE_DEBANDING
frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy) * sky_energy_multiplier * luminance_multiplier;
#endif
}

View file

@ -0,0 +1,89 @@
// Compatibility renames. These are exposed with the "godot_" prefix
// to work around two distinct Adreno bugs:
// 1. Some Adreno devices expose ES310 functions in ES300 shaders.
// Internally, we must use the "godot_" prefix, but user shaders
// will be mapped automatically.
// 2. Adreno 3XX devices have poor implementations of the other packing
// functions, so we just use our own there to keep it simple.
#ifdef USE_HALF2FLOAT
// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
// It appears to be safe to expose these on mobile, but when running through ANGLE this appears to break.
uint float2half(uint f) {
uint e = f & uint(0x7f800000);
if (e <= uint(0x38000000)) {
return uint(0);
} else {
return ((f >> uint(16)) & uint(0x8000)) |
(((e - uint(0x38000000)) >> uint(13)) & uint(0x7c00)) |
((f >> uint(13)) & uint(0x03ff));
}
}
uint half2float(uint h) {
uint h_e = h & uint(0x7c00);
return ((h & uint(0x8000)) << uint(16)) | uint((h_e >> uint(10)) != uint(0)) * (((h_e + uint(0x1c000)) << uint(13)) | ((h & uint(0x03ff)) << uint(13)));
}
uint godot_packHalf2x16(vec2 v) {
return float2half(floatBitsToUint(v.x)) | float2half(floatBitsToUint(v.y)) << uint(16);
}
vec2 godot_unpackHalf2x16(uint v) {
return vec2(uintBitsToFloat(half2float(v & uint(0xffff))),
uintBitsToFloat(half2float(v >> uint(16))));
}
uint godot_packUnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(0.0), vec2(1.0)) * 65535.0));
return uv.x | uv.y << uint(16);
}
vec2 godot_unpackUnorm2x16(uint p) {
return vec2(float(p & uint(0xffff)), float(p >> uint(16))) * 0.000015259021; // 1.0 / 65535.0 optimization
}
uint godot_packSnorm2x16(vec2 v) {
uvec2 uv = uvec2(round(clamp(v, vec2(-1.0), vec2(1.0)) * 32767.0) + 32767.0);
return uv.x | uv.y << uint(16);
}
vec2 godot_unpackSnorm2x16(uint p) {
vec2 v = vec2(float(p & uint(0xffff)), float(p >> uint(16)));
return clamp((v - 32767.0) * vec2(0.00003051851), vec2(-1.0), vec2(1.0));
}
#define packHalf2x16 godot_packHalf2x16
#define unpackHalf2x16 godot_unpackHalf2x16
#define packUnorm2x16 godot_packUnorm2x16
#define unpackUnorm2x16 godot_unpackUnorm2x16
#define packSnorm2x16 godot_packSnorm2x16
#define unpackSnorm2x16 godot_unpackSnorm2x16
#endif // USE_HALF2FLOAT
// Always expose these as they are ES310 functions and not available in ES300 or GLSL 330.
uint godot_packUnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(0.0), vec4(1.0)) * 255.0));
return uv.x | (uv.y << uint(8)) | (uv.z << uint(16)) | (uv.w << uint(24));
}
vec4 godot_unpackUnorm4x8(uint p) {
return vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24))) * 0.00392156862; // 1.0 / 255.0
}
uint godot_packSnorm4x8(vec4 v) {
uvec4 uv = uvec4(round(clamp(v, vec4(-1.0), vec4(1.0)) * 127.0) + 127.0);
return uv.x | uv.y << uint(8) | uv.z << uint(16) | uv.w << uint(24);
}
vec4 godot_unpackSnorm4x8(uint p) {
vec4 v = vec4(float(p & uint(0xff)), float((p >> uint(8)) & uint(0xff)), float((p >> uint(16)) & uint(0xff)), float(p >> uint(24)));
return clamp((v - vec4(127.0)) * vec4(0.00787401574), vec4(-1.0), vec4(1.0));
}
#define packUnorm4x8 godot_packUnorm4x8
#define unpackUnorm4x8 godot_unpackUnorm4x8
#define packSnorm4x8 godot_packSnorm4x8
#define unpackSnorm4x8 godot_unpackSnorm4x8

View file

@ -0,0 +1,182 @@
layout(std140) uniform TonemapData { //ubo:0
float exposure;
float white;
int tonemapper;
int pad;
int pad2;
float brightness;
float contrast;
float saturation;
};
// This expects 0-1 range input.
vec3 linear_to_srgb(vec3 color) {
//color = clamp(color, vec3(0.0), vec3(1.0));
//const vec3 a = vec3(0.055f);
//return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return max(vec3(1.055) * pow(color, vec3(0.416666667)) - vec3(0.055), vec3(0.0));
}
// This expects 0-1 range input, outside that range it behaves poorly.
vec3 srgb_to_linear(vec3 color) {
// Approximation from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
return color * (color * (color * 0.305306011 + 0.682171111) + 0.012522878);
}
#ifdef APPLY_TONEMAPPING
// Based on Reinhard's extended formula, see equation 4 in https://doi.org/cjbgrt
vec3 tonemap_reinhard(vec3 color, float p_white) {
float white_squared = p_white * p_white;
vec3 white_squared_color = white_squared * color;
// Equivalent to color * (1 + color / white_squared) / (1 + color)
return (white_squared_color + color * color) / (white_squared_color + white_squared);
}
vec3 tonemap_filmic(vec3 color, float p_white) {
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
// has no effect on the curve's general shape or visual properties
const float exposure_bias = 2.0f;
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
const float B = 0.30f * exposure_bias;
const float C = 0.10f;
const float D = 0.20f;
const float E = 0.01f;
const float F = 0.30f;
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
float p_white_tonemapped = ((p_white * (A * p_white + C * B) + D * E) / (p_white * (A * p_white + B) + D * F)) - E / F;
return color_tonemapped / p_white_tonemapped;
}
// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
// (MIT License).
vec3 tonemap_aces(vec3 color, float p_white) {
const float exposure_bias = 1.8f;
const float A = 0.0245786f;
const float B = 0.000090537f;
const float C = 0.983729f;
const float D = 0.432951f;
const float E = 0.238081f;
// Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
const mat3 rgb_to_rrt = mat3(
vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
const mat3 odt_to_rgb = mat3(
vec3(1.60475f, -0.53108f, -0.07367f),
vec3(-0.10208f, 1.10813f, -0.00605f),
vec3(-0.00327f, -0.07276f, 1.07602f));
color *= rgb_to_rrt;
vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
color_tonemapped *= odt_to_rgb;
p_white *= exposure_bias;
float p_white_tonemapped = (p_white * (p_white + A) - B) / (p_white * (C * p_white + D) + E);
return color_tonemapped / p_white_tonemapped;
}
// Polynomial approximation of EaryChow's AgX sigmoid curve.
// x must be within the range [0.0, 1.0]
vec3 agx_contrast_approx(vec3 x) {
// Generated with Excel trendline
// Input data: Generated using python sigmoid with EaryChow's configuration and 57 steps
// Additional padding values were added to give correct intersections at 0.0 and 1.0
// 6th order, intercept of 0.0 to remove an operation and ensure intersection at 0.0
vec3 x2 = x * x;
vec3 x4 = x2 * x2;
return 0.021 * x + 4.0111 * x2 - 25.682 * x2 * x + 70.359 * x4 - 74.778 * x4 * x + 27.069 * x4 * x2;
}
// This is an approximation and simplification of EaryChow's AgX implementation that is used by Blender.
// This code is based off of the script that generates the AgX_Base_sRGB.cube LUT that Blender uses.
// Source: https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBasesRGB.py
vec3 tonemap_agx(vec3 color) {
// Combined linear sRGB to linear Rec 2020 and Blender AgX inset matrices:
const mat3 srgb_to_rec2020_agx_inset_matrix = mat3(
0.54490813676363087053, 0.14044005884001287035, 0.088827411851915368603,
0.37377945959812267119, 0.75410959864013760045, 0.17887712465043811023,
0.081384976686407536266, 0.10543358536857773485, 0.73224999956948382528);
// Combined inverse AgX outset matrix and linear Rec 2020 to linear sRGB matrices.
const mat3 agx_outset_rec2020_to_srgb_matrix = mat3(
1.9645509602733325934, -0.29932243390911083839, -0.16436833806080403409,
-0.85585845117807513559, 1.3264510741502356555, -0.23822464068860595117,
-0.10886710826831608324, -0.027084020983874825605, 1.402665347143271889);
// LOG2_MIN = -10.0
// LOG2_MAX = +6.5
// MIDDLE_GRAY = 0.18
const float min_ev = -12.4739311883324; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
const float max_ev = 4.02606881166759; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
// Large negative values in one channel and large positive values in other
// channels can result in a colour that appears darker and more saturated than
// desired after passing it through the inset matrix. For this reason, it is
// best to prevent negative input values.
// This is done before the Rec. 2020 transform to allow the Rec. 2020
// transform to be combined with the AgX inset matrix. This results in a loss
// of color information that could be correctly interpreted within the
// Rec. 2020 color space as positive RGB values, but it is less common for Godot
// to provide this function with negative sRGB values and therefore not worth
// the performance cost of an additional matrix multiplication.
// A value of 2e-10 intentionally introduces insignificant error to prevent
// log2(0.0) after the inset matrix is applied; color will be >= 1e-10 after
// the matrix transform.
color = max(color, 2e-10);
// Do AGX in rec2020 to match Blender and then apply inset matrix.
color = srgb_to_rec2020_agx_inset_matrix * color;
// Log2 space encoding.
// Must be clamped because agx_contrast_approx may not work
// well with values outside of the range [0.0, 1.0]
color = clamp(log2(color), min_ev, max_ev);
color = (color - min_ev) / (max_ev - min_ev);
// Apply sigmoid function approximation.
color = agx_contrast_approx(color);
// Convert back to linear before applying outset matrix.
color = pow(color, vec3(2.4));
// Apply outset to make the result more chroma-laden and then go back to linear sRGB.
color = agx_outset_rec2020_to_srgb_matrix * color;
// Blender's lusRGB.compensate_low_side is too complex for this shader, so
// simply return the color, even if it has negative components. These negative
// components may be useful for subsequent color adjustments.
return color;
}
#define TONEMAPPER_LINEAR 0
#define TONEMAPPER_REINHARD 1
#define TONEMAPPER_FILMIC 2
#define TONEMAPPER_ACES 3
#define TONEMAPPER_AGX 4
vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR
// Ensure color values passed to tonemappers are positive.
// They can be negative in the case of negative lights, which leads to undesired behavior.
if (tonemapper == TONEMAPPER_LINEAR) {
return color;
} else if (tonemapper == TONEMAPPER_REINHARD) {
return tonemap_reinhard(max(vec3(0.0f), color), p_white);
} else if (tonemapper == TONEMAPPER_FILMIC) {
return tonemap_filmic(max(vec3(0.0f), color), p_white);
} else if (tonemapper == TONEMAPPER_ACES) {
return tonemap_aces(max(vec3(0.0f), color), p_white);
} else { // TONEMAPPER_AGX
return tonemap_agx(color);
}
}
#endif // APPLY_TONEMAPPING

View 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")

View file

@ -0,0 +1,253 @@
/**************************************************************************/
/* config.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "config.h"
#include "../rasterizer_gles3.h"
#ifdef WEB_ENABLED
#include <emscripten/html5_webgl.h>
#endif
using namespace GLES3;
#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
Config *Config::singleton = nullptr;
Config::Config() {
singleton = this;
#ifdef WEB_ENABLED
// Starting with Emscripten 3.1.51, glGetStringi(GL_EXTENSIONS, i) will only ever return
// a fixed list of extensions, regardless of what additional extensions are enabled. This
// isn't very useful for us in determining which extensions we can rely on here. So, instead
// we use emscripten_webgl_get_supported_extensions() to get all supported extensions, which
// is what Emscripten 3.1.50 and earlier do.
{
char *extension_array_string = emscripten_webgl_get_supported_extensions();
PackedStringArray extension_array = String((const char *)extension_array_string).split(" ");
extensions.reserve(extension_array.size() * 2);
for (const String &s : extension_array) {
extensions.insert(s);
extensions.insert("GL_" + s);
}
free(extension_array_string);
}
#else
{
GLint max_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
for (int i = 0; i < max_extensions; i++) {
const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
if (!s) {
break;
}
extensions.insert((const char *)s);
}
}
#endif
bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc");
astc_supported = extensions.has("GL_KHR_texture_compression_astc") || extensions.has("GL_OES_texture_compression_astc") || extensions.has("GL_KHR_texture_compression_astc_ldr") || extensions.has("GL_KHR_texture_compression_astc_hdr");
astc_hdr_supported = extensions.has("GL_KHR_texture_compression_astc_hdr");
astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d");
if (RasterizerGLES3::is_gles_over_gl()) {
float_texture_supported = true;
float_texture_linear_supported = true;
etc2_supported = false;
s3tc_supported = true;
rgtc_supported = true; //RGTC - core since OpenGL version 3.0
srgb_framebuffer_supported = true;
} else {
float_texture_supported = extensions.has("GL_EXT_color_buffer_float");
float_texture_linear_supported = extensions.has("GL_OES_texture_float_linear");
etc2_supported = true;
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
// This could be fixed but so few devices support it that it doesn't seem useful (and makes bigger APKs).
// For good measure we do the same hack for iOS, just in case.
s3tc_supported = false;
#else
s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc");
#endif
rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
srgb_framebuffer_supported = extensions.has("GL_EXT_sRGB_write_control");
}
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &max_vertex_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
GLint max_vertex_output;
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
GLint max_fragment_input;
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
// sanity clamp buffer size to 16K..1MB
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);
support_anisotropic_filter = extensions.has("GL_EXT_texture_filter_anisotropic");
if (support_anisotropic_filter) {
glGetFloatv(_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropic_level);
anisotropic_level = MIN(float(1 << int(GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level);
}
glGetIntegerv(GL_MAX_SAMPLES, &msaa_max_samples);
#ifdef WEB_ENABLED
msaa_supported = (msaa_max_samples > 0);
#else
msaa_supported = true;
#endif
#ifndef IOS_ENABLED
#ifdef WEB_ENABLED
msaa_multiview_supported = extensions.has("OCULUS_multiview");
rt_msaa_multiview_supported = msaa_multiview_supported;
#else
msaa_multiview_supported = extensions.has("GL_EXT_multiview_texture_multisample");
#endif
multiview_supported = extensions.has("OCULUS_multiview") || extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview");
#endif
#ifdef ANDROID_ENABLED
// These are GLES only
rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture");
rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture");
external_texture_supported = extensions.has("GL_OES_EGL_image_external_essl3");
if (multiview_supported) {
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
if (eglFramebufferTextureMultiviewOVR == nullptr) {
multiview_supported = false;
}
}
if (msaa_multiview_supported) {
eglTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)eglGetProcAddress("glTexStorage3DMultisample");
if (eglTexStorage3DMultisample == nullptr) {
msaa_multiview_supported = false;
}
}
if (rt_msaa_supported) {
eglFramebufferTexture2DMultisampleEXT = (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress("glFramebufferTexture2DMultisampleEXT");
if (eglFramebufferTexture2DMultisampleEXT == nullptr) {
rt_msaa_supported = false;
}
}
if (rt_msaa_multiview_supported) {
eglFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR");
if (eglFramebufferTextureMultisampleMultiviewOVR == nullptr) {
rt_msaa_multiview_supported = false;
}
}
if (external_texture_supported) {
eglEGLImageTargetTexture2DOES = (PFNEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (eglEGLImageTargetTexture2DOES == nullptr) {
external_texture_supported = false;
}
}
#endif
force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
if (use_depth_prepass) {
String vendors = GLOBAL_GET("rendering/driver/depth_prepass/disable_for_vendors");
Vector<String> vendor_match = vendors.split(",");
const String &renderer = String::utf8((const char *)glGetString(GL_RENDERER));
for (int i = 0; i < vendor_match.size(); i++) {
String v = vendor_match[i].strip_edges();
if (v == String()) {
continue;
}
if (renderer.containsn(v)) {
use_depth_prepass = false;
}
}
}
max_renderable_elements = GLOBAL_GET("rendering/limits/opengl/max_renderable_elements");
max_renderable_lights = GLOBAL_GET("rendering/limits/opengl/max_renderable_lights");
max_lights_per_object = GLOBAL_GET("rendering/limits/opengl/max_lights_per_object");
//Adreno 3xx Compatibility
const String rendering_device_name = String::utf8((const char *)glGetString(GL_RENDERER));
if (rendering_device_name.left(13) == "Adreno (TM) 3") {
flip_xy_workaround = true;
disable_particles_workaround = true;
// ignore driver version 331+
const String gl_version = String::utf8((const char *)glGetString(GL_VERSION));
// Adreno 3xx examples (https://opengles.gpuinfo.org/listreports.php):
// ===========================================================================
// OpenGL ES 3.0 V@84.0 AU@ (CL@)
// OpenGL ES 3.0 V@127.0 AU@ (GIT@I96aee987eb)
// OpenGL ES 3.0 V@140.0 AU@ (GIT@Ifd751822f5)
// OpenGL ES 3.0 V@251.0 AU@08.00.00.312.030 (GIT@Ie4790512f3)
// OpenGL ES 3.0 V@269.0 AU@ (GIT@I109c45a694)
// OpenGL ES 3.0 V@331.0 (GIT@35e467f, Ice9844a736) (Date:04/15/19)
// OpenGL ES 3.0 V@415.0 (GIT@d39f783, I79de86aa2c, 1591296226) (Date:06/04/20)
// OpenGL ES 3.0 V@0502.0 (GIT@09fef447e8, I1fe547a144, 1661493934) (Date:08/25/22)
String driver_version = gl_version.get_slice("V@", 1).get_slice(" ", 0);
if (driver_version.is_valid_float() && driver_version.to_float() >= 331.0) {
flip_xy_workaround = false;
//TODO: also 'GPUParticles'?
//https://github.com/godotengine/godot/issues/92662#issuecomment-2161199477
//disable_particles_workaround = false;
}
} else if (rendering_device_name == "PowerVR Rogue GE8320") {
disable_transform_feedback_shader_cache = true;
}
if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3_angle") {
polyfill_half2float = false;
}
#ifdef WEB_ENABLED
polyfill_half2float = false;
#endif
}
Config::~Config() {
singleton = nullptr;
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,127 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef CONFIG_GLES3_H
#define CONFIG_GLES3_H
#ifdef GLES3_ENABLED
#include "core/config/project_settings.h"
#include "core/string/ustring.h"
#include "core/templates/hash_set.h"
#include "platform_gl.h"
#ifdef ANDROID_ENABLED
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei);
typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);
typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei);
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei);
typedef void (*PFNEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum, void *);
#endif
namespace GLES3 {
class Config {
private:
static Config *singleton;
public:
bool use_nearest_mip_filter = false;
bool use_depth_prepass = true;
GLint max_vertex_texture_image_units = 0;
GLint max_texture_image_units = 0;
GLint max_texture_size = 0;
GLint max_viewport_size[2] = { 0, 0 };
GLint64 max_uniform_buffer_size = 0;
uint32_t max_shader_varyings = 0;
int64_t max_renderable_elements = 0;
int64_t max_renderable_lights = 0;
int64_t max_lights_per_object = 0;
bool generate_wireframes = false;
HashSet<String> extensions;
bool float_texture_supported = false;
bool float_texture_linear_supported = false;
bool s3tc_supported = false;
bool rgtc_supported = false;
bool bptc_supported = false;
bool etc2_supported = false;
bool astc_supported = false;
bool astc_hdr_supported = false;
bool astc_layered_supported = false;
bool srgb_framebuffer_supported = false;
bool force_vertex_shading = false;
bool support_anisotropic_filter = false;
float anisotropic_level = 0.0f;
GLint msaa_max_samples = 0;
bool msaa_supported = false;
bool msaa_multiview_supported = false;
bool rt_msaa_supported = false;
bool rt_msaa_multiview_supported = false;
bool multiview_supported = false;
bool external_texture_supported = false;
// Adreno 3XX compatibility.
bool disable_particles_workaround = false; // Set to 'true' to disable 'GPUParticles'.
bool flip_xy_workaround = false;
// PowerVR GE 8320 workaround.
bool disable_transform_feedback_shader_cache = false;
// ANGLE shader workaround.
bool polyfill_half2float = true;
#ifdef ANDROID_ENABLED
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr;
#endif
static Config *get_singleton() { return singleton; }
Config();
~Config();
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // CONFIG_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,913 @@
/**************************************************************************/
/* light_storage.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef LIGHT_STORAGE_GLES3_H
#define LIGHT_STORAGE_GLES3_H
#ifdef GLES3_ENABLED
#include "platform_gl.h"
#include "render_scene_buffers_gles3.h"
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "drivers/gles3/storage/texture_storage.h"
#include "servers/rendering/storage/light_storage.h"
#include "servers/rendering/storage/utilities.h"
namespace GLES3 {
/* LIGHT */
struct Light {
RS::LightType type;
float param[RS::LIGHT_PARAM_MAX];
Color color = Color(1, 1, 1, 1);
RID projector;
bool shadow = false;
bool negative = false;
bool reverse_cull = false;
RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
uint32_t max_sdfgi_cascade = 2;
uint32_t cull_mask = 0xFFFFFFFF;
uint32_t shadow_caster_mask = 0xFFFFFFFF;
bool distance_fade = false;
real_t distance_fade_begin = 40.0;
real_t distance_fade_shadow = 50.0;
real_t distance_fade_length = 10.0;
RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
bool directional_blend_splits = false;
RS::LightDirectionalSkyMode directional_sky_mode = RS::LIGHT_DIRECTIONAL_SKY_MODE_LIGHT_AND_SKY;
uint64_t version = 0;
Dependency dependency;
};
/* Light instance */
struct LightInstance {
struct ShadowTransform {
Projection camera;
Transform3D transform;
float farplane;
float split;
float bias_scale;
float shadow_texel_size;
float range_begin;
Rect2 atlas_rect;
Vector2 uv_scale;
};
ShadowTransform shadow_transform[6];
RS::LightType light_type = RS::LIGHT_DIRECTIONAL;
AABB aabb;
RID self;
RID light;
Transform3D transform;
uint64_t shadow_pass = 0;
uint64_t last_scene_pass = 0;
uint64_t last_scene_shadow_pass = 0;
uint64_t last_pass = 0;
uint32_t cull_mask = 0;
uint32_t light_directional_index = 0;
Rect2 directional_rect;
HashSet<RID> shadow_atlases; // Shadow atlases where this light is registered.
int32_t gl_id = -1;
int32_t shadow_id = -1;
LightInstance() {}
};
/* REFLECTION PROBE */
struct ReflectionProbe {
RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
int resolution = 256;
float intensity = 1.0;
float blend_distance = 1.0;
RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
Color ambient_color;
float ambient_color_energy = 1.0;
float max_distance = 0;
Vector3 size = Vector3(20, 20, 20);
Vector3 origin_offset;
bool interior = false;
bool box_projection = false;
bool enable_shadows = false;
uint32_t cull_mask = (1 << 20) - 1;
uint32_t reflection_mask = (1 << 20) - 1;
float mesh_lod_threshold = 0.01;
float baked_exposure = 1.0;
Dependency dependency;
};
/* REFLECTION ATLAS */
struct ReflectionAtlas {
int count = 0;
int size = 0;
int mipmap_count = 1; // number of mips, including original
int mipmap_size[8];
GLuint depth = 0;
struct Reflection {
RID owner;
GLuint color = 0;
GLuint radiance = 0;
GLuint fbos[7];
};
Vector<Reflection> reflections;
Ref<RenderSceneBuffersGLES3> render_buffers; // Further render buffers used.
};
/* REFLECTION PROBE INSTANCE */
struct ReflectionProbeInstance {
RID probe;
int atlas_index = -1;
RID atlas;
bool dirty = true;
bool rendering = false;
int processing_layer = 0;
uint64_t last_pass = 0;
uint32_t cull_mask = 0;
Transform3D transform;
};
/* LIGHTMAP */
struct Lightmap {
RID light_texture;
RID shadow_texture;
bool uses_spherical_harmonics = false;
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE;
PackedVector3Array points;
PackedColorArray point_sh;
PackedInt32Array tetrahedra;
PackedInt32Array bsp_tree;
struct BSP {
static const int32_t EMPTY_LEAF = INT32_MIN;
float plane[4];
int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
};
Dependency dependency;
};
struct LightmapInstance {
RID lightmap;
Transform3D transform;
};
class LightStorage : public RendererLightStorage {
public:
enum ShadowAtlastQuadrant : uint32_t {
QUADRANT_SHIFT = 27,
OMNI_LIGHT_FLAG = 1 << 26,
SHADOW_INDEX_MASK = OMNI_LIGHT_FLAG - 1,
SHADOW_INVALID = 0xFFFFFFFF
};
private:
static LightStorage *singleton;
/* LIGHT */
mutable RID_Owner<Light, true> light_owner;
/* Light instance */
mutable RID_Owner<LightInstance> light_instance_owner;
/* REFLECTION PROBE */
mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
/* REFLECTION ATLAS */
mutable RID_Owner<ReflectionAtlas> reflection_atlas_owner;
/* REFLECTION PROBE INSTANCE */
mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
/* LIGHTMAP */
float lightmap_probe_capture_update_speed = 4;
mutable RID_Owner<Lightmap, true> lightmap_owner;
/* LIGHTMAP INSTANCE */
mutable RID_Owner<LightmapInstance> lightmap_instance_owner;
/* SHADOW ATLAS */
// Note: The ShadowAtlas in the OpenGL is virtual. Each light gets assigned its
// own texture which is the same size as it would be if it were in a real atlas.
// This allows us to maintain the same behavior as the other renderers.
struct ShadowAtlas {
struct Quadrant {
uint32_t subdivision = 0;
struct Shadow {
RID owner;
bool owner_is_omni = false;
uint64_t version = 0;
uint64_t alloc_tick = 0;
Shadow() {}
};
Vector<Shadow> shadows;
LocalVector<GLuint> textures;
LocalVector<GLuint> fbos;
Quadrant() {}
} quadrants[4];
// Ordered from smallest (worst) shadow size to largest (best).
int size_order[4] = { 0, 1, 2, 3 };
uint32_t smallest_subdiv = 0;
int size = 0;
bool use_16_bits = true;
GLuint debug_texture = 0;
GLuint debug_fbo = 0;
HashMap<RID, uint32_t> shadow_owners;
};
uint64_t shadow_atlas_realloc_tolerance_msec = 500;
RID_Owner<ShadowAtlas> shadow_atlas_owner;
void _shadow_atlas_invalidate_shadow(ShadowAtlas::Quadrant::Shadow *p_shadow, RID p_atlas, ShadowAtlas *p_shadow_atlas, uint32_t p_quadrant, uint32_t p_shadow_idx);
bool _shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, bool p_omni, int &r_quadrant, int &r_shadow);
/* DIRECTIONAL SHADOW */
struct DirectionalShadow {
GLuint depth = 0;
GLuint fbo = 0;
int light_count = 0;
int size = 0;
bool use_16_bits = true;
int current_light = 0;
} directional_shadow;
public:
static LightStorage *get_singleton();
LightStorage();
virtual ~LightStorage();
/* Light API */
Light *get_light(RID p_rid) { return light_owner.get_or_null(p_rid); }
bool owns_light(RID p_rid) { return light_owner.owns(p_rid); }
void _light_initialize(RID p_rid, RS::LightType p_type);
virtual RID directional_light_allocate() override;
virtual void directional_light_initialize(RID p_rid) override;
virtual RID omni_light_allocate() override;
virtual void omni_light_initialize(RID p_rid) override;
virtual RID spot_light_allocate() override;
virtual void spot_light_initialize(RID p_rid) override;
virtual void light_free(RID p_rid) override;
virtual void light_set_color(RID p_light, const Color &p_color) override;
virtual void light_set_param(RID p_light, RS::LightParam p_param, float p_value) override;
virtual void light_set_shadow(RID p_light, bool p_enabled) override;
virtual void light_set_projector(RID p_light, RID p_texture) override;
virtual void light_set_negative(RID p_light, bool p_enable) override;
virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) override;
virtual void light_set_distance_fade(RID p_light, bool p_enabled, float p_begin, float p_shadow, float p_length) override;
virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) override;
virtual void light_set_shadow_caster_mask(RID p_light, uint32_t p_caster_mask) override;
virtual uint32_t light_get_shadow_caster_mask(RID p_light) const override;
virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) override;
virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) override {}
virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) override;
virtual void light_directional_set_shadow_mode(RID p_light, RS::LightDirectionalShadowMode p_mode) override;
virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) override;
virtual bool light_directional_get_blend_splits(RID p_light) const override;
virtual void light_directional_set_sky_mode(RID p_light, RS::LightDirectionalSkyMode p_mode) override;
virtual RS::LightDirectionalSkyMode light_directional_get_sky_mode(RID p_light) const override;
virtual RS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) override;
virtual RS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) override;
virtual RS::LightType light_get_type(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->type;
}
virtual AABB light_get_aabb(RID p_light) const override;
virtual float light_get_param(RID p_light, RS::LightParam p_param) override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, 0);
return light->param[p_param];
}
_FORCE_INLINE_ RID light_get_projector(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RID());
return light->projector;
}
virtual Color light_get_color(RID p_light) override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, Color());
return light->color;
}
_FORCE_INLINE_ bool light_is_distance_fade_enabled(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade;
}
_FORCE_INLINE_ float light_get_distance_fade_begin(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_begin;
}
_FORCE_INLINE_ float light_get_distance_fade_shadow(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_shadow;
}
_FORCE_INLINE_ float light_get_distance_fade_length(RID p_light) {
const Light *light = light_owner.get_or_null(p_light);
return light->distance_fade_length;
}
virtual bool light_has_shadow(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->shadow;
}
virtual bool light_has_projector(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return TextureStorage::get_singleton()->owns_texture(light->projector);
}
_FORCE_INLINE_ bool light_is_negative(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, RS::LIGHT_DIRECTIONAL);
return light->negative;
}
_FORCE_INLINE_ float light_get_transmittance_bias(RID p_light) const {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, 0.0);
return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
}
virtual bool light_get_reverse_cull_face_mode(RID p_light) const override {
const Light *light = light_owner.get_or_null(p_light);
ERR_FAIL_NULL_V(light, false);
return light->reverse_cull;
}
virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override;
virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; }
virtual uint64_t light_get_version(RID p_light) const override;
virtual uint32_t light_get_cull_mask(RID p_light) const override;
/* LIGHT INSTANCE API */
LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }
bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }
virtual RID light_instance_create(RID p_light) override;
virtual void light_instance_free(RID p_light_instance) override;
virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override;
virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override;
virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override;
virtual void light_instance_mark_visible(RID p_light_instance) override;
virtual bool light_instance_is_shadow_visible_at_position(RID p_light_instance, const Vector3 &p_position) const override {
const LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_NULL_V(light_instance, false);
const Light *light = light_owner.get_or_null(light_instance->light);
ERR_FAIL_NULL_V(light, false);
if (!light->shadow) {
return false;
}
if (!light->distance_fade) {
return true;
}
real_t distance = p_position.distance_to(light_instance->transform.origin);
if (distance > light->distance_fade_shadow + light->distance_fade_length) {
return false;
}
return true;
}
_FORCE_INLINE_ RID light_instance_get_base_light(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light;
}
_FORCE_INLINE_ Transform3D light_instance_get_base_transform(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->transform;
}
_FORCE_INLINE_ AABB light_instance_get_base_aabb(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->aabb;
}
_FORCE_INLINE_ void light_instance_set_cull_mask(RID p_light_instance, uint32_t p_cull_mask) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->cull_mask = p_cull_mask;
}
_FORCE_INLINE_ uint32_t light_instance_get_cull_mask(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->cull_mask;
}
_FORCE_INLINE_ GLuint light_instance_get_shadow_texture(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t shadow = key & SHADOW_INDEX_MASK;
ERR_FAIL_COND_V(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size(), 0);
return shadow_atlas_get_quadrant_shadow_texture(p_shadow_atlas, quadrant, shadow);
}
_FORCE_INLINE_ bool light_instance_has_shadow_atlas(RID p_light_instance, RID p_shadow_atlas) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_atlases.has(p_shadow_atlas);
}
_FORCE_INLINE_ float light_instance_get_shadow_texel_size(RID p_light_instance, RID p_shadow_atlas) {
#ifdef DEBUG_ENABLED
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
ERR_FAIL_COND_V(!li->shadow_atlases.has(p_shadow_atlas), 0);
#endif
ShadowAtlas *shadow_atlas = shadow_atlas_owner.get_or_null(p_shadow_atlas);
ERR_FAIL_NULL_V(shadow_atlas, 0);
#ifdef DEBUG_ENABLED
ERR_FAIL_COND_V(!shadow_atlas->shadow_owners.has(p_light_instance), 0);
#endif
uint32_t key = shadow_atlas->shadow_owners[p_light_instance];
uint32_t quadrant = (key >> QUADRANT_SHIFT) & 0x3;
uint32_t quadrant_size = shadow_atlas->size >> 1;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
return float(1.0) / shadow_size;
}
_FORCE_INLINE_ Projection light_instance_get_shadow_camera(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].camera;
}
_FORCE_INLINE_ Transform3D light_instance_get_shadow_transform(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].transform;
}
_FORCE_INLINE_ float light_instance_get_shadow_bias_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].bias_scale;
}
_FORCE_INLINE_ float light_instance_get_shadow_range(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].farplane;
}
_FORCE_INLINE_ float light_instance_get_shadow_range_begin(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].range_begin;
}
_FORCE_INLINE_ Vector2 light_instance_get_shadow_uv_scale(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].uv_scale;
}
_FORCE_INLINE_ void light_instance_set_directional_shadow_atlas_rect(RID p_light_instance, int p_index, const Rect2 p_atlas_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->shadow_transform[p_index].atlas_rect = p_atlas_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_shadow_atlas_rect(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].atlas_rect;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_split(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].split;
}
_FORCE_INLINE_ float light_instance_get_directional_shadow_texel_size(RID p_light_instance, int p_index) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_transform[p_index].shadow_texel_size;
}
_FORCE_INLINE_ void light_instance_set_render_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_render_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_pass;
}
_FORCE_INLINE_ void light_instance_set_shadow_pass(RID p_light_instance, uint64_t p_pass) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->last_scene_shadow_pass = p_pass;
}
_FORCE_INLINE_ uint64_t light_instance_get_shadow_pass(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->last_scene_shadow_pass;
}
_FORCE_INLINE_ void light_instance_set_directional_rect(RID p_light_instance, const Rect2 &p_directional_rect) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
li->directional_rect = p_directional_rect;
}
_FORCE_INLINE_ Rect2 light_instance_get_directional_rect(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->directional_rect;
}
_FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->light_type;
}
_FORCE_INLINE_ int32_t light_instance_get_gl_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->gl_id;
}
_FORCE_INLINE_ int32_t light_instance_get_shadow_id(RID p_light_instance) {
LightInstance *li = light_instance_owner.get_or_null(p_light_instance);
return li->shadow_id;
}
/* PROBE API */
ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); }
bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); }
virtual RID reflection_probe_allocate() override;
virtual void reflection_probe_initialize(RID p_rid) override;
virtual void reflection_probe_free(RID p_rid) override;
virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) override;
virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) override;
virtual void reflection_probe_set_blend_distance(RID p_probe, float p_blend_distance) override;
virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) override;
virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) override;
virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) override;
virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) override;
virtual void reflection_probe_set_size(RID p_probe, const Vector3 &p_size) override;
virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) override;
virtual void reflection_probe_set_as_interior(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) override;
virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) override;
virtual void reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) override;
virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override;
virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override;
virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override;
virtual AABB reflection_probe_get_aabb(RID p_probe) const override;
virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override;
virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override;
virtual uint32_t reflection_probe_get_reflection_mask(RID p_probe) const override;
virtual Vector3 reflection_probe_get_size(RID p_probe) const override;
virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const override;
virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override;
virtual bool reflection_probe_renders_shadows(RID p_probe) const override;
Dependency *reflection_probe_get_dependency(RID p_probe) const;
/* REFLECTION ATLAS */
bool owns_reflection_atlas(RID p_rid) { return reflection_atlas_owner.owns(p_rid); }
virtual RID reflection_atlas_create() override;
virtual void reflection_atlas_free(RID p_ref_atlas) override;
virtual int reflection_atlas_get_size(RID p_ref_atlas) const override;
virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override;
/* REFLECTION PROBE INSTANCE */
bool owns_reflection_probe_instance(RID p_rid) { return reflection_probe_instance_owner.owns(p_rid); }
virtual RID reflection_probe_instance_create(RID p_probe) override;
virtual void reflection_probe_instance_free(RID p_instance) override;
virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
virtual bool reflection_probe_has_atlas_index(RID p_instance) override;
virtual void reflection_probe_release_atlas_index(RID p_instance) override;
virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override;
virtual bool reflection_probe_instance_has_reflection(RID p_instance) override;
virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override;
virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
_FORCE_INLINE_ RID reflection_probe_instance_get_probe(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, RID());
return rpi->probe;
}
_FORCE_INLINE_ RID reflection_probe_instance_get_atlas(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, RID());
return rpi->atlas;
}
Transform3D reflection_probe_instance_get_transform(RID p_instance) {
ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
ERR_FAIL_NULL_V(rpi, Transform3D());
return rpi->transform;
}
GLuint reflection_probe_instance_get_texture(RID p_instance);
GLuint reflection_probe_instance_get_framebuffer(RID p_instance, int p_index);
/* LIGHTMAP CAPTURE */
Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }
bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }
virtual RID lightmap_allocate() override;
virtual void lightmap_initialize(RID p_rid) override;
virtual void lightmap_free(RID p_rid) override;
virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) override;
virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override;
virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override;
virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override;
virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override;
virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override;
virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override;
virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override;
virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const override;
virtual AABB lightmap_get_aabb(RID p_lightmap) const override;
virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override;
virtual bool lightmap_is_interior(RID p_lightmap) const override;
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override;
virtual float lightmap_get_probe_capture_update_speed() const override;
virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override;
virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override;
virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override;
/* LIGHTMAP INSTANCE */
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }
virtual RID lightmap_instance_create(RID p_lightmap) override;
virtual void lightmap_instance_free(RID p_lightmap) override;
virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override;
/* SHADOW ATLAS API */
bool owns_shadow_atlas(RID p_rid) { return shadow_atlas_owner.owns(p_rid); }
virtual RID shadow_atlas_create() override;
virtual void shadow_atlas_free(RID p_atlas) override;
virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override;
virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override;
virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_instance, float p_coverage, uint64_t p_light_version) override;
_FORCE_INLINE_ bool shadow_atlas_owns_light_instance(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
return atlas->shadow_owners.has(p_light_instance);
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_light_instance_key(RID p_atlas, RID p_light_instance) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, -1);
return atlas->shadow_owners[p_light_instance];
}
_FORCE_INLINE_ int shadow_atlas_get_size(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
return atlas->size;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_fb(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_fbo != 0) {
return atlas->debug_fbo;
}
glGenFramebuffers(1, &atlas->debug_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, atlas->debug_fbo);
if (atlas->debug_texture == 0) {
atlas->debug_texture = shadow_atlas_get_debug_texture(p_atlas);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, atlas->debug_texture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
return atlas->debug_fbo;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_debug_texture(RID p_atlas) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
if (atlas->debug_texture != 0) {
return atlas->debug_texture;
}
glGenTextures(1, &atlas->debug_texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, atlas->debug_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, atlas->size, atlas->size, 0, GL_RED, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
glBindTexture(GL_TEXTURE_2D, 0);
return atlas->debug_texture;
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadows_length(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].shadows.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_shadows_allocated(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].textures.size();
}
_FORCE_INLINE_ uint32_t shadow_atlas_get_quadrant_subdivision(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_texture(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].textures.size(), 0);
return atlas->quadrants[p_quadrant].textures[p_shadow];
}
_FORCE_INLINE_ GLuint shadow_atlas_get_quadrant_shadow_fb(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, atlas->quadrants[p_quadrant].fbos.size(), 0);
return atlas->quadrants[p_quadrant].fbos[p_shadow];
}
_FORCE_INLINE_ int shadow_atlas_get_quadrant_shadow_size(RID p_atlas, uint32_t p_quadrant) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, 0);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, 0);
return (atlas->size >> 1) / atlas->quadrants[p_quadrant].subdivision;
}
_FORCE_INLINE_ bool shadow_atlas_get_quadrant_shadow_is_omni(RID p_atlas, uint32_t p_quadrant, uint32_t p_shadow) {
ShadowAtlas *atlas = shadow_atlas_owner.get_or_null(p_atlas);
ERR_FAIL_NULL_V(atlas, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_quadrant, 4, false);
ERR_FAIL_UNSIGNED_INDEX_V(p_shadow, (uint32_t)atlas->quadrants[p_quadrant].shadows.size(), false);
return atlas->quadrants[p_quadrant].shadows[p_shadow].owner_is_omni;
}
virtual void shadow_atlas_update(RID p_atlas) override;
virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override;
virtual int get_directional_light_shadow_size(RID p_light_instance) override;
virtual void set_directional_shadow_count(int p_count) override;
Rect2i get_directional_shadow_rect();
void update_directional_shadow_atlas();
_FORCE_INLINE_ GLuint directional_shadow_get_texture() {
return directional_shadow.depth;
}
_FORCE_INLINE_ int directional_shadow_get_size() {
return directional_shadow.size;
}
_FORCE_INLINE_ GLuint direction_shadow_get_fb() {
return directional_shadow.fbo;
}
_FORCE_INLINE_ void directional_shadow_increase_current_light() {
directional_shadow.current_light++;
}
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // LIGHT_STORAGE_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,638 @@
/**************************************************************************/
/* material_storage.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MATERIAL_STORAGE_GLES3_H
#define MATERIAL_STORAGE_GLES3_H
#ifdef GLES3_ENABLED
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "servers/rendering/shader_compiler.h"
#include "servers/rendering/shader_language.h"
#include "servers/rendering/storage/material_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "drivers/gles3/shaders/canvas.glsl.gen.h"
#include "drivers/gles3/shaders/particles.glsl.gen.h"
#include "drivers/gles3/shaders/scene.glsl.gen.h"
#include "drivers/gles3/shaders/sky.glsl.gen.h"
namespace GLES3 {
/* Shader Structs */
struct ShaderData {
String path;
HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
HashMap<StringName, HashMap<int, RID>> default_texture_params;
virtual void set_path_hint(const String &p_hint);
virtual void set_default_texture_parameter(const StringName &p_name, RID p_texture, int p_index);
virtual Variant get_default_parameter(const StringName &p_parameter) const;
virtual void get_shader_uniform_list(List<PropertyInfo> *p_param_list) const;
virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const;
virtual bool is_parameter_texture(const StringName &p_param) const;
virtual void set_code(const String &p_Code) = 0;
virtual bool is_animated() const = 0;
virtual bool casts_shadows() const = 0;
virtual RS::ShaderNativeSourceCode get_native_source_code() const { return RS::ShaderNativeSourceCode(); }
virtual ~ShaderData() {}
};
typedef ShaderData *(*ShaderDataRequestFunction)();
struct Material;
struct Shader {
ShaderData *data = nullptr;
String code;
String path_hint;
RS::ShaderMode mode;
HashMap<StringName, HashMap<int, RID>> default_texture_parameter;
HashSet<Material *> owners;
};
/* Material structs */
struct MaterialData {
void update_uniform_buffer(const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const HashMap<StringName, Variant> &p_parameters, uint8_t *p_buffer, uint32_t p_buffer_size);
void update_textures(const HashMap<StringName, Variant> &p_parameters, const HashMap<StringName, HashMap<int, RID>> &p_default_textures, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, RID *p_textures, bool p_use_linear_color);
virtual void set_render_priority(int p_priority) = 0;
virtual void set_next_pass(RID p_pass) = 0;
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) = 0;
virtual void bind_uniforms() = 0;
virtual ~MaterialData();
// Used internally by all Materials
void update_parameters_internal(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, bool p_is_3d_shader_type);
protected:
Vector<uint8_t> ubo_data;
GLuint uniform_buffer = GLuint(0);
Vector<RID> texture_cache;
private:
friend class MaterialStorage;
RID self;
List<RID>::Element *global_buffer_E = nullptr;
List<RID>::Element *global_texture_E = nullptr;
uint64_t global_textures_pass = 0;
HashMap<StringName, uint64_t> used_global_textures;
};
typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *);
struct Material {
RID self;
MaterialData *data = nullptr;
Shader *shader = nullptr;
//shortcut to shader data and type
RS::ShaderMode shader_mode = RS::SHADER_MAX;
uint32_t shader_id = 0;
bool uniform_dirty = false;
bool texture_dirty = false;
HashMap<StringName, Variant> params;
int32_t priority = 0;
RID next_pass;
SelfList<Material> update_element;
Dependency dependency;
Material() :
update_element(this) {}
};
/* CanvasItem Materials */
struct CanvasShaderData : public ShaderData {
enum BlendMode { // Used internally.
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PMALPHA,
BLEND_MODE_DISABLED,
BLEND_MODE_LCD,
};
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
BlendMode blend_mode;
bool uses_screen_texture;
bool uses_screen_texture_mipmaps;
bool uses_sdf;
bool uses_time;
bool uses_custom0;
bool uses_custom1;
uint64_t vertex_input_mask;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
CanvasShaderData();
virtual ~CanvasShaderData();
};
ShaderData *_create_canvas_shader_func();
struct CanvasMaterialData : public MaterialData {
CanvasShaderData *shader_data = nullptr;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~CanvasMaterialData();
};
MaterialData *_create_canvas_material_func(ShaderData *p_shader);
/* Sky Materials */
struct SkyShaderData : public ShaderData {
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
bool uses_time;
bool uses_position;
bool uses_half_res;
bool uses_quarter_res;
bool uses_light;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
SkyShaderData();
virtual ~SkyShaderData();
};
ShaderData *_create_sky_shader_func();
struct SkyMaterialData : public MaterialData {
SkyShaderData *shader_data = nullptr;
bool uniform_set_updated = false;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~SkyMaterialData();
};
MaterialData *_create_sky_material_func(ShaderData *p_shader);
/* Scene Materials */
struct SceneShaderData : public ShaderData {
enum BlendMode { // Used internally.
BLEND_MODE_MIX,
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PREMULT_ALPHA,
BLEND_MODE_ALPHA_TO_COVERAGE
};
enum DepthDraw {
DEPTH_DRAW_DISABLED,
DEPTH_DRAW_OPAQUE,
DEPTH_DRAW_ALWAYS
};
enum DepthTest {
DEPTH_TEST_DISABLED,
DEPTH_TEST_ENABLED
};
enum AlphaAntiAliasing {
ALPHA_ANTIALIASING_OFF,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
};
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
BlendMode blend_mode;
AlphaAntiAliasing alpha_antialiasing_mode;
DepthDraw depth_draw;
DepthTest depth_test;
RS::CullMode cull_mode;
bool uses_point_size;
bool uses_alpha;
bool uses_alpha_clip;
bool uses_blend_alpha;
bool uses_depth_prepass_alpha;
bool uses_discard;
bool uses_roughness;
bool uses_normal;
bool uses_particle_trails;
bool wireframe;
bool unshaded;
bool uses_vertex;
bool uses_position;
bool uses_sss;
bool uses_transmittance;
bool uses_screen_texture;
bool uses_screen_texture_mipmaps;
bool uses_depth_texture;
bool uses_normal_texture;
bool uses_time;
bool uses_vertex_time;
bool uses_fragment_time;
bool writes_modelview_or_projection;
bool uses_world_coordinates;
bool uses_tangent;
bool uses_color;
bool uses_uv;
bool uses_uv2;
bool uses_custom0;
bool uses_custom1;
bool uses_custom2;
bool uses_custom3;
bool uses_bones;
bool uses_weights;
uint64_t vertex_input_mask;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
SceneShaderData();
virtual ~SceneShaderData();
};
ShaderData *_create_scene_shader_func();
struct SceneMaterialData : public MaterialData {
SceneShaderData *shader_data = nullptr;
uint64_t last_pass = 0;
uint32_t index = 0;
RID next_pass;
uint8_t priority = 0;
virtual void set_render_priority(int p_priority);
virtual void set_next_pass(RID p_pass);
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~SceneMaterialData();
};
MaterialData *_create_scene_material_func(ShaderData *p_shader);
/* Particle Shader */
enum {
PARTICLES_MAX_USERDATAS = 6
};
struct ParticlesShaderData : public ShaderData {
// All these members are (re)initialized in `set_code`.
// Make sure to add the init to `set_code` whenever adding new members.
bool valid;
RID version;
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;
Vector<uint32_t> ubo_offsets;
uint32_t ubo_size;
String code;
bool uses_collision;
bool uses_time;
bool userdatas_used[PARTICLES_MAX_USERDATAS] = {};
uint32_t userdata_count;
virtual void set_code(const String &p_Code);
virtual bool is_animated() const;
virtual bool casts_shadows() const;
virtual RS::ShaderNativeSourceCode get_native_source_code() const;
ParticlesShaderData() {}
virtual ~ParticlesShaderData();
};
ShaderData *_create_particles_shader_func();
struct ParticleProcessMaterialData : public MaterialData {
ParticlesShaderData *shader_data = nullptr;
RID uniform_set;
virtual void set_render_priority(int p_priority) {}
virtual void set_next_pass(RID p_pass) {}
virtual void update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty);
virtual void bind_uniforms();
virtual ~ParticleProcessMaterialData();
};
MaterialData *_create_particles_material_func(ShaderData *p_shader);
/* Global shader uniform structs */
struct GlobalShaderUniforms {
enum {
BUFFER_DIRTY_REGION_SIZE = 1024
};
struct Variable {
HashSet<RID> texture_materials; // materials using this
RS::GlobalShaderParameterType type;
Variant value;
Variant override;
int32_t buffer_index; //for vectors
int32_t buffer_elements; //for vectors
};
HashMap<StringName, Variable> variables;
struct Value {
float x;
float y;
float z;
float w;
};
struct ValueInt {
int32_t x;
int32_t y;
int32_t z;
int32_t w;
};
struct ValueUInt {
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t w;
};
struct ValueUsage {
uint32_t elements = 0;
};
List<RID> materials_using_buffer;
List<RID> materials_using_texture;
GLuint buffer = GLuint(0);
Value *buffer_values = nullptr;
ValueUsage *buffer_usage = nullptr;
bool *buffer_dirty_regions = nullptr;
uint32_t buffer_dirty_region_count = 0;
uint32_t buffer_size;
bool must_update_texture_materials = false;
bool must_update_buffer_materials = false;
HashMap<RID, int32_t> instance_buffer_pos;
};
class MaterialStorage : public RendererMaterialStorage {
private:
friend struct MaterialData;
static MaterialStorage *singleton;
/* GLOBAL SHADER UNIFORM API */
GlobalShaderUniforms global_shader_uniforms;
int32_t _global_shader_uniform_allocate(uint32_t p_elements);
void _global_shader_uniform_store_in_buffer(int32_t p_index, RS::GlobalShaderParameterType p_type, const Variant &p_value);
void _global_shader_uniform_mark_buffer_dirty(int32_t p_index, int32_t p_elements);
/* SHADER API */
ShaderDataRequestFunction shader_data_request_func[RS::SHADER_MAX];
mutable RID_Owner<Shader, true> shader_owner;
/* MATERIAL API */
MaterialDataRequestFunction material_data_request_func[RS::SHADER_MAX];
mutable RID_Owner<Material, true> material_owner;
SelfList<Material>::List material_update_list;
public:
static MaterialStorage *get_singleton();
MaterialStorage();
virtual ~MaterialStorage();
static _FORCE_INLINE_ void store_transform(const Transform3D &p_mtx, float *p_array) {
p_array[0] = p_mtx.basis.rows[0][0];
p_array[1] = p_mtx.basis.rows[1][0];
p_array[2] = p_mtx.basis.rows[2][0];
p_array[3] = 0;
p_array[4] = p_mtx.basis.rows[0][1];
p_array[5] = p_mtx.basis.rows[1][1];
p_array[6] = p_mtx.basis.rows[2][1];
p_array[7] = 0;
p_array[8] = p_mtx.basis.rows[0][2];
p_array[9] = p_mtx.basis.rows[1][2];
p_array[10] = p_mtx.basis.rows[2][2];
p_array[11] = 0;
p_array[12] = p_mtx.origin.x;
p_array[13] = p_mtx.origin.y;
p_array[14] = p_mtx.origin.z;
p_array[15] = 1;
}
static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_mtx, float *p_array) {
p_array[0] = p_mtx.rows[0][0];
p_array[1] = p_mtx.rows[1][0];
p_array[2] = p_mtx.rows[2][0];
p_array[3] = 0;
p_array[4] = p_mtx.rows[0][1];
p_array[5] = p_mtx.rows[1][1];
p_array[6] = p_mtx.rows[2][1];
p_array[7] = 0;
p_array[8] = p_mtx.rows[0][2];
p_array[9] = p_mtx.rows[1][2];
p_array[10] = p_mtx.rows[2][2];
p_array[11] = 0;
}
static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
p_array[i * 4 + j] = p_mtx.columns[i][j];
}
}
}
struct Shaders {
CanvasShaderGLES3 canvas_shader;
SkyShaderGLES3 sky_shader;
SceneShaderGLES3 scene_shader;
ParticlesShaderGLES3 particles_process_shader;
ShaderCompiler compiler_canvas;
ShaderCompiler compiler_scene;
ShaderCompiler compiler_particles;
ShaderCompiler compiler_sky;
} shaders;
/* GLOBAL SHADER UNIFORM API */
void _update_global_shader_uniforms();
virtual void global_shader_parameter_add(const StringName &p_name, RS::GlobalShaderParameterType p_type, const Variant &p_value) override;
virtual void global_shader_parameter_remove(const StringName &p_name) override;
virtual Vector<StringName> global_shader_parameter_get_list() const override;
virtual void global_shader_parameter_set(const StringName &p_name, const Variant &p_value) override;
virtual void global_shader_parameter_set_override(const StringName &p_name, const Variant &p_value) override;
virtual Variant global_shader_parameter_get(const StringName &p_name) const override;
virtual RS::GlobalShaderParameterType global_shader_parameter_get_type(const StringName &p_name) const override;
RS::GlobalShaderParameterType global_shader_parameter_get_type_internal(const StringName &p_name) const;
virtual void global_shader_parameters_load_settings(bool p_load_textures = true) override;
virtual void global_shader_parameters_clear() override;
virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override;
virtual void global_shader_parameters_instance_free(RID p_instance) override;
virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override;
GLuint global_shader_parameters_get_uniform_buffer() const;
/* SHADER API */
Shader *get_shader(RID p_rid) { return shader_owner.get_or_null(p_rid); }
bool owns_shader(RID p_rid) { return shader_owner.owns(p_rid); }
void _shader_make_dirty(Shader *p_shader);
virtual RID shader_allocate() override;
virtual void shader_initialize(RID p_rid) override;
virtual void shader_free(RID p_rid) override;
virtual void shader_set_code(RID p_shader, const String &p_code) override;
virtual void shader_set_path_hint(RID p_shader, const String &p_path) override;
virtual String shader_get_code(RID p_shader) const override;
virtual void get_shader_parameter_list(RID p_shader, List<PropertyInfo> *p_param_list) const override;
virtual void shader_set_default_texture_parameter(RID p_shader, const StringName &p_name, RID p_texture, int p_index) override;
virtual RID shader_get_default_texture_parameter(RID p_shader, const StringName &p_name, int p_index) const override;
virtual Variant shader_get_parameter_default(RID p_shader, const StringName &p_name) const override;
virtual RS::ShaderNativeSourceCode shader_get_native_source_code(RID p_shader) const override;
/* MATERIAL API */
Material *get_material(RID p_rid) { return material_owner.get_or_null(p_rid); }
bool owns_material(RID p_rid) { return material_owner.owns(p_rid); }
void _material_queue_update(Material *material, bool p_uniform, bool p_texture);
void _update_queued_materials();
virtual RID material_allocate() override;
virtual void material_initialize(RID p_rid) override;
virtual void material_free(RID p_rid) override;
virtual void material_set_shader(RID p_material, RID p_shader) override;
virtual void material_set_param(RID p_material, const StringName &p_param, const Variant &p_value) override;
virtual Variant material_get_param(RID p_material, const StringName &p_param) const override;
virtual void material_set_next_pass(RID p_material, RID p_next_material) override;
virtual void material_set_render_priority(RID p_material, int priority) override;
virtual bool material_is_animated(RID p_material) override;
virtual bool material_casts_shadows(RID p_material) override;
virtual RS::CullMode material_get_cull_mode(RID p_material) const override;
virtual void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters) override;
virtual void material_update_dependency(RID p_material, DependencyTracker *p_instance) override;
_FORCE_INLINE_ uint32_t material_get_shader_id(RID p_material) {
Material *material = material_owner.get_or_null(p_material);
return material->shader_id;
}
_FORCE_INLINE_ MaterialData *material_get_data(RID p_material, RS::ShaderMode p_shader_mode) {
Material *material = material_owner.get_or_null(p_material);
if (!material || material->shader_mode != p_shader_mode) {
return nullptr;
} else {
return material->data;
}
}
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // MATERIAL_STORAGE_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,608 @@
/**************************************************************************/
/* mesh_storage.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef MESH_STORAGE_GLES3_H
#define MESH_STORAGE_GLES3_H
#ifdef GLES3_ENABLED
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "drivers/gles3/shaders/skeleton.glsl.gen.h"
#include "servers/rendering/storage/mesh_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
struct MeshInstance;
struct Mesh {
struct Surface {
struct Attrib {
bool enabled;
bool integer;
GLint size;
GLenum type;
GLboolean normalized;
GLsizei stride;
uint32_t offset;
};
RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
uint64_t format = 0;
GLuint vertex_buffer = 0;
GLuint attribute_buffer = 0;
GLuint skin_buffer = 0;
uint32_t vertex_count = 0;
uint32_t vertex_buffer_size = 0;
uint32_t attribute_buffer_size = 0;
uint32_t skin_buffer_size = 0;
// Cache vertex arrays so they can be created
struct Version {
uint32_t input_mask = 0;
GLuint vertex_array = 0;
Attrib attribs[RS::ARRAY_MAX];
};
SpinLock version_lock; //needed to access versions
Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
GLuint index_buffer = 0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
struct Wireframe {
GLuint index_buffer = 0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
};
Wireframe *wireframe = nullptr;
struct LOD {
float edge_length = 0.0;
uint32_t index_count = 0;
uint32_t index_buffer_size = 0;
GLuint index_buffer = 0;
};
LOD *lods = nullptr;
uint32_t lod_count = 0;
AABB aabb;
Vector<AABB> bone_aabbs;
// Transform used in runtime bone AABBs compute.
// As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space.
Transform3D mesh_to_skeleton_xform;
Vector4 uv_scale;
struct BlendShape {
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
};
BlendShape *blend_shapes = nullptr;
GLuint skeleton_vertex_array = 0;
RID material;
};
uint32_t blend_shape_count = 0;
RS::BlendShapeMode blend_shape_mode = RS::BLEND_SHAPE_MODE_NORMALIZED;
Surface **surfaces = nullptr;
uint32_t surface_count = 0;
bool has_bone_weights = false;
AABB aabb;
AABB custom_aabb;
uint64_t skeleton_aabb_version = 0;
Vector<RID> material_cache;
List<MeshInstance *> instances;
RID shadow_mesh;
HashSet<Mesh *> shadow_owners;
String path;
Dependency dependency;
};
/* Mesh Instance */
struct MeshInstance {
Mesh *mesh = nullptr;
RID skeleton;
struct Surface {
GLuint vertex_buffers[2] = { 0, 0 };
GLuint vertex_arrays[2] = { 0, 0 };
GLuint vertex_buffer = 0;
int vertex_stride_cache = 0;
int vertex_size_cache = 0;
int vertex_normal_offset_cache = 0;
int vertex_tangent_offset_cache = 0;
uint64_t format_cache = 0;
Mesh::Surface::Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
};
LocalVector<Surface> surfaces;
LocalVector<float> blend_weights;
List<MeshInstance *>::Element *I = nullptr; //used to erase itself
uint64_t skeleton_version = 0;
bool dirty = false;
bool weights_dirty = false;
SelfList<MeshInstance> weight_update_list;
SelfList<MeshInstance> array_update_list;
Transform2D canvas_item_transform_2d;
MeshInstance() :
weight_update_list(this), array_update_list(this) {}
};
/* MultiMesh */
struct MultiMesh {
RID mesh;
int instances = 0;
RS::MultimeshTransformFormat xform_format = RS::MULTIMESH_TRANSFORM_3D;
bool uses_colors = false;
bool uses_custom_data = false;
int visible_instances = -1;
AABB aabb;
AABB custom_aabb;
bool aabb_dirty = false;
bool buffer_set = false;
uint32_t stride_cache = 0;
uint32_t color_offset_cache = 0;
uint32_t custom_data_offset_cache = 0;
Vector<float> data_cache; //used if individual setting is used
bool *data_cache_dirty_regions = nullptr;
uint32_t data_cache_used_dirty_regions = 0;
GLuint buffer = 0;
bool dirty = false;
MultiMesh *dirty_list = nullptr;
RendererMeshStorage::MultiMeshInterpolator interpolator;
Dependency dependency;
};
struct Skeleton {
bool use_2d = false;
int size = 0;
int height = 0;
LocalVector<float> data;
bool dirty = false;
Skeleton *dirty_list = nullptr;
Transform2D base_transform_2d;
GLuint transforms_texture = 0;
uint64_t version = 1;
Dependency dependency;
};
class MeshStorage : public RendererMeshStorage {
private:
static MeshStorage *singleton;
struct {
SkeletonShaderGLES3 shader;
RID shader_version;
} skeleton_shader;
/* Mesh */
mutable RID_Owner<Mesh, true> mesh_owner;
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr);
void _mesh_surface_clear(Mesh *mesh, int p_surface);
/* Mesh Instance API */
mutable RID_Owner<MeshInstance> mesh_instance_owner;
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
void _mesh_instance_remove_surface(MeshInstance *mi, int p_surface);
void _blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface);
SelfList<MeshInstance>::List dirty_mesh_instance_weights;
SelfList<MeshInstance>::List dirty_mesh_instance_arrays;
/* MultiMesh */
mutable RID_Owner<MultiMesh, true> multimesh_owner;
MultiMesh *multimesh_dirty_list = nullptr;
_FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const;
_FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb);
_FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb);
_FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances);
/* Skeleton */
mutable RID_Owner<Skeleton, true> skeleton_owner;
_FORCE_INLINE_ void _skeleton_make_dirty(Skeleton *skeleton);
void _compute_skeleton(MeshInstance *p_mi, Skeleton *p_sk, uint32_t p_surface);
Skeleton *skeleton_dirty_list = nullptr;
public:
static MeshStorage *get_singleton();
MeshStorage();
virtual ~MeshStorage();
/* MESH API */
Mesh *get_mesh(RID p_rid) { return mesh_owner.get_or_null(p_rid); }
bool owns_mesh(RID p_rid) { return mesh_owner.owns(p_rid); }
virtual RID mesh_allocate() override;
virtual void mesh_initialize(RID p_rid) override;
virtual void mesh_free(RID p_rid) override;
virtual void mesh_set_blend_shape_count(RID p_mesh, int p_blend_shape_count) override;
virtual bool mesh_needs_instance(RID p_mesh, bool p_has_skeleton) override;
virtual void mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) override;
virtual int mesh_get_blend_shape_count(RID p_mesh) const override;
virtual void mesh_set_blend_shape_mode(RID p_mesh, RS::BlendShapeMode p_mode) override;
virtual RS::BlendShapeMode mesh_get_blend_shape_mode(RID p_mesh) const override;
virtual void mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) override;
virtual void mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) override;
virtual RID mesh_surface_get_material(RID p_mesh, int p_surface) const override;
virtual RS::SurfaceData mesh_get_surface(RID p_mesh, int p_surface) const override;
virtual int mesh_get_surface_count(RID p_mesh) const override;
virtual void mesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) override;
virtual AABB mesh_get_custom_aabb(RID p_mesh) const override;
virtual AABB mesh_get_aabb(RID p_mesh, RID p_skeleton = RID()) override;
virtual void mesh_set_path(RID p_mesh, const String &p_path) override;
virtual String mesh_get_path(RID p_mesh) const override;
virtual void mesh_set_shadow_mesh(RID p_mesh, RID p_shadow_mesh) override;
virtual void mesh_clear(RID p_mesh) override;
virtual void mesh_surface_remove(RID p_mesh, int p_surface) override;
_FORCE_INLINE_ const RID *mesh_get_surface_count_and_materials(RID p_mesh, uint32_t &r_surface_count) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, nullptr);
r_surface_count = mesh->surface_count;
if (r_surface_count == 0) {
return nullptr;
}
if (mesh->material_cache.is_empty()) {
mesh->material_cache.resize(mesh->surface_count);
for (uint32_t i = 0; i < r_surface_count; i++) {
mesh->material_cache.write[i] = mesh->surfaces[i]->material;
}
}
return mesh->material_cache.ptr();
}
_FORCE_INLINE_ void *mesh_get_surface(RID p_mesh, uint32_t p_surface_index) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, nullptr);
ERR_FAIL_UNSIGNED_INDEX_V(p_surface_index, mesh->surface_count, nullptr);
return mesh->surfaces[p_surface_index];
}
_FORCE_INLINE_ RID mesh_get_shadow_mesh(RID p_mesh) {
Mesh *mesh = mesh_owner.get_or_null(p_mesh);
ERR_FAIL_NULL_V(mesh, RID());
return mesh->shadow_mesh;
}
_FORCE_INLINE_ RS::PrimitiveType mesh_surface_get_primitive(void *p_surface) {
Mesh::Surface *surface = reinterpret_cast<Mesh::Surface *>(p_surface);
return surface->primitive;
}
_FORCE_INLINE_ bool mesh_surface_has_lod(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->lod_count > 0;
}
_FORCE_INLINE_ uint32_t mesh_surface_get_vertices_drawn_count(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->index_count ? s->index_count : s->vertex_count;
}
_FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
ERR_FAIL_NULL_V(s, 0);
int32_t current_lod = -1;
r_index_count = s->index_count;
for (uint32_t i = 0; i < s->lod_count; i++) {
float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold;
if (screen_size > p_mesh_lod_threshold) {
break;
}
current_lod = i;
}
if (current_lod == -1) {
return 0;
} else {
r_index_count = s->lods[current_lod].index_count;
return current_lod + 1;
}
}
_FORCE_INLINE_ GLuint mesh_surface_get_index_buffer(void *p_surface, uint32_t p_lod) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
if (p_lod == 0) {
return s->index_buffer;
} else {
return s->lods[p_lod - 1].index_buffer;
}
}
_FORCE_INLINE_ GLuint mesh_surface_get_index_buffer_wireframe(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
if (s->wireframe) {
return s->wireframe->index_buffer;
}
return 0;
}
_FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return (s->vertex_count <= 65536 && s->vertex_count > 0) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
}
// Use this to cache Vertex Array Objects so they are only generated once
_FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
s->version_lock.lock();
// There will never be more than 3 or 4 versions, so iterating is the fastest way.
for (uint32_t i = 0; i < s->version_count; i++) {
if (s->versions[i].input_mask != p_input_mask) {
continue;
}
// We have this version, hooray.
r_vertex_array_gl = s->versions[i].vertex_array;
s->version_lock.unlock();
return;
}
uint32_t version = s->version_count;
s->version_count++;
s->versions = (Mesh::Surface::Version *)memrealloc(s->versions, sizeof(Mesh::Surface::Version) * s->version_count);
_mesh_surface_generate_version_for_input_mask(s->versions[version], s, p_input_mask);
r_vertex_array_gl = s->versions[version].vertex_array;
s->version_lock.unlock();
}
/* MESH INSTANCE API */
MeshInstance *get_mesh_instance(RID p_rid) { return mesh_instance_owner.get_or_null(p_rid); }
bool owns_mesh_instance(RID p_rid) { return mesh_instance_owner.owns(p_rid); }
virtual RID mesh_instance_create(RID p_base) override;
virtual void mesh_instance_free(RID p_rid) override;
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override;
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override;
virtual void mesh_instance_check_for_update(RID p_mesh_instance) override;
virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override;
virtual void update_mesh_instances() override;
// TODO: considering hashing versions with multimesh buffer RID.
// Doing so would allow us to avoid specifying multimesh buffer pointers every frame and may improve performance.
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
ERR_FAIL_NULL(mi);
Mesh *mesh = mi->mesh;
ERR_FAIL_UNSIGNED_INDEX(p_surface_index, mesh->surface_count);
MeshInstance::Surface *mis = &mi->surfaces[p_surface_index];
Mesh::Surface *s = mesh->surfaces[p_surface_index];
s->version_lock.lock();
//there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way
for (uint32_t i = 0; i < mis->version_count; i++) {
if (mis->versions[i].input_mask != p_input_mask) {
continue;
}
//we have this version, hooray
r_vertex_array_gl = mis->versions[i].vertex_array;
s->version_lock.unlock();
return;
}
uint32_t version = mis->version_count;
mis->version_count++;
mis->versions = (Mesh::Surface::Version *)memrealloc(mis->versions, sizeof(Mesh::Surface::Version) * mis->version_count);
_mesh_surface_generate_version_for_input_mask(mis->versions[version], s, p_input_mask, mis);
r_vertex_array_gl = mis->versions[version].vertex_array;
s->version_lock.unlock();
}
/* MULTIMESH API */
MultiMesh *get_multimesh(RID p_rid) { return multimesh_owner.get_or_null(p_rid); }
bool owns_multimesh(RID p_rid) { return multimesh_owner.owns(p_rid); }
virtual RID _multimesh_allocate() override;
virtual void _multimesh_initialize(RID p_rid) override;
virtual void _multimesh_free(RID p_rid) override;
virtual void _multimesh_allocate_data(RID p_multimesh, int p_instances, RS::MultimeshTransformFormat p_transform_format, bool p_use_colors = false, bool p_use_custom_data = false, bool p_use_indirect = false) override;
virtual int _multimesh_get_instance_count(RID p_multimesh) const override;
virtual void _multimesh_set_mesh(RID p_multimesh, RID p_mesh) override;
virtual void _multimesh_instance_set_transform(RID p_multimesh, int p_index, const Transform3D &p_transform) override;
virtual void _multimesh_instance_set_transform_2d(RID p_multimesh, int p_index, const Transform2D &p_transform) override;
virtual void _multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override;
virtual void _multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;
virtual RID _multimesh_get_mesh(RID p_multimesh) const override;
virtual void _multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB _multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB _multimesh_get_aabb(RID p_multimesh) override;
virtual Transform3D _multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
virtual Transform2D _multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
virtual Color _multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
virtual void _multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
virtual RID _multimesh_get_command_buffer_rd_rid(RID p_multimesh) const override;
virtual RID _multimesh_get_buffer_rd_rid(RID p_multimesh) const override;
virtual Vector<float> _multimesh_get_buffer(RID p_multimesh) const override;
virtual void _multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int _multimesh_get_visible_instances(RID p_multimesh) const override;
virtual MultiMeshInterpolator *_multimesh_get_interpolator(RID p_multimesh) const override;
void _update_dirty_multimeshes();
_FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->xform_format;
}
_FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->uses_colors;
}
_FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->uses_custom_data;
}
_FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
if (multimesh->visible_instances >= 0) {
return multimesh->visible_instances;
}
return multimesh->instances;
}
_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->buffer;
}
_FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->stride_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->color_offset_cache;
}
_FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
return multimesh->custom_data_offset_cache;
}
/* SKELETON API */
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); }
bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); }
virtual RID skeleton_allocate() override;
virtual void skeleton_initialize(RID p_rid) override;
virtual void skeleton_free(RID p_rid) override;
virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override;
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override;
virtual int skeleton_get_bone_count(RID p_skeleton) const override;
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override;
virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override;
virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override;
void _update_dirty_skeletons();
_FORCE_INLINE_ bool skeleton_is_valid(RID p_skeleton) {
return skeleton_owner.get_or_null(p_skeleton) != nullptr;
}
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // MESH_STORAGE_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,464 @@
/**************************************************************************/
/* particles_storage.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef PARTICLES_STORAGE_GLES3_H
#define PARTICLES_STORAGE_GLES3_H
#ifdef GLES3_ENABLED
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "drivers/gles3/shaders/particles_copy.glsl.gen.h"
#include "servers/rendering/storage/particles_storage.h"
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
enum ParticlesUniformLocation {
PARTICLES_FRAME_UNIFORM_LOCATION,
PARTICLES_GLOBALS_UNIFORM_LOCATION,
PARTICLES_MATERIAL_UNIFORM_LOCATION,
};
class ParticlesStorage : public RendererParticlesStorage {
private:
static ParticlesStorage *singleton;
/* PARTICLES */
struct ParticleInstanceData3D {
float xform[12];
float color[2]; // Color and custom are packed together into one vec4;
float custom[2];
};
struct ParticleInstanceData2D {
float xform[8];
float color[2]; // Color and custom are packed together into one vec4;
float custom[2];
};
struct ParticlesViewSort {
Vector3 z_dir;
bool operator()(const ParticleInstanceData3D &p_a, const ParticleInstanceData3D &p_b) const {
return z_dir.dot(Vector3(p_a.xform[3], p_a.xform[7], p_a.xform[11])) < z_dir.dot(Vector3(p_b.xform[3], p_b.xform[7], p_b.xform[11]));
}
};
struct ParticlesFrameParams {
enum {
MAX_ATTRACTORS = 32,
MAX_COLLIDERS = 32,
MAX_3D_TEXTURES = 0 // GLES3 renderer doesn't support using 3D textures for flow field or collisions.
};
enum AttractorType {
ATTRACTOR_TYPE_SPHERE,
ATTRACTOR_TYPE_BOX,
ATTRACTOR_TYPE_VECTOR_FIELD,
};
struct Attractor {
float transform[16];
float extents[4]; // Extents or radius. w-channel is padding.
uint32_t type;
float strength;
float attenuation;
float directionality;
};
enum CollisionType {
COLLISION_TYPE_SPHERE,
COLLISION_TYPE_BOX,
COLLISION_TYPE_SDF,
COLLISION_TYPE_HEIGHT_FIELD,
COLLISION_TYPE_2D_SDF,
};
struct Collider {
float transform[16];
float extents[4]; // Extents or radius. w-channel is padding.
uint32_t type;
float scale;
float pad0;
float pad1;
};
uint32_t emitting;
uint32_t cycle;
float system_phase;
float prev_system_phase;
float explosiveness;
float randomness;
float time;
float delta;
float particle_size;
float amount_ratio;
float pad1;
float pad2;
uint32_t random_seed;
uint32_t attractor_count;
uint32_t collider_count;
uint32_t frame;
float emission_transform[16];
float emitter_velocity[3];
float interp_to_end;
Attractor attractors[MAX_ATTRACTORS];
Collider colliders[MAX_COLLIDERS];
};
static_assert(sizeof(ParticlesFrameParams) % 16 == 0, "ParticlesFrameParams size must be a multiple of 16 bytes");
static_assert(sizeof(ParticlesFrameParams) < 16384, "ParticlesFrameParams must be 16384 bytes or smaller");
struct Particles {
RS::ParticlesMode mode = RS::PARTICLES_MODE_3D;
bool inactive = true;
double inactive_time = 0.0;
bool emitting = false;
bool one_shot = false;
float amount_ratio = 1.0;
int amount = 0;
double lifetime = 1.0;
double pre_process_time = 0.0;
real_t request_process_time = 0.0;
real_t explosiveness = 0.0;
real_t randomness = 0.0;
bool restart_request = false;
AABB custom_aabb = AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8));
bool use_local_coords = false;
bool has_collision_cache = false;
bool has_sdf_collision = false;
Transform2D sdf_collision_transform;
Rect2 sdf_collision_to_screen;
GLuint sdf_collision_texture = 0;
RID process_material;
uint32_t frame_counter = 0;
RS::ParticlesTransformAlign transform_align = RS::PARTICLES_TRANSFORM_ALIGN_DISABLED;
RS::ParticlesDrawOrder draw_order = RS::PARTICLES_DRAW_ORDER_INDEX;
Vector<RID> draw_passes;
GLuint frame_params_ubo = 0;
// We may process particles multiple times each frame (if they have a fixed FPS higher than the game FPS).
// Unfortunately, this means we can't just use a round-robin system of 3 buffers.
// To ensure the sort buffer is accurate, we copy the last frame instance buffer just before processing.
// Transform Feedback buffer and VAO for rendering.
// Each frame we render to this one.
GLuint front_vertex_array = 0; // Binds process buffer. Used for processing.
GLuint front_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing.
GLuint front_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering.
// VAO for transform feedback, contains last frame's data.
// Read from this one for particles process and then copy to last frame buffer.
GLuint back_vertex_array = 0; // Binds process buffer. Used for processing.
GLuint back_process_buffer = 0; // Transform + color + custom data + userdata + velocity + flags. Only needed for processing.
GLuint back_instance_buffer = 0; // Transform + color + custom data. In packed format needed for rendering.
uint32_t instance_buffer_size_cache = 0;
uint32_t instance_buffer_stride_cache = 0;
uint32_t num_attrib_arrays_cache = 0;
uint32_t process_buffer_stride_cache = 0;
// Only ever copied to, holds last frame's instance data, then swaps with sort_buffer.
GLuint last_frame_buffer = 0;
bool last_frame_buffer_filled = false;
float last_frame_phase = 0.0;
// The frame-before-last's instance buffer.
// Use this to copy data back for sorting or computing AABB.
GLuint sort_buffer = 0;
bool sort_buffer_filled = false;
float sort_buffer_phase = 0.0;
uint32_t userdata_count = 0;
bool dirty = false;
SelfList<Particles> update_list;
double phase = 0.0;
double prev_phase = 0.0;
uint64_t prev_ticks = 0;
uint32_t random_seed = 0;
uint32_t cycle_number = 0;
double speed_scale = 1.0;
int fixed_fps = 30;
bool interpolate = true;
bool fractional_delta = false;
double frame_remainder = 0;
real_t collision_base_size = 0.01;
bool clear = true;
Transform3D emission_transform;
Vector3 emitter_velocity;
float interp_to_end = 0.0;
HashSet<RID> collisions;
Dependency dependency;
double trail_length = 1.0;
bool trails_enabled = false;
Particles() :
update_list(this) {
random_seed = Math::rand();
}
};
void _particles_process(Particles *p_particles, double p_delta);
void _particles_free_data(Particles *particles);
void _particles_update_buffers(Particles *particles);
void _particles_allocate_history_buffers(Particles *particles);
void _particles_update_instance_buffer(Particles *particles, const Vector3 &p_axis, const Vector3 &p_up_axis);
template <typename T>
void _particles_reverse_lifetime_sort(Particles *particles);
struct ParticlesShader {
RID default_shader;
RID default_material;
RID default_shader_version;
ParticlesCopyShaderGLES3 copy_shader;
RID copy_shader_version;
} particles_shader;
SelfList<Particles>::List particle_update_list;
mutable RID_Owner<Particles, true> particles_owner;
/* Particles Collision */
struct ParticlesCollision {
RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT;
uint32_t cull_mask = 0xFFFFFFFF;
float radius = 1.0;
Vector3 extents = Vector3(1, 1, 1);
float attractor_strength = 1.0;
float attractor_attenuation = 1.0;
float attractor_directionality = 0.0;
GLuint field_texture = 0;
GLuint heightfield_texture = 0;
GLuint heightfield_fb = 0;
Size2i heightfield_fb_size;
uint32_t heightfield_mask = (1 << 20) - 1;
RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024;
Dependency dependency;
};
struct ParticlesCollisionInstance {
RID collision;
Transform3D transform;
bool active = false;
};
mutable RID_Owner<ParticlesCollision, true> particles_collision_owner;
mutable RID_Owner<ParticlesCollisionInstance> particles_collision_instance_owner;
public:
static ParticlesStorage *get_singleton();
ParticlesStorage();
virtual ~ParticlesStorage();
bool free(RID p_rid);
/* PARTICLES */
bool owns_particles(RID p_rid) { return particles_owner.owns(p_rid); }
virtual RID particles_allocate() override;
virtual void particles_initialize(RID p_rid) override;
virtual void particles_free(RID p_rid) override;
virtual void particles_set_mode(RID p_particles, RS::ParticlesMode p_mode) override;
virtual void particles_emit(RID p_particles, const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) override;
virtual void particles_set_emitting(RID p_particles, bool p_emitting) override;
virtual void particles_set_amount(RID p_particles, int p_amount) override;
virtual void particles_set_amount_ratio(RID p_particles, float p_amount_ratio) override;
virtual void particles_set_lifetime(RID p_particles, double p_lifetime) override;
virtual void particles_set_one_shot(RID p_particles, bool p_one_shot) override;
virtual void particles_set_pre_process_time(RID p_particles, double p_time) override;
virtual void particles_request_process_time(RID p_particles, real_t p_request_process_time) override;
virtual void particles_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
virtual void particles_set_speed_scale(RID p_particles, double p_scale) override;
virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override;
virtual void particles_set_process_material(RID p_particles, RID p_material) override;
virtual RID particles_get_process_material(RID p_particles) const override;
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) override;
virtual void particles_set_interpolate(RID p_particles, bool p_enable) override;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override;
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override;
virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override;
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override;
virtual void particles_set_seed(RID p_particles, uint32_t p_seed) override;
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override;
virtual void particles_restart(RID p_particles) override;
virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override;
virtual void particles_set_draw_passes(RID p_particles, int p_count) override;
virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override;
virtual void particles_request_process(RID p_particles) override;
virtual AABB particles_get_current_aabb(RID p_particles) override;
virtual AABB particles_get_aabb(RID p_particles) const override;
virtual void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override;
virtual void particles_set_emitter_velocity(RID p_particles, const Vector3 &p_velocity) override;
virtual void particles_set_interp_to_end(RID p_particles, float p_interp) override;
virtual bool particles_get_emitting(RID p_particles) override;
virtual int particles_get_draw_passes(RID p_particles) const override;
virtual RID particles_get_draw_pass_mesh(RID p_particles, int p_pass) const override;
virtual void particles_add_collision(RID p_particles, RID p_instance) override;
virtual void particles_remove_collision(RID p_particles, RID p_instance) override;
void particles_set_canvas_sdf_collision(RID p_particles, bool p_enable, const Transform2D &p_xform, const Rect2 &p_to_screen, GLuint p_texture);
virtual void update_particles() override;
virtual bool particles_is_inactive(RID p_particles) const override;
_FORCE_INLINE_ RS::ParticlesMode particles_get_mode(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, RS::PARTICLES_MODE_2D);
return particles->mode;
}
_FORCE_INLINE_ uint32_t particles_get_amount(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, 0);
return particles->amount;
}
_FORCE_INLINE_ GLuint particles_get_gl_buffer(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
if ((particles->draw_order == RS::PARTICLES_DRAW_ORDER_VIEW_DEPTH || particles->draw_order == RS::PARTICLES_DRAW_ORDER_REVERSE_LIFETIME) && particles->sort_buffer_filled) {
return particles->sort_buffer;
}
return particles->back_instance_buffer;
}
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
_FORCE_INLINE_ uint32_t particles_is_using_local_coords(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return particles->use_local_coords;
}
Dependency *particles_get_dependency(RID p_particles) const;
/* PARTICLES COLLISION */
bool owns_particles_collision(RID p_rid) { return particles_collision_owner.owns(p_rid); }
virtual RID particles_collision_allocate() override;
virtual void particles_collision_initialize(RID p_rid) override;
virtual void particles_collision_free(RID p_rid) override;
virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) override;
virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) override;
virtual void particles_collision_set_sphere_radius(RID p_particles_collision, real_t p_radius) override;
virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) override;
virtual void particles_collision_set_attractor_strength(RID p_particles_collision, real_t p_strength) override;
virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, real_t p_directionality) override;
virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, real_t p_curve) override;
virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) override;
virtual void particles_collision_height_field_update(RID p_particles_collision) override;
virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) override;
virtual AABB particles_collision_get_aabb(RID p_particles_collision) const override;
Vector3 particles_collision_get_extents(RID p_particles_collision) const;
virtual bool particles_collision_is_heightfield(RID p_particles_collision) const override;
GLuint particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const;
virtual uint32_t particles_collision_get_height_field_mask(RID p_particles_collision) const override;
virtual void particles_collision_set_height_field_mask(RID p_particles_collision, uint32_t p_heightfield_mask) override;
_FORCE_INLINE_ Size2i particles_collision_get_heightfield_size(RID p_particles_collision) const {
ParticlesCollision *particles_collision = particles_collision_owner.get_or_null(p_particles_collision);
ERR_FAIL_NULL_V(particles_collision, Size2i());
ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, Size2i());
return particles_collision->heightfield_fb_size;
}
Dependency *particles_collision_get_dependency(RID p_particles) const;
/* PARTICLES COLLISION INSTANCE*/
bool owns_particles_collision_instance(RID p_rid) { return particles_collision_instance_owner.owns(p_rid); }
virtual RID particles_collision_instance_create(RID p_collision) override;
virtual void particles_collision_instance_free(RID p_rid) override;
virtual void particles_collision_instance_set_transform(RID p_collision_instance, const Transform3D &p_transform) override;
virtual void particles_collision_instance_set_active(RID p_collision_instance, bool p_active) override;
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // PARTICLES_STORAGE_GLES3_H

View file

@ -0,0 +1,672 @@
/**************************************************************************/
/* render_scene_buffers_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. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "render_scene_buffers_gles3.h"
#include "config.h"
#include "texture_storage.h"
#include "utilities.h"
#ifdef ANDROID_ENABLED
#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
#define glTexStorage3DMultisample GLES3::Config::get_singleton()->eglTexStorage3DMultisample
#define glFramebufferTexture2DMultisampleEXT GLES3::Config::get_singleton()->eglFramebufferTexture2DMultisampleEXT
#define glFramebufferTextureMultisampleMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultisampleMultiviewOVR
#endif // ANDROID_ENABLED
// Will only be defined if GLES 3.2 headers are included
#ifndef GL_TEXTURE_2D_MULTISAMPLE_ARRAY
#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
#endif
RenderSceneBuffersGLES3::RenderSceneBuffersGLES3() {
for (int i = 0; i < 4; i++) {
glow.levels[i].color = 0;
glow.levels[i].fbo = 0;
}
}
RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() {
free_render_buffer_data();
}
void RenderSceneBuffersGLES3::_rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
if (p_view_count > 1) {
if (p_samples > 1) {
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, p_samples, 0, p_view_count);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, p_samples, 0, p_view_count);
#else
ERR_PRINT_ONCE("Multiview MSAA isn't supported on this platform.");
#endif
} else {
#ifndef IOS_ENABLED
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, p_color, 0, 0, p_view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, p_depth, 0, 0, p_view_count);
#else
ERR_PRINT_ONCE("Multiview isn't supported on this platform.");
#endif
}
} else {
if (p_samples > 1) {
#ifdef ANDROID_ENABLED
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0, p_samples);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0, p_samples);
#else
ERR_PRINT_ONCE("MSAA via EXT_multisampled_render_to_texture isn't supported on this platform.");
#endif
} else {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_color, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, p_depth, 0);
}
}
}
GLuint RenderSceneBuffersGLES3::_rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count) {
FBDEF new_fbo;
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED)
// There shouldn't be more then 3 entries in this...
for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
if (cached_fbo.color == p_color && cached_fbo.depth == p_depth) {
return cached_fbo.fbo;
}
}
new_fbo.color = p_color;
new_fbo.depth = p_depth;
glGenFramebuffers(1, &new_fbo.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, new_fbo.fbo);
_rt_attach_textures(p_color, p_depth, p_samples, p_view_count);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
glDeleteFramebuffers(1, &new_fbo.fbo);
new_fbo.fbo = 0;
} else {
// cache it!
msaa3d.cached_fbos.push_back(new_fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
return new_fbo.fbo;
}
void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p_config) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
free_render_buffer_data();
internal_size = p_config->get_internal_size();
target_size = p_config->get_target_size();
scaling_3d_mode = p_config->get_scaling_3d_mode();
//fsr_sharpness = p_config->get_fsr_sharpness();
//texture_mipmap_bias = p_config->get_texture_mipmap_bias();
//anisotropic_filtering_level = p_config->get_anisotropic_filtering_level();
render_target = p_config->get_render_target();
msaa3d.mode = p_config->get_msaa_3d();
//screen_space_aa = p_config->get_screen_space_aa();
//use_debanding = p_config->get_use_debanding();
view_count = config->multiview_supported ? p_config->get_view_count() : 1;
bool use_multiview = view_count > 1;
// Get color format data from our render target so we match those
if (render_target.is_valid()) {
color_internal_format = texture_storage->render_target_get_color_internal_format(render_target);
color_format = texture_storage->render_target_get_color_format(render_target);
color_type = texture_storage->render_target_get_color_type(render_target);
color_format_size = texture_storage->render_target_get_color_format_size(render_target);
} else {
// reflection probe? or error?
color_internal_format = GL_RGBA8;
color_format = GL_RGBA;
color_type = GL_UNSIGNED_BYTE;
color_format_size = 4;
}
// Check our scaling mode
if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) {
// Disable, no size set.
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
} else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size == target_size) {
// If size matches, we won't use scaling.
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
} else if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_BILINEAR) {
// We only support bilinear scaling atm.
WARN_PRINT_ONCE("GLES only supports bilinear scaling.");
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
}
// Check if we support MSAA.
if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) {
// Disable, no size set.
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
} else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) {
WARN_PRINT_ONCE("MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
} else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) {
WARN_PRINT_ONCE("Multiview MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
}
// We don't create our buffers right away because post effects can be made active at any time and change our buffer configuration.
}
void RenderSceneBuffersGLES3::_check_render_buffers() {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
ERR_FAIL_COND(view_count == 0);
bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF || apply_color_adjustments_in_post;
uint32_t depth_format_size = 3;
bool use_multiview = view_count > 1;
if ((!use_internal_buffer || internal3d.color != 0) && (msaa3d.mode == RS::VIEWPORT_MSAA_DISABLED || msaa3d.color != 0)) {
// already setup!
return;
}
if (use_internal_buffer && internal3d.color == 0) {
// Setup our internal buffer.
GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
// Create our color buffer.
glGenTextures(1, &internal3d.color);
glBindTexture(texture_target, internal3d.color);
if (use_multiview) {
glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr);
} else {
glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D color texture");
// Create our depth buffer.
glGenTextures(1, &internal3d.depth);
glBindTexture(texture_target, internal3d.depth);
if (use_multiview) {
glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
} else {
glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D depth texture");
// Create our internal 3D FBO.
// Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects.
glGenFramebuffers(1, &internal3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, internal3d.fbo);
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, internal3d.color, 0, 0, view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, internal3d.depth, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, internal3d.color, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, internal3d.depth, 0);
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_intermediate_buffers();
WARN_PRINT("Could not create 3D internal buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(texture_target, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && msaa3d.color == 0) {
// Setup MSAA.
const GLsizei samples[] = { 1, 2, 4, 8 };
msaa3d.samples = samples[msaa3d.mode];
// Constrain by limits of OpenGL driver.
if (msaa3d.samples > config->msaa_max_samples) {
msaa3d.samples = config->msaa_max_samples;
}
if (!use_multiview && !config->rt_msaa_supported) {
// Render to texture extensions not supported? fall back to MSAA framebuffer through GL_EXT_framebuffer_multisample.
// Note, if 2D MSAA matches 3D MSAA and we're not scaling, it would be ideal if we reuse our 2D MSAA buffer here.
// We can't however because we don't trigger a change in configuration if 2D MSAA changes.
// We'll accept the overhead in this situation.
msaa3d.needs_resolve = true;
msaa3d.check_fbo_cache = false;
// Create our color buffer.
glGenRenderbuffers(1, &msaa3d.color);
glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y);
GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color render buffer");
// Create our depth buffer.
glGenRenderbuffers(1, &msaa3d.depth);
glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.depth);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y);
GLES3::Utilities::get_singleton()->render_buffer_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * 3 * msaa3d.samples, "MSAA 3D depth render buffer");
// Create our MSAA 3D FBO.
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa3d.color);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa3d.depth);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#if !defined(IOS_ENABLED) && !defined(WEB_ENABLED)
} else if (use_multiview && !config->rt_msaa_multiview_supported) {
// Render to texture extensions not supported? fall back to MSAA textures through GL_EXT_multiview_texture_multisample.
msaa3d.needs_resolve = true;
msaa3d.check_fbo_cache = false;
// Create our color buffer.
glGenTextures(1, &msaa3d.color);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color);
#ifdef ANDROID_ENABLED
glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#else
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE);
#endif
GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * color_format_size * msaa3d.samples, "MSAA 3D color texture");
// Create our depth buffer.
glGenTextures(1, &msaa3d.depth);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.depth);
#ifdef ANDROID_ENABLED
glTexStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE);
#else
glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE);
#endif
GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth texture");
// Create our MSAA 3D FBO.
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, msaa3d.color, 0, 0, view_count);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, msaa3d.depth, 0, 0, view_count);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
#if defined(ANDROID_ENABLED) || defined(WEB_ENABLED) // Only supported on OpenGLES!
} else if (!use_internal_buffer) {
// We are going to render directly into our render target textures,
// these can change from frame to frame as we cycle through swapchains,
// hence we'll use our FBO cache here.
msaa3d.needs_resolve = false;
msaa3d.check_fbo_cache = true;
} else if (use_internal_buffer) {
// We can combine MSAA and scaling/effects.
msaa3d.needs_resolve = false;
msaa3d.check_fbo_cache = false;
// We render to our internal textures, MSAA is only done in tile memory only.
// On mobile this means MSAA never leaves tile memory = efficiency!
glGenFramebuffers(1, &msaa3d.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaa3d.fbo);
_rt_attach_textures(internal3d.color, internal3d.depth, msaa3d.samples, view_count);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_msaa3d_buffers();
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status));
}
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
#endif
} else {
// HUH? how did we get here?
WARN_PRINT_ONCE("MSAA is not supported on this device.");
msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED;
msaa3d.samples = 1;
msaa3d.check_fbo_cache = false;
}
} else {
msaa3d.samples = 1;
msaa3d.check_fbo_cache = false;
}
}
void RenderSceneBuffersGLES3::configure_for_probe(Size2i p_size) {
internal_size = p_size;
target_size = p_size;
scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
view_count = 1;
}
void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() {
for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
GLuint fbo = cached_fbo.fbo;
glDeleteFramebuffers(1, &fbo);
}
msaa3d.cached_fbos.clear();
if (msaa3d.fbo) {
glDeleteFramebuffers(1, &msaa3d.fbo);
msaa3d.fbo = 0;
}
if (msaa3d.color != 0) {
if (view_count == 1) {
GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.color);
} else {
GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.color);
}
msaa3d.color = 0;
}
if (msaa3d.depth != 0) {
if (view_count == 1) {
GLES3::Utilities::get_singleton()->render_buffer_free_data(msaa3d.depth);
} else {
GLES3::Utilities::get_singleton()->texture_free_data(msaa3d.depth);
}
msaa3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::_clear_intermediate_buffers() {
if (internal3d.fbo) {
glDeleteFramebuffers(1, &internal3d.fbo);
internal3d.fbo = 0;
}
if (internal3d.color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(internal3d.color);
internal3d.color = 0;
}
if (internal3d.depth != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(internal3d.depth);
internal3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_depth) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
// Setup our back buffer
if (backbuffer3d.fbo == 0) {
glGenFramebuffers(1, &backbuffer3d.fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo);
bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported;
GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
uint32_t depth_format_size = 3;
if (backbuffer3d.color == 0 && p_need_color) {
glGenTextures(1, &backbuffer3d.color);
glBindTexture(texture_target, backbuffer3d.color);
if (use_multiview) {
glTexImage3D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, view_count, 0, color_format, color_type, nullptr);
} else {
glTexImage2D(texture_target, 0, color_internal_format, internal_size.x, internal_size.y, 0, color_format, color_type, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D Back buffer color texture");
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, backbuffer3d.color, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_target, backbuffer3d.color, 0);
}
}
if (backbuffer3d.depth == 0 && p_need_depth) {
glGenTextures(1, &backbuffer3d.depth);
glBindTexture(texture_target, backbuffer3d.depth);
if (use_multiview) {
glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
} else {
glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
}
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D back buffer depth texture");
#ifndef IOS_ENABLED
if (use_multiview) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, backbuffer3d.depth, 0, 0, view_count);
} else {
#else
{
#endif
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_target, backbuffer3d.depth, 0);
}
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
_clear_back_buffers();
WARN_PRINT("Could not create 3D back buffers, status: " + texture_storage->get_framebuffer_error(status));
}
glBindTexture(texture_target, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
void RenderSceneBuffersGLES3::_clear_back_buffers() {
if (backbuffer3d.fbo) {
glDeleteFramebuffers(1, &backbuffer3d.fbo);
backbuffer3d.fbo = 0;
}
if (backbuffer3d.color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.color);
backbuffer3d.color = 0;
}
if (backbuffer3d.depth != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(backbuffer3d.depth);
backbuffer3d.depth = 0;
}
}
void RenderSceneBuffersGLES3::set_apply_color_adjustments_in_post(bool p_apply_in_post) {
apply_color_adjustments_in_post = p_apply_in_post;
}
void RenderSceneBuffersGLES3::check_glow_buffers() {
if (glow.levels[0].color != 0) {
// already have these setup..
return;
}
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
Size2i level_size = internal_size;
for (int i = 0; i < 4; i++) {
level_size = Size2i(level_size.x >> 1, level_size.y >> 1).maxi(4);
glow.levels[i].size = level_size;
// Create our texture
glGenTextures(1, &glow.levels[i].color);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glow.levels[i].color);
glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, level_size.x, level_size.y, 0, color_format, color_type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
GLES3::Utilities::get_singleton()->texture_allocated_data(glow.levels[i].color, level_size.x * level_size.y * color_format_size, String("Glow buffer ") + String::num_int64(i));
// Create our FBO
glGenFramebuffers(1, &glow.levels[i].fbo);
glBindFramebuffer(GL_FRAMEBUFFER, glow.levels[i].fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glow.levels[i].color, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
WARN_PRINT("Could not create glow buffers, status: " + texture_storage->get_framebuffer_error(status));
_clear_glow_buffers();
break;
}
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
void RenderSceneBuffersGLES3::_clear_glow_buffers() {
for (int i = 0; i < 4; i++) {
if (glow.levels[i].fbo != 0) {
glDeleteFramebuffers(1, &glow.levels[i].fbo);
glow.levels[i].fbo = 0;
}
if (glow.levels[i].color != 0) {
GLES3::Utilities::get_singleton()->texture_free_data(glow.levels[i].color);
glow.levels[i].color = 0;
}
}
}
void RenderSceneBuffersGLES3::free_render_buffer_data() {
_clear_msaa3d_buffers();
_clear_intermediate_buffers();
_clear_back_buffers();
_clear_glow_buffers();
}
GLuint RenderSceneBuffersGLES3::get_render_fbo() {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLuint rt_fbo = 0;
_check_render_buffers();
if (msaa3d.check_fbo_cache) {
GLuint color = texture_storage->render_target_get_color(render_target);
GLuint depth = texture_storage->render_target_get_depth(render_target);
rt_fbo = _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count);
if (rt_fbo == 0) {
// Somehow couldn't obtain this? Just render without MSAA.
rt_fbo = texture_storage->render_target_get_fbo(render_target);
}
} else if (msaa3d.fbo != 0) {
// We have an MSAA fbo, render to our MSAA buffer
return msaa3d.fbo;
} else if (internal3d.fbo != 0) {
// We have an internal buffer, render to our internal buffer!
return internal3d.fbo;
} else {
rt_fbo = texture_storage->render_target_get_fbo(render_target);
}
if (texture_storage->render_target_is_reattach_textures(render_target)) {
GLuint color = texture_storage->render_target_get_color(render_target);
GLuint depth = texture_storage->render_target_get_depth(render_target);
glBindFramebuffer(GL_FRAMEBUFFER, rt_fbo);
_rt_attach_textures(color, depth, msaa3d.samples, view_count);
glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo);
}
return rt_fbo;
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,168 @@
/**************************************************************************/
/* render_scene_buffers_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. */
/**************************************************************************/
#ifndef RENDER_SCENE_BUFFERS_GLES3_H
#define RENDER_SCENE_BUFFERS_GLES3_H
#ifdef GLES3_ENABLED
#include "drivers/gles3/effects/glow.h"
#include "servers/rendering/storage/render_scene_buffers.h"
#include "platform_gl.h"
class RenderSceneBuffersGLES3 : public RenderSceneBuffers {
GDCLASS(RenderSceneBuffersGLES3, RenderSceneBuffers);
public:
Size2i internal_size; // Size of the buffer we render 3D content to.
Size2i target_size; // Size of our output buffer (render target).
RS::ViewportScaling3DMode scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
//float fsr_sharpness = 0.2f;
//RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
//bool use_taa = false;
//bool use_debanding = false;
uint32_t view_count = 1;
bool apply_color_adjustments_in_post = false;
RID render_target;
// Color format details from our render target
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
GLuint color_type = GL_UNSIGNED_BYTE;
uint32_t color_format_size = 4;
struct FBDEF {
GLuint color = 0;
GLuint depth = 0;
GLuint fbo = 0;
};
struct RTMSAA3D {
RS::ViewportMSAA mode = RS::VIEWPORT_MSAA_DISABLED;
bool needs_resolve = false;
GLsizei samples = 1;
GLuint color = 0;
GLuint depth = 0;
GLuint fbo = 0;
bool check_fbo_cache = false;
Vector<FBDEF> cached_fbos;
} msaa3d; // MSAA buffers used to render 3D
FBDEF internal3d; // buffers used to either render 3D (scaled/post) or to resolve MSAA into
FBDEF backbuffer3d; // our back buffer
// Buffers for our glow implementation
struct GLOW {
GLES3::Glow::GLOWLEVEL levels[4];
} glow;
private:
void _check_render_buffers();
void _clear_msaa3d_buffers();
void _clear_intermediate_buffers();
void _clear_back_buffers();
void _clear_glow_buffers();
void _rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count);
public:
RenderSceneBuffersGLES3();
virtual ~RenderSceneBuffersGLES3();
virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
void configure_for_probe(Size2i p_size);
virtual void set_anisotropic_filtering_level(RS::ViewportAnisotropicFiltering p_anisotropic_filtering_level) override {}
virtual void set_fsr_sharpness(float p_fsr_sharpness) override {}
virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override {}
virtual void set_use_debanding(bool p_use_debanding) override {}
void set_apply_color_adjustments_in_post(bool p_apply_in_post);
void free_render_buffer_data();
void check_backbuffer(bool p_need_color, bool p_need_depth); // Check if we need to initialize our backbuffer.
void check_glow_buffers(); // Check if we need to initialize our glow buffers.
GLuint get_render_fbo();
GLuint get_msaa3d_fbo() {
_check_render_buffers();
return msaa3d.fbo;
}
GLuint get_msaa3d_color() {
_check_render_buffers();
return msaa3d.color;
}
GLuint get_msaa3d_depth() {
_check_render_buffers();
return msaa3d.depth;
}
bool get_msaa_needs_resolve() {
_check_render_buffers();
return msaa3d.needs_resolve;
}
GLuint get_internal_fbo() {
_check_render_buffers();
return internal3d.fbo;
}
GLuint get_internal_color() {
_check_render_buffers();
return internal3d.color;
}
GLuint get_internal_depth() {
_check_render_buffers();
return internal3d.depth;
}
GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; }
GLuint get_backbuffer() const { return backbuffer3d.color; }
GLuint get_backbuffer_depth() const { return backbuffer3d.depth; }
const GLES3::Glow::GLOWLEVEL *get_glow_buffers() const { return &glow.levels[0]; }
// Getters
_FORCE_INLINE_ RID get_render_target() const { return render_target; }
_FORCE_INLINE_ uint32_t get_view_count() const { return view_count; }
_FORCE_INLINE_ Size2i get_internal_size() const { return internal_size; }
_FORCE_INLINE_ Size2i get_target_size() const { return target_size; }
_FORCE_INLINE_ RS::ViewportScaling3DMode get_scaling_3d_mode() const { return scaling_3d_mode; }
//_FORCE_INLINE_ float get_fsr_sharpness() const { return fsr_sharpness; }
_FORCE_INLINE_ RS::ViewportMSAA get_msaa_3d() const { return msaa3d.mode; }
//_FORCE_INLINE_ RS::ViewportScreenSpaceAA get_screen_space_aa() const { return screen_space_aa; }
//_FORCE_INLINE_ bool get_use_taa() const { return use_taa; }
//_FORCE_INLINE_ bool get_use_debanding() const { return use_debanding; }
};
#endif // GLES3_ENABLED
#endif // RENDER_SCENE_BUFFERS_GLES3_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,734 @@
/**************************************************************************/
/* texture_storage.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEXTURE_STORAGE_GLES3_H
#define TEXTURE_STORAGE_GLES3_H
#ifdef GLES3_ENABLED
#include "platform_gl.h"
#include "config.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/storage/texture_storage.h"
#include "drivers/gles3/shaders/canvas_sdf.glsl.gen.h"
namespace GLES3 {
#define _GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#define _EXT_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define _EXT_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define _EXT_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#define _EXT_COMPRESSED_RED_RGTC1_EXT 0x8DBB
#define _EXT_COMPRESSED_RED_RGTC1 0x8DBB
#define _EXT_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
#define _EXT_COMPRESSED_RG_RGTC2 0x8DBD
#define _EXT_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
#define _EXT_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
#define _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
#define _EXT_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#define _EXT_ETC1_RGB8_OES 0x8D64
#define _EXT_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
#define _EXT_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D
#define _EXT_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
#define _EXT_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F
#define _EXT_COMPRESSED_R11_EAC 0x9270
#define _EXT_COMPRESSED_SIGNED_R11_EAC 0x9271
#define _EXT_COMPRESSED_RG11_EAC 0x9272
#define _EXT_COMPRESSED_SIGNED_RG11_EAC 0x9273
#define _EXT_COMPRESSED_RGB8_ETC2 0x9274
#define _EXT_COMPRESSED_SRGB8_ETC2 0x9275
#define _EXT_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
#define _EXT_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
#define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
#define _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
#define _EXT_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1
#define _EXT_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2
#define _EXT_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3
#define _EXT_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4
#define _EXT_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5
#define _EXT_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6
#define _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7
#define _EXT_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8
#define _EXT_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9
#define _EXT_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA
#define _EXT_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB
#define _EXT_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC
#define _EXT_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
#define _GL_TEXTURE_EXTERNAL_OES 0x8D65
#define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
enum DefaultGLTexture {
DEFAULT_GL_TEXTURE_WHITE,
DEFAULT_GL_TEXTURE_BLACK,
DEFAULT_GL_TEXTURE_TRANSPARENT,
DEFAULT_GL_TEXTURE_NORMAL,
DEFAULT_GL_TEXTURE_ANISO,
DEFAULT_GL_TEXTURE_DEPTH,
DEFAULT_GL_TEXTURE_CUBEMAP_BLACK,
//DEFAULT_GL_TEXTURE_CUBEMAP_ARRAY_BLACK, // Cubemap Arrays not supported in GL 3.3 or GL ES 3.0
DEFAULT_GL_TEXTURE_CUBEMAP_WHITE,
DEFAULT_GL_TEXTURE_3D_WHITE,
DEFAULT_GL_TEXTURE_3D_BLACK,
DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE,
DEFAULT_GL_TEXTURE_2D_UINT,
DEFAULT_GL_TEXTURE_EXT,
DEFAULT_GL_TEXTURE_MAX
};
struct CanvasTexture {
RID diffuse;
RID normal_map;
RID specular;
Color specular_color = Color(1, 1, 1, 1);
float shininess = 1.0;
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
};
struct RenderTarget;
struct Texture {
RID self;
bool is_proxy = false;
bool is_from_native_handle = false;
bool is_render_target = false;
RID proxy_to;
Vector<RID> proxies;
String path;
int width = 0;
int height = 0;
int depth = 0;
int mipmaps = 1;
int layers = 1;
int alloc_width = 0;
int alloc_height = 0;
Image::Format format = Image::FORMAT_R8;
Image::Format real_format = Image::FORMAT_R8;
enum Type {
TYPE_2D,
TYPE_LAYERED,
TYPE_3D
};
Type type = TYPE_2D;
RS::TextureLayeredType layered_type = RS::TEXTURE_LAYERED_2D_ARRAY;
GLenum target = GL_TEXTURE_2D;
GLenum gl_format_cache = 0;
GLenum gl_internal_format_cache = 0;
GLenum gl_type_cache = 0;
int total_data_size = 0;
bool compressed = false;
bool resize_to_po2 = false;
bool active = false;
GLuint tex_id = 0;
uint16_t stored_cube_sides = 0;
RenderTarget *render_target = nullptr;
Ref<Image> image_cache_2d;
Vector<Ref<Image>> image_cache_3d;
bool redraw_if_visible = false;
RS::TextureDetectCallback detect_3d_callback = nullptr;
void *detect_3d_callback_ud = nullptr;
RS::TextureDetectCallback detect_normal_callback = nullptr;
void *detect_normal_callback_ud = nullptr;
RS::TextureDetectRoughnessCallback detect_roughness_callback = nullptr;
void *detect_roughness_callback_ud = nullptr;
CanvasTexture *canvas_texture = nullptr;
void copy_from(const Texture &o) {
proxy_to = o.proxy_to;
is_proxy = o.is_proxy;
is_from_native_handle = o.is_from_native_handle;
width = o.width;
height = o.height;
alloc_width = o.alloc_width;
alloc_height = o.alloc_height;
format = o.format;
type = o.type;
layered_type = o.layered_type;
target = o.target;
total_data_size = o.total_data_size;
compressed = o.compressed;
mipmaps = o.mipmaps;
resize_to_po2 = o.resize_to_po2;
active = o.active;
tex_id = o.tex_id;
stored_cube_sides = o.stored_cube_sides;
render_target = o.render_target;
is_render_target = o.is_render_target;
redraw_if_visible = o.redraw_if_visible;
detect_3d_callback = o.detect_3d_callback;
detect_3d_callback_ud = o.detect_3d_callback_ud;
detect_normal_callback = o.detect_normal_callback;
detect_normal_callback_ud = o.detect_normal_callback_ud;
detect_roughness_callback = o.detect_roughness_callback;
detect_roughness_callback_ud = o.detect_roughness_callback_ud;
}
// texture state
void gl_set_filter(RS::CanvasItemTextureFilter p_filter) {
if (p_filter == state_filter) {
return;
}
Config *config = Config::get_singleton();
state_filter = p_filter;
GLenum pmin = GL_NEAREST;
GLenum pmag = GL_NEAREST;
GLint max_lod = 0;
GLfloat anisotropy = 1.0f;
switch (state_filter) {
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST: {
pmin = GL_NEAREST;
pmag = GL_NEAREST;
max_lod = 0;
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR: {
pmin = GL_LINEAR;
pmag = GL_LINEAR;
max_lod = 0;
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: {
anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: {
pmag = GL_NEAREST;
if (mipmaps <= 1) {
pmin = GL_NEAREST;
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_NEAREST_MIPMAP_NEAREST;
max_lod = 1000;
} else {
pmin = GL_NEAREST_MIPMAP_LINEAR;
max_lod = 1000;
}
} break;
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC: {
anisotropy = config->anisotropic_level;
};
[[fallthrough]];
case RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS: {
pmag = GL_LINEAR;
if (mipmaps <= 1) {
pmin = GL_LINEAR;
max_lod = 0;
} else if (config->use_nearest_mip_filter) {
pmin = GL_LINEAR_MIPMAP_NEAREST;
max_lod = 1000;
} else {
pmin = GL_LINEAR_MIPMAP_LINEAR;
max_lod = 1000;
}
} break;
default: {
return;
} break;
}
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, pmin);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, pmag);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, max_lod);
if (config->support_anisotropic_filter) {
glTexParameterf(target, _GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy);
}
}
void gl_set_repeat(RS::CanvasItemTextureRepeat p_repeat) {
if (p_repeat == state_repeat) {
return;
}
state_repeat = p_repeat;
GLenum prep = GL_CLAMP_TO_EDGE;
switch (state_repeat) {
case RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED: {
prep = GL_CLAMP_TO_EDGE;
} break;
case RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED: {
prep = GL_REPEAT;
} break;
case RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR: {
prep = GL_MIRRORED_REPEAT;
} break;
default: {
return;
} break;
}
glTexParameteri(target, GL_TEXTURE_WRAP_T, prep);
glTexParameteri(target, GL_TEXTURE_WRAP_R, prep);
glTexParameteri(target, GL_TEXTURE_WRAP_S, prep);
}
private:
RS::CanvasItemTextureFilter state_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
RS::CanvasItemTextureRepeat state_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
};
struct RenderTarget {
Point2i position = Point2i(0, 0);
Size2i size = Size2i(0, 0);
uint32_t view_count = 1;
int mipmap_count = 1;
RID self;
GLuint fbo = 0;
GLuint color = 0;
GLuint depth = 0;
GLuint backbuffer_fbo = 0;
GLuint backbuffer = 0;
GLuint backbuffer_depth = 0;
bool hdr = false; // For Compatibility this effects both 2D and 3D rendering!
GLuint color_internal_format = GL_RGBA8;
GLuint color_format = GL_RGBA;
GLuint color_type = GL_UNSIGNED_BYTE;
uint32_t color_format_size = 4;
Image::Format image_format = Image::FORMAT_RGBA8;
GLuint sdf_texture_write = 0;
GLuint sdf_texture_write_fb = 0;
GLuint sdf_texture_process[2] = { 0, 0 };
GLuint sdf_texture_read = 0;
RS::ViewportSDFOversize sdf_oversize = RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT;
RS::ViewportSDFScale sdf_scale = RS::VIEWPORT_SDF_SCALE_50_PERCENT;
Size2i process_size;
bool sdf_enabled = false;
bool is_transparent = false;
bool direct_to_screen = false;
bool used_in_frame = false;
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
bool reattach_textures = false;
Rect2i render_region;
struct RTOverridden {
bool is_overridden = false;
RID color;
RID depth;
RID velocity;
struct FBOCacheEntry {
GLuint fbo;
GLuint color;
GLuint depth;
Size2i size;
Vector<GLuint> allocated_textures;
};
RBMap<uint32_t, FBOCacheEntry> fbo_cache;
} overridden;
RID texture;
Color clear_color = Color(1, 1, 1, 1);
bool clear_requested = false;
RenderTarget() {
}
};
class TextureStorage : public RendererTextureStorage {
private:
static TextureStorage *singleton;
RID default_gl_textures[DEFAULT_GL_TEXTURE_MAX];
/* Canvas Texture API */
RID_Owner<CanvasTexture, true> canvas_texture_owner;
/* Texture API */
// Textures can be created from threads, so this RID_Owner is thread safe.
mutable RID_Owner<Texture, true> texture_owner;
Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const;
/* TEXTURE ATLAS API */
struct TextureAtlas {
struct Texture {
int users;
Rect2 uv_rect;
};
struct SortItem {
RID texture;
Size2i pixel_size;
Size2i size;
Point2i pos;
bool operator<(const SortItem &p_item) const {
//sort larger to smaller
if (size.height == p_item.size.height) {
return size.width > p_item.size.width;
} else {
return size.height > p_item.size.height;
}
}
};
HashMap<RID, Texture> textures;
bool dirty = true;
GLuint texture = 0;
GLuint framebuffer = 0;
Size2i size;
} texture_atlas;
/* Render Target API */
mutable RID_Owner<RenderTarget> render_target_owner;
void _clear_render_target(RenderTarget *rt);
void _update_render_target(RenderTarget *rt);
void _create_render_target_backbuffer(RenderTarget *rt);
void _render_target_allocate_sdf(RenderTarget *rt);
void _render_target_clear_sdf(RenderTarget *rt);
Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const;
void _texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer, bool p_initialize);
void _texture_set_3d_data(RID p_texture, const Vector<Ref<Image>> &p_data, bool p_initialize);
void _texture_set_swizzle(Texture *p_texture, Image::Format p_real_format);
Vector<Ref<Image>> _texture_3d_read_framebuffer(Texture *p_texture) const;
struct RenderTargetSDF {
CanvasSdfShaderGLES3 shader;
RID shader_version;
} sdf_shader;
public:
static TextureStorage *get_singleton();
TextureStorage();
virtual ~TextureStorage();
_FORCE_INLINE_ RID texture_gl_get_default(DefaultGLTexture p_texture) {
return default_gl_textures[p_texture];
}
/* Canvas Texture API */
CanvasTexture *get_canvas_texture(RID p_rid) { return canvas_texture_owner.get_or_null(p_rid); }
bool owns_canvas_texture(RID p_rid) { return canvas_texture_owner.owns(p_rid); }
virtual RID canvas_texture_allocate() override;
virtual void canvas_texture_initialize(RID p_rid) override;
virtual void canvas_texture_free(RID p_rid) override;
virtual void canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) override;
virtual void canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_base_color, float p_shininess) override;
virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
/* Texture API */
Texture *get_texture(RID p_rid) const {
Texture *texture = texture_owner.get_or_null(p_rid);
if (texture && texture->is_proxy) {
return texture_owner.get_or_null(texture->proxy_to);
}
return texture;
}
bool owns_texture(RID p_rid) { return texture_owner.owns(p_rid); }
void texture_2d_initialize_from_texture(RID p_texture, Texture &p_tex) {
texture_owner.initialize_rid(p_texture, p_tex);
}
virtual RID texture_allocate() override;
virtual void texture_free(RID p_rid) override;
virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override;
virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override;
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
Ref<Image> texture_2d_placeholder;
Vector<Ref<Image>> texture_2d_array_placeholder;
Vector<Ref<Image>> cubemap_placeholder;
Vector<Ref<Image>> texture_3d_placeholder;
//these two APIs can be used together or in combination with the others.
virtual void texture_2d_placeholder_initialize(RID p_texture) override;
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;
virtual void texture_3d_placeholder_initialize(RID p_texture) override;
virtual Ref<Image> texture_2d_get(RID p_texture) const override;
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override;
virtual Vector<Ref<Image>> texture_3d_get(RID p_texture) const override;
virtual void texture_replace(RID p_texture, RID p_by_texture) override;
virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override;
virtual void texture_set_path(RID p_texture, const String &p_path) override;
virtual String texture_get_path(RID p_texture) const override;
virtual void texture_set_detect_3d_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
void texture_set_detect_srgb_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata);
virtual void texture_set_detect_normal_callback(RID p_texture, RS::TextureDetectCallback p_callback, void *p_userdata) override;
virtual void texture_set_detect_roughness_callback(RID p_texture, RS::TextureDetectRoughnessCallback p_callback, void *p_userdata) override;
virtual void texture_debug_usage(List<RS::TextureInfo> *r_info) override;
virtual void texture_set_force_redraw_if_visible(RID p_texture, bool p_enable) override;
virtual Size2 texture_size_with_proxy(RID p_proxy) override;
virtual void texture_rd_initialize(RID p_texture, const RID &p_rd_texture, const RS::TextureLayeredType p_layer_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;
virtual RID texture_get_rd_texture(RID p_texture, bool p_srgb = false) const override;
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override;
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
Vector3i texture_get_size(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
uint32_t texture_get_depth(RID p_texture) const;
void texture_bind(RID p_texture, uint32_t p_texture_no);
/* TEXTURE ATLAS API */
void update_texture_atlas();
GLuint texture_atlas_get_texture() const;
_FORCE_INLINE_ Rect2 texture_atlas_get_texture_rect(RID p_texture) {
TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture);
if (!t) {
return Rect2();
}
return t->uv_rect;
}
void texture_add_to_texture_atlas(RID p_texture);
void texture_remove_from_texture_atlas(RID p_texture);
void texture_atlas_mark_dirty_on_texture(RID p_texture);
void texture_atlas_remove_texture(RID p_texture);
/* DECAL API */
virtual RID decal_allocate() override;
virtual void decal_initialize(RID p_rid) override;
virtual void decal_free(RID p_rid) override {}
virtual void decal_set_size(RID p_decal, const Vector3 &p_size) override;
virtual void decal_set_texture(RID p_decal, RS::DecalTexture p_type, RID p_texture) override;
virtual void decal_set_emission_energy(RID p_decal, float p_energy) override;
virtual void decal_set_albedo_mix(RID p_decal, float p_mix) override;
virtual void decal_set_modulate(RID p_decal, const Color &p_modulate) override;
virtual void decal_set_cull_mask(RID p_decal, uint32_t p_layers) override;
virtual void decal_set_distance_fade(RID p_decal, bool p_enabled, float p_begin, float p_length) override;
virtual void decal_set_fade(RID p_decal, float p_above, float p_below) override;
virtual void decal_set_normal_fade(RID p_decal, float p_fade) override;
virtual AABB decal_get_aabb(RID p_decal) const override;
virtual uint32_t decal_get_cull_mask(RID p_decal) const override { return 0; }
virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {}
/* DECAL INSTANCE */
virtual RID decal_instance_create(RID p_decal) override { return RID(); }
virtual void decal_instance_free(RID p_decal_instance) override {}
virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override {}
virtual void decal_instance_set_sorting_offset(RID p_decal_instance, float p_sorting_offset) override {}
/* RENDER TARGET API */
static GLuint system_fbo;
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); }
bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); }
void check_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture);
virtual RID render_target_create() override;
virtual void render_target_free(RID p_rid) override;
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override;
virtual Point2i render_target_get_position(RID p_render_target) const override;
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override;
virtual Size2i render_target_get_size(RID p_render_target) const override;
virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override;
virtual bool render_target_get_transparent(RID p_render_target) const override;
virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override;
virtual bool render_target_get_direct_to_screen(RID p_render_target) const override;
virtual bool render_target_was_used(RID p_render_target) const override;
void render_target_clear_used(RID p_render_target);
virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override;
virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override;
virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override {}
virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override { return false; }
virtual void render_target_do_msaa_resolve(RID p_render_target) override {}
virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override;
virtual bool render_target_is_using_hdr(RID p_render_target) const override;
// new
void render_target_set_as_unused(RID p_render_target) override {
render_target_clear_used(p_render_target);
}
GLuint render_target_get_color_internal_format(RID p_render_target) const;
GLuint render_target_get_color_format(RID p_render_target) const;
GLuint render_target_get_color_type(RID p_render_target) const;
uint32_t render_target_get_color_format_size(RID p_render_target) const;
void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override;
bool render_target_is_clear_requested(RID p_render_target) override;
Color render_target_get_clear_request_color(RID p_render_target) override;
void render_target_disable_clear_request(RID p_render_target) override;
void render_target_do_clear_request(RID p_render_target) override;
GLuint render_target_get_fbo(RID p_render_target) const;
GLuint render_target_get_color(RID p_render_target) const;
GLuint render_target_get_depth(RID p_render_target) const;
void render_target_set_reattach_textures(RID p_render_target, bool p_reattach_textures) const;
bool render_target_is_reattach_textures(RID p_render_target) const;
virtual void render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) override;
virtual Rect2i render_target_get_sdf_rect(RID p_render_target) const override;
GLuint render_target_get_sdf_texture(RID p_render_target);
GLuint render_target_get_sdf_framebuffer(RID p_render_target);
void render_target_sdf_process(RID p_render_target);
virtual void render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) override;
bool render_target_is_sdf_enabled(RID p_render_target) const;
void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps);
void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color);
void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region);
virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {}
virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; }
virtual void render_target_set_vrs_update_mode(RID p_render_target, RS::ViewportVRSUpdateMode p_mode) override {}
virtual RS::ViewportVRSUpdateMode render_target_get_vrs_update_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_UPDATE_DISABLED; }
virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {}
virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); }
virtual void render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture, RID p_velocity_depth_texture) override;
virtual RID render_target_get_override_color(RID p_render_target) const override;
virtual RID render_target_get_override_depth(RID p_render_target) const override;
virtual RID render_target_get_override_velocity(RID p_render_target) const override;
virtual RID render_target_get_override_velocity_depth(RID p_render_target) const override { return RID(); }
virtual void render_target_set_render_region(RID p_render_target, const Rect2i &p_render_region) override;
virtual Rect2i render_target_get_render_region(RID p_render_target) const override;
virtual RID render_target_get_texture(RID p_render_target) override;
virtual void render_target_set_velocity_target_size(RID p_render_target, const Size2i &p_target_size) override {}
virtual Size2i render_target_get_velocity_target_size(RID p_render_target) const override { return Size2i(); }
void bind_framebuffer(GLuint framebuffer) {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
void bind_framebuffer_system() {
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
}
String get_framebuffer_error(GLenum p_status);
};
inline String TextureStorage::get_framebuffer_error(GLenum p_status) {
#if defined(DEBUG_ENABLED) && defined(GL_API_ENABLED)
if (p_status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER) {
return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
} else if (p_status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER) {
return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
}
#endif
return itos(p_status);
}
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // TEXTURE_STORAGE_GLES3_H

View file

@ -0,0 +1,480 @@
/**************************************************************************/
/* utilities.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifdef GLES3_ENABLED
#include "utilities.h"
#include "../rasterizer_gles3.h"
#include "config.h"
#include "light_storage.h"
#include "material_storage.h"
#include "mesh_storage.h"
#include "particles_storage.h"
#include "texture_storage.h"
using namespace GLES3;
Utilities *Utilities::singleton = nullptr;
Utilities::Utilities() {
singleton = this;
frame = 0;
for (int i = 0; i < FRAME_COUNT; i++) {
frames[i].index = 0;
glGenQueries(max_timestamp_query_elements, frames[i].queries);
frames[i].timestamp_names.resize(max_timestamp_query_elements);
frames[i].timestamp_cpu_values.resize(max_timestamp_query_elements);
frames[i].timestamp_count = 0;
frames[i].timestamp_result_names.resize(max_timestamp_query_elements);
frames[i].timestamp_cpu_result_values.resize(max_timestamp_query_elements);
frames[i].timestamp_result_values.resize(max_timestamp_query_elements);
frames[i].timestamp_result_count = 0;
}
}
Utilities::~Utilities() {
singleton = nullptr;
for (int i = 0; i < FRAME_COUNT; i++) {
glDeleteQueries(max_timestamp_query_elements, frames[i].queries);
}
if (texture_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : texture_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Texture with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < texture_mem_cache) {
ERR_PRINT("Texture cache is not empty. There may be an additional texture leak of " + itos(texture_mem_cache - leaked_data_size) + " bytes.");
}
}
if (render_buffer_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : render_buffer_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Render buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < render_buffer_mem_cache) {
ERR_PRINT("Render buffer cache is not empty. There may be an additional render buffer leak of " + itos(render_buffer_mem_cache - leaked_data_size) + " bytes.");
}
}
if (buffer_mem_cache) {
uint32_t leaked_data_size = 0;
for (const KeyValue<GLuint, ResourceAllocation> &E : buffer_allocs_cache) {
#ifdef DEV_ENABLED
ERR_PRINT(E.value.name + ": leaked " + itos(E.value.size) + " bytes.");
#else
ERR_PRINT("Buffer with GL ID of " + itos(E.key) + ": leaked " + itos(E.value.size) + " bytes.");
#endif
leaked_data_size += E.value.size;
}
if (leaked_data_size < buffer_mem_cache) {
ERR_PRINT("Buffer cache is not empty. There may be an additional buffer leak of " + itos(buffer_mem_cache - leaked_data_size) + " bytes.");
}
}
}
Vector<uint8_t> Utilities::buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size) {
Vector<uint8_t> ret;
if (p_buffer_size == 0) {
return ret;
}
ret.resize(p_buffer_size);
glBindBuffer(p_target, p_buffer);
#if defined(__EMSCRIPTEN__)
{
uint8_t *w = ret.ptrw();
godot_webgl2_glGetBufferSubData(p_target, 0, p_buffer_size, w);
}
#else
void *data = glMapBufferRange(p_target, 0, p_buffer_size, GL_MAP_READ_BIT);
ERR_FAIL_NULL_V(data, Vector<uint8_t>());
{
uint8_t *w = ret.ptrw();
memcpy(w, data, p_buffer_size);
}
glUnmapBuffer(p_target);
#endif
glBindBuffer(p_target, 0);
return ret;
}
/* INSTANCES */
RS::InstanceType Utilities::get_base_type(RID p_rid) const {
if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
return RS::INSTANCE_MESH;
} else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
return RS::INSTANCE_MULTIMESH;
} else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
return RS::INSTANCE_LIGHT;
} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
return RS::INSTANCE_LIGHTMAP;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
return RS::INSTANCE_PARTICLES;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
return RS::INSTANCE_REFLECTION_PROBE;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
return RS::INSTANCE_PARTICLES_COLLISION;
} else if (owns_visibility_notifier(p_rid)) {
return RS::INSTANCE_VISIBLITY_NOTIFIER;
}
return RS::INSTANCE_NONE;
}
bool Utilities::free(RID p_rid) {
if (GLES3::TextureStorage::get_singleton()->owns_render_target(p_rid)) {
GLES3::TextureStorage::get_singleton()->render_target_free(p_rid);
return true;
} else if (GLES3::TextureStorage::get_singleton()->owns_texture(p_rid)) {
GLES3::TextureStorage::get_singleton()->texture_free(p_rid);
return true;
} else if (GLES3::TextureStorage::get_singleton()->owns_canvas_texture(p_rid)) {
GLES3::TextureStorage::get_singleton()->canvas_texture_free(p_rid);
return true;
} else if (GLES3::MaterialStorage::get_singleton()->owns_shader(p_rid)) {
GLES3::MaterialStorage::get_singleton()->shader_free(p_rid);
return true;
} else if (GLES3::MaterialStorage::get_singleton()->owns_material(p_rid)) {
GLES3::MaterialStorage::get_singleton()->material_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_mesh(p_rid)) {
GLES3::MeshStorage::get_singleton()->mesh_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_multimesh(p_rid)) {
GLES3::MeshStorage::get_singleton()->multimesh_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_mesh_instance(p_rid)) {
GLES3::MeshStorage::get_singleton()->mesh_instance_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) {
GLES3::LightStorage::get_singleton()->light_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
GLES3::LightStorage::get_singleton()->lightmap_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_probe_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_atlas(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_atlas_free(p_rid);
return true;
} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe_instance(p_rid)) {
GLES3::LightStorage::get_singleton()->reflection_probe_instance_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_collision_free(p_rid);
return true;
} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision_instance(p_rid)) {
GLES3::ParticlesStorage::get_singleton()->particles_collision_instance_free(p_rid);
return true;
} else if (GLES3::MeshStorage::get_singleton()->owns_skeleton(p_rid)) {
GLES3::MeshStorage::get_singleton()->skeleton_free(p_rid);
return true;
} else if (owns_visibility_notifier(p_rid)) {
visibility_notifier_free(p_rid);
return true;
} else {
return false;
}
}
/* DEPENDENCIES */
void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance) {
if (MeshStorage::get_singleton()->owns_mesh(p_base)) {
Mesh *mesh = MeshStorage::get_singleton()->get_mesh(p_base);
p_instance->update_dependency(&mesh->dependency);
} else if (MeshStorage::get_singleton()->owns_multimesh(p_base)) {
MultiMesh *multimesh = MeshStorage::get_singleton()->get_multimesh(p_base);
p_instance->update_dependency(&multimesh->dependency);
if (multimesh->mesh.is_valid()) {
base_update_dependency(multimesh->mesh, p_instance);
}
} else if (LightStorage::get_singleton()->owns_reflection_probe(p_base)) {
Dependency *dependency = LightStorage::get_singleton()->reflection_probe_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (LightStorage::get_singleton()->owns_light(p_base)) {
Light *l = LightStorage::get_singleton()->get_light(p_base);
p_instance->update_dependency(&l->dependency);
} else if (ParticlesStorage::get_singleton()->owns_particles(p_base)) {
Dependency *dependency = ParticlesStorage::get_singleton()->particles_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (ParticlesStorage::get_singleton()->owns_particles_collision(p_base)) {
Dependency *dependency = ParticlesStorage::get_singleton()->particles_collision_get_dependency(p_base);
p_instance->update_dependency(dependency);
} else if (owns_visibility_notifier(p_base)) {
VisibilityNotifier *vn = get_visibility_notifier(p_base);
p_instance->update_dependency(&vn->dependency);
}
}
/* VISIBILITY NOTIFIER */
RID Utilities::visibility_notifier_allocate() {
return visibility_notifier_owner.allocate_rid();
}
void Utilities::visibility_notifier_initialize(RID p_notifier) {
visibility_notifier_owner.initialize_rid(p_notifier, VisibilityNotifier());
}
void Utilities::visibility_notifier_free(RID p_notifier) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
vn->dependency.deleted_notify(p_notifier);
visibility_notifier_owner.free(p_notifier);
}
void Utilities::visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
vn->aabb = p_aabb;
vn->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
void Utilities::visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
vn->enter_callback = p_enter_callbable;
vn->exit_callback = p_exit_callable;
}
AABB Utilities::visibility_notifier_get_aabb(RID p_notifier) const {
const VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL_V(vn, AABB());
return vn->aabb;
}
void Utilities::visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) {
VisibilityNotifier *vn = visibility_notifier_owner.get_or_null(p_notifier);
ERR_FAIL_NULL(vn);
if (p_enter) {
if (vn->enter_callback.is_valid()) {
if (p_deferred) {
vn->enter_callback.call_deferred();
} else {
vn->enter_callback.call();
}
}
} else {
if (vn->exit_callback.is_valid()) {
if (p_deferred) {
vn->exit_callback.call_deferred();
} else {
vn->exit_callback.call();
}
}
}
}
/* TIMING */
void Utilities::capture_timestamps_begin() {
capture_timestamp("Frame Begin");
}
void Utilities::capture_timestamp(const String &p_name) {
ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements);
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
glQueryCounter(frames[frame].queries[frames[frame].timestamp_count], GL_TIMESTAMP);
}
#endif // GL_API_ENABLED
frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name;
frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec();
frames[frame].timestamp_count++;
}
void Utilities::_capture_timestamps_begin() {
// frame is incremented at the end of the frame so this gives us the queries for frame - 2. By then they should be ready.
if (frames[frame].timestamp_count) {
#ifdef GL_API_ENABLED
if (RasterizerGLES3::is_gles_over_gl()) {
for (uint32_t i = 0; i < frames[frame].timestamp_count; i++) {
uint64_t temp = 0;
glGetQueryObjectui64v(frames[frame].queries[i], GL_QUERY_RESULT, &temp);
frames[frame].timestamp_result_values[i] = temp;
}
}
#endif // GL_API_ENABLED
SWAP(frames[frame].timestamp_names, frames[frame].timestamp_result_names);
SWAP(frames[frame].timestamp_cpu_values, frames[frame].timestamp_cpu_result_values);
}
frames[frame].timestamp_result_count = frames[frame].timestamp_count;
frames[frame].timestamp_count = 0;
frames[frame].index = Engine::get_singleton()->get_frames_drawn();
capture_timestamp("Internal Begin");
}
void Utilities::capture_timestamps_end() {
capture_timestamp("Internal End");
frame = (frame + 1) % FRAME_COUNT;
}
uint32_t Utilities::get_captured_timestamps_count() const {
return frames[frame].timestamp_result_count;
}
uint64_t Utilities::get_captured_timestamps_frame() const {
return frames[frame].index;
}
uint64_t Utilities::get_captured_timestamp_gpu_time(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return frames[frame].timestamp_result_values[p_index];
}
uint64_t Utilities::get_captured_timestamp_cpu_time(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, 0);
return frames[frame].timestamp_cpu_result_values[p_index];
}
String Utilities::get_captured_timestamp_name(uint32_t p_index) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_index, frames[frame].timestamp_result_count, String());
return frames[frame].timestamp_result_names[p_index];
}
/* MISC */
void Utilities::update_dirty_resources() {
MaterialStorage::get_singleton()->_update_global_shader_uniforms();
MaterialStorage::get_singleton()->_update_queued_materials();
MeshStorage::get_singleton()->_update_dirty_skeletons();
MeshStorage::get_singleton()->_update_dirty_multimeshes();
TextureStorage::get_singleton()->update_texture_atlas();
}
void Utilities::set_debug_generate_wireframes(bool p_generate) {
Config *config = Config::get_singleton();
config->generate_wireframes = p_generate;
}
bool Utilities::has_os_feature(const String &p_feature) const {
Config *config = Config::get_singleton();
if (!config) {
return false;
}
if (p_feature == "rgtc") {
return config->rgtc_supported;
}
if (p_feature == "s3tc") {
return config->s3tc_supported;
}
if (p_feature == "bptc") {
return config->bptc_supported;
}
if (p_feature == "astc") {
return config->astc_supported;
}
if (p_feature == "etc2") {
return config->etc2_supported;
}
return false;
}
void Utilities::update_memory_info() {
}
uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) {
if (p_info == RS::RENDERING_INFO_TEXTURE_MEM_USED) {
return texture_mem_cache + render_buffer_mem_cache; // Add render buffer memory to our texture mem.
} else if (p_info == RS::RENDERING_INFO_BUFFER_MEM_USED) {
return buffer_mem_cache;
} else if (p_info == RS::RENDERING_INFO_VIDEO_MEM_USED) {
return texture_mem_cache + buffer_mem_cache + render_buffer_mem_cache;
}
return 0;
}
String Utilities::get_video_adapter_name() const {
const String rendering_device_name = String::utf8((const char *)glGetString(GL_RENDERER));
// NVIDIA suffixes all GPU model names with "/PCIe/SSE2" in OpenGL (but not Vulkan). This isn't necessary to display nowadays, so it can be trimmed.
return rendering_device_name.trim_suffix("/PCIe/SSE2");
}
String Utilities::get_video_adapter_vendor() const {
const String rendering_device_vendor = String::utf8((const char *)glGetString(GL_VENDOR));
// NVIDIA suffixes its vendor name with " Corporation". This is neither necessary to process nor display.
return rendering_device_vendor.trim_suffix(" Corporation");
}
RenderingDevice::DeviceType Utilities::get_video_adapter_type() const {
return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER;
}
String Utilities::get_video_adapter_api_version() const {
return String::utf8((const char *)glGetString(GL_VERSION));
}
Size2i Utilities::get_maximum_viewport_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, Size2i());
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
}
uint32_t Utilities::get_maximum_shader_varyings() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 31);
return config->max_shader_varyings;
}
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 65536);
return uint64_t(config->max_uniform_buffer_size);
}
#endif // GLES3_ENABLED

View file

@ -0,0 +1,237 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef UTILITIES_GLES3_H
#define UTILITIES_GLES3_H
#ifdef GLES3_ENABLED
#include "servers/rendering/storage/utilities.h"
#include "platform_gl.h"
namespace GLES3 {
/* VISIBILITY NOTIFIER */
struct VisibilityNotifier {
AABB aabb;
Callable enter_callback;
Callable exit_callback;
Dependency dependency;
};
class Utilities : public RendererUtilities {
private:
static Utilities *singleton;
/* VISIBILITY NOTIFIER */
mutable RID_Owner<VisibilityNotifier> visibility_notifier_owner;
/* MISC */
struct ResourceAllocation {
#ifdef DEV_ENABLED
String name;
#endif
uint32_t size = 0;
};
HashMap<GLuint, ResourceAllocation> buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> render_buffer_allocs_cache;
HashMap<GLuint, ResourceAllocation> texture_allocs_cache;
uint64_t buffer_mem_cache = 0;
uint64_t render_buffer_mem_cache = 0;
uint64_t texture_mem_cache = 0;
public:
static Utilities *get_singleton() { return singleton; }
Utilities();
~Utilities();
// Buffer size is specified in bytes
static Vector<uint8_t> buffer_get_data(GLenum p_target, GLuint p_buffer, uint32_t p_buffer_size);
// Allocate memory with glBufferData. Does not handle resizing.
_FORCE_INLINE_ void buffer_allocate_data(GLenum p_target, GLuint p_id, uint32_t p_size, const void *p_data, GLenum p_usage, String p_name = "") {
glBufferData(p_target, p_size, p_data, p_usage);
buffer_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(buffer_allocs_cache.has(p_id), "trying to allocate buffer with name " + p_name + " but ID already used by " + buffer_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
buffer_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void buffer_free_data(GLuint p_id) {
ERR_FAIL_COND(!buffer_allocs_cache.has(p_id));
glDeleteBuffers(1, &p_id);
buffer_mem_cache -= buffer_allocs_cache[p_id].size;
buffer_allocs_cache.erase(p_id);
}
_FORCE_INLINE_ void render_buffer_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") {
render_buffer_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(render_buffer_allocs_cache.has(p_id), "trying to allocate render buffer with name " + p_name + " but ID already used by " + render_buffer_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
render_buffer_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void render_buffer_free_data(GLuint p_id) {
ERR_FAIL_COND(!render_buffer_allocs_cache.has(p_id));
glDeleteRenderbuffers(1, &p_id);
render_buffer_mem_cache -= render_buffer_allocs_cache[p_id].size;
render_buffer_allocs_cache.erase(p_id);
}
// Records that data was allocated for state tracking purposes.
// Size is measured in bytes.
_FORCE_INLINE_ void texture_allocated_data(GLuint p_id, uint32_t p_size, String p_name = "") {
texture_mem_cache += p_size;
#ifdef DEV_ENABLED
ERR_FAIL_COND_MSG(texture_allocs_cache.has(p_id), "trying to allocate texture with name " + p_name + " but ID already used by " + texture_allocs_cache[p_id].name);
#endif
ResourceAllocation resource_allocation;
resource_allocation.size = p_size;
#ifdef DEV_ENABLED
resource_allocation.name = p_name + ": " + itos((uint64_t)p_id);
#endif
texture_allocs_cache[p_id] = resource_allocation;
}
_FORCE_INLINE_ void texture_free_data(GLuint p_id) {
ERR_FAIL_COND(!texture_allocs_cache.has(p_id));
glDeleteTextures(1, &p_id);
texture_mem_cache -= texture_allocs_cache[p_id].size;
texture_allocs_cache.erase(p_id);
}
_FORCE_INLINE_ void texture_resize_data(GLuint p_id, uint32_t p_size) {
ERR_FAIL_COND(!texture_allocs_cache.has(p_id));
texture_mem_cache -= texture_allocs_cache[p_id].size;
texture_mem_cache += p_size;
texture_allocs_cache[p_id].size = p_size;
}
/* INSTANCES */
virtual RS::InstanceType get_base_type(RID p_rid) const override;
virtual bool free(RID p_rid) override;
/* DEPENDENCIES */
virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override;
/* VISIBILITY NOTIFIER */
VisibilityNotifier *get_visibility_notifier(RID p_rid) { return visibility_notifier_owner.get_or_null(p_rid); }
bool owns_visibility_notifier(RID p_rid) const { return visibility_notifier_owner.owns(p_rid); }
virtual RID visibility_notifier_allocate() override;
virtual void visibility_notifier_initialize(RID p_notifier) override;
virtual void visibility_notifier_free(RID p_notifier) override;
virtual void visibility_notifier_set_aabb(RID p_notifier, const AABB &p_aabb) override;
virtual void visibility_notifier_set_callbacks(RID p_notifier, const Callable &p_enter_callbable, const Callable &p_exit_callable) override;
virtual AABB visibility_notifier_get_aabb(RID p_notifier) const override;
virtual void visibility_notifier_call(RID p_notifier, bool p_enter, bool p_deferred) override;
/* TIMING */
#define MAX_QUERIES 256
#define FRAME_COUNT 3
struct Frame {
GLuint queries[MAX_QUERIES];
TightLocalVector<String> timestamp_names;
TightLocalVector<uint64_t> timestamp_cpu_values;
uint32_t timestamp_count = 0;
TightLocalVector<String> timestamp_result_names;
TightLocalVector<uint64_t> timestamp_cpu_result_values;
TightLocalVector<uint64_t> timestamp_result_values;
uint32_t timestamp_result_count = 0;
uint64_t index = 0;
};
const uint32_t max_timestamp_query_elements = MAX_QUERIES;
Frame frames[FRAME_COUNT]; // Frames for capturing timestamps. We use 3 so we don't need to wait for commands to complete
uint32_t frame = 0;
virtual void capture_timestamps_begin() override;
virtual void capture_timestamp(const String &p_name) override;
virtual uint32_t get_captured_timestamps_count() const override;
virtual uint64_t get_captured_timestamps_frame() const override;
virtual uint64_t get_captured_timestamp_gpu_time(uint32_t p_index) const override;
virtual uint64_t get_captured_timestamp_cpu_time(uint32_t p_index) const override;
virtual String get_captured_timestamp_name(uint32_t p_index) const override;
void _capture_timestamps_begin();
void capture_timestamps_end();
/* MISC */
virtual void update_dirty_resources() override;
virtual void set_debug_generate_wireframes(bool p_generate) override;
virtual bool has_os_feature(const String &p_feature) const override;
virtual void update_memory_info() override;
virtual uint64_t get_rendering_info(RS::RenderingInfo p_info) override;
virtual String get_video_adapter_name() const override;
virtual String get_video_adapter_vendor() const override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
virtual String get_video_adapter_api_version() const override;
virtual Size2i get_maximum_viewport_size() const override;
virtual uint32_t get_maximum_shader_varyings() const override;
virtual uint64_t get_maximum_uniform_buffer_size() const override;
};
} // namespace GLES3
#endif // GLES3_ENABLED
#endif // UTILITIES_GLES3_H

View file

@ -0,0 +1,39 @@
# Metal Rendering Device
This document aims to describe the Metal rendering device implementation in Godot.
## Future work / ideas
* Use placement heaps
* Explicit hazard tracking
* [MetalFX] upscaling support?
## Acknowledgments
The Metal rendering owes a lot to the work of the [MoltenVK] project, which is a Vulkan implementation on top of Metal.
In accordance with the Apache 2.0 license, the following copyright notices have been included where applicable:
```
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
```
[MoltenVK]: https://github.com/KhronosGroup/MoltenVK
[MetalFX]: https://developer.apple.com/documentation/metalfx?language=objc

View file

@ -0,0 +1,50 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
env_metal = env.Clone()
# Thirdparty source files
thirdparty_obj = []
thirdparty_dir = "#thirdparty/spirv-cross/"
thirdparty_sources = [
"spirv_cfg.cpp",
"spirv_cross_util.cpp",
"spirv_cross.cpp",
"spirv_parser.cpp",
"spirv_msl.cpp",
"spirv_reflect.cpp",
"spirv_glsl.cpp",
"spirv_cross_parsed_ir.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_metal.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"])
# Must enable exceptions for SPIRV-Cross; otherwise, it will abort the process on errors.
if "-fno-exceptions" in env_metal["CXXFLAGS"]:
env_metal["CXXFLAGS"].remove("-fno-exceptions")
env_metal.Append(CXXFLAGS=["-fexceptions"])
env_thirdparty = env_metal.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env_metal.drivers_sources += thirdparty_obj
# Enable C++20 for the Objective-C++ Metal code, which uses C++20 concepts.
if "-std=gnu++17" in env_metal["CXXFLAGS"]:
env_metal["CXXFLAGS"].remove("-std=gnu++17")
env_metal.Append(CXXFLAGS=["-std=c++20"])
# Driver source files
driver_obj = []
env_metal.add_source_files(driver_obj, "*.mm")
env.drivers_sources += driver_obj
# Needed to force rebuilding the driver files when the thirdparty library is updated.
env.Depends(driver_obj, thirdparty_obj)

View file

@ -0,0 +1,125 @@
/**************************************************************************/
/* inflection_map.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef INFLECTION_MAP_H
#define INFLECTION_MAP_H
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
/// An unordered map that splits elements between a fast-access vector of LinearCount consecutively
/// indexed elements, and a slower-access map holding sparse indexes larger than LinearCount.
///
/// \tparam KeyType is used to lookup values, and must be a type that is convertible to an unsigned integer.
/// \tparam ValueType must have an empty constructor (default or otherwise).
/// \tparam LinearCount
/// \tparam IndexType must be a type that is convertible to an unsigned integer (eg. uint8_t...uint64_t), and which is large enough to represent the number of values in this map.
template <typename KeyType, typename ValueType, size_t LinearCount, typename IndexType = uint16_t>
class InflectionMap {
public:
using value_type = ValueType;
class Iterator {
InflectionMap *map;
IndexType index;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = ValueType;
using pointer = value_type *;
using reference = value_type &;
Iterator() :
map(nullptr), index(0) {}
Iterator(InflectionMap &p_m, const IndexType p_i) :
map(&p_m), index(p_i) {}
Iterator &operator=(const Iterator &p_it) {
map = p_it.map;
index = p_it.index;
return *this;
}
ValueType *operator->() { return &map->_values[index]; }
ValueType &operator*() { return map->_values[index]; }
operator ValueType *() { return &map->_values[index]; }
bool operator==(const Iterator &p_it) const { return map == p_it.map && index == p_it.index; }
bool operator!=(const Iterator &p_it) const { return map != p_it.map || index != p_it.index; }
Iterator &operator++() {
index++;
return *this;
}
Iterator operator++(int) {
Iterator t = *this;
index++;
return t;
}
bool is_valid() const { return index < map->_values.size(); }
};
const ValueType &operator[](const KeyType p_idx) const { return get_value(p_idx); }
ValueType &operator[](const KeyType p_idx) { return get_value(p_idx); }
Iterator begin() { return Iterator(*this, 0); }
Iterator end() { return Iterator(*this, _values.size()); }
bool is_empty() { return _values.is_empty(); }
size_t size() { return _values.size(); }
void reserve(size_t p_new_cap) { _values.reserve(p_new_cap); }
protected:
static constexpr IndexType INVALID = std::numeric_limits<IndexType>::max();
typedef struct IndexValue {
IndexType value = INVALID;
} IndexValue;
// Returns a reference to the value at the index.
// If the index has not been initialized, add an empty element at
// the end of the values array, and set the index to its position.
ValueType &get_value(KeyType p_idx) {
IndexValue *val_idx = p_idx < LinearCount ? &_linear_indexes[p_idx] : _inflection_indexes.getptr(p_idx);
if (val_idx == nullptr || val_idx->value == INVALID) {
_values.push_back({});
if (val_idx == nullptr) {
val_idx = &_inflection_indexes.insert(p_idx, {})->value;
}
val_idx->value = _values.size() - 1;
}
return _values[val_idx->value];
}
TightLocalVector<ValueType> _values;
HashMap<KeyType, IndexValue> _inflection_indexes;
IndexValue _linear_indexes[LinearCount];
};
#endif // INFLECTION_MAP_H

View file

@ -0,0 +1,156 @@
/**************************************************************************/
/* metal_device_properties.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. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#ifndef METAL_DEVICE_PROPERTIES_H
#define METAL_DEVICE_PROPERTIES_H
#import "servers/rendering/rendering_device.h"
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
/** The buffer index to use for vertex content. */
const static uint32_t VERT_CONTENT_BUFFER_INDEX = 0;
const static uint32_t MAX_COLOR_ATTACHMENT_COUNT = 8;
typedef NS_OPTIONS(NSUInteger, SampleCount) {
SampleCount1 = (1UL << 0),
SampleCount2 = (1UL << 1),
SampleCount4 = (1UL << 2),
SampleCount8 = (1UL << 3),
SampleCount16 = (1UL << 4),
SampleCount32 = (1UL << 5),
SampleCount64 = (1UL << 6),
};
struct API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalFeatures {
uint32_t mslVersion = 0;
MTLGPUFamily highestFamily = MTLGPUFamilyApple4;
bool supportsBCTextureCompression = false;
bool supportsDepth24Stencil8 = false;
bool supports32BitFloatFiltering = false;
bool supports32BitMSAA = false;
bool supportsMac = TARGET_OS_OSX;
MTLLanguageVersion mslVersionEnum = MTLLanguageVersion1_2;
SampleCount supportedSampleCounts = SampleCount1;
long hostMemoryPageSize = 0;
bool layeredRendering = false;
bool multisampleLayeredRendering = false;
bool quadPermute = false; /**< If true, quadgroup permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdPermute = false; /**< If true, SIMD-group permutation functions (vote, ballot, shuffle) are supported in shaders. */
bool simdReduction = false; /**< If true, SIMD-group reduction functions (arithmetic) are supported in shaders. */
bool tessellationShader = false; /**< If true, tessellation shaders are supported. */
bool imageCubeArray = false; /**< If true, image cube arrays are supported. */
MTLArgumentBuffersTier argument_buffers_tier = MTLArgumentBuffersTier1;
/// If true, argument encoders are required to encode arguments into an argument buffer.
bool needs_arg_encoders = true;
bool metal_fx_spatial = false; /**< If true, Metal FX spatial functions are supported. */
bool metal_fx_temporal = false; /**< If true, Metal FX temporal functions are supported. */
bool supports_gpu_address = false; /**< If true, referencing a GPU address in a shader is supported. */
};
struct MetalLimits {
uint64_t maxImageArrayLayers;
uint64_t maxFramebufferHeight;
uint64_t maxFramebufferWidth;
uint64_t maxImageDimension1D;
uint64_t maxImageDimension2D;
uint64_t maxImageDimension3D;
uint64_t maxImageDimensionCube;
uint64_t maxViewportDimensionX;
uint64_t maxViewportDimensionY;
MTLSize maxThreadsPerThreadGroup;
MTLSize maxComputeWorkGroupCount;
uint64_t maxBoundDescriptorSets;
uint64_t maxColorAttachments;
uint64_t maxTexturesPerArgumentBuffer;
uint64_t maxSamplersPerArgumentBuffer;
uint64_t maxBuffersPerArgumentBuffer;
uint64_t maxBufferLength;
uint64_t minUniformBufferOffsetAlignment;
uint64_t maxVertexDescriptorLayoutStride;
uint16_t maxViewports;
uint32_t maxPerStageBufferCount; /**< The total number of per-stage Metal buffers available for shader uniform content and attributes. */
uint32_t maxPerStageTextureCount; /**< The total number of per-stage Metal textures available for shader uniform content. */
uint32_t maxPerStageSamplerCount; /**< The total number of per-stage Metal samplers available for shader uniform content. */
uint32_t maxVertexInputAttributes;
uint32_t maxVertexInputBindings;
uint32_t maxVertexInputBindingStride;
uint32_t maxDrawIndexedIndexValue;
uint32_t maxShaderVaryings;
double temporalScalerInputContentMinScale;
double temporalScalerInputContentMaxScale;
uint32_t minSubgroupSize; /**< The minimum number of threads in a SIMD-group. */
uint32_t maxSubgroupSize; /**< The maximum number of threads in a SIMD-group. */
BitField<RDD::ShaderStage> subgroupSupportedShaderStages;
BitField<RD::SubgroupOperations> subgroupSupportedOperations; /**< The subgroup operations supported by the device. */
};
class API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) MetalDeviceProperties {
private:
void init_features(id<MTLDevice> p_device);
void init_limits(id<MTLDevice> p_device);
public:
MetalFeatures features;
MetalLimits limits;
SampleCount find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const;
MetalDeviceProperties(id<MTLDevice> p_device);
~MetalDeviceProperties();
private:
static const SampleCount sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX];
};
#endif // METAL_DEVICE_PROPERTIES_H

View file

@ -0,0 +1,367 @@
/**************************************************************************/
/* metal_device_properties.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. */
/**************************************************************************/
/**************************************************************************/
/* */
/* Portions of this code were derived from MoltenVK. */
/* */
/* Copyright (c) 2015-2023 The Brenwill Workshop Ltd. */
/* (http://www.brenwill.com) */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); */
/* you may not use this file except in compliance with the License. */
/* You may obtain a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */
/* implied. See the License for the specific language governing */
/* permissions and limitations under the License. */
/**************************************************************************/
#import "metal_device_properties.h"
#import <Metal/Metal.h>
#import <MetalFX/MetalFX.h>
#import <spirv_cross.hpp>
#import <spirv_msl.hpp>
// Common scaling multipliers.
#define KIBI (1024)
#define MEBI (KIBI * KIBI)
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MAX_ALLOWED < 140000) || (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED < 170000)
#define MTLGPUFamilyApple9 (MTLGPUFamily)1009
#endif
API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
MTLGPUFamily &operator--(MTLGPUFamily &p_family) {
p_family = static_cast<MTLGPUFamily>(static_cast<int>(p_family) - 1);
if (p_family < MTLGPUFamilyApple1) {
p_family = MTLGPUFamilyApple9;
}
return p_family;
}
void MetalDeviceProperties::init_features(id<MTLDevice> p_device) {
features = {};
features.highestFamily = MTLGPUFamilyApple1;
for (MTLGPUFamily family = MTLGPUFamilyApple9; family >= MTLGPUFamilyApple1; --family) {
if ([p_device supportsFamily:family]) {
features.highestFamily = family;
break;
}
}
if (@available(macOS 11, iOS 16.4, tvOS 16.4, *)) {
features.supportsBCTextureCompression = p_device.supportsBCTextureCompression;
} else {
features.supportsBCTextureCompression = false;
}
#if TARGET_OS_OSX
features.supportsDepth24Stencil8 = p_device.isDepth24Stencil8PixelFormatSupported;
#endif
if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
features.supports32BitFloatFiltering = p_device.supports32BitFloatFiltering;
features.supports32BitMSAA = p_device.supports32BitMSAA;
}
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.supports_gpu_address = true;
}
features.hostMemoryPageSize = sysconf(_SC_PAGESIZE);
for (SampleCount sc = SampleCount1; sc <= SampleCount64; sc <<= 1) {
if ([p_device supportsTextureSampleCount:sc]) {
features.supportedSampleCounts |= sc;
}
}
features.layeredRendering = [p_device supportsFamily:MTLGPUFamilyApple5];
features.multisampleLayeredRendering = [p_device supportsFamily:MTLGPUFamilyApple7];
features.tessellationShader = [p_device supportsFamily:MTLGPUFamilyApple3];
features.imageCubeArray = [p_device supportsFamily:MTLGPUFamilyApple3];
features.quadPermute = [p_device supportsFamily:MTLGPUFamilyApple4];
features.simdPermute = [p_device supportsFamily:MTLGPUFamilyApple6];
features.simdReduction = [p_device supportsFamily:MTLGPUFamilyApple7];
features.argument_buffers_tier = p_device.argumentBuffersSupport;
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.needs_arg_encoders = !([p_device supportsFamily:MTLGPUFamilyMetal3] && features.argument_buffers_tier == MTLArgumentBuffersTier2);
}
if (@available(macOS 13.0, iOS 16.0, tvOS 16.0, *)) {
features.metal_fx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:p_device];
features.metal_fx_temporal = [MTLFXTemporalScalerDescriptor supportsDevice:p_device];
}
MTLCompileOptions *opts = [MTLCompileOptions new];
features.mslVersionEnum = opts.languageVersion; // By default, Metal uses the most recent language version.
#define setMSLVersion(m_maj, m_min) \
features.mslVersion = SPIRV_CROSS_NAMESPACE::CompilerMSL::Options::make_msl_version(m_maj, m_min)
switch (features.mslVersionEnum) {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 180000 || __TV_OS_VERSION_MAX_ALLOWED >= 180000
case MTLLanguageVersion3_2:
setMSLVersion(3, 2);
break;
#endif
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 || __TV_OS_VERSION_MAX_ALLOWED >= 170000
case MTLLanguageVersion3_1:
setMSLVersion(3, 1);
break;
#endif
case MTLLanguageVersion3_0:
setMSLVersion(3, 0);
break;
case MTLLanguageVersion2_4:
setMSLVersion(2, 4);
break;
case MTLLanguageVersion2_3:
setMSLVersion(2, 3);
break;
case MTLLanguageVersion2_2:
setMSLVersion(2, 2);
break;
case MTLLanguageVersion2_1:
setMSLVersion(2, 1);
break;
case MTLLanguageVersion2_0:
setMSLVersion(2, 0);
break;
case MTLLanguageVersion1_2:
setMSLVersion(1, 2);
break;
case MTLLanguageVersion1_1:
setMSLVersion(1, 1);
break;
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
case MTLLanguageVersion1_0:
setMSLVersion(1, 0);
break;
#endif
}
}
void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
using std::max;
using std::min;
// FST: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
// FST: Maximum number of layers per 1D texture array, 2D texture array, or 3D texture.
limits.maxImageArrayLayers = 2048;
if ([p_device supportsFamily:MTLGPUFamilyApple3]) {
// FST: Maximum 2D texture width and height.
limits.maxFramebufferWidth = 16384;
limits.maxFramebufferHeight = 16384;
limits.maxViewportDimensionX = 16384;
limits.maxViewportDimensionY = 16384;
// FST: Maximum 1D texture width.
limits.maxImageDimension1D = 16384;
// FST: Maximum 2D texture width and height.
limits.maxImageDimension2D = 16384;
// FST: Maximum cube map texture width and height.
limits.maxImageDimensionCube = 16384;
} else {
// FST: Maximum 2D texture width and height.
limits.maxFramebufferWidth = 8192;
limits.maxFramebufferHeight = 8192;
limits.maxViewportDimensionX = 8192;
limits.maxViewportDimensionY = 8192;
// FST: Maximum 1D texture width.
limits.maxImageDimension1D = 8192;
// FST: Maximum 2D texture width and height.
limits.maxImageDimension2D = 8192;
// FST: Maximum cube map texture width and height.
limits.maxImageDimensionCube = 8192;
}
// FST: Maximum 3D texture width, height, and depth.
limits.maxImageDimension3D = 2048;
limits.maxThreadsPerThreadGroup = p_device.maxThreadsPerThreadgroup;
// No effective limits.
limits.maxComputeWorkGroupCount = { std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max() };
// https://github.com/KhronosGroup/MoltenVK/blob/568cc3acc0e2299931fdaecaaa1fc3ec5b4af281/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h#L85
limits.maxBoundDescriptorSets = SPIRV_CROSS_NAMESPACE::kMaxArgumentBuffers;
// FST: Maximum number of color render targets per render pass descriptor.
limits.maxColorAttachments = 8;
// Maximum number of textures the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxTexturesPerArgumentBuffer = 1'000'000;
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxTexturesPerArgumentBuffer = 96;
} else {
limits.maxTexturesPerArgumentBuffer = 31;
}
// Maximum number of samplers the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxSamplersPerArgumentBuffer = 1024;
} else {
limits.maxSamplersPerArgumentBuffer = 16;
}
// Maximum number of buffers the device can access, per stage, from an argument buffer.
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxBuffersPerArgumentBuffer = std::numeric_limits<uint64_t>::max();
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxBuffersPerArgumentBuffer = 96;
} else {
limits.maxBuffersPerArgumentBuffer = 31;
}
limits.minSubgroupSize = limits.maxSubgroupSize = 1;
// These values were taken from MoltenVK.
if (features.simdPermute) {
limits.minSubgroupSize = 4;
limits.maxSubgroupSize = 32;
} else if (features.quadPermute) {
limits.minSubgroupSize = limits.maxSubgroupSize = 4;
}
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_COMPUTE_BIT);
if (features.tessellationShader) {
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT);
}
limits.subgroupSupportedShaderStages.set_flag(RDD::ShaderStage::SHADER_STAGE_FRAGMENT_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BASIC_BIT);
if (features.simdPermute || features.quadPermute) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_VOTE_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_BALLOT_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_BIT);
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT);
}
if (features.simdReduction) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT);
}
if (features.quadPermute) {
limits.subgroupSupportedOperations.set_flag(RD::SubgroupOperations::SUBGROUP_QUAD_BIT);
}
limits.maxBufferLength = p_device.maxBufferLength;
// FST: Maximum size of vertex descriptor layout stride.
limits.maxVertexDescriptorLayoutStride = std::numeric_limits<uint64_t>::max();
// Maximum number of viewports.
if ([p_device supportsFamily:MTLGPUFamilyApple5]) {
limits.maxViewports = 16;
} else {
limits.maxViewports = 1;
}
limits.maxPerStageBufferCount = 31;
limits.maxPerStageSamplerCount = 16;
if ([p_device supportsFamily:MTLGPUFamilyApple6]) {
limits.maxPerStageTextureCount = 128;
} else if ([p_device supportsFamily:MTLGPUFamilyApple4]) {
limits.maxPerStageTextureCount = 96;
} else {
limits.maxPerStageTextureCount = 31;
}
limits.maxVertexInputAttributes = 31;
limits.maxVertexInputBindings = 31;
limits.maxVertexInputBindingStride = (2 * KIBI);
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
limits.minUniformBufferOffsetAlignment = 64;
#endif
#if TARGET_OS_OSX
// This is Apple Silicon specific.
limits.minUniformBufferOffsetAlignment = 16;
#endif
limits.maxDrawIndexedIndexValue = std::numeric_limits<uint32_t>::max() - 1;
if (@available(macOS 14.0, iOS 17.0, tvOS 17.0, *)) {
limits.temporalScalerInputContentMinScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMinScaleForDevice:p_device];
limits.temporalScalerInputContentMaxScale = (double)[MTLFXTemporalScalerDescriptor supportedInputContentMaxScaleForDevice:p_device];
} else {
// Defaults taken from macOS 14+
limits.temporalScalerInputContentMinScale = 1.0;
limits.temporalScalerInputContentMaxScale = 3.0;
}
}
MetalDeviceProperties::MetalDeviceProperties(id<MTLDevice> p_device) {
init_features(p_device);
init_limits(p_device);
}
MetalDeviceProperties::~MetalDeviceProperties() {
}
SampleCount MetalDeviceProperties::find_nearest_supported_sample_count(RenderingDevice::TextureSamples p_samples) const {
SampleCount supported = features.supportedSampleCounts;
if (supported & sample_count[p_samples]) {
return sample_count[p_samples];
}
SampleCount requested_sample_count = sample_count[p_samples];
// Find the nearest supported sample count.
while (requested_sample_count > SampleCount1) {
if (supported & requested_sample_count) {
return requested_sample_count;
}
requested_sample_count = (SampleCount)(requested_sample_count >> 1);
}
return SampleCount1;
}
// region static members
const SampleCount MetalDeviceProperties::sample_count[RenderingDevice::TextureSamples::TEXTURE_SAMPLES_MAX] = {
SampleCount1,
SampleCount2,
SampleCount4,
SampleCount8,
SampleCount16,
SampleCount32,
SampleCount64,
};
// endregion

Some files were not shown because too many files have changed in this diff Show more