feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
43
engine/drivers/SCsub
Normal file
43
engine/drivers/SCsub
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.drivers_sources = []
|
||||
|
||||
# OS drivers
|
||||
SConscript("unix/SCsub")
|
||||
SConscript("windows/SCsub")
|
||||
|
||||
# Sounds drivers
|
||||
SConscript("alsa/SCsub")
|
||||
SConscript("coreaudio/SCsub")
|
||||
SConscript("pulseaudio/SCsub")
|
||||
if env["platform"] == "windows":
|
||||
SConscript("wasapi/SCsub")
|
||||
if not env.msvc:
|
||||
SConscript("backtrace/SCsub")
|
||||
if env["xaudio2"]:
|
||||
SConscript("xaudio2/SCsub")
|
||||
|
||||
# Midi drivers
|
||||
SConscript("alsamidi/SCsub")
|
||||
SConscript("coremidi/SCsub")
|
||||
SConscript("winmidi/SCsub")
|
||||
|
||||
# Graphics drivers
|
||||
if env["vulkan"]:
|
||||
SConscript("vulkan/SCsub")
|
||||
if env["d3d12"]:
|
||||
SConscript("d3d12/SCsub")
|
||||
if env["opengl3"]:
|
||||
SConscript("gl_context/SCsub")
|
||||
SConscript("gles3/SCsub")
|
||||
SConscript("egl/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])
|
||||
9
engine/drivers/alsa/SCsub
Normal file
9
engine/drivers/alsa/SCsub
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "alsa" in env and env["alsa"]:
|
||||
if env["use_sowrap"]:
|
||||
env.add_source_files(env.drivers_sources, "asound-so_wrap.c")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
14004
engine/drivers/alsa/asound-so_wrap.c
Normal file
14004
engine/drivers/alsa/asound-so_wrap.c
Normal file
File diff suppressed because it is too large
Load diff
5102
engine/drivers/alsa/asound-so_wrap.h
Normal file
5102
engine/drivers/alsa/asound-so_wrap.h
Normal file
File diff suppressed because it is too large
Load diff
349
engine/drivers/alsa/audio_driver_alsa.cpp
Normal file
349
engine/drivers/alsa/audio_driver_alsa.cpp
Normal 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(";");
|
||||
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
|
||||
99
engine/drivers/alsa/audio_driver_alsa.h
Normal file
99
engine/drivers/alsa/audio_driver_alsa.h
Normal 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
|
||||
6
engine/drivers/alsamidi/SCsub
Normal file
6
engine/drivers/alsamidi/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
148
engine/drivers/alsamidi/midi_driver_alsamidi.cpp
Normal file
148
engine/drivers/alsamidi/midi_driver_alsamidi.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/string/print_string.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
|
||||
84
engine/drivers/alsamidi/midi_driver_alsamidi.h
Normal file
84
engine/drivers/alsamidi/midi_driver_alsamidi.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/**************************************************************************/
|
||||
/* 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
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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
|
||||
44
engine/drivers/backtrace/SCsub
Normal file
44
engine/drivers/backtrace/SCsub
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
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)
|
||||
6
engine/drivers/coreaudio/SCsub
Normal file
6
engine/drivers/coreaudio/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
678
engine/drivers/coreaudio/audio_driver_coreaudio.cpp
Normal file
678
engine/drivers/coreaudio/audio_driver_coreaudio.cpp
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_coreaudio.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_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;
|
||||
}
|
||||
#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 = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mix_rate = _get_configured_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);
|
||||
input_buf.resize(buffer_size);
|
||||
|
||||
print_verbose("CoreAudio: detected " + itos(channels) + " channels");
|
||||
print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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 = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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 deviceId;
|
||||
size = sizeof(AudioDeviceID);
|
||||
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
|
||||
|
||||
result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, 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;
|
||||
}
|
||||
|
||||
mix_rate = _get_configured_mix_rate();
|
||||
|
||||
memset(&strdesc, 0, sizeof(strdesc));
|
||||
strdesc.mFormatID = kAudioFormatLinearPCM;
|
||||
strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
strdesc.mChannelsPerFrame = capture_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(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc));
|
||||
ERR_FAIL_COND_V(result != noErr, FAILED);
|
||||
|
||||
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);
|
||||
|
||||
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 = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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(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 = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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 deviceId;
|
||||
bool found = false;
|
||||
if (output_device != "Default") {
|
||||
AudioObjectPropertyAddress prop;
|
||||
|
||||
prop.mSelector = kAudioHardwarePropertyDevices;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMaster;
|
||||
|
||||
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) {
|
||||
deviceId = 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, kAudioObjectPropertyElementMaster };
|
||||
|
||||
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId);
|
||||
ERR_FAIL_COND(result != noErr);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
|
||||
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
|
||||
125
engine/drivers/coreaudio/audio_driver_coreaudio.h
Normal file
125
engine/drivers/coreaudio/audio_driver_coreaudio.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/**************************************************************************/
|
||||
/* 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>
|
||||
#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;
|
||||
unsigned int channels = 2;
|
||||
unsigned int capture_channels = 2;
|
||||
unsigned int 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 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
|
||||
6
engine/drivers/coremidi/SCsub
Normal file
6
engine/drivers/coremidi/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
# Driver source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
133
engine/drivers/coremidi/midi_driver_coremidi.cpp
Normal file
133
engine/drivers/coremidi/midi_driver_coremidi.cpp
Normal 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
|
||||
71
engine/drivers/coremidi/midi_driver_coremidi.h
Normal file
71
engine/drivers/coremidi/midi_driver_coremidi.h
Normal 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
|
||||
161
engine/drivers/d3d12/SCsub
Normal file
161
engine/drivers/d3d12/SCsub
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
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 += [
|
||||
("__REQUIRED_RPCNDR_H_VERSION__", 475),
|
||||
"HAVE_STRUCT_TIMESPEC",
|
||||
]
|
||||
|
||||
# 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)
|
||||
60
engine/drivers/d3d12/d3d12_godot_nir_bridge.h
Normal file
60
engine/drivers/d3d12/d3d12_godot_nir_bridge.h
Normal 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
|
||||
64
engine/drivers/d3d12/d3d12ma.cpp
Normal file
64
engine/drivers/d3d12/d3d12ma.cpp
Normal 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 4324 4505)
|
||||
#endif
|
||||
|
||||
#include "thirdparty/d3d12ma/D3D12MemAlloc.cpp"
|
||||
209
engine/drivers/d3d12/dxil_hash.cpp
Normal file
209
engine/drivers/d3d12/dxil_hash.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/**************************************************************************/
|
||||
/* dxil_hash.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
// Based on the patched public domain implementation released by Microsoft here:
|
||||
// https://github.com/microsoft/hlsl-specs/blob/main/proposals/infra/INF-0004-validator-hashing.md
|
||||
|
||||
#include "dxil_hash.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
static const BYTE padding[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static void FF(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += ((b & c) | (~b & d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void GG(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += ((b & d) | (c & ~d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void HH(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += (b ^ c ^ d) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
static void II(UINT &a, UINT b, UINT c, UINT d, UINT x, UINT8 s, UINT ac) {
|
||||
a += (c ^ (b | ~d)) + x + ac;
|
||||
a = ((a << s) | (a >> (32 - s))) + b;
|
||||
}
|
||||
|
||||
void compute_dxil_hash(const BYTE *pData, UINT byteCount, BYTE *pOutHash) {
|
||||
UINT leftOver = byteCount & 0x3f;
|
||||
UINT padAmount;
|
||||
bool bTwoRowsPadding = false;
|
||||
if (leftOver < 56) {
|
||||
padAmount = 56 - leftOver;
|
||||
} else {
|
||||
padAmount = 120 - leftOver;
|
||||
bTwoRowsPadding = true;
|
||||
}
|
||||
UINT padAmountPlusSize = padAmount + 8;
|
||||
UINT state[4] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
|
||||
UINT N = (byteCount + padAmountPlusSize) >> 6;
|
||||
UINT offset = 0;
|
||||
UINT NextEndState = bTwoRowsPadding ? N - 2 : N - 1;
|
||||
const BYTE *pCurrData = pData;
|
||||
for (UINT i = 0; i < N; i++, offset += 64, pCurrData += 64) {
|
||||
UINT x[16];
|
||||
const UINT *pX;
|
||||
if (i == NextEndState) {
|
||||
if (!bTwoRowsPadding && i == N - 1) {
|
||||
UINT remainder = byteCount - offset;
|
||||
x[0] = byteCount << 3;
|
||||
memcpy((BYTE *)x + 4, pCurrData, remainder);
|
||||
memcpy((BYTE *)x + 4 + remainder, padding, padAmount);
|
||||
x[15] = 1 | (byteCount << 1);
|
||||
} else if (bTwoRowsPadding) {
|
||||
if (i == N - 2) {
|
||||
UINT remainder = byteCount - offset;
|
||||
memcpy(x, pCurrData, remainder);
|
||||
memcpy((BYTE *)x + remainder, padding, padAmount - 56);
|
||||
NextEndState = N - 1;
|
||||
} else if (i == N - 1) {
|
||||
x[0] = byteCount << 3;
|
||||
memcpy((BYTE *)x + 4, padding + padAmount - 56, 56);
|
||||
x[15] = 1 | (byteCount << 1);
|
||||
}
|
||||
}
|
||||
pX = x;
|
||||
} else {
|
||||
pX = (const UINT *)pCurrData;
|
||||
}
|
||||
|
||||
UINT a = state[0];
|
||||
UINT b = state[1];
|
||||
UINT c = state[2];
|
||||
UINT d = state[3];
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, pX[0], S11, 0xd76aa478); /* 1 */
|
||||
FF(d, a, b, c, pX[1], S12, 0xe8c7b756); /* 2 */
|
||||
FF(c, d, a, b, pX[2], S13, 0x242070db); /* 3 */
|
||||
FF(b, c, d, a, pX[3], S14, 0xc1bdceee); /* 4 */
|
||||
FF(a, b, c, d, pX[4], S11, 0xf57c0faf); /* 5 */
|
||||
FF(d, a, b, c, pX[5], S12, 0x4787c62a); /* 6 */
|
||||
FF(c, d, a, b, pX[6], S13, 0xa8304613); /* 7 */
|
||||
FF(b, c, d, a, pX[7], S14, 0xfd469501); /* 8 */
|
||||
FF(a, b, c, d, pX[8], S11, 0x698098d8); /* 9 */
|
||||
FF(d, a, b, c, pX[9], S12, 0x8b44f7af); /* 10 */
|
||||
FF(c, d, a, b, pX[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF(b, c, d, a, pX[11], S14, 0x895cd7be); /* 12 */
|
||||
FF(a, b, c, d, pX[12], S11, 0x6b901122); /* 13 */
|
||||
FF(d, a, b, c, pX[13], S12, 0xfd987193); /* 14 */
|
||||
FF(c, d, a, b, pX[14], S13, 0xa679438e); /* 15 */
|
||||
FF(b, c, d, a, pX[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, pX[1], S21, 0xf61e2562); /* 17 */
|
||||
GG(d, a, b, c, pX[6], S22, 0xc040b340); /* 18 */
|
||||
GG(c, d, a, b, pX[11], S23, 0x265e5a51); /* 19 */
|
||||
GG(b, c, d, a, pX[0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG(a, b, c, d, pX[5], S21, 0xd62f105d); /* 21 */
|
||||
GG(d, a, b, c, pX[10], S22, 0x2441453); /* 22 */
|
||||
GG(c, d, a, b, pX[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG(b, c, d, a, pX[4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG(a, b, c, d, pX[9], S21, 0x21e1cde6); /* 25 */
|
||||
GG(d, a, b, c, pX[14], S22, 0xc33707d6); /* 26 */
|
||||
GG(c, d, a, b, pX[3], S23, 0xf4d50d87); /* 27 */
|
||||
GG(b, c, d, a, pX[8], S24, 0x455a14ed); /* 28 */
|
||||
GG(a, b, c, d, pX[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG(d, a, b, c, pX[2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG(c, d, a, b, pX[7], S23, 0x676f02d9); /* 31 */
|
||||
GG(b, c, d, a, pX[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, pX[5], S31, 0xfffa3942); /* 33 */
|
||||
HH(d, a, b, c, pX[8], S32, 0x8771f681); /* 34 */
|
||||
HH(c, d, a, b, pX[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH(b, c, d, a, pX[14], S34, 0xfde5380c); /* 36 */
|
||||
HH(a, b, c, d, pX[1], S31, 0xa4beea44); /* 37 */
|
||||
HH(d, a, b, c, pX[4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH(c, d, a, b, pX[7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH(b, c, d, a, pX[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH(a, b, c, d, pX[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH(d, a, b, c, pX[0], S32, 0xeaa127fa); /* 42 */
|
||||
HH(c, d, a, b, pX[3], S33, 0xd4ef3085); /* 43 */
|
||||
HH(b, c, d, a, pX[6], S34, 0x4881d05); /* 44 */
|
||||
HH(a, b, c, d, pX[9], S31, 0xd9d4d039); /* 45 */
|
||||
HH(d, a, b, c, pX[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH(c, d, a, b, pX[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH(b, c, d, a, pX[2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II(a, b, c, d, pX[0], S41, 0xf4292244); /* 49 */
|
||||
II(d, a, b, c, pX[7], S42, 0x432aff97); /* 50 */
|
||||
II(c, d, a, b, pX[14], S43, 0xab9423a7); /* 51 */
|
||||
II(b, c, d, a, pX[5], S44, 0xfc93a039); /* 52 */
|
||||
II(a, b, c, d, pX[12], S41, 0x655b59c3); /* 53 */
|
||||
II(d, a, b, c, pX[3], S42, 0x8f0ccc92); /* 54 */
|
||||
II(c, d, a, b, pX[10], S43, 0xffeff47d); /* 55 */
|
||||
II(b, c, d, a, pX[1], S44, 0x85845dd1); /* 56 */
|
||||
II(a, b, c, d, pX[8], S41, 0x6fa87e4f); /* 57 */
|
||||
II(d, a, b, c, pX[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II(c, d, a, b, pX[6], S43, 0xa3014314); /* 59 */
|
||||
II(b, c, d, a, pX[13], S44, 0x4e0811a1); /* 60 */
|
||||
II(a, b, c, d, pX[4], S41, 0xf7537e82); /* 61 */
|
||||
II(d, a, b, c, pX[11], S42, 0xbd3af235); /* 62 */
|
||||
II(c, d, a, b, pX[2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II(b, c, d, a, pX[9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
}
|
||||
|
||||
memcpy(pOutHash, state, 16);
|
||||
}
|
||||
39
engine/drivers/d3d12/dxil_hash.h
Normal file
39
engine/drivers/d3d12/dxil_hash.h
Normal 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
|
||||
341
engine/drivers/d3d12/rendering_context_driver_d3d12.cpp
Normal file
341
engine/drivers/d3d12/rendering_context_driver_d3d12.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/**************************************************************************/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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 = 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;
|
||||
}
|
||||
132
engine/drivers/d3d12/rendering_context_driver_d3d12.h
Normal file
132
engine/drivers/d3d12/rendering_context_driver_d3d12.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**************************************************************************/
|
||||
/* 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
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
HMODULE lib_d3d12 = nullptr;
|
||||
HMODULE lib_dxgi = 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
|
||||
6690
engine/drivers/d3d12/rendering_device_driver_d3d12.cpp
Normal file
6690
engine/drivers/d3d12/rendering_device_driver_d3d12.cpp
Normal file
File diff suppressed because it is too large
Load diff
1035
engine/drivers/d3d12/rendering_device_driver_d3d12.h
Normal file
1035
engine/drivers/d3d12/rendering_device_driver_d3d12.h
Normal file
File diff suppressed because it is too large
Load diff
6
engine/drivers/egl/SCsub
Normal file
6
engine/drivers/egl/SCsub
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
# Godot source files
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
449
engine/drivers/egl/egl_manager.cpp
Normal file
449
engine/drivers/egl/egl_manager.cpp
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
#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
|
||||
|
||||
// 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
|
||||
|
||||
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;
|
||||
|
||||
if (GLAD_EGL_VERSION_1_5) {
|
||||
glwindow.egl_surface = eglCreatePlatformWindowSurface(gldisplay.egl_display, gldisplay.egl_config, p_native_window, nullptr);
|
||||
} 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;
|
||||
|
||||
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 ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
void EGLManager::swap_buffers() {
|
||||
if (!current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!current_window->initialized) {
|
||||
WARN_PRINT("Current OpenGL window is uninitialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
GLDisplay ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglSwapBuffers(current_display.egl_display, current_window->egl_surface);
|
||||
}
|
||||
|
||||
void EGLManager::window_make_current(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 ¤t_display = displays[current_window->gldisplay_id];
|
||||
|
||||
eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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
|
||||
118
engine/drivers/egl/egl_manager.h
Normal file
118
engine/drivers/egl/egl_manager.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**************************************************************************/
|
||||
/* 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/config/project_settings.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.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;
|
||||
};
|
||||
|
||||
// EGL specific window data.
|
||||
struct GLWindow {
|
||||
bool initialized = false;
|
||||
|
||||
// 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);
|
||||
|
||||
Error initialize(void *p_native_display = nullptr);
|
||||
|
||||
EGLManager();
|
||||
virtual ~EGLManager();
|
||||
};
|
||||
|
||||
#endif // EGL_ENABLED
|
||||
|
||||
#endif // EGL_MANAGER_H
|
||||
29
engine/drivers/gl_context/SCsub
Normal file
29
engine/drivers/gl_context/SCsub
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
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")
|
||||
10
engine/drivers/gles3/SCsub
Normal file
10
engine/drivers/gles3/SCsub
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
|
||||
SConscript("shaders/SCsub")
|
||||
SConscript("storage/SCsub")
|
||||
SConscript("effects/SCsub")
|
||||
SConscript("environment/SCsub")
|
||||
5
engine/drivers/gles3/effects/SCsub
Normal file
5
engine/drivers/gles3/effects/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
310
engine/drivers/gles3/effects/copy_effects.cpp
Normal file
310
engine/drivers/gles3/effects/copy_effects.cpp
Normal 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
|
||||
81
engine/drivers/gles3/effects/copy_effects.h
Normal file
81
engine/drivers/gles3/effects/copy_effects.h
Normal 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
|
||||
209
engine/drivers/gles3/effects/cubemap_filter.cpp
Normal file
209
engine/drivers/gles3/effects/cubemap_filter.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
ggx_samples = 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,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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[4] = { 1, ggx_samples / 4, ggx_samples / 2, ggx_samples };
|
||||
uint32_t sample_count = sample_counts[MIN(3, p_layer)];
|
||||
|
||||
float roughness = float(p_layer) / (p_mipmap_count);
|
||||
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
|
||||
70
engine/drivers/gles3/effects/cubemap_filter.h
Normal file
70
engine/drivers/gles3/effects/cubemap_filter.h
Normal 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
|
||||
173
engine/drivers/gles3/effects/glow.cpp
Normal file
173
engine/drivers/gles3/effects/glow.cpp
Normal 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
|
||||
89
engine/drivers/gles3/effects/glow.h
Normal file
89
engine/drivers/gles3/effects/glow.h
Normal 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
|
||||
153
engine/drivers/gles3/effects/post_effects.cpp
Normal file
153
engine/drivers/gles3/effects/post_effects.cpp
Normal 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
|
||||
69
engine/drivers/gles3/effects/post_effects.h
Normal file
69
engine/drivers/gles3/effects/post_effects.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/**************************************************************************/
|
||||
/* post_effects.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#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
|
||||
5
engine/drivers/gles3/environment/SCsub
Normal file
5
engine/drivers/gles3/environment/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
66
engine/drivers/gles3/environment/fog.cpp
Normal file
66
engine/drivers/gles3/environment/fog.cpp
Normal 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
|
||||
62
engine/drivers/gles3/environment/fog.h
Normal file
62
engine/drivers/gles3/environment/fog.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "servers/rendering/environment/renderer_fog.h"
|
||||
|
||||
namespace GLES3 {
|
||||
|
||||
class Fog : public RendererFog {
|
||||
public:
|
||||
/* FOG VOLUMES */
|
||||
|
||||
virtual RID fog_volume_allocate() override;
|
||||
virtual void fog_volume_initialize(RID p_rid) override;
|
||||
virtual void fog_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
|
||||
143
engine/drivers/gles3/environment/gi.cpp
Normal file
143
engine/drivers/gles3/environment/gi.cpp
Normal 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
|
||||
96
engine/drivers/gles3/environment/gi.h
Normal file
96
engine/drivers/gles3/environment/gi.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/templates/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "servers/rendering/environment/renderer_gi.h"
|
||||
|
||||
#include "platform_gl.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
|
||||
2908
engine/drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
2908
engine/drivers/gles3/rasterizer_canvas_gles3.cpp
Normal file
File diff suppressed because it is too large
Load diff
389
engine/drivers/gles3/rasterizer_canvas_gles3.h
Normal file
389
engine/drivers/gles3/rasterizer_canvas_gles3.h
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/**************************************************************************/
|
||||
/* 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 {
|
||||
|
||||
FLAGS_INSTANCING_MASK = 0x7F,
|
||||
FLAGS_INSTANCING_HAS_COLORS = (1 << 7),
|
||||
FLAGS_INSTANCING_HAS_CUSTOM_DATA = (1 << 8),
|
||||
|
||||
FLAGS_CLIP_RECT_UV = (1 << 9),
|
||||
FLAGS_TRANSPOSE_RECT = (1 << 10),
|
||||
|
||||
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
|
||||
|
||||
FLAGS_USE_SKELETON = (1 << 15),
|
||||
FLAGS_NINEPATCH_H_MODE_SHIFT = 16,
|
||||
FLAGS_NINEPATCH_V_MODE_SHIFT = 18,
|
||||
FLAGS_LIGHT_COUNT_SHIFT = 20,
|
||||
|
||||
FLAGS_DEFAULT_NORMAL_MAP_USED = (1 << 26),
|
||||
FLAGS_DEFAULT_SPECULAR_MAP_USED = (1 << 27),
|
||||
|
||||
FLAGS_USE_MSDF = (1 << 28),
|
||||
FLAGS_USE_LCD = (1 << 29),
|
||||
|
||||
FLAGS_FLIP_H = (1 << 30),
|
||||
FLAGS_FLIP_V = (1 << 31),
|
||||
};
|
||||
|
||||
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 specular_shininess;
|
||||
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;
|
||||
CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
|
||||
uint64_t vertex_input_mask = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_COLOR | RS::ARRAY_FORMAT_TEX_UV;
|
||||
|
||||
const Item::Command *command = nullptr;
|
||||
Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
|
||||
uint32_t primitive_points = 0;
|
||||
|
||||
bool lights_disabled = false;
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
//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) 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);
|
||||
void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_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 GL Compatibility backend.");
|
||||
}
|
||||
}
|
||||
|
||||
static RasterizerCanvasGLES3 *get_singleton();
|
||||
RasterizerCanvasGLES3();
|
||||
~RasterizerCanvasGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#endif // RASTERIZER_CANVAS_GLES3_H
|
||||
499
engine/drivers/gles3/rasterizer_gles3.cpp
Normal file
499
engine/drivers/gles3/rasterizer_gles3.cpp
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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 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
|
||||
|
||||
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(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);
|
||||
gi = memnew(GLES3::GI);
|
||||
fog = memnew(GLES3::Fog);
|
||||
canvas = memnew(RasterizerCanvasGLES3());
|
||||
scene = memnew(RasterizerSceneGLES3());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_ZERO, GL_ONE);
|
||||
glDepthMask(GL_FALSE);
|
||||
glClearColor(p_color.r, p_color.g, p_color.b, 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();
|
||||
}
|
||||
|
||||
// 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
|
||||
131
engine/drivers/gles3/rasterizer_gles3.h
Normal file
131
engine/drivers/gles3/rasterizer_gles3.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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;
|
||||
|
||||
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;
|
||||
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;
|
||||
_create_func = _create_current;
|
||||
low_end = true;
|
||||
}
|
||||
|
||||
_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; }
|
||||
|
||||
static RasterizerGLES3 *get_singleton() { return singleton; }
|
||||
RasterizerGLES3();
|
||||
~RasterizerGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#endif // RASTERIZER_GLES3_H
|
||||
4303
engine/drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
4303
engine/drivers/gles3/rasterizer_scene_gles3.cpp
Normal file
File diff suppressed because it is too large
Load diff
873
engine/drivers/gles3/rasterizer_scene_gles3.h
Normal file
873
engine/drivers/gles3/rasterizer_scene_gles3.h
Normal file
|
|
@ -0,0 +1,873 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
|
||||
Transform3D cam_transform;
|
||||
Transform3D inv_cam_transform;
|
||||
Projection cam_projection;
|
||||
bool cam_orthogonal = false;
|
||||
uint32_t camera_visible_layers = 0xFFFFFFFF;
|
||||
|
||||
// For billboards to cast correct shadows.
|
||||
Transform3D main_cam_transform;
|
||||
|
||||
// For stereo rendering
|
||||
uint32_t view_count = 1;
|
||||
Vector3 view_eye_offset[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
Projection view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
|
||||
|
||||
float z_near = 0.0;
|
||||
float z_far = 0.0;
|
||||
|
||||
const PagedArray<RenderGeometryInstance *> *instances = nullptr;
|
||||
const PagedArray<RID> *lights = nullptr;
|
||||
const PagedArray<RID> *reflection_probes = nullptr;
|
||||
RID environment;
|
||||
RID camera_attributes;
|
||||
RID shadow_atlas;
|
||||
RID reflection_probe;
|
||||
int reflection_probe_pass = 0;
|
||||
|
||||
float lod_distance_multiplier = 0.0;
|
||||
float screen_mesh_lod_threshold = 0.0;
|
||||
|
||||
uint32_t directional_light_count = 0;
|
||||
uint32_t directional_shadow_count = 0;
|
||||
|
||||
uint32_t spot_light_count = 0;
|
||||
uint32_t omni_light_count = 0;
|
||||
|
||||
float luminance_multiplier = 1.0;
|
||||
|
||||
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 lod_index : 8;
|
||||
uint64_t surface_index : 8;
|
||||
uint64_t geometry_id : 32;
|
||||
uint64_t material_id_low : 16;
|
||||
|
||||
uint64_t material_id_hi : 16;
|
||||
uint64_t shader_id : 32;
|
||||
uint64_t uses_softshadow : 1;
|
||||
uint64_t uses_projector : 1;
|
||||
uint64_t uses_forward_gi : 1;
|
||||
uint64_t uses_lightmap : 1;
|
||||
uint64_t depth_layer : 4;
|
||||
uint64_t priority : 8;
|
||||
};
|
||||
struct {
|
||||
uint64_t sort_key1;
|
||||
uint64_t sort_key2;
|
||||
};
|
||||
} sort;
|
||||
|
||||
RS::PrimitiveType primitive = RS::PRIMITIVE_MAX;
|
||||
uint32_t flags = 0;
|
||||
uint32_t surface_index = 0;
|
||||
uint32_t lod_index = 0;
|
||||
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;
|
||||
GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_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 = GLES3::SceneShaderData::CULL_BACK;
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
current_depth_draw_enabled = false;
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
current_depth_test_enabled = false;
|
||||
}
|
||||
|
||||
void set_gl_cull_mode(GLES3::SceneShaderData::Cull p_mode) {
|
||||
if (cull_mode != p_mode) {
|
||||
if (p_mode == GLES3::SceneShaderData::CULL_DISABLED) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) {
|
||||
// Last time was disabled, so enable and set proper face.
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
glCullFace(p_mode == GLES3::SceneShaderData::CULL_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;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
|
||||
RasterizerSceneGLES3();
|
||||
~RasterizerSceneGLES3();
|
||||
};
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#endif // RASTERIZER_SCENE_GLES3_H
|
||||
842
engine/drivers/gles3/shader_gles3.cpp
Normal file
842
engine/drivers/gles3/shader_gles3.cpp
Normal file
|
|
@ -0,0 +1,842 @@
|
|||
/**************************************************************************/
|
||||
/* 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/compression.h"
|
||||
#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");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// 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);
|
||||
if (shader_cache_dir_valid && _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 (shader_cache_dir_valid) {
|
||||
_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
|
||||
266
engine/drivers/gles3/shader_gles3.h
Normal file
266
engine/drivers/gles3/shader_gles3.h
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/**************************************************************************/
|
||||
/* 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/rb_map.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
#ifdef GLES3_ENABLED
|
||||
|
||||
#include "platform_gl.h"
|
||||
|
||||
#include <stdio.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
|
||||
32
engine/drivers/gles3/shaders/SCsub
Normal file
32
engine/drivers/gles3/shaders/SCsub
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
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("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")
|
||||
857
engine/drivers/gles3/shaders/canvas.glsl
Normal file
857
engine/drivers/gles3/shaders/canvas.glsl
Normal file
|
|
@ -0,0 +1,857 @@
|
|||
/* clang-format off */
|
||||
#[modes]
|
||||
|
||||
mode_quad =
|
||||
mode_ninepatch = #define USE_NINEPATCH
|
||||
mode_primitive = #define USE_PRIMITIVE
|
||||
mode_attributes = #define USE_ATTRIBUTES
|
||||
mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING
|
||||
|
||||
#[specializations]
|
||||
|
||||
DISABLE_LIGHTING = true
|
||||
USE_RGBA_SHADOWS = false
|
||||
SINGLE_INSTANCE = 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_specular_shininess 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
|
||||
/* 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_specular_shininess);
|
||||
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(read_draw_data_flags & FLAGS_INSTANCING_HAS_COLORS)) {
|
||||
vec4 instance_color;
|
||||
instance_color.xy = unpackHalf2x16(uint(instance_color_custom_data.x));
|
||||
instance_color.zw = unpackHalf2x16(uint(instance_color_custom_data.y));
|
||||
color *= instance_color;
|
||||
}
|
||||
if (bool(read_draw_data_flags & FLAGS_INSTANCING_HAS_CUSTOM_DATA)) {
|
||||
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 & 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;
|
||||
|
||||
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 = (canvas_transform * vec4(vertex, 0.0, 1.0)).xy;
|
||||
|
||||
vertex_interp = vertex;
|
||||
uv_interp = uv;
|
||||
|
||||
gl_Position = screen_transform * vec4(vertex, 0.0, 1.0);
|
||||
|
||||
#ifdef USE_POINT_SIZE
|
||||
gl_PointSize = point_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#include "canvas_uniforms_inc.glsl"
|
||||
#include "stdlib_inc.glsl"
|
||||
|
||||
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_specular_shininess 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
|
||||
|
||||
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 & FLAGS_NINEPACH_DRAW_CENTER)) {
|
||||
draw_center--;
|
||||
}
|
||||
|
||||
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
|
||||
if (np_repeat == 0) { // Stretch.
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
|
||||
} else if (np_repeat == 1) { // Tile.
|
||||
// Convert to offset.
|
||||
float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ofs) * tex_pixel_size;
|
||||
} else if (np_repeat == 2) { // Tile Fit.
|
||||
// Calculate scale.
|
||||
float src_area = draw_size - margin_begin - margin_end;
|
||||
float dst_area = tex_size - margin_begin - margin_end;
|
||||
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
|
||||
// Convert to ratio.
|
||||
float ratio = (pixel - margin_begin) / src_area;
|
||||
ratio = mod(ratio * scale, 1.0);
|
||||
// Scale to source texture.
|
||||
return (margin_begin + ratio * dst_area) * tex_pixel_size;
|
||||
} else { // Shouldn't happen, but silences compiler warning.
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
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 >> FLAGS_NINEPATCH_H_MODE_SHIFT) & 0x3, draw_center),
|
||||
map_ninepatch_axis(pixel_size_interp.y, abs(read_draw_data_dst_rect_w), read_draw_data_color_texture_pixel_size.y, read_draw_data_ninepatch_margins.y, read_draw_data_ninepatch_margins.w, int(read_draw_data_flags >> FLAGS_NINEPATCH_V_MODE_SHIFT) & 0x3, draw_center));
|
||||
|
||||
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 & FLAGS_CLIP_RECT_UV)) {
|
||||
uv = clamp(uv, read_draw_data_src_rect.xy, read_draw_data_src_rect.xy + abs(read_draw_data_src_rect.zw));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef USE_PRIMITIVE
|
||||
if (bool(read_draw_data_flags & 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 & 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(FLAGS_LIGHT_COUNT_SHIFT)) & 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(read_draw_data_flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
|
||||
normal.xy = texture(normal_texture, uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
|
||||
if (bool(read_draw_data_flags & FLAGS_TRANSPOSE_RECT)) {
|
||||
normal.xy = normal.yx;
|
||||
}
|
||||
if (bool(read_draw_data_flags & FLAGS_FLIP_H)) {
|
||||
normal.x = -normal.x;
|
||||
}
|
||||
if (bool(read_draw_data_flags & FLAGS_FLIP_V)) {
|
||||
normal.y = -normal.y;
|
||||
}
|
||||
normal.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(read_draw_data_flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
|
||||
specular_shininess = texture(specular_texture, uv);
|
||||
specular_shininess *= godot_unpackUnorm4x8(read_draw_data_specular_shininess);
|
||||
specular_shininess_used = true;
|
||||
} else {
|
||||
specular_shininess = vec4(1.0);
|
||||
}
|
||||
|
||||
#if defined(SCREEN_UV_USED)
|
||||
vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size;
|
||||
#else
|
||||
vec2 screen_uv = vec2(0.0);
|
||||
#endif
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
68
engine/drivers/gles3/shaders/canvas_occlusion.glsl
Normal file
68
engine/drivers/gles3/shaders/canvas_occlusion.glsl
Normal 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
|
||||
}
|
||||
205
engine/drivers/gles3/shaders/canvas_sdf.glsl
Normal file
205
engine/drivers/gles3/shaders/canvas_sdf.glsl
Normal 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
|
||||
}
|
||||
88
engine/drivers/gles3/shaders/canvas_uniforms_inc.glsl
Normal file
88
engine/drivers/gles3/shaders/canvas_uniforms_inc.glsl
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
#define MAX_LIGHTS_PER_ITEM uint(16)
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
#define SDF_MAX_LENGTH 16384.0
|
||||
|
||||
//1 means enabled, 2+ means trails in use
|
||||
#define FLAGS_INSTANCING_MASK uint(0x7F)
|
||||
#define FLAGS_INSTANCING_HAS_COLORS uint(1 << 7)
|
||||
#define FLAGS_INSTANCING_HAS_CUSTOM_DATA uint(1 << 8)
|
||||
|
||||
#define FLAGS_CLIP_RECT_UV uint(1 << 9)
|
||||
#define FLAGS_TRANSPOSE_RECT uint(1 << 10)
|
||||
// (1 << 11) is for FLAGS_CONVERT_ATTRIBUTES_TO_LINEAR in RD backends, unused here.
|
||||
#define FLAGS_NINEPACH_DRAW_CENTER uint(1 << 12)
|
||||
|
||||
#define FLAGS_NINEPATCH_H_MODE_SHIFT 16
|
||||
#define FLAGS_NINEPATCH_V_MODE_SHIFT 18
|
||||
|
||||
#define FLAGS_LIGHT_COUNT_SHIFT 20
|
||||
|
||||
#define FLAGS_DEFAULT_NORMAL_MAP_USED uint(1 << 26)
|
||||
#define FLAGS_DEFAULT_SPECULAR_MAP_USED uint(1 << 27)
|
||||
|
||||
#define FLAGS_USE_MSDF uint(1 << 28)
|
||||
#define FLAGS_USE_LCD uint(1 << 29)
|
||||
|
||||
#define FLAGS_FLIP_H uint(1 << 30)
|
||||
#define FLAGS_FLIP_V uint(1 << 31)
|
||||
|
||||
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
|
||||
100
engine/drivers/gles3/shaders/cube_to_dp.glsl
Normal file
100
engine/drivers/gles3/shaders/cube_to_dp.glsl
Normal 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;
|
||||
}
|
||||
291
engine/drivers/gles3/shaders/effect_blur.glsl
Normal file
291
engine/drivers/gles3/shaders/effect_blur.glsl
Normal 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
|
||||
}
|
||||
17
engine/drivers/gles3/shaders/effects/SCsub
Normal file
17
engine/drivers/gles3/shaders/effects/SCsub
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
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)
|
||||
176
engine/drivers/gles3/shaders/effects/copy.glsl
Normal file
176
engine/drivers/gles3/shaders/effects/copy.glsl
Normal 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
|
||||
}
|
||||
122
engine/drivers/gles3/shaders/effects/cubemap_filter.glsl
Normal file
122
engine/drivers/gles3/shaders/effects/cubemap_filter.glsl
Normal 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
|
||||
}
|
||||
113
engine/drivers/gles3/shaders/effects/glow.glsl
Normal file
113
engine/drivers/gles3/shaders/effects/glow.glsl
Normal 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
|
||||
}
|
||||
130
engine/drivers/gles3/shaders/effects/post.glsl
Normal file
130
engine/drivers/gles3/shaders/effects/post.glsl
Normal 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;
|
||||
}
|
||||
86
engine/drivers/gles3/shaders/lens_distorted.glsl
Normal file
86
engine/drivers/gles3/shaders/lens_distorted.glsl
Normal 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);
|
||||
}
|
||||
}
|
||||
512
engine/drivers/gles3/shaders/particles.glsl
Normal file
512
engine/drivers/gles3/shaders/particles.glsl
Normal 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 -= 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 = 1.0 - 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, 1.0 - 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, 1.0 - 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 */
|
||||
121
engine/drivers/gles3/shaders/particles_copy.glsl
Normal file
121
engine/drivers/gles3/shaders/particles_copy.glsl
Normal 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 */
|
||||
2152
engine/drivers/gles3/shaders/scene.glsl
Normal file
2152
engine/drivers/gles3/shaders/scene.glsl
Normal file
File diff suppressed because it is too large
Load diff
282
engine/drivers/gles3/shaders/skeleton.glsl
Normal file
282
engine/drivers/gles3/shaders/skeleton.glsl
Normal 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 mediump 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 */
|
||||
218
engine/drivers/gles3/shaders/sky.glsl
Normal file
218
engine/drivers/gles3/shaders/sky.glsl
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/* clang-format off */
|
||||
#[modes]
|
||||
|
||||
mode_background =
|
||||
mode_half_res = #define USE_HALF_RES_PASS
|
||||
mode_quarter_res = #define USE_QUARTER_RES_PASS
|
||||
mode_cubemap = #define USE_CUBEMAP_PASS
|
||||
mode_cubemap_half_res = #define USE_CUBEMAP_PASS \n#define USE_HALF_RES_PASS
|
||||
mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PASS
|
||||
|
||||
#[specializations]
|
||||
|
||||
USE_MULTIVIEW = false
|
||||
USE_INVERTED_Y = true
|
||||
APPLY_TONEMAPPING = true
|
||||
|
||||
#[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 vec3 fog_light_color;
|
||||
uniform float fog_sun_scatter;
|
||||
uniform bool fog_enabled;
|
||||
uniform float fog_density;
|
||||
uniform float z_far;
|
||||
uniform uint directional_light_count;
|
||||
|
||||
#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
|
||||
|
||||
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);
|
||||
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
|
||||
}
|
||||
83
engine/drivers/gles3/shaders/stdlib_inc.glsl
Normal file
83
engine/drivers/gles3/shaders/stdlib_inc.glsl
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
// Compatibility renames. These are exposed with the "godot_" prefix
|
||||
// to work around two distinct Adreno bugs:
|
||||
// 1. Some Adreno devices expose ES310 functions in ES300 shaders.
|
||||
// Internally, we must use the "godot_" prefix, but user shaders
|
||||
// will be mapped automatically.
|
||||
// 2. Adreno 3XX devices have poor implementations of the other packing
|
||||
// functions, so we just use our own everywhere to keep it simple.
|
||||
|
||||
// Floating point pack/unpack functions are part of the GLSL ES 300 specification used by web and mobile.
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
#define packHalf2x16 godot_packHalf2x16
|
||||
#define unpackHalf2x16 godot_unpackHalf2x16
|
||||
#define packUnorm2x16 godot_packUnorm2x16
|
||||
#define unpackUnorm2x16 godot_unpackUnorm2x16
|
||||
#define packSnorm2x16 godot_packSnorm2x16
|
||||
#define unpackSnorm2x16 godot_unpackSnorm2x16
|
||||
102
engine/drivers/gles3/shaders/tonemap_inc.glsl
Normal file
102
engine/drivers/gles3/shaders/tonemap_inc.glsl
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
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
|
||||
|
||||
vec3 tonemap_filmic(vec3 color, float p_white) {
|
||||
// exposure bias: input scale (color *= bias, white *= bias) to make the brightness consistent with other tonemappers
|
||||
// also useful to scale the input to the range that the tonemapper is designed for (some require very high input values)
|
||||
// has no effect on the curve's general shape or visual properties
|
||||
const float exposure_bias = 2.0f;
|
||||
const float A = 0.22f * exposure_bias * exposure_bias; // bias baked into constants for performance
|
||||
const float B = 0.30f * exposure_bias;
|
||||
const float C = 0.10f;
|
||||
const float D = 0.20f;
|
||||
const float E = 0.01f;
|
||||
const float F = 0.30f;
|
||||
|
||||
vec3 color_tonemapped = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
|
||||
float p_white_tonemapped = ((p_white * (A * p_white + C * B) + D * E) / (p_white * (A * p_white + B) + D * F)) - E / F;
|
||||
|
||||
return color_tonemapped / p_white_tonemapped;
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
|
||||
// (MIT License).
|
||||
vec3 tonemap_aces(vec3 color, float p_white) {
|
||||
const float exposure_bias = 1.8f;
|
||||
const float A = 0.0245786f;
|
||||
const float B = 0.000090537f;
|
||||
const float C = 0.983729f;
|
||||
const float D = 0.432951f;
|
||||
const float E = 0.238081f;
|
||||
|
||||
// Exposure bias baked into transform to save shader instructions. Equivalent to `color *= exposure_bias`
|
||||
const mat3 rgb_to_rrt = mat3(
|
||||
vec3(0.59719f * exposure_bias, 0.35458f * exposure_bias, 0.04823f * exposure_bias),
|
||||
vec3(0.07600f * exposure_bias, 0.90834f * exposure_bias, 0.01566f * exposure_bias),
|
||||
vec3(0.02840f * exposure_bias, 0.13383f * exposure_bias, 0.83777f * exposure_bias));
|
||||
|
||||
const mat3 odt_to_rgb = mat3(
|
||||
vec3(1.60475f, -0.53108f, -0.07367f),
|
||||
vec3(-0.10208f, 1.10813f, -0.00605f),
|
||||
vec3(-0.00327f, -0.07276f, 1.07602f));
|
||||
|
||||
color *= rgb_to_rrt;
|
||||
vec3 color_tonemapped = (color * (color + A) - B) / (color * (C * color + D) + E);
|
||||
color_tonemapped *= odt_to_rgb;
|
||||
|
||||
p_white *= exposure_bias;
|
||||
float p_white_tonemapped = (p_white * (p_white + A) - B) / (p_white * (C * p_white + D) + E);
|
||||
|
||||
return color_tonemapped / p_white_tonemapped;
|
||||
}
|
||||
|
||||
vec3 tonemap_reinhard(vec3 color, float p_white) {
|
||||
return (p_white * color + color) / (color * p_white + p_white);
|
||||
}
|
||||
|
||||
#define TONEMAPPER_LINEAR 0
|
||||
#define TONEMAPPER_REINHARD 1
|
||||
#define TONEMAPPER_FILMIC 2
|
||||
#define TONEMAPPER_ACES 3
|
||||
|
||||
vec3 apply_tonemapping(vec3 color, float p_white) { // inputs are LINEAR, always outputs clamped [0;1] color
|
||||
// Ensure color values passed to tonemappers are positive.
|
||||
// They can be negative in the case of negative lights, which leads to undesired behavior.
|
||||
if (tonemapper == TONEMAPPER_LINEAR) {
|
||||
return color;
|
||||
} else if (tonemapper == TONEMAPPER_REINHARD) {
|
||||
return tonemap_reinhard(max(vec3(0.0f), color), p_white);
|
||||
} else if (tonemapper == TONEMAPPER_FILMIC) {
|
||||
return tonemap_filmic(max(vec3(0.0f), color), p_white);
|
||||
} else { // TONEMAPPER_ACES
|
||||
return tonemap_aces(max(vec3(0.0f), color), p_white);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // APPLY_TONEMAPPING
|
||||
5
engine/drivers/gles3/storage/SCsub
Normal file
5
engine/drivers/gles3/storage/SCsub
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
228
engine/drivers/gles3/storage/config.cpp
Normal file
228
engine/drivers/gles3/storage/config.cpp
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
#include "texture_storage.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_ldr");
|
||||
astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d");
|
||||
|
||||
if (RasterizerGLES3::is_gles_over_gl()) {
|
||||
float_texture_supported = true;
|
||||
etc2_supported = false;
|
||||
s3tc_supported = true;
|
||||
rgtc_supported = true; //RGTC - core since OpenGL version 3.0
|
||||
} else {
|
||||
float_texture_supported = extensions.has("GL_EXT_color_buffer_float");
|
||||
etc2_supported = true;
|
||||
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
|
||||
// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
|
||||
// This could be fixed but so few devices support it that it doesn't seem useful (and makes bigger APKs).
|
||||
// For good measure we do the same hack for iOS, just in case.
|
||||
s3tc_supported = false;
|
||||
#else
|
||||
s3tc_supported = extensions.has("GL_EXT_texture_compression_dxt1") || extensions.has("GL_EXT_texture_compression_s3tc") || extensions.has("WEBGL_compressed_texture_s3tc");
|
||||
#endif
|
||||
rgtc_supported = extensions.has("GL_EXT_texture_compression_rgtc") || extensions.has("GL_ARB_texture_compression_rgtc") || extensions.has("EXT_texture_compression_rgtc");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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 = extensions.has("GL_EXT_framebuffer_multisample");
|
||||
#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");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
|
||||
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
|
||||
|
||||
use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
|
||||
if (use_depth_prepass) {
|
||||
String vendors = GLOBAL_GET("rendering/driver/depth_prepass/disable_for_vendors");
|
||||
Vector<String> vendor_match = vendors.split(",");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
116
engine/drivers/gles3/storage/config.h
Normal file
116
engine/drivers/gles3/storage/config.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/templates/vector.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);
|
||||
#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;
|
||||
|
||||
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 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 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;
|
||||
|
||||
// Adreno 3XX compatibility
|
||||
bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
|
||||
bool flip_xy_workaround = false;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr;
|
||||
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
|
||||
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
|
||||
#endif
|
||||
|
||||
static Config *get_singleton() { return singleton; };
|
||||
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
|
||||
} // namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#endif // CONFIG_GLES3_H
|
||||
1675
engine/drivers/gles3/storage/light_storage.cpp
Normal file
1675
engine/drivers/gles3/storage/light_storage.cpp
Normal file
File diff suppressed because it is too large
Load diff
905
engine/drivers/gles3/storage/light_storage.h
Normal file
905
engine/drivers/gles3/storage/light_storage.h
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
/**************************************************************************/
|
||||
/* 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 "core/templates/self_list.h"
|
||||
#include "drivers/gles3/storage/texture_storage.h"
|
||||
#include "servers/rendering/renderer_compositor.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;
|
||||
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;
|
||||
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;
|
||||
bool uses_spherical_harmonics = false;
|
||||
bool interior = false;
|
||||
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
|
||||
float baked_exposure = 1.0;
|
||||
int32_t array_index = -1; //unassigned
|
||||
PackedVector3Array points;
|
||||
PackedColorArray point_sh;
|
||||
PackedInt32Array tetrahedra;
|
||||
PackedInt32Array bsp_tree;
|
||||
|
||||
struct BSP {
|
||||
static const int32_t EMPTY_LEAF = INT32_MIN;
|
||||
float plane[4];
|
||||
int32_t over = EMPTY_LEAF, under = EMPTY_LEAF;
|
||||
};
|
||||
|
||||
Dependency dependency;
|
||||
};
|
||||
|
||||
struct LightmapInstance {
|
||||
RID lightmap;
|
||||
Transform3D transform;
|
||||
};
|
||||
|
||||
class LightStorage : public RendererLightStorage {
|
||||
public:
|
||||
enum ShadowAtlastQuadrant {
|
||||
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 */
|
||||
|
||||
Vector<RID> lightmap_textures;
|
||||
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_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_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;
|
||||
|
||||
/* 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_intance, 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_intance) 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
|
||||
3223
engine/drivers/gles3/storage/material_storage.cpp
Normal file
3223
engine/drivers/gles3/storage/material_storage.cpp
Normal file
File diff suppressed because it is too large
Load diff
645
engine/drivers/gles3/storage/material_storage.h
Normal file
645
engine/drivers/gles3/storage/material_storage.h
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
/**************************************************************************/
|
||||
/* 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/local_vector.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
#include "servers/rendering/shader_compiler.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
#include "servers/rendering/storage/material_storage.h"
|
||||
#include "servers/rendering/storage/utilities.h"
|
||||
|
||||
#include "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 Cull {
|
||||
CULL_DISABLED,
|
||||
CULL_FRONT,
|
||||
CULL_BACK
|
||||
};
|
||||
|
||||
enum AlphaAntiAliasing {
|
||||
ALPHA_ANTIALIASING_OFF,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE,
|
||||
ALPHA_ANTIALIASING_ALPHA_TO_COVERAGE_AND_TO_ONE
|
||||
};
|
||||
|
||||
// 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;
|
||||
Cull 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 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
|
||||
2341
engine/drivers/gles3/storage/mesh_storage.cpp
Normal file
2341
engine/drivers/gles3/storage/mesh_storage.cpp
Normal file
File diff suppressed because it is too large
Load diff
598
engine/drivers/gles3/storage/mesh_storage.h
Normal file
598
engine/drivers/gles3/storage/mesh_storage.h
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
|
||||
Dependency dependency;
|
||||
};
|
||||
|
||||
struct Skeleton {
|
||||
bool use_2d = false;
|
||||
int size = 0;
|
||||
int height = 0;
|
||||
Vector<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);
|
||||
|
||||
/* 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 _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;
|
||||
|
||||
_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) 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) const override;
|
||||
|
||||
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
|
||||
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const override;
|
||||
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const override;
|
||||
virtual Color multimesh_instance_get_custom_data(RID p_multimesh, int p_index) const override;
|
||||
virtual void multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_buffer) override;
|
||||
virtual Vector<float> multimesh_get_buffer(RID p_multimesh) const override;
|
||||
|
||||
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
|
||||
virtual int multimesh_get_visible_instances(RID p_multimesh) const override;
|
||||
|
||||
void _update_dirty_multimeshes();
|
||||
|
||||
_FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->xform_format;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool multimesh_uses_colors(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->uses_colors;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool multimesh_uses_custom_data(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->uses_custom_data;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t multimesh_get_instances_to_draw(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
if (multimesh->visible_instances >= 0) {
|
||||
return multimesh->visible_instances;
|
||||
}
|
||||
return multimesh->instances;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ GLuint multimesh_get_gl_buffer(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->buffer;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t multimesh_get_stride(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->stride_cache;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t multimesh_get_color_offset(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->color_offset_cache;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t multimesh_get_custom_data_offset(RID p_multimesh) const {
|
||||
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
|
||||
return multimesh->custom_data_offset_cache;
|
||||
}
|
||||
|
||||
/* SKELETON API */
|
||||
|
||||
Skeleton *get_skeleton(RID p_rid) { return skeleton_owner.get_or_null(p_rid); };
|
||||
bool owns_skeleton(RID p_rid) { return skeleton_owner.owns(p_rid); };
|
||||
|
||||
virtual RID skeleton_allocate() override;
|
||||
virtual void skeleton_initialize(RID p_rid) override;
|
||||
virtual void skeleton_free(RID p_rid) override;
|
||||
|
||||
virtual void skeleton_allocate_data(RID p_skeleton, int p_bones, bool p_2d_skeleton = false) override;
|
||||
virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) override;
|
||||
virtual int skeleton_get_bone_count(RID p_skeleton) const override;
|
||||
virtual void skeleton_bone_set_transform(RID p_skeleton, int p_bone, const Transform3D &p_transform) override;
|
||||
virtual Transform3D skeleton_bone_get_transform(RID p_skeleton, int p_bone) const override;
|
||||
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) override;
|
||||
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const override;
|
||||
|
||||
virtual void skeleton_update_dependency(RID p_base, DependencyTracker *p_instance) override;
|
||||
|
||||
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
|
||||
1436
engine/drivers/gles3/storage/particles_storage.cpp
Normal file
1436
engine/drivers/gles3/storage/particles_storage.cpp
Normal file
File diff suppressed because it is too large
Load diff
458
engine/drivers/gles3/storage/particles_storage.h
Normal file
458
engine/drivers/gles3/storage/particles_storage.h
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/**************************************************************************/
|
||||
/* 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/local_vector.h"
|
||||
#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 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) {
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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_set_explosiveness_ratio(RID p_particles, real_t p_ratio) override;
|
||||
virtual void particles_set_randomness_ratio(RID p_particles, real_t p_ratio) override;
|
||||
virtual void particles_set_custom_aabb(RID p_particles, const AABB &p_aabb) override;
|
||||
virtual void particles_set_speed_scale(RID p_particles, double p_scale) override;
|
||||
virtual void particles_set_use_local_coordinates(RID p_particles, bool p_enable) override;
|
||||
virtual void particles_set_process_material(RID p_particles, RID p_material) override;
|
||||
virtual RID particles_get_process_material(RID p_particles) const override;
|
||||
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) override;
|
||||
virtual void particles_set_interpolate(RID p_particles, bool p_enable) override;
|
||||
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) override;
|
||||
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) override;
|
||||
virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis, const Vector3 &p_up_axis) override;
|
||||
virtual void particles_set_collision_base_size(RID p_particles, real_t p_size) override;
|
||||
|
||||
virtual void particles_set_transform_align(RID p_particles, RS::ParticlesTransformAlign p_transform_align) override;
|
||||
|
||||
virtual void particles_set_trails(RID p_particles, bool p_enable, double p_length) override;
|
||||
virtual void particles_set_trail_bind_poses(RID p_particles, const Vector<Transform3D> &p_bind_poses) override;
|
||||
|
||||
virtual void particles_restart(RID p_particles) override;
|
||||
|
||||
virtual void particles_set_draw_order(RID p_particles, RS::ParticlesDrawOrder p_order) override;
|
||||
|
||||
virtual void particles_set_draw_passes(RID p_particles, int p_count) override;
|
||||
virtual void particles_set_draw_pass_mesh(RID p_particles, int p_pass, RID p_mesh) override;
|
||||
|
||||
virtual void particles_request_process(RID p_particles) override;
|
||||
virtual AABB particles_get_current_aabb(RID p_particles) override;
|
||||
virtual AABB particles_get_aabb(RID p_particles) const override;
|
||||
|
||||
virtual void particles_set_emission_transform(RID p_particles, const Transform3D &p_transform) override;
|
||||
virtual 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, 0);
|
||||
|
||||
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;
|
||||
|
||||
_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
|
||||
671
engine/drivers/gles3/storage/render_scene_buffers_gles3.cpp
Normal file
671
engine/drivers/gles3/storage/render_scene_buffers_gles3.cpp
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
/**************************************************************************/
|
||||
/* 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();
|
||||
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
|
||||
167
engine/drivers/gles3/storage/render_scene_buffers_gles3.h
Normal file
167
engine/drivers/gles3/storage/render_scene_buffers_gles3.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**************************************************************************/
|
||||
/* 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_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
|
||||
3078
engine/drivers/gles3/storage/texture_storage.cpp
Normal file
3078
engine/drivers/gles3/storage/texture_storage.cpp
Normal file
File diff suppressed because it is too large
Load diff
717
engine/drivers/gles3/storage/texture_storage.h
Normal file
717
engine/drivers/gles3/storage/texture_storage.h
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
/**************************************************************************/
|
||||
/* 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/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_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_external = 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_external = o.is_external;
|
||||
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;
|
||||
|
||||
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 bool can_create_resources_async() const override;
|
||||
|
||||
virtual RID texture_allocate() override;
|
||||
virtual void texture_free(RID p_rid) override;
|
||||
|
||||
virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override;
|
||||
virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override;
|
||||
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override;
|
||||
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent
|
||||
|
||||
RID texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY);
|
||||
|
||||
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
|
||||
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override;
|
||||
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;
|
||||
|
||||
//these two APIs can be used together or in combination with the others.
|
||||
virtual void texture_2d_placeholder_initialize(RID p_texture) override;
|
||||
virtual void texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) override;
|
||||
virtual void texture_3d_placeholder_initialize(RID p_texture) override;
|
||||
|
||||
virtual Ref<Image> texture_2d_get(RID p_texture) const override;
|
||||
virtual Ref<Image> texture_2d_layer_get(RID p_texture, int p_layer) const override;
|
||||
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;
|
||||
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) 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_texture(RID p_render_target) override;
|
||||
|
||||
void bind_framebuffer(GLuint framebuffer) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
}
|
||||
|
||||
void bind_framebuffer_system() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
|
||||
}
|
||||
|
||||
String get_framebuffer_error(GLenum p_status);
|
||||
};
|
||||
|
||||
inline String TextureStorage::get_framebuffer_error(GLenum p_status) {
|
||||
#if defined(DEBUG_ENABLED) && defined(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
|
||||
470
engine/drivers/gles3/storage/utilities.cpp
Normal file
470
engine/drivers/gles3/storage/utilities.cpp
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
#include "servers/rendering/rendering_server_globals.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]);
|
||||
}
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
235
engine/drivers/gles3/storage/utilities.h
Normal file
235
engine/drivers/gles3/storage/utilities.h
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/**************************************************************************/
|
||||
/* 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;
|
||||
};
|
||||
|
||||
} // namespace GLES3
|
||||
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#endif // UTILITIES_GLES3_H
|
||||
72
engine/drivers/png/SCsub
Normal file
72
engine/drivers/png/SCsub
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env_png = env.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
if env["builtin_libpng"]:
|
||||
thirdparty_dir = "#thirdparty/libpng/"
|
||||
thirdparty_sources = [
|
||||
"png.c",
|
||||
"pngerror.c",
|
||||
"pngget.c",
|
||||
"pngmem.c",
|
||||
"pngpread.c",
|
||||
"pngread.c",
|
||||
"pngrio.c",
|
||||
"pngrtran.c",
|
||||
"pngrutil.c",
|
||||
"pngset.c",
|
||||
"pngtrans.c",
|
||||
"pngwio.c",
|
||||
"pngwrite.c",
|
||||
"pngwtran.c",
|
||||
"pngwutil.c",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_png.Prepend(CPPPATH=[thirdparty_dir])
|
||||
# Needed for drivers includes and in platform/web.
|
||||
env.Prepend(CPPPATH=[thirdparty_dir])
|
||||
|
||||
env_thirdparty = env_png.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
|
||||
if env["arch"].startswith("arm"):
|
||||
if env.msvc: # Can't compile assembly files with MSVC.
|
||||
env_thirdparty.Append(CPPDEFINES=[("PNG_ARM_NEON_OPT", 0)])
|
||||
else:
|
||||
env_neon = env_thirdparty.Clone()
|
||||
if "S_compiler" in env:
|
||||
env_neon["CC"] = env["S_compiler"]
|
||||
neon_sources = []
|
||||
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
|
||||
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
|
||||
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
|
||||
neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
|
||||
thirdparty_obj += neon_sources
|
||||
elif env["arch"].startswith("x86"):
|
||||
env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"])
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c")
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c")
|
||||
elif env["arch"] == "ppc64":
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c")
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c")
|
||||
|
||||
env.drivers_sources += thirdparty_obj
|
||||
|
||||
|
||||
# Godot source files
|
||||
|
||||
driver_obj = []
|
||||
|
||||
env_png.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)
|
||||
107
engine/drivers/png/image_loader_png.cpp
Normal file
107
engine/drivers/png/image_loader_png.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**************************************************************************/
|
||||
/* image_loader_png.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 "image_loader_png.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "drivers/png/png_driver_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
|
||||
const uint64_t buffer_size = f->get_length();
|
||||
Vector<uint8_t> file_buffer;
|
||||
Error err = file_buffer.resize(buffer_size);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
{
|
||||
uint8_t *writer = file_buffer.ptrw();
|
||||
f->get_buffer(writer, buffer_size);
|
||||
}
|
||||
const uint8_t *reader = file_buffer.ptr();
|
||||
return PNGDriverCommon::png_to_image(reader, buffer_size, p_flags & FLAG_FORCE_LINEAR, p_image);
|
||||
}
|
||||
|
||||
void ImageLoaderPNG::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
p_extensions->push_back("png");
|
||||
}
|
||||
|
||||
Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) {
|
||||
Ref<Image> img;
|
||||
img.instantiate();
|
||||
|
||||
// the value of p_force_linear does not matter since it only applies to 16 bit
|
||||
Error err = PNGDriverCommon::png_to_image(p_png, p_size, false, img);
|
||||
ERR_FAIL_COND_V(err, Ref<Image>());
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
Ref<Image> ImageLoaderPNG::unpack_mem_png(const uint8_t *p_png, int p_size) {
|
||||
ERR_FAIL_COND_V(p_size < 4, Ref<Image>());
|
||||
ERR_FAIL_COND_V(p_png[0] != 'P' || p_png[1] != 'N' || p_png[2] != 'G' || p_png[3] != ' ', Ref<Image>());
|
||||
return load_mem_png(&p_png[4], p_size - 4);
|
||||
}
|
||||
|
||||
Ref<Image> ImageLoaderPNG::lossless_unpack_png(const Vector<uint8_t> &p_data) {
|
||||
return unpack_mem_png(p_data.ptr(), p_data.size());
|
||||
}
|
||||
|
||||
Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) {
|
||||
Vector<uint8_t> out_buffer;
|
||||
|
||||
// add Godot's own "PNG " prefix
|
||||
if (out_buffer.resize(4) != OK) {
|
||||
ERR_FAIL_V(Vector<uint8_t>());
|
||||
}
|
||||
|
||||
// scope for writer lifetime
|
||||
{
|
||||
// must be closed before call to image_to_png
|
||||
uint8_t *writer = out_buffer.ptrw();
|
||||
memcpy(writer, "PNG ", 4);
|
||||
}
|
||||
|
||||
Error err = PNGDriverCommon::image_to_png(p_image, out_buffer);
|
||||
if (err) {
|
||||
ERR_FAIL_V(Vector<uint8_t>());
|
||||
}
|
||||
|
||||
return out_buffer;
|
||||
}
|
||||
|
||||
ImageLoaderPNG::ImageLoaderPNG() {
|
||||
Image::_png_mem_loader_func = load_mem_png;
|
||||
Image::_png_mem_unpacker_func = unpack_mem_png;
|
||||
Image::png_unpacker = lossless_unpack_png;
|
||||
Image::png_packer = lossless_pack_png;
|
||||
}
|
||||
49
engine/drivers/png/image_loader_png.h
Normal file
49
engine/drivers/png/image_loader_png.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* image_loader_png.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 IMAGE_LOADER_PNG_H
|
||||
#define IMAGE_LOADER_PNG_H
|
||||
|
||||
#include "core/io/image_loader.h"
|
||||
|
||||
class ImageLoaderPNG : public ImageFormatLoader {
|
||||
private:
|
||||
static Vector<uint8_t> lossless_pack_png(const Ref<Image> &p_image);
|
||||
static Ref<Image> lossless_unpack_png(const Vector<uint8_t> &p_data);
|
||||
static Ref<Image> unpack_mem_png(const uint8_t *p_png, int p_size);
|
||||
static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size);
|
||||
|
||||
public:
|
||||
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale);
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const;
|
||||
ImageLoaderPNG();
|
||||
};
|
||||
|
||||
#endif // IMAGE_LOADER_PNG_H
|
||||
206
engine/drivers/png/png_driver_common.cpp
Normal file
206
engine/drivers/png/png_driver_common.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/**************************************************************************/
|
||||
/* png_driver_common.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 "png_driver_common.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include <png.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace PNGDriverCommon {
|
||||
|
||||
// Print any warnings.
|
||||
// On error, set explain and return true.
|
||||
// Call should be wrapped in ERR_FAIL_COND
|
||||
static bool check_error(const png_image &image) {
|
||||
const png_uint_32 failed = PNG_IMAGE_FAILED(image);
|
||||
if (failed & PNG_IMAGE_ERROR) {
|
||||
return true;
|
||||
} else if (failed) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
// suppress this warning, to avoid log spam when opening assetlib
|
||||
const static char *const noisy = "iCCP: known incorrect sRGB profile";
|
||||
const Engine *const eng = Engine::get_singleton();
|
||||
if (eng && eng->is_editor_hint() && !strcmp(image.message, noisy)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
WARN_PRINT(image.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image) {
|
||||
png_image png_img;
|
||||
memset(&png_img, 0, sizeof(png_img));
|
||||
png_img.version = PNG_IMAGE_VERSION;
|
||||
|
||||
// fetch image properties
|
||||
int success = png_image_begin_read_from_memory(&png_img, p_source, p_size);
|
||||
ERR_FAIL_COND_V_MSG(check_error(png_img), ERR_FILE_CORRUPT, png_img.message);
|
||||
ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT);
|
||||
|
||||
// flags to be masked out of input format to give target format
|
||||
const png_uint_32 format_mask = ~(
|
||||
// convert component order to RGBA
|
||||
PNG_FORMAT_FLAG_BGR | PNG_FORMAT_FLAG_AFIRST
|
||||
// convert 16 bit components to 8 bit
|
||||
| PNG_FORMAT_FLAG_LINEAR
|
||||
// convert indexed image to direct color
|
||||
| PNG_FORMAT_FLAG_COLORMAP);
|
||||
|
||||
png_img.format &= format_mask;
|
||||
|
||||
Image::Format dest_format;
|
||||
switch (png_img.format) {
|
||||
case PNG_FORMAT_GRAY:
|
||||
dest_format = Image::FORMAT_L8;
|
||||
break;
|
||||
case PNG_FORMAT_GA:
|
||||
dest_format = Image::FORMAT_LA8;
|
||||
break;
|
||||
case PNG_FORMAT_RGB:
|
||||
dest_format = Image::FORMAT_RGB8;
|
||||
break;
|
||||
case PNG_FORMAT_RGBA:
|
||||
dest_format = Image::FORMAT_RGBA8;
|
||||
break;
|
||||
default:
|
||||
png_image_free(&png_img); // only required when we return before finish_read
|
||||
ERR_PRINT("Unsupported png format.");
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (!p_force_linear) {
|
||||
// assume 16 bit pngs without sRGB or gAMA chunks are in sRGB format
|
||||
png_img.flags |= PNG_IMAGE_FLAG_16BIT_sRGB;
|
||||
}
|
||||
|
||||
const png_uint_32 stride = PNG_IMAGE_ROW_STRIDE(png_img);
|
||||
Vector<uint8_t> buffer;
|
||||
Error err = buffer.resize(PNG_IMAGE_BUFFER_SIZE(png_img, stride));
|
||||
if (err) {
|
||||
png_image_free(&png_img); // only required when we return before finish_read
|
||||
return err;
|
||||
}
|
||||
uint8_t *writer = buffer.ptrw();
|
||||
|
||||
// read image data to buffer and release libpng resources
|
||||
success = png_image_finish_read(&png_img, nullptr, writer, stride, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(check_error(png_img), ERR_FILE_CORRUPT, png_img.message);
|
||||
ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT);
|
||||
|
||||
//print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height));
|
||||
p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) {
|
||||
Ref<Image> source_image = p_image->duplicate();
|
||||
|
||||
if (source_image->is_compressed()) {
|
||||
source_image->decompress();
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(source_image->is_compressed(), FAILED);
|
||||
|
||||
png_image png_img;
|
||||
memset(&png_img, 0, sizeof(png_img));
|
||||
png_img.version = PNG_IMAGE_VERSION;
|
||||
png_img.width = source_image->get_width();
|
||||
png_img.height = source_image->get_height();
|
||||
|
||||
switch (source_image->get_format()) {
|
||||
case Image::FORMAT_L8:
|
||||
png_img.format = PNG_FORMAT_GRAY;
|
||||
break;
|
||||
case Image::FORMAT_LA8:
|
||||
png_img.format = PNG_FORMAT_GA;
|
||||
break;
|
||||
case Image::FORMAT_RGB8:
|
||||
png_img.format = PNG_FORMAT_RGB;
|
||||
break;
|
||||
case Image::FORMAT_RGBA8:
|
||||
png_img.format = PNG_FORMAT_RGBA;
|
||||
break;
|
||||
default:
|
||||
if (source_image->detect_alpha()) {
|
||||
source_image->convert(Image::FORMAT_RGBA8);
|
||||
png_img.format = PNG_FORMAT_RGBA;
|
||||
} else {
|
||||
source_image->convert(Image::FORMAT_RGB8);
|
||||
png_img.format = PNG_FORMAT_RGB;
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<uint8_t> image_data = source_image->get_data();
|
||||
const uint8_t *reader = image_data.ptr();
|
||||
|
||||
// we may be passed a buffer with existing content we're expected to append to
|
||||
const int buffer_offset = p_buffer.size();
|
||||
|
||||
const size_t png_size_estimate = PNG_IMAGE_PNG_SIZE_MAX(png_img);
|
||||
|
||||
// try with estimated size
|
||||
size_t compressed_size = png_size_estimate;
|
||||
int success = 0;
|
||||
{ // scope writer lifetime
|
||||
Error err = p_buffer.resize(buffer_offset + png_size_estimate);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
|
||||
uint8_t *writer = p_buffer.ptrw();
|
||||
success = png_image_write_to_memory(&png_img, &writer[buffer_offset],
|
||||
&compressed_size, 0, reader, 0, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(check_error(png_img), FAILED, png_img.message);
|
||||
}
|
||||
if (!success) {
|
||||
// buffer was big enough, must be some other error
|
||||
ERR_FAIL_COND_V(compressed_size <= png_size_estimate, FAILED);
|
||||
|
||||
// write failed due to buffer size, resize and retry
|
||||
Error err = p_buffer.resize(buffer_offset + compressed_size);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
|
||||
uint8_t *writer = p_buffer.ptrw();
|
||||
success = png_image_write_to_memory(&png_img, &writer[buffer_offset],
|
||||
&compressed_size, 0, reader, 0, nullptr);
|
||||
ERR_FAIL_COND_V_MSG(check_error(png_img), FAILED, png_img.message);
|
||||
ERR_FAIL_COND_V(!success, FAILED);
|
||||
}
|
||||
|
||||
// trim buffer size to content
|
||||
Error err = p_buffer.resize(buffer_offset + compressed_size);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
|
||||
return OK;
|
||||
}
|
||||
} // namespace PNGDriverCommon
|
||||
46
engine/drivers/png/png_driver_common.h
Normal file
46
engine/drivers/png/png_driver_common.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**************************************************************************/
|
||||
/* png_driver_common.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 PNG_DRIVER_COMMON_H
|
||||
#define PNG_DRIVER_COMMON_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
|
||||
namespace PNGDriverCommon {
|
||||
|
||||
// Attempt to load png from buffer (p_source, p_size) into p_image
|
||||
Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image);
|
||||
|
||||
// Append p_image, as a png, to p_buffer.
|
||||
// Contents of p_buffer is unspecified if error returned.
|
||||
Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer);
|
||||
} // namespace PNGDriverCommon
|
||||
|
||||
#endif // PNG_DRIVER_COMMON_H
|
||||
88
engine/drivers/png/resource_saver_png.cpp
Normal file
88
engine/drivers/png/resource_saver_png.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**************************************************************************/
|
||||
/* resource_saver_png.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 "resource_saver_png.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "drivers/png/png_driver_common.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
Error ResourceSaverPNG::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
|
||||
Ref<ImageTexture> texture = p_resource;
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as PNG.");
|
||||
ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as PNG.");
|
||||
|
||||
Ref<Image> img = texture->get_image();
|
||||
|
||||
Error err = save_image(p_path, img);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error ResourceSaverPNG::save_image(const String &p_path, const Ref<Image> &p_img) {
|
||||
Vector<uint8_t> buffer;
|
||||
Error err = PNGDriverCommon::image_to_png(p_img, buffer);
|
||||
ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG.");
|
||||
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
|
||||
ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save PNG at path: '%s'.", p_path));
|
||||
|
||||
const uint8_t *reader = buffer.ptr();
|
||||
|
||||
file->store_buffer(reader, buffer.size());
|
||||
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Vector<uint8_t> ResourceSaverPNG::save_image_to_buffer(const Ref<Image> &p_img) {
|
||||
Vector<uint8_t> buffer;
|
||||
Error err = PNGDriverCommon::image_to_png(p_img, buffer);
|
||||
ERR_FAIL_COND_V_MSG(err, Vector<uint8_t>(), "Can't convert image to PNG.");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool ResourceSaverPNG::recognize(const Ref<Resource> &p_resource) const {
|
||||
return (p_resource.is_valid() && p_resource->is_class("ImageTexture"));
|
||||
}
|
||||
|
||||
void ResourceSaverPNG::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
|
||||
if (Object::cast_to<ImageTexture>(*p_resource)) {
|
||||
p_extensions->push_back("png");
|
||||
}
|
||||
}
|
||||
|
||||
ResourceSaverPNG::ResourceSaverPNG() {
|
||||
Image::save_png_func = &save_image;
|
||||
Image::save_png_buffer_func = &save_image_to_buffer;
|
||||
}
|
||||
49
engine/drivers/png/resource_saver_png.h
Normal file
49
engine/drivers/png/resource_saver_png.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/**************************************************************************/
|
||||
/* resource_saver_png.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 RESOURCE_SAVER_PNG_H
|
||||
#define RESOURCE_SAVER_PNG_H
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
|
||||
class ResourceSaverPNG : public ResourceFormatSaver {
|
||||
public:
|
||||
static Error save_image(const String &p_path, const Ref<Image> &p_img);
|
||||
static Vector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img);
|
||||
|
||||
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override;
|
||||
virtual bool recognize(const Ref<Resource> &p_resource) const override;
|
||||
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override;
|
||||
|
||||
ResourceSaverPNG();
|
||||
};
|
||||
|
||||
#endif // RESOURCE_SAVER_PNG_H
|
||||
9
engine/drivers/pulseaudio/SCsub
Normal file
9
engine/drivers/pulseaudio/SCsub
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
if "pulseaudio" in env and env["pulseaudio"]:
|
||||
if env["use_sowrap"]:
|
||||
env.add_source_files(env.drivers_sources, "pulse-so_wrap.c")
|
||||
|
||||
env.add_source_files(env.drivers_sources, "*.cpp")
|
||||
845
engine/drivers/pulseaudio/audio_driver_pulseaudio.cpp
Normal file
845
engine/drivers/pulseaudio/audio_driver_pulseaudio.cpp
Normal file
|
|
@ -0,0 +1,845 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_pulseaudio.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_pulseaudio.h"
|
||||
|
||||
#ifdef PULSEAUDIO_ENABLED
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
|
||||
#ifdef ALSAMIDI_ENABLED
|
||||
#ifdef SOWRAP_ENABLED
|
||||
#include "drivers/alsa/asound-so_wrap.h"
|
||||
#else
|
||||
#include <alsa/asoundlib.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
switch (pa_context_get_state(c)) {
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
print_verbose("PulseAudio: context terminated");
|
||||
ad->pa_ready = -1;
|
||||
break;
|
||||
case PA_CONTEXT_FAILED:
|
||||
print_verbose("PulseAudio: context failed");
|
||||
ad->pa_ready = -1;
|
||||
break;
|
||||
case PA_CONTEXT_READY:
|
||||
print_verbose("PulseAudio: context ready");
|
||||
ad->pa_ready = 1;
|
||||
break;
|
||||
default:
|
||||
print_verbose("PulseAudio: context other");
|
||||
// TODO: Check if we want to handle some of the other
|
||||
// PA context states like PA_CONTEXT_UNCONNECTED.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
// If eol is set to a positive number, you're at the end of the list
|
||||
if (eol > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If eol is set to a negative number there's an error.
|
||||
if (eol < 0) {
|
||||
ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));
|
||||
ad->pa_status--;
|
||||
return;
|
||||
}
|
||||
|
||||
ad->pa_map = l->channel_map;
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
// If eol is set to a positive number, you're at the end of the list
|
||||
if (eol > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If eol is set to a negative number there's an error.
|
||||
if (eol < 0) {
|
||||
ERR_PRINT("PulseAudio: sink info error: " + String(pa_strerror(pa_context_errno(c))));
|
||||
ad->pa_status--;
|
||||
return;
|
||||
}
|
||||
|
||||
ad->pa_rec_map = l->channel_map;
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {
|
||||
ERR_FAIL_NULL_MSG(i, "PulseAudio server info is null.");
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
ad->default_input_device = i->default_source_name;
|
||||
ad->default_output_device = i->default_sink_name;
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::detect_channels(bool input) {
|
||||
pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map);
|
||||
|
||||
String device = input ? input_device_name : output_device_name;
|
||||
if (device == "Default") {
|
||||
// Get the default output device name
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this);
|
||||
if (pa_op) {
|
||||
while (pa_status == 0) {
|
||||
int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
pa_operation_unref(pa_op);
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
char dev[1024];
|
||||
if (device == "Default") {
|
||||
strcpy(dev, input ? default_input_device.utf8().get_data() : default_output_device.utf8().get_data());
|
||||
} else {
|
||||
strcpy(dev, device.utf8().get_data());
|
||||
}
|
||||
print_verbose("PulseAudio: Detecting channels for device: " + String(dev));
|
||||
|
||||
// Now using the device name get the amount of channels
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op;
|
||||
if (input) {
|
||||
pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this);
|
||||
} else {
|
||||
pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this);
|
||||
}
|
||||
|
||||
if (pa_op) {
|
||||
while (pa_status == 0) {
|
||||
int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
pa_operation_unref(pa_op);
|
||||
|
||||
if (pa_status == -1) {
|
||||
return FAILED;
|
||||
}
|
||||
} else {
|
||||
if (input) {
|
||||
ERR_PRINT("pa_context_get_source_info_by_name error");
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_sink_info_by_name error");
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::init_output_device() {
|
||||
// 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";
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the amount of channels PulseAudio is using
|
||||
// Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,
|
||||
// if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels
|
||||
// will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)
|
||||
Error err = detect_channels();
|
||||
if (err != OK) {
|
||||
// This most likely means there are no sinks.
|
||||
ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels");
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (pa_map.channels) {
|
||||
case 1: // Mono
|
||||
case 3: // Surround 2.1
|
||||
case 5: // Surround 5.0
|
||||
case 7: // Surround 7.0
|
||||
channels = pa_map.channels + 1;
|
||||
break;
|
||||
|
||||
case 2: // Stereo
|
||||
case 4: // Surround 4.0
|
||||
case 6: // Surround 5.1
|
||||
case 8: // Surround 7.1
|
||||
channels = pa_map.channels;
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));
|
||||
pa_channel_map_init_stereo(&pa_map);
|
||||
channels = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
int tmp_latency = Engine::get_singleton()->get_audio_output_latency();
|
||||
buffer_frames = closest_power_of_2(tmp_latency * mix_rate / 1000);
|
||||
pa_buffer_size = buffer_frames * pa_map.channels;
|
||||
|
||||
print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
|
||||
print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
|
||||
|
||||
pa_sample_spec spec;
|
||||
spec.format = PA_SAMPLE_S16LE;
|
||||
spec.channels = pa_map.channels;
|
||||
spec.rate = mix_rate;
|
||||
pa_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
pa_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
pa_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
pa_map.map[3] = PA_CHANNEL_POSITION_LFE;
|
||||
pa_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
pa_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
pa_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
|
||||
pa_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
|
||||
|
||||
pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
|
||||
if (pa_str == nullptr) {
|
||||
ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
|
||||
ERR_FAIL_V(ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
pa_buffer_attr attr;
|
||||
// set to appropriate buffer length (in bytes) from global settings
|
||||
// Note: PulseAudio defaults to 4 fragments, which means that the actual
|
||||
// latency is tlength / fragments. It seems that the PulseAudio has no way
|
||||
// to get the fragments number so we're hardcoding this to the default of 4
|
||||
const int fragments = 4;
|
||||
attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments;
|
||||
// set them to be automatically chosen
|
||||
attr.prebuf = (uint32_t)-1;
|
||||
attr.maxlength = (uint32_t)-1;
|
||||
attr.minreq = (uint32_t)-1;
|
||||
|
||||
const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data();
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr);
|
||||
ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN);
|
||||
|
||||
samples_in.resize(buffer_frames * channels);
|
||||
samples_out.resize(pa_buffer_size);
|
||||
|
||||
// Reset audio input to keep synchronization.
|
||||
input_position = 0;
|
||||
input_size = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::init() {
|
||||
#ifdef SOWRAP_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
int dylibloader_verbose = 1;
|
||||
#else
|
||||
int dylibloader_verbose = 0;
|
||||
#endif
|
||||
#ifdef ALSAMIDI_ENABLED
|
||||
// If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well
|
||||
initialize_asound(dylibloader_verbose);
|
||||
#endif
|
||||
if (initialize_pulse(dylibloader_verbose)) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
#endif
|
||||
bool ver_ok = false;
|
||||
String version = String::utf8(pa_get_library_version());
|
||||
Vector<String> ver_parts = version.split(".");
|
||||
if (ver_parts.size() >= 2) {
|
||||
ver_ok = (ver_parts[0].to_int() >= 8); // 8.0.0
|
||||
}
|
||||
print_verbose(vformat("PulseAudio %s detected.", version));
|
||||
if (!ver_ok) {
|
||||
print_verbose("Unsupported PulseAudio library version!");
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
active.clear();
|
||||
exit_thread.clear();
|
||||
|
||||
mix_rate = _get_configured_mix_rate();
|
||||
|
||||
pa_ml = pa_mainloop_new();
|
||||
ERR_FAIL_NULL_V(pa_ml, ERR_CANT_OPEN);
|
||||
|
||||
String context_name;
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
context_name = VERSION_NAME " Editor";
|
||||
} else {
|
||||
context_name = GLOBAL_GET("application/config/name");
|
||||
if (context_name.is_empty()) {
|
||||
context_name = VERSION_NAME " Project";
|
||||
}
|
||||
}
|
||||
|
||||
pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), context_name.utf8().ptr());
|
||||
ERR_FAIL_NULL_V(pa_ctx, ERR_CANT_OPEN);
|
||||
|
||||
pa_ready = 0;
|
||||
pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this);
|
||||
|
||||
int ret = pa_context_connect(pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
|
||||
if (ret < 0) {
|
||||
if (pa_ctx) {
|
||||
pa_context_unref(pa_ctx);
|
||||
pa_ctx = nullptr;
|
||||
}
|
||||
|
||||
if (pa_ml) {
|
||||
pa_mainloop_free(pa_ml);
|
||||
pa_ml = nullptr;
|
||||
}
|
||||
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
while (pa_ready == 0) {
|
||||
ret = pa_mainloop_iterate(pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
if (pa_ready < 0) {
|
||||
if (pa_ctx) {
|
||||
pa_context_disconnect(pa_ctx);
|
||||
pa_context_unref(pa_ctx);
|
||||
pa_ctx = nullptr;
|
||||
}
|
||||
|
||||
if (pa_ml) {
|
||||
pa_mainloop_free(pa_ml);
|
||||
pa_ml = nullptr;
|
||||
}
|
||||
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
init_output_device();
|
||||
thread.start(AudioDriverPulseAudio::thread_func, this);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
float AudioDriverPulseAudio::get_latency() {
|
||||
lock();
|
||||
|
||||
pa_usec_t pa_lat = 0;
|
||||
if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {
|
||||
int negative = 0;
|
||||
|
||||
if (pa_stream_get_latency(pa_str, &pa_lat, &negative) >= 0) {
|
||||
if (negative) {
|
||||
pa_lat = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pa_lat > 0) {
|
||||
latency = double(pa_lat) / 1000000.0;
|
||||
}
|
||||
|
||||
unlock();
|
||||
return latency;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::thread_func(void *p_udata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(p_udata);
|
||||
unsigned int write_ofs = 0;
|
||||
size_t avail_bytes = 0;
|
||||
uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();
|
||||
|
||||
while (!ad->exit_thread.is_set()) {
|
||||
size_t read_bytes = 0;
|
||||
size_t written_bytes = 0;
|
||||
|
||||
if (avail_bytes == 0) {
|
||||
ad->lock();
|
||||
ad->start_counting_ticks();
|
||||
|
||||
if (!ad->active.is_set()) {
|
||||
ad->samples_out.fill(0);
|
||||
} else {
|
||||
ad->audio_server_process(ad->buffer_frames, ad->samples_in.ptrw());
|
||||
|
||||
int16_t *out_ptr = ad->samples_out.ptrw();
|
||||
|
||||
if (ad->channels == ad->pa_map.channels) {
|
||||
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
|
||||
out_ptr[i] = ad->samples_in[i] >> 16;
|
||||
}
|
||||
} else {
|
||||
// Uneven amount of channels
|
||||
unsigned int in_idx = 0;
|
||||
unsigned int out_idx = 0;
|
||||
|
||||
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
|
||||
for (int j = 0; j < ad->pa_map.channels - 1; j++) {
|
||||
out_ptr[out_idx++] = ad->samples_in[in_idx++] >> 16;
|
||||
}
|
||||
uint32_t l = ad->samples_in[in_idx++] >> 16;
|
||||
uint32_t r = ad->samples_in[in_idx++] >> 16;
|
||||
out_ptr[out_idx++] = (l + r) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avail_bytes = ad->pa_buffer_size * sizeof(int16_t);
|
||||
write_ofs = 0;
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
}
|
||||
|
||||
ad->lock();
|
||||
ad->start_counting_ticks();
|
||||
|
||||
int ret;
|
||||
do {
|
||||
ret = pa_mainloop_iterate(ad->pa_ml, 0, nullptr);
|
||||
} while (ret > 0);
|
||||
|
||||
if (avail_bytes > 0 && pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {
|
||||
size_t bytes = pa_stream_writable_size(ad->pa_str);
|
||||
if (bytes > 0) {
|
||||
size_t bytes_to_write = MIN(bytes, avail_bytes);
|
||||
const void *ptr = ad->samples_out.ptr();
|
||||
ret = pa_stream_write(ad->pa_str, (char *)ptr + write_ofs, bytes_to_write, nullptr, 0LL, PA_SEEK_RELATIVE);
|
||||
if (ret != 0) {
|
||||
ERR_PRINT("PulseAudio: pa_stream_write error: " + String(pa_strerror(ret)));
|
||||
} else {
|
||||
avail_bytes -= bytes_to_write;
|
||||
write_ofs += bytes_to_write;
|
||||
written_bytes += bytes_to_write;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// User selected a new output device, finish the current one so we'll init the new output 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("PulseAudio: 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();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
avail_bytes = 0;
|
||||
write_ofs = 0;
|
||||
}
|
||||
|
||||
// If we're using the default output device, check that the current output device is still the default
|
||||
if (ad->output_device_name == "Default") {
|
||||
uint64_t msec = OS::get_singleton()->get_ticks_msec();
|
||||
if (msec > (default_device_msec + 1000)) {
|
||||
String old_default_device = ad->default_output_device;
|
||||
|
||||
default_device_msec = msec;
|
||||
|
||||
ad->pa_status = 0;
|
||||
pa_operation *pa_op = pa_context_get_server_info(ad->pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)ad);
|
||||
if (pa_op) {
|
||||
while (ad->pa_status == 0) {
|
||||
ret = pa_mainloop_iterate(ad->pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
pa_operation_unref(pa_op);
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx))));
|
||||
}
|
||||
|
||||
if (old_default_device != ad->default_output_device) {
|
||||
ad->finish_output_device();
|
||||
|
||||
Error err = ad->init_output_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("PulseAudio: init_output_device error");
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
break;
|
||||
}
|
||||
|
||||
avail_bytes = 0;
|
||||
write_ofs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ad->pa_rec_str && pa_stream_get_state(ad->pa_rec_str) == PA_STREAM_READY) {
|
||||
size_t bytes = pa_stream_readable_size(ad->pa_rec_str);
|
||||
if (bytes > 0) {
|
||||
const void *ptr = nullptr;
|
||||
size_t maxbytes = ad->input_buffer.size() * sizeof(int16_t);
|
||||
|
||||
bytes = MIN(bytes, maxbytes);
|
||||
ret = pa_stream_peek(ad->pa_rec_str, &ptr, &bytes);
|
||||
if (ret != 0) {
|
||||
ERR_PRINT("pa_stream_peek error");
|
||||
} else {
|
||||
int16_t *srcptr = (int16_t *)ptr;
|
||||
for (size_t i = bytes >> 1; i > 0; i--) {
|
||||
int32_t sample = int32_t(*srcptr++) << 16;
|
||||
ad->input_buffer_write(sample);
|
||||
|
||||
if (ad->pa_rec_map.channels == 1) {
|
||||
// In case input device is single channel convert it to Stereo
|
||||
ad->input_buffer_write(sample);
|
||||
}
|
||||
}
|
||||
|
||||
read_bytes += bytes;
|
||||
ret = pa_stream_drop(ad->pa_rec_str);
|
||||
if (ret != 0) {
|
||||
ERR_PRINT("pa_stream_drop error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// User selected a new input device, finish the current one so we'll init the new input device
|
||||
if (ad->input_device_name != ad->new_input_device) {
|
||||
ad->input_device_name = ad->new_input_device;
|
||||
ad->finish_input_device();
|
||||
|
||||
Error err = ad->init_input_device();
|
||||
if (err != OK) {
|
||||
ERR_PRINT("PulseAudio: init_input_device error");
|
||||
ad->input_device_name = "Default";
|
||||
ad->new_input_device = "Default";
|
||||
|
||||
err = ad->init_input_device();
|
||||
if (err != OK) {
|
||||
ad->active.clear();
|
||||
ad->exit_thread.set();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ad->stop_counting_ticks();
|
||||
ad->unlock();
|
||||
|
||||
// Let the thread rest a while if we haven't read or write anything
|
||||
if (written_bytes == 0 && read_bytes == 0) {
|
||||
OS::get_singleton()->delay_usec(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::start() {
|
||||
active.set();
|
||||
}
|
||||
|
||||
int AudioDriverPulseAudio::get_mix_rate() const {
|
||||
return mix_rate;
|
||||
}
|
||||
|
||||
AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const {
|
||||
return get_speaker_mode_by_total_channels(channels);
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
// If eol is set to a positive number, you're at the end of the list
|
||||
if (eol > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ad->pa_devices.push_back(l->name);
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverPulseAudio::get_output_device_list() {
|
||||
pa_devices.clear();
|
||||
pa_devices.push_back("Default");
|
||||
|
||||
if (pa_ctx == nullptr) {
|
||||
return pa_devices;
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
// Get the output device list
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this);
|
||||
if (pa_op) {
|
||||
while (pa_status == 0) {
|
||||
int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
pa_operation_unref(pa_op);
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_server_info error");
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
return pa_devices;
|
||||
}
|
||||
|
||||
String AudioDriverPulseAudio::get_output_device() {
|
||||
return output_device_name;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::set_output_device(const String &p_name) {
|
||||
lock();
|
||||
new_output_device = p_name;
|
||||
unlock();
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::finish_output_device() {
|
||||
if (pa_str) {
|
||||
pa_stream_disconnect(pa_str);
|
||||
pa_stream_unref(pa_str);
|
||||
pa_str = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::finish() {
|
||||
if (!thread.is_started()) {
|
||||
return;
|
||||
}
|
||||
|
||||
exit_thread.set();
|
||||
if (thread.is_started()) {
|
||||
thread.wait_to_finish();
|
||||
}
|
||||
|
||||
finish_output_device();
|
||||
|
||||
if (pa_ctx) {
|
||||
pa_context_disconnect(pa_ctx);
|
||||
pa_context_unref(pa_ctx);
|
||||
pa_ctx = nullptr;
|
||||
}
|
||||
|
||||
if (pa_ml) {
|
||||
pa_mainloop_free(pa_ml);
|
||||
pa_ml = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::init_input_device() {
|
||||
// If there is a specified input device, check that it is really present
|
||||
if (input_device_name != "Default") {
|
||||
PackedStringArray list = get_input_device_list();
|
||||
if (!list.has(input_device_name)) {
|
||||
input_device_name = "Default";
|
||||
new_input_device = "Default";
|
||||
}
|
||||
}
|
||||
|
||||
detect_channels(true);
|
||||
switch (pa_rec_map.channels) {
|
||||
case 1: // Mono
|
||||
case 2: // Stereo
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_PRINT("PulseAudio: Unsupported number of input channels: " + itos(pa_rec_map.channels));
|
||||
pa_channel_map_init_stereo(&pa_rec_map);
|
||||
break;
|
||||
}
|
||||
|
||||
print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
|
||||
|
||||
pa_sample_spec spec;
|
||||
|
||||
spec.format = PA_SAMPLE_S16LE;
|
||||
spec.channels = pa_rec_map.channels;
|
||||
spec.rate = mix_rate;
|
||||
|
||||
int input_latency = 30;
|
||||
int input_buffer_frames = closest_power_of_2(input_latency * mix_rate / 1000);
|
||||
int input_buffer_size = input_buffer_frames * spec.channels;
|
||||
|
||||
pa_buffer_attr attr;
|
||||
attr.fragsize = input_buffer_size * sizeof(int16_t);
|
||||
|
||||
pa_rec_str = pa_stream_new(pa_ctx, "Record", &spec, &pa_rec_map);
|
||||
if (pa_rec_str == nullptr) {
|
||||
ERR_PRINT("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
|
||||
ERR_FAIL_V(ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data();
|
||||
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
|
||||
int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags);
|
||||
if (error_code < 0) {
|
||||
ERR_PRINT("PulseAudio: pa_stream_connect_record error: " + String(pa_strerror(error_code)));
|
||||
ERR_FAIL_V(ERR_CANT_OPEN);
|
||||
}
|
||||
|
||||
input_buffer_init(input_buffer_frames);
|
||||
|
||||
print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
|
||||
print_verbose("PulseAudio: input buffer frames: " + itos(input_buffer_frames) + " calculated latency: " + itos(input_buffer_frames * 1000 / mix_rate) + "ms");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::finish_input_device() {
|
||||
if (pa_rec_str) {
|
||||
int ret = pa_stream_disconnect(pa_rec_str);
|
||||
if (ret != 0) {
|
||||
ERR_PRINT("PulseAudio: pa_stream_disconnect error: " + String(pa_strerror(ret)));
|
||||
}
|
||||
pa_stream_unref(pa_rec_str);
|
||||
pa_rec_str = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::input_start() {
|
||||
lock();
|
||||
Error err = init_input_device();
|
||||
unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Error AudioDriverPulseAudio::input_stop() {
|
||||
lock();
|
||||
finish_input_device();
|
||||
unlock();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {
|
||||
AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata);
|
||||
|
||||
// If eol is set to a positive number, you're at the end of the list
|
||||
if (eol > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->monitor_of_sink == PA_INVALID_INDEX) {
|
||||
ad->pa_rec_devices.push_back(l->name);
|
||||
}
|
||||
|
||||
ad->pa_status++;
|
||||
}
|
||||
|
||||
PackedStringArray AudioDriverPulseAudio::get_input_device_list() {
|
||||
pa_rec_devices.clear();
|
||||
pa_rec_devices.push_back("Default");
|
||||
|
||||
if (pa_ctx == nullptr) {
|
||||
return pa_rec_devices;
|
||||
}
|
||||
|
||||
lock();
|
||||
|
||||
// Get the device list
|
||||
pa_status = 0;
|
||||
pa_operation *pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, (void *)this);
|
||||
if (pa_op) {
|
||||
while (pa_status == 0) {
|
||||
int ret = pa_mainloop_iterate(pa_ml, 1, nullptr);
|
||||
if (ret < 0) {
|
||||
ERR_PRINT("pa_mainloop_iterate error");
|
||||
}
|
||||
}
|
||||
|
||||
pa_operation_unref(pa_op);
|
||||
} else {
|
||||
ERR_PRINT("pa_context_get_server_info error");
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
return pa_rec_devices;
|
||||
}
|
||||
|
||||
String AudioDriverPulseAudio::get_input_device() {
|
||||
lock();
|
||||
String name = input_device_name;
|
||||
unlock();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void AudioDriverPulseAudio::set_input_device(const String &p_name) {
|
||||
lock();
|
||||
new_input_device = p_name;
|
||||
unlock();
|
||||
}
|
||||
|
||||
AudioDriverPulseAudio::AudioDriverPulseAudio() {
|
||||
samples_in.clear();
|
||||
samples_out.clear();
|
||||
}
|
||||
|
||||
#endif // PULSEAUDIO_ENABLED
|
||||
132
engine/drivers/pulseaudio/audio_driver_pulseaudio.h
Normal file
132
engine/drivers/pulseaudio/audio_driver_pulseaudio.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**************************************************************************/
|
||||
/* audio_driver_pulseaudio.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_PULSEAUDIO_H
|
||||
#define AUDIO_DRIVER_PULSEAUDIO_H
|
||||
|
||||
#ifdef PULSEAUDIO_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 "pulse-so_wrap.h"
|
||||
#else
|
||||
#include <pulse/pulseaudio.h>
|
||||
#endif
|
||||
|
||||
class AudioDriverPulseAudio : public AudioDriver {
|
||||
Thread thread;
|
||||
Mutex mutex;
|
||||
|
||||
pa_mainloop *pa_ml = nullptr;
|
||||
pa_context *pa_ctx = nullptr;
|
||||
pa_stream *pa_str = nullptr;
|
||||
pa_stream *pa_rec_str = nullptr;
|
||||
pa_channel_map pa_map = {};
|
||||
pa_channel_map pa_rec_map = {};
|
||||
|
||||
String output_device_name = "Default";
|
||||
String new_output_device = "Default";
|
||||
String default_output_device;
|
||||
|
||||
String input_device_name;
|
||||
String new_input_device;
|
||||
String default_input_device;
|
||||
|
||||
Vector<int32_t> samples_in;
|
||||
Vector<int16_t> samples_out;
|
||||
|
||||
unsigned int mix_rate = 0;
|
||||
unsigned int buffer_frames = 0;
|
||||
unsigned int pa_buffer_size = 0;
|
||||
int channels = 0;
|
||||
int pa_ready = 0;
|
||||
int pa_status = 0;
|
||||
PackedStringArray pa_devices;
|
||||
PackedStringArray pa_rec_devices;
|
||||
|
||||
SafeFlag active;
|
||||
SafeFlag exit_thread;
|
||||
|
||||
float latency = 0;
|
||||
|
||||
static void pa_state_cb(pa_context *c, void *userdata);
|
||||
static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
|
||||
static void pa_source_info_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata);
|
||||
static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata);
|
||||
static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
|
||||
static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata);
|
||||
|
||||
Error init_output_device();
|
||||
void finish_output_device();
|
||||
|
||||
Error init_input_device();
|
||||
void finish_input_device();
|
||||
|
||||
Error detect_channels(bool capture = false);
|
||||
|
||||
static void thread_func(void *p_udata);
|
||||
|
||||
public:
|
||||
virtual const char *get_name() const override {
|
||||
return "PulseAudio";
|
||||
};
|
||||
|
||||
virtual Error init() override;
|
||||
virtual void start() override;
|
||||
virtual int get_mix_rate() const override;
|
||||
virtual SpeakerMode get_speaker_mode() const override;
|
||||
virtual float get_latency() 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;
|
||||
|
||||
virtual Error input_start() override;
|
||||
virtual Error input_stop() override;
|
||||
|
||||
virtual PackedStringArray get_input_device_list() override;
|
||||
virtual String get_input_device() override;
|
||||
virtual void set_input_device(const String &p_name) override;
|
||||
|
||||
AudioDriverPulseAudio();
|
||||
~AudioDriverPulseAudio() {}
|
||||
};
|
||||
|
||||
#endif // PULSEAUDIO_ENABLED
|
||||
|
||||
#endif // AUDIO_DRIVER_PULSEAUDIO_H
|
||||
3884
engine/drivers/pulseaudio/pulse-so_wrap.c
Normal file
3884
engine/drivers/pulseaudio/pulse-so_wrap.c
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue